Merge pull request #450 from InterN0te/main-delete-files-on-disk
Following Delete files on disk #426
This commit is contained in:
commit
e7788eb8fb
|
@ -0,0 +1,17 @@
|
||||||
|
# Generated by pac
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('sync', '0020_auto_20231024_1825'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='source',
|
||||||
|
name='delete_files_on_disk',
|
||||||
|
field=models.BooleanField(default=False, help_text='Delete files on disk when they are removed from TubeSync', verbose_name='delete files on disk'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -296,6 +296,11 @@ class Source(models.Model):
|
||||||
default=False,
|
default=False,
|
||||||
help_text=_('Delete media that is no longer on this playlist')
|
help_text=_('Delete media that is no longer on this playlist')
|
||||||
)
|
)
|
||||||
|
delete_files_on_disk = models.BooleanField(
|
||||||
|
_('delete files on disk'),
|
||||||
|
default=False,
|
||||||
|
help_text=_('Delete files on disk when they are removed from TubeSync')
|
||||||
|
)
|
||||||
source_resolution = models.CharField(
|
source_resolution = models.CharField(
|
||||||
_('source resolution'),
|
_('source resolution'),
|
||||||
max_length=8,
|
max_length=8,
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import os
|
import os
|
||||||
|
import glob
|
||||||
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
|
||||||
|
@ -74,6 +75,7 @@ def source_pre_delete(sender, instance, **kwargs):
|
||||||
media.delete()
|
media.delete()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_delete, sender=Source)
|
@receiver(post_delete, sender=Source)
|
||||||
def source_post_delete(sender, instance, **kwargs):
|
def source_post_delete(sender, instance, **kwargs):
|
||||||
# Triggered after a source is deleted
|
# Triggered after a source is deleted
|
||||||
|
@ -222,6 +224,16 @@ 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)
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
<p>
|
<p>
|
||||||
Are you sure you want to delete this source? Deleting a source is permanent.
|
Are you sure you want to delete this source? Deleting a source is permanent.
|
||||||
By default, deleting a source does not delete any saved media files. You can
|
By default, deleting a source does not delete any saved media files. You can
|
||||||
tick the "also delete downloaded media" checkbox to also remove save
|
<strong>tick the "also delete downloaded media" checkbox to also remove directory {{ source.directory_path }}
|
||||||
media when you delete the source. Deleting a source cannot be undone.
|
</strong>when you delete the source. Deleting a source cannot be undone.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -122,6 +122,10 @@
|
||||||
<tr title="Delete media that is no longer on this playlist?">
|
<tr title="Delete media that is no longer on this playlist?">
|
||||||
<td class="hide-on-small-only">Delete removed media</td>
|
<td class="hide-on-small-only">Delete removed media</td>
|
||||||
<td><span class="hide-on-med-and-up">Delete removed media<br></span><strong>{% if source.delete_removed_media %}<i class="fas fa-check"></i>{% else %}<i class="fas fa-times"></i>{% endif %}</strong></td>
|
<td><span class="hide-on-med-and-up">Delete removed media<br></span><strong>{% if source.delete_removed_media %}<i class="fas fa-check"></i>{% else %}<i class="fas fa-times"></i>{% endif %}</strong></td>
|
||||||
|
</tr>
|
||||||
|
<tr title="Delete files on disk when they are removed from TubeSync?">
|
||||||
|
<td class="hide-on-small-only">Delete files on disk</td>
|
||||||
|
<td><span class="hide-on-med-and-up">Delete files on disk<br></span><strong>{% if source.delete_files_on_disk %}<i class="fas fa-check"></i>{% else %}<i class="fas fa-times"></i>{% endif %}</strong></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% if source.delete_old_media and source.days_to_keep > 0 %}
|
{% if source.delete_old_media and source.days_to_keep > 0 %}
|
||||||
<tr title="Days after which your media from this source will be locally deleted">
|
<tr title="Days after which your media from this source will be locally deleted">
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
|
import glob
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
from base64 import b64decode
|
from base64 import b64decode
|
||||||
import pathlib
|
import pathlib
|
||||||
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.http import FileResponse, Http404, HttpResponseNotFound, HttpResponseRedirect
|
from django.http import FileResponse, Http404, HttpResponseNotFound, HttpResponseRedirect
|
||||||
|
@ -296,9 +298,9 @@ class EditSourceMixin:
|
||||||
model = Source
|
model = Source
|
||||||
fields = ('source_type', 'key', 'name', 'directory', 'filter_text', 'media_format',
|
fields = ('source_type', 'key', 'name', 'directory', 'filter_text', 'media_format',
|
||||||
'index_schedule', 'download_media', 'download_cap', 'delete_old_media',
|
'index_schedule', 'download_media', 'download_cap', 'delete_old_media',
|
||||||
'delete_removed_media', 'days_to_keep', 'source_resolution', 'source_vcodec',
|
'delete_removed_media', 'delete_files_on_disk', 'days_to_keep', 'source_resolution',
|
||||||
'source_acodec', 'prefer_60fps', 'prefer_hdr', 'fallback', 'copy_thumbnails',
|
'source_vcodec', 'source_acodec', 'prefer_60fps', 'prefer_hdr', 'fallback',
|
||||||
'write_nfo', 'write_json', 'embed_metadata', 'embed_thumbnail',
|
'copy_thumbnails', 'write_nfo', 'write_json', 'embed_metadata', 'embed_thumbnail',
|
||||||
'enable_sponsorblock', 'sponsorblock_categories', 'write_subtitles',
|
'enable_sponsorblock', 'sponsorblock_categories', 'write_subtitles',
|
||||||
'auto_subtitles', 'sub_langs')
|
'auto_subtitles', 'sub_langs')
|
||||||
errors = {
|
errors = {
|
||||||
|
@ -435,14 +437,13 @@ class DeleteSourceView(DeleteView, FormMixin):
|
||||||
source = self.get_object()
|
source = self.get_object()
|
||||||
for media in Media.objects.filter(source=source):
|
for media in Media.objects.filter(source=source):
|
||||||
if media.media_file:
|
if media.media_file:
|
||||||
# Delete the media file
|
file_path = media.media_file.path
|
||||||
delete_file(media.media_file.path)
|
matching_files = glob.glob(os.path.splitext(file_path)[0] + '.*')
|
||||||
# Delete thumbnail copy if it exists
|
for file in matching_files:
|
||||||
delete_file(media.thumbpath)
|
delete_file(file)
|
||||||
# Delete NFO file if it exists
|
directory_path = source.directory_path
|
||||||
delete_file(media.nfopath)
|
if os.path.exists(directory_path):
|
||||||
# Delete JSON file if it exists
|
shutil.rmtree(directory_path, True)
|
||||||
delete_file(media.jsonpath)
|
|
||||||
return super().post(request, *args, **kwargs)
|
return super().post(request, *args, **kwargs)
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
|
@ -653,12 +654,13 @@ class MediaSkipView(FormView, SingleObjectMixin):
|
||||||
delete_task_by_media('sync.tasks.download_media', (str(self.object.pk),))
|
delete_task_by_media('sync.tasks.download_media', (str(self.object.pk),))
|
||||||
# If the media file exists on disk, delete it
|
# If the media file exists on disk, delete it
|
||||||
if self.object.media_file_exists:
|
if self.object.media_file_exists:
|
||||||
delete_file(self.object.media_file.path)
|
# Delete all files which contains filename
|
||||||
self.object.media_file = None
|
filepath = self.object.media_file.path
|
||||||
# If the media has an associated thumbnail copied, also delete it
|
barefilepath, fileext = os.path.splitext(filepath)
|
||||||
delete_file(self.object.thumbpath)
|
# Get all files that start with the bare file path
|
||||||
# If the media has an associated NFO file with it, also delete it
|
all_related_files = glob.glob(f'{barefilepath}.*')
|
||||||
delete_file(self.object.nfopath)
|
for file in all_related_files:
|
||||||
|
delete_file(file)
|
||||||
# Reset all download data
|
# Reset all download data
|
||||||
self.object.metadata = None
|
self.object.metadata = None
|
||||||
self.object.downloaded = False
|
self.object.downloaded = False
|
||||||
|
|
Loading…
Reference in New Issue