mirror of
https://github.com/mediacms-io/mediacms.git
synced 2025-11-06 07:28:53 -05:00
m
This commit is contained in:
parent
f39de968c8
commit
1c9aff252e
@ -14,7 +14,6 @@ from django.core.files import File
|
||||
from django.core.mail import EmailMessage
|
||||
from django.db.models import Q
|
||||
from django.utils import timezone
|
||||
from contextlib import contextmanager
|
||||
|
||||
from cms import celery_app
|
||||
|
||||
@ -23,15 +22,6 @@ from .helpers import mask_ip
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@contextmanager
|
||||
def disable_signal(signal, receiver, sender):
|
||||
"""Context manager to temporarily disable a signal"""
|
||||
signal.disconnect(receiver, sender=sender)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
signal.connect(receiver, sender=sender)
|
||||
|
||||
|
||||
def get_user_or_session(request):
|
||||
"""Return a dictionary with user info
|
||||
@ -459,7 +449,7 @@ def copy_video(original_media, copy_encodings=True, title_suffix="(Trimmed)"):
|
||||
add_date=timezone.now()
|
||||
)
|
||||
models.Media.objects.bulk_create([new_media])
|
||||
# avoids calling signals
|
||||
# avoids calling signals since signals will call media_init and we don't want that
|
||||
|
||||
|
||||
if copy_encodings:
|
||||
@ -477,7 +467,7 @@ def copy_video(original_media, copy_encodings=True, title_suffix="(Trimmed)"):
|
||||
logs=f"Copied from encoding {encoding.id}"
|
||||
)
|
||||
models.Encoding.objects.bulk_create([new_encoding])
|
||||
# avoids calling signals
|
||||
# avoids calling signals as this is still not ready
|
||||
|
||||
# Copy categories and tags
|
||||
for category in original_media.category.all():
|
||||
|
||||
@ -526,6 +526,8 @@ class Media(models.Model):
|
||||
with open(self.media_file.path, "rb") as f:
|
||||
myfile = File(f)
|
||||
thumbnail_name = helpers.get_file_name(self.media_file.path) + ".jpg"
|
||||
# avoid saving the whole object, because something might have been changed
|
||||
# on the meanwhile
|
||||
self.thumbnail.save(content=myfile, name=thumbnail_name, save=False)
|
||||
self.poster.save(content=myfile, name=thumbnail_name, save=False)
|
||||
self.save(update_fields=["thumbnail", "poster"])
|
||||
@ -563,6 +565,8 @@ class Media(models.Model):
|
||||
with open(tf, "rb") as f:
|
||||
myfile = File(f)
|
||||
thumbnail_name = helpers.get_file_name(self.media_file.path) + ".jpg"
|
||||
# avoid saving the whole object, because something might have been changed
|
||||
# on the meanwhile
|
||||
self.thumbnail.save(content=myfile, name=thumbnail_name, save=False)
|
||||
self.poster.save(content=myfile, name=thumbnail_name, save=False)
|
||||
self.save(update_fields=["thumbnail", "poster"])
|
||||
|
||||
@ -62,9 +62,11 @@ ERRORS_LIST = [
|
||||
def handle_pending_running_encodings(media):
|
||||
"""Handle pending and running encodings for a media object.
|
||||
|
||||
we are trimming the original file. If there are encodings in success, this means that the encoding has run
|
||||
and has succeeded, so we can keep them (they will be trimmed). However for encodings that are in pending
|
||||
or running phase,
|
||||
we are trimming the original file. If there are encodings in success state, this means that the encoding has run
|
||||
and has succeeded, so we can keep them (they will be trimmed) or if we dont keep them we dont have to delete them
|
||||
here
|
||||
|
||||
However for encodings that are in pending or running phase, just delete them
|
||||
|
||||
Args:
|
||||
media: The media object to handle encodings for
|
||||
@ -86,15 +88,23 @@ def handle_pending_running_encodings(media):
|
||||
|
||||
|
||||
|
||||
def pre_trim_actions(media):
|
||||
# avoid re-running unnecessary encodings (or chunkize_media, which is the first step for them)
|
||||
# if the video is already completed. 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
|
||||
def pre_trim_video_actions(media):
|
||||
# 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)
|
||||
# if the video is already completed
|
||||
# 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
|
||||
|
||||
# if a video is fully processed (all encodings are success), or if a video is new, then things are clear
|
||||
|
||||
# HOWEVER there is a race condition and this is that some encodings are success and some are pending/running
|
||||
# Since we are making speed cutting, we will perform an ffmpeg -c copy on all of them and the result will be
|
||||
# that they will end up differently cut, because ffmpeg checks for I-frames
|
||||
# The result is fine if playing the video but is bad in case of HLS
|
||||
# So we need to delete all encodings inevitably to produce same results, if there are some that are success and some that
|
||||
# are still not finished.
|
||||
|
||||
# also since we are making speed cutting, if a video resolution (say 720 and 360) has been ffmpeg copied by the
|
||||
# original file, it has specificy information as I-frames. Now the original file was trimmed too. So now if we attempt
|
||||
# to trim it for a missing resolution (eg 240), it will pick different I-frames and the result will be different
|
||||
# while playing the video in HLS. Thus we need to re-encode the video for all resolutions to ensure they have the same information
|
||||
profiles = EncodeProfile.objects.filter(active=True, extension='mp4', resolution__lte=media.video_height)
|
||||
media_encodings = EncodeProfile.objects.filter(
|
||||
encoding__in=media.encodings.filter(
|
||||
@ -113,8 +123,8 @@ def pre_trim_actions(media):
|
||||
|
||||
|
||||
if picked:
|
||||
# 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")
|
||||
|
||||
media.encode()
|
||||
|
||||
return True
|
||||
@ -244,6 +254,9 @@ def encode_media(
|
||||
"""Encode a media to given profile, using ffmpeg, storing progress"""
|
||||
|
||||
logger.info(f"encode_media for {friendly_token}/{profile_id}/{encoding_id}/{force}/{chunk}")
|
||||
# TODO: this is new behavior, check whether it performs well. Before that check it would end up saving the Encoding
|
||||
# at some point below. Now it exits the task. Could it be that before it would give it a chance to re-run? Or it was
|
||||
# not being used at all?
|
||||
if not Encoding.objects.filter(id=encoding_id).exists():
|
||||
logger.info(f"Exiting for {friendly_token}/{profile_id}/{encoding_id}/{force} since encoding id not found")
|
||||
return False
|
||||
@ -390,6 +403,9 @@ def encode_media(
|
||||
logger.info("Saved {0}".format(round(percent, 2)))
|
||||
n_times += 1
|
||||
except DatabaseError:
|
||||
# primary reason for this is that the encoding has been deleted, because
|
||||
# the media file was deleted, or also that there was a trim video request
|
||||
# so it would be redundant to let it complete the encoding
|
||||
kill_ffmpeg_process(encoding.temp_file)
|
||||
kill_ffmpeg_process(encoding.chunk_file_path)
|
||||
return False
|
||||
@ -539,7 +555,6 @@ def create_hls(friendly_token):
|
||||
if os.path.exists(pp):
|
||||
if media.hls_file != pp:
|
||||
Media.objects.filter(pk=media.pk).update(hls_file=pp)
|
||||
hlsfile = Media.objects.filter(pk=media.pk).first().hls_file
|
||||
return True
|
||||
|
||||
|
||||
@ -882,6 +897,8 @@ def update_encoding_size(encoding_id):
|
||||
|
||||
@task(name="produce_video_chapters", queue="short_tasks")
|
||||
def produce_video_chapters(chapter_id):
|
||||
# this is not used
|
||||
return False
|
||||
chapter_object = VideoChapterData.objects.filter(id=chapter_id).first()
|
||||
if not chapter_object:
|
||||
return False
|
||||
@ -999,14 +1016,15 @@ def video_trim_task(self, trim_request_id):
|
||||
trim_request.media = new_media
|
||||
trim_request.save(update_fields=["media"])
|
||||
|
||||
# processing timestamps differently on encodings and original file, since they have different I-frames
|
||||
# the cut is made based on the I-frames
|
||||
# processing timestamps differently on encodings and original file, in case we do accuracy trimming (currently not)
|
||||
# these have different I-frames and the cut is made based on the I-frames
|
||||
|
||||
original_trim_result = trim_video_method(target_media.media_file.path, timestamps_original)
|
||||
if not original_trim_result:
|
||||
logger.info(f"Failed to trim original file for media {target_media.friendly_token}")
|
||||
|
||||
deleted_encodings = handle_pending_running_encodings(target_media)
|
||||
# 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)
|
||||
for encoding in encodings:
|
||||
trim_result = trim_video_method(encoding.media_file.path, timestamps_encodings)
|
||||
@ -1014,7 +1032,7 @@ def video_trim_task(self, trim_request_id):
|
||||
logger.info(f"Failed to trim encoding {encoding.id} for media {target_media.friendly_token}")
|
||||
encoding.delete()
|
||||
|
||||
pre_trim_actions(target_media)
|
||||
pre_trim_video_actions(target_media)
|
||||
post_trim_action.delay(target_media.friendly_token)
|
||||
|
||||
else:
|
||||
@ -1034,6 +1052,7 @@ def video_trim_task(self, trim_request_id):
|
||||
|
||||
original_trim_result = trim_video_method(target_media.media_file.path, [timestamp])
|
||||
deleted_encodings = handle_pending_running_encodings(target_media)
|
||||
# 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)
|
||||
for encoding in encodings:
|
||||
trim_result = trim_video_method(encoding.media_file.path, [timestamp])
|
||||
@ -1041,7 +1060,7 @@ def video_trim_task(self, trim_request_id):
|
||||
logger.info(f"Failed to trim encoding {encoding.id} for media {target_media.friendly_token}")
|
||||
encoding.delete()
|
||||
|
||||
pre_trim_actions(target_media)
|
||||
pre_trim_video_actions(target_media)
|
||||
post_trim_action.delay(target_media.friendly_token)
|
||||
|
||||
|
||||
|
||||
@ -246,6 +246,8 @@ def history(request):
|
||||
@csrf_exempt
|
||||
@login_required
|
||||
def video_chapters(request, friendly_token):
|
||||
# this is not ready...
|
||||
return False
|
||||
if not request.method == "POST":
|
||||
return HttpResponseRedirect("/")
|
||||
|
||||
@ -354,7 +356,8 @@ def publish_media(request):
|
||||
@login_required
|
||||
def edit_chapters(request):
|
||||
"""Edit chapters"""
|
||||
|
||||
# not implemented yet
|
||||
return False
|
||||
friendly_token = request.GET.get("m", "").strip()
|
||||
if not friendly_token:
|
||||
return HttpResponseRedirect("/")
|
||||
@ -614,7 +617,8 @@ def view_media(request):
|
||||
context["CAN_EDIT_MEDIA"] = True
|
||||
context["CAN_DELETE_COMMENTS"] = True
|
||||
|
||||
# TODO: explaim
|
||||
# in case media is video and is processing (eg the case a video was just uploaded)
|
||||
# attempt to show it (rather than showing a blank video player)
|
||||
if media.media_type == 'video':
|
||||
video_msg = None
|
||||
if media.encoding_status == "pending":
|
||||
|
||||
@ -7,7 +7,6 @@ A modern browser-based video editing tool built with React and TypeScript that i
|
||||
- ⏱️ Trim video start and end points
|
||||
- ✂️ Split videos into multiple segments
|
||||
- 👁️ Preview individual segments or the full edited video
|
||||
- 🔍 Zoom timeline for precise editing
|
||||
- 🔄 Undo/redo support for all editing operations
|
||||
- 🔊 Audio mute controls
|
||||
- 💾 Save edits directly to MediaCMS
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@ -9,8 +9,7 @@
|
||||
<link href="{% static "video_editor/video-editor.css" %}" rel="stylesheet">
|
||||
<script>
|
||||
window.MEDIA_DATA = {
|
||||
// videoUrl: "{{ media_file_path }}", // "http://temp.web357.com/SampleVideo_1280x720_30mb.mp4",
|
||||
videoUrl: "http://temp.web357.com/SampleVideo_1280x720_30mb.mp4",
|
||||
videoUrl: "",
|
||||
mediaId: "{{ media_id }}",
|
||||
chapters: [
|
||||
{
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user