Skip to content

Commit

Permalink
Merge pull request #598 from tcely/patch-3
Browse files Browse the repository at this point in the history
Remove files in post_delete when Media is deleted
  • Loading branch information
meeb authored Jan 31, 2025
2 parents 157f5aa + 5dc3cc2 commit 9375bba
Showing 1 changed file with 55 additions and 9 deletions.
64 changes: 55 additions & 9 deletions tubesync/sync/signals.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import os
import glob
from pathlib import Path
from django.conf import settings
from django.db.models.signals import pre_save, post_save, pre_delete, post_delete
from django.dispatch import receiver
Expand All @@ -14,7 +15,7 @@
download_media, rescan_media_server, download_source_images,
save_all_media_for_source, rename_all_media_for_source,
get_media_metadata_task)
from .utils import delete_file
from .utils import delete_file, glob_quote
from .filtering import filter_media


Expand Down Expand Up @@ -208,19 +209,63 @@ def media_pre_delete(sender, instance, **kwargs):
if thumbnail_url:
delete_task_by_media('sync.tasks.download_media_thumbnail',
(str(instance.pk), thumbnail_url))
if instance.source.delete_files_on_disk and (instance.media_file or instance.thumb):
# Delete all media files if it contains filename
filepath = instance.media_file.path if instance.media_file else instance.thumb.path
barefilepath, fileext = os.path.splitext(filepath)


@receiver(post_delete, sender=Media)
def media_post_delete(sender, instance, **kwargs):
# Remove thumbnail file for deleted media
if instance.thumb:
instance.thumb.delete(save=False)
# Remove the video file, when configured to do so
if instance.source.delete_files_on_disk and instance.media_file:
video_path = Path(str(instance.media_file.path)).resolve()
instance.media_file.delete(save=False)
# the other files we created have these known suffixes
for suffix in frozenset(('nfo', 'jpg', 'webp', 'info.json',)):
other_path = video_path.with_suffix(f'.{suffix}').resolve()
log.info(f'Deleting file for: {instance} path: {other_path!s}')
delete_file(other_path)
# Jellyfin creates .trickplay directories and posters
for suffix in frozenset(('.trickplay', '-poster.jpg', '-poster.webp',)):
# with_suffix insists on suffix beginning with '.' for no good reason
other_path = Path(str(video_path.with_suffix('')) + suffix).resolve()
if other_path.is_file():
log.info(f'Deleting file for: {instance} path: {other_path!s}')
delete_file(other_path)
elif other_path.is_dir():
# Delete the contents of the directory
paths = list(other_path.rglob('*'))
attempts = len(paths)
while paths and attempts > 0:
attempts -= 1
# delete files first
for p in list(filter(lambda x: x.is_file(), paths)):
log.info(f'Deleting file for: {instance} path: {p!s}')
delete_file(p)
# refresh the list
paths = list(other_path.rglob('*'))
# delete directories
# a directory with a subdirectory will fail
# we loop to try removing each of them
# a/b/c: c then b then a, 3 times around the loop
for p in list(filter(lambda x: x.is_dir(), paths)):
try:
p.rmdir()
log.info(f'Deleted directory for: {instance} path: {p!s}')
except OSError as e:
pass
# Delete the directory itself
try:
other_path.rmdir()
log.info(f'Deleted directory for: {instance} path: {other_path!s}')
except OSError as e:
pass
# Get all files that start with the bare file path
all_related_files = glob.glob(f'{barefilepath}.*')
all_related_files = video_path.parent.glob(f'{glob_quote(video_path.with_suffix("").name)}*')
for file in all_related_files:
log.info(f'Deleting file for: {instance} path: {file}')
delete_file(file)


@receiver(post_delete, sender=Media)
def media_post_delete(sender, instance, **kwargs):
# Schedule a task to update media servers
for mediaserver in MediaServer.objects.all():
log.info(f'Scheduling media server updates')
Expand All @@ -231,3 +276,4 @@ def media_post_delete(sender, instance, **kwargs):
verbose_name=verbose_name.format(mediaserver),
remove_existing_tasks=True
)

0 comments on commit 9375bba

Please sign in to comment.