minor ui updates/

This commit is contained in:
arabcoders 2025-08-19 19:27:45 +03:00
parent 53b7474069
commit 444e1458be
4 changed files with 84 additions and 22 deletions

View file

@ -144,7 +144,7 @@ async def get_info(request: Request, cache: Cache, config: Config) -> Response:
"ttl_left": data.get("_cached", {}).get("expires", time.time() + 300) - time.time(),
"expires": data.get("_cached", {}).get("expires", time.time() + 300),
}
return web.Response(body=json.dumps(data, indent=4, default=str), status=web.HTTPOk.status_code)
return web.json_response(body=json.dumps(data, indent=4, default=str), status=web.HTTPOk.status_code)
if ytdlp_proxy := config.get_ytdlp_args().get("proxy", None):
opts = opts.add({"proxy": ytdlp_proxy})
@ -203,7 +203,7 @@ async def get_info(request: Request, cache: Cache, config: Config) -> Response:
cache.set(key=key, value=data, ttl=300)
return web.Response(body=json.dumps(data, indent=4, default=str), status=web.HTTPOk.status_code)
return web.json_response(body=json.dumps(data, indent=4, default=str), status=web.HTTPOk.status_code)
except Exception as e:
LOG.exception(e)
LOG.error(f"Error encountered while getting video info for '{url}'. '{e!s}'.")

View file

@ -0,0 +1,61 @@
import { ref, onMounted, onBeforeUnmount, shallowReadonly, type Ref } from 'vue'
export interface MediaQueryOptions {
/**
* A custom media query string.
* Example: "(max-width: 640px) or (hover: none)"
* If provided, takes precedence over maxWidth.
*/
query?: string
/**
* Max width in px considered true.
* Ignored if `query` is provided. Default: 768.
*/
maxWidth?: number
}
/**
* Reactive state of a CSS media query.
*/
export function useMediaQuery(options: MediaQueryOptions = {}): Readonly<Ref<boolean>> {
const query = options.query ?? `(max-width: ${options.maxWidth ?? 1024}px)`
const matches = ref(false)
let mql: MediaQueryList | null = null
let onChange: ((ev: MediaQueryListEvent) => void) | null = null
const setup = () => {
if ('undefined' === typeof window || !window.matchMedia) {
return
}
mql = window.matchMedia(query)
matches.value = mql.matches
onChange = ev => { matches.value = ev.matches }
if ('addEventListener' in mql) {
mql.addEventListener('change', onChange as EventListener)
return
}
// @ts-expect-error legacy Safari
mql.addListener(onChange)
}
const teardown = () => {
if (!mql || !onChange) return
if ('removeEventListener' in mql) {
mql.removeEventListener('change', onChange as EventListener)
} else {
// @ts-expect-error legacy Safari
mql.removeListener(onChange)
}
mql = null
onChange = null
}
onMounted(setup)
onBeforeUnmount(teardown)
return shallowReadonly(matches)
}

View file

@ -75,27 +75,22 @@
</div>
</div>
<div class="navbar-item is-hidden-mobile" v-if="false === config.app.is_native">
<div class="navbar-item" v-if="false === config.app.is_native">
<button class="button is-dark" @click="reloadPage">
<span class="icon"><i class="fas fa-refresh" /></span>
</button>
</div>
<div class="navbar-item is-hidden-tablet">
<button class="button is-dark" @click="reloadPage">
<span class="icon"><i class="fas fa-refresh" /></span>
<span>Reload</span>
<span v-if="isMobile">Reload</span>
</button>
</div>
<NotifyDropdown />
<div class="navbar-item is-hidden-mobile">
<div class="navbar-item" v-if="!isMobile">
<button class="button is-dark has-tooltip-bottom mr-4" v-tooltip.bottom="'WebUI Settings'"
@click="show_settings = !show_settings">
<span class="icon"><i class="fas fa-cog" /></span>
</button>
</div>
<div class="navbar-item is-hidden-tablet">
<div class="navbar-item" v-if="isMobile">
<button class="button is-dark" @click="show_settings = !show_settings">
<span class="icon"><i class="fas fa-cog" /></span>
<span>WebUI Settings</span>
@ -116,11 +111,11 @@
<div class="column is-8-mobile">
<div class="has-text-left" v-if="config.app?.app_version">
© {{ Year }} - <NuxtLink href="https://github.com/ArabCoders/ytptube" target="_blank">YTPTube</NuxtLink>
<span class="is-hidden-mobile has-tooltip"
<span class="has-tooltip" v-if="!isMobile"
v-tooltip="`Build Date: ${config.app?.app_build_date}, Branch: ${config.app?.app_branch}, commit: ${config.app?.app_commit_sha}`">
&nbsp;({{ config?.app?.app_version || 'unknown' }})</span>
- <NuxtLink target="_blank" href="https://github.com/yt-dlp/yt-dlp">yt-dlp</NuxtLink>
<span class="is-hidden-mobile">&nbsp;({{ config?.app?.ytdlp_version || 'unknown' }})</span>
<span v-if="!isMobile">&nbsp;({{ config?.app?.ytdlp_version || 'unknown' }})</span>
- <NuxtLink to="/changelog">CHANGELOG</NuxtLink>
</div>
</div>
@ -137,6 +132,7 @@
</template>
<script setup lang="ts">
import { ref, onMounted, watch, readonly } from 'vue'
import 'assets/css/bulma.css'
import 'assets/css/style.css'
import 'assets/css/all.css'
@ -154,6 +150,7 @@ const loadingImage = ref(false)
const bg_enable = useStorage('random_bg', true)
const bg_opacity = useStorage('random_bg_opacity', 0.95)
const showMenu = ref(false)
const isMobile = useMediaQuery({ maxWidth: 1024 })
const applyPreferredColorScheme = (scheme: string) => {
if (!scheme || scheme === 'auto') {

View file

@ -20,26 +20,26 @@
<p class="control">
<button class="button is-danger is-light" @click="toggleFilter = !toggleFilter">
<span class="icon"><i class="fas fa-filter" /></span>
<span class="is-hidden-mobile">Filter</span>
<span v-if="!isMobile">Filter</span>
</button>
</p>
<p class="control" v-if="!config.app.basic_mode && false === config.app.basic_mode">
<button class="button is-warning" @click="pauseDownload" v-if="false === config.paused">
<span class="icon"><i class="fas fa-pause" /></span>
<span class="is-hidden-mobile">Pause</span>
<span v-if="!isMobile">Pause</span>
</button>
<button class="button is-danger" @click="socket.emit('resume', {})" v-else
v-tooltip.bottom="'Resume downloading.'">
<span class="icon"><i class="fas fa-play" /></span>
<span class="is-hidden-mobile">Resume</span>
<span v-if="!isMobile">Resume</span>
</button>
</p>
<p class="control" v-if="!config.app.basic_mode && false === config.app.basic_mode">
<button class="button is-primary has-tooltip-bottom" @click="config.showForm = !config.showForm">
<span class="icon"><i class="fa-solid fa-plus" /></span>
<span class="is-hidden-mobile">New Download</span>
<span v-if="!isMobile">New Download</span>
</button>
</p>
@ -48,13 +48,14 @@
@click="() => changeDisplay()">
<span class="icon"><i class="fa-solid"
:class="{ 'fa-table': display_style === 'cards', 'fa-table-list': display_style === 'list' }" /></span>
<span class="is-hidden-mobile">{{ display_style === 'cards' ? 'Cards' : 'List' }}</span>
<span v-if="!isMobile">
{{ display_style === 'cards' ? 'Cards' : 'List' }}
</span>
</button>
</p>
</div>
</div>
<div class="is-hidden-mobile">
<div v-if="!isMobile">
<span class="subtitle">
Queued and completed downloads are displayed here.
</span>
@ -68,7 +69,7 @@
<Queue @getInfo="(url: string, preset: string = '') => view_info(url, false, preset)" :thumbnails="show_thumbnail"
:query="query" @getItemInfo="(id: string) => view_info(`/api/history/${id}`, true)" @clear_search="query = ''" />
<History @getInfo="(url: string, preset: string = '') => view_info(url, false, preset)"
@add_new="(item: item_request) => 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 = ''" />
<GetInfo v-if="info_view.url" :link="info_view.url" :preset="info_view.preset" :useUrl="info_view.useUrl"
@closeModel="close_info()" />
@ -81,6 +82,7 @@
<script setup lang="ts">
import { useStorage } from '@vueuse/core'
import type { item_request } from '~/types/item'
import type { StoreItem } from '~/types/store'
const config = useConfigStore()
const stateStore = useStateStore()
@ -106,6 +108,8 @@ const dialog_confirm = ref({
options: [],
})
const isMobile = useMediaQuery({ maxWidth: 1024 })
watch(toggleFilter, () => {
if (!toggleFilter.value) {
query.value = ''
@ -170,7 +174,7 @@ watch(() => info_view.value.url, v => {
const changeDisplay = () => display_style.value = display_style.value === 'cards' ? 'list' : 'cards'
const toNewDownload = async (item: item_request) => {
const toNewDownload = async (item: item_request | Partial<StoreItem>) => {
if (!item) {
return
}