migrated /api/archive to /api/history/{id}/archive
This commit is contained in:
parent
fd999211ef
commit
20cfc00640
8 changed files with 150 additions and 220 deletions
20
API.md
20
API.md
|
|
@ -25,8 +25,8 @@ This document describes the available endpoints and their usage. All endpoints r
|
||||||
- [POST /api/history/{id}](#post-apihistoryid)
|
- [POST /api/history/{id}](#post-apihistoryid)
|
||||||
- [GET /api/history/{id}](#get-apihistoryid)
|
- [GET /api/history/{id}](#get-apihistoryid)
|
||||||
- [GET /api/history](#get-apihistory)
|
- [GET /api/history](#get-apihistory)
|
||||||
- [DELETE /api/archive/{id}](#delete-apiarchiveid)
|
- [DELETE /api/history/{id}/archive](#delete-apihistoryidarchive)
|
||||||
- [POST /api/archive/{id}](#post-apiarchiveid)
|
- [POST /api/history/{id}/archive](#post-apihistoryidarchive)
|
||||||
- [GET /api/tasks](#get-apitasks)
|
- [GET /api/tasks](#get-apitasks)
|
||||||
- [PUT /api/tasks](#put-apitasks)
|
- [PUT /api/tasks](#put-apitasks)
|
||||||
- [POST /api/tasks/{id}/mark](#post-apitasksidmark)
|
- [POST /api/tasks/{id}/mark](#post-apitasksidmark)
|
||||||
|
|
@ -361,17 +361,11 @@ or an error:
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### DELETE /api/archive/{id}
|
### DELETE /api/history/{id}/archive
|
||||||
**Purpose**: Remove an item's URL from the yt-dlp archive file, allowing it to be re-downloaded.
|
**Purpose**: Remove an item from archive file, allowing it to be re-downloaded.
|
||||||
|
|
||||||
**Path Parameter**:
|
**Path Parameter**:
|
||||||
- `id`: Item ID from the history (if body `url` is not provided).
|
- `id`: Item ID from the history.
|
||||||
|
|
||||||
**Body (optional)**:
|
|
||||||
```json
|
|
||||||
{ "url": "https://..." }
|
|
||||||
```
|
|
||||||
If `url` is provided, it is used directly; otherwise the route resolves the item by `id` and uses its URL.
|
|
||||||
|
|
||||||
**Response**:
|
**Response**:
|
||||||
```json
|
```json
|
||||||
|
|
@ -387,8 +381,8 @@ or an error:
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### POST /api/archive/{id}
|
### POST /api/history/{id}/archive
|
||||||
**Purpose**: Manually mark an item as archived by writing its archive ID to the archive file.
|
**Purpose**: Add item to the archive file preventing it from being downloaded.
|
||||||
|
|
||||||
**Path Parameter**:
|
**Path Parameter**:
|
||||||
- `id`: Item ID from the history.
|
- `id`: Item ID from the history.
|
||||||
|
|
|
||||||
|
|
@ -1,148 +0,0 @@
|
||||||
import logging
|
|
||||||
from pathlib import Path
|
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
from aiohttp import web
|
|
||||||
from aiohttp.web import Request, Response
|
|
||||||
|
|
||||||
from app.library.Download import Download
|
|
||||||
from app.library.DownloadQueue import DownloadQueue
|
|
||||||
from app.library.router import route
|
|
||||||
from app.library.Utils import archive_add, archive_delete, archive_read, get_archive_id
|
|
||||||
from app.library.YTDLPOpts import YTDLPOpts
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from library.Download import Download
|
|
||||||
|
|
||||||
LOG: logging.Logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
@route("POST", r"api/archive/{id}", "archive.item")
|
|
||||||
async def archive_item(request: Request, queue: DownloadQueue):
|
|
||||||
"""
|
|
||||||
Manually mark an item as archived.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (Request): The request object.
|
|
||||||
queue (DownloadQueue): The download queue instance.
|
|
||||||
config (Config): The configuration instance.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Response: The response object.
|
|
||||||
|
|
||||||
"""
|
|
||||||
if not (id := request.match_info.get("id")):
|
|
||||||
return web.json_response(data={"error": "id is required."}, status=web.HTTPBadRequest.status_code)
|
|
||||||
|
|
||||||
try:
|
|
||||||
item: Download | None = queue.done.get_by_id(id)
|
|
||||||
if not item:
|
|
||||||
return web.json_response(data={"error": f"item '{id}' not found."}, status=web.HTTPNotFound.status_code)
|
|
||||||
except KeyError:
|
|
||||||
return web.json_response(data={"error": f"item '{id}' not found."}, status=web.HTTPNotFound.status_code)
|
|
||||||
|
|
||||||
params: dict = item.get_ytdlp_opts().get_all()
|
|
||||||
|
|
||||||
if not (archive_file := params.get("download_archive")):
|
|
||||||
return web.json_response(
|
|
||||||
data={"error": f"item '{item.info.title}' does not have an archive file."},
|
|
||||||
status=web.HTTPBadRequest.status_code,
|
|
||||||
)
|
|
||||||
|
|
||||||
idDict = get_archive_id(url=item.info.url)
|
|
||||||
if not (archive_id := idDict.get("archive_id")):
|
|
||||||
return web.json_response(
|
|
||||||
data={"error": f"item '{item.info.title}' does not have an archive ID."},
|
|
||||||
status=web.HTTPBadRequest.status_code,
|
|
||||||
)
|
|
||||||
|
|
||||||
if len(archive_read(archive_file, [archive_id])) > 0:
|
|
||||||
return web.json_response(
|
|
||||||
data={"error": f"item '{item.info.title}' already archived."},
|
|
||||||
status=web.HTTPConflict.status_code,
|
|
||||||
)
|
|
||||||
|
|
||||||
archive_add(archive_file, [archive_id])
|
|
||||||
|
|
||||||
return web.json_response(
|
|
||||||
data={"message": f"item '{item.info.title}' archived."},
|
|
||||||
status=web.HTTPOk.status_code,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@route("DELETE", r"api/archive/{id}", "archive.remove")
|
|
||||||
async def archive_remove(request: Request, queue: DownloadQueue) -> Response:
|
|
||||||
"""
|
|
||||||
Remove an item from the archive.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (Request): The request object.
|
|
||||||
queue (DownloadQueue): The download queue instance.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Response: The response object.
|
|
||||||
|
|
||||||
"""
|
|
||||||
item = None
|
|
||||||
try:
|
|
||||||
data: dict | None = await request.json()
|
|
||||||
except Exception:
|
|
||||||
data = {}
|
|
||||||
|
|
||||||
title: str = ""
|
|
||||||
url: str | None = data.get("url")
|
|
||||||
id: str | None = request.match_info.get("id")
|
|
||||||
preset: str | None = data.get("preset",)
|
|
||||||
|
|
||||||
if not id and not url:
|
|
||||||
return web.json_response(data={"error": "id or url is required."}, status=web.HTTPBadRequest.status_code)
|
|
||||||
|
|
||||||
if id:
|
|
||||||
try:
|
|
||||||
item: Download | None = queue.done.get_by_id(id)
|
|
||||||
if not item:
|
|
||||||
return web.json_response(data={"error": f"item '{id}' not found."}, status=web.HTTPNotFound.status_code)
|
|
||||||
|
|
||||||
url = item.info.url
|
|
||||||
title = f" '{item.info.title}'"
|
|
||||||
params = item.get_ytdlp_opts().get_all()
|
|
||||||
except KeyError:
|
|
||||||
return web.json_response(data={"error": f"item '{id}' not found."}, status=web.HTTPNotFound.status_code)
|
|
||||||
else:
|
|
||||||
params: YTDLPOpts = YTDLPOpts.get_instance()
|
|
||||||
if preset := data.get("preset"):
|
|
||||||
params = params.preset(name=preset)
|
|
||||||
|
|
||||||
params = params.get_all()
|
|
||||||
|
|
||||||
if not (archive_file := params.get("download_archive", None)):
|
|
||||||
return web.json_response(
|
|
||||||
data={"error": "Archive file is not configured."},
|
|
||||||
status=web.HTTPBadRequest.status_code,
|
|
||||||
)
|
|
||||||
|
|
||||||
archive_file = Path(archive_file)
|
|
||||||
|
|
||||||
if not archive_file.exists():
|
|
||||||
return web.json_response(
|
|
||||||
data={"error": f"Archive file '{archive_file}' does not exist."},
|
|
||||||
status=web.HTTPNotFound.status_code,
|
|
||||||
)
|
|
||||||
|
|
||||||
idDict = get_archive_id(url=url)
|
|
||||||
if not (archive_id := idDict.get("archive_id")):
|
|
||||||
return web.json_response(
|
|
||||||
data={"error": "item does not have an archive ID."},
|
|
||||||
status=web.HTTPBadRequest.status_code,
|
|
||||||
)
|
|
||||||
|
|
||||||
if not archive_delete(archive_file, [archive_id]):
|
|
||||||
return web.json_response(
|
|
||||||
data={"error": f"item{title} not found in '{archive_file}' archive."},
|
|
||||||
status=web.HTTPNotFound.status_code,
|
|
||||||
)
|
|
||||||
|
|
||||||
return web.json_response(
|
|
||||||
data={"message": f"item{title} removed from '{archive_file}' archive."},
|
|
||||||
status=web.HTTPOk.status_code,
|
|
||||||
)
|
|
||||||
|
|
@ -7,13 +7,14 @@ from aiohttp import web
|
||||||
from aiohttp.web import Request, Response
|
from aiohttp.web import Request, Response
|
||||||
|
|
||||||
from app.library.config import Config
|
from app.library.config import Config
|
||||||
|
from app.library.Download import Download
|
||||||
from app.library.DownloadQueue import DownloadQueue
|
from app.library.DownloadQueue import DownloadQueue
|
||||||
from app.library.encoder import Encoder
|
from app.library.encoder import Encoder
|
||||||
from app.library.Events import EventBus, Events
|
from app.library.Events import EventBus, Events
|
||||||
from app.library.ItemDTO import Item
|
from app.library.ItemDTO import Item
|
||||||
from app.library.Presets import Preset, Presets
|
from app.library.Presets import Preset, Presets
|
||||||
from app.library.router import route
|
from app.library.router import route
|
||||||
from app.library.Utils import archive_read
|
from app.library.Utils import archive_add, archive_delete, archive_read, get_archive_id
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from library.Download import Download
|
from library.Download import Download
|
||||||
|
|
@ -263,3 +264,116 @@ async def items_add(request: Request, queue: DownloadQueue, encoder: Encoder) ->
|
||||||
response.append({"item": item, "status": "ok" == status[i].get("status"), "msg": status[i].get("msg")})
|
response.append({"item": item, "status": "ok" == status[i].get("status"), "msg": status[i].get("msg")})
|
||||||
|
|
||||||
return web.json_response(data=response, status=web.HTTPOk.status_code, dumps=encoder.encode)
|
return web.json_response(data=response, status=web.HTTPOk.status_code, dumps=encoder.encode)
|
||||||
|
|
||||||
|
|
||||||
|
@route("POST", r"api/history/{id}/archive", "history.item.archive.add")
|
||||||
|
async def item_archive_add(request: Request, queue: DownloadQueue) -> Response:
|
||||||
|
"""
|
||||||
|
Manually mark an item as archived.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (Request): The request object.
|
||||||
|
queue (DownloadQueue): The download queue instance.
|
||||||
|
config (Config): The configuration instance.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Response: The response object.
|
||||||
|
|
||||||
|
"""
|
||||||
|
if not (id := request.match_info.get("id")):
|
||||||
|
return web.json_response(data={"error": "id is required."}, status=web.HTTPBadRequest.status_code)
|
||||||
|
|
||||||
|
try:
|
||||||
|
item: Download | None = queue.done.get_by_id(id)
|
||||||
|
if not item:
|
||||||
|
return web.json_response(data={"error": f"item '{id}' not found."}, status=web.HTTPNotFound.status_code)
|
||||||
|
except KeyError:
|
||||||
|
return web.json_response(data={"error": f"item '{id}' not found."}, status=web.HTTPNotFound.status_code)
|
||||||
|
|
||||||
|
params: dict = item.get_ytdlp_opts().get_all()
|
||||||
|
|
||||||
|
if not (archive_file := params.get("download_archive")):
|
||||||
|
return web.json_response(
|
||||||
|
data={"error": f"item '{item.info.title}' does not have an archive file."},
|
||||||
|
status=web.HTTPBadRequest.status_code,
|
||||||
|
)
|
||||||
|
|
||||||
|
idDict = get_archive_id(url=item.info.url)
|
||||||
|
if not (archive_id := idDict.get("archive_id")):
|
||||||
|
return web.json_response(
|
||||||
|
data={"error": f"item '{item.info.title}' does not have an archive ID."},
|
||||||
|
status=web.HTTPBadRequest.status_code,
|
||||||
|
)
|
||||||
|
|
||||||
|
if len(archive_read(archive_file, [archive_id])) > 0:
|
||||||
|
return web.json_response(
|
||||||
|
data={"error": f"item '{item.info.title}' already archived."},
|
||||||
|
status=web.HTTPConflict.status_code,
|
||||||
|
)
|
||||||
|
|
||||||
|
archive_add(archive_file, [archive_id])
|
||||||
|
|
||||||
|
return web.json_response(
|
||||||
|
data={"message": f"item '{item.info.title}' archived."},
|
||||||
|
status=web.HTTPOk.status_code,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@route("DELETE", r"api/history/{id}/archive", "history.item.archive.delete")
|
||||||
|
async def item_archive_delete(request: Request, queue: DownloadQueue) -> Response:
|
||||||
|
"""
|
||||||
|
Remove an item from the archive.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (Request): The request object.
|
||||||
|
queue (DownloadQueue): The download queue instance.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Response: The response object.
|
||||||
|
|
||||||
|
"""
|
||||||
|
if not (id := request.match_info.get("id")):
|
||||||
|
return web.json_response(data={"error": "id is required."}, status=web.HTTPBadRequest.status_code)
|
||||||
|
|
||||||
|
try:
|
||||||
|
item: Download | None = queue.done.get_by_id(id)
|
||||||
|
if not item:
|
||||||
|
return web.json_response(data={"error": f"item '{id}' not found."}, status=web.HTTPNotFound.status_code)
|
||||||
|
except KeyError:
|
||||||
|
return web.json_response(data={"error": f"item '{id}' not found."}, status=web.HTTPNotFound.status_code)
|
||||||
|
|
||||||
|
url: str = item.info.url
|
||||||
|
title: str = f" '{item.info.title}'"
|
||||||
|
params: dict = item.get_ytdlp_opts().get_all()
|
||||||
|
|
||||||
|
if not (archive_file := params.get("download_archive")):
|
||||||
|
return web.json_response(
|
||||||
|
data={"error": "Archive file is not configured."},
|
||||||
|
status=web.HTTPBadRequest.status_code,
|
||||||
|
)
|
||||||
|
|
||||||
|
archive_file = Path(archive_file)
|
||||||
|
|
||||||
|
if not archive_file.exists():
|
||||||
|
return web.json_response(
|
||||||
|
data={"error": f"Archive file '{archive_file}' does not exist."},
|
||||||
|
status=web.HTTPNotFound.status_code,
|
||||||
|
)
|
||||||
|
|
||||||
|
idDict = get_archive_id(url=url)
|
||||||
|
if not (archive_id := idDict.get("archive_id")):
|
||||||
|
return web.json_response(
|
||||||
|
data={"error": "item does not have an archive ID."},
|
||||||
|
status=web.HTTPBadRequest.status_code,
|
||||||
|
)
|
||||||
|
|
||||||
|
if not archive_delete(archive_file, [archive_id]):
|
||||||
|
return web.json_response(
|
||||||
|
data={"error": f"item{title} not found in '{archive_file}' archive."},
|
||||||
|
status=web.HTTPNotFound.status_code,
|
||||||
|
)
|
||||||
|
|
||||||
|
return web.json_response(
|
||||||
|
data={"message": f"item{title} removed from '{archive_file}' archive."},
|
||||||
|
status=web.HTTPOk.status_code,
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ const emitter = defineEmits<{ (e: 'closeModel'): void }>()
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
link?: string
|
link?: string
|
||||||
preset?: string
|
preset?: string
|
||||||
|
cli?: string
|
||||||
useUrl?: boolean
|
useUrl?: boolean
|
||||||
externalModel?: boolean
|
externalModel?: boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
@ -71,6 +72,9 @@ onMounted(async (): Promise<void> => {
|
||||||
if (props.preset) {
|
if (props.preset) {
|
||||||
params.append('preset', props.preset)
|
params.append('preset', props.preset)
|
||||||
}
|
}
|
||||||
|
if (props.cli) {
|
||||||
|
params.append('args', props.cli)
|
||||||
|
}
|
||||||
params.append('url', props.link || '')
|
params.append('url', props.link || '')
|
||||||
url += '?' + params.toString()
|
url += '?' + params.toString()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -193,7 +193,7 @@
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
<hr class="dropdown-divider" />
|
<hr class="dropdown-divider" />
|
||||||
</template>
|
</template>
|
||||||
<NuxtLink class="dropdown-item" @click="emitter('getInfo', item.url, item.preset)">
|
<NuxtLink class="dropdown-item" @click="emitter('getInfo', item.url, item.preset, item.cli)">
|
||||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||||
<span>yt-dlp Information</span>
|
<span>yt-dlp Information</span>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
|
|
@ -368,7 +368,7 @@
|
||||||
<hr class="dropdown-divider" />
|
<hr class="dropdown-divider" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<NuxtLink class="dropdown-item" @click="emitter('getInfo', item.url, item.preset)"
|
<NuxtLink class="dropdown-item" @click="emitter('getInfo', item.url, item.preset, item.cli)"
|
||||||
v-if="!config.app.basic_mode">
|
v-if="!config.app.basic_mode">
|
||||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||||
<span>yt-dlp Information</span>
|
<span>yt-dlp Information</span>
|
||||||
|
|
@ -466,7 +466,7 @@ import { useStorage } from '@vueuse/core'
|
||||||
import type { StoreItem } from '~/types/store'
|
import type { StoreItem } from '~/types/store'
|
||||||
|
|
||||||
const emitter = defineEmits<{
|
const emitter = defineEmits<{
|
||||||
(e: 'getInfo', url: string, preset: string): void
|
(e: 'getInfo', url: string, preset: string, cli: string): void
|
||||||
(e: 'add_new', item: Partial<StoreItem>): void
|
(e: 'add_new', item: Partial<StoreItem>): void
|
||||||
(e: 'getItemInfo', id: string): void
|
(e: 'getItemInfo', id: string): void
|
||||||
(e: 'clear_search'): void
|
(e: 'clear_search'): void
|
||||||
|
|
@ -737,7 +737,7 @@ const addArchiveDialog = (item: StoreItem) => {
|
||||||
|
|
||||||
const archiveItem = async (item: StoreItem, opts = {}) => {
|
const archiveItem = async (item: StoreItem, opts = {}) => {
|
||||||
try {
|
try {
|
||||||
const req = await request(`/api/archive/${item._id}`, {
|
const req = await request(`/api/history/${item._id}/archive`, {
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
})
|
})
|
||||||
|
|
@ -871,10 +871,7 @@ const removeFromArchiveDialog = (item: StoreItem) => {
|
||||||
const removeFromArchive = async (item: StoreItem, opts?: { re_add?: boolean, remove_history?: boolean }) => {
|
const removeFromArchive = async (item: StoreItem, opts?: { re_add?: boolean, remove_history?: boolean }) => {
|
||||||
console.log('Removing from archive:', item, opts)
|
console.log('Removing from archive:', item, opts)
|
||||||
try {
|
try {
|
||||||
const req = await request(`/api/archive/${item._id}`, {
|
const req = await request(`/api/history/${item._id}/archive`, { credentials: 'include', method: 'DELETE' })
|
||||||
credentials: 'include',
|
|
||||||
method: 'DELETE',
|
|
||||||
})
|
|
||||||
const data = await req.json()
|
const data = await req.json()
|
||||||
if (!req.ok) {
|
if (!req.ok) {
|
||||||
toast.error(data.error)
|
toast.error(data.error)
|
||||||
|
|
|
||||||
|
|
@ -167,16 +167,11 @@
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
<hr class="dropdown-divider" />
|
<hr class="dropdown-divider" />
|
||||||
|
|
||||||
<NuxtLink class="dropdown-item" @click="emitter('getInfo', form.url, form.preset)">
|
<NuxtLink class="dropdown-item" @click="emitter('getInfo', form.url, form.preset, form.cli)">
|
||||||
<span class="icon has-text-info"><i class="fa-solid fa-info" /></span>
|
<span class="icon has-text-info"><i class="fa-solid fa-info" /></span>
|
||||||
<span>yt-dlp Information</span>
|
<span>yt-dlp Information</span>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
|
|
||||||
<NuxtLink class="dropdown-item" @click="removeFromArchive(form.url)">
|
|
||||||
<span class="icon has-text-warning"><i class="fa-solid fa-box-archive" /></span>
|
|
||||||
<span>Remove from archive</span>
|
|
||||||
</NuxtLink>
|
|
||||||
|
|
||||||
<hr class="dropdown-divider" />
|
<hr class="dropdown-divider" />
|
||||||
<NuxtLink class="dropdown-item" @click="resetConfig">
|
<NuxtLink class="dropdown-item" @click="resetConfig">
|
||||||
<span class="icon has-text-danger"><i class="fa-solid fa-rotate-left" /></span>
|
<span class="icon has-text-danger"><i class="fa-solid fa-rotate-left" /></span>
|
||||||
|
|
@ -194,7 +189,8 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<button type="button" class="button is-info" @click="emitter('getInfo', form.url, form.preset)"
|
<button type="button" class="button is-info"
|
||||||
|
@click="emitter('getInfo', form.url, form.preset, form.cli)"
|
||||||
:class="{ 'is-loading': !socket.isConnected }"
|
:class="{ 'is-loading': !socket.isConnected }"
|
||||||
:disabled="!socket.isConnected || addInProgress || !form?.url">
|
: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>
|
||||||
|
|
@ -202,15 +198,6 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="control">
|
|
||||||
<button type="button" class="button is-warning" @click="removeFromArchive(form.url)"
|
|
||||||
:class="{ 'is-loading': !socket.isConnected }"
|
|
||||||
:disabled="!socket.isConnected || addInProgress || !form?.url">
|
|
||||||
<span class="icon"><i class="fa-solid fa-box-archive" /></span>
|
|
||||||
<span>Remove from archive</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<button type="button" class="button is-danger" @click="resetConfig"
|
<button type="button" class="button is-danger" @click="resetConfig"
|
||||||
:disabled="!!(!socket.isConnected || form?.id)" v-tooltip="'Reset local settings'">
|
:disabled="!!(!socket.isConnected || form?.id)" v-tooltip="'Reset local settings'">
|
||||||
|
|
@ -248,7 +235,7 @@ import type { AutoCompleteOptions } from '~/types/autocomplete';
|
||||||
|
|
||||||
const props = defineProps<{ item?: Partial<item_request> }>()
|
const props = defineProps<{ item?: Partial<item_request> }>()
|
||||||
const emitter = defineEmits<{
|
const emitter = defineEmits<{
|
||||||
(e: 'getInfo', url: string, preset: string | undefined): void
|
(e: 'getInfo', url: string, preset: string | undefined, cli: string | undefined): void
|
||||||
(e: 'clear_form'): void
|
(e: 'clear_form'): void
|
||||||
(e: 'remove_archive', url: string): void
|
(e: 'remove_archive', url: string): void
|
||||||
}>()
|
}>()
|
||||||
|
|
@ -497,27 +484,6 @@ const filter_presets = (flag: boolean = true) => config.presets.filter(item => i
|
||||||
const get_preset = (name: string | undefined) => config.presets.find(item => item.name === name)
|
const get_preset = (name: string | undefined) => config.presets.find(item => item.name === name)
|
||||||
const expand_description = (e: Event) => toggleClass(e.target as HTMLElement, ['is-ellipsis', 'is-pre-wrap'])
|
const expand_description = (e: Event) => toggleClass(e.target as HTMLElement, ['is-ellipsis', 'is-pre-wrap'])
|
||||||
|
|
||||||
const removeFromArchive = async (url: string) => {
|
|
||||||
try {
|
|
||||||
const req = await request(`/api/archive/0`, {
|
|
||||||
credentials: 'include',
|
|
||||||
method: 'DELETE',
|
|
||||||
body: JSON.stringify({ url }),
|
|
||||||
})
|
|
||||||
|
|
||||||
const data = await req.json()
|
|
||||||
|
|
||||||
if (!req.ok) {
|
|
||||||
toast.error(data.error)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
toast.success(data.message ?? `Removed item from archive.`)
|
|
||||||
} catch (e: any) {
|
|
||||||
toast.error(`Error: ${e.message}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const get_output_template = () => {
|
const get_output_template = () => {
|
||||||
if (form.value.preset && !hasFormatInConfig.value) {
|
if (form.value.preset && !hasFormatInConfig.value) {
|
||||||
const preset = config.presets.find(p => p.name === form.value.preset)
|
const preset = config.presets.find(p => p.name === form.value.preset)
|
||||||
|
|
|
||||||
|
|
@ -150,7 +150,7 @@
|
||||||
<template v-if="!config.app.basic_mode">
|
<template v-if="!config.app.basic_mode">
|
||||||
<hr class="dropdown-divider" />
|
<hr class="dropdown-divider" />
|
||||||
|
|
||||||
<NuxtLink class="dropdown-item" @click="emitter('getInfo', item.url, item.preset)">
|
<NuxtLink class="dropdown-item" @click="emitter('getInfo', item.url, item.preset, item.cli)">
|
||||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||||
<span>yt-dlp Information</span>
|
<span>yt-dlp Information</span>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
|
|
@ -275,7 +275,7 @@
|
||||||
<hr class="dropdown-divider" v-if="!config.app.basic_mode" />
|
<hr class="dropdown-divider" v-if="!config.app.basic_mode" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<NuxtLink class="dropdown-item" @click="emitter('getInfo', item.url, item.preset)"
|
<NuxtLink class="dropdown-item" @click="emitter('getInfo', item.url, item.preset, item.cli)"
|
||||||
v-if="!config.app.basic_mode">
|
v-if="!config.app.basic_mode">
|
||||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||||
<span>yt-dlp Information</span>
|
<span>yt-dlp Information</span>
|
||||||
|
|
@ -319,7 +319,7 @@ import { useStorage } from '@vueuse/core'
|
||||||
import type { StoreItem } from '~/types/store'
|
import type { StoreItem } from '~/types/store'
|
||||||
|
|
||||||
const emitter = defineEmits<{
|
const emitter = defineEmits<{
|
||||||
(e: 'getInfo', url: string, preset: string): void
|
(e: 'getInfo', url: string, preset: string, cli: string): void
|
||||||
(e: 'getItemInfo', id: string): void
|
(e: 'getItemInfo', id: string): void
|
||||||
(e: 'clear_search'): void
|
(e: 'clear_search'): void
|
||||||
}>()
|
}>()
|
||||||
|
|
|
||||||
|
|
@ -96,15 +96,16 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<NewDownload v-if="config.showForm || config.app.basic_mode"
|
<NewDownload v-if="config.showForm || config.app.basic_mode"
|
||||||
@getInfo="(url: string, preset: string = '') => view_info(url, false, preset)" :item="item_form"
|
@getInfo="(url: string, preset: string = '', cli: string = '') => view_info(url, false, preset, cli)"
|
||||||
@clear_form="item_form = {}" @remove_archive="" />
|
:item="item_form" @clear_form="item_form = {}" @remove_archive="" />
|
||||||
<Queue @getInfo="(url: string, preset: string = '') => view_info(url, false, preset)" :thumbnails="show_thumbnail"
|
<Queue @getInfo="(url: string, preset: string = '', cli: string = '') => view_info(url, false, preset, cli)"
|
||||||
:query="query" @getItemInfo="(id: string) => view_info(`/api/history/${id}`, true)" @clear_search="query = ''" />
|
:thumbnails="show_thumbnail" :query="query" @getItemInfo="(id: string) => view_info(`/api/history/${id}`, true)"
|
||||||
<History @getInfo="(url: string, preset: string = '') => view_info(url, false, preset)"
|
@clear_search="query = ''" />
|
||||||
|
<History @getInfo="(url: string, preset: string = '', cli: string = '') => view_info(url, false, preset, cli)"
|
||||||
@add_new="(item: Partial<StoreItem>) => toNewDownload(item)" :query="query" :thumbnails="show_thumbnail"
|
@add_new="(item: Partial<StoreItem>) => toNewDownload(item)" :query="query" :thumbnails="show_thumbnail"
|
||||||
@getItemInfo="(id: string) => view_info(`/api/history/${id}`, true)" @clear_search="query = ''" />
|
@getItemInfo="(id: string) => view_info(`/api/history/${id}`, true)" @clear_search="query = ''" />
|
||||||
<GetInfo v-if="info_view.url" :link="info_view.url" :preset="info_view.preset" :useUrl="info_view.useUrl"
|
<GetInfo v-if="info_view.url" :link="info_view.url" :preset="info_view.preset" :cli="info_view.cli"
|
||||||
@closeModel="close_info()" />
|
:useUrl="info_view.useUrl" @closeModel="close_info()" />
|
||||||
<ConfirmDialog v-if="dialog_confirm.visible" :visible="dialog_confirm.visible" :title="dialog_confirm.title"
|
<ConfirmDialog v-if="dialog_confirm.visible" :visible="dialog_confirm.visible" :title="dialog_confirm.title"
|
||||||
:html_message="dialog_confirm.html_message" :options="dialog_confirm.options" @confirm="dialog_confirm.confirm"
|
:html_message="dialog_confirm.html_message" :options="dialog_confirm.options" @confirm="dialog_confirm.confirm"
|
||||||
@cancel="() => dialog_confirm.visible = false" />
|
@cancel="() => dialog_confirm.visible = false" />
|
||||||
|
|
@ -128,8 +129,9 @@ const show_thumbnail = useStorage<boolean>('show_thumbnail', true)
|
||||||
const info_view = ref({
|
const info_view = ref({
|
||||||
url: '',
|
url: '',
|
||||||
preset: '',
|
preset: '',
|
||||||
|
cli: '',
|
||||||
useUrl: false,
|
useUrl: false,
|
||||||
}) as Ref<{ url: string, preset: string, useUrl: boolean }>
|
}) as Ref<{ url: string, preset: string, cli: string, useUrl: boolean }>
|
||||||
const item_form = ref<item_request | object>({})
|
const item_form = ref<item_request | object>({})
|
||||||
const query = ref()
|
const query = ref()
|
||||||
const toggleFilter = ref(false)
|
const toggleFilter = ref(false)
|
||||||
|
|
@ -198,10 +200,11 @@ const close_info = () => {
|
||||||
info_view.value.useUrl = false
|
info_view.value.useUrl = false
|
||||||
}
|
}
|
||||||
|
|
||||||
const view_info = (url: string, useUrl: boolean = false, preset: string = '') => {
|
const view_info = (url: string, useUrl: boolean = false, preset: string = '', cli: string = '') => {
|
||||||
info_view.value.url = url
|
info_view.value.url = url
|
||||||
info_view.value.useUrl = useUrl
|
info_view.value.useUrl = useUrl
|
||||||
info_view.value.preset = preset
|
info_view.value.preset = preset
|
||||||
|
info_view.value.cli = cli
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(() => info_view.value.url, v => {
|
watch(() => info_view.value.url, v => {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue