diff --git a/src/invidious/channels/about.cr b/src/invidious/channels/about.cr index da71e9a8c..565f2bcac 100644 --- a/src/invidious/channels/about.cr +++ b/src/invidious/channels/about.cr @@ -61,6 +61,7 @@ def get_about_info(ucid, locale) : AboutChannel author = initdata["metadata"]["channelMetadataRenderer"]["title"].as_s author_url = initdata["metadata"]["channelMetadataRenderer"]["channelUrl"].as_s author_thumbnail = initdata["metadata"]["channelMetadataRenderer"]["avatar"]["thumbnails"][0]["url"].as_s + author_verified = has_verified_badge?(initdata.dig?("header", "c4TabbedHeaderRenderer", "badges")) ucid = initdata["metadata"]["channelMetadataRenderer"]["externalId"].as_s @@ -71,9 +72,6 @@ def get_about_info(ucid, locale) : AboutChannel # if banner.includes? "channels/c4/default_banner" # banner = nil # end - # author_verified_badges = initdata["header"]?.try &.["c4TabbedHeaderRenderer"]?.try &.["badges"]? - author_verified_badge = initdata["header"].dig?("c4TabbedHeaderRenderer", "badges", 0, "metadataBadgeRenderer", "tooltip") - author_verified = (author_verified_badge && author_verified_badge == "Verified") description_node = initdata["metadata"]["channelMetadataRenderer"]?.try &.["description"]? diff --git a/src/invidious/routes/feeds.cr b/src/invidious/routes/feeds.cr index b5b583996..2e6043f7c 100644 --- a/src/invidious/routes/feeds.cr +++ b/src/invidious/routes/feeds.cr @@ -182,7 +182,7 @@ module Invidious::Routes::Feeds paid: false, premium: false, premiere_timestamp: nil, - author_verified: false, # ¯\_(ツ)_/¯ + author_verified: false, }) end diff --git a/src/invidious/videos.cr b/src/invidious/videos.cr index f65b05bb5..8ba667dbd 100644 --- a/src/invidious/videos.cr +++ b/src/invidious/videos.cr @@ -868,11 +868,7 @@ def parse_related_video(related : JSON::Any) : Hash(String, JSON::Any)? .try &.dig?("runs", 0) author = channel_info.try &.dig?("text") - author_verified_badge = related["ownerBadges"]?.try do |badges_array| - badges_array.as_a.find(&.dig("metadataBadgeRenderer", "tooltip").as_s.== "Verified") - end - - author_verified = (author_verified_badge && author_verified_badge.size > 0).to_s + author_verified = has_verified_badge?(related["ownerBadges"]?) ucid = channel_info.try { |ci| HelperExtractors.get_browse_id(ci) } @@ -1089,17 +1085,19 @@ def extract_video_info(video_id : String, proxy_region : String? = nil, context_ # Author infos - author_info = video_secondary_renderer.try &.dig?("owner", "videoOwnerRenderer") - author_thumbnail = author_info.try &.dig?("thumbnail", "thumbnails", 0, "url") + if author_info = video_secondary_renderer.try &.dig?("owner", "videoOwnerRenderer") + author_thumbnail = author_info.dig?("thumbnail", "thumbnails", 0, "url") + params["authorThumbnail"] = JSON::Any.new(author_thumbnail.try &.as_s || "") - author_verified_badge = author_info.try &.dig?("badges", 0, "metadataBadgeRenderer", "tooltip") - author_verified = (!author_verified_badge.nil? && author_verified_badge == "Verified") - params["authorVerified"] = JSON::Any.new(author_verified) + author_verified = has_verified_badge?(author_info["badges"]?) + params["authorVerified"] = JSON::Any.new(author_verified) - params["authorThumbnail"] = JSON::Any.new(author_thumbnail.try &.as_s || "") + subs_text = author_info["subscriberCountText"]? + .try { |t| t["simpleText"]? || t.dig?("runs", 0, "text") } + .try &.as_s.split(" ", 2)[0] - params["subCountText"] = JSON::Any.new(author_info.try &.["subscriberCountText"]? - .try { |t| t["simpleText"]? || t.dig?("runs", 0, "text") }.try &.as_s.split(" ", 2)[0] || "-") + params["subCountText"] = JSON::Any.new(subs_text || "-") + end # Return data diff --git a/src/invidious/yt_backend/extractors.cr b/src/invidious/yt_backend/extractors.cr index 7e7cf85b4..f394da843 100644 --- a/src/invidious/yt_backend/extractors.cr +++ b/src/invidious/yt_backend/extractors.cr @@ -60,6 +60,8 @@ private module Parsers author_id = author_fallback.id end + author_verified = has_verified_badge?(item_contents["ownerBadges"]?) + # For live videos (and possibly recently premiered videos) there is no published information. # Instead, in its place is the amount of people currently watching. This behavior should be replicated # on Invidious once all features of livestreams are supported. On an unrelated note, defaulting to the current @@ -105,11 +107,7 @@ private module Parsers premium = false premiere_timestamp = item_contents.dig?("upcomingEventData", "startTime").try { |t| Time.unix(t.as_s.to_i64) } - author_verified_badge = item_contents["ownerBadges"]?.try do |badges_array| - badges_array.as_a.find(&.dig("metadataBadgeRenderer", "tooltip").as_s.== "Verified") - end - author_verified = (author_verified_badge && author_verified_badge.size > 0) item_contents["badges"]?.try &.as_a.each do |badge| b = badge["metadataBadgeRenderer"] case b["label"].as_s @@ -136,7 +134,7 @@ private module Parsers live_now: live_now, premium: premium, premiere_timestamp: premiere_timestamp, - author_verified: author_verified || false, + author_verified: author_verified, }) end @@ -164,12 +162,9 @@ private module Parsers private def self.parse(item_contents, author_fallback) author = extract_text(item_contents["title"]) || author_fallback.name author_id = item_contents["channelId"]?.try &.as_s || author_fallback.id - author_verified_badge = item_contents["ownerBadges"]?.try do |badges_array| - badges_array.as_a.find(&.dig("metadataBadgeRenderer", "tooltip").as_s.== "Verified") - end - - author_verified = (author_verified_badge && author_verified_badge.size > 0) + author_verified = has_verified_badge?(item_contents["ownerBadges"]?) author_thumbnail = HelperExtractors.get_thumbnails(item_contents) + # When public subscriber count is disabled, the subscriberCountText isn't sent by InnerTube. # Always simpleText # TODO change default value to nil @@ -191,7 +186,7 @@ private module Parsers video_count: video_count, description_html: description_html, auto_generated: auto_generated, - author_verified: author_verified || false, + author_verified: author_verified, }) end @@ -219,11 +214,9 @@ private module Parsers private def self.parse(item_contents, author_fallback) title = extract_text(item_contents["title"]) || "" plid = item_contents["playlistId"]?.try &.as_s || "" - author_verified_badge = item_contents["ownerBadges"]?.try do |badges_array| - badges_array.as_a.find(&.dig("metadataBadgeRenderer", "tooltip").as_s.== "Verified") - end - author_verified = (author_verified_badge && author_verified_badge.size > 0) + author_verified = has_verified_badge?(item_contents["ownerBadges"]?) + video_count = HelperExtractors.get_video_count(item_contents) playlist_thumbnail = HelperExtractors.get_thumbnails(item_contents) @@ -235,7 +228,7 @@ private module Parsers video_count: video_count, videos: [] of SearchPlaylistVideo, thumbnail: playlist_thumbnail, - author_verified: author_verified || false, + author_verified: author_verified, }) end @@ -269,11 +262,8 @@ private module Parsers author_info = item_contents.dig?("shortBylineText", "runs", 0) author = author_info.try &.["text"].as_s || author_fallback.name author_id = author_info.try { |x| HelperExtractors.get_browse_id(x) } || author_fallback.id - author_verified_badge = item_contents["ownerBadges"]?.try do |badges_array| - badges_array.as_a.find(&.dig("metadataBadgeRenderer", "tooltip").as_s.== "Verified") - end + author_verified = has_verified_badge?(item_contents["ownerBadges"]?) - author_verified = (author_verified_badge && author_verified_badge.size > 0) videos = item_contents["videos"]?.try &.as_a.map do |v| v = v["childVideoRenderer"] v_title = v.dig?("title", "simpleText").try &.as_s || "" @@ -296,7 +286,7 @@ private module Parsers video_count: video_count, videos: videos, thumbnail: playlist_thumbnail, - author_verified: author_verified || false, + author_verified: author_verified, }) end diff --git a/src/invidious/yt_backend/extractors_utils.cr b/src/invidious/yt_backend/extractors_utils.cr index add5f488b..3d5e57876 100644 --- a/src/invidious/yt_backend/extractors_utils.cr +++ b/src/invidious/yt_backend/extractors_utils.cr @@ -29,6 +29,45 @@ def extract_text(item : JSON::Any?) : String? end end +# Check if an "ownerBadges" or a "badges" element contains a verified badge. +# There is currently two known types of verified badges: +# +# "ownerBadges": [{ +# "metadataBadgeRenderer": { +# "icon": { "iconType": "CHECK_CIRCLE_THICK" }, +# "style": "BADGE_STYLE_TYPE_VERIFIED", +# "tooltip": "Verified", +# "accessibilityData": { "label": "Verified" } +# } +# }], +# +# "ownerBadges": [{ +# "metadataBadgeRenderer": { +# "icon": { "iconType": "OFFICIAL_ARTIST_BADGE" }, +# "style": "BADGE_STYLE_TYPE_VERIFIED_ARTIST", +# "tooltip": "Official Artist Channel", +# "accessibilityData": { "label": "Official Artist Channel" } +# } +# }], +# +def has_verified_badge?(badges : JSON::Any?) + return false if badges.nil? + + badges.as_a.each do |badge| + style = badge.dig("metadataBadgeRenderer", "style").as_s + + return true if style == "BADGE_STYLE_TYPE_VERIFIED" + return true if style == "BADGE_STYLE_TYPE_VERIFIED_ARTIST" + end + + return false +rescue ex + LOGGER.debug("Unable to parse owner badges. Got exception: #{ex.message}") + LOGGER.trace("Owner badges data: #{badges.to_json}") + + return false +end + def extract_videos(initial_data : Hash(String, JSON::Any), author_fallback : String? = nil, author_id_fallback : String? = nil) extracted = extract_items(initial_data, author_fallback, author_id_fallback)