fix: fix playlists videos parsing

This commit is contained in:
Fijxu 2026-06-24 22:05:34 -04:00
parent b9b61e6201
commit d14093f815
No known key found for this signature in database
GPG Key ID: 32C1DDF333EDA6A4
2 changed files with 36 additions and 14 deletions

View File

@ -454,6 +454,10 @@ def get_playlist_videos(playlist : InvidiousPlaylist | Playlist, offset : Int32,
end end
end end
# TODO (2026-06-24): Migrate this function to use parsers instead, as it uses,
# the same LockupViewModel used in Channel videos and Youtube playlists that
# appears on searches (Invidious /search endpoint).
# Related to https://github.com/iv-org/invidious/pull/5736
def extract_playlist_videos(playlist_id : String, initial_data : Hash(String, JSON::Any)) def extract_playlist_videos(playlist_id : String, initial_data : Hash(String, JSON::Any))
videos = [] of PlaylistVideo | ProblematicTimelineItem videos = [] of PlaylistVideo | ProblematicTimelineItem
@ -479,18 +483,38 @@ def extract_playlist_videos(playlist_id : String, initial_data : Hash(String, JS
contents.try &.each do |item| contents.try &.each do |item|
if i = item["lockupViewModel"]? if i = item["lockupViewModel"]?
thumbnail_view_model = i.dig?(
"contentImage", "thumbnailViewModel"
)
watch_endpoint = i.dig?("rendererContext", "commandContext", "onTap", "innertubeCommand", "watchEndpoint") watch_endpoint = i.dig?("rendererContext", "commandContext", "onTap", "innertubeCommand", "watchEndpoint")
video_id = watch_endpoint.try &.["videoId"]?.try &.as_s video_id = watch_endpoint.try &.["videoId"]?.try &.as_s
plid = watch_endpoint.try &.["playlistId"]?.try &.as_s || playlist_id plid = watch_endpoint.try &.["playlistId"]?.try &.as_s || playlist_id
index = watch_endpoint.try &.["index"]?.try &.as_i64 index = watch_endpoint.try &.["index"]?.try &.as_i64
metadata = i["metadata"]? metadata = i["metadata"]?
lockup_metadata_view_model = metadata["lockupMetadataViewModel"]? lockup_metadata_view_model = metadata.try &.dig?("lockupMetadataViewModel")
title = lookup_metadata_view_model.dig?("title", "content").try &.as_s title = lockup_metadata_view_model.try &.dig?("title", "content").try &.as_s
# Bellow is broken xd lockup_metadata = lockup_metadata_view_model.try &.dig?("metadata")
author = i["shortBylineText"]?.try &.["runs"][0]["text"].as_s || "" metadata_rows = lockup_metadata.try &.dig?("contentMetadataViewModel", "metadataRows").try &.as_a
ucid = i["shortBylineText"]?.try &.["runs"][0]["navigationEndpoint"]["browseEndpoint"]["browseId"].as_s || ""
length_seconds = i["lengthSeconds"]?.try &.as_s.to_i # Find the metadataParts with commandRuns inside, which contains author
# information.
metadata_parts = metadata_rows.try &.find { |row|
parts = row["metadataParts"]?.try &.as_a
parts && parts.any? { |item| item.dig?("text", "commandRuns").try &.as_a }
}.try &.["metadataParts"].as_a
if author_info = metadata_parts.try &.find(&.dig?("text", "commandRuns"))
.try &.["text"]
author = author_info["content"].as_s
ucid = author_info.dig?("commandRuns", 0, "onTap", "innertubeCommand", "browseEndpoint", "browseId")
.try &.as_s
end
length = thumbnail_view_model.try &.dig?("overlays", 0, "thumbnailBottomOverlayViewModel", "badges", 0, "thumbnailBadgeViewModel", "text").try &.as_s
length_seconds = decode_length_seconds(length) if length
live = false live = false
if !length_seconds if !length_seconds
@ -499,15 +523,15 @@ def extract_playlist_videos(playlist_id : String, initial_data : Hash(String, JS
end end
videos << PlaylistVideo.new({ videos << PlaylistVideo.new({
title: title, title: title || "",
id: video_id, id: video_id || "",
author: author, author: author || "",
ucid: ucid, ucid: ucid || "",
length_seconds: length_seconds, length_seconds: length_seconds,
published: Time.utc, published: Time.utc,
plid: plid, plid: plid,
live_now: live, live_now: live,
index: index, index: index || -1_i64,
}) })
end end
rescue ex rescue ex

View File

@ -630,8 +630,7 @@ private module Parsers
end end
end end
# Parses an InnerTube lockupViewModel into a SearchPlaylist, SearchVideo # Parses an InnerTube lockupViewModel into a SearchPlaylist, SearchVideo.
# or a PlaylistVideo.
# Returns nil when the given object is not a lockupViewModel. # Returns nil when the given object is not a lockupViewModel.
# #
# This structure is present since November 2024 on the "podcasts" and # This structure is present since November 2024 on the "podcasts" and
@ -639,7 +638,6 @@ private module Parsers
# a richItemRenderer or a richGridRenderer. # a richItemRenderer or a richGridRenderer.
# #
# Since 2026-05-21, now channel videos are encapsulated in a lockupViewModel. # Since 2026-05-21, now channel videos are encapsulated in a lockupViewModel.
# Since 2026-06-12, now videos inside a playlist are encapsulated in a lockupViewModel.
module LockupViewModelParser module LockupViewModelParser
extend self extend self
include BaseParser include BaseParser