0
0
Fork 0
mirror of https://github.com/VueTubeApp/VueTube synced 2024-11-22 19:25:16 +00:00

fix: search function fully functional

This commit is contained in:
Alex 2022-03-25 00:47:13 +13:00
parent 23233c5275
commit eed6223a8a
14 changed files with 265 additions and 274 deletions

View file

@ -4,42 +4,17 @@
:to="`/watch?v=${video.videoId}`"
flat
>
<div style="position: relative" class="thumbnail-container">
<v-img
:aspect-ratio="16 / 9"
:src="
$youtube.getThumbnail(
video.videoId,
'max',
video.thumbnail.thumbnails
)
"
/>
<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
"
:href="video.navigationEndpoint.browseEndpoint.canonicalBaseUrl"
class="avatar-link pt-2"
>
<v-img
class="avatar-thumbnail"
:src="video.channelThumbnail.thumbnails[0].url"
:src="
video.thumbnail.thumbnails[video.thumbnail.thumbnails.length - 1]
.url
"
/>
</a>
<v-card-text class="video-info pt-2">
@ -47,7 +22,7 @@
v-for="title in video.title.runs"
:key="title.text"
style="margin-top: 0.5em"
class="font-weight-medium vid-title"
class="vid-title"
>
{{ title.text }}
</div>
@ -62,21 +37,6 @@
.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;
@ -90,36 +50,24 @@
margin-top: 0.5rem;
margin-left: 0.5rem;
border-radius: 50%;
width: 35px;
height: 35px;
width: 50px;
height: 50px;
}
#details {
display: flex;
flex-direction: row;
flex-basis: auto;
padding: 10px;
}
@media screen and (orientation: landscape) {
.entry {
margin-bottom: 8px;
}
.thumbnail-container {
width: 50vh;
float: left;
}
#details {
flex-direction: column-reverse;
}
.avatar-thumbnail {
margin-top: 0;
margin-left: 16px;
}
.video-info {
padding-top: 0 !important;
padding-bottom: 0;
margin-top: 0;
}
}
</style>
@ -130,11 +78,9 @@ export default {
methods: {
parseBottom(video) {
const bottomText = [
video.shortBylineText?.runs[0].text,
video.shortViewCountText?.runs[0].text,
video.subscriberCountText?.runs[0].text,
video.videoCountText?.runs.map((run) => run.text).join(" "),
];
if (video.publishedTimeText?.runs[0].text)
bottomText.push(video.publishedTimeText?.runs[0].text);
return bottomText.join(" · ");
},
},

View file

@ -0,0 +1,39 @@
<template>
<div>
<v-list-item
v-for="(video, index) in render.items"
:key="index"
class="pa-0"
>
<component
v-if="getComponents()[Object.keys(video)[0]]"
:is="Object.keys(video)[0]"
:key="video[Object.keys(video)[0]].videoId"
:video="video[Object.keys(video)[0]]"
></component>
</v-list-item>
</div>
</template>
<script>
import compactVideoRenderer from "../CompactRenderers/compactVideoRenderer.vue";
import gridVideoRenderer from "../GridRenderers/gridVideoRenderer.vue";
export default {
components: {
gridVideoRenderer,
compactVideoRenderer,
},
props: ["render"],
methods: {
getComponents() {
return this.$options.components;
},
},
mounted() {
console.log("horizontalListRenderer received: ", this.render);
},
};
</script>

View file

@ -0,0 +1,35 @@
<template>
<div>
<v-list-item
v-for="(renderer, index) in render.contents"
:key="index"
class="pa-0"
>
<component
v-if="getComponents()[Object.keys(renderer)[0]]"
:is="Object.keys(renderer)[0]"
:key="index"
:render="renderer[Object.keys(renderer)[0]]"
></component>
</v-list-item>
</div>
</template>
<script>
import itemSectionRenderer from "../SectionRenderers/itemSectionRenderer.vue";
import shelfRenderer from "../SectionRenderers/shelfRenderer.vue";
export default {
components: {
itemSectionRenderer,
shelfRenderer,
},
props: ["render"],
methods: {
getComponents() {
return this.$options.components;
},
},
};
</script>

View file

@ -0,0 +1,35 @@
<template>
<div>
<v-list-item
v-for="(video, index) in render.items"
:key="index"
class="pa-0"
>
<component
v-if="getComponents()[Object.keys(video)[0]]"
:is="Object.keys(video)[0]"
:key="video[Object.keys(video)[0]].videoId"
:video="video[Object.keys(video)[0]]"
></component>
</v-list-item>
</div>
</template>
<script>
import gridVideoRenderer from "../GridRenderers/gridVideoRenderer.vue";
import compactVideoRenderer from "../CompactRenderers/compactVideoRenderer.vue";
export default {
components: {
gridVideoRenderer,
compactVideoRenderer,
},
props: ["render"],
methods: {
getComponents() {
return this.$options.components;
},
},
};
</script>

View file

@ -0,0 +1,55 @@
<template>
<div>
<div
v-for="(video, index) in render.contents"
:key="index"
class="pa-0 fill-screen"
>
<component
v-if="getComponents()[Object.keys(video)[0]]"
:is="Object.keys(video)[0]"
:key="video[Object.keys(video)[0]].videoId"
:video="video[Object.keys(video)[0]]"
></component>
</div>
<div
v-if="
render.separatorDetails && render.separatorDetails.hasBottomSeparator
"
class="separator-bottom grey"
:style="{ height: render.separatorDetails.height + 'px' }"
></div>
</div>
</template>
<style scoped>
.shelf-header {
width: 100%; /* Prevent Loading Weirdness */
padding: 10px;
}
.fill-screen {
widows: 100 vw; /* Very Hacky */
}
</style>
<script>
import compactVideoRenderer from "../CompactRenderers/compactVideoRenderer.vue";
import compactChannelRenderer from "../CompactRenderers/compactChannelRenderer.vue";
import gridVideoRenderer from "../GridRenderers/gridVideoRenderer.vue";
export default {
components: {
gridVideoRenderer,
compactVideoRenderer,
compactChannelRenderer,
},
props: ["render"],
methods: {
getComponents() {
return this.$options.components;
},
},
};
</script>

View file

@ -0,0 +1,46 @@
<template>
<div>
<h4 v-if="render.headerRenderer" class="font-weight-bold shelf-header">
{{
render.headerRenderer.elementRenderer.newElement.type.componentType
.model.shelfHeaderModel.shelfHeaderData.title
}}
</h4>
<component
v-if="render.content && getComponents()[Object.keys(render.content)[0]]"
:is="Object.keys(render.content)[0]"
:render="render.content[Object.keys(render.content)[0]]"
></component>
<div
v-if="render.separator && render.separator.hasBottomSeparator"
class="separator-bottom grey"
:style="{ height: render.separator.height + 'px' }"
></div>
</div>
</template>
<style scoped>
.shelf-header {
width: 100%; /* Prevent Loading Weirdness */
padding: 10px;
}
</style>
<script>
import verticalListRenderer from "../ListRenderers/verticalListRenderer.vue";
import horizontalListRenderer from "../ListRenderers/horizontalListRenderer.vue";
export default {
components: {
horizontalListRenderer,
verticalListRenderer,
},
props: ["render"],
methods: {
getComponents() {
return this.$options.components;
},
},
};
</script>

View file

@ -137,5 +137,9 @@ export default {
return bottomText.join(" · ");
},
},
mounted() {
console.log("gridVideoRenderer received: ", this.video);
},
};
</script>

View file

@ -1,45 +0,0 @@
<template>
<div>
<!-- Video Loading Animation -->
<center v-if="recommends.length == 0">
<v-skeleton-loader type="card-avatar, article, actions" />
<v-skeleton-loader type="card-avatar, article, actions" />
</center>
<v-list-item v-for="(video, index) in recommends" :key="index" class="pa-0">
<component
v-if="getComponents()[Object.keys(video)[0]]"
:is="Object.keys(video)[0]"
:key="video[Object.keys(video)[0]].videoId"
:video="video[Object.keys(video)[0]]"
></component>
</v-list-item>
</div>
</template>
<script>
import compactVideoRenderer from "./CompactRenderers/compactVideoRenderer.vue";
import gridVideoRenderer from "./VideoRenderers/gridVideoRenderer.vue";
export default {
components: {
gridVideoRenderer,
compactVideoRenderer,
},
props: {
recommends: Array,
},
methods: {
parseBottom(video) {
const bottomText = [video.channel, video.metadata.views];
if (video.metadata.published) bottomText.push(video.metadata.published);
return bottomText.join(" • ");
},
getComponents() {
return this.$options.components;
},
},
};
</script>

View file

@ -0,0 +1,7 @@
// this is an loading animation for videos
<template>
<center>
<v-skeleton-loader type="card-avatar, article, actions" />
<v-skeleton-loader type="card-avatar, article, actions" />
</center>
</template>

View file

@ -4,16 +4,22 @@
* This is to allow use of "recommended" videos on other pages such as /watch
* -Front
* -->
<horizontal-list-renderer :recommends="recommends" />
<div>
<!-- Video Loading Animation -->
<vid-load-renderer v-if="!recommends" />
<horizontal-list-renderer v-else :render="recommends" />
</div>
</template>
<script>
import horizontalListRenderer from "../components/horizontalListRenderer.vue";
import horizontalListRenderer from "../components/ListRenderers/horizontalListRenderer.vue";
import VidLoadRenderer from "../components/vidLoadRenderer.vue";
export default {
components: { horizontalListRenderer },
components: { horizontalListRenderer, VidLoadRenderer },
data() {
return {
recommends: [],
recommends: null,
};
},

View file

@ -1,11 +1,8 @@
<template>
<div class="accent">
<center v-if="videos.length == -1">
<v-skeleton-loader type="card-avatar, article, actions" />
<v-skeleton-loader type="card-avatar, article, actions" />
</center>
<horizontal-list-renderer :recommends="videos" />
<!-- Video Loading Animation -->
<vid-load-renderer v-if="renderer.length <= 0" />
<sectionListRenderer :render="renderer" />
</div>
</template>
@ -24,12 +21,17 @@
</style>
<script>
import horizontalListRenderer from "../components/horizontalListRenderer.vue";
import sectionListRenderer from "../components/ListRenderers/sectionListRenderer.vue";
import VidLoadRenderer from "../components/vidLoadRenderer.vue";
export default {
components: { horizontalListRenderer },
components: {
sectionListRenderer,
VidLoadRenderer,
},
data() {
return {
videos: [],
renderer: [],
};
},
mounted() {
@ -39,7 +41,7 @@ export default {
getSearch() {
const searchQuestion = this.$route.query.q;
this.$youtube.search(searchQuestion).then((response) => {
this.videos = response.items;
this.renderer = response;
});
},
},

View file

@ -86,8 +86,8 @@
</v-sheet>
</v-bottom-sheet> -->
</v-card>
<horizontal-list-renderer :recommends="recommends" />
<vid-load-renderer v-if="!recommends" />
<shelf-renderer v-else :render="recommends" />
</div>
</template>
@ -100,10 +100,11 @@
<script>
import { Share } from "@capacitor/share";
import horizontalListRenderer from "../components/horizontalListRenderer.vue";
import ShelfRenderer from "../components/SectionRenderers/shelfRenderer.vue";
import VidLoadRenderer from "../components/vidLoadRenderer.vue";
export default {
components: { horizontalListRenderer },
components: { ShelfRenderer, VidLoadRenderer },
data() {
return {
interactions: [
@ -137,7 +138,7 @@ export default {
vidSrc: null,
description: null,
views: null,
recommends: [],
recommends: null,
loaded: false,
};
},
@ -197,7 +198,7 @@ export default {
this.$vuetube.statusBar.show();
this.$vuetube.navigationBar.show();
}
}
},
},
watch: {
// Watch for change in the route query string (in this case, ?v=xxxxxxxx to ?v=yyyyyyyy)

View file

@ -246,7 +246,7 @@ class Innertube {
)?.slimVideoDescriptionRenderer.description.runs,
recommendations: columnUI?.contents.find(
(contents) => contents.shelfRenderer
).shelfRenderer?.content?.horizontalListRenderer?.items,
).shelfRenderer,
recommendationsContinuation:
columnUI?.continuations[0].reloadContinuationData?.continuation,
},
@ -264,9 +264,7 @@ class Innertube {
`Could not get search results: ${search.status_code} - ${search.message}`
);
console.log(search.data);
return search.data.contents.sectionListRenderer.contents.find(
(contents) => contents.shelfRenderer
).shelfRenderer;
return search.data;
}
}

View file

@ -14,83 +14,6 @@ function logger(func, data, isError = false) {
});
}
//--- Youtube Base Parser ---//
function youtubeParse(html, callback) {
//--- Replace Encoded Characters ---///
html = html.replace(/\\x([0-9A-F]{2})/gi, (...items) => {
return String.fromCharCode(parseInt(items[1], 16));
});
//--- Properly Format JSON ---//
html = html.replaceAll('\\\\"', "");
//--- Parse JSON ---//
html = JSON.parse(html);
//--- Get Results ---// ( Thanks To appit-online On Github ) -> https://github.com/appit-online/youtube-search/blob/master/src/lib/search.ts
let results;
if (
html &&
html.contents &&
html.contents.sectionListRenderer &&
html.contents.sectionListRenderer.contents &&
html.contents.sectionListRenderer.contents.length > 0 &&
html.contents.sectionListRenderer.contents[0].itemSectionRenderer &&
html.contents.sectionListRenderer.contents[0].itemSectionRenderer.contents
.length > 0
) {
results =
html.contents.sectionListRenderer.contents[0].itemSectionRenderer
.contents;
logger(constants.LOGGER_NAMES.search, results);
callback(results);
} else {
try {
results = JSON.parse(
html
.split('{"itemSectionRenderer":{"contents":')
[html.split('{"itemSectionRenderer":{"contents":').length - 1].split(
',"continuations":[{'
)[0]
);
logger(constants.LOGGER_NAMES.search, results);
callback(results);
} catch (e) {}
try {
results = JSON.parse(
html
.split('{"itemSectionRenderer":')
[html.split('{"itemSectionRenderer":').length - 1].split(
'},{"continuationItemRenderer":{'
)[0]
).contents;
logger(constants.LOGGER_NAMES.search, results);
callback(results);
} catch (e) {}
}
}
//--- Search Main Function ---//
function youtubeSearch(text, callback) {
Http.request({
method: "GET",
url: `${constants.URLS.YT_URL}/results`,
params: { q: text, hl: "en" },
})
.then((res) => {
//--- Get HTML Only ---//
let html = res.data;
//--- Isolate The Script Containing Video Information ---//
html = html.split("var ytInitialData = '")[1].split("';</script>")[0];
youtubeParse(html, (data) => {
callback(data);
});
})
.catch((err) => {
logger(constants.LOGGER_NAMES.search, err, true);
callback(err);
});
}
const searchModule = {
logs: new Array(),
@ -111,66 +34,6 @@ const searchModule = {
});
},
// search(text, callback) {
// let results = new Array();
// youtubeSearch(text, (videos) => {
// for (const i in videos) {
// const video = videos[i];
// if (video.compactVideoRenderer) {
// //--- If Entry Is A Video ---//
// results.push({
// id: video.compactVideoRenderer.videoId,
// title: video.compactVideoRenderer.title.runs[0].text,
// runtime: video.compactVideoRenderer.lengthText.runs[0].text,
// uploaded: video.compactVideoRenderer.publishedTimeText.runs[0].text,
// views: video.compactVideoRenderer.viewCountText.runs[0].text,
// thumbnails: video.compactVideoRenderer.thumbnail.thumbnails,
// });
// } else {
// //--- If Entry Is Not A Video ---//
// //logger(constants.LOGGER_NAMES.search, { type: "Error Caught Successfully", error: video }, true);
// }
// }
// });
// callback(results);
// },
getRemainingVideoInfo(id, callback) {
String.prototype.decodeEscapeSequence = function () {
return this.replace(/\\x([0-9A-Fa-f]{2})/g, function () {
return String.fromCharCode(parseInt(arguments[1], 16));
});
};
Http.request({
method: "GET",
url: `${constants.URLS.YT_URL}/watch`,
params: { v: id },
})
.then((res) => {
let dataUpdated = res.data.decodeEscapeSequence();
let likes = dataUpdated
.split(
`"defaultIcon":{"iconType":"LIKE"},"defaultText":{"runs":[{"text":"`
)[1]
.split(`"}],"accessibility":`)[0];
let uploadDate = dataUpdated
.split(`"uploadDate":"`)[1]
.split(`}},"trackingParams":"`)[0]
.slice(0, -2);
let data = {
likes: likes,
uploadDate: uploadDate,
};
logger("vidData", data);
callback(data);
})
.catch((err) => {
logger("codeRun", err, true);
callback(err);
});
},
getReturnYoutubeDislike(id, callback) {
Http.request({
method: "GET",
@ -237,8 +100,7 @@ const innertubeModule = {
response.data.contents.singleColumnBrowseResultsRenderer.tabs[0]
.tabRenderer.content.sectionListRenderer.contents;
const final = contents.map((shelves) => {
const video =
shelves.shelfRenderer?.content?.horizontalListRenderer?.items;
const video = shelves.shelfRenderer?.content?.horizontalListRenderer;
if (video) return video;
});
@ -249,7 +111,7 @@ const innertubeModule = {
async search(query) {
try {
const response = await InnertubeAPI.getSearchAsync(query);
return response.content.verticalListRenderer;
return response.contents.sectionListRenderer;
} catch (err) {
logger(constants.LOGGER_NAMES.search, err, true);
}