feat: subtitle delay keybinds and info, keybind descriptions

This commit is contained in:
ThaUnknown 2025-06-06 21:56:25 +02:00
parent 14720a07c6
commit bb9f7a79d0
No known key found for this signature in database
6 changed files with 63 additions and 21 deletions

View file

@ -1,6 +1,6 @@
{
"name": "ui",
"version": "6.3.45",
"version": "6.3.46",
"license": "BUSL-1.1",
"private": true,
"packageManager": "pnpm@9.14.4",

View file

@ -69,6 +69,8 @@
<script lang='ts'>
export let clickable = false
export let pointerOver: (bind: Bind | undefined) => void
let dragged: HTMLDivElement | null = null
function draggable (node: HTMLDivElement, code: KeyCode) {
const ctrl = new AbortController()
@ -113,7 +115,7 @@
}
</script>
<div class='svelte-keybinds'>
<div class='svelte-keybinds' on:pointerleave>
{#each Object.values(keys) as key (key.name)}
{@const { size, dark, name } = key}
<div
@ -123,6 +125,7 @@
class='w-{size ?? 50}'
{...$$restProps}
use:draggable={name}
on:pointerover={() => pointerOver($binds[name])}
on:click={(e) => clickable && runBind(e, name)}>
<slot prop={$binds[name]} />
</div>

View file

@ -70,6 +70,8 @@
let fullscreenElement: HTMLElement | null = null
export let id = ''
let keybindDesc: unknown = null
</script>
<Dialog.Root portal={wrapper} bind:open>
@ -82,17 +84,17 @@
<div on:pointerdown|self={close} class='size-full flex justify-center items-center flex-col overflow-y-scroll text-[6px] lg:text-xs' use:dragScroll>
{#if showKeybinds}
<div class='bg-black py-3 px-4 rounded-md text-sm lg:text-lg font-bold mb-4'>
Drag and drop binds to change them
{keybindDesc ?? 'Drag and drop binds to change them'}
</div>
<Keybinds let:prop={item} autosave={true} clickable={true}>
<Keybinds let:prop={item} autosave={true} clickable={true} on:pointerleave={() => { keybindDesc = null }} pointerOver={item => { keybindDesc = item?.desc }}>
{#if item?.type}
<div class='size-full flex justify-center p-1.5 lg:p-3' title={item.desc}>
<div class='size-full flex justify-center p-1.5 lg:p-3'>
{#if item.icon}
<svelte:component this={item.icon} size='2rem' class='h-full' fill={item.id === 'play_arrow' ? 'currentColor' : 'none'} />
{/if}
</div>
{:else}
<div class='size-full content-center text-center lg:text-lg' title={item?.desc}>{item?.id ?? ''}</div>
<div class='size-full content-center text-center lg:text-lg'>{item?.id ?? ''}</div>
{/if}
</Keybinds>
{:else}

View file

@ -4,6 +4,8 @@
import ChevronDown from 'lucide-svelte/icons/chevron-down'
import ChevronUp from 'lucide-svelte/icons/chevron-up'
import Contrast from 'lucide-svelte/icons/contrast'
import DecimalsArrowLeft from 'lucide-svelte/icons/decimals-arrow-left'
import DecimalsArrowRight from 'lucide-svelte/icons/decimals-arrow-right'
import FastForward from 'lucide-svelte/icons/fast-forward'
import List from 'lucide-svelte/icons/list'
import Maximize from 'lucide-svelte/icons/maximize'
@ -622,6 +624,20 @@
id: 'schedule',
type: 'icon',
desc: 'Reset Playback Rate'
},
Semicolon: {
fn: () => { subtitleDelay -= 0.1 },
icon: DecimalsArrowLeft,
type: 'icon',
id: 'subtitle_delay_minus',
desc: 'Decrease Subtitle Delay'
},
Quote: {
fn: () => { subtitleDelay += 0.1 },
icon: DecimalsArrowRight,
type: 'icon',
id: 'subtitle_delay_plus',
desc: 'Increase Subtitle Delay'
}
})
@ -736,6 +752,7 @@
Resolution: {stats.resolution}<br />
Buffer health: {stats.buffer}<br />
Playback speed: x{stats.speed?.toFixed(1)}<br />
Subtitle delay: {subtitleDelay} sec
</div>
{/if}
<Options {wrapper} bind:openSubs {video} {seekTo} {selectAudio} {selectVideo} {fullscreen} {chapters} {subtitles} {videoFiles} {selectFile} {pip} bind:playbackRate bind:subtitleDelay

View file

@ -7,9 +7,16 @@ export interface OAuth {
token_type: string
}
export interface KitsuError {
export type KitsuError = {
error: string
error_description: string
} | {
errors: Array<{
code: string
status: string
title: string
detail: string
}>
}
export interface Resource<T> {

View file

@ -118,9 +118,6 @@ export default new class KitsuSync {
body: body ? JSON.stringify(body) : undefined
})
if (!res.ok) {
throw new Error(`Kitsu API Error: ${res.status} ${res.statusText}`)
}
if (method === 'DELETE') return undefined as T
const json = await res.json() as object | KitsuError
@ -128,6 +125,11 @@ export default new class KitsuSync {
if ('error' in json) {
toast.error('Kitsu Error', { description: json.error_description })
console.error(json)
} else if ('errors' in json) {
for (const error of json.errors) {
toast.error('Kitsu Error', { description: error.detail })
console.error(error)
}
}
return json as T | KitsuError
@ -172,10 +174,10 @@ export default new class KitsuSync {
refresh_token: this.auth.value?.refresh_token
}
)
if ('error' in data) {
this.auth.value = undefined
} else {
if ('access_token' in data) {
this.auth.value = data
} else {
this.auth.value = undefined
}
}
@ -190,10 +192,10 @@ export default new class KitsuSync {
}
)
if ('error' in data) {
this.auth.value = undefined
} else {
if ('access_token' in data) {
this.auth.value = data
} else {
this.auth.value = undefined
}
}
@ -216,7 +218,7 @@ export default new class KitsuSync {
}
)
if ('error' in res || !res.data[0]) return
if ('error' in res || 'errors' in res || !res.data[0]) return
this._entriesToML(res)
@ -323,7 +325,7 @@ export default new class KitsuSync {
}
)
if ('error' in data) return
if (!('data' in data)) return
this.favorites.value[kitsuAnimeId] = data.data.id
}
@ -343,7 +345,7 @@ export default new class KitsuSync {
}
)
if ('error' in data) return
if (!('data' in data)) return
this.userlist.value[alId] = this._kitsuEntryToAl(data.data)
}
@ -356,11 +358,12 @@ export default new class KitsuSync {
}
)
if ('error' in data) return
if (!('data' in data)) return
this.userlist.value[alId] = this._kitsuEntryToAl(data.data)
}
// TODO: use kitsu's own API for this instead?
async _getKitsuId (alId: number) {
const kitsuId = this.ALToKitsu[alId.toString()]
if (kitsuId) return kitsuId
@ -431,7 +434,17 @@ export default new class KitsuSync {
}
following (id: number) {
// TODO
return null
// TODO: this doesnt work
// this._get<Res<KEntry, Anime | Mapping>>(
// ENDPOINTS.API_USER_LIBRARY,
// {
// 'filter[following]': true,
// 'filter[user_id]': this.viewer.value?.id,
// 'filter[animeId]': 42765,
// include: 'anime.mappings,user'
// }
// )
}
async entry (variables: VariablesOf<typeof Entry>) {