mirror of https://github.com/VueTubeApp/VueTube
Merge branch 'main' of https://github.com/VueTubeApp/VueTube into main
This commit is contained in:
commit
26d79330d2
|
@ -0,0 +1,37 @@
|
|||
name: ❓ Question
|
||||
description: Ask a quesion related to VueTube
|
||||
labels: [question]
|
||||
body:
|
||||
|
||||
- type: textarea
|
||||
id: question
|
||||
attributes:
|
||||
label: Ask your question
|
||||
description: What do you want to know?
|
||||
placeholder: |
|
||||
Example:
|
||||
"How do I add a plugin?"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: aditional-info
|
||||
attributes:
|
||||
label: Aditional information
|
||||
placeholder: |
|
||||
Additional useful information, for example, a screenshot.
|
||||
|
||||
- type: checkboxes
|
||||
id: acknowledgements
|
||||
attributes:
|
||||
label: Acknowledgements
|
||||
description: Your question will be closed if you haven't done these steps.
|
||||
options:
|
||||
- label: I have searched the existing issues and this is a new question, **NOT** a duplicate or related to another open issue.
|
||||
required: true
|
||||
- label: I have written a short but informative title.
|
||||
required: true
|
||||
- label: I will fill out all of the requested information in this form.
|
||||
required: true
|
||||
- label: My question isn't asked in FAQ (Frequently Asked Questions).
|
||||
required: true
|
|
@ -106,7 +106,7 @@ jobs:
|
|||
CODE_SIGNING_ALLOWED="NO"
|
||||
CODE_SIGN_ENTITLEMENTS=""
|
||||
- name: Make IPA
|
||||
run: mkdir Payload && mv ~/Library/Developer/Xcode/DerivedData/App-*/Build/Products/Debug-iphoneos/App.app Payload && zip -r Payload.zip Payload && mv Payload.zip VueTube.ipa
|
||||
run: mkdir Payload && mv ~/Library/Developer/Xcode/DerivedData/App-*/Build/Products/Debug-maccatalyst/App.app/ Payload && zip -r Payload.zip Payload && mv Payload.zip VueTube.ipa
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
|
|
|
@ -4,10 +4,13 @@
|
|||
text
|
||||
small
|
||||
color="white"
|
||||
style="position: absolute; bottom: 0.25rem; right: 0.25rem"
|
||||
style="position: absolute; right: 0.25rem"
|
||||
:style="fullscreen ? 'bottom: 3.5rem' : 'bottom: 0.25rem'"
|
||||
@click.stop="$emit('fullscreen')"
|
||||
>
|
||||
<v-icon>{{ fullscreen ? "mdi-fullscreen-exit" : "mdi-fullscreen" }}</v-icon>
|
||||
<v-icon size="1.25rem">{{
|
||||
fullscreen ? "mdi-fullscreen-exit" : "mdi-fullscreen"
|
||||
}}</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -74,7 +74,11 @@
|
|||
|
||||
<div
|
||||
style="transition: opacity 0.15s ease-in-out"
|
||||
:style="controls ? 'opacity: 1;' : 'opacity: 0; pointer-events: none'"
|
||||
:style="
|
||||
controls && !seeking
|
||||
? 'opacity: 1;'
|
||||
: 'opacity: 0; pointer-events: none'
|
||||
"
|
||||
>
|
||||
<minimize />
|
||||
<loop />
|
||||
|
@ -143,7 +147,89 @@
|
|||
<v-icon size="1rem">mdi-fast-forward-5</v-icon>
|
||||
</v-btn>
|
||||
|
||||
<watchtime v-if="$refs.player" :video="$refs.player" />
|
||||
<watchtime
|
||||
v-if="$refs.player"
|
||||
:video="$refs.player"
|
||||
:fullscreen="isFullscreen"
|
||||
/>
|
||||
<v-btn
|
||||
v-if="isFullscreen"
|
||||
fab
|
||||
text
|
||||
small
|
||||
outlined
|
||||
style="position: absolute; bottom: 0.25rem; left: 1rem"
|
||||
color="white"
|
||||
disabled
|
||||
@click.stop=""
|
||||
>
|
||||
<v-icon>mdi-thumb-up-outline</v-icon>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-if="isFullscreen"
|
||||
fab
|
||||
text
|
||||
small
|
||||
outlined
|
||||
style="position: absolute; bottom: 0.25rem; left: 4rem"
|
||||
color="white"
|
||||
disabled
|
||||
@click.stop=""
|
||||
>
|
||||
<v-icon>mdi-thumb-down-outline</v-icon>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-if="isFullscreen"
|
||||
fab
|
||||
text
|
||||
small
|
||||
outlined
|
||||
style="position: absolute; bottom: 0.25rem; left: 7rem"
|
||||
color="white"
|
||||
disabled
|
||||
@click.stop=""
|
||||
>
|
||||
<v-icon>mdi-share-outline</v-icon>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-if="isFullscreen"
|
||||
fab
|
||||
text
|
||||
small
|
||||
outlined
|
||||
style="position: absolute; bottom: 0.25rem; left: 10rem"
|
||||
color="white"
|
||||
disabled
|
||||
@click.stop=""
|
||||
>
|
||||
<v-icon>mdi-plus-box-multiple-outline</v-icon>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-if="isFullscreen"
|
||||
fab
|
||||
text
|
||||
small
|
||||
outlined
|
||||
style="position: absolute; bottom: 0.25rem; left: 13rem"
|
||||
color="white"
|
||||
disabled
|
||||
@click.stop=""
|
||||
>
|
||||
<v-icon>mdi-comment-text-outline</v-icon>
|
||||
</v-btn>
|
||||
|
||||
<v-btn
|
||||
v-if="isFullscreen"
|
||||
fab
|
||||
text
|
||||
small
|
||||
style="position: absolute; bottom: 0.25rem; right: 0.25rem"
|
||||
color="white"
|
||||
disabled
|
||||
@click.stop=""
|
||||
>
|
||||
<v-icon>mdi-cards-outline</v-icon>
|
||||
</v-btn>
|
||||
<!-- // TODO: merge the bottom 2 into 1 reusable component -->
|
||||
<quality v-if="$refs.player" :video="$refs.player" :sources="sources" />
|
||||
<speed v-if="$refs.player" :video="$refs.player" />
|
||||
|
@ -151,8 +237,27 @@
|
|||
:fullscreen="isFullscreen"
|
||||
@fullscreen="(controls = $refs.player.paused), handleFullscreenChange()"
|
||||
/>
|
||||
<v-btn
|
||||
v-if="isFullscreen"
|
||||
fab
|
||||
text
|
||||
small
|
||||
style="position: absolute; bottom: 0.25rem; right: 0.25rem"
|
||||
color="white"
|
||||
disabled
|
||||
@click.stop=""
|
||||
>
|
||||
<v-icon>mdi-cards-outline</v-icon>
|
||||
</v-btn>
|
||||
</div>
|
||||
<!-- NOTE: breaks in fullscreen -->
|
||||
<progressbar
|
||||
v-if="$refs.player"
|
||||
:video="$refs.player"
|
||||
:seeking="seeking"
|
||||
:controls="controls"
|
||||
:fullscreen="isFullscreen"
|
||||
:current-time="$refs.player.currentTime"
|
||||
/>
|
||||
<seekbar
|
||||
v-if="$refs.player"
|
||||
v-show="!isFullscreen || controls"
|
||||
|
@ -160,8 +265,17 @@
|
|||
:video="$refs.player"
|
||||
:sources="sources"
|
||||
:controls="controls"
|
||||
:current-time="$refs.player.currentTime"
|
||||
@seeking="seeking = !seeking"
|
||||
/>
|
||||
<sponsorblock
|
||||
v-if="$refs.player"
|
||||
:video="$refs.player"
|
||||
:seeking="seeking"
|
||||
:videoid="videoid"
|
||||
:controls="controls"
|
||||
:fullscreen="isFullscreen"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -176,8 +290,12 @@ import captions from "~/components/Player/captions.vue";
|
|||
import playpause from "~/components/Player/playpause.vue";
|
||||
import watchtime from "~/components/Player/watchtime.vue";
|
||||
import fullscreen from "~/components/Player/fullscreen.vue";
|
||||
import progressbar from "~/components/Player/progressbar.vue";
|
||||
import sponsorblock from "~/components/Player/sponsorblock.vue";
|
||||
export default {
|
||||
components: {
|
||||
sponsorblock,
|
||||
progressbar,
|
||||
fullscreen,
|
||||
watchtime,
|
||||
playpause,
|
||||
|
@ -198,6 +316,10 @@ export default {
|
|||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
videoid: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -213,21 +335,6 @@ export default {
|
|||
this.vidSrc = this.sources[this.sources.length - 1].url;
|
||||
// TODO: detect orientation change and enter fullscreen
|
||||
// TODO: detect video loading state and send this.loading to play button :loading = loading
|
||||
|
||||
|
||||
this.$youtube.getSponsorBlock(this.$route.query.v, (data) => {
|
||||
sponsorBlock = data.segment;
|
||||
});
|
||||
|
||||
this.$refs.player.ontimeupdate = () => {
|
||||
let vidTime = this.$refs.player.currentTime;
|
||||
for (let i = 0; i < sponsorBlock.length; i++) {
|
||||
if (vidTime > sponsorBlock[i][0] && vidTime < sponsorBlock[0][i]) {
|
||||
this.$refs.player.currentTime = sponsorBlock[i][0];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.isFullscreen) this.exitFullscreen();
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
<template>
|
||||
<v-progress-linear
|
||||
style="
|
||||
z-index: 2;
|
||||
position: absolute;
|
||||
background: #ffffff22;
|
||||
transform: translateY(50%);
|
||||
"
|
||||
background-opacity="0.5"
|
||||
background-color="white"
|
||||
:buffer-value="buffered"
|
||||
:value="(currentTime / video.duration) * 100"
|
||||
:class="!fullscreen || controls ? '' : 'invisible'"
|
||||
color="primary"
|
||||
:height="seeking ? 4 : 2"
|
||||
:style="
|
||||
fullscreen
|
||||
? 'width: calc(100% - 2rem); left: 1rem; bottom: 3.25rem;'
|
||||
: 'width: 100%; left: 0; bottom: 1px;'
|
||||
"
|
||||
/>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
video: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
seeking: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
fullscreen: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
currentTime: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
controls: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data: () => ({
|
||||
buffered: 0,
|
||||
}),
|
||||
mounted() {
|
||||
this.video.addEventListener("loadeddata", (e) => {
|
||||
if (this.video.readyState >= 3) {
|
||||
this.video.addEventListener("progress", () => {
|
||||
this.buffered =
|
||||
(this.video.buffered.end(0) / this.video.duration) * 100;
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
</script>
|
|
@ -18,13 +18,25 @@
|
|||
{{ sources.find((src) => src.url == video.src).qualityLabel }}
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-card
|
||||
v-touch="{
|
||||
down: () => (sheet = false),
|
||||
}"
|
||||
class="background"
|
||||
>
|
||||
<v-subheader>Quality for current video</v-subheader>
|
||||
<v-card class="background">
|
||||
<v-subheader
|
||||
v-touch="{
|
||||
down: () => (sheet = false),
|
||||
}"
|
||||
>
|
||||
Quality for current video
|
||||
<v-btn
|
||||
fab
|
||||
text
|
||||
small
|
||||
color="white"
|
||||
style="position: absolute; right: 0.25rem"
|
||||
@click="sheet = false"
|
||||
>
|
||||
<v-icon>mdi-close</v-icon>
|
||||
</v-btn>
|
||||
</v-subheader>
|
||||
<v-divider />
|
||||
<v-card-text style="max-height: 50vh" class="pa-0">
|
||||
<v-list-item
|
||||
v-for="src in sources"
|
||||
|
|
|
@ -7,34 +7,20 @@
|
|||
style="display: none"
|
||||
:src="vidWrs"
|
||||
/>
|
||||
<v-progress-linear
|
||||
query
|
||||
active
|
||||
style="width: 100%; background: #ffffff22"
|
||||
background-opacity="0.5"
|
||||
background-color="white"
|
||||
:buffer-value="buffered"
|
||||
:value="percent"
|
||||
color="primary"
|
||||
height="3"
|
||||
:style="
|
||||
fullscreen
|
||||
? 'width: calc(100% - 2rem); left: 1rem; position: absolute; bottom: 3rem;'
|
||||
: 'width: 100%'
|
||||
"
|
||||
/>
|
||||
<!-- Scrubber -->
|
||||
<v-slider
|
||||
id="scrubber"
|
||||
hide-details
|
||||
height="2"
|
||||
dense
|
||||
color="transparent"
|
||||
thumb-color="primary"
|
||||
track-color="transparent"
|
||||
:class="!controls && !fullscreen && !scrubbing ? 'invisible' : ''"
|
||||
style="position: absolute; z-index: 2"
|
||||
style="position: absolute; z-index: 4"
|
||||
:style="
|
||||
fullscreen
|
||||
? 'width: calc(100% - 2rem); left: 1rem; bottom: 3rem;'
|
||||
? 'width: calc(100% - 2rem); left: 1rem; bottom: 51px;'
|
||||
: 'width: calc(100% - 0.8rem); left: 0.4rem; bottom: 0;'
|
||||
"
|
||||
:thumb-size="0"
|
||||
|
@ -70,43 +56,64 @@
|
|||
|
||||
<script>
|
||||
export default {
|
||||
props: ["sources", "video", "controls", "fullscreen"],
|
||||
|
||||
data() {
|
||||
return {
|
||||
scrubbing: false,
|
||||
percent: 0,
|
||||
progress: 0,
|
||||
buffered: 0,
|
||||
duration: 0,
|
||||
vidSrc: "",
|
||||
vidWrs: "",
|
||||
};
|
||||
props: {
|
||||
video: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
controls: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
fullscreen: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
sources: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
currentTime: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data: () => ({
|
||||
scrubbing: false,
|
||||
progress: 0,
|
||||
duration: 0,
|
||||
vidWrs: "",
|
||||
}),
|
||||
mounted() {
|
||||
console.log("sources", this.sources);
|
||||
this.vidSrc = this.sources[this.sources.length - 1].url;
|
||||
this.vidWrs = this.sources[1].url;
|
||||
let vid = this.video;
|
||||
vid.addEventListener("loadeddata", (e) => {
|
||||
// console.log("%c loadeddata", "color: #00ff00");
|
||||
console.log(e);
|
||||
//Video should now be loaded but we can add a second check
|
||||
if (vid.readyState >= 3) {
|
||||
vid.ontimeupdate = () => {
|
||||
// console.log("%c timeupdate", "color: #aaaaff");
|
||||
this.duration = vid.duration;
|
||||
if (!this.scrubbing) this.progress = vid.currentTime;
|
||||
this.percent = (vid.currentTime / vid.duration) * 100;
|
||||
};
|
||||
vid.onprogress = () => {
|
||||
// console.log("%c progress", "color: #ff00ff");
|
||||
this.buffered = (vid.buffered.end(0) / vid.duration) * 100;
|
||||
};
|
||||
this.video.addEventListener("loadeddata", (e) => {
|
||||
if (this.video.readyState >= 3) {
|
||||
this.video.addEventListener("timeupdate", () => {
|
||||
this.duration = this.video.duration;
|
||||
if (!this.scrubbing) this.progress = this.currentTime;
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
seek(e) {
|
||||
// console.log(`scrubbing ${e}`);
|
||||
let vid = this.$refs.playerfake;
|
||||
let canvas = this.$refs.preview;
|
||||
this.$refs.playerfake.currentTime = e;
|
||||
canvas
|
||||
.getContext("2d")
|
||||
.drawImage(
|
||||
vid,
|
||||
0,
|
||||
0,
|
||||
this.video.clientWidth / 3,
|
||||
this.video.clientHeight / 3
|
||||
);
|
||||
},
|
||||
scrub(e) {
|
||||
this.video.currentTime = e;
|
||||
},
|
||||
// TODO: better scrubbing preview
|
||||
loadVideoFrames() {
|
||||
// Exit loop if desired number of frames have been extracted
|
||||
|
@ -228,24 +235,6 @@ export default {
|
|||
console.log(this.frames);
|
||||
},
|
||||
// TODO: scrubbing preview end
|
||||
seek(e) {
|
||||
// console.log(`scrubbing ${e}`);
|
||||
let vid = this.$refs.playerfake;
|
||||
let canvas = this.$refs.preview;
|
||||
this.$refs.playerfake.currentTime = e;
|
||||
canvas
|
||||
.getContext("2d")
|
||||
.drawImage(
|
||||
vid,
|
||||
0,
|
||||
0,
|
||||
this.video.clientWidth / 3,
|
||||
this.video.clientHeight / 3
|
||||
);
|
||||
},
|
||||
scrub(e) {
|
||||
this.video.currentTime = e;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -18,13 +18,25 @@
|
|||
{{ video.playbackRate }}X
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-card
|
||||
v-touch="{
|
||||
down: () => (sheet = false),
|
||||
}"
|
||||
class="background"
|
||||
>
|
||||
<v-subheader>Playback Speed</v-subheader>
|
||||
<v-card class="background">
|
||||
<v-subheader
|
||||
v-touch="{
|
||||
down: () => (sheet = false),
|
||||
}"
|
||||
>
|
||||
Playback Speed
|
||||
<v-btn
|
||||
fab
|
||||
text
|
||||
small
|
||||
color="white"
|
||||
style="position: absolute; right: 0.25rem"
|
||||
@click="sheet = false"
|
||||
>
|
||||
<v-icon>mdi-close</v-icon>
|
||||
</v-btn>
|
||||
</v-subheader>
|
||||
<v-divider />
|
||||
<v-card-text style="height: 50vh" class="pa-0">
|
||||
<v-list-item
|
||||
v-for="sped in speeds"
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
<template>
|
||||
<div>
|
||||
<v-progress-linear
|
||||
v-for="block in blocks"
|
||||
:key="block.UUID"
|
||||
:buffer-value="(block.segment[1] / video.duration) * 100"
|
||||
:value="(block.segment[0] / video.duration) * 100"
|
||||
style="
|
||||
z-index: 3;
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
background: transparent;
|
||||
transform: translateY(50%);
|
||||
"
|
||||
:class="!fullscreen || controls ? '' : 'invisible'"
|
||||
background-color="white"
|
||||
background-opacity="1"
|
||||
color="transparent"
|
||||
:height="seeking ? 4 : 2"
|
||||
:style="
|
||||
fullscreen
|
||||
? 'width: calc(100% - 2rem); left: 1rem; bottom: 3.25rem;'
|
||||
: 'width: 100%; left: 0; bottom: 1px;'
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
video: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
seeking: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
videoid: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
fullscreen: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
controls: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data: () => ({
|
||||
blocks: [],
|
||||
}),
|
||||
mounted() {
|
||||
let vid = this.video;
|
||||
let id = this.videoid;
|
||||
|
||||
vid.addEventListener("loadeddata", (e) => {
|
||||
if (vid.readyState >= 3) {
|
||||
this.$youtube.getSponsorBlock(id, (data) => {
|
||||
console.log("sbreturn", data);
|
||||
if (Array.isArray(data)) {
|
||||
this.blocks = data;
|
||||
|
||||
// iterate over data.segments array
|
||||
vid.addEventListener("timeupdate", () => {
|
||||
// console.log("sb check", data);
|
||||
data.forEach((sponsor) => {
|
||||
let vidTime = vid.currentTime;
|
||||
|
||||
if (
|
||||
vidTime >= sponsor.segment[0] &&
|
||||
vidTime <= sponsor.segment[1]
|
||||
) {
|
||||
console.log("Skipping the sponsor");
|
||||
this.$youtube.showToast("Skipped sponsor");
|
||||
vid.currentTime = sponsor.segment[1] + 1;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.sponsor {
|
||||
color: green;
|
||||
}
|
||||
.selfpromo {
|
||||
color: yellow;
|
||||
}
|
||||
.exclusive_access {
|
||||
color: orange;
|
||||
}
|
||||
.interaction {
|
||||
color: blue;
|
||||
}
|
||||
.intro {
|
||||
color: purple;
|
||||
}
|
||||
.outro {
|
||||
color: purple;
|
||||
}
|
||||
.music_offtopic {
|
||||
color: red;
|
||||
}
|
||||
.poi_highlight {
|
||||
color: #ff00ff;
|
||||
}
|
||||
.filler {
|
||||
color: blue;
|
||||
}
|
||||
</style>
|
|
@ -1,12 +1,7 @@
|
|||
<template>
|
||||
<div
|
||||
style="
|
||||
color: #fff;
|
||||
left: 1rem;
|
||||
bottom: 1rem;
|
||||
font-size: 0.75rem;
|
||||
position: absolute;
|
||||
"
|
||||
style="color: #fff; left: 1rem; font-size: 0.75rem; position: absolute"
|
||||
:style="fullscreen ? 'bottom: 4.25rem' : 'bottom: 1rem'"
|
||||
>
|
||||
{{ watched }}
|
||||
<span style="color: #aaa"> / {{ duration }} </span>
|
||||
|
@ -15,7 +10,16 @@
|
|||
|
||||
<script>
|
||||
export default {
|
||||
props: ["video"],
|
||||
props: {
|
||||
video: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
fullscreen: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
watched: 0,
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
<style scoped>
|
||||
.description {
|
||||
white-space: pre-line;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
ref="player"
|
||||
:video="video"
|
||||
:sources="sources"
|
||||
:videoid="$route.query.v"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
@ -21,13 +22,13 @@
|
|||
<v-card v-if="loaded" class="background rounded-0" flat>
|
||||
<div
|
||||
v-ripple
|
||||
class="d-flex justify-space-between align-start px-3 pt-3"
|
||||
class="d-flex justify-space-between align-start px-3 pt-4"
|
||||
@click="showMore = !showMore"
|
||||
>
|
||||
<div class="d-flex flex-column">
|
||||
<v-card-title
|
||||
class="pa-0"
|
||||
style="font-size: 0.95rem; line-height: 1.15rem"
|
||||
style="font-size: 0.95rem; line-height: 1.15rem; overflow-wrap: break-word;"
|
||||
v-text="video.title"
|
||||
v-emoji
|
||||
/>
|
||||
|
@ -163,7 +164,7 @@
|
|||
|
||||
<!-- Description -->
|
||||
<div v-if="showMore">
|
||||
<div class="scroll-y ma-4">
|
||||
<div class="scroll-y ma-4 pt-1">
|
||||
<slim-video-description-renderer
|
||||
:render="video.renderedData.description"
|
||||
/>
|
||||
|
@ -227,6 +228,7 @@
|
|||
hide-overlay
|
||||
persistent
|
||||
no-click-animation
|
||||
style="z-index: 2 !important"
|
||||
attach="#content-container"
|
||||
>
|
||||
<mainCommentRenderer
|
||||
|
|
|
@ -32,7 +32,9 @@ const module = {
|
|||
|
||||
}),
|
||||
|
||||
|
||||
async addPlugin(content) {
|
||||
|
||||
await ensureStructure();
|
||||
new Promise(async (resolve, reject) => {
|
||||
const fileName = require("./utils").getCpn(); // Im not sure what this is actually meant for but im using it as a random string generator
|
||||
|
|
|
@ -134,35 +134,18 @@ const module = {
|
|||
humanTime(seconds = 0) {
|
||||
seconds = Math.floor(seconds); // Not doing this seems to break the calculation
|
||||
let levels = [
|
||||
Math.floor(seconds / 31536000), //Years
|
||||
Math.floor((seconds % 31536000) / 86400), //Days
|
||||
Math.floor(((seconds % 31536000) % 86400) / 3600), //Hours
|
||||
Math.floor(seconds / 31536000) || null, //Years
|
||||
Math.floor((seconds % 31536000) / 86400) || null, //Days
|
||||
Math.floor(((seconds % 31536000) % 86400) / 3600) || null, //Hours
|
||||
Math.floor((((seconds % 31536000) % 86400) % 3600) / 60), //Minutes
|
||||
(((seconds % 31536000) % 86400) % 3600) % 60, //Seconds
|
||||
Math.floor((((seconds % 31536000) % 86400) % 3600) % 60), //Seconds
|
||||
];
|
||||
|
||||
let returntext = new String();
|
||||
for (const i in levels) {
|
||||
const num =
|
||||
levels[i].toString().length == 1 ? "0" + levels[i] : levels[i]; // If Number Is Single Digit, Add 0 In Front
|
||||
returntext += ":" + num;
|
||||
levels = levels.filter((level) => level !== null);
|
||||
for (let i = 1; i < levels.length; i++) {
|
||||
levels[i] = levels[i].toString().padStart(2, "0");
|
||||
}
|
||||
while (returntext.startsWith(":00")) {
|
||||
returntext = returntext.substring(3);
|
||||
} // Remove Prepending 0s (eg. 00:00:00:01:00)
|
||||
if (returntext.startsWith(":0")) {
|
||||
returntext = returntext.substring(2);
|
||||
} else {
|
||||
returntext = returntext.substring(1);
|
||||
} // Prevent Time Starting With 0 (eg. 01:00)
|
||||
|
||||
if (!returntext.includes(":")) {
|
||||
if (returntext.length == 1) {
|
||||
returntext = "0" + returntext; // Make tens digit in seconds always visible (eg. 0:09)
|
||||
}
|
||||
returntext = "0:" + returntext; // Make minutes visible as 0 when sub 60 seconds (eg. 0:51)
|
||||
}
|
||||
|
||||
// join the array into a string with : as a separator
|
||||
let returntext = levels.join(":");
|
||||
return returntext;
|
||||
},
|
||||
//--- End Convert Time To Human Readable String ---//
|
||||
|
|
|
@ -78,6 +78,9 @@ const searchModule = {
|
|||
logger("codeRun", err, true);
|
||||
callback(err);
|
||||
});
|
||||
},
|
||||
showToast(text) {
|
||||
Toast.show({ text: text });
|
||||
}
|
||||
};
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 7.7 KiB |
|
@ -1,17 +1,17 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="deploymentTargetDropDown">
|
||||
<targetSelectedWithDropDown>
|
||||
<runningDeviceTargetSelectedWithDropDown>
|
||||
<Target>
|
||||
<type value="QUICK_BOOT_TARGET" />
|
||||
<type value="RUNNING_DEVICE_TARGET" />
|
||||
<deviceKey>
|
||||
<Key>
|
||||
<type value="VIRTUAL_DEVICE_PATH" />
|
||||
<value value="$USER_HOME$/.android/avd/Pixel_3a_API_31_arm64-v8a.avd" />
|
||||
<type value="SERIAL_NUMBER" />
|
||||
<value value="adb-97QAY11P1S-NELaqI._adb-tls-connect._tcp." />
|
||||
</Key>
|
||||
</deviceKey>
|
||||
</Target>
|
||||
</targetSelectedWithDropDown>
|
||||
<timeTargetWasSelectedWithDropDown value="2022-05-14T02:50:17.689302Z" />
|
||||
</runningDeviceTargetSelectedWithDropDown>
|
||||
<timeTargetWasSelectedWithDropDown value="2022-06-07T16:21:41.251850Z" />
|
||||
</component>
|
||||
</project>
|
|
@ -2,5 +2,9 @@
|
|||
<widget version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
|
||||
<access origin="*" />
|
||||
|
||||
<feature name="CDVOrientation">
|
||||
<param name="android-package" value="cordova.plugins.screenorientation.CDVOrientation"/>
|
||||
</feature>
|
||||
|
||||
|
||||
</widget>
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package cordova.plugins.screenorientation;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.apache.cordova.CordovaPlugin;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.util.Log;
|
||||
|
||||
public class CDVOrientation extends CordovaPlugin {
|
||||
|
||||
private static final String TAG = "YoikScreenOrientation";
|
||||
|
||||
/**
|
||||
* Screen Orientation Constants
|
||||
*/
|
||||
|
||||
private static final String ANY = "any";
|
||||
private static final String PORTRAIT_PRIMARY = "portrait-primary";
|
||||
private static final String PORTRAIT_SECONDARY = "portrait-secondary";
|
||||
private static final String LANDSCAPE_PRIMARY = "landscape-primary";
|
||||
private static final String LANDSCAPE_SECONDARY = "landscape-secondary";
|
||||
private static final String PORTRAIT = "portrait";
|
||||
private static final String LANDSCAPE = "landscape";
|
||||
|
||||
@Override
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) {
|
||||
|
||||
Log.d(TAG, "execute action: " + action);
|
||||
|
||||
// Route the Action
|
||||
if (action.equals("screenOrientation")) {
|
||||
return routeScreenOrientation(args, callbackContext);
|
||||
}
|
||||
|
||||
// Action not found
|
||||
callbackContext.error("action not recognised");
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean routeScreenOrientation(JSONArray args, CallbackContext callbackContext) {
|
||||
|
||||
String action = args.optString(0);
|
||||
|
||||
|
||||
|
||||
String orientation = args.optString(1);
|
||||
|
||||
Log.d(TAG, "Requested ScreenOrientation: " + orientation);
|
||||
|
||||
Activity activity = cordova.getActivity();
|
||||
|
||||
if (orientation.equals(ANY)) {
|
||||
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
|
||||
} else if (orientation.equals(LANDSCAPE_PRIMARY)) {
|
||||
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
|
||||
} else if (orientation.equals(PORTRAIT_PRIMARY)) {
|
||||
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
||||
} else if (orientation.equals(LANDSCAPE)) {
|
||||
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
|
||||
} else if (orientation.equals(PORTRAIT)) {
|
||||
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
|
||||
} else if (orientation.equals(LANDSCAPE_SECONDARY)) {
|
||||
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
|
||||
} else if (orientation.equals(PORTRAIT_SECONDARY)) {
|
||||
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
|
||||
}
|
||||
|
||||
callbackContext.success();
|
||||
return true;
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -2,5 +2,9 @@
|
|||
<widget version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
|
||||
<access origin="*" />
|
||||
|
||||
<feature name="CDVOrientation">
|
||||
<param name="ios-package" value="CDVOrientation"/>
|
||||
</feature>
|
||||
|
||||
|
||||
</widget>
|
|
@ -9,17 +9,18 @@ install! 'cocoapods', :disable_input_output_paths => true
|
|||
def capacitor_pods
|
||||
pod 'Capacitor', :path => '../../node_modules/@capacitor/ios'
|
||||
pod 'CapacitorCordova', :path => '../../node_modules/@capacitor/ios'
|
||||
pod 'CapacitorCommunityHttp', :path => '..\..\node_modules\@capacitor-community\http'
|
||||
pod 'CapacitorApp', :path => '..\..\node_modules\@capacitor\app'
|
||||
pod 'CapacitorBrowser', :path => '..\..\node_modules\@capacitor\browser'
|
||||
pod 'CapacitorDevice', :path => '..\..\node_modules\@capacitor\device'
|
||||
pod 'CapacitorFilesystem', :path => '..\..\node_modules\@capacitor\filesystem'
|
||||
pod 'CapacitorHaptics', :path => '..\..\node_modules\@capacitor\haptics'
|
||||
pod 'CapacitorShare', :path => '..\..\node_modules\@capacitor\share'
|
||||
pod 'CapacitorSplashScreen', :path => '..\..\node_modules\@capacitor\splash-screen'
|
||||
pod 'CapacitorStatusBar', :path => '..\..\node_modules\@capacitor\status-bar'
|
||||
pod 'CapacitorToast', :path => '..\..\node_modules\@capacitor\toast'
|
||||
pod 'HugotomaziCapacitorNavigationBar', :path => '..\..\node_modules\@hugotomazi\capacitor-navigation-bar'
|
||||
pod 'CapacitorCommunityHttp', :path => '../../node_modules/@capacitor-community/http'
|
||||
pod 'CapacitorApp', :path => '../../node_modules/@capacitor/app'
|
||||
pod 'CapacitorBrowser', :path => '../../node_modules/@capacitor/browser'
|
||||
pod 'CapacitorDevice', :path => '../../node_modules/@capacitor/device'
|
||||
pod 'CapacitorFilesystem', :path => '../../node_modules/@capacitor/filesystem'
|
||||
pod 'CapacitorHaptics', :path => '../../node_modules/@capacitor/haptics'
|
||||
pod 'CapacitorShare', :path => '../../node_modules/@capacitor/share'
|
||||
pod 'CapacitorSplashScreen', :path => '../../node_modules/@capacitor/splash-screen'
|
||||
pod 'CapacitorStatusBar', :path => '../../node_modules/@capacitor/status-bar'
|
||||
pod 'CapacitorToast', :path => '../../node_modules/@capacitor/toast'
|
||||
pod 'HugotomaziCapacitorNavigationBar', :path => '../../node_modules/@hugotomazi/capacitor-navigation-bar'
|
||||
pod 'CordovaPlugins', :path => '../capacitor-cordova-ios-plugins'
|
||||
end
|
||||
|
||||
target 'App' do
|
||||
|
|
Loading…
Reference in New Issue