Trimming video file name to avoid error when file name is too long.

This commit is contained in:
Roland 2024-07-14 09:54:02 -06:00
parent 37ff1ab2cd
commit cdc73d9d16
No known key found for this signature in database
GPG key ID: 1BA620CE6C9A4680
13 changed files with 140 additions and 27 deletions

View file

@ -8,12 +8,14 @@ import argparse
import asyncio
import difflib
import functools
import os.path
import re
import sys
from difflib import SequenceMatcher
from typing import List, Generator, Optional, Dict
from modules.videos.models import Video, Channel
from modules.videos.lib import parse_video_file_name
from modules.videos.models import Video, Channel
from wrolpi.common import get_relative_to_media_directory, chain
from wrolpi.db import get_db_curs, get_db_session
from wrolpi.files.models import FileGroup
@ -95,6 +97,8 @@ async def rank_video_quality(video: Video, sizes_by_source_id: Dict[str, int]) -
rank += 2
if video_file_matches_channel_info_json(video):
rank += 2
if video.get_comments():
rank += 2
# These are more common.
if video.channel_id:
rank += 1
@ -124,7 +128,7 @@ async def rank_video_quality(video: Video, sizes_by_source_id: Dict[str, int]) -
def delete_video(video_id: int):
with get_db_session(commit=True) as session:
video = session.query(Video).filter_by(id=video_id).one()
video: Video = session.query(Video).filter_by(id=video_id).one()
video.delete()
@ -183,12 +187,29 @@ async def handle_user_delete_duplicates(similar_videos: List[Video], video_url:
if all_changes.isdigit():
print(f'\nSkipping {similar_videos[0]} and similar because they are parts')
return
if delete_lower_ranked_automatically and any(PART_MATCH.match(i.file_group.title) for i in similar_videos):
# "Part" detection is far too inaccurate, do not delete part videos.
print(f'\nSkipping {similar_videos[0]} and similar because they may be parts')
return
# Preserve the largest duplicate video.
sizes_by_source_id = largest_size_by_source_id(similar_videos)
print('\n')
ranked_videos = [(await rank_video_quality(i, sizes_by_source_id), i) for i in similar_videos]
# Largest video gets a rank bump.
largest_video_idx = None
largest_video_size = None
for idx, video in enumerate([i[1] for i in ranked_videos]):
if largest_video_size and video.file_group.size > largest_video_size:
largest_video_idx = idx
largest_video_size = video.file_group.size
else:
largest_video_idx = idx
largest_video_size = video.file_group.size
ranked_videos[largest_video_idx] = \
(ranked_videos[largest_video_idx][0] + 1, ranked_videos[largest_video_idx][1])
# Order videos by rank, then size.
ranked_videos = sorted(ranked_videos, key=lambda i: (i[0], i[1].file_group.size), reverse=True)
for rank, video in ranked_videos:
fg = video.file_group
@ -212,13 +233,17 @@ async def handle_user_delete_duplicates(similar_videos: List[Video], video_url:
index = similar_videos.index(lowest_video)
del similar_videos[index]
continue
response = input('Delete the lowest ranked video? (y/N)').lower()
response = input('Delete the lowest ranked video? Merge? (y/m/N)').lower()
if response == 'y':
# Don't need to confirm when the video has been ranked low.
delete_video(lowest_video.id)
index = similar_videos.index(lowest_video)
del similar_videos[index]
elif response == 'n':
elif response == 'm':
success = await handle_merge(ranked_videos)
if success:
break
elif response == 'n' or response == '':
break
elif response == 'c':
raise SkipChannel()
@ -237,6 +262,47 @@ async def handle_user_delete_duplicates(similar_videos: List[Video], video_url:
raise SkipChannel()
async def handle_merge(videos: List[tuple[int, Video]]) -> bool | None:
"""Replace the video file of Video with better data. Delete all other Videos."""
response = input(
'Keep which video file? Empty will replace largest video with best rank. (starting at 0..., n to skip)')
if response == 'n':
return
if response == '':
# Largest Video.
keep_video = sorted(videos, key=lambda i: i[1].video_path.stat().st_size, reverse=True)[0][1]
# Highest ranked video.
keep_data = videos[0][1]
else:
if not response.isdigit() or ((keep_video := int(response)) and keep_video > len(videos) - 1):
print('Invalid file index', file=sys.stderr)
return
if len(videos) == 2:
keep_data = 0 if keep_video == 1 else 1
else:
response = input('Keep which video data? (starting at 0..., n to skip)')
if response == 'n':
return
if not response.isdigit() or ((keep_data := int(response)) and keep_data > len(videos) - 1):
print('Invalid file index', file=sys.stderr)
return
keep_video = videos[keep_video][1]
keep_data = videos[keep_data][1]
if keep_video == keep_data:
print('Cannot keep and delete same video', file=sys.stderr),
return
# Replace video file of Video with better metadata.
print(f'{keep_video.video_path} -> {keep_data.video_path}')
os.replace(keep_video.video_path, keep_data.video_path)
for _, video in videos:
if video.id != keep_data.id:
delete_video(video.id)
keep_data.validate()
return True
async def main(channel_id: int = None, minimum_rank: float = MINIMUM_RANK, video_url: str = None,
directory: str = None, delete_lower_ranked_automatically: bool = False):
with get_db_curs() as curs: