mirror of
https://github.com/iv-org/invidious.git
synced 2026-10-25 06:19:12 -05:00
fix(extractors): parse collab lockup views/published across metadataRows
Channel collaboration videos list co-authors in earlier metadataRows, so views and published timestamps were missed when only the first row was read. Flatten every row's metadataParts with nil-safe lookups (matching the playlist branch style) and keep the existing views/"ago" filters. Adds a focused LockupViewModelParser regression spec covering the split-row collab shape and a normal single-row listing. Fixes: #5740
This commit is contained in:
parent
049d591d29
commit
4468145e2b
93
spec/invidious/yt_backend/lockup_view_model_parser_spec.cr
Normal file
93
spec/invidious/yt_backend/lockup_view_model_parser_spec.cr
Normal file
@ -0,0 +1,93 @@
|
||||
require "../../parsers_helper.cr"
|
||||
|
||||
Spectator.describe "LockupViewModelParser channel videos" do
|
||||
# Minimal lockupViewModel shape used by channel video listings.
|
||||
def lockup_video(metadata_rows : Array(JSON::Any)) : JSON::Any
|
||||
JSON.parse({
|
||||
"lockupViewModel" => {
|
||||
"contentType" => "LOCKUP_CONTENT_TYPE_VIDEO",
|
||||
"contentId" => "dQw4w9WgXcQ",
|
||||
"contentImage" => {
|
||||
"thumbnailViewModel" => {
|
||||
"image" => {
|
||||
"sources" => [{"url" => "https://i.ytimg.com/vi/dQw4w9WgXcQ/hqdefault.jpg"}],
|
||||
},
|
||||
"overlays" => [{
|
||||
"thumbnailBottomOverlayViewModel" => {
|
||||
"badges" => [{
|
||||
"thumbnailBadgeViewModel" => {"text" => "12:34"},
|
||||
}],
|
||||
},
|
||||
}],
|
||||
},
|
||||
},
|
||||
"metadata" => {
|
||||
"lockupMetadataViewModel" => {
|
||||
"title" => {"content" => "Google Maps is unreasonably fast. Let me explain"},
|
||||
"metadata" => {
|
||||
"contentMetadataViewModel" => {
|
||||
"metadataRows" => metadata_rows.map(&.raw),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}.to_json)
|
||||
end
|
||||
|
||||
it "parses views and published from the first metadata row (non-collab)" do
|
||||
rows = [
|
||||
JSON.parse({
|
||||
"metadataParts" => [
|
||||
{"text" => {"content" => "1.5M views"}},
|
||||
{"text" => {"content" => "3 days ago"}},
|
||||
],
|
||||
}.to_json),
|
||||
]
|
||||
|
||||
result = parse_item(lockup_video(rows), "Veritasium", "UCHnyfMqiRRG1u-2MsSQLbXA")
|
||||
expect(result).to be_a(SearchVideo)
|
||||
|
||||
video = result.as(SearchVideo)
|
||||
expect(video.title).to eq("Google Maps is unreasonably fast. Let me explain")
|
||||
expect(video.id).to eq("dQw4w9WgXcQ")
|
||||
expect(video.views).to eq(1_500_000)
|
||||
expect(video.published).to be_close(Time.utc - 3.days, 2.seconds)
|
||||
expect(video.length_seconds).to eq(754)
|
||||
end
|
||||
|
||||
it "parses views and published when collab authors occupy earlier metadata rows" do
|
||||
# Collaboration listings place co-authors in the first row(s); views/"ago" follow later.
|
||||
# Rows without metadataParts must be skipped without raising.
|
||||
rows = [
|
||||
JSON.parse("{}"),
|
||||
JSON.parse({
|
||||
"metadataParts" => [
|
||||
{
|
||||
"text" => {"content" => "Veritasium"},
|
||||
"icon" => {"name" => "CHECK_CIRCLE_FILLED"},
|
||||
},
|
||||
{
|
||||
"text" => {"content" => "and Linus Tech Tips"},
|
||||
"icon" => {"name" => "CHECK_CIRCLE_FILLED"},
|
||||
},
|
||||
],
|
||||
}.to_json),
|
||||
JSON.parse({
|
||||
"metadataParts" => [
|
||||
{"text" => {"content" => "1.5M views"}},
|
||||
{"text" => {"content" => "3 days ago"}},
|
||||
],
|
||||
}.to_json),
|
||||
]
|
||||
|
||||
result = parse_item(lockup_video(rows), "Veritasium", "UCHnyfMqiRRG1u-2MsSQLbXA")
|
||||
expect(result).to be_a(SearchVideo)
|
||||
|
||||
video = result.as(SearchVideo)
|
||||
expect(video.views).to eq(1_500_000)
|
||||
expect(video.published).to be_close(Time.utc - 3.days, 2.seconds)
|
||||
expect(video.author).to eq("Veritasium")
|
||||
expect(video.ucid).to eq("UCHnyfMqiRRG1u-2MsSQLbXA")
|
||||
end
|
||||
end
|
||||
@ -662,7 +662,12 @@ private module Parsers
|
||||
metadata = item_contents.dig("metadata", "lockupMetadataViewModel")
|
||||
title = metadata.dig("title", "content").as_s
|
||||
# Contains the views of the video and the published time of the video.
|
||||
metadata_parts = metadata.dig("metadata", "contentMetadataViewModel", "metadataRows", 0, "metadataParts").try &.as_a
|
||||
# For collaboration videos the first metadataRows entry lists co-authors,
|
||||
# with views/"ago" in a later row — search every row (nil-safe).
|
||||
metadata_rows = metadata.dig?("metadata", "contentMetadataViewModel", "metadataRows").try &.as_a
|
||||
metadata_parts = metadata_rows.try &.compact_map { |row|
|
||||
row["metadataParts"]?.try &.as_a
|
||||
}.try &.flatten
|
||||
|
||||
view_count_text = metadata_parts.try &.find { |item| item["icon"]?.nil? && item.dig?("text", "content").try &.as_s.includes?("views") }
|
||||
.try &.dig("text", "content").as_s
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user