fix black

This commit is contained in:
Markos Gogoulos 2025-05-26 09:52:44 +03:00
parent 68a209501e
commit 77503b38b9
9 changed files with 48 additions and 110 deletions

View File

@ -548,4 +548,3 @@ if GLOBAL_LOGIN_REQUIRED:
r'/accounts/confirm-email/.*/$', r'/accounts/confirm-email/.*/$',
# r'/api/v[0-9]+/', # r'/api/v[0-9]+/',
] ]

View File

@ -13,9 +13,9 @@ from .models import (
Encoding, Encoding,
Language, Language,
Media, Media,
VideoTrimRequest,
Subtitle, Subtitle,
Tag, Tag,
VideoTrimRequest,
) )
@ -199,6 +199,7 @@ class LanguageAdmin(admin.ModelAdmin):
class SubtitleAdmin(admin.ModelAdmin): class SubtitleAdmin(admin.ModelAdmin):
pass pass
class VideoTrimRequestAdmin(admin.ModelAdmin): class VideoTrimRequestAdmin(admin.ModelAdmin):
pass pass

View File

@ -5,7 +5,7 @@ from django import forms
from django.conf import settings from django.conf import settings
from .methods import get_next_state, is_mediacms_editor from .methods import get_next_state, is_mediacms_editor
from .models import Category, Media, MEDIA_STATES, Subtitle from .models import MEDIA_STATES, Category, Media, Subtitle
class CustomField(Field): class CustomField(Field):
@ -92,12 +92,7 @@ class MediaMetadataForm(forms.ModelForm):
class MediaPublishForm(forms.ModelForm): class MediaPublishForm(forms.ModelForm):
confirm_state = forms.BooleanField( confirm_state = forms.BooleanField(required=False, initial=False, label="Acknowledge sharing status", help_text="")
required=False,
initial=False,
label="Acknowledge sharing status",
help_text=""
)
class Meta: class Meta:
model = Media model = Media
@ -129,7 +124,6 @@ class MediaPublishForm(forms.ModelForm):
valid_states.append(self.instance.state) valid_states.append(self.instance.state)
self.fields["state"].choices = [(state, dict(MEDIA_STATES).get(state, state)) for state in valid_states] self.fields["state"].choices = [(state, dict(MEDIA_STATES).get(state, state)) for state in valid_states]
if getattr(settings, 'USE_RBAC', False) and 'category' in self.fields: if getattr(settings, 'USE_RBAC', False) and 'category' in self.fields:
if is_mediacms_editor(user): if is_mediacms_editor(user):
pass pass
@ -187,7 +181,6 @@ class MediaPublishForm(forms.ModelForm):
layout_items.insert(state_index + 1, CustomField('confirm_state')) layout_items.insert(state_index + 1, CustomField('confirm_state'))
self.helper.layout = Layout(*layout_items) self.helper.layout = Layout(*layout_items)
if not cleaned_data.get('confirm_state'): if not cleaned_data.get('confirm_state'):
error_message = f"I understand that although media state is {state}, the media is also shared with users that have access to the following categories: {', '.join(rbac_categories)}" error_message = f"I understand that although media state is {state}, the media is also shared with users that have access to the following categories: {', '.join(rbac_categories)}"
self.add_error('confirm_state', error_message) self.add_error('confirm_state', error_message)

View File

@ -822,6 +822,7 @@ def seconds_to_timestamp(seconds):
return f"{hours:02d}:{minutes:02d}:{seconds_int:02d}.{milliseconds:03d}" return f"{hours:02d}:{minutes:02d}:{seconds_int:02d}.{milliseconds:03d}"
def get_trim_timestamps(media_file_path, timestamps_list, run_ffprobe=False): def get_trim_timestamps(media_file_path, timestamps_list, run_ffprobe=False):
"""Process a list of timestamps to align start times with I-frames for better video trimming """Process a list of timestamps to align start times with I-frames for better video trimming
@ -866,12 +867,17 @@ def get_trim_timestamps(media_file_path, timestamps_list, run_ffprobe=False):
# Create ffprobe command to find nearest I-frame # Create ffprobe command to find nearest I-frame
cmd = [ cmd = [
settings.FFPROBE_COMMAND, settings.FFPROBE_COMMAND,
"-v", "error", "-v",
"-select_streams", "v:0", "error",
"-show_entries", "frame=pts_time,pict_type", "-select_streams",
"-of", "csv=p=0", "v:0",
"-read_intervals", f"{search_start}%{startTime}", "-show_entries",
media_file_path "frame=pts_time,pict_type",
"-of",
"csv=p=0",
"-read_intervals",
f"{search_start}%{startTime}",
media_file_path,
] ]
cmd = [str(s) for s in cmd] cmd = [str(s) for s in cmd]
logger.info(f"trim cmd: {cmd}") logger.info(f"trim cmd: {cmd}")
@ -887,12 +893,9 @@ def get_trim_timestamps(media_file_path, timestamps_list, run_ffprobe=False):
adjusted_startTime = seconds_to_timestamp(float(i_frames[-1])) adjusted_startTime = seconds_to_timestamp(float(i_frames[-1]))
if not i_frames: if not i_frames:
adjusted_startTime = startTime adjusted_startTime = startTime
timestamps_results.append({ timestamps_results.append({'startTime': adjusted_startTime, 'endTime': endTime})
'startTime': adjusted_startTime,
'endTime': endTime
})
return timestamps_results return timestamps_results
@ -926,18 +929,9 @@ def trim_video_method(media_file_path, timestamps_list):
# For multiple timestamps, we need to create segment files # For multiple timestamps, we need to create segment files
segment_file = output_file if len(timestamps_list) == 1 else os.path.join(temp_dir, f"segment_{i}.mp4") segment_file = output_file if len(timestamps_list) == 1 else os.path.join(temp_dir, f"segment_{i}.mp4")
cmd = [ cmd = [settings.FFMPEG_COMMAND, "-y", "-ss", str(item['startTime']), "-i", media_file_path, "-t", str(duration), "-c", "copy", "-avoid_negative_ts", "1", segment_file]
settings.FFMPEG_COMMAND,
"-y",
"-ss", str(item['startTime']),
"-i", media_file_path,
"-t", str(duration),
"-c", "copy",
"-avoid_negative_ts", "1",
segment_file
]
result = run_command(cmd) result = run_command(cmd) # noqa
if os.path.exists(segment_file) and os.path.getsize(segment_file) > 0: if os.path.exists(segment_file) and os.path.getsize(segment_file) > 0:
if len(timestamps_list) > 1: if len(timestamps_list) > 1:
@ -953,17 +947,9 @@ def trim_video_method(media_file_path, timestamps_list):
with open(concat_list_path, "w") as f: with open(concat_list_path, "w") as f:
for segment in segment_files: for segment in segment_files:
f.write(f"file '{segment}'\n") f.write(f"file '{segment}'\n")
concat_cmd = [ concat_cmd = [settings.FFMPEG_COMMAND, "-y", "-f", "concat", "-safe", "0", "-i", concat_list_path, "-c", "copy", output_file]
settings.FFMPEG_COMMAND,
"-y",
"-f", "concat",
"-safe", "0",
"-i", concat_list_path,
"-c", "copy",
output_file
]
concat_result = run_command(concat_cmd) concat_result = run_command(concat_cmd) # noqa
if not os.path.exists(output_file) or os.path.getsize(output_file) == 0: if not os.path.exists(output_file) or os.path.getsize(output_file) == 0:
return False return False

View File

@ -5,8 +5,8 @@ import itertools
import logging import logging
import random import random
import re import re
from datetime import datetime
import subprocess import subprocess
from datetime import datetime
from django.conf import settings from django.conf import settings
from django.core.cache import cache from django.core.cache import cache
@ -448,25 +448,18 @@ def copy_video(original_media, copy_encodings=True, title_suffix="(Trimmed)"):
listable=original_media.listable, listable=original_media.listable,
add_date=timezone.now(), add_date=timezone.now(),
video_height=original_media.video_height, video_height=original_media.video_height,
media_info=original_media.media_info media_info=original_media.media_info,
) )
models.Media.objects.bulk_create([new_media]) models.Media.objects.bulk_create([new_media])
# avoids calling signals since signals will call media_init and we don't want that # avoids calling signals since signals will call media_init and we don't want that
if copy_encodings: if copy_encodings:
for encoding in original_media.encodings.filter(chunk=False, status="success"): for encoding in original_media.encodings.filter(chunk=False, status="success"):
if encoding.media_file: if encoding.media_file:
with open(encoding.media_file.path, "rb") as f: with open(encoding.media_file.path, "rb") as f:
myfile = File(f) myfile = File(f)
new_encoding = models.Encoding( new_encoding = models.Encoding(
media_file=myfile, media_file=myfile, media=new_media, profile=encoding.profile, status="success", progress=100, chunk=False, logs=f"Copied from encoding {encoding.id}"
media=new_media,
profile=encoding.profile,
status="success",
progress=100,
chunk=False,
logs=f"Copied from encoding {encoding.id}"
) )
models.Encoding.objects.bulk_create([new_encoding]) models.Encoding.objects.bulk_create([new_encoding])
# avoids calling signals as this is still not ready # avoids calling signals as this is still not ready
@ -483,13 +476,11 @@ def copy_video(original_media, copy_encodings=True, title_suffix="(Trimmed)"):
thumbnail_name = helpers.get_file_name(original_media.thumbnail.path) thumbnail_name = helpers.get_file_name(original_media.thumbnail.path)
new_media.thumbnail.save(thumbnail_name, File(f)) new_media.thumbnail.save(thumbnail_name, File(f))
if original_media.poster: if original_media.poster:
with open(original_media.poster.path, 'rb') as f: with open(original_media.poster.path, 'rb') as f:
poster_name = helpers.get_file_name(original_media.poster.path) poster_name = helpers.get_file_name(original_media.poster.path)
new_media.poster.save(poster_name, File(f)) new_media.poster.save(poster_name, File(f))
return new_media return new_media
@ -510,13 +501,7 @@ def create_video_trim_request(media, data):
elif data.get('saveAsCopy'): elif data.get('saveAsCopy'):
video_action = "save_new" video_action = "save_new"
video_trim_request = models.VideoTrimRequest.objects.create( video_trim_request = models.VideoTrimRequest.objects.create(media=media, status="initial", video_action=video_action, media_trim_style='no_encoding', timestamps=data.get('segments', {}))
media=media,
status="initial",
video_action=video_action,
media_trim_style='no_encoding',
timestamps=data.get('segments', {})
)
return video_trim_request return video_trim_request

View File

@ -5,7 +5,6 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('files', '0007_alter_media_state_videochapterdata'), ('files', '0007_alter_media_state_videochapterdata'),
] ]

View File

@ -84,7 +84,6 @@ ENCODE_EXTENSIONS_KEYS = [extension for extension, name in ENCODE_EXTENSIONS]
ENCODE_RESOLUTIONS_KEYS = [resolution for resolution, name in ENCODE_RESOLUTIONS] ENCODE_RESOLUTIONS_KEYS = [resolution for resolution, name in ENCODE_RESOLUTIONS]
def generate_uid(): def generate_uid():
return get_random_string(length=16) return get_random_string(length=16)
@ -651,6 +650,7 @@ class Media(models.Model):
if encoding and encoding.status == "success" and encoding.profile.codec == "h264" and action == "add" and not encoding.chunk: if encoding and encoding.status == "success" and encoding.profile.codec == "h264" and action == "add" and not encoding.chunk:
from . import tasks from . import tasks
tasks.create_hls.delay(self.friendly_token) tasks.create_hls.delay(self.friendly_token)
# TODO: ideally would ensure this is run only at the end when the last encoding is done... # TODO: ideally would ensure this is run only at the end when the last encoding is done...
@ -692,7 +692,6 @@ class Media(models.Model):
# showing the original file # showing the original file
return helpers.url_from_path(self.media_file.path) return helpers.url_from_path(self.media_file.path)
@property @property
def trim_video_path(self): def trim_video_path(self):
if self.media_type not in ["video"]: if self.media_type not in ["video"]:

View File

@ -2,13 +2,11 @@ import json
import os import os
import re import re
import shutil import shutil
import subprocess
import tempfile import tempfile
from datetime import datetime, timedelta from datetime import datetime, timedelta
from celery import Task from celery import Task
from celery import shared_task as task from celery import shared_task as task
from celery.exceptions import SoftTimeLimitExceeded
from celery.signals import task_revoked from celery.signals import task_revoked
# from celery.task.control import revoke # from celery.task.control import revoke
@ -16,8 +14,9 @@ from celery.utils.log import get_task_logger
from django.conf import settings from django.conf import settings
from django.core.cache import cache from django.core.cache import cache
from django.core.files import File from django.core.files import File
from django.db.models import Q
from django.db import DatabaseError from django.db import DatabaseError
from django.db.models import Q
from actions.models import USER_MEDIA_ACTIONS, MediaAction from actions.models import USER_MEDIA_ACTIONS, MediaAction
from users.models import User from users.models import User
@ -36,7 +35,13 @@ from .helpers import (
run_command, run_command,
trim_video_method, trim_video_method,
) )
from .methods import list_tasks, notify_users, pre_save_action, copy_video, kill_ffmpeg_process from .methods import (
copy_video,
kill_ffmpeg_process,
list_tasks,
notify_users,
pre_save_action,
)
from .models import ( from .models import (
Category, Category,
EncodeProfile, EncodeProfile,
@ -45,7 +50,7 @@ from .models import (
Rating, Rating,
Tag, Tag,
VideoChapterData, VideoChapterData,
VideoTrimRequest VideoTrimRequest,
) )
logger = get_task_logger(__name__) logger = get_task_logger(__name__)
@ -87,12 +92,11 @@ def handle_pending_running_encodings(media):
return deleted return deleted
def pre_trim_video_actions(media): def pre_trim_video_actions(media):
# the reason for this function is to perform tasks before trimming a video # the reason for this function is to perform tasks before trimming a video
# avoid re-running unnecessary encodings (or chunkize_media, which is the first step for them) # avoid re-running unnecessary encodings (or chunkize_media, which is the first step for them)
# if the video is already completed # if the video is already completed
# however if it is a new video (user uploded the video and starts trimming # however if it is a new video (user uploded the video and starts trimming
# before the video is processed), this is necessary, so encode has to be called to give it a chance to encode # before the video is processed), this is necessary, so encode has to be called to give it a chance to encode
@ -106,13 +110,7 @@ def pre_trim_video_actions(media):
# are still not finished. # are still not finished.
profiles = EncodeProfile.objects.filter(active=True, extension='mp4', resolution__lte=media.video_height) profiles = EncodeProfile.objects.filter(active=True, extension='mp4', resolution__lte=media.video_height)
media_encodings = EncodeProfile.objects.filter( media_encodings = EncodeProfile.objects.filter(encoding__in=media.encodings.filter(status="success", chunk=False), extension='mp4').distinct()
encoding__in=media.encodings.filter(
status="success",
chunk=False
),
extension='mp4'
).distinct()
picked = [] picked = []
for profile in profiles: for profile in profiles:
@ -121,7 +119,6 @@ def pre_trim_video_actions(media):
else: else:
picked.append(profile) picked.append(profile)
if picked: if picked:
# by calling encode will re-encode all. The logic is explained above... # by calling encode will re-encode all. The logic is explained above...
logger.info(f"Encoding media {media.friendly_token} will have to be performed for all profiles") logger.info(f"Encoding media {media.friendly_token} will have to be performed for all profiles")
@ -129,6 +126,7 @@ def pre_trim_video_actions(media):
return True return True
@task(name="chunkize_media", bind=True, queue="short_tasks", soft_time_limit=60 * 30 * 4) @task(name="chunkize_media", bind=True, queue="short_tasks", soft_time_limit=60 * 30 * 4)
def chunkize_media(self, friendly_token, profiles, force=True): def chunkize_media(self, friendly_token, profiles, force=True):
"""Break media in chunks and start encoding tasks""" """Break media in chunks and start encoding tasks"""
@ -417,7 +415,6 @@ def encode_media(
raise raise
except Exception as e: except Exception as e:
try: try:
# output is empty, fail message is on the exception # output is empty, fail message is on the exception
output = e.message output = e.message
@ -496,11 +493,7 @@ def produce_sprite_from_video(friendly_token):
with open(output_name, "rb") as f: with open(output_name, "rb") as f:
myfile = File(f) myfile = File(f)
# SOS: avoid race condition, since this runs for a long time and will replace any other media changes on the meanwhile!!! # SOS: avoid race condition, since this runs for a long time and will replace any other media changes on the meanwhile!!!
media.sprites.save( media.sprites.save(content=myfile, name=get_file_name(media.media_file.path) + "sprites.jpg", save=False)
content=myfile,
name=get_file_name(media.media_file.path) + "sprites.jpg",
save=False
)
media.save(update_fields=["sprites"]) media.save(update_fields=["sprites"])
except Exception as e: except Exception as e:
@ -877,8 +870,6 @@ def task_sent_handler(sender=None, headers=None, body=None, **kwargs):
return True return True
@task(name="remove_media_file", base=Task, queue="long_tasks") @task(name="remove_media_file", base=Task, queue="long_tasks")
def remove_media_file(media_file=None): def remove_media_file(media_file=None):
rm_file(media_file) rm_file(media_file)
@ -1006,9 +997,7 @@ def video_trim_task(self, trim_request_id):
else: else:
proceed_with_single_file = False proceed_with_single_file = False
if proceed_with_single_file: if proceed_with_single_file:
if trim_request.video_action == "save_new" or trim_request.video_action == "create_segments" and len(timestamps_encodings) == 1: if trim_request.video_action == "save_new" or trim_request.video_action == "create_segments" and len(timestamps_encodings) == 1:
new_media = copy_video(original_media, copy_encodings=True) new_media = copy_video(original_media, copy_encodings=True)
@ -1042,16 +1031,10 @@ def video_trim_task(self, trim_request_id):
# file on different times. # file on different times.
target_media = copy_video(original_media, title_suffix=f"(Trimmed) {i}", copy_encodings=True) target_media = copy_video(original_media, title_suffix=f"(Trimmed) {i}", copy_encodings=True)
video_trim_request = VideoTrimRequest.objects.create( video_trim_request = VideoTrimRequest.objects.create(media=target_media, status="running", video_action="create_segments", media_trim_style='no_encoding', timestamps=[timestamp]) # noqa
media=target_media,
status="running",
video_action="create_segments",
media_trim_style='no_encoding',
timestamps=[timestamp]
)
original_trim_result = trim_video_method(target_media.media_file.path, [timestamp]) original_trim_result = trim_video_method(target_media.media_file.path, [timestamp])
deleted_encodings = handle_pending_running_encodings(target_media) deleted_encodings = handle_pending_running_encodings(target_media) # noqa
# the following could be un-necessary, read commend in pre_trim_video_actions to see why # the following could be un-necessary, read commend in pre_trim_video_actions to see why
encodings = target_media.encodings.filter(status="success", profile__extension='mp4', chunk=False) encodings = target_media.encodings.filter(status="success", profile__extension='mp4', chunk=False)
for encoding in encodings: for encoding in encodings:
@ -1063,7 +1046,6 @@ def video_trim_task(self, trim_request_id):
pre_trim_video_actions(target_media) pre_trim_video_actions(target_media)
post_trim_action.delay(target_media.friendly_token) post_trim_action.delay(target_media.friendly_token)
# set as completed the initial trim_request # set as completed the initial trim_request
trim_request.status = "success" trim_request.status = "success"
trim_request.save(update_fields=["status"]) trim_request.save(update_fields=["status"])

View File

@ -1,6 +1,6 @@
import json import json
from datetime import datetime, timedelta from datetime import datetime, timedelta
from . import helpers
from allauth.socialaccount.models import SocialApp from allauth.socialaccount.models import SocialApp
from django.conf import settings from django.conf import settings
from django.contrib import messages from django.contrib import messages
@ -38,6 +38,7 @@ from cms.version import VERSION
from identity_providers.models import LoginOption from identity_providers.models import LoginOption
from users.models import User from users.models import User
from . import helpers
from .forms import ( from .forms import (
ContactForm, ContactForm,
EditSubtitleForm, EditSubtitleForm,
@ -393,10 +394,7 @@ def trim_video(request, friendly_token):
if not (request.user == media.user or is_mediacms_editor(request.user)): if not (request.user == media.user or is_mediacms_editor(request.user)):
return HttpResponseRedirect("/") return HttpResponseRedirect("/")
existing_requests = VideoTrimRequest.objects.filter( existing_requests = VideoTrimRequest.objects.filter(media=media, status__in=["initial", "running"]).exists()
media=media,
status__in=["initial", "running"]
).exists()
if existing_requests: if existing_requests:
return JsonResponse({"success": False, "error": "A trim request is already in progress for this video"}, status=400) return JsonResponse({"success": False, "error": "A trim request is already in progress for this video"}, status=400)
@ -407,7 +405,7 @@ def trim_video(request, friendly_token):
video_trim_task.delay(video_trim_request.id) video_trim_task.delay(video_trim_request.id)
ret = {"success": True, "request_id": video_trim_request.id} ret = {"success": True, "request_id": video_trim_request.id}
return JsonResponse(ret, safe=False, status=200) return JsonResponse(ret, safe=False, status=200)
except Exception as e: except Exception as e: # noqa
ret = {"success": False, "error": "Incorrect request data"} ret = {"success": False, "error": "Incorrect request data"}
return JsonResponse(ret, safe=False, status=400) return JsonResponse(ret, safe=False, status=400)
@ -435,12 +433,8 @@ def edit_video(request):
messages.add_message(request, messages.INFO, "Video Trimmer is not enabled") messages.add_message(request, messages.INFO, "Video Trimmer is not enabled")
return HttpResponseRedirect(media.get_absolute_url()) return HttpResponseRedirect(media.get_absolute_url())
# Check if there's a running trim request # Check if there's a running trim request
running_trim_request = VideoTrimRequest.objects.filter( running_trim_request = VideoTrimRequest.objects.filter(media=media, status__in=["initial", "running"]).exists()
media=media,
status__in=["initial", "running"]
).exists()
if running_trim_request: if running_trim_request:
messages.add_message(request, messages.INFO, "Video trim request is already running") messages.add_message(request, messages.INFO, "Video trim request is already running")