improvements to the sleep timer
🏗️ Build and Test JellyTau / Run Tests (push) Failing after 14s
Traceability Validation / Check Requirement Traces (push) Failing after 2s
🏗️ Build and Test JellyTau / Build Android APK (push) Has been skipped

This commit is contained in:
2026-02-28 20:33:22 +01:00
parent e8e37649fa
commit c5be9eb18c
19 changed files with 475 additions and 57 deletions
+33 -1
View File
@@ -6,7 +6,10 @@
import type { MediaItem } from "$lib/api/types";
import { auth } from "$lib/stores/auth";
import VolumeControl from "./VolumeControl.svelte";
import SleepTimerModal from "./SleepTimerModal.svelte";
import SleepTimerIndicator from "./SleepTimerIndicator.svelte";
import CachedImage from "../common/CachedImage.svelte";
import { sleepTimerActive } from "$lib/stores/sleepTimer";
interface Props {
media: MediaItem | null;
@@ -20,15 +23,18 @@
onReportStart?: (positionSeconds: number) => void;
onReportStop?: (positionSeconds: number) => void;
onEnded?: () => void; // Called when video playback ends naturally
onNext?: () => void; // Called when user clicks next episode button
hasNext?: boolean; // Whether there is a next episode available
}
let { media, streamUrl, mediaSourceId, initialPosition, needsTranscoding = false, onClose, onSeek, onReportProgress, onReportStart, onReportStop, onEnded }: Props = $props();
let { media, streamUrl, mediaSourceId, initialPosition, needsTranscoding = false, onClose, onSeek, onReportProgress, onReportStart, onReportStop, onEnded, onNext, hasNext = false }: Props = $props();
let videoElement: HTMLVideoElement | null = $state(null);
let isPlaying = $state(false);
let currentTime = $state(0);
let isFullscreen = $state(false);
let showControls = $state(true);
let showSleepTimerModal = $state(false);
let isBuffering = $state(false);
let controlsTimeout: ReturnType<typeof setTimeout> | null = null;
let seekOffset = $state(0); // Track offset when seeking in transcoded streams
@@ -1408,6 +1414,15 @@
</svg>
{/if}
</button>
<!-- Next Episode -->
{#if hasNext}
<button onclick={onNext} class="text-white hover:text-gray-300" aria-label="Next episode">
<svg class="w-7 h-7" fill="currentColor" viewBox="0 0 24 24">
<path d="M6 18l8.5-6L6 6v12zM16 6v12h2V6h-2z" />
</svg>
</button>
{/if}
</div>
<div class="flex items-center gap-4">
@@ -1520,6 +1535,21 @@
</div>
{/if}
<!-- Sleep Timer -->
{#if $sleepTimerActive}
<SleepTimerIndicator onClick={() => { showSleepTimerModal = true; }} />
{:else}
<button
onclick={() => { showSleepTimerModal = true; }}
class="text-white hover:text-gray-300"
aria-label="Sleep timer"
>
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
<path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67z" />
</svg>
</button>
{/if}
<!-- Volume Control -->
<VolumeControl size="md" />
@@ -1545,6 +1575,8 @@
</div>
</div>
<SleepTimerModal isOpen={showSleepTimerModal} onClose={() => { showSleepTimerModal = false; }} mediaType={media?.type} />
<style>
@keyframes fade-out {
0% {