more updates for new UI
This commit is contained in:
parent
9fc047dba3
commit
c61fd133d8
12 changed files with 351 additions and 197 deletions
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
|
|
@ -84,7 +84,7 @@
|
|||
"--port",
|
||||
"8082"
|
||||
],
|
||||
"runtimeExecutable": "npm",
|
||||
"runtimeExecutable": "yarn",
|
||||
"type": "node",
|
||||
"cwd": "${workspaceFolder}/ui",
|
||||
"env": {
|
||||
|
|
|
|||
|
|
@ -224,8 +224,6 @@ import moment from "moment";
|
|||
import { useStorage } from '@vueuse/core'
|
||||
import LazyLoader from './LazyLoader'
|
||||
import { makeDownload, formatBytes, ucFirst } from '~/utils/index'
|
||||
import { useConfigStore } from '~/store/ConfigStore';
|
||||
import { useStateStore } from '~/store/StateStore';
|
||||
|
||||
const emits = defineEmits(['deleteItem', 'addItem', 'playItem', 'archiveItem'])
|
||||
const config = useConfigStore()
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
<div class="column is-12">
|
||||
<div class="control has-icons-left">
|
||||
<input type="url" class="input" id="url" placeholder="Video or playlist link"
|
||||
:disabled="!config.isConnected || addInProgress" v-model="url">
|
||||
:disabled="!socket.isConnected || addInProgress" v-model="url">
|
||||
<span class="icon is-small is-left">
|
||||
<i class="fa-solid fa-link" />
|
||||
</span>
|
||||
|
|
@ -19,9 +19,9 @@
|
|||
</div>
|
||||
<div class="control is-expanded">
|
||||
<div class="select is-fullwidth">
|
||||
<select id="preset" class="is-fullwidth" :disabled="!config.isConnected" v-model="selectedPreset">
|
||||
<option v-for="item in downloadFormats" :key="item.id" :value="item.id">
|
||||
{{ item.text }}
|
||||
<select id="preset" class="is-fullwidth" :disabled="!socket.isConnected" v-model="selectedPreset">
|
||||
<option v-for="item in config.presets" :key="item.name" :value="item.format">
|
||||
{{ item.name }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
|
@ -35,27 +35,23 @@
|
|||
</div>
|
||||
<div class="control is-expanded">
|
||||
<input type="text" class="input is-fullwidth" id="path" v-model="downloadPath" placeholder="Default"
|
||||
:disabled="!config.isConnected" list="directories">
|
||||
:disabled="!socket.isConnected" list="directories">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column">
|
||||
<button type="submit" class="button is-primary" @click="addDownload"
|
||||
:class="{ 'is-loading': !config.isConnected || addInProgress }"
|
||||
:disabled="!config.isConnected || addInProgress || !url">
|
||||
<span class="icon">
|
||||
<i class="fa-solid fa-plus" />
|
||||
</span>
|
||||
<span>Add Link</span>
|
||||
:class="{ 'is-loading': !socket.isConnected || addInProgress }"
|
||||
:disabled="!socket.isConnected || addInProgress || !url">
|
||||
<span class="icon"><i class="fa-solid fa-plus" /></span>
|
||||
<span>Add</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="column">
|
||||
<button type="submit" class="button is-info" @click="showAdvanced = !showAdvanced"
|
||||
v-tooltip="'Show advanced options'" :class="{ 'is-loading': !config.isConnected }"
|
||||
:disabled="!config.isConnected">
|
||||
<span class="icon">
|
||||
<i class="fa-solid fa-cog" />
|
||||
</span>
|
||||
v-tooltip="'Show advanced options'" :class="{ 'is-loading': !socket.isConnected }"
|
||||
:disabled="!socket.isConnected">
|
||||
<span class="icon"><i class="fa-solid fa-cog" /></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -84,7 +80,7 @@
|
|||
</label>
|
||||
<div class="control">
|
||||
<textarea class="textarea" id="ytdlpConfig" v-model="ytdlpConfig"
|
||||
:disabled="!config.isConnected"></textarea>
|
||||
:disabled="!socket.isConnected"></textarea>
|
||||
</div>
|
||||
<span class="subtitle is-6 has-text-info">
|
||||
Some config fields are ignored like cookiefile, path, and output_format etc.
|
||||
|
|
@ -101,7 +97,7 @@
|
|||
</label>
|
||||
<div class="control">
|
||||
<textarea class="textarea" id="ytdlpCookies" v-model="ytdlpCookies"
|
||||
:disabled="!config.isConnected"></textarea>
|
||||
:disabled="!socket.isConnected"></textarea>
|
||||
</div>
|
||||
<span class="subtitle is-6 has-text-info">
|
||||
Use something like <a class="has-text-danger" href="https://github.com/jrie/flagCookies">flagCookies</a>
|
||||
|
|
@ -112,7 +108,7 @@
|
|||
<div class="column is-12 has-text-right">
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<button type="submit" class="button is-danger" @click="resetConfig" :disabled="!config.isConnected"
|
||||
<button type="submit" class="button is-danger" @click="resetConfig" :disabled="!socket.isConnected"
|
||||
v-tooltip="'This configuration are stored locally in your browser.'">
|
||||
<span class="icon">
|
||||
<i class="fa-solid fa-trash" />
|
||||
|
|
@ -132,15 +128,16 @@
|
|||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineEmits, defineProps, onMounted, ref } from 'vue'
|
||||
import { useStorage, useEventBus } from '@vueuse/core'
|
||||
import { useConfigStore } from '~/store/ConfigStore';
|
||||
|
||||
const config = useConfigStore();
|
||||
const socket = useSocketStore();
|
||||
|
||||
const bus = useEventBus('item_added', 'task_edit');
|
||||
const emits = defineEmits(['addItem']);
|
||||
|
||||
const selectedFormat = useStorage('selectedFormat', 'any')
|
||||
const selectedPreset = useStorage('selectedPreset', 'default')
|
||||
const selectedQuality = useStorage('selectedQuality', '')
|
||||
const ytdlpConfig = useStorage('ytdlp_config', '')
|
||||
const ytdlpCookies = useStorage('ytdlp_cookies', '')
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@
|
|||
</div>
|
||||
|
||||
<div class="content has-text-centered" v-if="!hasQueuedItems">
|
||||
<p v-if="config.isConnected">
|
||||
<p v-if="socket.isConnected">
|
||||
<span class="icon-text">
|
||||
<span class="icon has-text-success">
|
||||
<i class="fa-solid fa-circle-check" />
|
||||
|
|
@ -127,13 +127,12 @@ import { defineProps, defineEmits, ref, watch, computed } from 'vue';
|
|||
import moment from "moment";
|
||||
import { useStorage } from '@vueuse/core'
|
||||
import LazyLoader from './LazyLoader'
|
||||
import { useConfigStore } from '~/store/ConfigStore';
|
||||
import { useStateStore } from '~/store/StateStore';
|
||||
|
||||
const emit = defineEmits(['deleteItem']);
|
||||
|
||||
const config = useConfigStore();
|
||||
const stateStore = useStateStore();
|
||||
const socket = useSocketStore();
|
||||
|
||||
const selectedElms = ref([]);
|
||||
const masterSelectAll = ref(false);
|
||||
|
|
|
|||
|
|
@ -49,6 +49,5 @@ defineProps({
|
|||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
const showStacks = ref(false)
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,87 +1,77 @@
|
|||
<template>
|
||||
<div id="main_container" class="container" :class="{ 'is-max-fullwidth': expandContainer }">
|
||||
<nav class="navbar is-dark mb-5">
|
||||
<div id="main_container" class="container">
|
||||
<nav class="navbar is-mobile is-dark">
|
||||
<div class="navbar-brand pl-5">
|
||||
<NuxtLink class="navbar-item" to="/">
|
||||
<NuxtLink class="navbar-item has-tooltip-bottom" to="/"
|
||||
v-tooltip="socket.isConnected ? 'Connected' : 'Connecting'">
|
||||
<span class="icon-text">
|
||||
<span class="icon"><i class="fas fa-home"></i></span>
|
||||
<span>Home</span>
|
||||
<span :class="socket.isConnected ? 'has-text-success' : 'has-text-danger'"><b>YTPTube</b></span>
|
||||
</span>
|
||||
</NuxtLink>
|
||||
|
||||
<a class="navbar-item is-hidden-tablet" id="top" href="#bottom">
|
||||
<span class="icon"><i class="fas fa-arrow-down"></i></span>
|
||||
</a>
|
||||
|
||||
<button class="navbar-burger burger" @click="showMenu = !showMenu">
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="navbar-end is-flex">
|
||||
<div class="navbar-item">
|
||||
<NuxtLink class="button is-dark has-tooltip-bottom" to="/console">
|
||||
<span class="icon"><i class="fa-solid fa-terminal" /></span>
|
||||
<span>Terminal</span>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
|
||||
<div class="navbar-menu is-unselectable" :class="{ 'is-active': showMenu }">
|
||||
<div class="navbar-end pr-3">
|
||||
|
||||
<div class="navbar-item">
|
||||
<button v-tooltip.bottom="'Toggle Console'" class="button is-dark has-tooltip-bottom"
|
||||
@click="config.showConsole = !config.showConsole">
|
||||
<span class="icon">
|
||||
<i class="fa-solid fa-terminal" />
|
||||
</span>
|
||||
<span class="is-hidden-mobile">Console</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="navbar-item">
|
||||
<button v-tooltip.bottom="'Toggle Add Form'" class="button is-dark has-tooltip-bottom"
|
||||
<div class="navbar-item">
|
||||
<button v-tooltip.bottom="'Toggle Add Form'" class="button is-dark has-tooltip-bottom"
|
||||
@click="config.showForm = !config.showForm">
|
||||
<span class="icon"><i class="fa-solid fa-plus" /></span>
|
||||
<span class="is-hidden-mobile">Add</span>
|
||||
</button>
|
||||
</div>
|
||||
<span class="icon"><i class="fa-solid fa-plus" /></span>
|
||||
<span>Add</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="navbar-item" v-if="config.tasks.length > 0">
|
||||
<button v-tooltip.bottom="'Toggle Tasks'" class="button is-dark has-tooltip-bottom"
|
||||
@click="config.showTasks = !config.showTasks">
|
||||
<span class="icon">
|
||||
<i class="fa-solid fa-tasks" />
|
||||
</span>
|
||||
<span class="is-hidden-mobile">Tasks</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="navbar-item" v-if="config.tasks.length > 0">
|
||||
<button v-tooltip.bottom="'Toggle Tasks'" class="button is-dark has-tooltip-bottom"
|
||||
@click="config.showTasks = !config.showTasks">
|
||||
<span class="icon">
|
||||
<i class="fa-solid fa-tasks" />
|
||||
</span>
|
||||
<span class="is-hidden-mobile">Tasks</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="navbar-item">
|
||||
<button class="button is-dark" @click="selectedTheme = 'light'" v-if="'dark' === selectedTheme"
|
||||
v-tooltip="'Switch to light theme'">
|
||||
<span class="icon has-text-warning"><i class="fas fa-sun"></i></span>
|
||||
</button>
|
||||
<button class="button is-dark" @click="selectedTheme = 'dark'" v-if="'light' === selectedTheme"
|
||||
v-tooltip="'Switch to dark theme'">
|
||||
<span class="icon"><i class="fas fa-moon"></i></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="navbar-item">
|
||||
<button class="button is-dark" @click="reloadPage">
|
||||
<span class="icon"><i class="fas fa-refresh"></i></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="navbar-item">
|
||||
<button class="button is-dark" @click="selectedTheme = 'light'" v-if="'dark' === selectedTheme"
|
||||
v-tooltip="'Switch to light theme'">
|
||||
<span class="icon has-text-warning"><i class="fas fa-sun"></i></span>
|
||||
</button>
|
||||
<button class="button is-dark" @click="selectedTheme = 'dark'" v-if="'light' === selectedTheme"
|
||||
v-tooltip="'Switch to dark theme'">
|
||||
<span class="icon"><i class="fas fa-moon"></i></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="navbar-item">
|
||||
<button class="button is-dark" @click="reloadPage">
|
||||
<span class="icon"><i class="fas fa-refresh"></i></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<NuxtPage />
|
||||
|
||||
<div class="columns is-multiline is-mobile mt-3">
|
||||
<div class="column is-12 is-hidden-tablet has-text-centered">
|
||||
<a href="#top" id="bottom" class="button">
|
||||
<span class="icon"><i class="fas fa-arrow-up"></i> </span>
|
||||
<span>Go to Top</span>
|
||||
</a>
|
||||
<div class="columns mt-3 is-mobile">
|
||||
<div class="column is-8-mobile">
|
||||
<div class="has-text-left" v-if="config.app?.version">
|
||||
© {{ Year }} - <a href="https://github.com/ArabCoders/ytptube" target="_blank">YTPTube</a>
|
||||
<span class="is-hidden-mobile"> ({{ config.app?.version || 'unknown' }})</span>
|
||||
- <a href="https://github.com/yt-dlp/yt-dlp">yt-dlp</a>
|
||||
<span class="is-hidden-mobile"> ({{ config?.app.ytdlp_version || 'unknown' }})</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-6 is-9-mobile has-text-left"></div>
|
||||
<div class="column is-6 is-4-mobile has-text-right">
|
||||
v{{ VERSION }}
|
||||
<div class="column is-4-mobile" v-if="config.app?.started">
|
||||
<div class="has-text-right">
|
||||
<span class="user-hint"
|
||||
v-tooltip="'App Started: ' + moment.unix(config.app?.started).format('YYYY-M-DD H:mm Z')">
|
||||
{{ moment.unix(config.app?.started).fromNow() }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -95,15 +85,13 @@ import 'assets/css/bulma.css'
|
|||
import 'assets/css/style.css'
|
||||
import 'assets/css/all.css'
|
||||
import { useStorage } from '@vueuse/core'
|
||||
import { useConfigStore } from '~/store/ConfigStore'
|
||||
import moment from "moment";
|
||||
|
||||
const runtimeConfig = useRuntimeConfig()
|
||||
const config = useConfigStore()
|
||||
const Year = new Date().getFullYear()
|
||||
const selectedTheme = useStorage('theme', (() => window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')())
|
||||
const showMenu = ref(false)
|
||||
const VERSION = ref(runtimeConfig.public.version)
|
||||
const expandContainer = ref(false)
|
||||
const bus = useEventBus('show_form')
|
||||
const socket = useSocketStore()
|
||||
const config = useConfigStore()
|
||||
|
||||
const applyPreferredColorScheme = scheme => {
|
||||
for (let s = 0; s < document.styleSheets.length; s++) {
|
||||
|
|
@ -167,3 +155,11 @@ const toggleForm = () => {
|
|||
dEvent('show_form', { foo: 'bar' })
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.user-hint {
|
||||
user-select: none;
|
||||
cursor: help;
|
||||
border-bottom: 1px dotted;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ export default defineNuxtConfig({
|
|||
runtimeConfig: {
|
||||
public: {
|
||||
domain: '/',
|
||||
wss: ':8081',
|
||||
wss: ':8081/',
|
||||
version: '2.0.0',
|
||||
}
|
||||
},
|
||||
|
|
|
|||
157
ui/pages/console.vue
Normal file
157
ui/pages/console.vue
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
<template>
|
||||
<div class="mt-1 columns is-multiline">
|
||||
<div class="column is-12 is-clearfix">
|
||||
<h1 class="title is-4">Terminal</h1>
|
||||
<div class="subtitle is-6">
|
||||
You can use this terminal window to execute non-interactive commands. The interface is jailed to the
|
||||
<code>yt-dlp</code>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-12">
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<p class="card-header-title">
|
||||
<span class="icon"><i class="fa-solid fa-terminal" /></span> Console Output
|
||||
</p>
|
||||
<p class="card-header-icon">
|
||||
<span v-tooltip.top="'Clear console window'" class="icon" @click="clearOutput">
|
||||
<i class="fa-solid fa-broom" /></span>
|
||||
</p>
|
||||
</header>
|
||||
<section class="card-content p-0 m-0">
|
||||
<div ref="terminal_window" style="min-height: 60vh;max-height:70vh;" />
|
||||
</section>
|
||||
<section class="card-content p-1 m-1">
|
||||
<div class="field is-grouped">
|
||||
<div class="control">
|
||||
<span class="icon-text input is-unselectable">
|
||||
<span class="icon"><i class="fa-solid fa-terminal" /></span>
|
||||
<span>yt-dlp</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="control is-expanded">
|
||||
<input type="text" class="input" v-model="command" placeholder="--help" autocomplete="off"
|
||||
ref="command_input" @keydown.enter="runCommand" :disabled="isLoading" id="command">
|
||||
</div>
|
||||
<p class="control">
|
||||
<button class="button is-primary" type="button" :disabled="isLoading || '' === command"
|
||||
@click="runCommand">
|
||||
<span class="icon">
|
||||
<i class="fa-solid fa-spinner" spin v-if="isLoading" />
|
||||
<i class="fa-solid fa-paper-plane" v-else />
|
||||
</span>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import "@xterm/xterm/css/xterm.css"
|
||||
import { Terminal } from "@xterm/xterm"
|
||||
import { FitAddon } from "@xterm/addon-fit"
|
||||
|
||||
const emitter = defineEmits(['runCommand', 'cli_clear']);
|
||||
|
||||
const terminal = ref()
|
||||
const terminalFit = ref()
|
||||
|
||||
const command = ref('')
|
||||
const terminal_window = ref()
|
||||
const command_input = ref()
|
||||
const isLoading = ref(false)
|
||||
|
||||
const socket = useSocketStore()
|
||||
|
||||
watch(() => isLoading.value, async value => {
|
||||
if (value) {
|
||||
return
|
||||
}
|
||||
command.value = ''
|
||||
await nextTick();
|
||||
focusInput()
|
||||
}, { immediate: true })
|
||||
|
||||
const reSizeTerminal = () => {
|
||||
if (!terminal.value) {
|
||||
return
|
||||
}
|
||||
terminalFit.value.fit()
|
||||
}
|
||||
|
||||
const runCommand = async () => {
|
||||
if ('' === command.value) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!terminal.value) {
|
||||
terminal.value = new Terminal({
|
||||
fontSize: 14,
|
||||
fontFamily: "'JetBrains Mono', monospace",
|
||||
cursorBlink: false,
|
||||
cursorStyle: 'underline',
|
||||
cols: 108,
|
||||
rows: 10,
|
||||
buffer: 1000,
|
||||
disableStdin: true,
|
||||
scrollback: 1000,
|
||||
})
|
||||
terminalFit.value = new FitAddon()
|
||||
terminal.value.loadAddon(terminalFit.value)
|
||||
terminal.value.open(terminal_window.value)
|
||||
terminalFit.value.fit();
|
||||
}
|
||||
|
||||
if ('clear' === command.value) {
|
||||
clearOutput(true)
|
||||
return
|
||||
}
|
||||
|
||||
socket.emit('cli_post', command.value)
|
||||
isLoading.value = true
|
||||
terminal.value.writeln(`user@YTPTube ~`)
|
||||
terminal.value.writeln(`$ yt-dlp ${command.value}`)
|
||||
}
|
||||
|
||||
const clearOutput = async (withCommand = false) => {
|
||||
if (terminal.value) {
|
||||
terminal.value.clear()
|
||||
}
|
||||
|
||||
if (true === withCommand) {
|
||||
command.value = ''
|
||||
}
|
||||
|
||||
focusInput()
|
||||
}
|
||||
|
||||
const focusInput = () => {
|
||||
if (!command_input.value) {
|
||||
return
|
||||
}
|
||||
command_input.value.focus()
|
||||
}
|
||||
|
||||
onUnmounted(() => window.removeEventListener('resize', reSizeTerminal));
|
||||
|
||||
const writer = s => {
|
||||
if (!terminal.value) {
|
||||
return
|
||||
}
|
||||
terminal.value.writeln(s.line)
|
||||
}
|
||||
|
||||
const loader = () => isLoading.value = false
|
||||
|
||||
onMounted(async () => {
|
||||
window.addEventListener('resize', reSizeTerminal);
|
||||
focusInput()
|
||||
socket.off('cli_close', loader)
|
||||
socket.off('cli_output', writer)
|
||||
socket.on('cli_close', loader)
|
||||
socket.on('cli_output', writer)
|
||||
})
|
||||
</script>
|
||||
|
|
@ -7,18 +7,9 @@
|
|||
</template>
|
||||
|
||||
<script setup>
|
||||
import { io } from "socket.io-client"
|
||||
import { useConfigStore } from "~/store/ConfigStore"
|
||||
import { useStateStore } from "~/store/StateStore"
|
||||
|
||||
const bus = useEventBus('item_added', 'show_form', 'task_edit')
|
||||
const config = useConfigStore()
|
||||
|
||||
const runtimeConfig = useRuntimeConfig()
|
||||
const socket = ref()
|
||||
const cli_output = ref([])
|
||||
const cli_isLoading = ref(false)
|
||||
const stateStore = useStateStore()
|
||||
const socket = useSocketStore()
|
||||
|
||||
bus.on((event, data) => {
|
||||
console.log({ e: event, d: data })
|
||||
|
|
@ -34,86 +25,4 @@ bus.on((event, data) => {
|
|||
})
|
||||
|
||||
useHead({ title: 'Index' })
|
||||
onMounted(() => {
|
||||
socket.value = io(runtimeConfig.public.wss, {
|
||||
path: document.location.pathname + 'socket.io',
|
||||
})
|
||||
|
||||
socket.value.on('connect', () => config.isConnected = true);
|
||||
socket.value.on('disconnect', () => config.isConnected = false);
|
||||
|
||||
socket.value.on('initial_data', stream => {
|
||||
const initialData = JSON.parse(stream)
|
||||
|
||||
config.setAll({
|
||||
app: initialData['config'],
|
||||
tasks: initialData['tasks'],
|
||||
directories: initialData['directories'],
|
||||
})
|
||||
stateStore.addAll('queue', initialData['queue'] ?? {})
|
||||
stateStore.addAll('history', initialData['done'] ?? {})
|
||||
})
|
||||
|
||||
socket.value.on('added', stream => {
|
||||
const item = JSON.parse(stream);
|
||||
stateStore.add('queue', item);
|
||||
toast.success(`Item queued successfully: ${stateStore.get('queue', item._id)?.title}`);
|
||||
});
|
||||
|
||||
socket.value.on('error', stream => {
|
||||
const [item, error] = JSON.parse(stream);
|
||||
toast.error(`${item?.id}: Error: ${error}`);
|
||||
});
|
||||
|
||||
socket.value.on('completed', stream => {
|
||||
const item = JSON.parse(stream);
|
||||
if (true === stateStore.has('queue', item._id)) {
|
||||
stateStore.move('queue', 'history', item._id);
|
||||
return
|
||||
}
|
||||
stateStore.add('history', item);
|
||||
});
|
||||
|
||||
socket.value.on('canceled', stream => {
|
||||
const id = JSON.parse(stream);
|
||||
|
||||
if (true !== stateStore.has('queue', id)) {
|
||||
return
|
||||
}
|
||||
|
||||
toast.info('Download canceled: ' + stateStore.get('queue', id)?.title);
|
||||
|
||||
if (true === stateStore.has('queue', id)) {
|
||||
stateStore.remove('queue', id);
|
||||
}
|
||||
});
|
||||
|
||||
socket.value.on('cleared', stream => {
|
||||
const id = JSON.parse(stream);
|
||||
if (true !== stateStore.has('history', id)) {
|
||||
return
|
||||
}
|
||||
stateStore.remove('history', id);
|
||||
});
|
||||
|
||||
socket.value.on("updated", stream => {
|
||||
const data = JSON.parse(stream);
|
||||
let dl = stateStore.get('queue', data._id, {});
|
||||
data.deleting = dl?.deleting;
|
||||
stateStore.update('queue', data._id, data);
|
||||
});
|
||||
|
||||
socket.value.on("update", stream => {
|
||||
const data = JSON.parse(stream);
|
||||
if (true === stateStore.has('history', data._id)) {
|
||||
stateStore.update('history', data._id, data);
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
socket.value.on('cli_close', () => cli_isLoading.value = false);
|
||||
socket.value.on('cli_output', s => cli_output.value.push(s));
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
import { defineStore } from 'pinia';
|
||||
|
||||
const CONFIG_KEYS = {
|
||||
isConnected: false,
|
||||
showForm: false,
|
||||
|
|
@ -12,6 +10,12 @@ const CONFIG_KEYS = {
|
|||
keep_archive: false,
|
||||
output_template: '',
|
||||
},
|
||||
presets: [
|
||||
{
|
||||
name: 'Default - Use Predefined yt-dlp Format',
|
||||
format: 'default',
|
||||
}
|
||||
],
|
||||
directories: [],
|
||||
};
|
||||
|
||||
97
ui/stores/SocketStore.js
Normal file
97
ui/stores/SocketStore.js
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
import { io } from "socket.io-client";
|
||||
|
||||
export const useSocketStore = defineStore('socket', () => {
|
||||
const runtimeConfig = useRuntimeConfig()
|
||||
const config = useConfigStore()
|
||||
const stateStore = useStateStore()
|
||||
|
||||
const socket = ref(null);
|
||||
const isConnected = ref(false);
|
||||
|
||||
const connect = () => {
|
||||
socket.value = io(runtimeConfig.public.wss)
|
||||
|
||||
socket.value.on('connect', () => isConnected.value = true);
|
||||
socket.value.on('disconnect', () => isConnected.value = false);
|
||||
|
||||
socket.value.on('initial_data', stream => {
|
||||
const initialData = JSON.parse(stream)
|
||||
|
||||
config.setAll({
|
||||
app: initialData['config'],
|
||||
tasks: initialData['tasks'],
|
||||
directories: initialData['directories'],
|
||||
})
|
||||
|
||||
stateStore.addAll('queue', initialData['queue'] ?? {})
|
||||
stateStore.addAll('history', initialData['done'] ?? {})
|
||||
})
|
||||
|
||||
socket.value.on('added', stream => {
|
||||
const item = JSON.parse(stream);
|
||||
stateStore.add('queue', item);
|
||||
toast.success(`Item queued successfully: ${stateStore.get('queue', item._id)?.title}`);
|
||||
});
|
||||
|
||||
socket.value.on('error', stream => {
|
||||
const [item, error] = JSON.parse(stream);
|
||||
toast.error(`${item?.id}: Error: ${error}`);
|
||||
});
|
||||
|
||||
socket.value.on('completed', stream => {
|
||||
const item = JSON.parse(stream);
|
||||
if (true === stateStore.has('queue', item._id)) {
|
||||
stateStore.move('queue', 'history', item._id);
|
||||
return
|
||||
}
|
||||
stateStore.add('history', item);
|
||||
});
|
||||
|
||||
socket.value.on('canceled', stream => {
|
||||
const id = JSON.parse(stream);
|
||||
|
||||
if (true !== stateStore.has('queue', id)) {
|
||||
return
|
||||
}
|
||||
|
||||
toast.info('Download canceled: ' + stateStore.get('queue', id)?.title);
|
||||
|
||||
if (true === stateStore.has('queue', id)) {
|
||||
stateStore.remove('queue', id);
|
||||
}
|
||||
});
|
||||
|
||||
socket.value.on('cleared', stream => {
|
||||
const id = JSON.parse(stream);
|
||||
if (true !== stateStore.has('history', id)) {
|
||||
return
|
||||
}
|
||||
stateStore.remove('history', id);
|
||||
});
|
||||
|
||||
socket.value.on("updated", stream => {
|
||||
const data = JSON.parse(stream);
|
||||
let dl = stateStore.get('queue', data._id, {});
|
||||
data.deleting = dl?.deleting;
|
||||
stateStore.update('queue', data._id, data);
|
||||
});
|
||||
|
||||
socket.value.on("update", stream => {
|
||||
const data = JSON.parse(stream);
|
||||
if (true === stateStore.has('history', data._id)) {
|
||||
stateStore.update('history', data._id, data);
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const on = (event, callback) => socket.value.on(event, callback);
|
||||
const off = (event, callback) => socket.value.off(event, callback);
|
||||
const emit = (event, data) => socket.value.emit(event, data);
|
||||
|
||||
if (false === isConnected.value) {
|
||||
connect();
|
||||
}
|
||||
|
||||
return { connect, on, off, emit, socket, isConnected };
|
||||
});
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
import { defineStore } from 'pinia';
|
||||
|
||||
export const useStateStore = defineStore('state', () => {
|
||||
const state = reactive({
|
||||
queue: {},
|
||||
Loading…
Reference in a new issue