diff --git a/README.md b/README.md index ef6c615..8ad8ed9 100644 --- a/README.md +++ b/README.md @@ -110,6 +110,7 @@ It is meant to feel look, work and perform like a premium streaming service, but * Support for most popular BEP's. * Persist torrents, cache progress, and rescan instantly. * View detailed torrent and peer info. +* Batch downloads.

diff --git a/package.json b/package.json index 9065318..40e0ce9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ui", - "version": "6.4.118", + "version": "6.4.119", "license": "BUSL-1.1", "private": true, "packageManager": "pnpm@9.15.5", diff --git a/src/lib/components/ui/player/animations.svelte b/src/lib/components/ui/player/animations.svelte new file mode 100644 index 0000000..f0cf3dc --- /dev/null +++ b/src/lib/components/ui/player/animations.svelte @@ -0,0 +1,71 @@ + + +{#if !$settings.minimalPlayerUI} + {#each $animations as { type, id } (id)} +

endAnimation(id)}> + {#if type === 'play'} + + {:else if type === 'pause'} + + {:else if type === 'seekforw'} + + {:else if type === 'seekback'} + + {:else if type === 'volumeup'} + + {:else if type === 'volumedown'} + + {:else} +
{type}
+ {/if} +
+ {/each} +{/if} + + diff --git a/src/lib/components/ui/player/player.svelte b/src/lib/components/ui/player/player.svelte index 37f087f..019a5ca 100644 --- a/src/lib/components/ui/player/player.svelte +++ b/src/lib/components/ui/player/player.svelte @@ -28,6 +28,7 @@ import { toast } from 'svelte-sonner' import VideoDeband from 'video-deband' + import Animations, { playAnimation } from './animations.svelte' import { condition, loadWithDefaults } from './keybinds.svelte' import Options from './options.svelte' import PictureInPicture from './pip' @@ -178,10 +179,15 @@ }) } } + function changeVolume (delta: number) { + playAnimation(delta > 0 ? 'volumeup' : 'volumedown') + $volume = Math.min(1, Math.max(0, $volume + delta)) + } function selectAudio (id: string) { if (id) { for (const track of video.audioTracks ?? []) { track.enabled = track.id === id + playAnimation(track.label) } seek(-0.2) // stupid fix because video freezes up when chaging tracks } @@ -190,6 +196,7 @@ if (id) { for (const track of video.videoTracks ?? []) { track.selected = track.id === id + playAnimation(track.label) } } } @@ -200,8 +207,8 @@ playAnimation(time > 0 ? 'seekforw' : 'seekback') } function seekTo (time: number) { - playAnimation(time > currentTime ? 'seekforw' : 'seekback') video.currentTime = currentTime = time + playAnimation(time > currentTime ? 'seekforw' : 'seekback') } let wasPaused = false function startSeek () { @@ -235,25 +242,6 @@ }) } - // animations - - function playAnimation (type: 'play' | 'pause' | 'seekforw' | 'seekback') { - animations.push({ type, id: crypto.randomUUID() }) - // eslint-disable-next-line no-self-assign - animations = animations - } - function endAnimation (id: string) { - const index = animations.findIndex(animation => animation.id === id) - if (index !== -1) animations.splice(index, 1) - // eslint-disable-next-line no-self-assign - animations = animations - } - interface Animation { - type: 'play' | 'pause' | 'seekforw' | 'seekback' - id: string - } - let animations: Animation[] = [] - let chapters: Chapter[] = [] const chaptersPromise = native.chapters(mediaInfo.file.hash, mediaInfo.file.id) async function loadChapters (pr: typeof chaptersPromise, safeduration: number) { @@ -364,8 +352,8 @@ $: if (currentSkippable && $settings.playerSkip) skip() const skippableChaptersRx: Array<[string, RegExp]> = [ - ['Opening', /^op$|opening$|^ncop/mi], - ['Ending', /^ed$|ending$|^nced/mi], + ['Opening', /^op$|opening$|^ncop|^opening /mi], + ['Ending', /^ed$|ending$|^nced|^ending /mi], ['Recap', /recap/mi] ] function isChapterSkippable (chapter: Chapter) { @@ -478,9 +466,11 @@ function cycleSubtitles () { if (!subtitles) return const entries = Object.entries(subtitles._tracks.value) - const index = entries.findIndex(([index]) => index === subtitles!.current.value) - const nextIndex = (index + 1) - subtitles.selectCaptions((index + 1) >= entries.length ? -1 : entries[nextIndex]![0]) + if (!entries.length) return + const index = entries.findIndex(([index]) => index === subtitles!.current.value) + 1 + const [id, info] = entries[index] ?? [-1, { meta: { name: 'Off', language: 'Eng' } }] + playAnimation(info.meta.name ?? info.meta.language ?? 'Eng') + subtitles.selectCaptions(id) } function seekBarKey (event: KeyboardEvent) { @@ -632,7 +622,7 @@ e.preventDefault() e.stopImmediatePropagation() e.stopPropagation() - $volume = Math.min(1, $volume + 0.05) + changeVolume(0.05) }, id: 'volume_up', icon: Volume2, @@ -645,7 +635,7 @@ e.preventDefault() e.stopImmediatePropagation() e.stopPropagation() - $volume = Math.max(0, $volume - 0.05) + changeVolume(-0.05) }, id: 'volume_down', icon: Volume1, @@ -779,11 +769,20 @@ stopProgressBar() } }) + + function handleWheel ({ shiftKey, deltaY }: WheelEvent) { + const sign = Math.sign(deltaY) + if (shiftKey) { + seek(Number($settings.playerSeek) * sign * -1) + } else { + changeVolume(-0.05 * sign) + } + } -
resetMove(2000)}> +
resetMove(2000)} on:wheel={handleWheel}>
{/if} - {#if !$settings.minimalPlayerUI} - {#each animations as { type, id } (id)} -
endAnimation(id)}> - {#if type === 'play'} - - {:else if type === 'pause'} - - {:else if type === 'seekforw'} - - {:else if type === 'seekback'} - - {/if} -
- {/each} - {/if} +
@@ -1029,19 +1014,4 @@ .gradient-to-bottom { background: linear-gradient(to bottom, oklab(0 0 0 / 0.85) 0%, oklab(0 0 0 / 0.7) 35%, oklab(0 0 0 / 0) 100%); } - - .animate-pulse-once { - animation: pulse-once .4s linear; - } - - @keyframes pulse-once { - 0% { - opacity: 1; - scale: 1; - } - 100% { - opacity: 0; - scale: 1.2; - } - }