Merge pull request #308 from arabcoders/dev
Make it possible to view local item info
This commit is contained in:
commit
3f3f63f0e2
5 changed files with 108 additions and 16 deletions
29
API.md
29
API.md
|
|
@ -23,6 +23,7 @@ This document describes the available endpoints and their usage. All endpoints r
|
|||
- [POST /api/history](#post-apihistory)
|
||||
- [DELETE /api/history](#delete-apihistory)
|
||||
- [POST /api/history/{id}](#post-apihistoryid)
|
||||
- [GET /api/history/{id}](#get-apihistoryid)
|
||||
- [GET /api/history](#get-apihistory)
|
||||
- [GET /api/tasks](#get-apitasks)
|
||||
- [PUT /api/tasks](#put-apitasks)
|
||||
|
|
@ -296,6 +297,34 @@ or an error:
|
|||
|
||||
---
|
||||
|
||||
### GET /api/history/{id}
|
||||
**Purpose**: View details of a specific item in the database.
|
||||
|
||||
**Path Parameter**:
|
||||
- `id` = Unique item ID.
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"_id": "<uuid>",
|
||||
"title": "Video Title",
|
||||
"url": "https://youtube.com/watch?v=...",
|
||||
....
|
||||
}
|
||||
```
|
||||
or an error:
|
||||
```json
|
||||
{
|
||||
"error": "text"
|
||||
}
|
||||
```
|
||||
|
||||
- `200 OK` If the item exists and is returned.
|
||||
- `404 Not Found` if the item doesn’t exist.
|
||||
- `400 Bad Request` if id is missing.
|
||||
|
||||
---
|
||||
|
||||
### GET /api/history
|
||||
**Purpose**: Returns the download queue and the download history.
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ from dataclasses import dataclass, field
|
|||
from email.utils import formatdate
|
||||
from typing import Any
|
||||
|
||||
from app.library.Utils import clean_item
|
||||
|
||||
LOG = logging.getLogger("ItemDTO")
|
||||
|
||||
|
||||
|
|
@ -224,7 +226,8 @@ class ItemDTO:
|
|||
eta: str | None = None
|
||||
|
||||
def serialize(self) -> dict:
|
||||
return self.__dict__.copy()
|
||||
item, _ = clean_item(self.__dict__.copy(), ItemDTO.removed_fields())
|
||||
return item
|
||||
|
||||
def json(self) -> str:
|
||||
return json.dumps(self.serialize(), default=lambda o: o.__dict__, sort_keys=True, indent=4)
|
||||
|
|
|
|||
|
|
@ -69,6 +69,32 @@ async def item_delete(request: Request, queue: DownloadQueue, encoder: Encoder)
|
|||
)
|
||||
|
||||
|
||||
@route("GET", "api/history/{id}", "item_view")
|
||||
async def item_view(request: Request, queue: DownloadQueue, encoder: Encoder) -> Response:
|
||||
"""
|
||||
Update an item in the history.
|
||||
|
||||
Args:
|
||||
request (Request): The request object.
|
||||
queue (DownloadQueue): The download queue instance.
|
||||
encoder (Encoder): The encoder instance.
|
||||
notify (EventBus): The event bus instance.
|
||||
|
||||
Returns:
|
||||
Response: The response object.
|
||||
|
||||
"""
|
||||
id: str = request.match_info.get("id")
|
||||
if not id:
|
||||
return web.json_response(data={"error": "id is required."}, status=web.HTTPBadRequest.status_code)
|
||||
|
||||
item: Download | None = queue.done.get_by_id(id) or queue.queue.get_by_id(id)
|
||||
if not item:
|
||||
return web.json_response(data={"error": "item not found."}, status=web.HTTPNotFound.status_code)
|
||||
|
||||
return web.json_response(data=item.info, status=web.HTTPOk.status_code, dumps=encoder.encode)
|
||||
|
||||
|
||||
@route("POST", "api/history/{id}", "item_update")
|
||||
async def item_update(request: Request, queue: DownloadQueue, encoder: Encoder, notify: EventBus) -> Response:
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -190,7 +190,12 @@
|
|||
</template>
|
||||
<NuxtLink class="dropdown-item" @click="emitter('getInfo', item.url)">
|
||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||
<span>Information</span>
|
||||
<span>yt-dlp Information</span>
|
||||
</NuxtLink>
|
||||
|
||||
<NuxtLink class="dropdown-item" @click="emitter('getItemInfo', item._id)">
|
||||
<span class="icon"><i class="fa-solid fa-info-circle" /></span>
|
||||
<span>Local Information</span>
|
||||
</NuxtLink>
|
||||
|
||||
<template v-if="item.status != 'finished' || !item.filename">
|
||||
|
|
@ -311,7 +316,8 @@
|
|||
</div>
|
||||
<div class="column is-half-mobile has-text-centered is-text-overflow is-unselectable"
|
||||
v-if="item.file_size">
|
||||
{{ formatBytes(item.file_size) }}
|
||||
<span class="has-tooltip" v-tooltip="`Path: ${makePath(item)}`">{{
|
||||
formatBytes(item.file_size) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="columns is-mobile is-multiline">
|
||||
|
|
@ -361,7 +367,12 @@
|
|||
|
||||
<NuxtLink class="dropdown-item" @click="emitter('getInfo', item.url)">
|
||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||
<span>Information</span>
|
||||
<span>yt-dlp Information</span>
|
||||
</NuxtLink>
|
||||
|
||||
<NuxtLink class="dropdown-item" @click="emitter('getItemInfo', item._id)">
|
||||
<span class="icon"><i class="fa-solid fa-info-circle" /></span>
|
||||
<span>Local Information</span>
|
||||
</NuxtLink>
|
||||
|
||||
<template v-if="item.status != 'finished' || !item.filename">
|
||||
|
|
@ -435,7 +446,7 @@ import { getEmbedable, isEmbedable } from '~/utils/embedable'
|
|||
import { formatBytes, makeDownload, uri } from '~/utils/index'
|
||||
import Dropdown from './Dropdown.vue'
|
||||
|
||||
const emitter = defineEmits(['getInfo', 'add_new'])
|
||||
const emitter = defineEmits(['getInfo', 'add_new', 'getItemInfo'])
|
||||
|
||||
const props = defineProps({
|
||||
thumbnails: {
|
||||
|
|
@ -862,4 +873,14 @@ const is_queued = item => {
|
|||
|
||||
return item.live_in || item.extras?.live_in || item.extras?.release_in ? 'fa-spin fa-spin-10' : ''
|
||||
}
|
||||
|
||||
const makePath = item => {
|
||||
if (!item?.filename) {
|
||||
return ''
|
||||
}
|
||||
|
||||
const real_path = eTrim(item.download_dir, '/') + '/' + sTrim(item.filename, '/')
|
||||
|
||||
return real_path.replace(config.app.download_path, '').replace(/^\//, '')
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -49,13 +49,15 @@
|
|||
|
||||
<NewDownload v-if="config.showForm || config.app.basic_mode" @getInfo="url => get_info = url" :item="item_form"
|
||||
@clear_form="item_form = {}" @remove_archive="" />
|
||||
<Queue @getInfo="url => get_info = url" :thumbnails="show_thumbnail" />
|
||||
<History @getInfo="url => get_info = url" @add_new="item => toNewDownload(item)" :thumbnails="show_thumbnail" />
|
||||
<GetInfo v-if="get_info" :link="get_info" @closeModel="get_info = ''" />
|
||||
<Queue @getInfo="(url: string) => view_info(url, false)" :thumbnails="show_thumbnail"
|
||||
@getItemInfo="(id: string) => view_info(`/api/history/${id}`, true)" />
|
||||
<History @getInfo="(url: string) => view_info(url, false)" @add_new="item => toNewDownload(item)"
|
||||
:thumbnails="show_thumbnail" @getItemInfo="(id: string) => view_info(`/api/history/${id}`, true)" />
|
||||
<GetInfo v-if="get_info" :link="get_info" :useUrl="get_info_use_url" @closeModel="close_info()" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
import { useStorage } from '@vueuse/core'
|
||||
|
||||
const config = useConfigStore()
|
||||
|
|
@ -63,11 +65,12 @@ const stateStore = useStateStore()
|
|||
const socket = useSocketStore()
|
||||
const box = useConfirm()
|
||||
|
||||
const get_info = ref('')
|
||||
const bg_enable = useStorage('random_bg', true)
|
||||
const bg_opacity = useStorage('random_bg_opacity', 0.85)
|
||||
const display_style = useStorage('display_style', 'cards')
|
||||
const show_thumbnail = useStorage('show_thumbnail', true)
|
||||
const get_info = ref<string>('')
|
||||
const get_info_use_url = ref<boolean>(false)
|
||||
const bg_enable = useStorage<boolean>('random_bg', true)
|
||||
const bg_opacity = useStorage<number>('random_bg_opacity', 0.85)
|
||||
const display_style = useStorage<string>('display_style', 'cards')
|
||||
const show_thumbnail = useStorage<boolean>('show_thumbnail', true)
|
||||
|
||||
const item_form = ref({})
|
||||
|
||||
|
|
@ -101,17 +104,27 @@ const pauseDownload = () => {
|
|||
socket.emit('pause', {})
|
||||
}
|
||||
|
||||
const close_info = () => {
|
||||
get_info.value = ''
|
||||
get_info_use_url.value = false
|
||||
}
|
||||
|
||||
const view_info = (url: string, useUrl: boolean = false) => {
|
||||
get_info.value = url
|
||||
get_info_use_url.value = useUrl
|
||||
}
|
||||
|
||||
watch(get_info, v => {
|
||||
if (!bg_enable.value) {
|
||||
return
|
||||
}
|
||||
|
||||
document.querySelector('body').setAttribute("style", `opacity: ${v ? 1 : bg_opacity.value}`)
|
||||
document.querySelector('body')?.setAttribute("style", `opacity: ${v ? 1 : bg_opacity.value}`)
|
||||
})
|
||||
|
||||
const changeDisplay = () => display_style.value = display_style.value === 'cards' ? 'list' : 'cards'
|
||||
|
||||
const toNewDownload = async (item) => {
|
||||
const toNewDownload = async (item: any) => {
|
||||
if (!item) {
|
||||
return
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue