+
{#if heroItems.length > 0}
diff --git a/src/routes/library/+layout.svelte b/src/routes/library/+layout.svelte
index b26036a..0a77959 100644
--- a/src/routes/library/+layout.svelte
+++ b/src/routes/library/+layout.svelte
@@ -1,17 +1,22 @@
diff --git a/src/routes/login/+page.svelte b/src/routes/login/+page.svelte
index 7ec0b06..915f18c 100644
--- a/src/routes/login/+page.svelte
+++ b/src/routes/login/+page.svelte
@@ -25,6 +25,13 @@
connecting = true;
localError = null;
+ // Reject plain HTTP — all connections must use HTTPS
+ if (serverUrl.trim().toLowerCase().startsWith("http://")) {
+ localError = "HTTP connections are not allowed. Please use HTTPS (e.g., https://your-server.com).";
+ connecting = false;
+ return;
+ }
+
try {
const info = await auth.connectToServer(serverUrl);
serverName = info.name;
diff --git a/src/routes/player/[id]/+page.svelte b/src/routes/player/[id]/+page.svelte
index 71b1bc2..e3d70ea 100644
--- a/src/routes/player/[id]/+page.svelte
+++ b/src/routes/player/[id]/+page.svelte
@@ -8,7 +8,7 @@
import { library } from "$lib/stores/library";
import { queue, currentQueueItem } from "$lib/stores/queue";
import { downloads, type DownloadInfo } from "$lib/stores/downloads";
- import { playbackPosition, playbackDuration } from "$lib/stores/player";
+ import { playbackPosition, playbackDuration, currentMedia as storeCurrentMedia } from "$lib/stores/player";
import { get } from "svelte/store";
import AudioPlayer from "$lib/components/player/AudioPlayer.svelte";
import VideoPlayer from "$lib/components/player/VideoPlayer.svelte";
@@ -126,6 +126,25 @@
return;
}
+ // If this track is already playing in the backend, just show the UI
+ // without restarting playback (e.g., when expanding from MiniPlayer)
+ const alreadyPlayingMedia = get(storeCurrentMedia);
+ if (alreadyPlayingMedia?.id === id && !startPosition) {
+ console.log("loadAndPlay: Track already playing, showing UI without restarting");
+ isVideo = item.type === "Movie" || item.type === "Episode";
+ isPlaying = true;
+ loading = false;
+ // Sync queue status
+ try {
+ const queueStatus = await invoke<{ hasNext: boolean; hasPrevious: boolean }>("player_get_queue");
+ hasNext = queueStatus.hasNext;
+ hasPrevious = queueStatus.hasPrevious;
+ } catch (e) {
+ // Ignore - queue status will update via polling
+ }
+ return;
+ }
+
// Determine if this is video content (Movie and Episode are video types)
isVideo = item.type === "Movie" || item.type === "Episode";
@@ -214,17 +233,20 @@
} else {
// Local audio playback via MPV backend
console.log("loadAndPlay: Using MPV backend for offline audio");
- await invoke("player_play_item", {
- item: {
- id: item.id,
- title: item.name,
- artist: item.artists?.join(", ") || null,
- album: item.albumName || null,
- duration: item.runTimeTicks ? item.runTimeTicks / 10000000 : null,
- artworkUrl: null, // Local file may not have artwork
- mediaType: "audio",
- streamUrl: localUrl,
- jellyfinItemId: item.id,
+ // Use player_play_tracks - backend fetches all metadata from single ID
+ const repo = auth.getRepository();
+ const repositoryHandle = repo.getHandle();
+
+ await invoke("player_play_tracks", {
+ repositoryHandle,
+ request: {
+ trackIds: [item.id],
+ startIndex: 0,
+ shuffle: false,
+ context: {
+ type: "search",
+ searchQuery: "",
+ },
},
});
if (startPosition) {
@@ -334,17 +356,20 @@
} else {
// Fallback to single item playback
console.log("loadAndPlay: No audio tracks found in parent, falling back to single item");
- await invoke("player_play_item", {
- item: {
- id: item.id,
- title: item.name,
- artist: item.artists?.join(", ") || null,
- album: item.albumName || null,
- duration: item.runTimeTicks ? item.runTimeTicks / 10000000 : null,
- artworkUrl: repo.getImageUrl(item.id, "Primary", { maxWidth: 500 }),
- mediaType: "audio",
- streamUrl: playbackInfo.streamUrl,
- jellyfinItemId: item.id,
+ // Use player_play_tracks - backend fetches all metadata from single ID
+ const repo = auth.getRepository();
+ const repositoryHandle = repo.getHandle();
+
+ await invoke("player_play_tracks", {
+ repositoryHandle,
+ request: {
+ trackIds: [item.id],
+ startIndex: 0,
+ shuffle: false,
+ context: {
+ type: "search",
+ searchQuery: "",
+ },
},
});
@@ -353,17 +378,20 @@
}
} else {
// No queue parameter - single item playback
- await invoke("player_play_item", {
- item: {
- id: item.id,
- title: item.name,
- artist: item.artists?.join(", ") || null,
- album: item.albumName || null,
- duration: item.runTimeTicks ? item.runTimeTicks / 10000000 : null,
- artworkUrl: repo.getImageUrl(item.id, "Primary", { maxWidth: 500 }),
- mediaType: "audio",
- streamUrl: playbackInfo.streamUrl,
- jellyfinItemId: item.id,
+ // Use player_play_tracks - backend fetches all metadata from single ID
+ const repo = auth.getRepository();
+ const repositoryHandle = repo.getHandle();
+
+ await invoke("player_play_tracks", {
+ repositoryHandle,
+ request: {
+ trackIds: [item.id],
+ startIndex: 0,
+ shuffle: false,
+ context: {
+ type: "search",
+ searchQuery: "",
+ },
},
});
diff --git a/src/routes/search/+page.svelte b/src/routes/search/+page.svelte
index 578f00c..cbd4c7d 100644
--- a/src/routes/search/+page.svelte
+++ b/src/routes/search/+page.svelte
@@ -59,7 +59,7 @@
{#if searchQuery.trim()}
0}
onItemClick={handleItemClick}
/>
{:else}