Split software arch desc for easier manintenance. Many fixes related to next video playing and remote playback
This commit is contained in:
@@ -19,6 +19,7 @@
|
||||
import PersonDetailView from "$lib/components/library/PersonDetailView.svelte";
|
||||
import RelatedItemsSection from "$lib/components/library/RelatedItemsSection.svelte";
|
||||
import ArtistDetailView from "$lib/components/library/ArtistDetailView.svelte";
|
||||
import PlaylistDetailView from "$lib/components/library/PlaylistDetailView.svelte";
|
||||
import CrewLinks from "$lib/components/library/CrewLinks.svelte";
|
||||
import GenreTags from "$lib/components/library/GenreTags.svelte";
|
||||
import CachedImage from "$lib/components/common/CachedImage.svelte";
|
||||
@@ -545,6 +546,9 @@
|
||||
{:else if item.type === "MusicArtist"}
|
||||
<!-- Enhanced artist detail view with discography -->
|
||||
<ArtistDetailView artist={item} />
|
||||
{:else if item.type === "Playlist"}
|
||||
<!-- Playlist detail view with track management -->
|
||||
<PlaylistDetailView playlist={item} />
|
||||
{:else}
|
||||
<!-- Other content in grid view -->
|
||||
<LibraryGrid
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
<script lang="ts">
|
||||
import GenericMediaListPage from "$lib/components/library/GenericMediaListPage.svelte";
|
||||
import CreatePlaylistModal from "$lib/components/playlist/CreatePlaylistModal.svelte";
|
||||
|
||||
/**
|
||||
* Playlist browser
|
||||
* @req: UR-007 - Navigate media in library
|
||||
* @req: UR-008 - Search media across libraries
|
||||
* @req: DR-007 - Library browsing screens
|
||||
* @req: UR-014 - Make and edit playlists
|
||||
*/
|
||||
|
||||
let showCreateModal = $state(false);
|
||||
|
||||
const config = {
|
||||
itemType: "Playlist" as const,
|
||||
title: "Playlists",
|
||||
@@ -20,4 +24,22 @@
|
||||
};
|
||||
</script>
|
||||
|
||||
<GenericMediaListPage {config} />
|
||||
<div class="relative">
|
||||
<!-- Floating create button -->
|
||||
<button
|
||||
onclick={() => showCreateModal = true}
|
||||
class="fixed bottom-20 right-4 z-40 w-14 h-14 bg-[var(--color-jellyfin)] hover:bg-[var(--color-jellyfin-dark)] rounded-full shadow-lg flex items-center justify-center transition-colors"
|
||||
aria-label="Create playlist"
|
||||
>
|
||||
<svg class="w-7 h-7 text-white" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<GenericMediaListPage {config} />
|
||||
</div>
|
||||
|
||||
<CreatePlaylistModal
|
||||
isOpen={showCreateModal}
|
||||
onClose={() => showCreateModal = false}
|
||||
/>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
reportPlaybackProgress,
|
||||
reportPlaybackStopped,
|
||||
} from "$lib/services/playbackReporting";
|
||||
import { cleanup as cleanupNextEpisode, handleEpisodeEnded } from "$lib/services/nextEpisodeService";
|
||||
import { cleanup as cleanupNextEpisode } from "$lib/services/nextEpisodeService";
|
||||
|
||||
const itemId = $derived($page.params.id);
|
||||
const queueParam = $derived($page.url.searchParams.get("queue"));
|
||||
@@ -62,25 +62,12 @@
|
||||
let pollInterval: ReturnType<typeof setInterval> | null = null;
|
||||
let loadedItemId: string | null = null;
|
||||
|
||||
// Handle next episode navigation event from popup
|
||||
function handlePlayNextEpisode(event: Event) {
|
||||
const customEvent = event as CustomEvent<{ episode: MediaItem }>;
|
||||
const episode = customEvent.detail.episode;
|
||||
if (episode) {
|
||||
goto(`/player/${episode.id}`);
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
// Start position polling (only for audio via MPV backend)
|
||||
pollInterval = setInterval(updateStatus, 1000);
|
||||
|
||||
// Listen for next episode navigation events
|
||||
window.addEventListener("playNextEpisode", handlePlayNextEpisode);
|
||||
|
||||
return () => {
|
||||
if (pollInterval) clearInterval(pollInterval);
|
||||
window.removeEventListener("playNextEpisode", handlePlayNextEpisode);
|
||||
};
|
||||
});
|
||||
|
||||
@@ -92,6 +79,7 @@
|
||||
$effect(() => {
|
||||
const id = itemId;
|
||||
if (id && id !== loadedItemId) {
|
||||
console.log("[AutoPlay] $effect triggered: loading new item", id, "(was:", loadedItemId, ")");
|
||||
loadAndPlay(id);
|
||||
}
|
||||
});
|
||||
@@ -526,10 +514,20 @@
|
||||
|
||||
async function handleVideoEnded() {
|
||||
// Call backend to handle autoplay decision (works on both Android and Linux)
|
||||
// Pass the item ID and repository handle so the backend can look up the item
|
||||
// and check for next episodes. HTML5 video plays independently of the Rust
|
||||
// backend queue, so the backend needs these to know what just finished.
|
||||
const mediaId = currentMedia?.id ?? null;
|
||||
console.log("[AutoPlay] Video ended. currentMedia:", mediaId, currentMedia?.name, "itemId (URL):", itemId);
|
||||
try {
|
||||
await invoke("player_on_playback_ended");
|
||||
const repo = auth.getRepository();
|
||||
const repoHandle = repo.getHandle();
|
||||
await invoke("player_on_playback_ended", {
|
||||
itemId: mediaId,
|
||||
repositoryHandle: repoHandle,
|
||||
});
|
||||
} catch (e) {
|
||||
console.error("[VideoPlayer] Failed to handle playback ended:", e);
|
||||
console.error("[AutoPlay] Failed to handle playback ended:", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user