Remove files in post_delete when Media is deleted

This commit is contained in:
tcely 2024-12-22 09:10:03 -05:00 committed by GitHub
parent 904c57f603
commit 22a46315a1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -1,5 +1,6 @@
import os import os
import glob import glob
from pathlib import Path
from django.conf import settings from django.conf import settings
from django.db.models.signals import pre_save, post_save, pre_delete, post_delete from django.db.models.signals import pre_save, post_save, pre_delete, post_delete
from django.dispatch import receiver from django.dispatch import receiver
@ -13,7 +14,7 @@ from .tasks import (delete_task_by_source, delete_task_by_media, index_source_ta
map_task_to_instance, check_source_directory_exists, map_task_to_instance, check_source_directory_exists,
download_media, rescan_media_server, download_source_images, download_media, rescan_media_server, download_source_images,
save_all_media_for_source) save_all_media_for_source)
from .utils import delete_file from .utils import delete_file, glob_quote
from .filtering import filter_media from .filtering import filter_media
@ -185,19 +186,63 @@ def media_pre_delete(sender, instance, **kwargs):
if thumbnail_url: if thumbnail_url:
delete_task_by_media('sync.tasks.download_media_thumbnail', delete_task_by_media('sync.tasks.download_media_thumbnail',
(str(instance.pk), thumbnail_url)) (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)
# Get all files that start with the bare file path
all_related_files = glob.glob(f'{barefilepath}.*')
for file in all_related_files:
log.info(f'Deleting file for: {instance} path: {file}')
delete_file(file)
@receiver(post_delete, sender=Media) @receiver(post_delete, sender=Media)
def media_post_delete(sender, instance, **kwargs): 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 = 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)
# Schedule a task to update media servers # Schedule a task to update media servers
for mediaserver in MediaServer.objects.all(): for mediaserver in MediaServer.objects.all():
log.info(f'Scheduling media server updates') log.info(f'Scheduling media server updates')
@ -208,3 +253,4 @@ def media_post_delete(sender, instance, **kwargs):
verbose_name=verbose_name.format(mediaserver), verbose_name=verbose_name.format(mediaserver),
remove_existing_tasks=True remove_existing_tasks=True
) )