Merge pull request #799 from tcely/patch-6

Rename to a temporary path then final destination
This commit is contained in:
meeb 2025-03-04 21:16:45 +11:00 committed by GitHub
commit efb01103f0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 67 additions and 9 deletions

View File

@ -775,6 +775,22 @@ class Media(models.Model):
)
def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
# Correct the path after a source is renamed
if self.created and self.downloaded and not self.media_file_exists:
fp_list = list((self.filepath,))
if self.media_file:
fp_list.append(self.filepath.parent / Path(self.media_file.path).name)
for filepath in fp_list:
if filepath.exists():
self.media_file.name = str(
filepath.relative_to(
self.media_file.storage.location
)
)
self.skip = False
if update_fields is not None:
update_fields = {'media_file', 'skip'}.union(update_fields)
# Trigger an update of derived fields from metadata
if self.metadata:
setattr(self, '_cached_metadata_dict', None)
@ -1537,7 +1553,8 @@ class Media(models.Model):
# update the media_file in the db
self.media_file.name = str(new_video_path.relative_to(self.media_file.storage.location))
self.save()
self.skip = False
self.save(update_fields=('media_file', 'skip'))
log.info(f'Updated "media_file" in the database for: {self!s}')
(new_prefix_path, new_stem) = directory_and_stem(new_video_path)

View File

@ -1,4 +1,5 @@
from pathlib import Path
from tempfile import TemporaryDirectory
from django.conf import settings
from django.db.models.signals import pre_save, post_save, pre_delete, post_delete
from django.dispatch import receiver
@ -27,15 +28,55 @@ def source_pre_save(sender, instance, **kwargs):
except Source.DoesNotExist:
log.debug(f'source_pre_save signal: no existing source: {sender} - {instance}')
return
existing_dirpath = existing_source.directory_path.resolve(strict=True)
new_dirpath = instance.directory_path.resolve(strict=False)
rename_source_directory = (
existing_dirpath != new_dirpath and
not new_dirpath.exists()
)
if rename_source_directory:
if existing_dirpath != new_dirpath:
path_name = lambda p: p.name
relative_dir = existing_source.directory
rd_parents = Path(relative_dir).parents
rd_parents_set = set(map(path_name, rd_parents))
ad_parents = existing_dirpath.parents
ad_parents_set = set(map(path_name, ad_parents))
# the names in the relative path are also in the absolute path
parents_count = len(ad_parents_set.intersection(rd_parents_set))
work_directory = existing_dirpath
for _count in range(parents_count, 0, -1):
work_directory = work_directory.parent
with TemporaryDirectory(suffix=('.'+new_dirpath.name), prefix='.tmp.', dir=work_directory) as tmp_dir:
tmp_dirpath = Path(tmp_dir)
existed = None
previous = existing_dirpath.rename(tmp_dirpath / 'previous')
try:
if new_dirpath.exists():
existed = new_dirpath.rename(tmp_dirpath / 'existed')
mkdir_p(new_dirpath.parent)
existing_dirpath.rename(new_dirpath)
previous.rename(new_dirpath)
except Exception:
# try to preserve the directory, if anything went wrong
previous.rename(existing_dirpath)
raise
else:
existing_dirpath = previous = None
if existed and existed.is_dir():
existed = existed.rename(new_dirpath / '.existed')
for entry_path in existed.iterdir():
try:
target = new_dirpath / entry_path.name
if not target.exists():
entry_path = entry_path.rename(target)
except Exception as e:
log.exception(e)
try:
existed.rmdir()
except Exception as e:
log.exception(e)
elif existed:
try:
existed = existed.rename(new_dirpath / ('.existed-' + new_dirpath.name))
except Exception as e:
log.exception(e)
recreate_index_source_task = (
existing_source.name != instance.name or
existing_source.index_schedule != instance.index_schedule
@ -200,7 +241,7 @@ def media_post_save(sender, instance, created, **kwargs):
)
existing_media_download_task = get_media_download_task(str(instance.pk))
# If the media has not yet been downloaded schedule it to be downloaded
if not (instance.media_file_exists or existing_media_download_task):
if not (instance.media_file_exists or instance.filepath.exists() or existing_media_download_task):
# The file was deleted after it was downloaded, skip this media.
if instance.can_download and instance.downloaded:
skip_changed = True != instance.skip