Added the ability to add item in paused state
This commit is contained in:
parent
1239643f97
commit
9ff9735a2f
12 changed files with 344 additions and 25 deletions
1
API.md
1
API.md
|
|
@ -210,6 +210,7 @@ or an error:
|
|||
"cookies": "...", // -- optional. If provided, it MUST BE in Netscape HTTP Cookie format.
|
||||
"template": "%(title)s.%(ext)s", // -- optional. The filename template to use for this item.
|
||||
"cli": "--write-subs --embed-subs", // -- optional. Additional command options for yt-dlp to apply to this item.
|
||||
"auto_start": true // -- optional. Whether to auto-start the download after adding it. Defaults to true.
|
||||
}
|
||||
|
||||
// Or multiple items (array of objects)
|
||||
|
|
|
|||
|
|
@ -147,12 +147,13 @@ class DataStore:
|
|||
if 0 == len(self._dict):
|
||||
return False
|
||||
|
||||
return any(self._dict[key].started() is False for key in self._dict)
|
||||
return any(self._dict[key].info.auto_start and self._dict[key].started() is False for key in self._dict)
|
||||
|
||||
def get_next_download(self) -> Download:
|
||||
for key in self._dict:
|
||||
if self._dict[key].started() is False and self._dict[key].is_cancelled() is False:
|
||||
return self._dict[key]
|
||||
ref = self._dict[key]
|
||||
if ref.info.auto_start and ref.started() is False and ref.is_cancelled() is False:
|
||||
return ref
|
||||
|
||||
return None
|
||||
|
||||
|
|
|
|||
|
|
@ -150,6 +150,94 @@ class DownloadQueue(metaclass=Singleton):
|
|||
)
|
||||
asyncio.create_task(self._download_pool(), name="download_pool")
|
||||
|
||||
async def start_items(self, ids: list[str]) -> dict[str, str]:
|
||||
"""
|
||||
Start one or more queued downloads that were added with auto_started=False.
|
||||
|
||||
Args:
|
||||
ids (list[str]): List of item IDs to start.
|
||||
|
||||
Returns:
|
||||
dict[str, str]: Dictionary of per-ID results and overall status.
|
||||
|
||||
"""
|
||||
status: dict[str, str] = {"status": "ok"}
|
||||
started = False
|
||||
tasks = []
|
||||
|
||||
for item_id in ids:
|
||||
try:
|
||||
item = self.queue.get(key=item_id)
|
||||
except KeyError as e:
|
||||
status[item_id] = f"not found: {e!s}"
|
||||
status["status"] = "error"
|
||||
LOG.warning(f"Start requested for non-existent item {item_id=}.")
|
||||
continue
|
||||
|
||||
if item.info.auto_start:
|
||||
status[item_id] = "already started"
|
||||
LOG.debug(f"Item {item.info.name()} already started.")
|
||||
continue
|
||||
|
||||
item.info.auto_start = True
|
||||
updated = self.queue.put(item)
|
||||
tasks.append(self._notify.emit(Events.UPDATED, data=updated.info))
|
||||
status[item_id] = "started"
|
||||
started = True
|
||||
LOG.debug(f"Item {item.info.name()} marked as started.")
|
||||
|
||||
if started:
|
||||
self.event.set()
|
||||
|
||||
if len(tasks) > 0:
|
||||
await asyncio.gather(*tasks)
|
||||
|
||||
return status
|
||||
|
||||
async def pause_items(self, ids: list[str]) -> dict[str, str]:
|
||||
"""
|
||||
Pause one or more queued downloads that were added with auto_started=True.
|
||||
|
||||
Args:
|
||||
ids (list[str]): List of item IDs to pause.
|
||||
|
||||
Returns:
|
||||
dict[str, str]: Dictionary of per-ID results and overall status.
|
||||
|
||||
"""
|
||||
status: dict[str, str] = {"status": "ok"}
|
||||
tasks = []
|
||||
|
||||
for item_id in ids:
|
||||
try:
|
||||
item = self.queue.get(key=item_id)
|
||||
except KeyError as e:
|
||||
status[item_id] = f"not found: {e!s}"
|
||||
status["status"] = "error"
|
||||
LOG.warning(f"Start requested for non-existent item {item_id=}.")
|
||||
continue
|
||||
|
||||
if item.started() or item.is_cancelled():
|
||||
status[item_id] = "already started"
|
||||
LOG.debug(f"Item {item.info.name()} already started.")
|
||||
continue
|
||||
|
||||
if item.info.auto_start is False:
|
||||
status[item_id] = "not started"
|
||||
LOG.debug(f"Item {item.info.name()} is not set to auto-start.")
|
||||
continue
|
||||
|
||||
item.info.auto_start = False
|
||||
updated = self.queue.put(item)
|
||||
tasks.append(self._notify.emit(Events.UPDATED, data=updated.info))
|
||||
status[item_id] = "paused"
|
||||
LOG.debug(f"Item {item.info.name()} marked as paused.")
|
||||
|
||||
if len(tasks) > 0:
|
||||
await asyncio.gather(*tasks)
|
||||
|
||||
return status
|
||||
|
||||
def pause(self, shutdown: bool = False) -> bool:
|
||||
"""
|
||||
Pause the download queue.
|
||||
|
|
@ -358,11 +446,12 @@ class DownloadQueue(metaclass=Singleton):
|
|||
live_in=live_in if live_in else item.extras.get("live_in", None),
|
||||
options=options,
|
||||
cli=item.cli,
|
||||
auto_start=item.auto_start,
|
||||
extras=item.extras,
|
||||
)
|
||||
|
||||
try:
|
||||
dlInfo: Download = Download(info=dl, info_dict=entry, logs=logs)
|
||||
dlInfo: Download = Download(info=dl, info_dict=entry if item.auto_start else None, logs=logs)
|
||||
|
||||
text_logs: str = ""
|
||||
if filtered_logs := extract_ytdlp_logs(logs):
|
||||
|
|
@ -407,7 +496,10 @@ class DownloadQueue(metaclass=Singleton):
|
|||
if _requeue:
|
||||
NotifyEvent = Events.ADDED
|
||||
itemDownload = self.queue.put(dlInfo)
|
||||
self.event.set()
|
||||
if item.auto_start:
|
||||
self.event.set()
|
||||
else:
|
||||
LOG.debug(f"Item {itemDownload.info.name()} is not set to auto-start.")
|
||||
else:
|
||||
dlInfo.info.status = "not_live"
|
||||
itemDownload = self.done.put(dlInfo)
|
||||
|
|
@ -419,7 +511,10 @@ class DownloadQueue(metaclass=Singleton):
|
|||
else:
|
||||
NotifyEvent = Events.ADDED
|
||||
itemDownload = self.queue.put(dlInfo)
|
||||
self.event.set()
|
||||
if item.auto_start:
|
||||
self.event.set()
|
||||
else:
|
||||
LOG.debug(f"Item {itemDownload.info.name()} is not set to auto-start.")
|
||||
|
||||
await self._notify.emit(NotifyEvent, data=itemDownload.info.serialize())
|
||||
|
||||
|
|
@ -628,7 +723,7 @@ class DownloadQueue(metaclass=Singleton):
|
|||
self.queue.delete(id)
|
||||
await self._notify.emit(Events.CANCELLED, data=item.info.serialize())
|
||||
item.info.status = "cancelled"
|
||||
item.info.error = "Cancelled by user."
|
||||
# item.info.error = "Cancelled by user."
|
||||
self.done.put(item)
|
||||
await self._notify.emit(Events.COMPLETED, data=item.info.serialize())
|
||||
LOG.info(f"Deleted from queue {item_ref}")
|
||||
|
|
@ -751,7 +846,7 @@ class DownloadQueue(metaclass=Singleton):
|
|||
LOG.info("Download pool resumed downloading.")
|
||||
|
||||
for _id, entry in list(self.queue.items()):
|
||||
if entry.started() or entry.is_cancelled():
|
||||
if entry.started() or entry.is_cancelled() or entry.info.auto_start is False:
|
||||
continue
|
||||
|
||||
if entry.is_live:
|
||||
|
|
@ -812,7 +907,7 @@ class DownloadQueue(metaclass=Singleton):
|
|||
if entry.is_cancelled() is True:
|
||||
await self._notify.emit(Events.CANCELLED, data=entry.info.serialize())
|
||||
entry.info.status = "cancelled"
|
||||
entry.info.error = "Cancelled by user."
|
||||
# entry.info.error = "Cancelled by user."
|
||||
|
||||
self.done.put(value=entry)
|
||||
await self._notify.emit(Events.COMPLETED, data=entry.info.serialize())
|
||||
|
|
|
|||
|
|
@ -41,6 +41,9 @@ class Item:
|
|||
requeued: bool = False
|
||||
"""If the item has been retried already via conditions."""
|
||||
|
||||
auto_start: bool = True
|
||||
"""If the item should be started automatically."""
|
||||
|
||||
def serialize(self) -> dict:
|
||||
return self.__dict__.copy()
|
||||
|
||||
|
|
@ -95,7 +98,7 @@ class Item:
|
|||
|
||||
data = {}
|
||||
for k, v in self.serialize().items():
|
||||
if not v:
|
||||
if not v and k not in ("auto_start"):
|
||||
continue
|
||||
|
||||
if k == "cli":
|
||||
|
|
@ -157,6 +160,10 @@ class Item:
|
|||
if item.get("template") and isinstance(item.get("template"), str):
|
||||
data["template"] = item.get("template")
|
||||
|
||||
if "auto_start" in item and isinstance(item.get("auto_start"), bool):
|
||||
LOG.info("Item '%s' auto_start is set to %s.", url, item.get("auto_start"))
|
||||
data["auto_start"] = bool(item.get("auto_start"))
|
||||
|
||||
extras = item.get("extras")
|
||||
if extras and isinstance(extras, dict) and len(extras) > 0:
|
||||
data["extras"] = extras
|
||||
|
|
@ -213,6 +220,7 @@ class ItemDTO:
|
|||
options: dict = field(default_factory=dict)
|
||||
extras: dict = field(default_factory=dict)
|
||||
cli: str = ""
|
||||
auto_start: bool = True
|
||||
|
||||
# yt-dlp injected fields.
|
||||
tmpfilename: str | None = None
|
||||
|
|
@ -239,7 +247,7 @@ class ItemDTO:
|
|||
return self._id
|
||||
|
||||
def name(self) -> str:
|
||||
return f'id="{self.id}", title="{self.title}"'
|
||||
return f'id="{self.id}", title="{self.title}"'
|
||||
|
||||
@staticmethod
|
||||
def removed_fields() -> tuple:
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ class Task:
|
|||
timer: str = ""
|
||||
template: str = ""
|
||||
cli: str = ""
|
||||
auto_start: bool = True
|
||||
|
||||
def serialize(self) -> dict:
|
||||
return self.__dict__
|
||||
|
|
|
|||
|
|
@ -1131,6 +1131,7 @@ def get_archive_id(url: str) -> tuple[bool, dict[str | None, str | None, str | N
|
|||
idDict["archive_id"] = YTDLP_INFO_CLS._make_archive_id(idDict)
|
||||
break
|
||||
except Exception as e:
|
||||
LOG.exception(e)
|
||||
LOG.error(f"Error getting archive ID: {e}")
|
||||
|
||||
return idDict
|
||||
|
|
|
|||
|
|
@ -128,3 +128,27 @@ async def archive_item(config: Config, data: dict):
|
|||
LOG.info(f"Archiving url '{data['url']}' with id '{idDict['archive_id']}'.")
|
||||
else:
|
||||
LOG.info(f"URL '{data['url']}' with id '{idDict['archive_id']}' already archived.")
|
||||
|
||||
|
||||
@route(RouteType.SOCKET, "item_start", "item_start")
|
||||
async def item_start(queue: DownloadQueue, notify: EventBus, sid: str, data: list | str) -> None:
|
||||
if not data:
|
||||
await notify.emit(Events.ERROR, data=error("Invalid request."), to=sid)
|
||||
return
|
||||
|
||||
if isinstance(data, str):
|
||||
data = [data]
|
||||
|
||||
await queue.start_items(data)
|
||||
|
||||
|
||||
@route(RouteType.SOCKET, "item_pause", "item_pause")
|
||||
async def item_pause(queue: DownloadQueue, notify: EventBus, sid: str, data: list | str) -> None:
|
||||
if not data:
|
||||
await notify.emit(Events.ERROR, data=error("Invalid request."), to=sid)
|
||||
return
|
||||
|
||||
if isinstance(data, str):
|
||||
data = [data]
|
||||
|
||||
await queue.pause_items(data)
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ type task_item = {
|
|||
cli?: string,
|
||||
timer?: string,
|
||||
in_progress?: boolean,
|
||||
auto_start?: boolean,
|
||||
}
|
||||
|
||||
type exported_task = task_item & { _type: string, _version: string }
|
||||
|
|
|
|||
|
|
@ -9,11 +9,19 @@
|
|||
<span class="icon"><i class="fa-solid fa-link" /></span>
|
||||
URLs
|
||||
</label>
|
||||
<div class="control">
|
||||
<input type="text" class="input" id="url" placeholder="Video or playlist link"
|
||||
:disabled="!socket.isConnected || addInProgress" v-model="form.url">
|
||||
<div class="field is-grouped">
|
||||
<div class="control is-expanded">
|
||||
<input type="text" class="input" id="url" placeholder="Video or playlist link"
|
||||
:disabled="!socket.isConnected || addInProgress" v-model="form.url">
|
||||
</div>
|
||||
<div class="control is-align-content-space-around" v-if="!config.app.basic_mode"
|
||||
v-tooltip="'Auto start the download after adding it.'">
|
||||
<input id="auto_start" type="checkbox" v-model="auto_start" :disabled="addInProgress"
|
||||
class="switch is-success" />
|
||||
<label for="auto_start" class="is-unselectable">Start</label>
|
||||
</div>
|
||||
</div>
|
||||
<span class="help is-bold">
|
||||
<span class="help is-bold is-unselectable">
|
||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||
<span>You can add multiple URLs separated by <code
|
||||
class="is-bold">{{ getSeparatorsName(separator) }}</code>.</span>
|
||||
|
|
@ -233,6 +241,7 @@
|
|||
</template>
|
||||
|
||||
<script setup>
|
||||
import 'assets/css/bulma-switch.css'
|
||||
import { useStorage } from '@vueuse/core'
|
||||
|
||||
const props = defineProps({
|
||||
|
|
@ -264,6 +273,7 @@ const getSeparatorsName = (value) => {
|
|||
|
||||
const showAdvanced = useStorage('show_advanced', false)
|
||||
const separator = useStorage('url_separator', separators[0].value)
|
||||
const auto_start = useStorage('auto_start', true)
|
||||
|
||||
const addInProgress = ref(false)
|
||||
|
||||
|
|
@ -300,6 +310,7 @@ const addDownload = async () => {
|
|||
template: config.app.basic_mode ? null : form.value.template,
|
||||
cookies: config.app.basic_mode ? '' : form.value.cookies,
|
||||
cli: config.app.basic_mode ? null : form.value.cli,
|
||||
auto_start: config.app.basic_mode ? true : auto_start.value
|
||||
}
|
||||
|
||||
if (form.value?.extras && Object.keys(form.value.extras).length > 0) {
|
||||
|
|
|
|||
|
|
@ -23,6 +23,19 @@
|
|||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="column is-half-mobile" v-if="hasManualStart">
|
||||
<button type="button" class="button is-fullwidth is-success" :disabled="!hasSelected" @click="startItems">
|
||||
<span class="icon"><i class="fa-solid fa-circle-play" /></span>
|
||||
<span>Start</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="column is-half-mobile" v-if="hasPausable">
|
||||
<button type="button" class="button is-fullwidth is-background-warning-85" :disabled="!hasSelected"
|
||||
@click="pauseSelected">
|
||||
<span class="icon"><i class="fa-solid fa-pause" /></span>
|
||||
<span>Pause</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="column is-half-mobile" v-if="('cards' === display_style || hasSelected)">
|
||||
<button type="button" class="button is-fullwidth is-warning" :disabled="!hasSelected" @click="cancelSelected">
|
||||
<span class="icon"><i class="fa-solid fa-eject" /></span>
|
||||
|
|
@ -105,6 +118,14 @@
|
|||
<td class="is-vcentered is-items-center">
|
||||
<Dropdown icons="fa-solid fa-cogs" @open_state="s => table_container = !s"
|
||||
:button_classes="'is-small'" label="Actions">
|
||||
<template v-if="!item.auto_start">
|
||||
<NuxtLink class="dropdown-item has-text-success" @click="startItem(item)">
|
||||
<span class="icon"><i class="fa-solid fa-circle-play" /></span>
|
||||
<span>Start Download</span>
|
||||
</NuxtLink>
|
||||
<hr class="dropdown-divider" />
|
||||
</template>
|
||||
|
||||
<template v-if="isEmbedable(item.url)">
|
||||
<NuxtLink class="dropdown-item has-text-danger" @click="embed_url = getEmbedable(item.url)">
|
||||
<span class="icon"><i class="fa-solid fa-play" /></span>
|
||||
|
|
@ -219,6 +240,18 @@
|
|||
<span>Cancel</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="column is-half-mobile" v-if="!item.auto_start && !item.status">
|
||||
<button class="button is-success is-fullwidth" @click="startItem(item)">
|
||||
<span class="icon"><i class="fa-solid fa-circle-play" /></span>
|
||||
<span>Start</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="column is-half-mobile" v-if="item.auto_start && !item.status">
|
||||
<button class="button is-background-warning-85 is-fullwidth" @click="pauseItem(item)">
|
||||
<span class="icon"><i class="fa-solid fa-pause" /></span>
|
||||
<span>Pause</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="column is-half-mobile">
|
||||
<Dropdown icons="fa-solid fa-cogs" @open_state="s => table_container = !s" label="Actions">
|
||||
<template v-if="isEmbedable(item.url)">
|
||||
|
|
@ -324,8 +357,42 @@ const filteredItems = computed(() => {
|
|||
|
||||
const hasSelected = computed(() => selectedElms.value.length > 0)
|
||||
const hasQueuedItems = computed(() => stateStore.count('queue') > 0)
|
||||
const hasManualStart = computed(() => {
|
||||
if (stateStore.count('queue') < 0) {
|
||||
return false
|
||||
}
|
||||
|
||||
for (const key in stateStore.queue) {
|
||||
const item = stateStore.queue[key]
|
||||
if (!item.status && item.auto_start === false) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
const hasPausable = computed(() => {
|
||||
if (stateStore.count('queue') < 0) {
|
||||
return false
|
||||
}
|
||||
|
||||
for (const key in stateStore.queue) {
|
||||
const item = stateStore.queue[key]
|
||||
if (!item.status && item.auto_start === true) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
|
||||
const setIcon = item => {
|
||||
if (!item.auto_start) {
|
||||
return 'fa-hourglass-half'
|
||||
}
|
||||
|
||||
if ('downloading' === item.status && item.is_live) {
|
||||
return 'fa-globe fa-spin'
|
||||
}
|
||||
|
|
@ -350,6 +417,10 @@ const setIcon = item => {
|
|||
}
|
||||
|
||||
const setStatus = item => {
|
||||
if (!item.auto_start) {
|
||||
return 'Pending'
|
||||
}
|
||||
|
||||
if (null === item.status && true === config.paused) {
|
||||
return 'Paused'
|
||||
}
|
||||
|
|
@ -378,7 +449,7 @@ const setIconColor = item => {
|
|||
return 'has-text-info'
|
||||
}
|
||||
|
||||
if (null === item.status && true === config.paused) {
|
||||
if (!item.auto_start || (null === item.status && true === config.paused)) {
|
||||
return 'has-text-warning'
|
||||
}
|
||||
|
||||
|
|
@ -422,8 +493,12 @@ const percentPipe = value => {
|
|||
const updateProgress = (item) => {
|
||||
let string = ''
|
||||
|
||||
if (!item.auto_start) {
|
||||
return 'Manual start'
|
||||
}
|
||||
|
||||
if (null === item.status && true === config.paused) {
|
||||
return 'Paused'
|
||||
return 'Global Pause'
|
||||
}
|
||||
|
||||
if ('postprocessing' === item.status) {
|
||||
|
|
@ -482,6 +557,65 @@ const cancelItems = item => {
|
|||
items.forEach(id => socket.emit('item_cancel', id))
|
||||
}
|
||||
|
||||
const startItem = item => socket.emit('item_start', item._id)
|
||||
const pauseItem = item => socket.emit('item_pause', item._id)
|
||||
|
||||
const startItems = () => {
|
||||
if (selectedElms.value.length < 1) {
|
||||
return
|
||||
}
|
||||
|
||||
let filtered = []
|
||||
|
||||
selectedElms.value.forEach(id => {
|
||||
const item = stateStore.get('queue', id)
|
||||
if (item && !item.auto_start && !item.status) {
|
||||
filtered.push(id)
|
||||
}
|
||||
})
|
||||
|
||||
selectedElms.value = []
|
||||
|
||||
if (filtered.length < 1) {
|
||||
toast.error('No eligible items to start.')
|
||||
return
|
||||
}
|
||||
|
||||
if (true !== box.confirm(`Start '${filtered.length}' selected items?`)) {
|
||||
return false
|
||||
}
|
||||
|
||||
filtered.forEach(id => socket.emit('item_start', id))
|
||||
}
|
||||
|
||||
const pauseSelected = () => {
|
||||
if (selectedElms.value.length < 1) {
|
||||
return
|
||||
}
|
||||
|
||||
let filtered = []
|
||||
|
||||
selectedElms.value.forEach(id => {
|
||||
const item = stateStore.get('queue', id)
|
||||
if (item && item.auto_start && !item.status) {
|
||||
filtered.push(id)
|
||||
}
|
||||
})
|
||||
|
||||
selectedElms.value = []
|
||||
|
||||
if (filtered.length < 1) {
|
||||
toast.error('No eligible items to pause.')
|
||||
return
|
||||
}
|
||||
|
||||
if (true !== box.confirm(`Pause '${filtered.length}' selected items?`)) {
|
||||
return false
|
||||
}
|
||||
|
||||
filtered.forEach(id => socket.emit('item_pause', id))
|
||||
}
|
||||
|
||||
const pImg = e => e.target.naturalHeight > e.target.naturalWidth ? e.target.classList.add('image-portrait') : null
|
||||
|
||||
watch(embed_url, v => {
|
||||
|
|
|
|||
|
|
@ -195,6 +195,26 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column is-6-tablet is-12-mobile">
|
||||
<div class="field">
|
||||
<label class="label is-inline" for="output_template">
|
||||
<span class="icon"><i class="fa-solid fa-circle-play" /></span>
|
||||
Auto Start
|
||||
</label>
|
||||
<div class="control is-unselectable">
|
||||
<input id="auto_start" type="checkbox" v-model="form.auto_start" :disabled="addInProgress"
|
||||
class="switch is-success" />
|
||||
<label for="auto_start" class="is-unselectable">
|
||||
{{ form.auto_start ? 'Yes' : 'No' }}
|
||||
</label>
|
||||
</div>
|
||||
<span class="help">
|
||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||
<span>Whether to automatically start downloading or just queue them in paused state.</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column is-12">
|
||||
<div class="field">
|
||||
<label class="label is-inline" for="cli_options">
|
||||
|
|
@ -269,6 +289,7 @@
|
|||
</template>
|
||||
|
||||
<script setup>
|
||||
import 'assets/css/bulma-switch.css'
|
||||
import { useStorage } from '@vueuse/core'
|
||||
import { CronExpressionParser } from 'cron-parser'
|
||||
import { decode } from '~/utils/importer'
|
||||
|
|
@ -392,6 +413,8 @@ const importItem = async () => {
|
|||
form.cli = item.cli
|
||||
}
|
||||
|
||||
form.auto_start = item?.auto_start ?? true
|
||||
|
||||
if (item.preset) {
|
||||
// -- check if the preset exists in config.presets
|
||||
const preset = config.presets.find(p => p.name === item.preset)
|
||||
|
|
|
|||
|
|
@ -71,8 +71,7 @@ div.is-centered {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="columns is-multiline is-mobile" v-if="filteredTasks && filteredTasks.length > 0">
|
||||
<div class="columns is-multiline is-mobile" v-if="!toggleForm && filteredTasks && filteredTasks.length > 0">
|
||||
<div class="column" v-if="'list' !== display_style">
|
||||
<button type="button" class="button is-fullwidth is-ghost is-inverted"
|
||||
@click="masterSelectAll = !masterSelectAll">
|
||||
|
|
@ -103,10 +102,8 @@ div.is-centered {
|
|||
</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="columns is-multiline" v-if="!isLoading && !toggleForm && filteredTasks && filteredTasks.length > 0">
|
||||
<template v-if="'list' === display_style">
|
||||
<div class="column is-12">
|
||||
|
|
@ -152,9 +149,21 @@ div.is-centered {
|
|||
{{ remove_tags(item.name) }}
|
||||
</NuxtLink>
|
||||
</div>
|
||||
<div v-if="item.preset">
|
||||
<span class="icon"><i class="fa-solid fa-tv" /></span>
|
||||
<span>{{ item.preset ?? config.app.default_preset }}</span>
|
||||
<div>
|
||||
<span class="icon-text">
|
||||
<span class="icon">
|
||||
<i class="fa-solid"
|
||||
:class="{ 'fa-circle-pause': item.auto_start, 'fa-circle-play': !item.auto_start }" />
|
||||
</span>
|
||||
<span>
|
||||
{{ item.auto_start ? 'Auto' : 'Manual' }}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<span class="icon-text" v-if="item.preset">
|
||||
<span class="icon"><i class="fa-solid fa-tv" /></span>
|
||||
<span>{{ item.preset ?? config.app.default_preset }}</span>
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="is-vcentered has-text-centered">
|
||||
|
|
@ -218,7 +227,12 @@ div.is-centered {
|
|||
{{ tag }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="control">
|
||||
<span class="icon" v-tooltip="`${item.auto_start ? 'Auto' : 'Manual'} start`">
|
||||
<i class="fa-solid"
|
||||
:class="{ 'fa-circle-pause': item.auto_start, 'fa-circle-play': !item.auto_start }" />
|
||||
</span>
|
||||
</div>
|
||||
<div class="control">
|
||||
<a class="has-text-primary" v-tooltip="'Export task.'" @click.prevent="exportItem(item)">
|
||||
<span class="icon"><i class="fa-solid fa-file-export" /></span>
|
||||
|
|
@ -653,6 +667,10 @@ const runNow = async (item: task_item, mass: boolean = false) => {
|
|||
data.cli = item.cli
|
||||
}
|
||||
|
||||
if (item?.auto_start !== undefined) {
|
||||
data.auto_start = item.auto_start
|
||||
}
|
||||
|
||||
socket.emit('add_url', data)
|
||||
|
||||
if (true === mass) {
|
||||
|
|
@ -685,6 +703,7 @@ const exportItem = async (item: task_item) => {
|
|||
preset: info.preset,
|
||||
timer: info.timer,
|
||||
folder: info.folder,
|
||||
auto_start: info?.auto_start ?? true,
|
||||
} as exported_task
|
||||
|
||||
if (info.template) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue