Merge branch 'Frontesque:main' into main
4
.gitignore
vendored
|
@ -6,3 +6,7 @@ package-lock.json
|
||||||
temp.js
|
temp.js
|
||||||
temp.json
|
temp.json
|
||||||
.vscode/settings.json
|
.vscode/settings.json
|
||||||
|
|
||||||
|
# Capacitor Splash Screens
|
||||||
|
resources/android
|
||||||
|
resources/ios
|
88
NUXT/components/CompactRenderers/compactChannelRenderer.vue
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
<template>
|
||||||
|
<v-card
|
||||||
|
class="entry gridVideoRenderer background"
|
||||||
|
:to="`/watch?v=${video.videoId}`"
|
||||||
|
flat
|
||||||
|
>
|
||||||
|
<div id="details">
|
||||||
|
<a
|
||||||
|
:href="video.navigationEndpoint.browseEndpoint.canonicalBaseUrl"
|
||||||
|
class="avatar-link pt-2"
|
||||||
|
>
|
||||||
|
<v-img
|
||||||
|
class="avatar-thumbnail"
|
||||||
|
:src="
|
||||||
|
video.thumbnail.thumbnails[video.thumbnail.thumbnails.length - 1]
|
||||||
|
.url
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<v-card-text class="video-info pt-2">
|
||||||
|
<div
|
||||||
|
v-for="title in video.title.runs"
|
||||||
|
:key="title.text"
|
||||||
|
style="margin-top: 0.5em"
|
||||||
|
class="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 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.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: 50px;
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#details {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-basis: auto;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (orientation: landscape) {
|
||||||
|
.entry {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
#details {
|
||||||
|
flex-direction: column-reverse;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: ["video"],
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
parseBottom(video) {
|
||||||
|
const bottomText = [
|
||||||
|
video.subscriberCountText?.runs[0].text,
|
||||||
|
video.videoCountText?.runs.map((run) => run.text).join(" "),
|
||||||
|
];
|
||||||
|
return bottomText.join(" · ");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -7,7 +7,13 @@
|
||||||
<div style="position: relative" class="thumbnail-container">
|
<div style="position: relative" class="thumbnail-container">
|
||||||
<v-img
|
<v-img
|
||||||
:aspect-ratio="16 / 9"
|
:aspect-ratio="16 / 9"
|
||||||
:src="$youtube.getThumbnail(video.videoId, 'max')"
|
:src="
|
||||||
|
$youtube.getThumbnail(
|
||||||
|
video.videoId,
|
||||||
|
'max',
|
||||||
|
video.thumbnail.thumbnails
|
||||||
|
)
|
||||||
|
"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
class="videoRuntimeFloat"
|
class="videoRuntimeFloat"
|
39
NUXT/components/ListRenderers/horizontalListRenderer.vue
Normal 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 "~/components/CompactRenderers/compactVideoRenderer.vue";
|
||||||
|
import gridVideoRenderer from "~/components/gridRenderers/gridVideoRenderer.vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
gridVideoRenderer,
|
||||||
|
compactVideoRenderer,
|
||||||
|
},
|
||||||
|
props: ["render"],
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
getComponents() {
|
||||||
|
return this.$options.components;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
console.log("horizontalListRenderer received: ", this.render);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
35
NUXT/components/ListRenderers/sectionListRenderer.vue
Normal 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 "~/components/SectionRenderers/itemSectionRenderer.vue";
|
||||||
|
import shelfRenderer from "~/components/SectionRenderers/shelfRenderer.vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
itemSectionRenderer,
|
||||||
|
shelfRenderer,
|
||||||
|
},
|
||||||
|
props: ["render"],
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
getComponents() {
|
||||||
|
return this.$options.components;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
35
NUXT/components/ListRenderers/verticalListRenderer.vue
Normal 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 "~/components/gridRenderers/gridVideoRenderer.vue";
|
||||||
|
import compactVideoRenderer from "~/components/CompactRenderers/compactVideoRenderer.vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
gridVideoRenderer,
|
||||||
|
compactVideoRenderer,
|
||||||
|
},
|
||||||
|
props: ["render"],
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
getComponents() {
|
||||||
|
return this.$options.components;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
55
NUXT/components/SectionRenderers/itemSectionRenderer.vue
Normal 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 {
|
||||||
|
width: 100vw; /* Very Hacky */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import compactVideoRenderer from "~/components/CompactRenderers/compactVideoRenderer.vue";
|
||||||
|
import compactChannelRenderer from "~/components/CompactRenderers/compactChannelRenderer.vue";
|
||||||
|
import gridVideoRenderer from "~/components/gridRenderers/gridVideoRenderer.vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
gridVideoRenderer,
|
||||||
|
compactVideoRenderer,
|
||||||
|
compactChannelRenderer,
|
||||||
|
},
|
||||||
|
props: ["render"],
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
getComponents() {
|
||||||
|
return this.$options.components;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
46
NUXT/components/SectionRenderers/shelfRenderer.vue
Normal 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 "~/components/ListRenderers/verticalListRenderer.vue";
|
||||||
|
import horizontalListRenderer from "~/components/ListRenderers/horizontalListRenderer.vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
horizontalListRenderer,
|
||||||
|
verticalListRenderer,
|
||||||
|
},
|
||||||
|
props: ["render"],
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
getComponents() {
|
||||||
|
return this.$options.components;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -18,6 +18,12 @@
|
||||||
:class="tabSelection == i ? 'tab primary lighten-2' : ''"
|
:class="tabSelection == i ? 'tab primary lighten-2' : ''"
|
||||||
v-text="item.icon"
|
v-text="item.icon"
|
||||||
/>
|
/>
|
||||||
|
<!--
|
||||||
|
Add the following to 'v-text- above to make the icons outlined unless active
|
||||||
|
+ (tabSelection == i ? '' : '-outline')
|
||||||
|
|
||||||
|
|
||||||
|
-->
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<!-- <v-btn text class="navButton mr-2 fill-height" color="white" @click="searchBtn()"
|
<!-- <v-btn text class="navButton mr-2 fill-height" color="white" @click="searchBtn()"
|
||||||
><v-icon>mdi-magnify</v-icon></v-btn
|
><v-icon>mdi-magnify</v-icon></v-btn
|
||||||
|
|
|
@ -7,7 +7,13 @@
|
||||||
<div style="position: relative" class="thumbnail-container">
|
<div style="position: relative" class="thumbnail-container">
|
||||||
<v-img
|
<v-img
|
||||||
:aspect-ratio="16 / 9"
|
:aspect-ratio="16 / 9"
|
||||||
:src="$youtube.getThumbnail(video.videoId, 'max')"
|
:src="
|
||||||
|
$youtube.getThumbnail(
|
||||||
|
video.videoId,
|
||||||
|
'max',
|
||||||
|
video.thumbnail.thumbnails
|
||||||
|
)
|
||||||
|
"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
class="videoRuntimeFloat"
|
class="videoRuntimeFloat"
|
||||||
|
@ -131,5 +137,9 @@ export default {
|
||||||
return bottomText.join(" · ");
|
return bottomText.join(" · ");
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
console.log("gridVideoRenderer received: ", this.video);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
|
@ -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 "./VideoRenderers/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>
|
|
7
NUXT/components/vidLoadRenderer.vue
Normal 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>
|
|
@ -4,13 +4,19 @@
|
||||||
* This is to allow use of "recommended" videos on other pages such as /watch
|
* This is to allow use of "recommended" videos on other pages such as /watch
|
||||||
* -Front
|
* -Front
|
||||||
* -->
|
* -->
|
||||||
<horizontal-list-renderer :recommends="recommends" class="video-list" />
|
|
||||||
|
<div>
|
||||||
|
<!-- Video Loading Animation -->
|
||||||
|
<vid-load-renderer v-if="!recommends" />
|
||||||
|
<horizontal-list-renderer v-else :render="recommends" />
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import horizontalListRenderer from "../components/horizontalListRenderer.vue";
|
import horizontalListRenderer from "~/components/ListRenderers/horizontalListRenderer.vue";
|
||||||
|
import VidLoadRenderer from "~/components/vidLoadRenderer.vue";
|
||||||
export default {
|
export default {
|
||||||
components: { horizontalListRenderer },
|
components: { horizontalListRenderer, VidLoadRenderer },
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
recommends: {
|
recommends: {
|
||||||
|
|
|
@ -16,10 +16,6 @@ import { SplashScreen } from "@capacitor/splash-screen";
|
||||||
export default {
|
export default {
|
||||||
layout: "empty",
|
layout: "empty",
|
||||||
async mounted() {
|
async mounted() {
|
||||||
//--- Hide Splash Screen ---//
|
|
||||||
await SplashScreen.hide();
|
|
||||||
//-------------------------------//
|
|
||||||
|
|
||||||
//--- Theme Loader Moved From '~/layouts/default.vue' (because this only needs to be run once) -Front ---//
|
//--- Theme Loader Moved From '~/layouts/default.vue' (because this only needs to be run once) -Front ---//
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
//Set timeout is required to make it load properly... dont ask me why -Front
|
//Set timeout is required to make it load properly... dont ask me why -Front
|
||||||
|
@ -52,6 +48,7 @@ export default {
|
||||||
//-----------------------------------------------------------------------------------------------------------//
|
//-----------------------------------------------------------------------------------------------------------//
|
||||||
|
|
||||||
await this.$youtube.getAPI();
|
await this.$youtube.getAPI();
|
||||||
|
await SplashScreen.hide();
|
||||||
this.$router.push(`/${localStorage.getItem("startPage") || "home"}`);
|
this.$router.push(`/${localStorage.getItem("startPage") || "home"}`);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
<div>OS: {{ deviceInfo.operatingSystem }} ({{ deviceInfo.osVersion }})</div>
|
<div>OS: {{ deviceInfo.operatingSystem }} ({{ deviceInfo.osVersion }})</div>
|
||||||
<div>Model: {{ deviceInfo.model }}</div>
|
<div>Model: {{ deviceInfo.model }}</div>
|
||||||
<div>Manufacturer: {{ deviceInfo.manufacturer }}</div>
|
<div>Manufacturer: {{ deviceInfo.manufacturer }}</div>
|
||||||
<div>Virtual: {{ deviceInfo.isVirtual }}</div>
|
<div>Virtual Device: {{ deviceInfo.isVirtual ? 'yes' : 'no' }}</div>
|
||||||
</center>
|
</center>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="accent">
|
<div class="accent">
|
||||||
<center v-if="videos.length == -1">
|
<!-- Video Loading Animation -->
|
||||||
<v-skeleton-loader type="card-avatar, article, actions" />
|
<vid-load-renderer v-if="renderer.length <= 0" />
|
||||||
<v-skeleton-loader type="card-avatar, article, actions" />
|
<sectionListRenderer :render="renderer" />
|
||||||
</center>
|
|
||||||
|
|
||||||
<horizontal-list-renderer :recommends="videos" />
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -24,12 +21,17 @@
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import horizontalListRenderer from "../components/horizontalListRenderer.vue";
|
import sectionListRenderer from "~/components/ListRenderers/sectionListRenderer.vue";
|
||||||
|
import VidLoadRenderer from "~/components/vidLoadRenderer.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { horizontalListRenderer },
|
components: {
|
||||||
|
sectionListRenderer,
|
||||||
|
VidLoadRenderer,
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
videos: [],
|
renderer: [],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
@ -39,7 +41,7 @@ export default {
|
||||||
getSearch() {
|
getSearch() {
|
||||||
const searchQuestion = this.$route.query.q;
|
const searchQuestion = this.$route.query.q;
|
||||||
this.$youtube.search(searchQuestion).then((response) => {
|
this.$youtube.search(searchQuestion).then((response) => {
|
||||||
this.videos = response.items;
|
this.renderer = response;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -86,8 +86,8 @@
|
||||||
</v-sheet>
|
</v-sheet>
|
||||||
</v-bottom-sheet> -->
|
</v-bottom-sheet> -->
|
||||||
</v-card>
|
</v-card>
|
||||||
|
<vid-load-renderer v-if="!recommends" />
|
||||||
<horizontal-list-renderer :recommends="recommends" />
|
<shelf-renderer v-else :render="recommends" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -100,10 +100,11 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { Share } from "@capacitor/share";
|
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 {
|
export default {
|
||||||
components: { horizontalListRenderer },
|
components: { ShelfRenderer, VidLoadRenderer },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
interactions: [
|
interactions: [
|
||||||
|
@ -137,7 +138,7 @@ export default {
|
||||||
vidSrc: null,
|
vidSrc: null,
|
||||||
description: null,
|
description: null,
|
||||||
views: null,
|
views: null,
|
||||||
recommends: [],
|
recommends: null,
|
||||||
loaded: false,
|
loaded: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -197,7 +198,7 @@ export default {
|
||||||
this.$vuetube.statusBar.show();
|
this.$vuetube.statusBar.show();
|
||||||
this.$vuetube.navigationBar.show();
|
this.$vuetube.navigationBar.show();
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
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)
|
||||||
|
|
|
@ -98,7 +98,7 @@ class Innertube {
|
||||||
async getVidAsync(id) {
|
async getVidAsync(id) {
|
||||||
let data = { context: this.context, videoId: id };
|
let data = { context: this.context, videoId: id };
|
||||||
const responseNext = await Http.post({
|
const responseNext = await Http.post({
|
||||||
url: `${constants.URLS.YT_BASE_API}/next?v=${id}`,
|
url: `${constants.URLS.YT_BASE_API}/next?key=${this.key}`,
|
||||||
data: data,
|
data: data,
|
||||||
headers: constants.INNERTUBE_HEADER(this.context.client),
|
headers: constants.INNERTUBE_HEADER(this.context.client),
|
||||||
}).catch((error) => error);
|
}).catch((error) => error);
|
||||||
|
@ -181,11 +181,9 @@ class Innertube {
|
||||||
response.data.output?.playabilityStatus?.status == ("ERROR" || undefined)
|
response.data.output?.playabilityStatus?.status == ("ERROR" || undefined)
|
||||||
)
|
)
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Could not get information for video: ${
|
`Could not get information for video: ${response.status_code ||
|
||||||
response.status_code ||
|
response.data.output?.playabilityStatus?.status
|
||||||
response.data.output?.playabilityStatus?.status
|
} - ${response.message || response.data.output?.playabilityStatus?.reason
|
||||||
} - ${
|
|
||||||
response.message || response.data.output?.playabilityStatus?.reason
|
|
||||||
}`
|
}`
|
||||||
);
|
);
|
||||||
const responseInfo = response.data.output;
|
const responseInfo = response.data.output;
|
||||||
|
@ -246,7 +244,7 @@ class Innertube {
|
||||||
)?.slimVideoDescriptionRenderer.description.runs,
|
)?.slimVideoDescriptionRenderer.description.runs,
|
||||||
recommendations: columnUI?.contents.find(
|
recommendations: columnUI?.contents.find(
|
||||||
(contents) => contents.shelfRenderer
|
(contents) => contents.shelfRenderer
|
||||||
).shelfRenderer?.content?.horizontalListRenderer?.items,
|
).shelfRenderer,
|
||||||
recommendationsContinuation:
|
recommendationsContinuation:
|
||||||
columnUI?.continuations[0].reloadContinuationData?.continuation,
|
columnUI?.continuations[0].reloadContinuationData?.continuation,
|
||||||
},
|
},
|
||||||
|
@ -264,9 +262,7 @@ class Innertube {
|
||||||
`Could not get search results: ${search.status_code} - ${search.message}`
|
`Could not get search results: ${search.status_code} - ${search.message}`
|
||||||
);
|
);
|
||||||
console.log(search.data);
|
console.log(search.data);
|
||||||
return search.data.contents.sectionListRenderer.contents.find(
|
return search.data;
|
||||||
(contents) => contents.shelfRenderer
|
|
||||||
).shelfRenderer;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 = {
|
const searchModule = {
|
||||||
logs: new Array(),
|
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) {
|
getReturnYoutubeDislike(id, callback) {
|
||||||
Http.request({
|
Http.request({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
@ -212,7 +75,7 @@ const innertubeModule = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
getThumbnail(id, resolution) {
|
getThumbnail(id, resolution, backupThumbnail) {
|
||||||
if (resolution == "max") {
|
if (resolution == "max") {
|
||||||
const url = `https://img.youtube.com/vi/${id}/maxresdefault.jpg`;
|
const url = `https://img.youtube.com/vi/${id}/maxresdefault.jpg`;
|
||||||
let img = new Image();
|
let img = new Image();
|
||||||
|
@ -221,7 +84,9 @@ const innertubeModule = {
|
||||||
if (img.height !== 120) return url;
|
if (img.height !== 120) return url;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return `https://img.youtube.com/vi/${id}/mqdefault.jpg`;
|
if (backupThumbnail[backupThumbnail.length - 1])
|
||||||
|
return backupThumbnail[backupThumbnail.length - 1].url;
|
||||||
|
else return `https://img.youtube.com/vi/${id}/mqdefault.jpg`;
|
||||||
},
|
},
|
||||||
|
|
||||||
// It just works™
|
// It just works™
|
||||||
|
@ -235,8 +100,7 @@ const innertubeModule = {
|
||||||
response.data.contents.singleColumnBrowseResultsRenderer.tabs[0]
|
response.data.contents.singleColumnBrowseResultsRenderer.tabs[0]
|
||||||
.tabRenderer.content.sectionListRenderer.contents;
|
.tabRenderer.content.sectionListRenderer.contents;
|
||||||
const final = contents.map((shelves) => {
|
const final = contents.map((shelves) => {
|
||||||
const video =
|
const video = shelves.shelfRenderer?.content?.horizontalListRenderer;
|
||||||
shelves.shelfRenderer?.content?.horizontalListRenderer?.items;
|
|
||||||
|
|
||||||
if (video) return video;
|
if (video) return video;
|
||||||
});
|
});
|
||||||
|
@ -247,7 +111,7 @@ const innertubeModule = {
|
||||||
async search(query) {
|
async search(query) {
|
||||||
try {
|
try {
|
||||||
const response = await InnertubeAPI.getSearchAsync(query);
|
const response = await InnertubeAPI.getSearchAsync(query);
|
||||||
return response.content.verticalListRenderer;
|
return response.contents.sectionListRenderer;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger(constants.LOGGER_NAMES.search, err, true);
|
logger(constants.LOGGER_NAMES.search, err, true);
|
||||||
}
|
}
|
||||||
|
|
Before Width: | Height: | Size: 8.5 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 6.2 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 7.4 KiB |
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 7.4 KiB |
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 6.7 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 6 KiB |
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 6 KiB |
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 9.2 KiB |
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 9.2 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 5 KiB After Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 2 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 7.4 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 8.5 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 4 KiB |
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 4 KiB |
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 6.2 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 6.7 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 6 KiB |
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 9.2 KiB |
Before Width: | Height: | Size: 6 KiB After Width: | Height: | Size: 6.7 KiB |
Before Width: | Height: | Size: 3 KiB After Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 7.5 KiB |
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 9.2 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 5 KiB After Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 13 KiB |