@ -164,148 +164,168 @@ def extract_videos(initial_data : Hash(String, JSON::Any), author_fallback : Str
extract_items ( initial_data , author_fallback , author_id_fallback ) . select ( & . is_a? ( SearchVideo ) ) . map ( & . as ( SearchVideo ) )
end
def extract_item ( item : JSON :: Any , author_fallback : String ? = nil , author_id_fallback : String ? = nil )
if i = ( item [ " videoRenderer " ]? || item [ " gridVideoRenderer " ]? )
video_id = i [ " videoId " ] . as_s
title = i [ " title " ] . try { | t | t [ " simpleText " ]? . try & . as_s || t [ " runs " ]? . try & . as_a . map ( & . [ " text " ] . as_s ) . join ( " " ) } || " "
author_info = i [ " ownerText " ]? . try & . [ " runs " ] . as_a [ 0 ]?
author = author_info . try & . [ " text " ] . as_s || author_fallback || " "
author_id = author_info . try & . [ " navigationEndpoint " ]? . try & . [ " browseEndpoint " ] [ " browseId " ] . as_s || author_id_fallback || " "
published = i [ " publishedTimeText " ]? . try & . [ " simpleText " ]? . try { | t | decode_date ( t . as_s ) } || Time . local
view_count = i [ " viewCountText " ]? . try & . [ " simpleText " ]? . try & . as_s . gsub ( / \ D+ / , " " ) . to_i64? || 0 _i64
description_html = i [ " descriptionSnippet " ]? . try { | t | parse_content ( t ) } || " "
length_seconds = i [ " lengthText " ]? . try & . [ " simpleText " ]? . try & . as_s . try { | t | decode_length_seconds ( t ) } ||
i [ " thumbnailOverlays " ]? . try & . as_a . find ( & . [ " thumbnailOverlayTimeStatusRenderer " ]? ) . try & . [ " thumbnailOverlayTimeStatusRenderer " ]?
. try & . [ " text " ]? . try & . [ " simpleText " ]? . try & . as_s . try { | t | decode_length_seconds ( t ) } || 0
live_now = false
paid = false
premium = false
premiere_timestamp = i [ " upcomingEventData " ]? . try & . [ " startTime " ]? . try { | t | Time . unix ( t . as_s . to_i64 ) }
i [ " badges " ]? . try & . as_a . each do | badge |
b = badge [ " metadataBadgeRenderer " ]
case b [ " label " ] . as_s
when " LIVE NOW "
live_now = true
when " New " , " 4K " , " CC "
# TODO
when " Premium "
paid = true
# TODO: Potentially available as i["topStandaloneBadge"]["metadataBadgeRenderer"]
premium = true
else nil # Ignore
end
end
SearchVideo . new ( {
title : title ,
id : video_id ,
author : author ,
ucid : author_id ,
published : published ,
views : view_count ,
description_html : description_html ,
length_seconds : length_seconds ,
live_now : live_now ,
paid : paid ,
premium : premium ,
premiere_timestamp : premiere_timestamp ,
} )
elsif i = item [ " channelRenderer " ]?
author = i [ " title " ] [ " simpleText " ]? . try & . as_s || author_fallback || " "
author_id = i [ " channelId " ]? . try & . as_s || author_id_fallback || " "
author_thumbnail = i [ " thumbnail " ] [ " thumbnails " ]? . try & . as_a [ 0 ]? . try { | u | " https: #{ u [ " url " ] } " } || " "
subscriber_count = i [ " subscriberCountText " ]? . try & . [ " simpleText " ]? . try & . as_s . try { | s | short_text_to_number ( s . split ( " " ) [ 0 ] ) } || 0
auto_generated = false
auto_generated = true if ! i [ " videoCountText " ]?
video_count = i [ " videoCountText " ]? . try & . [ " runs " ] . as_a [ 0 ]? . try & . [ " text " ] . as_s . gsub ( / \ D / , " " ) . to_i || 0
description_html = i [ " descriptionSnippet " ]? . try { | t | parse_content ( t ) } || " "
SearchChannel . new ( {
author : author ,
ucid : author_id ,
author_thumbnail : author_thumbnail ,
subscriber_count : subscriber_count ,
video_count : video_count ,
description_html : description_html ,
auto_generated : auto_generated ,
} )
elsif i = item [ " gridPlaylistRenderer " ]?
title = i [ " title " ] [ " runs " ] . as_a [ 0 ]? . try & . [ " text " ] . as_s || " "
plid = i [ " playlistId " ]? . try & . as_s || " "
video_count = i [ " videoCountText " ] [ " runs " ] . as_a [ 0 ]? . try & . [ " text " ] . as_s . gsub ( / \ D / , " " ) . to_i || 0
playlist_thumbnail = i [ " thumbnail " ] [ " thumbnails " ] [ 0 ]? . try & . [ " url " ]? . try & . as_s || " "
SearchPlaylist . new ( {
title : title ,
id : plid ,
author : author_fallback || " " ,
ucid : author_id_fallback || " " ,
video_count : video_count ,
videos : [ ] of SearchPlaylistVideo ,
thumbnail : playlist_thumbnail ,
} )
elsif i = item [ " playlistRenderer " ]?
title = i [ " title " ] [ " simpleText " ]? . try & . as_s || " "
plid = i [ " playlistId " ]? . try & . as_s || " "
video_count = i [ " videoCount " ]? . try & . as_s . to_i || 0
playlist_thumbnail = i [ " thumbnails " ] . as_a [ 0 ]? . try & . [ " thumbnails " ]? . try & . as_a [ 0 ]? . try & . [ " url " ] . as_s || " "
author_info = i [ " shortBylineText " ]? . try & . [ " runs " ] . as_a [ 0 ]?
author = author_info . try & . [ " text " ] . as_s || author_fallback || " "
author_id = author_info . try & . [ " navigationEndpoint " ]? . try & . [ " browseEndpoint " ] [ " browseId " ] . as_s || author_id_fallback || " "
videos = i [ " videos " ]? . try & . as_a . map do | v |
v = v [ " childVideoRenderer " ]
v_title = v [ " title " ] [ " simpleText " ]? . try & . as_s || " "
v_id = v [ " videoId " ]? . try & . as_s || " "
v_length_seconds = v [ " lengthText " ]? . try & . [ " simpleText " ]? . try { | t | decode_length_seconds ( t . as_s ) } || 0
SearchPlaylistVideo . new ( {
title : v_title ,
id : v_id ,
length_seconds : v_length_seconds ,
} )
end || [ ] of SearchPlaylistVideo
# TODO: i["publishedTimeText"]?
SearchPlaylist . new ( {
title : title ,
id : plid ,
author : author ,
ucid : author_id ,
video_count : video_count ,
videos : videos ,
thumbnail : playlist_thumbnail ,
} )
elsif i = item [ " radioRenderer " ]? # Mix
# TODO
elsif i = item [ " showRenderer " ]? # Show
# TODO
elsif i = item [ " shelfRenderer " ]?
elsif i = item [ " horizontalCardListRenderer " ]?
elsif i = item [ " searchPyvRenderer " ]? # Ad
end
end
def extract_items ( initial_data : Hash ( String , JSON :: Any ) , author_fallback : String ? = nil , author_id_fallback : String ? = nil )
items = [ ] of SearchItem
initial_data . try { | t | t [ " contents " ]? || t [ " response " ]? }
. try { | t | t [ " twoColumnBrowseResultsRenderer " ]? . try & . [ " tabs " ] . as_a . select ( & . [ " tabRenderer " ]? . try & . [ " selected " ] . as_bool ) [ 0 ]? . try & . [ " tabRenderer " ] [ " content " ] ||
t [ " twoColumnSearchResultsRenderer " ]? . try & . [ " primaryContents " ] ||
t [ " continuationContents " ]? }
. try { | t | t [ " sectionListRenderer " ]? || t [ " sectionListContinuation " ]? }
. try & . [ " contents " ] . as_a
. each { | c | c . try & . [ " itemSectionRenderer " ]? . try & . [ " contents " ] . as_a
. try { | t | t [ 0 ]? . try & . [ " shelfRenderer " ]? . try & . [ " content " ] [ " expandedShelfContentsRenderer " ]? . try & . [ " items " ] . as_a ||
t [ 0 ]? . try & . [ " gridRenderer " ]? . try & . [ " items " ] . as_a || t }
. each { | item |
if i = item [ " videoRenderer " ]?
video_id = i [ " videoId " ] . as_s
title = i [ " title " ] . try { | t | t [ " simpleText " ]? . try & . as_s || t [ " runs " ]? . try & . as_a . map ( & . [ " text " ] . as_s ) . join ( " " ) } || " "
author_info = i [ " ownerText " ]? . try & . [ " runs " ] . as_a [ 0 ]?
author = author_info . try & . [ " text " ] . as_s || author_fallback || " "
author_id = author_info . try & . [ " navigationEndpoint " ]? . try & . [ " browseEndpoint " ] [ " browseId " ] . as_s || author_id_fallback || " "
published = i [ " publishedTimeText " ]? . try & . [ " simpleText " ]? . try { | t | decode_date ( t . as_s ) } || Time . local
view_count = i [ " viewCountText " ]? . try & . [ " simpleText " ]? . try & . as_s . gsub ( / \ D+ / , " " ) . to_i64? || 0 _i64
description_html = i [ " descriptionSnippet " ]? . try { | t | parse_content ( t ) } || " "
length_seconds = i [ " lengthText " ]? . try & . [ " simpleText " ]? . try & . as_s . try { | t | decode_length_seconds ( t ) } || 0
live_now = false
paid = false
premium = false
premiere_timestamp = i [ " upcomingEventData " ]? . try & . [ " startTime " ]? . try { | t | Time . unix ( t . as_s . to_i64 ) }
i [ " badges " ]? . try & . as_a . each do | badge |
b = badge [ " metadataBadgeRenderer " ]
case b [ " label " ] . as_s
when " LIVE NOW "
live_now = true
when " New " , " 4K " , " CC "
# TODO
when " Premium "
paid = true
# TODO: Potentially available as i["topStandaloneBadge"]["metadataBadgeRenderer"]
premium = true
else nil # Ignore
end
end
items << SearchVideo . new ( {
title : title ,
id : video_id ,
author : author ,
ucid : author_id ,
published : published ,
views : view_count ,
description_html : description_html ,
length_seconds : length_seconds ,
live_now : live_now ,
paid : paid ,
premium : premium ,
premiere_timestamp : premiere_timestamp ,
} )
elsif i = item [ " channelRenderer " ]?
author = i [ " title " ] [ " simpleText " ]? . try & . as_s || author_fallback || " "
author_id = i [ " channelId " ]? . try & . as_s || author_id_fallback || " "
author_thumbnail = i [ " thumbnail " ] [ " thumbnails " ]? . try & . as_a [ 0 ]? . try { | u | " https: #{ u [ " url " ] } " } || " "
subscriber_count = i [ " subscriberCountText " ]? . try & . [ " simpleText " ]? . try & . as_s . try { | s | short_text_to_number ( s . split ( " " ) [ 0 ] ) } || 0
auto_generated = false
auto_generated = true if ! i [ " videoCountText " ]?
video_count = i [ " videoCountText " ]? . try & . [ " runs " ] . as_a [ 0 ]? . try & . [ " text " ] . as_s . gsub ( / \ D / , " " ) . to_i || 0
description_html = i [ " descriptionSnippet " ]? . try { | t | parse_content ( t ) } || " "
items << SearchChannel . new ( {
author : author ,
ucid : author_id ,
author_thumbnail : author_thumbnail ,
subscriber_count : subscriber_count ,
video_count : video_count ,
description_html : description_html ,
auto_generated : auto_generated ,
} )
elsif i = item [ " gridPlaylistRenderer " ]?
title = i [ " title " ] [ " runs " ] . as_a [ 0 ]? . try & . [ " text " ] . as_s || " "
plid = i [ " playlistId " ]? . try & . as_s || " "
video_count = i [ " videoCountText " ] [ " runs " ] . as_a [ 0 ]? . try & . [ " text " ] . as_s . gsub ( / \ D / , " " ) . to_i || 0
playlist_thumbnail = i [ " thumbnail " ] [ " thumbnails " ] [ 0 ]? . try & . [ " url " ]? . try & . as_s || " "
items << SearchPlaylist . new ( {
title : title ,
id : plid ,
author : author_fallback || " " ,
ucid : author_id_fallback || " " ,
video_count : video_count ,
videos : [ ] of SearchPlaylistVideo ,
thumbnail : playlist_thumbnail ,
} )
elsif i = item [ " playlistRenderer " ]?
title = i [ " title " ] [ " simpleText " ]? . try & . as_s || " "
plid = i [ " playlistId " ]? . try & . as_s || " "
video_count = i [ " videoCount " ]? . try & . as_s . to_i || 0
playlist_thumbnail = i [ " thumbnails " ] . as_a [ 0 ]? . try & . [ " thumbnails " ]? . try & . as_a [ 0 ]? . try & . [ " url " ] . as_s || " "
author_info = i [ " shortBylineText " ]? . try & . [ " runs " ] . as_a [ 0 ]?
author = author_info . try & . [ " text " ] . as_s || author_fallback || " "
author_id = author_info . try & . [ " navigationEndpoint " ]? . try & . [ " browseEndpoint " ] [ " browseId " ] . as_s || author_id_fallback || " "
videos = i [ " videos " ]? . try & . as_a . map do | v |
v = v [ " childVideoRenderer " ]
v_title = v [ " title " ] [ " simpleText " ]? . try & . as_s || " "
v_id = v [ " videoId " ]? . try & . as_s || " "
v_length_seconds = v [ " lengthText " ]? . try & . [ " simpleText " ]? . try { | t | decode_length_seconds ( t . as_s ) } || 0
SearchPlaylistVideo . new ( {
title : v_title ,
id : v_id ,
length_seconds : v_length_seconds ,
} )
end || [ ] of SearchPlaylistVideo
# TODO: i["publishedTimeText"]?
items << SearchPlaylist . new ( {
title : title ,
id : plid ,
author : author ,
ucid : author_id ,
video_count : video_count ,
videos : videos ,
thumbnail : playlist_thumbnail ,
} )
elsif i = item [ " radioRenderer " ]? # Mix
# TODO
elsif i = item [ " showRenderer " ]? # Show
# TODO
elsif i = item [ " shelfRenderer " ]?
elsif i = item [ " horizontalCardListRenderer " ]?
elsif i = item [ " searchPyvRenderer " ]? # Ad
end
} }
channel_v2_response = initial_data
. try & . [ " response " ]?
. try & . [ " continuationContents " ]?
. try & . [ " gridContinuation " ]?
. try & . [ " items " ]?
if channel_v2_response
channel_v2_response . try & . as_a . each { | item |
extract_item ( item , author_fallback , author_id_fallback )
. try { | t | items << t }
}
else
initial_data . try { | t | t [ " contents " ]? || t [ " response " ]? }
. try { | t | t [ " twoColumnBrowseResultsRenderer " ]? . try & . [ " tabs " ] . as_a . select ( & . [ " tabRenderer " ]? . try & . [ " selected " ] . as_bool ) [ 0 ]? . try & . [ " tabRenderer " ] [ " content " ] ||
t [ " twoColumnSearchResultsRenderer " ]? . try & . [ " primaryContents " ] ||
t [ " continuationContents " ]? }
. try { | t | t [ " sectionListRenderer " ]? || t [ " sectionListContinuation " ]? }
. try & . [ " contents " ] . as_a
. each { | c | c . try & . [ " itemSectionRenderer " ]? . try & . [ " contents " ] . as_a
. try { | t | t [ 0 ]? . try & . [ " shelfRenderer " ]? . try & . [ " content " ] [ " expandedShelfContentsRenderer " ]? . try & . [ " items " ] . as_a ||
t [ 0 ]? . try & . [ " gridRenderer " ]? . try & . [ " items " ] . as_a || t }
. each { | item |
extract_item ( item , author_fallback , author_id_fallback )
. try { | t | items << t }
} }
end
items
end