diff --git a/modules/videos/lib.py b/modules/videos/lib.py index 72beec05..f14ee4aa 100644 --- a/modules/videos/lib.py +++ b/modules/videos/lib.py @@ -841,9 +841,9 @@ def get_video_metadata(video) -> dict: """Extract video metadata with fallback chain. Fallback strategy: - 1. Primary: info_json + database fields (video.source_id, file_group.published_datetime) - 2. Secondary: parse_video_file_name() - ALL-OR-NOTHING, only if it provides channel AND date AND source_id - 3. Tertiary: video.channel.name for uploader only + 1. Primary: video.channel.name for uploader (when video belongs to a channel) + 2. Secondary: info_json + database fields (video.source_id, file_group.published_datetime) + 3. Tertiary: parse_video_file_name() - ALL-OR-NOTHING, only if it provides channel AND date AND source_id Returns: dict with keys: upload_date (datetime|None), uploader (str), source_id (str), title (str) @@ -856,7 +856,13 @@ def get_video_metadata(video) -> dict: if not upload_date and info_json.get('upload_date'): upload_date = dates.strpdate(info_json['upload_date']) - uploader = info_json.get('uploader') or info_json.get('channel') or '' + # Prioritize WROLPi channel name when video belongs to a channel + # This ensures reorganized files use the user's channel name, not the YouTube uploader name + if video.channel and video.channel.name: + uploader = video.channel.name + else: + uploader = info_json.get('uploader') or info_json.get('channel') or '' + source_id = video.source_id or info_json.get('id') or '' title = file_group.title or info_json.get('title') or info_json.get('fulltitle') or 'untitled' @@ -873,10 +879,6 @@ def get_video_metadata(video) -> dict: if not uploader: uploader = parsed_channel - # Final fallback: Channel.name for uploader only - if not uploader and video.channel: - uploader = video.channel.name or '' - return { 'upload_date': upload_date, 'uploader': uploader, diff --git a/modules/videos/test/test_lib.py b/modules/videos/test/test_lib.py index ef1d7eff..6c1bd613 100644 --- a/modules/videos/test/test_lib.py +++ b/modules/videos/test/test_lib.py @@ -355,3 +355,35 @@ def test_format_video_filename_without_upload_date(test_session, video_factory, # Template result: '{upload_year}/{uploader}_{upload_date}_{id}_{title}.mp4' # With empty year/date: '/__Test Video_Test Video.mp4' (has a slash for subdirectory) assert '/' in result # Confirms template was used (has subdirectory), not fallback + + +def test_format_video_filename_uses_channel_name_over_info_json(test_session, video_factory, channel_factory, + test_videos_downloader_config): + """When a video belongs to a channel, format_video_filename should use the WROLPi channel name, + not the YouTube uploader name from info_json. + + This ensures that when a user renames their channel, reorganized files use the new name. + """ + from modules.videos.lib import format_video_filename + + # Create channel with WROLPi name + channel = channel_factory(name='My Custom Channel') + + # Create video in channel with different YouTube uploader name in info_json + video = video_factory( + channel_id=channel.id, + title='My Video Title', + with_video_file=True, + with_info_json={ + 'id': 'abc123', + 'uploader': 'Learning Self Reliance', # Different from channel name! + 'upload_date': '20240115', + } + ) + + template = '%(uploader)s_%(upload_date)s_%(id)s_%(title)s.%(ext)s' + result = format_video_filename(video, template) + + # Should use WROLPi channel name, NOT the YouTube uploader + assert 'My Custom Channel' in result + assert 'Learning Self Reliance' not in result diff --git a/wrolpi/collections/test/test_reorganize.py b/wrolpi/collections/test/test_reorganize.py index 63511ed4..f39c6c38 100644 --- a/wrolpi/collections/test/test_reorganize.py +++ b/wrolpi/collections/test/test_reorganize.py @@ -2222,7 +2222,8 @@ def test_video_metadata_fallback_uses_channel_name_for_uploader(test_session, te @pytest.mark.parametrize( "filename,channel_name,title,db_datetime,db_source_id,expected_year,expected_uploader,expected_source_id,should_not_contain", [ - # Complete filename with all fields - uses filename fallback values + # Complete filename with all fields - channel name takes priority over filename + # This ensures reorganized files use the WROLPi channel name, not the YouTube uploader ( "TestUploader_20170529_p_MzsCFkUPU_My Test Video.mp4", "FormatFallbackChannel", @@ -2230,9 +2231,9 @@ def test_video_metadata_fallback_uses_channel_name_for_uploader(test_session, te None, # No DB datetime None, # No DB source_id "2017", - "TestUploader", + "FormatFallbackChannel", # Channel name takes priority "p_MzsCFkUPU", - [], # No forbidden values + ["TestUploader"], # Should NOT contain filename uploader ), # Incomplete filename (no source_id) - uses DB values, not filename (