improvements to the sleep timer
This commit is contained in:
@@ -158,7 +158,7 @@
|
||||
</div>
|
||||
|
||||
<!-- User menu -->
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="ml-auto flex items-center gap-3">
|
||||
<span class="text-sm text-gray-400 hidden md:inline">{$currentUser?.name}</span>
|
||||
|
||||
<!-- Desktop: Downloads icon -->
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
let error = $state<string | null>(null);
|
||||
let showResumeDialog = $state(false);
|
||||
let savedProgress = $state<{ positionSeconds: number; progressPercent: number } | null>(null);
|
||||
let nextEpisode = $state<MediaItem | null>(null); // Next episode for video skip button
|
||||
|
||||
let pollInterval: ReturnType<typeof setInterval> | null = null;
|
||||
let loadedItemId: string | null = null;
|
||||
@@ -408,6 +409,11 @@
|
||||
|
||||
isPlaying = true;
|
||||
loading = false;
|
||||
|
||||
// Fetch next episode for video episodes (for skip button)
|
||||
if (isVideo && currentMedia) {
|
||||
fetchNextEpisode(currentMedia);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("loadAndPlay error:", e);
|
||||
// Show detailed error including the full error object
|
||||
@@ -520,6 +526,26 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchNextEpisode(media: MediaItem) {
|
||||
nextEpisode = null;
|
||||
if (media.type !== "Episode" || !media.seriesId) return;
|
||||
try {
|
||||
const repo = auth.getRepository();
|
||||
const episodes = await repo.getNextUpEpisodes(media.seriesId, 1);
|
||||
if (episodes.length > 0 && episodes[0].id !== media.id) {
|
||||
nextEpisode = episodes[0];
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Failed to fetch next episode:", e);
|
||||
}
|
||||
}
|
||||
|
||||
function handleSkipToNextEpisode() {
|
||||
if (nextEpisode) {
|
||||
goto(`/player/${nextEpisode.id}`);
|
||||
}
|
||||
}
|
||||
|
||||
function formatTime(seconds: number): string {
|
||||
const hours = Math.floor(seconds / 3600);
|
||||
const minutes = Math.floor((seconds % 3600) / 60);
|
||||
@@ -588,6 +614,8 @@
|
||||
onReportProgress={handleReportProgress}
|
||||
onReportStop={handleReportStop}
|
||||
onEnded={handleVideoEnded}
|
||||
hasNext={nextEpisode !== null}
|
||||
onNext={handleSkipToNextEpisode}
|
||||
/>
|
||||
<NextEpisodePopup />
|
||||
{:else}
|
||||
|
||||
@@ -23,8 +23,18 @@
|
||||
interface VideoSettings {
|
||||
autoPlayNextEpisode: boolean;
|
||||
autoPlayCountdownSeconds: number;
|
||||
autoPlayMaxEpisodes: number;
|
||||
}
|
||||
|
||||
const episodeLimitOptions = [
|
||||
{ value: 0, label: "Unlimited" },
|
||||
{ value: 1, label: "1" },
|
||||
{ value: 2, label: "2" },
|
||||
{ value: 3, label: "3" },
|
||||
{ value: 5, label: "5" },
|
||||
{ value: 10, label: "10" },
|
||||
];
|
||||
|
||||
let settings = $state<AudioSettings>({
|
||||
crossfadeDuration: 0,
|
||||
gaplessPlayback: true,
|
||||
@@ -35,6 +45,7 @@
|
||||
let videoSettings = $state<VideoSettings>({
|
||||
autoPlayNextEpisode: true,
|
||||
autoPlayCountdownSeconds: 10,
|
||||
autoPlayMaxEpisodes: 0,
|
||||
});
|
||||
|
||||
let loading = $state(true);
|
||||
@@ -172,7 +183,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="max-w-2xl mx-auto space-y-8 p-6">
|
||||
<div class="max-w-2xl mx-auto space-y-8 p-6 pb-24 h-full overflow-y-auto">
|
||||
<div>
|
||||
<h1 class="text-3xl font-bold text-white mb-2">Audio Settings</h1>
|
||||
<p class="text-gray-400">Configure playback and audio processing</p>
|
||||
@@ -359,6 +370,29 @@
|
||||
<span>30s</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Episode Limit -->
|
||||
<div class="pt-4 border-t border-gray-700">
|
||||
<div class="mb-4">
|
||||
<p class="text-sm font-medium text-gray-300">Episode Limit</p>
|
||||
<p class="text-xs text-gray-500 mt-1">
|
||||
Stop auto-playing after this many consecutive episodes
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 md:grid-cols-6 gap-2">
|
||||
{#each episodeLimitOptions as option}
|
||||
<button
|
||||
onclick={() => { videoSettings.autoPlayMaxEpisodes = option.value; }}
|
||||
class="py-3 px-3 rounded-lg transition-all text-sm
|
||||
{videoSettings.autoPlayMaxEpisodes === option.value
|
||||
? 'bg-[var(--color-jellyfin)] text-white'
|
||||
: 'bg-gray-700 text-gray-300 hover:bg-gray-600'}"
|
||||
>
|
||||
<div class="font-semibold">{option.label}</div>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user