diff --git a/tubesync/sync/migrations/0003_source_copy_thumbnails.py b/tubesync/sync/migrations/0003_source_copy_thumbnails.py new file mode 100644 index 0000000..7bdbfdb --- /dev/null +++ b/tubesync/sync/migrations/0003_source_copy_thumbnails.py @@ -0,0 +1,18 @@ +# Generated by Django 3.1.4 on 2020-12-18 01:34 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('sync', '0002_auto_20201213_0817'), + ] + + operations = [ + migrations.AddField( + model_name='source', + name='copy_thumbnails', + field=models.BooleanField(default=False, help_text='Copy thumbnails with the media, these may be detected and used by some media servers', verbose_name='copy thumbnails'), + ), + ] diff --git a/tubesync/sync/models.py b/tubesync/sync/models.py index 901dbc6..b7c5c0b 100644 --- a/tubesync/sync/models.py +++ b/tubesync/sync/models.py @@ -234,6 +234,11 @@ class Source(models.Model): default=FALLBACK_NEXT_BEST_HD, help_text=_('What do do when media in your source resolution and codecs is not available') ) + copy_thumbnails = models.BooleanField( + _('copy thumbnails'), + default=False, + help_text=_('Copy thumbnails with the media, these may be detected and used by some media servers') + ) has_failed = models.BooleanField( _('has failed'), default=False, diff --git a/tubesync/sync/signals.py b/tubesync/sync/signals.py index 1c42c6e..129d5e5 100644 --- a/tubesync/sync/signals.py +++ b/tubesync/sync/signals.py @@ -150,8 +150,15 @@ def media_pre_delete(sender, instance, **kwargs): delete_file(instance.thumb.path) # Delete the media file if it exists if instance.media_file: - log.info(f'Deleting media for: {instance} path: {instance.media_file.path}') + filepath = instance.media_file.path + log.info(f'Deleting media for: {instance} path: {filepath}') delete_file(instance.media_file.path) + # Delete thumbnail copy if it exists + barefilepath, fileext = os.path.splitext(filepath) + thumbpath = f'{barefilepath}.jpg' + log.info(f'Copying thumbnail: {instance} path: {thumbpath}') + delete_file(thumbpath) + @receiver(post_delete, sender=Media) def media_post_delete(sender, instance, **kwargs): diff --git a/tubesync/sync/tasks.py b/tubesync/sync/tasks.py index ffa6a7e..96b0266 100644 --- a/tubesync/sync/tasks.py +++ b/tubesync/sync/tasks.py @@ -11,6 +11,7 @@ import uuid from io import BytesIO from hashlib import sha1 from datetime import timedelta +from shutil import copyfile from PIL import Image from django.conf import settings from django.core.files.uploadedfile import SimpleUploadedFile @@ -242,7 +243,7 @@ def download_media_thumbnail(media_id, url): f'{width}x{height}: {url}') i = resize_image_to_height(i, width, height) image_file = BytesIO() - i.save(image_file, 'JPEG', quality=80, optimize=True, progressive=True) + i.save(image_file, 'JPEG', quality=85, optimize=True, progressive=True) image_file.seek(0) media.thumb.save( 'thumb', @@ -272,17 +273,18 @@ def download_media(media_id): log.warn(f'Download task triggeredd media: {media} (UUID: {media.pk}) but it ' f'is now marked to be skipped, not downloading') return - log.info(f'Downloading media: {media} (UUID: {media.pk}) to: "{media.filepath}"') + filepath = media.filepath + log.info(f'Downloading media: {media} (UUID: {media.pk}) to: "{filepath}"') format_str, container = media.download_media() - if os.path.exists(media.filepath): + if os.path.exists(filepath): # Media has been downloaded successfully log.info(f'Successfully downloaded media: {media} (UUID: {media.pk}) to: ' - f'"{media.filepath}"') + f'"{filepath}"') # Link the media file to the object and update info about the download - media.media_file.name = str(media.filepath) + media.media_file.name = str(filepath) media.downloaded = True media.download_date = timezone.now() - media.downloaded_filesize = os.path.getsize(media.filepath) + media.downloaded_filesize = os.path.getsize(filepath) media.downloaded_container = container if '+' in format_str: # Seperate audio and video streams @@ -313,6 +315,13 @@ def download_media(media_id): else: media.downloaded_format = 'audio' media.save() + # If selected, copy the thumbnail over as well + if media.source.copy_thumbnails and media.thumb: + barefilepath, fileext = os.path.splitext(filepath) + thumbpath = f'{barefilepath}.jpg' + log.info(f'Copying media thumbnail from: {media.thumb.path} ' + f'to: {thumbpath}') + copyfile(media.thumb.path, thumbpath) # Schedule a task to update media servers for mediaserver in MediaServer.objects.all(): log.info(f'Scheduling media server updates') diff --git a/tubesync/sync/views.py b/tubesync/sync/views.py index 2e78dc6..9af6d7a 100644 --- a/tubesync/sync/views.py +++ b/tubesync/sync/views.py @@ -254,7 +254,8 @@ class AddSourceView(CreateView): model = Source fields = ('source_type', 'key', 'name', 'directory', 'index_schedule', 'delete_old_media', 'days_to_keep', 'source_resolution', 'source_vcodec', - 'source_acodec', 'prefer_60fps', 'prefer_hdr', 'fallback') + 'source_acodec', 'prefer_60fps', 'prefer_hdr', 'fallback', + 'copy_thumbnails') def __init__(self, *args, **kwargs): self.prepopulated_data = {} @@ -325,7 +326,8 @@ class UpdateSourceView(UpdateView): model = Source fields = ('source_type', 'key', 'name', 'directory', 'index_schedule', 'delete_old_media', 'days_to_keep', 'source_resolution', 'source_vcodec', - 'source_acodec', 'prefer_60fps', 'prefer_hdr', 'fallback') + 'source_acodec', 'prefer_60fps', 'prefer_hdr', 'fallback', + 'copy_thumbnails') def get_success_url(self): url = reverse_lazy('sync:source', kwargs={'pk': self.object.pk})