mirror of https://github.com/VueTubeApp/VueTube
Merge pull request #169 from 404-Program-not-found/main
Reworked Video info to use the /watch and /next endpoint, reworked gridVideoRenderer render method
This commit is contained in:
commit
9cb862a2b3
|
@ -0,0 +1,46 @@
|
||||||
|
<template>
|
||||||
|
<v-card class="entry compactVideoRenderer" :to="`/watch?v=${video.id}`">
|
||||||
|
<v-card-text>
|
||||||
|
<div style="position: relative">
|
||||||
|
<v-img :src="video.thumbnail" />
|
||||||
|
<div
|
||||||
|
class="videoRuntimeFloat"
|
||||||
|
style="color: #fff"
|
||||||
|
v-text="video.metadata.overlay[0]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div style="margin-top: 0.5em" v-text="video.title" />
|
||||||
|
<div v-text="parseBottom(video)" />
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.entry {
|
||||||
|
width: 100%; /* Prevent Loading Weirdness */
|
||||||
|
}
|
||||||
|
.videoRuntimeFloat {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 10px;
|
||||||
|
right: 10px;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 0px 4px 0px 4px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
video: Object,
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
parseBottom(video) {
|
||||||
|
const bottomText = [video.channel, video.metadata.views];
|
||||||
|
if (video.metadata.published) bottomText.push(video.metadata.published);
|
||||||
|
return bottomText.join(" • ");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,113 @@
|
||||||
|
<template>
|
||||||
|
<v-card
|
||||||
|
class="entry gridVideoRenderer background"
|
||||||
|
:to="`/watch?v=${video.videoId}`"
|
||||||
|
flat
|
||||||
|
>
|
||||||
|
<div style="position: relative">
|
||||||
|
<v-img
|
||||||
|
:aspect-ratio="16 / 9"
|
||||||
|
:src="$youtube.getThumbnail(video.videoId, 'max')"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="videoRuntimeFloat"
|
||||||
|
:class="
|
||||||
|
'style-' +
|
||||||
|
video.thumbnailOverlays[0].thumbnailOverlayTimeStatusRenderer.style
|
||||||
|
"
|
||||||
|
style="color: #fff"
|
||||||
|
v-text="
|
||||||
|
video.thumbnailOverlays[0].thumbnailOverlayTimeStatusRenderer.text
|
||||||
|
.runs[0].text
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div id="details">
|
||||||
|
<a
|
||||||
|
:href="
|
||||||
|
video.shortBylineText.runs[0].navigationEndpoint.browseEndpoint
|
||||||
|
.canonicalBaseUrl
|
||||||
|
"
|
||||||
|
class="avatar-link pt-2"
|
||||||
|
>
|
||||||
|
<v-img
|
||||||
|
class="avatar-thumbnail"
|
||||||
|
:src="video.channelThumbnail.thumbnails[0].url"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<v-card-text class="pt-2">
|
||||||
|
<div
|
||||||
|
v-for="title in video.title.runs"
|
||||||
|
:key="title.text"
|
||||||
|
style="margin-top: 0.5em"
|
||||||
|
class="font-weight-medium vid-title"
|
||||||
|
>
|
||||||
|
{{ title.text }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grey--text caption" v-text="parseBottom(video)" />
|
||||||
|
</v-card-text>
|
||||||
|
</div>
|
||||||
|
</v-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.entry {
|
||||||
|
width: 100%; /* Prevent Loading Weirdness */
|
||||||
|
}
|
||||||
|
.videoRuntimeFloat {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 10px;
|
||||||
|
right: 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 0px 4px 0px 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.videoRuntimeFloat.style-DEFAULT {
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.videoRuntimeFloat.style-LIVE {
|
||||||
|
background: rgba(255, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vid-title {
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-thumbnail {
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 35px;
|
||||||
|
height: 35px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#details {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-basis: auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: ["video"],
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
parseBottom(video) {
|
||||||
|
const bottomText = [
|
||||||
|
video.shortBylineText?.runs[0].text,
|
||||||
|
video.shortViewCountText?.runs[0].text,
|
||||||
|
];
|
||||||
|
if (video.publishedTimeText?.runs[0].text)
|
||||||
|
bottomText.push(video.publishedTimeText?.runs[0].text);
|
||||||
|
return bottomText.join(" · ");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -6,42 +6,23 @@
|
||||||
<v-skeleton-loader type="card-avatar, article, actions" />
|
<v-skeleton-loader type="card-avatar, article, actions" />
|
||||||
</center>
|
</center>
|
||||||
|
|
||||||
<v-list-item v-for="(video, index) in recommends" :key="index">
|
<v-list-item v-for="(video, index) in recommends" :key="index" class="pa-0">
|
||||||
<v-card class="entry" :to="`/watch?v=${video.id}`">
|
<component
|
||||||
<v-card-text>
|
:is="Object.keys(video)[0]"
|
||||||
<div style="position: relative">
|
:key="video[Object.keys(video)[0]].videoId"
|
||||||
<v-img :src="video.thumbnail" />
|
:video="video[Object.keys(video)[0]]"
|
||||||
<div
|
></component>
|
||||||
class="videoRuntimeFloat"
|
|
||||||
style="color: #fff"
|
|
||||||
v-text="video.metadata.overlay[0]"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div style="margin-top: 0.5em" v-text="video.title" />
|
|
||||||
<div v-text="parseBottom(video)" />
|
|
||||||
</v-card-text>
|
|
||||||
</v-card>
|
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.entry {
|
|
||||||
margin-top: 1em;
|
|
||||||
width: 100%; /* Prevent Loading Weirdness */
|
|
||||||
}
|
|
||||||
.videoRuntimeFloat {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 10px;
|
|
||||||
right: 10px;
|
|
||||||
background: rgba(0, 0, 0, 0.5);
|
|
||||||
border-radius: 5px;
|
|
||||||
padding: 0 3px 0 3px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import gridVideoRenderer from "./VideoRenderers/gridVideoRenderer.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
components: {
|
||||||
|
gridVideoRenderer,
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
recommends: Array,
|
recommends: Array,
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,13 +1,27 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<video controls autoplay :src="vidSrc" width="100%" style="max-height: 50vh" />
|
<video
|
||||||
|
controls
|
||||||
|
autoplay
|
||||||
|
:src="vidSrc"
|
||||||
|
width="100%"
|
||||||
|
style="max-height: 50vh"
|
||||||
|
/>
|
||||||
<v-card v-if="loaded" class="ml-2 mr-2 background" flat>
|
<v-card v-if="loaded" class="ml-2 mr-2 background" flat>
|
||||||
<v-card-title class="mt-2"
|
<v-card-title
|
||||||
style="padding-top: 0; padding-bottom: 0; font-size: 0.95rem; line-height: 1rem;"
|
class="mt-2"
|
||||||
|
style="
|
||||||
|
padding-top: 0;
|
||||||
|
padding-bottom: 0;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
line-height: 1rem;
|
||||||
|
"
|
||||||
v-text="title"
|
v-text="title"
|
||||||
/>
|
/>
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
<div style="margin-bottom: 1rem;">{{ views }} views • {{ uploaded }}</div>
|
<div style="margin-bottom: 1rem">
|
||||||
|
{{ views }} views • {{ uploaded }}
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Scrolling Div For Interactions --->
|
<!-- Scrolling Div For Interactions --->
|
||||||
<div style="display: flex; margin-bottom: 1em">
|
<div style="display: flex; margin-bottom: 1em">
|
||||||
|
@ -25,7 +39,11 @@
|
||||||
@click="callMethodByName(item.actionName)"
|
@click="callMethodByName(item.actionName)"
|
||||||
>
|
>
|
||||||
<v-icon v-text="item.icon" />
|
<v-icon v-text="item.icon" />
|
||||||
<div class="mt-2" style="font-size: .66rem;" v-text="item.value || item.name" />
|
<div
|
||||||
|
class="mt-2"
|
||||||
|
style="font-size: 0.66rem"
|
||||||
|
v-text="item.value || item.name"
|
||||||
|
/>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
|
|
||||||
|
@ -131,7 +149,9 @@ export default {
|
||||||
console.log("Video info data", result);
|
console.log("Video info data", result);
|
||||||
console.log(result.availableResolutions);
|
console.log(result.availableResolutions);
|
||||||
this.vidSrc =
|
this.vidSrc =
|
||||||
result.availableResolutions[result.availableResolutions.length - 1].url; // Takes the highest available resolution with both video and Audio. Note this will be lower than the actual highest resolution
|
result.availableResolutions[
|
||||||
|
result.availableResolutions.length - 1
|
||||||
|
].url; // Takes the highest available resolution with both video and Audio. Note this will be lower than the actual highest resolution
|
||||||
this.title = result.title;
|
this.title = result.title;
|
||||||
this.description = result.metadata.description; // While this works, I do recommend using the rendered description instead in the future as there are some things a pure string wouldn't work with
|
this.description = result.metadata.description; // While this works, I do recommend using the rendered description instead in the future as there are some things a pure string wouldn't work with
|
||||||
this.views = result.metadata.viewCount.toLocaleString();
|
this.views = result.metadata.viewCount.toLocaleString();
|
||||||
|
@ -140,11 +160,7 @@ export default {
|
||||||
this.interactions[0].value = result.metadata.likes;
|
this.interactions[0].value = result.metadata.likes;
|
||||||
this.loaded = true;
|
this.loaded = true;
|
||||||
|
|
||||||
this.recommends = this.$youtube
|
this.recommends = result.renderedData.recommendations;
|
||||||
.viewRecommends(result.renderedData.recommendations)
|
|
||||||
.filter((element) => {
|
|
||||||
return element !== undefined;
|
|
||||||
});
|
|
||||||
// .catch((error) => this.$logger("Watch", error, true));
|
// .catch((error) => this.$logger("Watch", error, true));
|
||||||
console.log("recommendations:", this.recommends);
|
console.log("recommendations:", this.recommends);
|
||||||
});
|
});
|
||||||
|
@ -165,10 +181,10 @@ export default {
|
||||||
await Share.share({
|
await Share.share({
|
||||||
title: this.title,
|
title: this.title,
|
||||||
text: this.title,
|
text: this.title,
|
||||||
url: 'https://youtu.be/' + this.$route.query.v,
|
url: "https://youtu.be/" + this.$route.query.v,
|
||||||
dialogTitle: 'Share video',
|
dialogTitle: "Share video",
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
// Watch for change in the route query string (in this case, ?v=xxxxxxxx to ?v=yyyyyyyy)
|
// Watch for change in the route query string (in this case, ?v=xxxxxxxx to ?v=yyyyyyyy)
|
||||||
|
@ -180,8 +196,8 @@ export default {
|
||||||
this.vidSrc = "";
|
this.vidSrc = "";
|
||||||
this.getVideo();
|
this.getVideo();
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -39,8 +39,6 @@ module.exports = {
|
||||||
"x-goog-visitor-id": info.visitorData || "",
|
"x-goog-visitor-id": info.visitorData || "",
|
||||||
"x-youtube-client-name": ytApiVal.CLIENTNAME,
|
"x-youtube-client-name": ytApiVal.CLIENTNAME,
|
||||||
"x-youtube-client-version": ytApiVal.VERSION,
|
"x-youtube-client-version": ytApiVal.VERSION,
|
||||||
"x-origin": info.originalUrl,
|
|
||||||
origin: info.originalUrl,
|
|
||||||
};
|
};
|
||||||
return headers;
|
return headers;
|
||||||
},
|
},
|
||||||
|
|
|
@ -109,33 +109,35 @@ class Innertube {
|
||||||
|
|
||||||
async getVidAsync(id) {
|
async getVidAsync(id) {
|
||||||
let data = { context: this.context, videoId: id };
|
let data = { context: this.context, videoId: id };
|
||||||
const response = await Http.get({
|
const responseNext = await Http.post({
|
||||||
url: `https://m.youtube.com/watch?v=${id}&pbj=1`,
|
url: `${constants.URLS.YT_BASE_API}/next?v=${id}`,
|
||||||
params: {},
|
data: data,
|
||||||
headers: Object.assign(this.header, {
|
headers: constants.INNERTUBE_HEADER(this.context.client),
|
||||||
referer: `https://m.youtube.com/watch?v=${id}`,
|
|
||||||
"x-youtube-client-name": constants.YT_API_VALUES.CLIENT_WEB,
|
|
||||||
"x-youtube-client-version": constants.YT_API_VALUES.VERSION_WEB,
|
|
||||||
}),
|
|
||||||
}).catch((error) => error);
|
}).catch((error) => error);
|
||||||
|
|
||||||
const responseMobile = await Http.post({
|
const response = await Http.post({
|
||||||
url: `${constants.URLS.YT_BASE_API}/player?key=${this.key}`,
|
url: `${constants.URLS.YT_BASE_API}/player?key=${this.key}`,
|
||||||
data: data,
|
data: data,
|
||||||
headers: constants.INNERTUBE_HEADER(this.context),
|
headers: constants.INNERTUBE_HEADER(this.context.client),
|
||||||
}).catch((error) => error);
|
}).catch((error) => error);
|
||||||
|
|
||||||
if (response instanceof Error)
|
if (response.error)
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
status_code: response.response.status,
|
status_code: response.status,
|
||||||
message: response.message,
|
message: response.message,
|
||||||
};
|
}
|
||||||
|
else if (responseNext.error)
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
status_code: responseNext.status,
|
||||||
|
message: responseNext.message,
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
status_code: response.status,
|
status_code: response.status,
|
||||||
data: { webOutput: response.data, appOutput: responseMobile.data },
|
data: { output: response.data, outputNext: responseNext.data },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,68 +152,57 @@ class Innertube {
|
||||||
let response = await this.getVidAsync(id);
|
let response = await this.getVidAsync(id);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
response.success &&
|
response.success == false ||
|
||||||
response.data.webOutput[2].playerResponse?.playabilityStatus?.status ==
|
response.data.output?.playabilityStatus?.status ==
|
||||||
("ERROR" || undefined)
|
("ERROR" || undefined)
|
||||||
)
|
)
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Could not get information for video: ${response[2].playerResponse?.playabilityStatus?.status} - ${response[2].playerResponse?.playabilityStatus?.reason}`
|
`Could not get information for video: ${response.status_code || response.data.output?.playabilityStatus?.status} - ${response.message || response.data.output?.playabilityStatus?.reason}`
|
||||||
);
|
);
|
||||||
const responseWeb = response.data.webOutput;
|
const responseInfo = response.data.output;
|
||||||
const responseApp = response.data.appOutput;
|
const responseNext = response.data.outputNext;
|
||||||
const details = responseWeb[2].playerResponse?.videoDetails;
|
const details = responseInfo.videoDetails;
|
||||||
const microformat =
|
// const columnUI =
|
||||||
responseWeb[2].playerResponse?.microformat?.playerMicroformatRenderer;
|
// responseInfo[3].response?.contents.singleColumnWatchNextResults?.results
|
||||||
const renderedPanels = responseWeb[3].response?.engagementPanels;
|
// ?.results;
|
||||||
const columnUI =
|
const resolutions = responseInfo.streamingData;
|
||||||
responseWeb[3].response?.contents.singleColumnWatchNextResults?.results
|
const columnUI = responseNext.contents.singleColumnWatchNextResults.results.results
|
||||||
?.results;
|
|
||||||
const resolutions = responseApp.streamingData;
|
|
||||||
|
|
||||||
console.log(columnUI.contents.length);
|
const vidData = {
|
||||||
|
|
||||||
return {
|
|
||||||
id: details.videoId,
|
id: details.videoId,
|
||||||
title: details.title || microformat.title?.runs[0].text,
|
title: details.title,
|
||||||
isLive:
|
isLive: details.isLiveContent,
|
||||||
details.isLiveContent ||
|
channelName: details.author,
|
||||||
microformat.liveBroadcastDetails?.isLiveNow ||
|
channelUrl: columnUI?.contents[0].slimVideoMetadataSectionRenderer?.contents.find(contents => contents.elementRenderer)?.newElement?.type?.componentType?.model?.channelBarModel?.videoChannelBarData
|
||||||
false,
|
?.onTap?.innertubeCommand?.browseEndpoint?.canonicalBaseUrl,
|
||||||
channelName: details.author || microformat.ownerChannelName,
|
channelImg: columnUI?.contents[0].slimVideoMetadataSectionRenderer?.contents.find(contents => contents.elementRenderer)?.newElement?.type?.componentType?.model?.channelBarModel?.videoChannelBarData
|
||||||
channelUrl: microformat.ownerProfileUrl,
|
?.avatar?.image?.sources[0].url,
|
||||||
availableResolutions: resolutions?.formats,
|
availableResolutions: resolutions?.formats,
|
||||||
availableResolutionsAdaptive: resolutions?.adaptiveFormats,
|
availableResolutionsAdaptive: resolutions?.adaptiveFormats,
|
||||||
metadata: {
|
metadata: {
|
||||||
description: microformat.description?.runs[0].text,
|
description: details.shortDescription,
|
||||||
descriptionShort: details.shortDescription,
|
thumbnails: details.thumbnails?.thumbnails,
|
||||||
thumbnails:
|
uploadDate: columnUI?.contents[0].slimVideoMetadataSectionRenderer?.contents.find(contents => contents.slimVideoDescriptionRenderer)?.slimVideoDescriptionRenderer.publishDate.runs[0].text,
|
||||||
details.thumbnails?.thumbnails || microformat.thumbnails?.thumbnails,
|
|
||||||
isFamilySafe: microformat.isFamilySafe,
|
|
||||||
availableCountries: microformat.availableCountries,
|
|
||||||
liveBroadcastDetails: microformat.liveBroadcastDetails,
|
|
||||||
uploadDate: microformat.uploadDate,
|
|
||||||
publishDate: microformat.publishDate,
|
|
||||||
isPrivate: details.isPrivate,
|
isPrivate: details.isPrivate,
|
||||||
viewCount: details.viewCount || microformat.viewCount,
|
viewCount: details.viewCount,
|
||||||
lengthSeconds: details.lengthSeconds || microformat.lengthSeconds,
|
lengthSeconds: details.lengthSeconds,
|
||||||
likes: parseInt(
|
likes: parseInt(columnUI?.contents[0].slimVideoMetadataSectionRenderer?.contents.find(contents => contents.slimVideoScrollableActionBarRenderer)?.slimVideoScrollableActionBarRenderer
|
||||||
columnUI?.contents[1].slimVideoMetadataSectionRenderer?.contents[1].slimVideoActionBarRenderer?.buttons[0].slimMetadataToggleButtonRenderer?.button?.toggleButtonRenderer?.defaultText?.accessibility?.accessibilityData?.label?.replace(
|
.buttons.find(button => button.slimMetadataToggleButtonRenderer.isLike == true)?.slimMetadataToggleButtonRenderer?.button
|
||||||
/\D/g,
|
.toggleButtonRenderer?.defaultText?.accessibility?.accessibilityData?.label?.replace(/\D/g,"")), // Yes. I know.
|
||||||
""
|
|
||||||
)
|
|
||||||
), // Yes. I know.
|
|
||||||
},
|
},
|
||||||
renderedData: {
|
renderedData: {
|
||||||
description:
|
description: columnUI?.contents.find(contents => contents.slimVideoMetadataSectionRenderer).slimVideoMetadataSectionRenderer?.contents.find(contents => contents.slimVideoDescriptionRenderer)?.slimVideoDescriptionRenderer.description.runs,
|
||||||
renderedPanels[0].engagementPanelSectionListRenderer?.content
|
recommendations: columnUI?.contents.find(contents => contents.shelfRenderer).shelfRenderer?.content?.horizontalListRenderer?.items,
|
||||||
.structuredDescriptionContentRenderer?.items[1]
|
recommendationsContinuation: columnUI?.continuations[0].reloadContinuationData?.continuation
|
||||||
.expandableVideoDescriptionBodyRenderer?.descriptionBodyText.runs,
|
|
||||||
recommendations:
|
|
||||||
columnUI?.contents[columnUI.contents.length - 1].itemSectionRenderer
|
|
||||||
?.contents,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
console.log(vidData)
|
||||||
|
|
||||||
|
return vidData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Innertube;
|
export default Innertube;
|
||||||
|
|
|
@ -10,6 +10,8 @@ function useRender(video, renderer) {
|
||||||
return gridVideoRenderer(video);
|
return gridVideoRenderer(video);
|
||||||
case "compactAutoplayRenderer":
|
case "compactAutoplayRenderer":
|
||||||
return compactAutoplayRenderer(video);
|
return compactAutoplayRenderer(video);
|
||||||
|
case "compactVideoRenderer":
|
||||||
|
return compactVideoRenderer(video);
|
||||||
default:
|
default:
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
@ -49,35 +51,6 @@ function gridVideoRenderer(video) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function videoWithContextRenderer(video) {
|
|
||||||
return {
|
|
||||||
id: video.videoId,
|
|
||||||
title: video.headline?.runs[0].text,
|
|
||||||
thumbnail: Innertube.getThumbnail(video.videoId, "max"),
|
|
||||||
channel: video.shortBylineText?.runs[0].text,
|
|
||||||
channelURL:
|
|
||||||
video.channelThumbnail?.channelThumbnailWithLinkRenderer
|
|
||||||
?.navigationEndpoint?.browseEndpoint?.canonicalBaseUrl,
|
|
||||||
channelId:
|
|
||||||
video.channelThumbnail?.channelThumbnailWithLinkRenderer
|
|
||||||
?.navigationEndpoint?.browseEndpoint?.browseId,
|
|
||||||
channelThumbnail:
|
|
||||||
video.channelThumbnail?.channelThumbnailWithLinkRenderer?.thumbnail
|
|
||||||
.thumbnails[0].url,
|
|
||||||
metadata: {
|
|
||||||
views: video.shortViewCountText?.runs[0].text,
|
|
||||||
length: video.lengthText?.runs[0].text,
|
|
||||||
overlayStyle: video.thumbnailOverlays?.map(
|
|
||||||
(overlay) => overlay.thumbnailOverlayTimeStatusRenderer?.style
|
|
||||||
),
|
|
||||||
overlay: video.thumbnailOverlays?.map(
|
|
||||||
(overlay) =>
|
|
||||||
overlay.thumbnailOverlayTimeStatusRenderer?.text.runs[0].text
|
|
||||||
),
|
|
||||||
isWatched: video.isWatched,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
function compactAutoplayRenderer(video) {
|
function compactAutoplayRenderer(video) {
|
||||||
video = video.contents;
|
video = video.contents;
|
||||||
let item;
|
let item;
|
||||||
|
@ -86,4 +59,29 @@ function compactAutoplayRenderer(video) {
|
||||||
else return undefined;
|
else return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function compactVideoRenderer(video) {
|
||||||
|
return {
|
||||||
|
id: video.videoId,
|
||||||
|
title: video.title?.runs[0].text,
|
||||||
|
thumbnail: Innertube.getThumbnail(video.videoId, "max"),
|
||||||
|
channel: video.shortBylineText?.runs[0].text,
|
||||||
|
channelURL:
|
||||||
|
video.shortBylineText?.runs[0].navigationEndpoint?.browseEndpoint
|
||||||
|
?.canonicalBaseUrl,
|
||||||
|
channelThumbnail: video.channelThumbnail?.thumbnails[0].url,
|
||||||
|
metadata: {
|
||||||
|
views: video.viewCountText?.runs[0].text,
|
||||||
|
length: video.lengthText?.runs[0].text,
|
||||||
|
publishedTimeText: video.publishedTimeText.runs[0].text,
|
||||||
|
overlayStyle: video.thumbnailOverlays?.map(
|
||||||
|
(overlay) => overlay.thumbnailOverlayTimeStatusRenderer?.style
|
||||||
|
),
|
||||||
|
overlay: video.thumbnailOverlays?.map(
|
||||||
|
(overlay) =>
|
||||||
|
overlay.thumbnailOverlayTimeStatusRenderer?.text.runs[0].text
|
||||||
|
),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export default useRender;
|
export default useRender;
|
||||||
|
|
|
@ -212,6 +212,18 @@ const innertubeModule = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getThumbnail(id, resolution) {
|
||||||
|
if (resolution == "max") {
|
||||||
|
const url = `https://img.youtube.com/vi/${id}/maxresdefault.jpg`;
|
||||||
|
let img = new Image();
|
||||||
|
img.src = url;
|
||||||
|
img.onload = function () {
|
||||||
|
if (img.height !== 120) return url;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return `https://img.youtube.com/vi/${id}/mqdefault.jpg`;
|
||||||
|
},
|
||||||
|
|
||||||
// It just works™
|
// It just works™
|
||||||
// Front page recommendation
|
// Front page recommendation
|
||||||
async recommend() {
|
async recommend() {
|
||||||
|
@ -226,35 +238,26 @@ const innertubeModule = {
|
||||||
const video =
|
const video =
|
||||||
shelves.shelfRenderer?.content?.horizontalListRenderer?.items;
|
shelves.shelfRenderer?.content?.horizontalListRenderer?.items;
|
||||||
|
|
||||||
if (video)
|
if (video) return video;
|
||||||
return video.map((item) => {
|
// if (video)
|
||||||
if (item) {
|
// return video.map((item) => {
|
||||||
const renderedItem = useRender(
|
// if (item) {
|
||||||
item[Object.keys(item)[0]],
|
// const renderedItem = useRender(
|
||||||
Object.keys(item)[0]
|
// item[Object.keys(item)[0]],
|
||||||
);
|
// Object.keys(item)[0]
|
||||||
console.log(renderedItem);
|
// );
|
||||||
return renderedItem;
|
// console.log(renderedItem);
|
||||||
} else {
|
// return renderedItem;
|
||||||
return undefined;
|
// } else {
|
||||||
}
|
// return undefined;
|
||||||
});
|
// }
|
||||||
|
// });
|
||||||
});
|
});
|
||||||
console.log(final);
|
console.log(final);
|
||||||
return final;
|
return final;
|
||||||
},
|
},
|
||||||
|
|
||||||
// This is the recommendations that exist under videos
|
// This is the recommendations that exist under videos
|
||||||
viewRecommends(recommendList) {
|
|
||||||
if (recommendList)
|
|
||||||
return recommendList.map((item) => {
|
|
||||||
if (item) {
|
|
||||||
return useRender(item[Object.keys(item)[0]], Object.keys(item)[0]);
|
|
||||||
} else {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//--- Start ---//
|
//--- Start ---//
|
||||||
|
|
Loading…
Reference in New Issue