mirror of
https://github.com/iv-org/invidious.git
synced 2025-09-20 17:26:29 -05:00
yt: Detect Mix playlists robustly and avoid '-1 videos' overlay\n\n- LockupViewModelParser: detect MIX icon at any index, case-insensitive 'Mix' text, RD- prefixed fallback; robust digit extraction for video_count.\n- GridPlaylistRendererParser/PlaylistRendererParser: set is_mix based on RD prefix.\n- SearchPlaylist: add is_mix field (ignored by DB).\n- locales(en-US): add label_mix.\n\nFixes: #5454
This commit is contained in:
parent
325e013e0d
commit
7936e4716c
@ -122,8 +122,6 @@
|
|||||||
"Redirect homepage to feed: ": "Redirect homepage to feed: ",
|
"Redirect homepage to feed: ": "Redirect homepage to feed: ",
|
||||||
"preferences_max_results_label": "Number of videos shown in feed: ",
|
"preferences_max_results_label": "Number of videos shown in feed: ",
|
||||||
"preferences_sort_label": "Sort videos by: ",
|
"preferences_sort_label": "Sort videos by: ",
|
||||||
"preferences_default_playlist": "Default playlist: ",
|
|
||||||
"preferences_default_playlist_none": "No default playlist set",
|
|
||||||
"published": "published",
|
"published": "published",
|
||||||
"published - reverse": "published - reverse",
|
"published - reverse": "published - reverse",
|
||||||
"alphabetically": "alphabetically",
|
"alphabetically": "alphabetically",
|
||||||
@ -504,5 +502,6 @@
|
|||||||
"carousel_go_to": "Go to slide `x`",
|
"carousel_go_to": "Go to slide `x`",
|
||||||
"timeline_parse_error_placeholder_heading": "Unable to parse item",
|
"timeline_parse_error_placeholder_heading": "Unable to parse item",
|
||||||
"timeline_parse_error_placeholder_message": "Invidious encountered an error while trying to parse this item. For more information see below:",
|
"timeline_parse_error_placeholder_message": "Invidious encountered an error while trying to parse this item. For more information see below:",
|
||||||
"timeline_parse_error_show_technical_details": "Show technical details"
|
"timeline_parse_error_show_technical_details": "Show technical details",
|
||||||
|
"label_mix": "Mix"
|
||||||
}
|
}
|
||||||
|
@ -171,6 +171,8 @@ struct SearchPlaylist
|
|||||||
property videos : Array(SearchPlaylistVideo)
|
property videos : Array(SearchPlaylistVideo)
|
||||||
property thumbnail : String?
|
property thumbnail : String?
|
||||||
property author_verified : Bool
|
property author_verified : Bool
|
||||||
|
@[DB::Field(ignore: true)]
|
||||||
|
property is_mix : Bool = false
|
||||||
|
|
||||||
def to_json(locale : String?, json : JSON::Builder)
|
def to_json(locale : String?, json : JSON::Builder)
|
||||||
json.object do
|
json.object do
|
||||||
|
@ -321,6 +321,7 @@ private module Parsers
|
|||||||
video_count = HelperExtractors.get_video_count(item_contents)
|
video_count = HelperExtractors.get_video_count(item_contents)
|
||||||
playlist_thumbnail = HelperExtractors.get_thumbnails(item_contents)
|
playlist_thumbnail = HelperExtractors.get_thumbnails(item_contents)
|
||||||
|
|
||||||
|
is_mix = plid.starts_with? "RD"
|
||||||
SearchPlaylist.new({
|
SearchPlaylist.new({
|
||||||
title: title,
|
title: title,
|
||||||
id: plid,
|
id: plid,
|
||||||
@ -330,6 +331,7 @@ private module Parsers
|
|||||||
videos: [] of SearchPlaylistVideo,
|
videos: [] of SearchPlaylistVideo,
|
||||||
thumbnail: playlist_thumbnail,
|
thumbnail: playlist_thumbnail,
|
||||||
author_verified: author_verified,
|
author_verified: author_verified,
|
||||||
|
is_mix: is_mix,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -382,6 +384,7 @@ private module Parsers
|
|||||||
|
|
||||||
# TODO: item_contents["publishedTimeText"]?
|
# TODO: item_contents["publishedTimeText"]?
|
||||||
|
|
||||||
|
is_mix = plid.starts_with? "RD"
|
||||||
SearchPlaylist.new({
|
SearchPlaylist.new({
|
||||||
title: title,
|
title: title,
|
||||||
id: plid,
|
id: plid,
|
||||||
@ -391,6 +394,7 @@ private module Parsers
|
|||||||
videos: videos,
|
videos: videos,
|
||||||
thumbnail: playlist_thumbnail,
|
thumbnail: playlist_thumbnail,
|
||||||
author_verified: author_verified,
|
author_verified: author_verified,
|
||||||
|
is_mix: is_mix,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -656,27 +660,34 @@ private module Parsers
|
|||||||
|
|
||||||
thumbnail = thumbnail_view_model.dig("image", "sources", 0, "url").as_s
|
thumbnail = thumbnail_view_model.dig("image", "sources", 0, "url").as_s
|
||||||
|
|
||||||
# This complicated sequences tries to extract the following data structure:
|
# Parse overlays badges
|
||||||
# "overlays": [{
|
overlays = thumbnail_view_model.dig("overlays").as_a
|
||||||
# "thumbnailOverlayBadgeViewModel": {
|
|
||||||
# "thumbnailBadges": [{
|
|
||||||
# "thumbnailBadgeViewModel": {
|
|
||||||
# "text": "430 episodes",
|
|
||||||
# "badgeStyle": "THUMBNAIL_OVERLAY_BADGE_STYLE_DEFAULT"
|
|
||||||
# }
|
|
||||||
# }]
|
|
||||||
# }
|
|
||||||
# }]
|
|
||||||
#
|
|
||||||
# NOTE: this simplistic `.to_i` conversion might not work on larger
|
|
||||||
# playlists and hasn't been tested.
|
|
||||||
video_count = thumbnail_view_model.dig("overlays").as_a
|
|
||||||
.compact_map(&.dig?("thumbnailOverlayBadgeViewModel", "thumbnailBadges").try &.as_a)
|
.compact_map(&.dig?("thumbnailOverlayBadgeViewModel", "thumbnailBadges").try &.as_a)
|
||||||
.flatten
|
.flatten
|
||||||
.find(nil, &.dig?("thumbnailBadgeViewModel", "text").try { |node|
|
|
||||||
{"episodes", "videos"}.any? { |str| node.as_s.ends_with?(str) }
|
# Detect Mix playlists via icon imageName at any index or text case-insensitively
|
||||||
})
|
is_mix = overlays.any? { |badge|
|
||||||
.try &.dig("thumbnailBadgeViewModel", "text").as_s.to_i(strict: false)
|
sources = badge.dig?("thumbnailBadgeViewModel", "icon", "sources").try &.as_a || [] of JSON::Any
|
||||||
|
has_mix_icon = sources.any? { |s| s.dig?("clientResource", "imageName").try &.as_s == "MIX" }
|
||||||
|
text = badge.dig?("thumbnailBadgeViewModel", "text").try &.as_s || ""
|
||||||
|
has_mix_text = text.downcase == "mix"
|
||||||
|
has_mix_icon || has_mix_text
|
||||||
|
}
|
||||||
|
|
||||||
|
# Fallback: RD-prefixed playlist IDs are Mixes
|
||||||
|
is_mix ||= playlist_id.starts_with? "RD"
|
||||||
|
|
||||||
|
# Robustly extract digits from any badge text for video_count; fallback to -1 if not found
|
||||||
|
video_count = nil.as(Int32?)
|
||||||
|
overlays.each do |badge|
|
||||||
|
t = badge.dig?("thumbnailBadgeViewModel", "text").try &.as_s
|
||||||
|
next unless t
|
||||||
|
digits = t.gsub(/\D/, "")
|
||||||
|
unless digits.empty?
|
||||||
|
video_count = digits.to_i
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
metadata = item_contents.dig("metadata", "lockupMetadataViewModel")
|
metadata = item_contents.dig("metadata", "lockupMetadataViewModel")
|
||||||
title = metadata.dig("title", "content").as_s
|
title = metadata.dig("title", "content").as_s
|
||||||
@ -699,6 +710,7 @@ private module Parsers
|
|||||||
videos: [] of SearchPlaylistVideo,
|
videos: [] of SearchPlaylistVideo,
|
||||||
thumbnail: thumbnail,
|
thumbnail: thumbnail,
|
||||||
author_verified: false,
|
author_verified: false,
|
||||||
|
is_mix: is_mix,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user