0
0
Fork 0
mirror of https://github.com/VueTubeApp/VueTube synced 2024-11-22 03:05:15 +00:00

Merge branch 'main' of github.com:Frontesque/VueTube into fullscreen-fixes

This commit is contained in:
Ethan 2022-03-23 10:31:04 -05:00
commit ce4e49af93
136 changed files with 616 additions and 333 deletions

View file

@ -1,3 +1 @@
<svg width="238" height="270" viewBox="0 0 238 270" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M157.761 148.395C168.456 142.242 168.456 126.811 157.761 120.658L119.096 98.412L158.969 79.8191L175.714 89.4534L229.951 120.658C240.646 126.811 240.646 142.242 229.951 148.395L175.732 179.588L175.714 179.599L157.643 189.996L117.77 171.403L157.761 148.395ZM36 172.994V190.783V190.79C36.0047 203.089 49.3079 210.784 59.9714 204.656L59.979 204.652L79.6367 193.342L119.509 211.935L77.9318 235.856L77.9038 235.872L23.979 266.897C13.3124 273.034 0 265.334 0 253.028L2.73036e-06 190.783L4.24704e-06 156.207L36 172.994ZM120.835 57.8797L80.9624 76.4726L59.979 64.4002L59.9661 64.3927C49.3016 58.2695 36 65.9676 36 78.2687V97.4389L6.08852e-06 114.226L7.66578e-06 78.2687L1.03961e-05 16.0238C1.09359e-05 3.71778 13.3124 -3.98159 23.979 2.15528L77.9253 33.1923L77.9318 33.196L120.835 57.8797Z" fill="black"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="238" height="270" fill="none" viewBox="0 0 238 270"><path fill="#000" fill-rule="evenodd" d="M157.761 148.395C168.456 142.242 168.456 126.811 157.761 120.658L119.096 98.412L158.969 79.8191L175.714 89.4534L229.951 120.658C240.646 126.811 240.646 142.242 229.951 148.395L175.732 179.588L175.714 179.599L157.643 189.996L117.77 171.403L157.761 148.395ZM36 172.994V190.783V190.79C36.0047 203.089 49.3079 210.784 59.9714 204.656L59.979 204.652L79.6367 193.342L119.509 211.935L77.9318 235.856L77.9038 235.872L23.979 266.897C13.3124 273.034 0 265.334 0 253.028L2.73036e-06 190.783L4.24704e-06 156.207L36 172.994ZM120.835 57.8797L80.9624 76.4726L59.979 64.4002L59.9661 64.3927C49.3016 58.2695 36 65.9676 36 78.2687V97.4389L6.08852e-06 114.226L7.66578e-06 78.2687L1.03961e-05 16.0238C1.09359e-05 3.71778 13.3124 -3.98159 23.979 2.15528L77.9253 33.1923L77.9318 33.196L120.835 57.8797Z" clip-rule="evenodd"/></svg>

Before

Width:  |  Height:  |  Size: 957 B

After

Width:  |  Height:  |  Size: 949 B

View file

@ -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>

View file

@ -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>

View file

@ -0,0 +1,45 @@
<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>

View file

@ -1,57 +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">
<v-card class="entry" :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>
</v-list-item>
</div>
</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>
export default {
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(" • ");
},
},
};
</script>

View file

@ -69,38 +69,6 @@
</v-app>
</template>
<style>
* {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen,
Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
}
.scroll-y {
overflow-y: scroll !important; /* has to be scroll, not auto */
-webkit-overflow-scrolling: touch !important;
}
html,
body {
background: black;
overflow: hidden;
}
p,
span,
div {
-webkit-user-select: none; /* Safari */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* IE10+/Edge */
user-select: none; /* Standard */
}
</style>
<style scoped>
.searchButton {
width: 100%;
justify-content: left !important;
}
</style>
<script>
import { App as CapacitorApp } from "@capacitor/app";
import { mapState } from "vuex";
@ -111,10 +79,35 @@ export default {
response: [],
stateLoaded: false,
}),
computed: {
...mapState({
roundTweak: (state) => state.tweaks.roundTweak,
}),
page: function () {
const splitPath = this.$route.path.split("/");
let pageName = splitPath[splitPath.length - 1];
pageName = pageName.charAt(0).toUpperCase() + pageName.slice(1);
return pageName || "Home";
},
},
watch: {
// Watch for any changes in the route string
// When change is detected, scroll main div back to the top
$route() {
this.$refs.pgscroll.scrollTop = 0; // scroll back to top when moving to new route
// Exit fullscreen if currently in fullscreen
this.$vuetube.statusBar.show();
this.$vuetube.navigationBar.show();
}
},
beforeCreate() {
// initializes UI tweaks to the saved state
this.$store.commit("tweaks/initTweaks");
},
mounted() {
this.stateLoaded = true;
//--- Back Button Listener ---//
@ -131,17 +124,6 @@ export default {
}
});
},
computed: {
...mapState({
roundTweak: (state) => state.tweaks.roundTweak,
}),
page: function () {
const splitPath = this.$route.path.split("/");
let pageName = splitPath[splitPath.length - 1];
pageName = pageName.charAt(0).toUpperCase() + pageName.slice(1);
return pageName || "Home";
},
},
methods: {
textChanged(text) {
@ -174,16 +156,41 @@ export default {
}
},
},
watch: {
// Watch for any changes in the route string
// When change is detected, scroll main div back to the top
$route() {
this.$refs.pgscroll.scrollTop = 0; // scroll back to top when moving to new route
// Exit fullscreen if currently in fullscreen
this.$vuetube.statusBar.show();
this.$vuetube.navigationBar.show();
}
}
};
</script>
<style>
* {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen,
Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
}
.scroll-y {
overflow-y: scroll !important; /* has to be scroll, not auto */
-webkit-overflow-scrolling: touch !important;
}
html,
body {
background: black;
overflow: hidden;
}
p,
span,
div {
-webkit-user-select: none; /* Safari */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* IE10+/Edge */
user-select: none; /* Standard */
}
.invert {
filter: invert(100%);
}
</style>
<style scoped>
.searchButton {
width: 100%;
justify-content: left !important;
}
</style>

View file

@ -4,11 +4,13 @@
* This is to allow use of "recommended" videos on other pages such as /watch
* -Front
* -->
<recommended :recommends="recommends" />
<horizontal-list-renderer :recommends="recommends" />
</template>
<script>
import horizontalListRenderer from "../components/horizontalListRenderer.vue";
export default {
components: { horizontalListRenderer },
data() {
return {
recommends: [],

View file

@ -1,22 +1,15 @@
<template>
<center class="container">
<v-img src="/icon.svg" width="10em" style="margin-bottom: 1em" />
<v-img
src="/icon.svg"
width="10em"
style="margin-bottom: 1em"
:class="$vuetify.theme.dark ? '' : 'invert'"
/>
<v-progress-circular size="50" indeterminate color="primary" />
</center>
</template>
<style scoped>
.container {
padding-top: 3em;
display: block;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -80%);
}
</style>
<script>
import { SplashScreen } from "@capacitor/splash-screen";
@ -63,3 +56,15 @@ export default {
},
};
</script>
<style scoped>
.container {
padding-top: 3em;
display: block;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -80%);
}
</style>

View file

@ -2,7 +2,11 @@
<center style="padding: 1em">
<div class="row pa-4" style="flex-direction: column">
<div>
<v-img src="/icon.svg" width="100px" />
<v-img
src="/icon.svg"
width="100px"
:class="$vuetify.theme.dark ? '' : 'invert'"
/>
</div>
<v-spacer />
<div>
@ -28,12 +32,6 @@
</center>
</template>
<style scoped>
.pageTitle {
margin-bottom: 1em;
}
</style>
<script>
import { Browser } from "@capacitor/browser";
import { Device } from "@capacitor/device";
@ -57,3 +55,9 @@ export default {
},
};
</script>
<style scoped>
.pageTitle {
margin-bottom: 1em;
}
</style>

View file

@ -5,22 +5,7 @@
<v-skeleton-loader type="card-avatar, article, actions" />
</center>
<v-list-item v-for="(video, index) in videos" :key="index" class="pa-0">
<v-card class="entry background" :to="`/watch?v=${video.id}`" flat>
<div style="position: relative">
<v-img :src="video.thumbnails[video.thumbnails.length - 1].url" />
<div
class="videoRuntimeFloat"
style="color: #fff"
v-text="video.runtime"
/>
</div>
<div class="px-4 pt-4" v-text="video.title" />
<v-card-text class="pt-0">
<div v-text="`${video.views} • ${video.uploaded}`" />
</v-card-text>
</v-card>
</v-list-item>
<horizontal-list-renderer :recommends="videos" />
</div>
</template>
@ -39,7 +24,9 @@
</style>
<script>
import horizontalListRenderer from "../components/horizontalListRenderer.vue";
export default {
components: { horizontalListRenderer },
data() {
return {
videos: [],
@ -51,9 +38,8 @@ export default {
methods: {
getSearch() {
const searchQuestion = this.$route.query.q;
const vm = this;
this.$youtube.search(searchQuestion, (data) => {
vm.videos = data;
this.$youtube.search(searchQuestion).then((response) => {
this.videos = response.items;
});
},
},

View file

@ -10,12 +10,20 @@
style="max-height: 50vh"
/>
<v-card v-if="loaded" class="ml-2 mr-2 background" flat>
<v-card-title class="mt-2"
style="padding-top: 0; padding-bottom: 0; font-size: 0.95rem; line-height: 1rem;"
<v-card-title
class="mt-2"
style="
padding-top: 0;
padding-bottom: 0;
font-size: 0.95rem;
line-height: 1rem;
"
v-text="title"
/>
<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 --->
<div style="display: flex; margin-bottom: 1em">
@ -33,7 +41,11 @@
@click="callMethodByName(item.actionName)"
>
<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-list-item>
@ -75,7 +87,7 @@
</v-bottom-sheet> -->
</v-card>
<recommended :recommends="recommends" />
<horizontal-list-renderer :recommends="recommends" />
</div>
</template>
@ -88,8 +100,10 @@
<script>
import { Share } from "@capacitor/share";
import horizontalListRenderer from "../components/horizontalListRenderer.vue";
export default {
components: { horizontalListRenderer },
data() {
return {
interactions: [
@ -139,7 +153,9 @@ export default {
console.log("Video info data", result);
console.log(result.availableResolutions);
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.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();
@ -148,11 +164,7 @@ export default {
this.interactions[0].value = result.metadata.likes;
this.loaded = true;
this.recommends = this.$youtube
.viewRecommends(result.renderedData.recommendations)
.filter((element) => {
return element !== undefined;
});
this.recommends = result.renderedData.recommendations;
// .catch((error) => this.$logger("Watch", error, true));
console.log("recommendations:", this.recommends);
});
@ -173,8 +185,8 @@ export default {
await Share.share({
title: this.title,
text: this.title,
url: 'https://youtu.be/' + this.$route.query.v,
dialogTitle: 'Share video',
url: "https://youtu.be/" + this.$route.query.v,
dialogTitle: "Share video",
});
},
handleFullscreenChange() {
@ -199,8 +211,8 @@ export default {
this.vidSrc = "";
this.getVideo();
}
}
}
}
},
},
},
};
</script>

View file

@ -39,8 +39,6 @@ module.exports = {
"x-goog-visitor-id": info.visitorData || "",
"x-youtube-client-name": ytApiVal.CLIENTNAME,
"x-youtube-client-version": ytApiVal.VERSION,
"x-origin": info.originalUrl,
origin: info.originalUrl,
};
return headers;
},

View file

@ -95,6 +95,65 @@ class Innertube {
};
}
async getVidAsync(id) {
let data = { context: this.context, videoId: id };
const responseNext = await Http.post({
url: `${constants.URLS.YT_BASE_API}/next?v=${id}`,
data: data,
headers: constants.INNERTUBE_HEADER(this.context.client),
}).catch((error) => error);
const response = await Http.post({
url: `${constants.URLS.YT_BASE_API}/player?key=${this.key}`,
data: data,
headers: constants.INNERTUBE_HEADER(this.context.client),
}).catch((error) => error);
if (response.error)
return {
success: false,
status_code: response.status,
message: response.message,
};
else if (responseNext.error)
return {
success: false,
status_code: responseNext.status,
message: responseNext.message,
};
return {
success: true,
status_code: response.status,
data: { output: response.data, outputNext: responseNext.data },
};
}
async searchAsync(query) {
let data = { context: this.context, query: query };
const response = await Http.post({
url: `${constants.URLS.YT_BASE_API}/search?key=${this.key}`,
data: data,
headers: { "Content-Type": "application/json" },
}).catch((error) => error);
if (response instanceof Error)
return {
success: false,
status_code: response.status,
message: response.message,
};
return {
success: true,
status_code: response.status,
data: response.data,
};
}
// Static methods
static getThumbnail(id, resolution) {
if (resolution == "max") {
const url = `https://img.youtube.com/vi/${id}/maxresdefault.jpg`;
@ -107,38 +166,6 @@ class Innertube {
return `https://img.youtube.com/vi/${id}/mqdefault.jpg`;
}
async getVidAsync(id) {
let data = { context: this.context, videoId: id };
const response = await Http.get({
url: `https://m.youtube.com/watch?v=${id}&pbj=1`,
params: {},
headers: Object.assign(this.header, {
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);
const responseMobile = await Http.post({
url: `${constants.URLS.YT_BASE_API}/player?key=${this.key}`,
data: data,
headers: constants.INNERTUBE_HEADER(this.context),
}).catch((error) => error);
if (response instanceof Error)
return {
success: false,
status_code: response.response.status,
message: response.message,
};
return {
success: true,
status_code: response.status,
data: { webOutput: response.data, appOutput: responseMobile.data },
};
}
// Simple Wrappers
async getRecommendationsAsync() {
const rec = await this.browseAsync("recommendations");
@ -150,67 +177,96 @@ class Innertube {
let response = await this.getVidAsync(id);
if (
response.success &&
response.data.webOutput[2].playerResponse?.playabilityStatus?.status ==
("ERROR" || undefined)
response.success == false ||
response.data.output?.playabilityStatus?.status == ("ERROR" || undefined)
)
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 responseApp = response.data.appOutput;
const details = responseWeb[2].playerResponse?.videoDetails;
const microformat =
responseWeb[2].playerResponse?.microformat?.playerMicroformatRenderer;
const renderedPanels = responseWeb[3].response?.engagementPanels;
const responseInfo = response.data.output;
const responseNext = response.data.outputNext;
const details = responseInfo.videoDetails;
// const columnUI =
// responseInfo[3].response?.contents.singleColumnWatchNextResults?.results
// ?.results;
const resolutions = responseInfo.streamingData;
const columnUI =
responseWeb[3].response?.contents.singleColumnWatchNextResults?.results
?.results;
const resolutions = responseApp.streamingData;
responseNext.contents.singleColumnWatchNextResults.results.results;
console.log(columnUI.contents.length);
return {
const vidData = {
id: details.videoId,
title: details.title || microformat.title?.runs[0].text,
isLive:
details.isLiveContent ||
microformat.liveBroadcastDetails?.isLiveNow ||
false,
channelName: details.author || microformat.ownerChannelName,
channelUrl: microformat.ownerProfileUrl,
title: details.title,
isLive: details.isLiveContent,
channelName: details.author,
channelUrl:
columnUI?.contents[0].slimVideoMetadataSectionRenderer?.contents.find(
(contents) => contents.elementRenderer
)?.newElement?.type?.componentType?.model?.channelBarModel
?.videoChannelBarData?.onTap?.innertubeCommand?.browseEndpoint
?.canonicalBaseUrl,
channelImg:
columnUI?.contents[0].slimVideoMetadataSectionRenderer?.contents.find(
(contents) => contents.elementRenderer
)?.newElement?.type?.componentType?.model?.channelBarModel
?.videoChannelBarData?.avatar?.image?.sources[0].url,
availableResolutions: resolutions?.formats,
availableResolutionsAdaptive: resolutions?.adaptiveFormats,
metadata: {
description: microformat.description?.runs[0].text,
descriptionShort: details.shortDescription,
thumbnails:
details.thumbnails?.thumbnails || microformat.thumbnails?.thumbnails,
isFamilySafe: microformat.isFamilySafe,
availableCountries: microformat.availableCountries,
liveBroadcastDetails: microformat.liveBroadcastDetails,
uploadDate: microformat.uploadDate,
publishDate: microformat.publishDate,
description: details.shortDescription,
thumbnails: details.thumbnails?.thumbnails,
uploadDate:
columnUI?.contents[0].slimVideoMetadataSectionRenderer?.contents.find(
(contents) => contents.slimVideoDescriptionRenderer
)?.slimVideoDescriptionRenderer.publishDate.runs[0].text,
isPrivate: details.isPrivate,
viewCount: details.viewCount || microformat.viewCount,
lengthSeconds: details.lengthSeconds || microformat.lengthSeconds,
viewCount: details.viewCount,
lengthSeconds: details.lengthSeconds,
likes: parseInt(
columnUI?.contents[1].slimVideoMetadataSectionRenderer?.contents[1].slimVideoActionBarRenderer?.buttons[0].slimMetadataToggleButtonRenderer?.button?.toggleButtonRenderer?.defaultText?.accessibility?.accessibilityData?.label?.replace(
/\D/g,
""
)
columnUI?.contents[0].slimVideoMetadataSectionRenderer?.contents
.find((contents) => contents.slimVideoScrollableActionBarRenderer)
?.slimVideoScrollableActionBarRenderer.buttons.find(
(button) => button.slimMetadataToggleButtonRenderer.isLike == true
)
?.slimMetadataToggleButtonRenderer?.button.toggleButtonRenderer?.defaultText?.accessibility?.accessibilityData?.label?.replace(
/\D/g,
""
)
), // Yes. I know.
},
renderedData: {
description:
renderedPanels[0].engagementPanelSectionListRenderer?.content
.structuredDescriptionContentRenderer?.items[1]
.expandableVideoDescriptionBodyRenderer?.descriptionBodyText.runs,
recommendations:
columnUI?.contents[columnUI.contents.length - 1].itemSectionRenderer
?.contents,
description: columnUI?.contents
.find((contents) => contents.slimVideoMetadataSectionRenderer)
.slimVideoMetadataSectionRenderer?.contents.find(
(contents) => contents.slimVideoDescriptionRenderer
)?.slimVideoDescriptionRenderer.description.runs,
recommendations: columnUI?.contents.find(
(contents) => contents.shelfRenderer
).shelfRenderer?.content?.horizontalListRenderer?.items,
recommendationsContinuation:
columnUI?.continuations[0].reloadContinuationData?.continuation,
},
};
console.log(vidData);
return vidData;
}
async getSearchAsync(query) {
const search = await this.searchAsync(query);
if (search.success == false)
throw new Error(
`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;
}
}

View file

@ -10,6 +10,8 @@ function useRender(video, renderer) {
return gridVideoRenderer(video);
case "compactAutoplayRenderer":
return compactAutoplayRenderer(video);
case "compactVideoRenderer":
return compactVideoRenderer(video);
default:
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) {
video = video.contents;
let item;
@ -86,4 +59,29 @@ function compactAutoplayRenderer(video) {
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;

View file

@ -111,30 +111,30 @@ const searchModule = {
});
},
search(text, callback) {
let results = new Array();
youtubeSearch(text, (videos) => {
for (const i in videos) {
const video = videos[i];
// 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);
},
// 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 () {
@ -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™
// Front page recommendation
async recommend() {
@ -226,34 +238,19 @@ const innertubeModule = {
const video =
shelves.shelfRenderer?.content?.horizontalListRenderer?.items;
if (video)
return video.map((item) => {
if (item) {
const renderedItem = useRender(
item[Object.keys(item)[0]],
Object.keys(item)[0]
);
console.log(renderedItem);
return renderedItem;
} else {
return undefined;
}
});
if (video) return video;
});
console.log(final);
return final;
},
// 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;
}
});
async search(query) {
try {
const response = await InnertubeAPI.getSearchAsync(query);
return response.content.verticalListRenderer;
} catch (err) {
logger(constants.LOGGER_NAMES.search, err, true);
}
},
};

View file

@ -1 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100%" height="100%" version="1.1" viewBox="0 0 512 512" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2"><rect width="512" height="512" x="0" y="0" style="fill:url(#_Linear1)"/><g><circle cx="256" cy="256" r="200" style="fill:#fff;fill-opacity:.1"/></g><path d="M318.761,269.869c10.695,-6.153 10.695,-21.584 0,-27.737l-38.665,-22.246l39.873,-18.593l16.745,9.634l54.237,31.205c10.695,6.153 10.695,21.584 0,27.737l-54.219,31.193l-0.018,0.011l-18.071,10.397l-39.873,-18.593l39.991,-23.008Zm-121.761,24.599l-0,17.796c0.005,12.299 13.308,19.994 23.971,13.866l0.008,-0.004l19.658,-11.31l39.872,18.593l-41.577,23.921l-0.028,0.016l-53.925,31.025c-10.667,6.137 -23.979,-1.563 -23.979,-13.869l-0,-96.821l36,16.787Zm84.835,-115.114l-39.873,18.593l-20.983,-12.073l-0.013,-0.007c-10.664,-6.123 -23.966,1.575 -23.966,13.876l-0,19.17l-36,16.787l-0,-98.202c-0,-12.306 13.312,-20.006 23.979,-13.869l53.946,31.037l0.007,0.004l42.903,24.684Z" style="fill:#fff"/><defs><linearGradient id="_Linear1" x1="0" x2="1" y1="0" y2="0" gradientTransform="matrix(512,512,-512,512,0,0)" gradientUnits="userSpaceOnUse"><stop offset="0" style="stop-color:#4a8;stop-opacity:1"/><stop offset="1" style="stop-color:#345;stop-opacity:1"/></linearGradient></defs></svg>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 512 512" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g transform="matrix(1.84351,0,0,1.84351,-236.17,-215.938)">
<path d="M318.761,269.869C329.456,263.716 329.456,248.285 318.761,242.132L280.096,219.886L319.969,201.293L336.714,210.927L390.951,242.132C401.646,248.285 401.646,263.716 390.951,269.869L336.732,301.062L336.714,301.073L318.643,311.47L278.77,292.877L318.761,269.869ZM197,294.468L197,312.264C197.005,324.563 210.308,332.258 220.971,326.13L220.979,326.126L240.637,314.816L280.509,333.409L238.932,357.33L238.904,357.346L184.979,388.371C174.312,394.508 161,386.808 161,374.502L161,277.681L197,294.468ZM281.835,179.354L241.962,197.947L220.979,185.874L220.966,185.867C210.302,179.744 197,187.442 197,199.743L197,218.913L161,235.7L161,137.498C161,125.192 174.312,117.492 184.979,123.629L238.925,154.666L238.932,154.67L281.835,179.354Z" style="fill:white;"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -10,16 +10,16 @@
"plugins": {
"SplashScreen": {
"launchShowDuration": 100,
"launchAutoHide": true,
"launchAutoHide": false,
"backgroundColor": "#111111",
"androidSplashResourceName": "splash",
"androidScaleType": "CENTER_CROP",
"androidSpinnerStyle": "large",
"iosSpinnerStyle": "small",
"spinnerColor": "#999999",
"showSpinner": true,
"splashFullScreen": true,
"splashImmersive": true
"showSpinner": false,
"splashFullScreen": false,
"splashImmersive": false
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 159 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 250 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 373 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 149 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 234 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 355 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 947 B

After

Width:  |  Height:  |  Size: 978 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 668 B

After

Width:  |  Height:  |  Size: 694 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View file

@ -12,16 +12,16 @@
"plugins": {
"SplashScreen": {
"launchShowDuration": 100,
"launchAutoHide": true,
"launchAutoHide": false,
"backgroundColor": "#111111",
"androidSplashResourceName": "splash",
"androidScaleType": "CENTER_CROP",
"androidSpinnerStyle": "large",
"iosSpinnerStyle": "small",
"spinnerColor": "#999999",
"showSpinner": true,
"splashFullScreen": true,
"splashImmersive": true
"showSpinner": false,
"splashFullScreen": false,
"splashImmersive": false
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 557 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 557 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 557 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 159 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 250 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 373 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 149 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 234 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 355 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 525 KiB

After

Width:  |  Height:  |  Size: 454 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

Some files were not shown because too many files have changed in this diff Show more