minor changes to newDownload form

This commit is contained in:
arabcoders 2025-04-04 22:53:21 +03:00
parent 93e830f029
commit 260901cfa9
6 changed files with 128 additions and 61 deletions

View file

@ -18,7 +18,7 @@ from .AsyncPool import AsyncPool
from .config import Config from .config import Config
from .DataStore import DataStore from .DataStore import DataStore
from .Download import Download from .Download import Download
from .Events import EventBus, Events from .Events import EventBus, Events, info
from .ItemDTO import Item, ItemDTO from .ItemDTO import Item, ItemDTO
from .Presets import Presets from .Presets import Presets
from .Singleton import Singleton from .Singleton import Singleton
@ -307,6 +307,14 @@ class DownloadQueue(metaclass=Singleton):
dlInfo.info.status = "not_live" dlInfo.info.status = "not_live"
itemDownload = self.done.put(dlInfo) itemDownload = self.done.put(dlInfo)
NotifyEvent = Events.COMPLETED NotifyEvent = Events.COMPLETED
log_message = f"{dl.title or dl.id or dl._id}: stream is not live yet."
if dlInfo.info.live_in:
log_message += f" Will start in {dlInfo.info.live_in}."
await self._notify.emit(
Events.LOG_INFO,
data=info(msg=log_message, data=itemDownload.info.serialize()),
)
elif self.config.allow_manifestless is False and is_manifestless is True: elif self.config.allow_manifestless is False and is_manifestless is True:
dlInfo.info.status = "error" dlInfo.info.status = "error"
dlInfo.info.error = "Video is in post-live manifestless mode." dlInfo.info.error = "Video is in post-live manifestless mode."

View file

@ -309,3 +309,7 @@ hr {
.is-auto { .is-auto {
unicode-bidi: embed; unicode-bidi: embed;
} }
.is-justify-self-end {
justify-self: end;
}

View file

@ -153,7 +153,7 @@
</div> </div>
<div class="control" v-if="item.status != 'finished' || !item.filename"> <div class="control" v-if="item.status != 'finished' || !item.filename">
<button class="button is-warning is-fullwidth is-small" v-tooltip="'Re-queue video'" <button class="button is-warning is-fullwidth is-small" v-tooltip="'Re-queue video'"
@click="reQueueItem(item)"> @click="(event) => reQueueItem(item, event)">
<span class="icon"><i class="fa-solid fa-rotate-right" /></span> <span class="icon"><i class="fa-solid fa-rotate-right" /></span>
</button> </button>
</div> </div>
@ -283,7 +283,7 @@
</div> </div>
<div class="columns is-mobile is-multiline"> <div class="columns is-mobile is-multiline">
<div class="column is-half-mobile" v-if="item.status != 'finished' || !item.filename"> <div class="column is-half-mobile" v-if="item.status != 'finished' || !item.filename">
<a class="button is-warning is-fullwidth" @click="reQueueItem(item)"> <a class="button is-warning is-fullwidth" @click="(event) => reQueueItem(item, event)">
<span class="icon-text is-block"> <span class="icon-text is-block">
<span class="icon"><i class="fa-solid fa-rotate-right" /></span> <span class="icon"><i class="fa-solid fa-rotate-right" /></span>
<span>Re-queue</span> <span>Re-queue</span>
@ -371,13 +371,13 @@
import moment from 'moment' import moment from 'moment'
import { useStorage } from '@vueuse/core' import { useStorage } from '@vueuse/core'
import { makeDownload, formatBytes } from '~/utils/index' import { makeDownload, formatBytes } from '~/utils/index'
import toast from '~/plugins/toast'
import { isEmbedable, getEmbedable } from '~/utils/embedable' import { isEmbedable, getEmbedable } from '~/utils/embedable'
const emitter = defineEmits(['getInfo']) const emitter = defineEmits(['getInfo', 'add_new'])
const config = useConfigStore() const config = useConfigStore()
const stateStore = useStateStore() const stateStore = useStateStore()
const socket = useSocketStore() const socket = useSocketStore()
const toast = useToast()
const selectedElms = ref([]) const selectedElms = ref([])
const masterSelectAll = ref(false) const masterSelectAll = ref(false)
@ -624,9 +624,7 @@ const removeItem = item => {
}) })
} }
const reQueueItem = item => { const reQueueItem = (item, event = null) => {
socket.emit('item_delete', { id: item._id, remove_file: false })
let extras = {} let extras = {}
if (item.extras) { if (item.extras) {
@ -637,7 +635,7 @@ const reQueueItem = item => {
}) })
} }
socket.emit('add_url', { const item_req = {
url: item.url, url: item.url,
preset: item.preset, preset: item.preset,
folder: item.folder, folder: item.folder,
@ -645,7 +643,17 @@ const reQueueItem = item => {
template: item.template, template: item.template,
cli: item?.cli, cli: item?.cli,
extras: extras extras: extras
}) };
socket.emit('item_delete', { id: item._id, remove_file: false })
if (event && (event?.altKey && true === event?.altKey)) {
toast.info('Removed the item from history, and added it to the new download form.')
emitter('add_new', item_req)
return
}
socket.emit('add_url', item_req)
} }
const pImg = e => e.target.naturalHeight > e.target.naturalWidth ? e.target.classList.add('image-portrait') : null const pImg = e => e.target.naturalHeight > e.target.naturalWidth ? e.target.classList.add('image-portrait') : null

View file

@ -11,7 +11,7 @@
</label> </label>
<div class="control"> <div class="control">
<input type="text" class="input" id="url" placeholder="Video or playlist link" <input type="text" class="input" id="url" placeholder="Video or playlist link"
:disabled="!socket.isConnected || addInProgress" v-model="url"> :disabled="!socket.isConnected || addInProgress" v-model="form.url">
</div> </div>
<span class="help"> <span class="help">
<span class="icon"><i class="fa-solid fa-info" /></span> <span class="icon"><i class="fa-solid fa-info" /></span>
@ -29,7 +29,7 @@
<div class="control is-expanded"> <div class="control is-expanded">
<div class="select is-fullwidth"> <div class="select is-fullwidth">
<select id="preset" class="is-fullwidth" <select id="preset" class="is-fullwidth"
:disabled="!socket.isConnected || addInProgress || hasFormatInConfig" v-model="selectedPreset" :disabled="!socket.isConnected || addInProgress || hasFormatInConfig" v-model="form.preset"
v-tooltip.bottom="hasFormatInConfig ? 'Presets are disabled. Format key is present in the Command arguments for yt-dlp.' : ''"> v-tooltip.bottom="hasFormatInConfig ? 'Presets are disabled. Format key is present in the Command arguments for yt-dlp.' : ''">
<optgroup label="Custom presets" v-if="config?.presets.filter(p => !p?.default).length > 0"> <optgroup label="Custom presets" v-if="config?.presets.filter(p => !p?.default).length > 0">
<option v-for="item in filter_presets(false)" :key="item.name" :value="item.name"> <option v-for="item in filter_presets(false)" :key="item.name" :value="item.name">
@ -55,7 +55,7 @@
</a> </a>
</div> </div>
<div class="control is-expanded"> <div class="control is-expanded">
<input type="text" class="input is-fullwidth" id="path" v-model="downloadPath" placeholder="Default" <input type="text" class="input is-fullwidth" id="path" v-model="form.folder" placeholder="Default"
:disabled="!socket.isConnected || addInProgress" list="folders"> :disabled="!socket.isConnected || addInProgress" list="folders">
</div> </div>
</div> </div>
@ -63,7 +63,7 @@
<div class="column"> <div class="column">
<button type="submit" class="button is-primary" <button type="submit" class="button is-primary"
:class="{ 'is-loading': !socket.isConnected || addInProgress }" :class="{ 'is-loading': !socket.isConnected || addInProgress }"
:disabled="!socket.isConnected || addInProgress || !url"> :disabled="!socket.isConnected || addInProgress || !form?.url">
<span class="icon"><i class="fa-solid fa-plus" /></span> <span class="icon"><i class="fa-solid fa-plus" /></span>
<span>Add</span> <span>Add</span>
</button> </button>
@ -85,7 +85,7 @@
Output Template Output Template
</label> </label>
<div class="control"> <div class="control">
<input type="text" class="input" v-model="output_template" id="output_format" <input type="text" class="input" v-model="form.template" id="output_format"
:disabled="!socket.isConnected || addInProgress" :disabled="!socket.isConnected || addInProgress"
placeholder="Uses default output template naming if empty."> placeholder="Uses default output template naming if empty.">
</div> </div>
@ -104,7 +104,7 @@
Command arguments for yt-dlp Command arguments for yt-dlp
</label> </label>
<div class="control"> <div class="control">
<textarea class="textarea is-pre" v-model="ytdlp_cli" id="cli_options" <textarea class="textarea is-pre" v-model="form.cli" id="cli_options"
:disabled="!socket.isConnected || addInProgress" :disabled="!socket.isConnected || addInProgress"
placeholder="command options to use, e.g. --no-embed-metadata --no-embed-thumbnail" /> placeholder="command options to use, e.g. --no-embed-metadata --no-embed-thumbnail" />
</div> </div>
@ -127,7 +127,7 @@
Cookies Cookies
</label> </label>
<div class="control"> <div class="control">
<textarea class="textarea is-pre" id="ytdlpCookies" v-model="ytdlpCookies" <textarea class="textarea is-pre" id="ytdlpCookies" v-model="form.cookies"
:disabled="!socket.isConnected || addInProgress" /> :disabled="!socket.isConnected || addInProgress" />
</div> </div>
<span class="help"> <span class="help">
@ -140,21 +140,20 @@
</div> </div>
</div> </div>
<div class="column is-6-tablet is-4-mobile has-text-left"> <div class="column is-6-tablet is-4-mobile has-text-left">
<button type="button" class="button is-info" @click="emitter('getInfo', url)" <button type="button" class="button is-info" @click="emitter('getInfo', form.url)"
:class="{ 'is-loading': !socket.isConnected }" :disabled="!socket.isConnected || addInProgress || !url"> :class="{ 'is-loading': !socket.isConnected }"
:disabled="!socket.isConnected || addInProgress || !form?.url">
<span class="icon"><i class="fa-solid fa-info" /></span> <span class="icon"><i class="fa-solid fa-info" /></span>
<span>Information</span> <span>Information</span>
</button> </button>
</div> </div>
<div class="column is-6-tablet is-6-mobile has-text-right"> <div class="column is-6-tablet is-6-mobile has-text-right">
<div class="field"> <div class="field is-grouped is-justify-self-end">
<div class="control"> <div class="control">
<button type="button" class="button is-danger" @click="resetConfig" :disabled="!socket.isConnected" <button type="button" class="button is-danger" @click="resetConfig"
v-tooltip="'This configuration are stored locally in your browser.'"> :disabled="!socket.isConnected || form?.id" v-tooltip="'Reset local settings'">
<span class="icon"> <span class="icon"><i class="fa-solid fa-rotate-left" /></span>
<i class="fa-solid fa-trash" /> <span>Reset</span>
</span>
<span>Reset Local Configuration</span>
</button> </button>
</div> </div>
</div> </div>
@ -172,24 +171,35 @@
<script setup> <script setup>
import { useStorage } from '@vueuse/core' import { useStorage } from '@vueuse/core'
const emitter = defineEmits(['getInfo']) const props = defineProps({
item: {
type: Object,
required: false,
default: () => { },
},
})
const emitter = defineEmits(['getInfo', 'clear_form'])
const config = useConfigStore() const config = useConfigStore()
const socket = useSocketStore() const socket = useSocketStore()
const toast = useToast() const toast = useToast()
const selectedPreset = useStorage('selectedPreset', config.app.default_preset)
const ytdlpCookies = useStorage('ytdlp_cookies', '')
const ytdlp_cli = useStorage('ytdlp_cli', '')
const output_template = useStorage('output_template', null)
const downloadPath = useStorage('downloadPath', null)
const url = useStorage('downloadUrl', null)
const showAdvanced = useStorage('show_advanced', false) const showAdvanced = useStorage('show_advanced', false)
const addInProgress = ref(false) const addInProgress = ref(false)
const form = useStorage('local_config', {
id: null,
url: '',
preset: config.app.default_preset,
cookies: '',
cli: '',
template: '',
folder: '',
})
const addDownload = async () => { const addDownload = async () => {
// -- send request to convert cli options to JSON if (form.value?.cli && '' !== form.value.cli) {
if (ytdlp_cli.value && '' !== ytdlp_cli.value) { const options = await convertOptions(form.value.cli)
const options = await convertOptions(ytdlp_cli.value)
if (null === options) { if (null === options) {
return return
} }
@ -197,17 +207,17 @@ const addDownload = async () => {
addInProgress.value = true addInProgress.value = true
url.value.split(',').forEach(url => { form.value.url.split(',').forEach(url => {
if (!url.trim()) { if (!url.trim()) {
return return
} }
socket.emit('add_url', { socket.emit('add_url', {
url: url, url: url,
preset: config.app.basic_mode ? config.app.default_preset : selectedPreset.value, preset: config.app.basic_mode ? config.app.default_preset : form.value.preset,
folder: config.app.basic_mode ? null : downloadPath.value, folder: config.app.basic_mode ? null : form.value.folder,
cookies: config.app.basic_mode ? '' : ytdlpCookies.value, template: config.app.basic_mode ? null : form.value.template,
template: config.app.basic_mode ? null : output_template.value, cookies: config.app.basic_mode ? '' : form.value.cookies,
cli: config.app.basic_mode ? null : ytdlp_cli.value, cli: config.app.basic_mode ? null : form.value.cli,
}) })
}) })
} }
@ -217,12 +227,16 @@ const resetConfig = () => {
return return
} }
selectedPreset.value = config.app.default_preset form.value = {
ytdlpCookies.value = '' id: null,
ytdlp_cli.value = '' url: '',
output_template.value = null preset: config.app.default_preset,
url.value = null cookies: '',
downloadPath.value = null cli: '',
template: '',
folder: '',
}
showAdvanced.value = false showAdvanced.value = false
toast.success('Local configuration has been reset.') toast.success('Local configuration has been reset.')
@ -238,7 +252,7 @@ const statusHandler = async stream => {
return return
} }
url.value = '' form.value.url = ''
} }
const unlockDownload = async stream => { const unlockDownload = async stream => {
@ -246,7 +260,7 @@ const unlockDownload = async stream => {
if (!json?.data) { if (!json?.data) {
return return
} }
if ("unlock" in json.data && json.data.unlock === true) { if ("unlock" in json.data && true === json.data.unlock) {
addInProgress.value = false addInProgress.value = false
} }
} }
@ -256,11 +270,11 @@ const convertOptions = async args => {
const response = await convertCliOptions(args) const response = await convertCliOptions(args)
if (response.output_template) { if (response.output_template) {
output_template.value = response.output_template form.value.template = response.output_template
} }
if (response.download_path) { if (response.download_path) {
downloadPath.value = response.download_path form.value.folder = response.download_path
} }
return response.opts return response.opts
@ -271,11 +285,24 @@ const convertOptions = async args => {
return null; return null;
} }
onMounted(() => { onMounted(async () => {
socket.on('status', statusHandler) socket.on('status', statusHandler)
socket.on('error', unlockDownload) socket.on('error', unlockDownload)
if ('' === selectedPreset.value) {
selectedPreset.value = config.app.default_preset.value await nextTick()
if ('' === form.value?.preset) {
form.value.preset = config.app.default_preset
}
if (props?.item) {
Object.keys(props.item).forEach(key => {
console.log(key, form.value[key], props.item[key])
if (key in form.value) {
form.value[key] = props.item[key]
}
})
emitter('clear_form');
} }
}) })
@ -285,11 +312,11 @@ onUnmounted(() => {
}) })
const hasFormatInConfig = computed(() => { const hasFormatInConfig = computed(() => {
if (!ytdlp_cli.value) { if (!form?.value?.value) {
return false return false
} }
return /(?<!\S)(-f|--format)(=|\s)(\S+)/.test(ytdlp_cli.value) return /(?<!\S)(-f|--format)(=|\s)(\S+)/.test(form.value.cli)
}) })
const filter_presets = (flag = true) => config.presets.filter(item => item.default === flag) const filter_presets = (flag = true) => config.presets.filter(item => item.default === flag)

View file

@ -43,11 +43,13 @@
</template> </template>
<script setup> <script setup>
defineProps({ const props = defineProps({
error: { error: {
type: Object, type: Object,
required: true required: true
} }
}) })
const showStacks = ref(false) const showStacks = ref(false)
onMounted(() => console.error(props.error))
</script> </script>

View file

@ -49,7 +49,7 @@
</p> </p>
<p class="control" v-if="!config.app.basic_mode"> <p class="control" v-if="!config.app.basic_mode">
<button v-tooltip.bottom="'Toggle Add Form'" class="button is-primary has-tooltip-bottom" <button v-tooltip.bottom="'Toggle new download form'" class="button is-primary has-tooltip-bottom"
@click="config.showForm = !config.showForm"> @click="config.showForm = !config.showForm">
<span class="icon"><i class="fa-solid fa-plus" /></span> <span class="icon"><i class="fa-solid fa-plus" /></span>
</button> </button>
@ -73,9 +73,10 @@
</div> </div>
</div> </div>
<NewDownload v-if="config.showForm || config.app.basic_mode" @getInfo="url => get_info = url" /> <NewDownload v-if="config.showForm || config.app.basic_mode" @getInfo="url => get_info = url" :item="item_form"
@clear_form="item_form = {}" />
<Queue @getInfo="url => get_info = url" /> <Queue @getInfo="url => get_info = url" />
<History @getInfo="url => get_info = url" /> <History @getInfo="url => get_info = url" @add_new="item => toNewDownload(item)" />
<GetInfo v-if="get_info" :link="get_info" @closeModel="get_info = ''" /> <GetInfo v-if="get_info" :link="get_info" @closeModel="get_info = ''" />
</div> </div>
</template> </template>
@ -94,6 +95,7 @@ const get_info = ref('')
const bg_enable = useStorage('random_bg', true) const bg_enable = useStorage('random_bg', true)
const bg_opacity = useStorage('random_bg_opacity', 0.85) const bg_opacity = useStorage('random_bg_opacity', 0.85)
const display_style = useStorage('display_style', 'cards') const display_style = useStorage('display_style', 'cards')
const item_form = ref({})
onMounted(() => { onMounted(() => {
if (!config.app.ui_update_title) { if (!config.app.ui_update_title) {
@ -159,4 +161,20 @@ watch(get_info, v => {
}) })
const changeDisplay = () => display_style.value = display_style.value === 'cards' ? 'list' : 'cards' const changeDisplay = () => display_style.value = display_style.value === 'cards' ? 'list' : 'cards'
const toNewDownload = async (item) => {
if (!item) {
return
}
if (config.showForm) {
config.showForm = false
await nextTick()
}
item_form.value = item
await nextTick()
config.showForm = true
}
</script> </script>