commit
6d77de76cd
6 changed files with 258 additions and 46 deletions
|
|
@ -130,7 +130,6 @@ hr {
|
|||
}
|
||||
|
||||
.progress-bar {
|
||||
border-radius: 15px;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
|
|
@ -164,6 +163,10 @@ hr {
|
|||
color: #fff;
|
||||
}
|
||||
|
||||
.progress,
|
||||
.progress-percentage {
|
||||
border-radius: unset !important;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.footer {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
<div v-if="showCompleted">
|
||||
<div class="columns is-multiline is-mobile has-text-centered" v-if="hasItems">
|
||||
<div class="column is-half-mobile">
|
||||
<div class="column is-half-mobile" v-if="display_style === 'cards'">
|
||||
<button type="button" class="button is-fullwidth is-ghost is-inverted"
|
||||
@click="masterSelectAll = !masterSelectAll">
|
||||
<span class="icon-text is-block">
|
||||
|
|
@ -74,7 +74,122 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="columns is-multiline">
|
||||
<div class="columns is-multiline" v-if="'list' === display_style">
|
||||
<div class="column is-12" v-if="hasItems">
|
||||
<div class="table-container">
|
||||
<table class="table is-striped is-hoverable is-fullwidth is-bordered" style="table-layout: fixed;">
|
||||
<thead>
|
||||
<tr class="has-text-centered is-unselectable">
|
||||
<th width="5%">
|
||||
<a href="#" @click.prevent="masterSelectAll = !masterSelectAll">
|
||||
<span class="icon-text is-block">
|
||||
<span class="icon">
|
||||
<i class="fa-regular"
|
||||
:class="{ 'fa-square-check': !masterSelectAll, 'fa-square': masterSelectAll }" />
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
</th>
|
||||
<th width="40%">Video Title</th>
|
||||
<th width="15%">Status</th>
|
||||
<th width="15%">Created</th>
|
||||
<th width="10%">Size/Starts</th>
|
||||
<th width="20%">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="item in sortCompleted" :key="item._id">
|
||||
<td class="is-vcentered has-text-centered">
|
||||
<label class="checkbox is-block">
|
||||
<input class="completed-checkbox" type="checkbox" v-model="selectedElms"
|
||||
:id="'checkbox-' + item._id" :value="item._id">
|
||||
</label>
|
||||
</td>
|
||||
<td class="is-vcentered">
|
||||
<div class="is-text-overflow" v-tooltip="item.title">
|
||||
<NuxtLink target="_blank" :href="item.url">{{ item.title }}</NuxtLink>
|
||||
</div>
|
||||
<div v-if="item.error">
|
||||
<span class="has-text-danger">{{ item.error }}</span>
|
||||
</div>
|
||||
<div v-if="showMessage(item)">
|
||||
<span class="has-text-danger">{{ item.msg }}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="is-vcentered has-text-centered is-unselectable">
|
||||
<span class="icon-text">
|
||||
<span class="icon" :class="setIconColor(item)"><i :class="setIcon(item)" /></span>
|
||||
<span>{{ setStatus(item) }}</span>
|
||||
</span>
|
||||
</td>
|
||||
<td class="is-vcentered has-text-centered is-unselectable">
|
||||
<span class="user-hint" :date-datetime="item.datetime"
|
||||
v-tooltip="moment(item.datetime).format('YYYY-M-DD H:mm Z')">
|
||||
{{ moment(item.datetime).fromNow() }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="is-vcentered has-text-centered is-unselectable"
|
||||
v-if="item.live_in && 'not_live' === item.status">
|
||||
<span :date-datetime="item.live_in" class="user-hint"
|
||||
v-tooltip="'Starts at: ' + moment(item.live_in).format('YYYY-M-DD H:mm Z')">
|
||||
{{ moment(item.live_in).fromNow() }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="is-vcentered has-text-centered is-unselectable" v-else>
|
||||
{{ item.file_size ? formatBytes(item.file_size) : '-' }}
|
||||
</td>
|
||||
<td class="is-vcentered is-items-center">
|
||||
<div class="field is-grouped is-grouped-centered">
|
||||
<div class="control" v-if="'finished' === item.status || isEmbedable(item.url)">
|
||||
<button v-if="'finished' === item.status" @click="playVideo(item)" v-tooltip="'Play video'"
|
||||
class="button is-danger is-light is-small">
|
||||
<span class="icon"><i class="fa-solid fa-play" /></span>
|
||||
</button>
|
||||
<button v-else @click="embed_url = getEmbedable(item.url)" v-tooltip="'Play video'"
|
||||
class="button is-danger is-light is-small">
|
||||
<span class="icon"><i class="fa-solid fa-play" /></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="control" v-if="item.status != 'finished'">
|
||||
<button class="button is-warning is-fullwidth is-small" v-tooltip="'Re-queue video'"
|
||||
@click="reQueueItem(item)">
|
||||
<span class="icon"><i class="fa-solid fa-rotate-right" /></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="control" v-if="config.app?.keep_archive && item.status != 'finished'">
|
||||
<button class="button is-danger is-light is-fullwidth is-small" v-tooltip="'Add link to archive'"
|
||||
@click="archiveItem(item)">
|
||||
<span class="icon"><i class="fa-solid fa-box-archive" /></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="control" v-if="item.filename && item.status === 'finished'">
|
||||
<a class="button is-link is-fullwidth is-small" :href="makeDownload(config, item)"
|
||||
v-tooltip="'Download video'" :download="item.filename?.split('/').reverse()[0]">
|
||||
<span class="icon"><i class="fa-solid fa-download" /></span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="control" v-if="item.url && !config.app.basic_mode">
|
||||
<button class="button is-info is-fullwidth is-small" @click="emitter('getInfo', item.url)"
|
||||
v-tooltip="'Show video information'">
|
||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="control">
|
||||
<button class="button is-danger is-fullwidth is-small" @click="removeItem(item)"
|
||||
v-tooltip="'Remove video'">
|
||||
<span class="icon"><i class="fa-solid fa-trash-can" /></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="columns is-multiline" v-else>
|
||||
<LateLoader :unrender="true" :min-height="hideThumbnail ? 210 : 410" class="column is-6"
|
||||
v-for="item in sortCompleted" :key="item._id">
|
||||
<div class="card"
|
||||
|
|
@ -166,7 +281,7 @@
|
|||
</div>
|
||||
<div class="columns is-mobile is-multiline">
|
||||
<div class="column is-half-mobile" v-if="item.status != 'finished'">
|
||||
<a class="button is-warning is-fullwidth" v-tooltip="'Re-queue item.'" @click="reQueueItem(item)">
|
||||
<a class="button is-warning is-fullwidth" @click="reQueueItem(item)">
|
||||
<span class="icon-text is-block">
|
||||
<span class="icon"><i class="fa-solid fa-rotate-right" /></span>
|
||||
<span>Re-queue</span>
|
||||
|
|
@ -182,8 +297,7 @@
|
|||
</a>
|
||||
</div>
|
||||
<div class="column is-half-mobile" v-if="config.app?.keep_archive && item.status != 'finished'">
|
||||
<a class="button is-danger is-light is-fullwidth" v-tooltip="'Add link to archive.'"
|
||||
@click="archiveItem(item)">
|
||||
<a class="button is-danger is-light is-fullwidth" @click="archiveItem(item)">
|
||||
<span class="icon-text is-block">
|
||||
<span class="icon"><i class="fa-solid fa-box-archive" /></span>
|
||||
<span>Archive</span>
|
||||
|
|
@ -268,6 +382,7 @@ const masterSelectAll = ref(false)
|
|||
const showCompleted = useStorage('showCompleted', true)
|
||||
const hideThumbnail = useStorage('hideThumbnailHistory', false)
|
||||
const direction = useStorage('sortCompleted', 'desc')
|
||||
const display_style = useStorage('display_style', 'cards')
|
||||
|
||||
const embed_url = ref('')
|
||||
const video_item = ref(null)
|
||||
|
|
@ -439,11 +554,11 @@ const setStatus = item => {
|
|||
}
|
||||
|
||||
if ('cancelled' === item.status) {
|
||||
return 'User Cancelled'
|
||||
return display_style.value === 'cards' ? 'User Cancelled' : 'Cancelled'
|
||||
}
|
||||
|
||||
if ('not_live' === item.status) {
|
||||
return 'Live Stream'
|
||||
return display_style.value === 'cards' ? 'Live Stream' : 'Live'
|
||||
}
|
||||
|
||||
return item.status
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
<div v-if="showQueue">
|
||||
<div class="columns is-multiline is-mobile has-text-centered" v-if="hasQueuedItems">
|
||||
<div class="column is-half-mobile">
|
||||
<div class="column is-half-mobile" v-if="'cards' === display_style">
|
||||
<button type="button" class="button is-fullwidth is-ghost" @click="masterSelectAll = !masterSelectAll">
|
||||
<span class="icon-text is-block">
|
||||
<span class="icon">
|
||||
|
|
@ -21,7 +21,7 @@
|
|||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="column is-half-mobile">
|
||||
<div class="column is-half-mobile" v-if="('cards' === display_style || hasSelected)">
|
||||
<button type="button" class="button is-fullwidth is-danger" :disabled="!hasSelected" @click="cancelSelected">
|
||||
<span class="icon-text is-block">
|
||||
<span class="icon">
|
||||
|
|
@ -33,7 +33,90 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="columns is-multiline">
|
||||
<div class="columns is-multiline" v-if="'list' === display_style">
|
||||
<div class="column is-12" v-if="hasQueuedItems">
|
||||
<div class="table-container">
|
||||
<table class="table is-striped is-hoverable is-fullwidth is-bordered" style="table-layout: fixed;">
|
||||
<thead>
|
||||
<tr class="has-text-centered is-unselectable">
|
||||
<th width="5%">
|
||||
<a href="#" @click.prevent="masterSelectAll = !masterSelectAll">
|
||||
<span class="icon-text is-block">
|
||||
<span class="icon">
|
||||
<i class="fa-regular"
|
||||
:class="{ 'fa-square-check': !masterSelectAll, 'fa-square': masterSelectAll }" />
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
</th>
|
||||
<th width="40%">Video Title</th>
|
||||
<th width="15%">Status</th>
|
||||
<th width="20%">Progress</th>
|
||||
<th width="15%">Created</th>
|
||||
<th width="10%">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="item in stateStore.queue" :key="item._id">
|
||||
<td class="has-text-centered is-vcentered">
|
||||
<label class="checkbox is-block">
|
||||
<input class="completed-checkbox" type="checkbox" v-model="selectedElms"
|
||||
:id="'checkbox-' + item._id" :value="item._id">
|
||||
</label>
|
||||
</td>
|
||||
<td class="is-text-overflow is-vcentered" v-tooltip="item.title">
|
||||
<NuxtLink target="_blank" :href="item.url">{{ item.title }}</NuxtLink>
|
||||
</td>
|
||||
<td class="has-text-centered is-text-overflow is-unselectable">
|
||||
<span class="icon-text">
|
||||
<span class="icon" :class="setIconColor(item)">
|
||||
<i class="fas fa-solid" :class="setIcon(item)" />
|
||||
</span>
|
||||
<span v-text="setStatus(item)" />
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<div class="progress-bar is-unselectable">
|
||||
<div class="progress-percentage">{{ updateProgress(item) }}</div>
|
||||
<div class="progress" :style="{ width: percentPipe(item.percent) + '%' }"></div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="has-text-centered is-text-overflow is-unselectable">
|
||||
<span :data-datetime="item.datetime"
|
||||
v-tooltip="moment(item.datetime).format('MMMM Do YYYY, h:mm:ss a')">
|
||||
{{ moment(item.datetime).fromNow() }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="is-vcentered is-items-center">
|
||||
<div class="field is-grouped is-grouped-centered">
|
||||
<div class="control" v-if="isEmbedable(item.url)">
|
||||
<button @click="() => embed_url = getEmbedable(item.url)" v-tooltip="'Play video'"
|
||||
class="button is-danger is-light is-small">
|
||||
<span class="icon"><i class="fa-solid fa-play" /></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="control">
|
||||
<button class="button is-warning is-small" @click="confirmCancel(item);"
|
||||
v-tooltip="'Cancel download'">
|
||||
<span class="icon"><i class="fa-solid fa-eject" /></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="control" v-if="item.url && !config.app.basic_mode">
|
||||
<button class="button is-info is-small" @click="emitter('getInfo', item.url)"
|
||||
v-tooltip="'Show video information'">
|
||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="columns is-multiline" v-else>
|
||||
<LateLoader :unrender="true" :min-height="hideThumbnail ? 265 : 475" class="column is-6"
|
||||
v-for="item in stateStore.queue" :key="item._id">
|
||||
<div class="card">
|
||||
|
|
@ -69,13 +152,13 @@
|
|||
<div class="card-content">
|
||||
<div class="columns is-multiline is-mobile">
|
||||
<div class="column is-12">
|
||||
<div class="progress-bar is-round">
|
||||
<div class="progress-bar is-unselectable">
|
||||
<div class="progress-percentage">{{ updateProgress(item) }}</div>
|
||||
<div class="progress" :style="{ width: percentPipe(item.percent) + '%' }"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-half-mobile has-text-centered is-text-overflow">
|
||||
<span class="icon-text">
|
||||
<span class="icon-text is-unselectable">
|
||||
<span class="icon" :class="setIconColor(item)">
|
||||
<i class="fas fa-solid" :class="setIcon(item)" />
|
||||
</span>
|
||||
|
|
@ -98,11 +181,9 @@
|
|||
</div>
|
||||
<div class="columns is-multiline is-mobile">
|
||||
<div class="column is-half-mobile">
|
||||
<button class="button is-danger is-fullwidth" @click="confirmCancel(item);">
|
||||
<button class="button is-warning is-fullwidth" @click="confirmCancel(item);">
|
||||
<span class="icon-text is-block">
|
||||
<span class="icon">
|
||||
<i class="fa-solid fa-trash-can" />
|
||||
</span>
|
||||
<span class="icon"><i class="fa-solid fa-eject" /></span>
|
||||
<span>Cancel</span>
|
||||
</span>
|
||||
</button>
|
||||
|
|
@ -152,7 +233,7 @@
|
|||
|
||||
<script setup>
|
||||
import moment from 'moment'
|
||||
import { set, useStorage } from '@vueuse/core'
|
||||
import { useStorage } from '@vueuse/core'
|
||||
import { ucFirst } from '~/utils/index'
|
||||
import { isEmbedable, getEmbedable } from '~/utils/embedable'
|
||||
|
||||
|
|
@ -165,6 +246,8 @@ const selectedElms = ref([]);
|
|||
const masterSelectAll = ref(false);
|
||||
const showQueue = useStorage('showQueue', true)
|
||||
const hideThumbnail = useStorage('hideThumbnailQueue', false)
|
||||
const display_style = useStorage('display_style', 'cards')
|
||||
|
||||
const embed_url = ref('')
|
||||
|
||||
const bg_enable = useStorage('random_bg', true)
|
||||
|
|
@ -344,7 +427,7 @@ watch(embed_url, v => {
|
|||
if (!bg_enable.value) {
|
||||
return
|
||||
}
|
||||
document.querySelector('body').setAttribute("style", `opacity: ${ v ? 1 : bg_opacity.value}`)
|
||||
document.querySelector('body').setAttribute("style", `opacity: ${v ? 1 : bg_opacity.value}`)
|
||||
})
|
||||
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -9,13 +9,32 @@
|
|||
</span>
|
||||
</header>
|
||||
<div class="card-content">
|
||||
<div class="field">
|
||||
<label class="label" for="random_bg">Color scheme</label>
|
||||
<div class="control">
|
||||
<label for="auto" class="radio">
|
||||
<input id="auto" type="radio" v-model="selectedTheme" value="auto">
|
||||
System Default
|
||||
</label>
|
||||
<label for="light" class="radio">
|
||||
<input id="light" type="radio" v-model="selectedTheme" value="light">
|
||||
Light
|
||||
</label>
|
||||
<label for="dark" class="radio">
|
||||
<input id="dark" type="radio" v-model="selectedTheme" value="dark">
|
||||
Dark
|
||||
</label>
|
||||
</div>
|
||||
<p class="help">Select the color scheme for the WebUI.</p>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label" for="random_bg">Backgrounds</label>
|
||||
<div class="control">
|
||||
<input id="random_bg" type="checkbox" class="switch is-success" v-model="bg_enable">
|
||||
<label for="random_bg" class="is-unselectable"> Enable</label>
|
||||
<p class="help">Use random background image from your media backends.</p>
|
||||
</div>
|
||||
<p class="help">Use random background image from your media backends.</p>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
|
|
@ -25,8 +44,8 @@
|
|||
<div class="control">
|
||||
<input id="random_bg_opacity" style="width: 100%" type="range" v-model="bg_opacity" min="0.50" max="1.00"
|
||||
step="0.05">
|
||||
<p class="help">How visible the background image should be.</p>
|
||||
</div>
|
||||
<p class="help">How visible the background image should be.</p>
|
||||
</div>
|
||||
|
||||
<div class="field" v-if="bg_enable">
|
||||
|
|
@ -41,10 +60,9 @@
|
|||
<span>Reload</span>
|
||||
</span>
|
||||
</button>
|
||||
<p class="help">Change the displayed picture.</p>
|
||||
</div>
|
||||
<p class="help">Change the displayed picture.</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -65,5 +83,6 @@ defineProps({
|
|||
|
||||
const bg_enable = useStorage('random_bg', true)
|
||||
const bg_opacity = useStorage('random_bg_opacity', 0.85)
|
||||
const selectedTheme = useStorage('theme', 'auto')
|
||||
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -59,21 +59,6 @@
|
|||
</NuxtLink>
|
||||
</div>
|
||||
|
||||
<div class="navbar-item">
|
||||
<button class="button is-dark has-tooltip-bottom" v-tooltip.bottom="'Switch to Light theme'"
|
||||
v-if="'auto' == selectedTheme" @click="selectTheme('light')">
|
||||
<span class="icon has-text-warning"><i class="fas fa-sun" /></span>
|
||||
</button>
|
||||
<button class="button is-dark has-tooltip-bottom" v-tooltip.bottom="'Switch to Dark theme'"
|
||||
v-if="'light' == selectedTheme" @click="selectTheme('dark')">
|
||||
<span class="icon"><i class="fas fa-moon" /></span>
|
||||
</button>
|
||||
<button class="button is-dark has-tooltip-bottom" v-tooltip.bottom="'Switch to auto theme'"
|
||||
v-if="'dark' == selectedTheme" @click="selectTheme('auto')">
|
||||
<span class="icon"><i class="fas fa-microchip" /></span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="navbar-item is-hidden-mobile">
|
||||
<button class="button is-dark" @click="reloadPage">
|
||||
<span class="icon"><i class="fas fa-refresh"></i></span>
|
||||
|
|
@ -191,6 +176,10 @@ watch(() => config.app.sentry_dsn, dsn => {
|
|||
onMounted(async () => {
|
||||
try {
|
||||
await handleImage(bg_enable.value)
|
||||
} catch (e) {
|
||||
}
|
||||
|
||||
try {
|
||||
applyPreferredColorScheme(selectedTheme.value)
|
||||
} catch (e) {
|
||||
}
|
||||
|
|
@ -204,13 +193,6 @@ watch(selectedTheme, value => {
|
|||
|
||||
const reloadPage = () => window.location.reload()
|
||||
|
||||
const selectTheme = theme => {
|
||||
selectedTheme.value = theme
|
||||
if ('auto' === theme) {
|
||||
return reloadPage()
|
||||
}
|
||||
}
|
||||
|
||||
watch(bg_enable, async v => await handleImage(v))
|
||||
watch(bg_opacity, v => {
|
||||
if (false === bg_enable.value) {
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@
|
|||
|
||||
<div class="is-pulled-right" v-if="socket.isConnected && false === config.app.basic_mode">
|
||||
<div class="field is-grouped">
|
||||
|
||||
<p class="control" v-if="config.app.has_cookies">
|
||||
<button class="button is-purple" @click="checkCookies" v-tooltip.bottom="'Check youtube cookies status.'"
|
||||
:disabled="isChecking">
|
||||
|
|
@ -56,6 +55,14 @@
|
|||
</button>
|
||||
</p>
|
||||
|
||||
<p class="control">
|
||||
<button v-tooltip.bottom="'Change display style'" class="button has-tooltip-bottom"
|
||||
@click="() => changeDisplay()">
|
||||
<span class="icon"><i class="fa-solid"
|
||||
:class="{ 'fa-table': display_style === 'cards', 'fa-table-list': display_style === 'list' }" /></span>
|
||||
</button>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="is-hidden-mobile">
|
||||
|
|
@ -86,6 +93,7 @@ const isChecking = ref(false)
|
|||
const get_info = ref('')
|
||||
const bg_enable = useStorage('random_bg', true)
|
||||
const bg_opacity = useStorage('random_bg_opacity', 0.85)
|
||||
const display_style = useStorage('display_style', 'cards')
|
||||
|
||||
onMounted(() => {
|
||||
if (!config.app.ui_update_title) {
|
||||
|
|
@ -149,4 +157,6 @@ watch(get_info, v => {
|
|||
|
||||
document.querySelector('body').setAttribute("style", `opacity: ${v ? 1 : bg_opacity.value}`)
|
||||
})
|
||||
|
||||
const changeDisplay = () => display_style.value = display_style.value === 'cards' ? 'list' : 'cards'
|
||||
</script>
|
||||
|
|
|
|||
Loading…
Reference in a new issue