Split software arch desc for easier manintenance. Many fixes related to next video playing and remote playback
🏗️ Build and Test JellyTau / Run Tests (push) Failing after 12s
🏗️ Build and Test JellyTau / Build Android APK (push) Has been skipped
Traceability Validation / Check Requirement Traces (push) Failing after 1s

This commit is contained in:
2026-03-01 19:47:46 +01:00
parent 3a9c126dfe
commit 09780103a7
45 changed files with 5663 additions and 3332 deletions
+117 -138
View File
@@ -31,11 +31,9 @@
import { formatTime, calculateProgress } from "$lib/utils/playbackUnits";
import { haptics } from "$lib/utils/haptics";
import { toast } from "$lib/stores/toast";
import { sleepTimerActive } from "$lib/stores/sleepTimer";
import Controls from "./Controls.svelte";
import FavoriteButton from "$lib/components/FavoriteButton.svelte";
import CastButton from "$lib/components/sessions/CastButton.svelte";
import SleepTimerIndicator from "./SleepTimerIndicator.svelte";
import VolumeControl from "./VolumeControl.svelte";
import CachedImage from "../common/CachedImage.svelte";
@@ -289,14 +287,14 @@
<div
role="region"
class="px-4 py-3 flex items-center gap-4 touch-pan-y relative"
class="px-4 py-2 touch-pan-y relative"
ontouchstart={handleTouchStart}
ontouchmove={handleTouchMove}
ontouchend={handleTouchEnd}
style="transform: translateX({swipeTransform}px); transition: {isSwiping ? 'none' : 'transform 0.3s ease-out'}"
>
<!-- Media info -->
<div class="flex items-center gap-3 flex-1 min-w-0">
<!-- Row 1: Media info, like, cast, overflow -->
<div class="flex items-center gap-3">
<!-- Artwork -->
<div
class="w-12 h-12 rounded bg-gray-800 flex-shrink-0 overflow-hidden"
@@ -354,146 +352,127 @@
{/if}
</div>
</div>
</div>
<!-- Favorite Button -->
{#if displayMedia}
<div class="hidden sm:block">
<!-- Like Button -->
{#if displayMedia}
<FavoriteButton
itemId={displayMedia?.id ?? ""}
isFavorite={displayMedia?.userData?.isFavorite ?? false}
size="sm"
/>
</div>
{/if}
<!-- Cast Button (visible on all screen sizes) -->
<CastButton size="sm" />
<!-- Sleep Timer Button (always visible on larger screens) -->
{#if !$sleepTimerActive}
<button
onclick={(e) => {
e.stopPropagation();
onSleepTimerClick?.();
}}
ontouchstart={(e) => e.stopPropagation()}
ontouchmove={(e) => e.stopPropagation()}
ontouchend={(e) => e.stopPropagation()}
class="p-2 rounded-full hover:bg-white/10 transition-colors hidden sm:block"
title="Sleep timer"
aria-label="Sleep timer"
>
<svg class="w-5 h-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z" />
</svg>
</button>
{/if}
<!-- Sleep Timer Indicator (shows when active) -->
<SleepTimerIndicator onClick={onSleepTimerClick} />
<!-- Volume Control (Linux only) -->
<div class="hidden sm:block">
<VolumeControl size="sm" />
</div>
<!-- Time -->
<div class="text-xs text-gray-400 hidden sm:block">
{formatTime(displayPosition)} / {formatTime(displayDuration)}
</div>
<!-- Controls -->
<Controls
isPlaying={displayIsPlaying}
{hasPrevious}
{hasNext}
{shuffle}
{repeat}
onPlayPause={handlePlayPause}
onPrevious={handlePrevious}
onNext={handleNext}
onToggleShuffle={handleToggleShuffle}
onCycleRepeat={handleCycleRepeat}
{onSleepTimerClick}
/>
<!-- Overflow Menu Button -->
<div class="relative">
<button
onclick={() => {
showOverflowMenu = !showOverflowMenu;
haptics.tap();
}}
class="p-2 hover:bg-white/10 rounded-full transition-colors"
aria-label="More options"
>
<svg class="w-5 h-5 text-gray-400" fill="currentColor" viewBox="0 0 24 24">
<path d="M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/>
</svg>
</button>
<!-- Overflow Menu Dropdown -->
{#if showOverflowMenu}
<div
class="absolute bottom-full right-0 mb-2 w-56 bg-[var(--color-surface)] border border-gray-700 rounded-lg shadow-2xl overflow-hidden z-[70]"
role="menu"
>
<button
onclick={handleViewQueue}
class="w-full px-4 py-3 text-left text-sm text-white hover:bg-white/10 transition-colors flex items-center gap-3"
>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 10h16M4 14h16M4 18h16" />
</svg>
View Queue
</button>
{#if displayMedia?.albumId}
<button
onclick={handleGoToAlbum}
class="w-full px-4 py-3 text-left text-sm text-white hover:bg-white/10 transition-colors flex items-center gap-3"
>
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 14.5v-9l6 4.5-6 4.5z"/>
</svg>
Go to Album
</button>
{/if}
{#if displayMedia?.artistItems?.length}
<button
onclick={handleGoToArtist}
class="w-full px-4 py-3 text-left text-sm text-white hover:bg-white/10 transition-colors flex items-center gap-3"
>
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
<path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/>
</svg>
Go to Artist
</button>
{/if}
<button
onclick={handleAddToPlaylist}
class="w-full px-4 py-3 text-left text-sm text-white hover:bg-white/10 transition-colors flex items-center gap-3"
>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
</svg>
Add to Playlist
</button>
<button
onclick={handleShare}
class="w-full px-4 py-3 text-left text-sm text-white hover:bg-white/10 transition-colors flex items-center gap-3"
>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.368 2.684 3 3 0 00-5.368-2.684z" />
</svg>
Share
</button>
</div>
{/if}
<!-- Cast Button -->
<CastButton size="sm" />
<!-- Overflow Menu Button -->
<div class="relative">
<button
onclick={() => {
showOverflowMenu = !showOverflowMenu;
haptics.tap();
}}
class="p-2 hover:bg-white/10 rounded-full transition-colors"
aria-label="More options"
>
<svg class="w-5 h-5 text-gray-400" fill="currentColor" viewBox="0 0 24 24">
<path d="M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/>
</svg>
</button>
<!-- Overflow Menu Dropdown -->
{#if showOverflowMenu}
<div
class="absolute bottom-full right-0 mb-2 w-56 bg-[var(--color-surface)] border border-gray-700 rounded-lg shadow-2xl overflow-hidden z-[70]"
role="menu"
>
<button
onclick={handleViewQueue}
class="w-full px-4 py-3 text-left text-sm text-white hover:bg-white/10 transition-colors flex items-center gap-3"
>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 10h16M4 14h16M4 18h16" />
</svg>
View Queue
</button>
{#if displayMedia?.albumId}
<button
onclick={handleGoToAlbum}
class="w-full px-4 py-3 text-left text-sm text-white hover:bg-white/10 transition-colors flex items-center gap-3"
>
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 14.5v-9l6 4.5-6 4.5z"/>
</svg>
Go to Album
</button>
{/if}
{#if displayMedia?.artistItems?.length}
<button
onclick={handleGoToArtist}
class="w-full px-4 py-3 text-left text-sm text-white hover:bg-white/10 transition-colors flex items-center gap-3"
>
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
<path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/>
</svg>
Go to Artist
</button>
{/if}
<button
onclick={handleAddToPlaylist}
class="w-full px-4 py-3 text-left text-sm text-white hover:bg-white/10 transition-colors flex items-center gap-3"
>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
</svg>
Add to Playlist
</button>
<button
onclick={handleShare}
class="w-full px-4 py-3 text-left text-sm text-white hover:bg-white/10 transition-colors flex items-center gap-3"
>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.368 2.684 3 3 0 00-5.368-2.684z" />
</svg>
Share
</button>
</div>
{/if}
</div>
</div>
<!-- Row 2: Playback controls (play/pause centered) -->
<div class="flex items-center mt-1">
<div class="flex-1"></div>
<Controls
isPlaying={displayIsPlaying}
{hasPrevious}
{hasNext}
{shuffle}
{repeat}
onPlayPause={handlePlayPause}
onPrevious={handlePrevious}
onNext={handleNext}
onToggleShuffle={handleToggleShuffle}
onCycleRepeat={handleCycleRepeat}
{onSleepTimerClick}
/>
<div class="flex-1 flex items-center justify-end gap-2">
<!-- Volume Control (desktop only) -->
<div class="hidden sm:block">
<VolumeControl size="sm" />
</div>
<!-- Time (desktop only) -->
<div class="text-xs text-gray-400 hidden sm:block">
{formatTime(displayPosition)} / {formatTime(displayDuration)}
</div>
</div>
</div>
</div>
</div>