mirror of
https://github.com/mediacms-io/mediacms.git
synced 2025-07-25 15:03:22 +00:00
feat: adds minimum resolution of 144p
This commit is contained in:
14
README.md
14
README.md
@ -38,7 +38,7 @@ A demo is available at https://demo.mediacms.io
|
|||||||
- **Configurable actions**: allow download, add comments, add likes, dislikes, report media
|
- **Configurable actions**: allow download, add comments, add likes, dislikes, report media
|
||||||
- **Configuration options**: change logos, fonts, styling, add more pages
|
- **Configuration options**: change logos, fonts, styling, add more pages
|
||||||
- **Enhanced video player**: customized video.js player with multiple resolution and playback speed options
|
- **Enhanced video player**: customized video.js player with multiple resolution and playback speed options
|
||||||
- **Multiple transcoding profiles**: sane defaults for multiple dimensions (240p, 360p, 480p, 720p, 1080p) and multiple profiles (h264, h265, vp9)
|
- **Multiple transcoding profiles**: sane defaults for multiple dimensions (144p, 240p, 360p, 480p, 720p, 1080p) and multiple profiles (h264, h265, vp9)
|
||||||
- **Adaptive video streaming**: possible through HLS protocol
|
- **Adaptive video streaming**: possible through HLS protocol
|
||||||
- **Subtitles/CC**: support for multilingual subtitle files
|
- **Subtitles/CC**: support for multilingual subtitle files
|
||||||
- **Scalable transcoding**: transcoding through priorities. Experimental support for remote workers
|
- **Scalable transcoding**: transcoding through priorities. Experimental support for remote workers
|
||||||
@ -93,20 +93,14 @@ There are two ways to run MediaCMS, through Docker Compose and through installin
|
|||||||
|
|
||||||
A complete guide can be found on the blog post [How to self-host and share your videos in 2021](https://medium.com/@MediaCMS.io/how-to-self-host-and-share-your-videos-in-2021-14067e3b291b).
|
A complete guide can be found on the blog post [How to self-host and share your videos in 2021](https://medium.com/@MediaCMS.io/how-to-self-host-and-share-your-videos-in-2021-14067e3b291b).
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
Visit [Configuration](docs/admins_docs.md#5-configuration) page.
|
|
||||||
|
|
||||||
|
|
||||||
## Information for developers
|
|
||||||
Check out the new section on the [Developer Experience](docs/dev_exp.md) page
|
|
||||||
|
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
* [Users documentation](docs/user_docs.md) page
|
* [Users documentation](docs/user_docs.md) page
|
||||||
* [Administrators documentation](docs/admins_docs.md) page
|
* [Administrators documentation](docs/admins_docs.md) page
|
||||||
* [Developers documentation](docs/developers_docs.md) page
|
* [Developers documentation](docs/developers_docs.md) page
|
||||||
|
* [Configuration](docs/admins_docs.md#5-configuration) page
|
||||||
|
* [Transcoding](docs/transcoding.md) page
|
||||||
|
* [Developer Experience](docs/dev_exp.md) page
|
||||||
|
|
||||||
|
|
||||||
## Technology
|
## Technology
|
||||||
|
@ -186,7 +186,7 @@ CHUNKIZE_VIDEO_DURATION = 60 * 5
|
|||||||
VIDEO_CHUNKS_DURATION = 60 * 4
|
VIDEO_CHUNKS_DURATION = 60 * 4
|
||||||
|
|
||||||
# always get these two, even if upscaling
|
# always get these two, even if upscaling
|
||||||
MINIMUM_RESOLUTIONS_TO_ENCODE = [240, 360]
|
MINIMUM_RESOLUTIONS_TO_ENCODE = [144, 240]
|
||||||
|
|
||||||
# default settings for notifications
|
# default settings for notifications
|
||||||
# not all of them are implemented
|
# not all of them are implemented
|
||||||
@ -497,6 +497,10 @@ USE_ROUNDED_CORNERS = True
|
|||||||
ALLOW_VIDEO_TRIMMER = True
|
ALLOW_VIDEO_TRIMMER = True
|
||||||
|
|
||||||
ALLOW_CUSTOM_MEDIA_URLS = False
|
ALLOW_CUSTOM_MEDIA_URLS = False
|
||||||
|
|
||||||
|
# ffmpeg options
|
||||||
|
FFMPEG_DEFAULT_PRESET = "medium" # see https://trac.ffmpeg.org/wiki/Encode/H.264
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# keep a local_settings.py file for local overrides
|
# keep a local_settings.py file for local overrides
|
||||||
from .local_settings import * # noqa
|
from .local_settings import * # noqa
|
||||||
|
@ -72,7 +72,7 @@ services:
|
|||||||
POSTGRES_DB: mediacms
|
POSTGRES_DB: mediacms
|
||||||
TZ: Europe/London
|
TZ: Europe/London
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}", "--host=db", "--dbname=$POSTGRES_DB", "--username=$POSTGRES_USER"]
|
test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"]
|
||||||
interval: 10s
|
interval: 10s
|
||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 5
|
retries: 5
|
||||||
@ -81,6 +81,6 @@ services:
|
|||||||
restart: always
|
restart: always
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "redis-cli","ping"]
|
test: ["CMD", "redis-cli","ping"]
|
||||||
interval: 30s
|
interval: 10s
|
||||||
timeout: 10s
|
timeout: 5s
|
||||||
retries: 3
|
retries: 3
|
||||||
|
50
docs/transcoding.md
Normal file
50
docs/transcoding.md
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
# Transcoding in MediaCMS
|
||||||
|
|
||||||
|
MediaCMS uses FFmpeg for transcoding media files. Most of the transcoding settings and configurations are defined in `files/helpers.py`.
|
||||||
|
|
||||||
|
## Configuration Options
|
||||||
|
|
||||||
|
Several transcoding parameters can be customized in `cms/settings.py`:
|
||||||
|
|
||||||
|
### FFmpeg Preset
|
||||||
|
|
||||||
|
The default FFmpeg preset is set to "medium". This setting controls the encoding speed and compression efficiency trade-off.
|
||||||
|
|
||||||
|
```python
|
||||||
|
# ffmpeg options
|
||||||
|
FFMPEG_DEFAULT_PRESET = "medium" # see https://trac.ffmpeg.org/wiki/Encode/H.264
|
||||||
|
```
|
||||||
|
|
||||||
|
Available presets include:
|
||||||
|
- ultrafast
|
||||||
|
- superfast
|
||||||
|
- veryfast
|
||||||
|
- faster
|
||||||
|
- fast
|
||||||
|
- medium (default)
|
||||||
|
- slow
|
||||||
|
- slower
|
||||||
|
- veryslow
|
||||||
|
|
||||||
|
Faster presets result in larger file sizes for the same quality, while slower presets provide better compression but take longer to encode.
|
||||||
|
|
||||||
|
### Other Transcoding Settings
|
||||||
|
|
||||||
|
Additional transcoding settings in `settings.py` include:
|
||||||
|
|
||||||
|
- `FFMPEG_COMMAND`: Path to the FFmpeg executable
|
||||||
|
- `FFPROBE_COMMAND`: Path to the FFprobe executable
|
||||||
|
- `DO_NOT_TRANSCODE_VIDEO`: If set to True, only the original video is shown without transcoding
|
||||||
|
- `CHUNKIZE_VIDEO_DURATION`: For videos longer than this duration (in seconds), they get split into chunks and encoded independently
|
||||||
|
- `VIDEO_CHUNKS_DURATION`: Duration of each chunk (must be smaller than CHUNKIZE_VIDEO_DURATION)
|
||||||
|
- `MINIMUM_RESOLUTIONS_TO_ENCODE`: Always encode these resolutions, even if upscaling is required
|
||||||
|
|
||||||
|
## Advanced Configuration
|
||||||
|
|
||||||
|
For more advanced transcoding settings, you may need to modify the following in `files/helpers.py`:
|
||||||
|
|
||||||
|
- Video bitrates for different codecs and resolutions
|
||||||
|
- Audio encoders and bitrates
|
||||||
|
- CRF (Constant Rate Factor) values
|
||||||
|
- Keyframe settings
|
||||||
|
- Encoding parameters for different codecs (H.264, H.265, VP9)
|
@ -34,12 +34,6 @@ BUF_SIZE_MULTIPLIER = 1.5
|
|||||||
KEYFRAME_DISTANCE = 4
|
KEYFRAME_DISTANCE = 4
|
||||||
KEYFRAME_DISTANCE_MIN = 2
|
KEYFRAME_DISTANCE_MIN = 2
|
||||||
|
|
||||||
# speed presets
|
|
||||||
# see https://trac.ffmpeg.org/wiki/Encode/H.264
|
|
||||||
X26x_PRESET = "medium" # "medium"
|
|
||||||
X265_PRESET = "medium"
|
|
||||||
X26x_PRESET_BIG_HEIGHT = "faster"
|
|
||||||
|
|
||||||
# VP9_SPEED = 1 # between 0 and 4, lower is slower
|
# VP9_SPEED = 1 # between 0 and 4, lower is slower
|
||||||
VP9_SPEED = 2
|
VP9_SPEED = 2
|
||||||
|
|
||||||
@ -55,6 +49,7 @@ VIDEO_CRFS = {
|
|||||||
VIDEO_BITRATES = {
|
VIDEO_BITRATES = {
|
||||||
"h264": {
|
"h264": {
|
||||||
25: {
|
25: {
|
||||||
|
144: 150,
|
||||||
240: 300,
|
240: 300,
|
||||||
360: 500,
|
360: 500,
|
||||||
480: 1000,
|
480: 1000,
|
||||||
@ -67,6 +62,7 @@ VIDEO_BITRATES = {
|
|||||||
},
|
},
|
||||||
"h265": {
|
"h265": {
|
||||||
25: {
|
25: {
|
||||||
|
144: 75,
|
||||||
240: 150,
|
240: 150,
|
||||||
360: 275,
|
360: 275,
|
||||||
480: 500,
|
480: 500,
|
||||||
@ -79,6 +75,7 @@ VIDEO_BITRATES = {
|
|||||||
},
|
},
|
||||||
"vp9": {
|
"vp9": {
|
||||||
25: {
|
25: {
|
||||||
|
144: 75,
|
||||||
240: 150,
|
240: 150,
|
||||||
360: 275,
|
360: 275,
|
||||||
480: 500,
|
480: 500,
|
||||||
@ -596,17 +593,13 @@ def get_base_ffmpeg_command(
|
|||||||
cmd = base_cmd[:]
|
cmd = base_cmd[:]
|
||||||
|
|
||||||
# preset settings
|
# preset settings
|
||||||
|
preset = getattr(settings, "FFMPEG_DEFAULT_PRESET", "medium")
|
||||||
|
|
||||||
if encoder == "libvpx-vp9":
|
if encoder == "libvpx-vp9":
|
||||||
if pass_number == 1:
|
if pass_number == 1:
|
||||||
speed = 4
|
speed = 4
|
||||||
else:
|
else:
|
||||||
speed = VP9_SPEED
|
speed = VP9_SPEED
|
||||||
elif encoder in ["libx264"]:
|
|
||||||
preset = X26x_PRESET
|
|
||||||
elif encoder in ["libx265"]:
|
|
||||||
preset = X265_PRESET
|
|
||||||
if target_height >= 720:
|
|
||||||
preset = X26x_PRESET_BIG_HEIGHT
|
|
||||||
|
|
||||||
if encoder == "libx264":
|
if encoder == "libx264":
|
||||||
level = "4.2" if target_height <= 1080 else "5.2"
|
level = "4.2" if target_height <= 1080 else "5.2"
|
||||||
@ -730,7 +723,7 @@ def produce_ffmpeg_commands(media_file, media_info, resolution, codec, output_fi
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
if media_info.get("video_height") < resolution:
|
if media_info.get("video_height") < resolution:
|
||||||
if resolution not in [240, 360]: # always get these two
|
if resolution not in settings.MINIMUM_RESOLUTIONS_TO_ENCODE:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# if codec == "h264_baseline":
|
# if codec == "h264_baseline":
|
||||||
|
17
files/migrations/0010_alter_encodeprofile_resolution.py
Normal file
17
files/migrations/0010_alter_encodeprofile_resolution.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# Generated by Django 5.1.6 on 2025-07-05 11:49
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
('files', '0009_alter_media_friendly_token'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='encodeprofile',
|
||||||
|
name='resolution',
|
||||||
|
field=models.IntegerField(blank=True, choices=[(2160, '2160'), (1440, '1440'), (1080, '1080'), (720, '720'), (480, '480'), (360, '360'), (240, '240'), (144, '144')], null=True),
|
||||||
|
),
|
||||||
|
]
|
@ -73,6 +73,7 @@ ENCODE_RESOLUTIONS = (
|
|||||||
(480, "480"),
|
(480, "480"),
|
||||||
(360, "360"),
|
(360, "360"),
|
||||||
(240, "240"),
|
(240, "240"),
|
||||||
|
(144, "144"),
|
||||||
)
|
)
|
||||||
|
|
||||||
CODECS = (
|
CODECS = (
|
||||||
@ -896,7 +897,7 @@ class Media(models.Model):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
res = {}
|
res = {}
|
||||||
valid_resolutions = [240, 360, 480, 720, 1080, 1440, 2160]
|
valid_resolutions = [144, 240, 360, 480, 720, 1080, 1440, 2160]
|
||||||
if self.hls_file:
|
if self.hls_file:
|
||||||
if os.path.exists(self.hls_file):
|
if os.path.exists(self.hls_file):
|
||||||
hls_file = self.hls_file
|
hls_file = self.hls_file
|
||||||
|
@ -1 +1 @@
|
|||||||
[{"model": "files.encodeprofile", "pk": 19, "fields": {"name": "h264-2160", "extension": "mp4", "resolution": 2160, "codec": "h264", "description": "", "active": false}}, {"model": "files.encodeprofile", "pk": 22, "fields": {"name": "vp9-2160", "extension": "webm", "resolution": 2160, "codec": "vp9", "description": "", "active": false}}, {"model": "files.encodeprofile", "pk": 16, "fields": {"name": "h265-2160", "extension": "mp4", "resolution": 2160, "codec": "h265", "description": "", "active": false}}, {"model": "files.encodeprofile", "pk": 4, "fields": {"name": "h264-1440", "extension": "mp4", "resolution": 1440, "codec": "h264", "description": "", "active": false}}, {"model": "files.encodeprofile", "pk": 5, "fields": {"name": "vp9-1440", "extension": "webm", "resolution": 1440, "codec": "vp9", "description": "", "active": false}}, {"model": "files.encodeprofile", "pk": 6, "fields": {"name": "h265-1440", "extension": "mp4", "resolution": 1440, "codec": "h265", "description": "", "active": false}}, {"model": "files.encodeprofile", "pk": 7, "fields": {"name": "h264-1080", "extension": "mp4", "resolution": 1080, "codec": "h264", "description": "", "active": true}}, {"model": "files.encodeprofile", "pk": 8, "fields": {"name": "vp9-1080", "extension": "webm", "resolution": 1080, "codec": "vp9", "description": "", "active": false}}, {"model": "files.encodeprofile", "pk": 9, "fields": {"name": "h265-1080", "extension": "mp4", "resolution": 1080, "codec": "h265", "description": "", "active": false}}, {"model": "files.encodeprofile", "pk": 10, "fields": {"name": "h264-720", "extension": "mp4", "resolution": 720, "codec": "h264", "description": "", "active": true}}, {"model": "files.encodeprofile", "pk": 11, "fields": {"name": "vp9-720", "extension": "webm", "resolution": 720, "codec": "vp9", "description": "", "active": false}}, {"model": "files.encodeprofile", "pk": 12, "fields": {"name": "h265-720", "extension": "mp4", "resolution": 720, "codec": "h265", "description": "", "active": false}}, {"model": "files.encodeprofile", "pk": 13, "fields": {"name": "h264-480", "extension": "mp4", "resolution": 480, "codec": "h264", "description": "", "active": true}}, {"model": "files.encodeprofile", "pk": 14, "fields": {"name": "vp9-480", "extension": "webm", "resolution": 480, "codec": "vp9", "description": "", "active": false}}, {"model": "files.encodeprofile", "pk": 15, "fields": {"name": "h265-480", "extension": "mp4", "resolution": 480, "codec": "h265", "description": "", "active": false}}, {"model": "files.encodeprofile", "pk": 3, "fields": {"name": "h264-360", "extension": "mp4", "resolution": 360, "codec": "h264", "description": "", "active": true}}, {"model": "files.encodeprofile", "pk": 17, "fields": {"name": "vp9-360", "extension": "webm", "resolution": 360, "codec": "vp9", "description": "", "active": false}}, {"model": "files.encodeprofile", "pk": 18, "fields": {"name": "h265-360", "extension": "mp4", "resolution": 360, "codec": "h265", "description": "", "active": false}}, {"model": "files.encodeprofile", "pk": 2, "fields": {"name": "h264-240", "extension": "mp4", "resolution": 240, "codec": "h264", "description": "", "active": true}}, {"model": "files.encodeprofile", "pk": 20, "fields": {"name": "vp9-240", "extension": "webm", "resolution": 240, "codec": "vp9", "description": "", "active": false}}, {"model": "files.encodeprofile", "pk": 21, "fields": {"name": "h265-240", "extension": "mp4", "resolution": 240, "codec": "h265", "description": "", "active": false}}, {"model": "files.encodeprofile", "pk": 1, "fields": {"name": "preview", "extension": "gif", "resolution": null, "codec": null, "description": "", "active": true}}]
|
[{"model": "files.encodeprofile", "pk": 19, "fields": {"name": "h264-2160", "extension": "mp4", "resolution": 2160, "codec": "h264", "description": "", "active": false}}, {"model": "files.encodeprofile", "pk": 22, "fields": {"name": "vp9-2160", "extension": "webm", "resolution": 2160, "codec": "vp9", "description": "", "active": false}}, {"model": "files.encodeprofile", "pk": 23, "fields": {"name": "h264-144", "extension": "mp4", "resolution": 144, "codec": "h264", "description": "", "active": true}}, {"model": "files.encodeprofile", "pk": 16, "fields": {"name": "h265-2160", "extension": "mp4", "resolution": 2160, "codec": "h265", "description": "", "active": false}}, {"model": "files.encodeprofile", "pk": 4, "fields": {"name": "h264-1440", "extension": "mp4", "resolution": 1440, "codec": "h264", "description": "", "active": false}}, {"model": "files.encodeprofile", "pk": 5, "fields": {"name": "vp9-1440", "extension": "webm", "resolution": 1440, "codec": "vp9", "description": "", "active": false}}, {"model": "files.encodeprofile", "pk": 6, "fields": {"name": "h265-1440", "extension": "mp4", "resolution": 1440, "codec": "h265", "description": "", "active": false}}, {"model": "files.encodeprofile", "pk": 7, "fields": {"name": "h264-1080", "extension": "mp4", "resolution": 1080, "codec": "h264", "description": "", "active": true}}, {"model": "files.encodeprofile", "pk": 8, "fields": {"name": "vp9-1080", "extension": "webm", "resolution": 1080, "codec": "vp9", "description": "", "active": false}}, {"model": "files.encodeprofile", "pk": 9, "fields": {"name": "h265-1080", "extension": "mp4", "resolution": 1080, "codec": "h265", "description": "", "active": false}}, {"model": "files.encodeprofile", "pk": 10, "fields": {"name": "h264-720", "extension": "mp4", "resolution": 720, "codec": "h264", "description": "", "active": true}}, {"model": "files.encodeprofile", "pk": 11, "fields": {"name": "vp9-720", "extension": "webm", "resolution": 720, "codec": "vp9", "description": "", "active": false}}, {"model": "files.encodeprofile", "pk": 12, "fields": {"name": "h265-720", "extension": "mp4", "resolution": 720, "codec": "h265", "description": "", "active": false}}, {"model": "files.encodeprofile", "pk": 13, "fields": {"name": "h264-480", "extension": "mp4", "resolution": 480, "codec": "h264", "description": "", "active": true}}, {"model": "files.encodeprofile", "pk": 14, "fields": {"name": "vp9-480", "extension": "webm", "resolution": 480, "codec": "vp9", "description": "", "active": false}}, {"model": "files.encodeprofile", "pk": 15, "fields": {"name": "h265-480", "extension": "mp4", "resolution": 480, "codec": "h265", "description": "", "active": false}}, {"model": "files.encodeprofile", "pk": 3, "fields": {"name": "h264-360", "extension": "mp4", "resolution": 360, "codec": "h264", "description": "", "active": true}}, {"model": "files.encodeprofile", "pk": 17, "fields": {"name": "vp9-360", "extension": "webm", "resolution": 360, "codec": "vp9", "description": "", "active": false}}, {"model": "files.encodeprofile", "pk": 18, "fields": {"name": "h265-360", "extension": "mp4", "resolution": 360, "codec": "h265", "description": "", "active": false}}, {"model": "files.encodeprofile", "pk": 2, "fields": {"name": "h264-240", "extension": "mp4", "resolution": 240, "codec": "h264", "description": "", "active": true}}, {"model": "files.encodeprofile", "pk": 20, "fields": {"name": "vp9-240", "extension": "webm", "resolution": 240, "codec": "vp9", "description": "", "active": false}}, {"model": "files.encodeprofile", "pk": 21, "fields": {"name": "h265-240", "extension": "mp4", "resolution": 240, "codec": "h265", "description": "", "active": false}}, {"model": "files.encodeprofile", "pk": 1, "fields": {"name": "preview", "extension": "gif", "resolution": null, "codec": null, "description": "", "active": true}}]
|
||||||
|
@ -43,8 +43,8 @@ class TestX(TestCase):
|
|||||||
self.assertEqual(Media.objects.filter(media_type='image').count(), 1, "Media identification failed")
|
self.assertEqual(Media.objects.filter(media_type='image').count(), 1, "Media identification failed")
|
||||||
self.assertEqual(Media.objects.filter(user=self.user).count(), 3, "User assignment failed")
|
self.assertEqual(Media.objects.filter(user=self.user).count(), 3, "User assignment failed")
|
||||||
medium_video = Media.objects.get(title="medium_video.mp4")
|
medium_video = Media.objects.get(title="medium_video.mp4")
|
||||||
self.assertEqual(len(medium_video.hls_info), 11, "Problem with HLS info")
|
self.assertEqual(len(medium_video.hls_info), 13, "Problem with HLS info")
|
||||||
|
|
||||||
# using the provided EncodeProfiles, these two files should produce 9 Encoding objects.
|
# using the provided EncodeProfiles, these two files should produce 9 Encoding objects.
|
||||||
# if new EncodeProfiles are added and enabled, this will break!
|
# if new EncodeProfiles are added and enabled, this will break!
|
||||||
self.assertEqual(Encoding.objects.filter(status='success').count(), 9, "Not all video transcodings finished well")
|
self.assertEqual(Encoding.objects.filter(status='success').count(), 10, "Not all video transcodings finished well")
|
||||||
|
@ -24,12 +24,12 @@ class TestFixtures(TestCase):
|
|||||||
profiles = EncodeProfile.objects.all()
|
profiles = EncodeProfile.objects.all()
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
profiles.count(),
|
profiles.count(),
|
||||||
22,
|
23,
|
||||||
"Problem with Encode Profile fixtures",
|
"Problem with Encode Profile fixtures",
|
||||||
)
|
)
|
||||||
profiles = EncodeProfile.objects.filter(active=True)
|
profiles = EncodeProfile.objects.filter(active=True)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
profiles.count(),
|
profiles.count(),
|
||||||
6,
|
7,
|
||||||
"Problem with Encode Profile fixtures, not as active as expected",
|
"Problem with Encode Profile fixtures, not as active as expected",
|
||||||
)
|
)
|
||||||
|
Reference in New Issue
Block a user