Skip to content

Commit d1b7b0e

Browse files
authored
[YoutubeBridge] handle new lockupViewModel layout (#4982)
* [YoutubeBridge] handle new lockupViewModel layout Feeds with duration_min or duration_max set return zero entries: YouTube changed the channel /videos listing to wrap each item in lockupViewModel instead of videoRenderer, and the bridge no longer recognises the shape. Items fall through with no readable duration, all parse to zero seconds, and the duration filter drops every one. This adds a branch for the new shape. A small wrapLockupViewModel helper picks out the video ID, title, and duration badge (e.g. 12:07) and packages them in a stdClass that matches the existing fields the rest of the loop reads. Description and timestamp aren't in lockupViewModel; fetchVideoDetails continues to fill those in. The existing richItemRenderer branch is also tightened to require an inner videoRenderer; without that, the new shape enters the branch and crashes on null->lengthText. * chore: add polybjorn to CONTRIBUTORS.md --------- Co-authored-by: Bjørn A. Andersen <polybjorn@users.noreply.github.com>
1 parent e884acb commit d1b7b0e

2 files changed

Lines changed: 35 additions & 1 deletion

File tree

CONTRIBUTORS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@
165165
* [pitchoule](https://github.com/pitchoule)
166166
* [pmaziere](https://github.com/pmaziere)
167167
* [Pofilo](https://github.com/Pofilo)
168+
* [polybjorn](https://github.com/polybjorn)
168169
* [prysme01](https://github.com/prysme01)
169170
* [pubak42](https://github.com/pubak42)
170171
* [Qluxzz](https://github.com/Qluxzz)

bridges/YoutubeBridge.php

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -441,8 +441,14 @@ private function fetchItemsFromFromJsonData($jsonData)
441441
$wrapper = $item->videoRenderer;
442442
} elseif (isset($item->playlistVideoRenderer)) {
443443
$wrapper = $item->playlistVideoRenderer;
444-
} elseif (isset($item->richItemRenderer)) {
444+
} elseif (isset($item->richItemRenderer->content->videoRenderer)) {
445445
$wrapper = $item->richItemRenderer->content->videoRenderer;
446+
} elseif (isset($item->richItemRenderer->content->lockupViewModel)) {
447+
// Newer YouTube layout: richItemRenderer can wrap a lockupViewModel rather than a videoRenderer.
448+
$wrapper = $this->wrapLockupViewModel($item->richItemRenderer->content->lockupViewModel);
449+
if ($wrapper === null) {
450+
continue;
451+
}
446452
} else {
447453
continue;
448454
}
@@ -503,6 +509,33 @@ private function fetchItemsFromFromJsonData($jsonData)
503509
}
504510
}
505511

512+
private function wrapLockupViewModel($lockup)
513+
{
514+
$videoId = $lockup->contentId ?? null;
515+
$title = $lockup->metadata->lockupMetadataViewModel->title->content ?? null;
516+
if (!$videoId || !$title) {
517+
return null;
518+
}
519+
520+
$wrapper = new \stdClass();
521+
$wrapper->videoId = $videoId;
522+
$wrapper->title = (object) ['runs' => [(object) ['text' => $title]]];
523+
$wrapper->thumbnailOverlays = [];
524+
525+
// Duration sits on a thumbnail badge such as "12:07".
526+
foreach ($lockup->contentImage->thumbnailViewModel->overlays ?? [] as $overlay) {
527+
foreach ($overlay->thumbnailBottomOverlayViewModel->badges ?? [] as $badge) {
528+
$text = $badge->thumbnailBadgeViewModel->text ?? null;
529+
if (is_string($text) && preg_match('/^\d{1,2}(:\d{2}){1,2}$/', $text)) {
530+
$wrapper->lengthText = (object) ['simpleText' => $text];
531+
break 2;
532+
}
533+
}
534+
}
535+
536+
return $wrapper;
537+
}
538+
506539
private function addItem($videoId, $title, $author, $description, $timestamp, $thumbnail = '')
507540
{
508541
$description = nl2br($description);

0 commit comments

Comments
 (0)