fix: handle collaboratively authored videos in playlists and videos

endpoint
currently, the videos endpoint returns the video uploader name,
and not "author1, author2, author3" as is the case for videoRenderer
and playlistVideoRenderer - this might change in the future in order for
the endpoints to return the same data
This commit is contained in:
2025-09-28 05:02:51 +02:00
parent f63c620541
commit 34e00e2492
2 changed files with 23 additions and 5 deletions

View File

@@ -454,11 +454,15 @@ def videos(data):
author = safeTraverse(video_details, ['author'], default="Unknown Author")
ucid = safeTraverse(video_details, ['channelId'], default="UNKNOWNCHANNELID")
subs = ydata['channel_follower_count']
author_thumbnail = ythdd_extractor.generateChannelAvatarsFromUrl(safeTraverse(video_secondary_renderer, ['owner', 'videoOwnerRenderer', 'thumbnail', 'thumbnails', 0, 'url'], default=DEFAULT_AVATAR))
# so far it seems to be impossible to tell if a channel is verified or not,
# that is - without making another request
author_thumbnail = safeTraverse(video_secondary_renderer, ['owner', 'videoOwnerRenderer', 'thumbnail', 'thumbnails', 0, 'url'])
author_verified = ythdd_extractor.isVerified(safeTraverse(video_secondary_renderer, ['owner', 'videoOwnerRenderer', 'badges', 0], default=[]))
if author_thumbnail is None:
# there might be multiple authors (on a collaborative video)
# if so, then try to extract first channel's (uploader's) avatar
livm = safeTraverse(video_secondary_renderer, ["owner", "videoOwnerRenderer", "attributedTitle", "commandRuns", 0, "onTap", "innertubeCommand", "showDialogCommand", "panelLoadingStrategy", "inlineContent", "dialogViewModel", "customContent", "listViewModel", "listItems"], default=[])
author_thumbnail = safeTraverse(livm, [0, "listItemViewModel", "leadingAccessory", "avatarViewModel", "image", "sources", 0, "url"], default=DEFAULT_AVATAR)
author_verified = author_verified or safeTraverse(livm, [0, "listItemViewModel", "title", "attachmentRuns", 0, "element", "type", "imageType", "image", "sources", 0, "clientResource", "imageName"]) in ("AUDIO_BADGE", "CHECK_CIRCLE_FILLED")
author_thumbnail = ythdd_extractor.generateChannelAvatarsFromUrl(author_thumbnail)
format_streams = []
# adaptive_formats, format_streams = rebuildFormats(adaptive_formats)

View File

@@ -346,7 +346,7 @@ def parseRenderers(entry: dict, context: dict = {}) -> dict:
video_id = safeTraverse(entry, ["playlistVideoRenderer", "videoId"], default="UnknownVideoId")
title = safeTraverse(entry, ["playlistVideoRenderer", "title", "runs", 0, "text"], default="Unknown video title")
author_ucid = safeTraverse(entry, ["playlistVideoRenderer", "shortBylineText", "runs", 0, "navigationEndpoint", "browseEndpoint", "browseId"], default="UNKNOWNCHANNELID")
author_ucid = safeTraverse(entry, ["playlistVideoRenderer", "shortBylineText", "runs", 0, "navigationEndpoint", "browseEndpoint", "browseId"])
author_name = safeTraverse(entry, ["playlistVideoRenderer", "shortBylineText", "runs", 0, "text"], default="Unknown author")
video_index = int(safeTraverse(entry, ["playlistVideoRenderer", "index", "simpleText"], default="1")) - 1
length = parseLengthFromTimeBadge(safeTraverse(entry, ["playlistVideoRenderer", "lengthText", "simpleText"], default="0:0"))
@@ -357,6 +357,20 @@ def parseRenderers(entry: dict, context: dict = {}) -> dict:
if not published_date:
published_date = "now"
if author_ucid is None:
# likely a collaborative video, let's try
# to fetch the uploader's ucid with that in mind
livm = safeTraverse(entry, ["playlistVideoRenderer", "shortBylineText", "runs", 0, "navigationEndpoint", "showDialogCommand", "panelLoadingStrategy", "inlineContent", "dialogViewModel", "customContent", "listViewModel", "listItems"], default=[])
# name extraction logic the same as in videoRenderer
all_authors = []
for collaborative_author in livm:
collaborative_author_name = safeTraverse(collaborative_author, ["listItemViewModel", "title", "content"])
if collaborative_author_name is not None:
all_authors.append(collaborative_author_name)
if all_authors != []:
author_name = ", ".join(all_authors)
author_ucid = safeTraverse(livm, [0, "listItemViewModel", "title", "commandRuns", 0, "onTap", "innertubeCommand", "browseEndpoint", "browseId"], default="UNKNOWNCHANNELID")
return {
"type": "video",
"title": title,