Merge pull request #538 from arabcoders/dev
Feat: add flag to ignore already archived items from showing in download history.
This commit is contained in:
commit
1189d31f5c
10 changed files with 128 additions and 131 deletions
1
FAQ.md
1
FAQ.md
|
|
@ -55,6 +55,7 @@ or the `environment:` section in `compose.yaml` file.
|
|||
| YTP_AUTO_CLEAR_HISTORY_DAYS | Number of days after which completed download history is cleared. | `0` |
|
||||
| YTP_DEFAULT_PAGINATION | The default number of items per page for history. | `50` |
|
||||
| YTP_TASK_HANDLER_RANDOM_DELAY | The maximum random delay in seconds before starting a task handler. | `60` |
|
||||
| YTP_IGNORE_ARCHIVED_ITEMS | Don't report archived items in the download history. | `false` |
|
||||
|
||||
> [!NOTE]
|
||||
> To raise the maximum workers for specific extractor, you need to add a ENV variable that follows the pattern `YTP_MAX_WORKERS_FOR_<EXTRACTOR_NAME>`.
|
||||
|
|
|
|||
|
|
@ -734,7 +734,7 @@ class DownloadQueue(metaclass=Singleton):
|
|||
# This sometimes can be different from the final extracted ID, so we need to verify again after extraction.
|
||||
if archive_id and item.is_archived():
|
||||
store_type, dlInfo = await self.get_item(archive_id=archive_id)
|
||||
if not store_type:
|
||||
if not store_type and not self.config.ignore_archived_items:
|
||||
dlInfo = Download(
|
||||
info=ItemDTO(
|
||||
id=archive_id.split()[1],
|
||||
|
|
@ -763,7 +763,7 @@ class DownloadQueue(metaclass=Singleton):
|
|||
)
|
||||
return {"status": "ok"}
|
||||
|
||||
message: str = f"The URL '{item.url}' is already downloaded and recorded in archive."
|
||||
message: str = f"The URL '{item.url}':'{archive_id}' is already downloaded and recorded in archive."
|
||||
LOG.warning(message)
|
||||
self._notify.emit(
|
||||
Events.LOG_INFO, data={"preset": item.preset}, title="Already Downloaded", message=message
|
||||
|
|
@ -827,7 +827,7 @@ class DownloadQueue(metaclass=Singleton):
|
|||
if store_type:
|
||||
break
|
||||
|
||||
if not store_type:
|
||||
if not store_type and not self.config.ignore_archived_items:
|
||||
new_archive_id = new_archive_id or extra_ids.pop(0)
|
||||
dlInfo = Download(
|
||||
info=ItemDTO(
|
||||
|
|
@ -855,7 +855,7 @@ class DownloadQueue(metaclass=Singleton):
|
|||
)
|
||||
return {"status": "ok"}
|
||||
|
||||
message: str = f"The URL '{item.url}' is already downloaded and recorded in archive."
|
||||
message: str = f"The URL '{item.url}':'{archive_ids.pop(0)}' is already downloaded and recorded in archive."
|
||||
LOG.warning(message)
|
||||
self._notify.emit(
|
||||
Events.LOG_INFO, data={"preset": item.preset}, title="Already Downloaded", message=message
|
||||
|
|
|
|||
|
|
@ -216,6 +216,9 @@ class Config(metaclass=Singleton):
|
|||
task_handler_random_delay: float = 60.0
|
||||
"""The maximum random delay in seconds before starting a task handler."""
|
||||
|
||||
ignore_archived_items: bool = False
|
||||
"""Dont report archived items in the download history."""
|
||||
|
||||
pictures_backends: list[str] = [
|
||||
"https://unsplash.it/1920/1080?random",
|
||||
"https://picsum.photos/1920/1080",
|
||||
|
|
@ -280,6 +283,7 @@ class Config(metaclass=Singleton):
|
|||
"temp_disabled",
|
||||
"allow_internal_urls",
|
||||
"simple_mode",
|
||||
"ignore_archived_items",
|
||||
)
|
||||
"The variables that are booleans."
|
||||
|
||||
|
|
|
|||
|
|
@ -1,64 +1,48 @@
|
|||
<template>
|
||||
<div class="columns is-multiline is-mobile has-text-centered" v-if="hasItems">
|
||||
<div class="column is-half-mobile">
|
||||
<button type="button" class="button is-fullwidth is-ghost is-inverted"
|
||||
@click="masterSelectAll = !masterSelectAll">
|
||||
<span v-if="selectedElms.length > 0" class="mr-2 tag is-danger">
|
||||
{{ selectedElms.length }}
|
||||
<div class="columns is-multiline is-mobile has-text-centered is-justify-content-flex-end" v-if="hasItems">
|
||||
<div class="column is-narrow">
|
||||
<button type="button" class="button is-fullwidth" @click="masterSelectAll = !masterSelectAll"
|
||||
:class="{ 'has-text-primary': !masterSelectAll, 'has-text-danger': masterSelectAll }">
|
||||
<span class="icon">
|
||||
<i :class="!masterSelectAll ? 'fa-regular fa-square-check' : 'fa-regular fa-square'" />
|
||||
</span>
|
||||
<span class="icon-text is-block">
|
||||
<span class="icon">
|
||||
<i :class="!masterSelectAll ? 'fa-regular fa-square-check' : 'fa-regular fa-square'" />
|
||||
</span>
|
||||
<span v-if="!masterSelectAll">Select All</span>
|
||||
<span v-else>Unselect All</span>
|
||||
<span v-if="!masterSelectAll">Select</span>
|
||||
<span v-else>Unselect</span>
|
||||
<span v-if="selectedElms.length > 0">
|
||||
(<u class="has-text-danger">{{ selectedElms.length }}</u>)
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="column is-half-mobile">
|
||||
<button type="button" class="button is-fullwidth is-link" @click="downloadSelected"
|
||||
:disabled="!hasDownloaded || !hasSelected"
|
||||
v-tooltip="!hasSelected || !hasDownloaded ? '' : 'Download items as zip'">
|
||||
<span class="icon-text is-block">
|
||||
<div class="column is-2-tablet is-5-mobile">
|
||||
<Dropdown label="Actions" icons="fa-solid fa-list">
|
||||
<a class="dropdown-item has-text-link" @click="(hasDownloaded && hasSelected) ? downloadSelected() : null"
|
||||
:style="{ opacity: (!hasDownloaded || !hasSelected) ? 0.5 : 1, cursor: (!hasDownloaded || !hasSelected) ? 'not-allowed' : 'pointer' }"
|
||||
v-tooltip="!hasSelected || !hasDownloaded ? '' : 'Download items as zip'">
|
||||
<span class="icon"><i class="fa-solid fa-compress-alt" /></span>
|
||||
<span>Download</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="column is-half-mobile">
|
||||
<button type="button" class="button is-fullwidth is-danger" @click="deleteSelectedItems" :disabled="!hasSelected">
|
||||
<span class="icon-text is-block">
|
||||
</a>
|
||||
<a class="dropdown-item has-text-danger" @click="hasSelected ? deleteSelectedItems() : null"
|
||||
:style="{ opacity: !hasSelected ? 0.5 : 1, cursor: !hasSelected ? 'not-allowed' : 'pointer' }">
|
||||
<span class="icon"><i class="fa-solid fa-trash-can" /></span>
|
||||
<span>{{ config.app.remove_files ? 'Remove' : 'Clear' }}</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="column is-half-mobile" v-if="hasCompleted">
|
||||
<button type="button" class="button is-fullwidth is-primary is-inverted" @click="clearCompleted">
|
||||
<span class="icon-text is-block">
|
||||
</a>
|
||||
<hr class="dropdown-divider" v-if="hasCompleted || hasIncomplete">
|
||||
<a v-if="hasCompleted" class="dropdown-item has-text-primary" @click="clearCompleted">
|
||||
<span class="icon"><i class="fa-solid fa-circle-check" /></span>
|
||||
<span>Clear Completed</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="column is-half-mobile" v-if="hasIncomplete">
|
||||
<button type="button" class="button is-fullwidth is-info is-inverted" @click="clearIncomplete">
|
||||
<span class="icon-text is-block">
|
||||
</a>
|
||||
<a v-if="hasIncomplete" class="dropdown-item has-text-info" @click="clearIncomplete">
|
||||
<span class="icon"><i class="fa-solid fa-circle-xmark" /></span>
|
||||
<span>Clear Incomplete</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="column is-half-mobile" v-if="hasIncomplete">
|
||||
<button type="button" class="button is-fullwidth is-warning is-inverted" @click="retryIncomplete">
|
||||
<span class="icon-text is-block">
|
||||
</a>
|
||||
<a v-if="hasIncomplete" class="dropdown-item has-text-warning" @click="retryIncomplete">
|
||||
<span class="icon"><i class="fa-solid fa-rotate-right" /></span>
|
||||
<span>Retry Incomplete</span>
|
||||
</span>
|
||||
</button>
|
||||
</a>
|
||||
</Dropdown>
|
||||
</div>
|
||||
<div class="column is-1-tablet">
|
||||
<button type="button" class="button is-fullwidth" @click="direction = direction === 'desc' ? 'asc' : 'desc'">
|
||||
<div class="column is-narrow">
|
||||
<button type="button" class="button" @click="direction = direction === 'desc' ? 'asc' : 'desc'">
|
||||
<span class="icon-text is-block">
|
||||
<span class="icon">
|
||||
<i class="fa-solid" :class="direction === 'desc' ? 'fa-arrow-down-a-z' : 'fa-arrow-up-a-z'" />
|
||||
|
|
@ -725,7 +709,10 @@ const clearCompleted = async () => {
|
|||
return
|
||||
}
|
||||
selectedElms.value = []
|
||||
await stateStore.deleteItems('history', { status: 'finished', removeFile: false })
|
||||
Promise.all([
|
||||
stateStore.deleteItems('history', { status: 'finished', removeFile: false }),
|
||||
stateStore.deleteItems('history', { status: 'skip', removeFile: false })
|
||||
])
|
||||
}
|
||||
|
||||
const clearIncomplete = async () => {
|
||||
|
|
|
|||
|
|
@ -1,37 +1,37 @@
|
|||
<template>
|
||||
<div class="columns is-multiline is-mobile has-text-centered" v-if="filteredItems.length > 0">
|
||||
<div class="column is-half-mobile">
|
||||
<button type="button" class="button is-fullwidth is-ghost" @click="masterSelectAll = !masterSelectAll">
|
||||
<span v-if="selectedElms.length > 0" class="mr-2 tag is-danger">
|
||||
{{ selectedElms.length }}
|
||||
<div class="columns is-multiline is-mobile has-text-centered is-justify-content-flex-end"
|
||||
v-if="filteredItems.length > 0">
|
||||
<div class="column is-narrow">
|
||||
<button type="button" class="button" @click="masterSelectAll = !masterSelectAll"
|
||||
:class="{ 'has-text-primary': !masterSelectAll, 'has-text-danger': masterSelectAll }">
|
||||
<span class="icon">
|
||||
<i :class="!masterSelectAll ? 'fa-regular fa-square-check' : 'fa-regular fa-square'" />
|
||||
</span>
|
||||
<span class="icon-text is-block">
|
||||
<span class="icon">
|
||||
<i class="fas" :class="!masterSelectAll ? 'fa-regular fa-square-check' : 'fa-regular fa-square'" />
|
||||
</span>
|
||||
<span v-if="!masterSelectAll">Select All</span>
|
||||
<span v-else> Unselect All</span>
|
||||
<span v-if="!masterSelectAll">Select</span>
|
||||
<span v-else>Unselect</span>
|
||||
<span v-if="selectedElms.length > 0">
|
||||
(<u class="has-text-danger">{{ selectedElms.length }}</u>)
|
||||
</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-warning 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">
|
||||
<button type="button" class="button is-fullwidth is-warning" :disabled="!hasSelected" @click="cancelSelected">
|
||||
<span class="icon"><i class="fa-solid fa-eject" /></span>
|
||||
<span>Cancel</span>
|
||||
</button>
|
||||
<div class="column is-2-tablet is-5-mobile">
|
||||
<Dropdown label="Actions" icons="fa-solid fa-list">
|
||||
<a v-if="hasManualStart" class="dropdown-item has-text-success" @click="hasSelected ? startItems() : null"
|
||||
:style="{ opacity: !hasSelected ? 0.5 : 1, cursor: !hasSelected ? 'not-allowed' : 'pointer' }">
|
||||
<span class="icon"><i class="fa-solid fa-circle-play" /></span>
|
||||
<span>Start</span>
|
||||
</a>
|
||||
<a v-if="hasPausable" class="dropdown-item has-text-warning" @click="hasSelected ? pauseSelected() : null"
|
||||
:style="{ opacity: !hasSelected ? 0.5 : 1, cursor: !hasSelected ? 'not-allowed' : 'pointer' }">
|
||||
<span class="icon"><i class="fa-solid fa-pause" /></span>
|
||||
<span>Pause</span>
|
||||
</a>
|
||||
<a class="dropdown-item has-text-warning" @click="hasSelected ? cancelSelected() : null"
|
||||
:style="{ opacity: !hasSelected ? 0.5 : 1, cursor: !hasSelected ? 'not-allowed' : 'pointer' }">
|
||||
<span class="icon"><i class="fa-solid fa-eject" /></span>
|
||||
<span>Cancel</span>
|
||||
</a>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -105,36 +105,36 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="columns is-mobile is-multiline" v-if="config.app.browser_control_enabled && items && items.length > 0">
|
||||
<div class="column is-12-mobile">
|
||||
<button type="button" class="button is-fullwidth is-ghost is-inverted"
|
||||
@click="masterSelectAll = !masterSelectAll" :disabled="isLoading || items.length < 1">
|
||||
<span class="icon-text is-block">
|
||||
<span class="icon">
|
||||
<i :class="!masterSelectAll ? 'fa-regular fa-square-check' : 'fa-regular fa-square'" />
|
||||
</span>
|
||||
<span v-if="!masterSelectAll">Select All</span>
|
||||
<span v-else>Unselect All</span>
|
||||
<div class="columns is-mobile is-multiline is-justify-content-flex-end" v-if="config.app.browser_control_enabled && items && items.length > 0">
|
||||
<div class="column is-narrow">
|
||||
<button type="button" class="button" @click="masterSelectAll = !masterSelectAll"
|
||||
:class="{ 'has-text-primary': !masterSelectAll, 'has-text-danger': masterSelectAll }"
|
||||
:disabled="isLoading || items.length < 1">
|
||||
<span class="icon">
|
||||
<i :class="!masterSelectAll ? 'fa-regular fa-square-check' : 'fa-regular fa-square'" />
|
||||
</span>
|
||||
<span v-if="!masterSelectAll">Select</span>
|
||||
<span v-else>Unselect</span>
|
||||
<span v-if="selectedElms.length > 0">
|
||||
(<u class="has-text-danger">{{ selectedElms.length }}</u>)
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="column is-half-mobile">
|
||||
<button type="button" class="button is-fullwidth is-danger" @click="deleteSelected"
|
||||
:disabled="selectedElms.length < 1 || isLoading || items.length < 1">
|
||||
<span class="icon-text is-block">
|
||||
<div class="column is-2-tablet is-5-mobile">
|
||||
<Dropdown label="Actions" icons="fa-solid fa-list">
|
||||
<a class="dropdown-item has-text-danger"
|
||||
@click="(selectedElms.length > 0 && !isLoading) ? deleteSelected() : null"
|
||||
:style="{ opacity: (selectedElms.length < 1 || isLoading) ? 0.5 : 1, cursor: (selectedElms.length < 1 || isLoading) ? 'not-allowed' : 'pointer' }">
|
||||
<span class="icon"><i class="fa-solid fa-trash-can" /></span>
|
||||
<span>Delete {{ selectedElms.length > 0 ? selectedElms.length : '' }} items</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="column is-half-mobile">
|
||||
<button type="button" class="button is-fullwidth is-link" @click="moveSelected"
|
||||
:disabled="selectedElms.length < 1 || isLoading || items.length < 1">
|
||||
<span class="icon-text is-block">
|
||||
<span>Delete{{ selectedElms.length > 0 ? ` (${selectedElms.length})` : '' }}</span>
|
||||
</a>
|
||||
<a class="dropdown-item has-text-link"
|
||||
@click="(selectedElms.length > 0 && !isLoading) ? moveSelected() : null"
|
||||
:style="{ opacity: (selectedElms.length < 1 || isLoading) ? 0.5 : 1, cursor: (selectedElms.length < 1 || isLoading) ? 'not-allowed' : 'pointer' }">
|
||||
<span class="icon"><i class="fa-solid fa-arrows-alt" /></span>
|
||||
<span>Move {{ selectedElms.length > 0 ? selectedElms.length : '' }} items</span>
|
||||
</span>
|
||||
</button>
|
||||
<span>Move{{ selectedElms.length > 0 ? ` (${selectedElms.length})` : '' }}</span>
|
||||
</a>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -197,7 +197,7 @@
|
|||
<span class="icon"><i class="fa-solid fa-list" /></span>
|
||||
<span>Extras:
|
||||
<span v-for="(value, key) in cond.extras" :key="key" class="tag is-info mr-2">
|
||||
<strong>{{ key }}</strong>: {{ value }}
|
||||
<b>{{ key }}</b>: {{ value }}
|
||||
</span>
|
||||
</span>
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -72,36 +72,36 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="columns is-multiline is-mobile" v-if="!toggleForm && filteredTasks && filteredTasks.length > 0">
|
||||
<div class="column">
|
||||
<button type="button" class="button is-fullwidth is-ghost is-inverted"
|
||||
@click="masterSelectAll = !masterSelectAll">
|
||||
<span class="icon-text is-block">
|
||||
<span class="icon">
|
||||
<i :class="!masterSelectAll ? 'fa-regular fa-square-check' : 'fa-regular fa-square'" />
|
||||
</span>
|
||||
<span v-if="!masterSelectAll">Select All</span>
|
||||
<span v-else>Unselect All</span>
|
||||
<div class="columns is-multiline is-mobile is-justify-content-flex-end" v-if="!toggleForm && filteredTasks && filteredTasks.length > 0">
|
||||
<div class="column is-narrow">
|
||||
<button type="button" class="button" @click="masterSelectAll = !masterSelectAll"
|
||||
:class="{ 'has-text-primary': !masterSelectAll, 'has-text-danger': masterSelectAll }">
|
||||
<span class="icon">
|
||||
<i :class="!masterSelectAll ? 'fa-regular fa-square-check' : 'fa-regular fa-square'" />
|
||||
</span>
|
||||
<span v-if="!masterSelectAll">Select</span>
|
||||
<span v-else>Unselect</span>
|
||||
<span v-if="selectedElms.length > 0">
|
||||
(<u class="has-text-danger">{{ selectedElms.length }}</u>)
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="column">
|
||||
<button class="button is-purple is-fullwidth" @click="runConfirm()"
|
||||
:disabled="selectedElms.length < 1 || massRun" :class="{ 'is-loading': massRun }">
|
||||
<span class="icon"><i class="fa-solid fa-up-right-from-square" /></span>
|
||||
<span>Run Selected</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="column">
|
||||
<button type="button" class="button is-fullwidth is-danger" @click="deleteConfirm()"
|
||||
:disabled="selectedElms.length < 1 || massDelete" :class="{ 'is-loading': massDelete }">
|
||||
<span class="icon-text is-block">
|
||||
<div class="column is-2-tablet is-5-mobile">
|
||||
<Dropdown label="Actions" icons="fa-solid fa-list">
|
||||
<a class="dropdown-item has-text-purple"
|
||||
@click="(selectedElms.length > 0 && !massRun) ? runConfirm() : null"
|
||||
:style="{ opacity: (selectedElms.length < 1 || massRun) ? 0.5 : 1, cursor: (selectedElms.length < 1 || massRun) ? 'not-allowed' : 'pointer' }">
|
||||
<span class="icon"><i class="fa-solid fa-up-right-from-square" /></span>
|
||||
<span>Run Selected</span>
|
||||
</a>
|
||||
<a class="dropdown-item has-text-danger"
|
||||
@click="(selectedElms.length > 0 && !massDelete) ? deleteConfirm() : null"
|
||||
:style="{ opacity: (selectedElms.length < 1 || massDelete) ? 0.5 : 1, cursor: (selectedElms.length < 1 || massDelete) ? 'not-allowed' : 'pointer' }">
|
||||
<span class="icon"><i class="fa-solid fa-trash-can" /></span>
|
||||
<span>Remove Selected</span>
|
||||
</span>
|
||||
</button>
|
||||
</a>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ export const useSocketStore = defineStore('socket', () => {
|
|||
return;
|
||||
}
|
||||
|
||||
if (json.data?.folder) {
|
||||
if (json.data?.folders) {
|
||||
config.add('folders', json.data.folders)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -176,7 +176,12 @@ export const useStateStore = defineStore('state', () => {
|
|||
|
||||
const getPagination = () => state.pagination
|
||||
|
||||
const setHistoryCount = (count: number) => state.pagination.total = count
|
||||
const setHistoryCount = (count: number) => {
|
||||
state.pagination.total = count
|
||||
if (count > 0 && !state.pagination.isLoaded) {
|
||||
state.pagination.isLoaded = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete items by specific IDs or status filter.
|
||||
|
|
|
|||
Loading…
Reference in a new issue