From 8e0cdbf0aec034732dfa33af8f72d9d7d3b712ba Mon Sep 17 00:00:00 2001 From: Fijxu Date: Sat, 23 May 2026 00:37:34 -0400 Subject: [PATCH] improve playlist metadataRows and metadataParts parsing for channel playlists --- src/invidious/yt_backend/extractors.cr | 42 +++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/src/invidious/yt_backend/extractors.cr b/src/invidious/yt_backend/extractors.cr index e188caa9..bddecd76 100644 --- a/src/invidious/yt_backend/extractors.cr +++ b/src/invidious/yt_backend/extractors.cr @@ -758,11 +758,45 @@ private module Parsers # "metadataParts": [ ... ] <-- metadataParts with the information we are searching for. # } # ] - metadata_parts = metadata.dig?("metadata", "contentMetadataViewModel", "metadataRows").try &.as_a - .find { |row| row["metadataParts"]? } - .try &.dig("metadataParts", 0) + # + # Playlist on channels also contain metadataRows, but not with the type of data we are searching + # for which are the channel name and channel ID, instead they have two fields depending of the playlist + # updated date: + # + # It looks like this: + # "metadataRows": [ + # { + # "metadataParts": [ + # { + # "text": { + # "content": "Updated 4 days ago" + # } + # } <-- This object is missing if the playlist has not been updated in around 7 + # days + # ] + # }, + # { + # "metadataParts": [ + # { + # "text": { + # "content": "View full playlist", + # "commandRuns": [ ... ], + # "styleRuns": [ ... ]. + # } + # } <-- This object is always present, so we use this to determine if the + # metadataParts can be used or not. + # ] + # } + # ] + # + metadata_rows = metadata.dig?("metadata", "contentMetadataViewModel", "metadataRows").try &.as_a + metadata_parts = metadata_rows.try &.find { |row| + parts = row["metadataParts"]?.try &.as_a + parts && !parts.any? { |item| item.dig?("text", "content").try &.as_s == "View full playlist" } + }.try &.["metadataParts"].as_a - if author_info = metadata_parts.try &.["text"]? + if author_info = metadata_parts.try &.find { |item| item.dig?("text", "commandRuns") } + .try &.["text"] author = author_info["content"].as_s author_id = author_info.dig?("commandRuns", 0, "onTap", "innertubeCommand", "browseEndpoint", "browseId") .try &.as_s || author_fallback.id