Make it possible to embed content if the content is being downloaded or yet to complete.
This commit is contained in:
parent
f89e72666b
commit
64c84e5c3a
4 changed files with 137 additions and 5 deletions
30
ui/components/EmbedPlayer.vue
Normal file
30
ui/components/EmbedPlayer.vue
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
<template>
|
||||||
|
<div class="content">
|
||||||
|
<h1 class="has-text-white">Not downloaded yet.</h1>
|
||||||
|
<figure class="image is-16by9">
|
||||||
|
<iframe class="has-ratio" :src="url" frameborder="0" allowfullscreen />
|
||||||
|
</figure>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { onMounted, onUnmounted } from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
url: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const emitter = defineEmits(['closeModel'])
|
||||||
|
|
||||||
|
const eventFunc = e => {
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
emitter('closeModel')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => window.addEventListener('keydown', eventFunc))
|
||||||
|
onUnmounted(() => window.removeEventListener('keydown', eventFunc))
|
||||||
|
</script>
|
||||||
|
|
@ -103,6 +103,12 @@
|
||||||
v-if="item.extras?.thumbnail" />
|
v-if="item.extras?.thumbnail" />
|
||||||
<img v-else src="/images/placeholder.png" />
|
<img v-else src="/images/placeholder.png" />
|
||||||
</span>
|
</span>
|
||||||
|
<span v-else-if="isEmbedable(item.url)" @click="embed_url = getEmbedable(item.url)" class="play-overlay">
|
||||||
|
<div class="play-icon"></div>
|
||||||
|
<img @load="e => pImg(e)" :src="'/api/thumbnail?url=' + encodePath(item.extras.thumbnail)"
|
||||||
|
v-if="item.extras?.thumbnail" />
|
||||||
|
<img v-else src="/images/placeholder.png" />
|
||||||
|
</span>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<img @load="e => pImg(e)" v-if="item.extras?.thumbnail"
|
<img @load="e => pImg(e)" v-if="item.extras?.thumbnail"
|
||||||
:src="'/api/thumbnail?url=' + encodePath(item.extras.thumbnail)" />
|
:src="'/api/thumbnail?url=' + encodePath(item.extras.thumbnail)" />
|
||||||
|
|
@ -243,6 +249,14 @@
|
||||||
</div>
|
</div>
|
||||||
<button class="modal-close is-large" aria-label="close" @click="closeVideo"></button>
|
<button class="modal-close is-large" aria-label="close" @click="closeVideo"></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="modal is-active" v-if="embed_url">
|
||||||
|
<div class="modal-background" @click="embed_url = ''"></div>
|
||||||
|
<div class="modal-content">
|
||||||
|
<EmbedPlayer :url="embed_url" @closeModel="embed_url = ''" />
|
||||||
|
</div>
|
||||||
|
<button class="modal-close is-large" aria-label="close" @click="embed_url = ''"></button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -251,6 +265,7 @@ import moment from 'moment'
|
||||||
import { useStorage } from '@vueuse/core'
|
import { useStorage } from '@vueuse/core'
|
||||||
import { makeDownload, formatBytes, ucFirst } from '~/utils/index'
|
import { makeDownload, formatBytes, ucFirst } from '~/utils/index'
|
||||||
import toast from '~/plugins/toast'
|
import toast from '~/plugins/toast'
|
||||||
|
import { isEmbedable, getEmbedable } from '~/utils/embedable'
|
||||||
|
|
||||||
const emitter = defineEmits(['getInfo'])
|
const emitter = defineEmits(['getInfo'])
|
||||||
const config = useConfigStore()
|
const config = useConfigStore()
|
||||||
|
|
@ -263,6 +278,7 @@ const showCompleted = useStorage('showCompleted', true)
|
||||||
const hideThumbnail = useStorage('hideThumbnailHistory', false)
|
const hideThumbnail = useStorage('hideThumbnailHistory', false)
|
||||||
const direction = useStorage('sortCompleted', 'desc')
|
const direction = useStorage('sortCompleted', 'desc')
|
||||||
|
|
||||||
|
const embed_url = ref('')
|
||||||
const video_item = ref(null)
|
const video_item = ref(null)
|
||||||
|
|
||||||
const playVideo = item => video_item.value = item
|
const playVideo = item => video_item.value = item
|
||||||
|
|
|
||||||
|
|
@ -52,11 +52,18 @@
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<div v-if="false === hideThumbnail" class="card-image">
|
<div v-if="false === hideThumbnail" class="card-image">
|
||||||
<figure class="image is-3by1" v-if="item.extras?.thumbnail">
|
<figure class="image is-3by1">
|
||||||
<img :alt="item.title" :src="'/api/thumbnail?url=' + encodePath(item.extras.thumbnail)" />
|
<span v-if="isEmbedable(item.url)" @click="() => embed_url = getEmbedable(item.url)" class="play-overlay">
|
||||||
</figure>
|
<div class="play-icon"></div>
|
||||||
<figure class="image is-3by1" v-else>
|
<img @load="e => pImg(e)" :src="'/api/thumbnail?url=' + encodePath(item.extras.thumbnail)"
|
||||||
<img :src="'/images/placeholder.png'" />
|
v-if="item.extras?.thumbnail" />
|
||||||
|
<img v-else src="/images/placeholder.png" />
|
||||||
|
</span>
|
||||||
|
<template v-else>
|
||||||
|
<img @load="e => pImg(e)" v-if="item.extras?.thumbnail"
|
||||||
|
:src="'/api/thumbnail?url=' + encodePath(item.extras.thumbnail)" />
|
||||||
|
<img v-else src="/images/placeholder.png" />
|
||||||
|
</template>
|
||||||
</figure>
|
</figure>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
|
|
@ -133,6 +140,14 @@
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="modal is-active" v-if="embed_url">
|
||||||
|
<div class="modal-background" @click="embed_url = ''"></div>
|
||||||
|
<div class="modal-content">
|
||||||
|
<EmbedPlayer :url="embed_url" @closeModel="embed_url = ''" />
|
||||||
|
</div>
|
||||||
|
<button class="modal-close is-large" aria-label="close" @click="embed_url = ''"></button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -140,6 +155,7 @@
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import { useStorage } from '@vueuse/core'
|
import { useStorage } from '@vueuse/core'
|
||||||
import { ucFirst } from '~/utils/index'
|
import { ucFirst } from '~/utils/index'
|
||||||
|
import { isEmbedable, getEmbedable } from '~/utils/embedable'
|
||||||
|
|
||||||
const emitter = defineEmits(['getInfo'])
|
const emitter = defineEmits(['getInfo'])
|
||||||
const config = useConfigStore();
|
const config = useConfigStore();
|
||||||
|
|
@ -150,6 +166,7 @@ const selectedElms = ref([]);
|
||||||
const masterSelectAll = ref(false);
|
const masterSelectAll = ref(false);
|
||||||
const showQueue = useStorage('showQueue', true)
|
const showQueue = useStorage('showQueue', true)
|
||||||
const hideThumbnail = useStorage('hideThumbnailQueue', false)
|
const hideThumbnail = useStorage('hideThumbnailQueue', false)
|
||||||
|
const embed_url = ref('')
|
||||||
|
|
||||||
watch(masterSelectAll, (value) => {
|
watch(masterSelectAll, (value) => {
|
||||||
for (const key in stateStore.queue) {
|
for (const key in stateStore.queue) {
|
||||||
|
|
|
||||||
69
ui/utils/embedable.js
Normal file
69
ui/utils/embedable.js
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
const sources = [
|
||||||
|
{
|
||||||
|
name: 'youtube',
|
||||||
|
url: 'https://www.youtube-nocookie.com/embed/',
|
||||||
|
regex: /(?:https?:\/\/)?(?:www\.)?(?:youtube\.com\/(?:[^\/\n\s]+\/\S+\/|(?:v|e(?:mbed)?)\/|\S*?[?&]v=)|youtu\.be\/)([a-zA-Z0-9_-]{11})/,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "instagram",
|
||||||
|
url: "https://www.instagram.com/p/",
|
||||||
|
regex: /https?:\/\/(?:www\.)?instagram\.com\/p\/([^/?#&]+)/,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "twitter",
|
||||||
|
url: "https://twitter.com/i/status/",
|
||||||
|
regex: /https?:\/\/(?:www\.)?twitter\.com\/i\/status\/([^/?#&]+)/,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "facebook",
|
||||||
|
url: "https://www.facebook.com/plugins/post.php?href=",
|
||||||
|
regex: /https?:\/\/(?:www\.)?facebook\.com\/(?:[^/?#&]+)\/posts\/([^/?#&]+)/,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "tiktok",
|
||||||
|
url: "https://www.tiktok.com/embed/",
|
||||||
|
regex: /https?:\/\/(?:www\.)?tiktok\.com\/(?:[^/?#&]+)\/video\/([^/?#&]+)/,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "vimeo",
|
||||||
|
url: "https://player.vimeo.com/video/",
|
||||||
|
regex: /https?:\/\/(?:www\.)?vimeo\.com\/(?:[^/?#&]+)\/([^/?#&]+)/,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "soundcloud",
|
||||||
|
url: "https://w.soundcloud.com/player/?url=",
|
||||||
|
regex: /https?:\/\/(?:www\.)?soundcloud\.com\/(?:[^/?#&]+)\/([^/?#&]+)/,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "spotify",
|
||||||
|
url: "https://open.spotify.com/embed/",
|
||||||
|
regex: /https?:\/\/(?:open\.)?spotify\.com\/(?:[^/?#&]+)\/([^/?#&]+)/,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "twitch",
|
||||||
|
url: "https://player.twitch.tv/?channel=",
|
||||||
|
regex: /https?:\/\/(?:www\.)?twitch\.tv\/(?:[^/?#&]+)\/([^/?#&]+)/,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "mixcloud",
|
||||||
|
url: "https://www.mixcloud.com/widget/iframe/",
|
||||||
|
regex: /https?:\/\/(?:www\.)?mixcloud\.com\/(?:[^/?#&]+)\/([^/?#&]+)/,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "dailymotion",
|
||||||
|
url: "https://www.dailymotion.com/embed/video/",
|
||||||
|
regex: /https?:\/\/(?:www\.)?dailymotion\.com\/(?:[^/?#&]+)\/video\/([^/?#&]+)/,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const isEmbedable = url => {
|
||||||
|
return sources.some(source => source.regex.test(url));
|
||||||
|
}
|
||||||
|
|
||||||
|
const getEmbedable = url => {
|
||||||
|
const source = sources.find(source => source.regex.test(url));
|
||||||
|
const id = source.regex.exec(url)[1];
|
||||||
|
return source.url + id;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { isEmbedable, getEmbedable };
|
||||||
Loading…
Reference in a new issue