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;
- }
- }