Skip to content

Commit

Permalink
Add support for viewing movie trailers with local api (#4391)
Browse files Browse the repository at this point in the history
* Add support for viewing movie trailers with local api

* add support for age restricted trailers

* remove unused changes

* always show trailer, regardless of video playability status

* Improve movie parsing logic

Co-authored-by: absidue <[email protected]>

* check for null instead of truthy

* Exclude unneeded properties

Co-authored-by: absidue <[email protected]>

---------

Co-authored-by: absidue <[email protected]>
  • Loading branch information
ChunkyProgrammer and absidue authored Jan 30, 2024
1 parent 057c9d7 commit c983e02
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 23 deletions.
5 changes: 4 additions & 1 deletion src/renderer/components/ft-list-video/ft-list-video.vue
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,10 @@
>
<span>{{ channelName }}</span>
</router-link>
<template v-if="!isLive && !isUpcoming && !isPremium && !hideViews">
<span v-else-if="channelName !== null">
{{ channelName }}
</span>
<template v-if="!isLive && !isUpcoming && !isPremium && !hideViews && viewCount != null">
<span class="viewCount">
<template v-if="channelId !== null"> • </template>
{{ $tc('Global.Counts.View Count', viewCount, {count: parsedViewCount}) }}
Expand Down
56 changes: 38 additions & 18 deletions src/renderer/helpers/api/local.js
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,7 @@ function handleSearchResponse(response) {

const results = response.results
.filter((item) => {
return item.type === 'Video' || item.type === 'Channel' || item.type === 'Playlist' || item.type === 'HashtagTile'
return item.type === 'Video' || item.type === 'Channel' || item.type === 'Playlist' || item.type === 'HashtagTile' || item.type === 'Movie'
})
.map((item) => parseListItem(item))

Expand Down Expand Up @@ -537,22 +537,41 @@ export function parseLocalPlaylistVideo(video) {
}

/**
* @param {import('youtubei.js').YTNodes.Video} video
* @param {import('youtubei.js').YTNodes.Video | import('youtubei.js').YTNodes.Movie} item
*/
export function parseLocalListVideo(video) {
return {
type: 'video',
videoId: video.id,
title: video.title.text,
author: video.author.name,
authorId: video.author.id,
description: video.description,
viewCount: extractNumberFromString(video.view_count.text),
publishedText: video.published.isEmpty() ? null : video.published.text,
lengthSeconds: isNaN(video.duration.seconds) ? '' : video.duration.seconds,
liveNow: video.is_live,
isUpcoming: video.is_upcoming || video.is_premiere,
premiereDate: video.upcoming
export function parseLocalListVideo(item) {
if (item.type === 'Movie') {
/** @type {import('youtubei.js').YTNodes.Movie} */
const movie = item

return {
type: 'video',
videoId: movie.id,
title: movie.title.text,
author: movie.author.name,
authorId: movie.author.id !== 'N/A' ? movie.author.id : null,
description: movie.description_snippet?.text,
lengthSeconds: isNaN(movie.duration.seconds) ? '' : movie.duration.seconds,
liveNow: false,
isUpcoming: false,
}
} else {
/** @type {import('youtubei.js').YTNodes.Video} */
const video = item
return {
type: 'video',
videoId: video.id,
title: video.title.text,
author: video.author.name,
authorId: video.author.id,
description: video.description,
viewCount: video.view_count == null ? null : extractNumberFromString(video.view_count.text),
publishedText: (video.published == null || video.published.isEmpty()) ? null : video.published.text,
lengthSeconds: isNaN(video.duration.seconds) ? '' : video.duration.seconds,
liveNow: video.is_live,
isUpcoming: video.is_upcoming || video.is_premiere,
premiereDate: video.upcoming
}
}
}

Expand All @@ -561,6 +580,7 @@ export function parseLocalListVideo(video) {
*/
function parseListItem(item) {
switch (item.type) {
case 'Movie':
case 'Video':
return parseLocalListVideo(item)
case 'Channel': {
Expand Down Expand Up @@ -627,8 +647,8 @@ export function parseLocalWatchNextVideo(video) {
title: video.title.text,
author: video.author.name,
authorId: video.author.id,
viewCount: extractNumberFromString(video.view_count.text),
publishedText: video.published.isEmpty() ? null : video.published.text,
viewCount: video.view_count == null ? null : extractNumberFromString(video.view_count.text),
publishedText: (video.published == null || video.published.isEmpty()) ? null : video.published.text,
lengthSeconds: isNaN(video.duration.seconds) ? '' : video.duration.seconds,
liveNow: video.is_live,
isUpcoming: video.is_premiere
Expand Down
26 changes: 22 additions & 4 deletions src/renderer/views/Watch/Watch.js
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ export default defineComponent({
this.isFamilyFriendly = result.basic_info.is_family_safe

const recommendedVideos = result.watch_next_feed
?.filter((item) => item.type === 'CompactVideo')
?.filter((item) => item.type === 'CompactVideo' || item.type === 'CompactMovie')
.map(parseLocalWatchNextVideo) ?? []

// place watched recommended videos last
Expand All @@ -335,10 +335,28 @@ export default defineComponent({

let playabilityStatus = result.playability_status
let bypassedResult = null
if (playabilityStatus.status === 'LOGIN_REQUIRED') {
let streamingVideoId = this.videoId
let trailerIsNull = false

// if widevine support is added then we should check if playabilityStatus.status is UNPLAYABLE too
if (result.has_trailer) {
bypassedResult = result.getTrailerInfo()
/**
* @type {import ('youtubei.js').YTNodes.PlayerLegacyDesktopYpcTrailer}
*/
const trailerScreen = result.playability_status.error_screen
streamingVideoId = trailerScreen.video_id
// if the trailer is null then it is likely age restricted.
trailerIsNull = bypassedResult == null
if (!trailerIsNull) {
playabilityStatus = bypassedResult.playability_status
}
}

if (playabilityStatus.status === 'LOGIN_REQUIRED' || trailerIsNull) {
// try to bypass the age restriction
bypassedResult = await getLocalVideoInfo(this.videoId, true)
playabilityStatus = result.playability_status
bypassedResult = await getLocalVideoInfo(streamingVideoId, true)
playabilityStatus = bypassedResult.playability_status
}

if (playabilityStatus.status === 'UNPLAYABLE') {
Expand Down

0 comments on commit c983e02

Please sign in to comment.