def _move_files_safe(self, src_web_path, dst_web_path): """ Move file in collection from path1 to path2. Also move associated thumbnail and preview file, and any files with same prefix in collection folder (mainly RAWS) """ src_file_phys_path = locations.collection_phys_path(src_web_path) dst_file_phys_path = locations.collection_phys_path(dst_web_path) # create list of modifications files will be moved in loop at the end of method renames = [(src_file_phys_path, dst_file_phys_path)] # query generators and add all generated miniatures to renames for generator in MINIATURE_GENERATORS: if generator.will_output_file(src_web_path): src_miniature_phys_path = generator.miniature_phys_path(src_file_phys_path) dst_miniature_phys_path = self._calculate_dst_path_after_move(src_web_path, dst_web_path, src_miniature_phys_path) renames.append((src_miniature_phys_path, dst_miniature_phys_path)) # add move of "other files in group" in collection folder self._add_similar_files_modifications(src_file_phys_path, dst_file_phys_path, renames) # move files in batch # TODO: rollback changes (move files back to their original positions) on error for (src, dst) in renames: move_without_overwriting(src, dst, # allow creating destination folders only in trash create_destination_dir=locations.web_path_in_trash(dst_web_path))
def remove_old_trash_files(): month_ago = localized_time(datetime.now() - timedelta(days=30)) # select images from trash that were moved month before or earlier old_images_in_trash = Image.objects.filter( Q(directory__path__startswith="Trash/") | Q(directory__path__exact="Trash") ).filter(trash_time__lte=month_ago) # remove file in collection # thumbnails and database object will be removed by Thumbnailer and Indexer respectively for image in old_images_in_trash: image_phys_path = locations.collection_phys_path(image.path) all_files_with_prefix = set(glob.glob(os.path.splitext(image_phys_path)[0] + ".*")) logging.info("Removing outdated files in trash: " + " ".join(all_files_with_prefix)) for file_name in all_files_with_prefix: os.unlink(file_name) # remove directory in trash if empty trash_dir_phys_path = locations.collection_phys_path(locations.TRASH_DIR_NAME) for (root, dirs, files) in os.walk(trash_dir_phys_path, topdown=False): for directory in dirs: try: dir_path = os.path.join(root, directory) os.rmdir(dir_path) logging.info("Removing empty directory in trash: " + dir_path) except IOError as e: # directory isn't empty - skipping pass
def _try_copying_existing_miniature(cls, original_phys_path, miniature_phys_path, generator): """ When original in collection is moved to new directory, copy existing miniature instead of creating a new one. """ original_mtime = get_mtime_datetime(original_phys_path) original_basename = os.path.basename(original_phys_path) # originals with same basename and modification time are considered as identical # therefore if there exists thumbnail it can be used instead of creating new one same_original_query = File.objects.filter(name=original_basename, modification_time=original_mtime) for same_original in same_original_query.all(): same_original_phys_path = locations.collection_phys_path(same_original.path) same_original_miniature_phys_path = generator.miniature_phys_path(same_original_phys_path) copy_args = (same_original_miniature_phys_path, miniature_phys_path) # underlying thumbnail exists - thumbnails can be copied if os.path.exists(same_original_miniature_phys_path): logger.info( "there exists already original with same name and mtime: copying {} -> {}".format(*copy_args)) shutil.copy(*copy_args) return True # there was no thumbnail yet # this could happen when two exact copies are added and no thumbnail was created for any of them return False
def _move_files(self, src_web_path, dst_web_path): src_phys_path = locations.collection_phys_path(src_web_path) if not os.path.isfile(src_phys_path): raise BadRequestException("Source file doesn't exist: " + src_phys_path) self._move_files_safe(src_web_path, dst_web_path)
def _create_links(file): video_generator = VideoGenerator() original_phys_path = locations.collection_phys_path(file.path) new_original_phys_path = os.path.splitext(original_phys_path)[0] + video_generator.extension() miniature_phys_path = video_generator.miniature_phys_path(original_phys_path) if not os.path.exists(miniature_phys_path): logger.warn("Miniature doesn't exist, skipping: {}".format(miniature_phys_path)) return if os.path.islink(miniature_phys_path): logger.warn("Miniature is already symbolic link, skipping: {}".format(miniature_phys_path)) file.substitute_original = False file.save() return logger.info("Swapping original with miniature: {}".format(original_phys_path)) poster_generator = FirstFrameGenerator() # poster phys path has to be calculated before original is overwritten poster_phys_path = poster_generator.miniature_phys_path(original_phys_path) Linker._move_miniature_to_collection(original_phys_path, new_original_phys_path, video_generator) Linker._create_symlink(video_generator, new_original_phys_path) Linker._remove_original(original_phys_path, new_original_phys_path) Linker._move_poster(poster_phys_path, poster_generator, new_original_phys_path) Linker._update_file_information(file, new_original_phys_path)
def _update_mtime_if_needed(cls, file_object): file_phys_path = collection_phys_path(file_object.path) mtime = get_mtime_datetime(file_phys_path) if file_object.modification_time != mtime: logger.info("updating file mtime (and possibly: aspect_ratio): " + file_object.path) file_object.modification_time = mtime if is_jpeg(file_phys_path): file_object.aspect_ratio = cls.get_image_aspect_ratio(file_phys_path) file_object.save()
def find_or_create_directory(web_path, parent=None, update_modification_time=True): directory_mtime = get_mtime_datetime(locations.collection_phys_path(web_path)) directory, created = Directory.objects.get_or_create(path=web_path, defaults={'modification_time': directory_mtime, 'parent': parent}) if update_modification_time and directory.modification_time != directory_mtime: logger.info("updating directory mtime: " + directory.path) directory.modification_time = directory_mtime directory.save() return directory
def post(self, request, *args, **kwargs): media_file = self.get_object() destination = self.request.data.get('destination', None) if destination is None: return Response({'reason': 'Required "destination" param is missing'}, status=status.HTTP_400_BAD_REQUEST) dst_folder_phys_path = locations.collection_phys_path(destination) move_to_trash = locations.web_path_in_trash(destination) # destination directory should exists unless media_file is moved to trash (directory tree should be created then) if not move_to_trash and not os.path.isdir(dst_folder_phys_path): return Response({'reason': 'Invalid destination directory'}, status=status.HTTP_404_NOT_FOUND) dst_file_web_path = normpath_join(destination, media_file.name) return self._handle_move_request(media_file.path, dst_file_web_path)
def perform_requested_rotations(cls): for rotated_image in Image.objects.filter(~Q(orientation='up')): image_phys_path = locations.collection_phys_path(rotated_image.path) cls._rotate_by_jpegtran(image_phys_path, rotated_image)
def _update_directory_mtime(new_directory): new_directory.modification_time = get_mtime_datetime(collection_phys_path(new_directory.path)) new_directory.save()