- {#if item.type === "MusicAlbum"}
+ {#if item.kind === "album"}
@@ -558,14 +557,14 @@
{#if item.genres?.length || item.artistItems?.length}
a.id)}
limit={12}
/>
{/if}
- {:else if item.type === "Series"}
+ {:else if item.kind === "series"}
{#if $isLibraryLoading}
@@ -595,17 +594,17 @@
{#if item.genres?.length || item.people?.length}
{/if}
- {:else if item.type === "MusicArtist"}
+ {:else if item.kind === "artist"}
- {:else if item.type === "Playlist"}
+ {:else if item.kind === "playlist"}
{:else}
diff --git a/src/routes/player/[id]/+page.svelte b/src/routes/player/[id]/+page.svelte
index bd580ac5..f2041ffe 100644
--- a/src/routes/player/[id]/+page.svelte
+++ b/src/routes/player/[id]/+page.svelte
@@ -5,7 +5,7 @@
import { convertFileSrc } from "@tauri-apps/api/core";
import { commands } from "$lib/api/bindings";
import type { PlayQueueRequest } from "$lib/api/bindings";
- import type { MediaItem } from "$lib/api/types";
+ import type { MediaItem, MediaKind } from "$lib/api/types";
import { auth } from "$lib/stores/auth";
import { library } from "$lib/stores/library";
import { queue, currentQueueItem, isShuffle, repeatMode, hasNext as hasNextStore, hasPrevious as hasPreviousStore } from "$lib/stores/queue";
@@ -100,7 +100,7 @@
// Only for audio content - video content uses direct loading and shouldn't be affected by audio queue
$effect(() => {
const queueItem = $currentQueueItem;
- const currentIsVideo = currentMedia?.type === "Movie" || currentMedia?.type === "Episode";
+ const currentIsVideo = currentMedia?.kind === "movie" || currentMedia?.kind === "episode";
if (queueItem && queueItem.id !== currentMedia?.id && !currentIsVideo) {
currentMedia = queueItem;
}
@@ -110,7 +110,8 @@
// treat it as video when it carries a video media stream.
function isVideoChannelItem(item: MediaItem): boolean {
return (
- item.type === "ChannelFolderItem" &&
+ item.kind === "channelItem" &&
+ // stream.type is Jellyfin stream vocabulary (migrated in a later phase).
(item.mediaStreams?.some((s) => s.type === "Video") ?? false)
);
}
@@ -125,14 +126,13 @@
console.log("loadAndPlay: Loading item", id);
// Load item details
const item = await library.loadItem(id);
- console.log("loadAndPlay: Loaded item", item.name, "type:", item.type);
+ console.log("loadAndPlay: Loaded item", item.name, "kind:", item.kind);
currentMedia = item;
// Check if this is a non-playable collection type that should be viewed in library instead
- const collectionTypes = ["MusicAlbum", "MusicArtist", "Series", "Season", "Folder", "CollectionFolder", "Playlist", "Channel"];
- // A ChannelFolderItem that is itself a folder is a container, not playable.
- if (collectionTypes.includes(item.type) || (item.type === "ChannelFolderItem" && item.isFolder)) {
- console.log("loadAndPlay: Redirecting collection type to library:", item.type);
+ const collectionKinds: MediaKind[] = ["album", "artist", "series", "season", "folder", "playlist", "channel"];
+ if (item.kind && collectionKinds.includes(item.kind)) {
+ console.log("loadAndPlay: Redirecting collection type to library:", item.kind);
goto(`/library/${id}`);
return;
}
@@ -144,8 +144,8 @@
const alreadyPlayingMedia = get(storeCurrentMedia);
if (alreadyPlayingMedia?.id === id && !startPosition && !forceRestart) {
console.log("loadAndPlay: Track already playing, showing UI without restarting");
- isLive = item.type === "TvChannel";
- isVideo = item.type === "Movie" || item.type === "Episode" || isLive || isVideoChannelItem(item);
+ isLive = item.kind === "liveChannel";
+ isVideo = item.kind === "movie" || item.kind === "episode" || isLive || isVideoChannelItem(item);
isPlaying = true;
loading = false;
// hasNext/hasPrevious come from the event-driven queue store.
@@ -158,8 +158,8 @@
// Determine if this is video content (Movie, Episode, live TV channels, and
// channel leaf items that carry a video stream).
- isLive = item.type === "TvChannel";
- isVideo = item.type === "Movie" || item.type === "Episode" || isLive || isVideoChannelItem(item);
+ isLive = item.kind === "liveChannel";
+ isVideo = item.kind === "movie" || item.kind === "episode" || isLive || isVideoChannelItem(item);
// When switching to video, stop audio playback and clear the queue
// This prevents audio from continuing in the background and clears stale state
@@ -330,7 +330,7 @@
sortOrder: "Ascending",
limit: 500,
});
- const audioTracks = result.items.filter(t => t.type === "Audio");
+ const audioTracks = result.items.filter(t => t.kind === "track");
if (audioTracks.length > 0) {
// Find the index of the current item in the tracks
@@ -428,7 +428,7 @@
loading = false;
// Fetch next episode for video episodes (for skip button)
- console.log("[NextEpisode] Post-load check: isVideo=", isVideo, "currentMedia=", currentMedia?.type, currentMedia?.name);
+ console.log("[NextEpisode] Post-load check: isVideo=", isVideo, "currentMedia=", currentMedia?.kind, currentMedia?.name);
if (isVideo && currentMedia) {
fetchNextEpisode(currentMedia);
} else {
@@ -566,8 +566,8 @@
async function fetchNextEpisode(media: MediaItem) {
nextEpisode = null;
- console.log("[NextEpisode] fetchNextEpisode called:", { type: media.type, seriesId: media.seriesId, seasonId: media.seasonId, indexNumber: media.indexNumber, id: media.id, name: media.name });
- if (media.type !== "Episode" || !media.seasonId || media.indexNumber == null) {
+ console.log("[NextEpisode] fetchNextEpisode called:", { kind: media.kind, seriesId: media.seriesId, seasonId: media.seasonId, indexNumber: media.indexNumber, id: media.id, name: media.name });
+ if (media.kind !== "episode" || !media.seasonId || media.indexNumber == null) {
console.log("[NextEpisode] Skipping - not an episode or missing seasonId/indexNumber");
return;
}
@@ -575,7 +575,7 @@
const repo = auth.getRepository();
// Fetch all episodes in the season sorted by episode number
const result = await repo.getItems(media.seasonId, { sortBy: "IndexNumber", sortOrder: "Ascending", limit: 500 });
- const episodes = result.items.filter(e => e.type === "Episode");
+ const episodes = result.items.filter(e => e.kind === "episode");
console.log("[NextEpisode] Season has", episodes.length, "episodes, current index:", media.indexNumber);
// Find the episode after the current one by index number