Compare commits

...

4 Commits

Author SHA1 Message Date
renovate[bot]
65be3d4059 chore(deps): Update dependency @react-navigation/material-top-tabs to v7.4.12 2026-01-15 14:25:10 +00:00
renovate[bot]
baeb83581e chore(deps): Update actions/setup-node action to v6.2.0 (#1372)
Some checks failed
🏗️ Build Apps / 🤖 Build Android APK (Phone) (push) Has been cancelled
🏗️ Build Apps / 🤖 Build Android APK (TV) (push) Has been cancelled
🏗️ Build Apps / 🍎 Build iOS IPA (Phone) (push) Has been cancelled
🏗️ Build Apps / 🍎 Build iOS IPA (Phone - Unsigned) (push) Has been cancelled
🔒 Lockfile Consistency Check / 🔍 Check bun.lock and package.json consistency (push) Has been cancelled
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (actions) (push) Has been cancelled
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (javascript-typescript) (push) Has been cancelled
🏷️🔀Merge Conflict Labeler / 🏷️ Labeling Merge Conflicts (push) Has been cancelled
🚦 Security & Quality Gate / 📝 Validate PR Title (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Vulnerable Dependencies (push) Has been cancelled
🚦 Security & Quality Gate / 🚑 Expo Doctor Check (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (check) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (format) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (lint) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (typecheck) (push) Has been cancelled
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-15 15:22:19 +01:00
Fredrik Burmester
05b7a4c50d fix: downloads should work when connecting through QC 2026-01-15 07:54:08 +01:00
Fredrik Burmester
28b67f3ad6 fix(mpv): handle audio track selection for transcoded streams on iOS 2026-01-15 07:53:15 +01:00
7 changed files with 27 additions and 12 deletions

View File

@@ -107,7 +107,7 @@ jobs:
fetch-depth: 0
- name: "🟢 Setup Node.js"
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version: '24.x'

View File

@@ -21,7 +21,7 @@ jobs:
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: "🟢 Setup Node.js"
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version: '24.x'
cache: 'npm'

View File

@@ -531,7 +531,11 @@ export default function page() {
subtitleIndex,
isTranscoding,
);
const initialAudioId = getMpvAudioId(mediaSource, audioIndex);
const initialAudioId = getMpvAudioId(
mediaSource,
audioIndex,
isTranscoding,
);
// Calculate start position directly here to avoid timing issues
const startTicks = playbackPositionFromUrl

View File

@@ -14,7 +14,7 @@
"@gorhom/bottom-sheet": "5.2.8",
"@jellyfin/sdk": "^0.13.0",
"@react-native-community/netinfo": "^11.4.1",
"@react-navigation/material-top-tabs": "7.4.9",
"@react-navigation/material-top-tabs": "7.4.12",
"@react-navigation/native": "^7.0.14",
"@shopify/flash-list": "2.0.2",
"@tanstack/query-sync-storage-persister": "^5.90.18",
@@ -566,9 +566,9 @@
"@react-navigation/core": ["@react-navigation/core@7.13.0", "", { "dependencies": { "@react-navigation/routers": "^7.5.1", "escape-string-regexp": "^4.0.0", "fast-deep-equal": "^3.1.3", "nanoid": "^3.3.11", "query-string": "^7.1.3", "react-is": "^19.1.0", "use-latest-callback": "^0.2.4", "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "react": ">= 18.2.0" } }, "sha512-Fc/SO23HnlGnkou/z8JQUzwEMvhxuUhr4rdPTIZp/c8q1atq3k632Nfh8fEiGtk+MP1wtIvXdN2a5hBIWpLq3g=="],
"@react-navigation/elements": ["@react-navigation/elements@2.9.2", "", { "dependencies": { "color": "^4.2.3", "use-latest-callback": "^0.2.4", "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "@react-native-masked-view/masked-view": ">= 0.2.0", "@react-navigation/native": "^7.1.25", "react": ">= 18.2.0", "react-native": "*", "react-native-safe-area-context": ">= 4.0.0" }, "optionalPeers": ["@react-native-masked-view/masked-view"] }, "sha512-J1GltOAGowNLznEphV/kr4zs0U7mUBO1wVA2CqpkN8ePBsoxrAmsd+T5sEYUCXN9KgTDFvc6IfcDqrGSQngd/g=="],
"@react-navigation/elements": ["@react-navigation/elements@2.9.4", "", { "dependencies": { "color": "^4.2.3", "use-latest-callback": "^0.2.4", "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "@react-native-masked-view/masked-view": ">= 0.2.0", "@react-navigation/native": "^7.1.27", "react": ">= 18.2.0", "react-native": "*", "react-native-safe-area-context": ">= 4.0.0" }, "optionalPeers": ["@react-native-masked-view/masked-view"] }, "sha512-TMFh+QzwesEuSaKpvZk4BFC5105t5gJs9eq+jG7jtfdlMKyrcFwheu2/dy1zfrW4WYbVcoMxhzFqCU4mKwI4fw=="],
"@react-navigation/material-top-tabs": ["@react-navigation/material-top-tabs@7.4.9", "", { "dependencies": { "@react-navigation/elements": "^2.9.2", "color": "^4.2.3", "react-native-tab-view": "^4.2.0" }, "peerDependencies": { "@react-navigation/native": "^7.1.25", "react": ">= 18.2.0", "react-native": "*", "react-native-pager-view": ">= 6.0.0", "react-native-safe-area-context": ">= 4.0.0" } }, "sha512-oYpdTfa2D1Tn0HJER9dRCR260agKGgYe+ydSHt3RIsJ9sLg8hU7ntKYWo1FnEC/Nsv1/N1u/tRst7ZpQRjjl4A=="],
"@react-navigation/material-top-tabs": ["@react-navigation/material-top-tabs@7.4.12", "", { "dependencies": { "@react-navigation/elements": "^2.9.4", "color": "^4.2.3", "react-native-tab-view": "^4.2.2" }, "peerDependencies": { "@react-navigation/native": "^7.1.27", "react": ">= 18.2.0", "react-native": "*", "react-native-pager-view": ">= 6.0.0", "react-native-safe-area-context": ">= 4.0.0" } }, "sha512-SrftYrhyx/bnpl51EP62yyymT+AyOtSSP9XIc0vLVPf88e7W0dWJI1SgGdWoYnvFPT5O7QN2xmB/Il39deDhzQ=="],
"@react-navigation/native": ["@react-navigation/native@7.1.19", "", { "dependencies": { "@react-navigation/core": "^7.13.0", "escape-string-regexp": "^4.0.0", "fast-deep-equal": "^3.1.3", "nanoid": "^3.3.11", "use-latest-callback": "^0.2.4" }, "peerDependencies": { "react": ">= 18.2.0", "react-native": "*" } }, "sha512-fM7q8di4Q8sp2WUhiUWOe7bEDRyRhbzsKQOd5N2k+lHeCx3UncsRYuw4Q/KN0EovM3wWKqMMmhy/YWuEO04kgw=="],
@@ -1692,7 +1692,7 @@
"react-native-svg": ["react-native-svg@15.12.1", "", { "dependencies": { "css-select": "^5.1.0", "css-tree": "^1.1.3", "warn-once": "0.1.1" }, "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-vCuZJDf8a5aNC2dlMovEv4Z0jjEUET53lm/iILFnFewa15b4atjVxU6Wirm6O9y6dEsdjDZVD7Q3QM4T1wlI8g=="],
"react-native-tab-view": ["react-native-tab-view@4.2.0", "", { "dependencies": { "use-latest-callback": "^0.2.4" }, "peerDependencies": { "react": ">= 18.2.0", "react-native": "*", "react-native-pager-view": ">= 6.0.0" } }, "sha512-TUbh7Yr0tE/99t1pJQLbQ+4/Px67xkT7/r3AhfV+93Q3WoUira0Lx7yuKUP2C118doqxub8NCLERwcqsHr29nQ=="],
"react-native-tab-view": ["react-native-tab-view@4.2.2", "", { "dependencies": { "use-latest-callback": "^0.2.4" }, "peerDependencies": { "react": ">= 18.2.0", "react-native": "*", "react-native-pager-view": ">= 6.0.0" } }, "sha512-NXtrG6OchvbGjsvbySJGVocXxo4Y2vA17ph4rAaWtA2jh+AasD8OyikKBRg2SmllEfeQ+GEhcKe8kulHv8BhTg=="],
"react-native-text-ticker": ["react-native-text-ticker@1.15.0", "", {}, "sha512-d/uK+PIOhsYMy1r8h825iq/nADiHsabz3WMbRJSnkpQYn+K9aykUAXRRhu8ZbTAzk4CgnUWajJEFxS5ZDygsdg=="],

View File

@@ -34,7 +34,7 @@
"@gorhom/bottom-sheet": "5.2.8",
"@jellyfin/sdk": "^0.13.0",
"@react-native-community/netinfo": "^11.4.1",
"@react-navigation/material-top-tabs": "7.4.9",
"@react-navigation/material-top-tabs": "7.4.12",
"@react-navigation/native": "^7.0.14",
"@shopify/flash-list": "2.0.2",
"@tanstack/query-sync-storage-persister": "^5.90.18",

View File

@@ -147,7 +147,7 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
}, [api, deviceId, headers]);
const pollQuickConnect = useCallback(async () => {
if (!api || !secret) return;
if (!api || !secret || !jellyfin) return;
try {
const response = await api.axiosInstance.get(
@@ -169,8 +169,8 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
);
const { AccessToken, User } = authResponse.data;
api.accessToken = AccessToken;
setUser(User);
setApi(jellyfin.createApi(api.basePath, AccessToken));
storage.set("token", AccessToken);
storage.set("user", JSON.stringify(User));
return true;
@@ -186,7 +186,7 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
console.error("Error polling Quick Connect:", error);
throw error;
}
}, [api, secret, headers]);
}, [api, secret, headers, jellyfin]);
useEffect(() => {
(async () => {

View File

@@ -91,21 +91,32 @@ export const getMpvSubtitleId = (
/**
* Calculate the MPV track ID for a given Jellyfin audio index.
*
* Audio tracks are simpler - they're always in MPV (no burn-in like image subs).
* For direct play: Audio tracks map to their position in the file (1-based).
* For transcoding: Only ONE audio track exists in the HLS stream (the selected one),
* so we should return 1 or undefined to use the default track.
*
* MPV track IDs are 1-based.
*
* @param mediaSource - The media source containing audio streams
* @param jellyfinAudioIndex - The Jellyfin server-side audio index
* @param isTranscoding - Whether the stream is being transcoded
* @returns MPV track ID (1-based), or undefined if not found
*/
export const getMpvAudioId = (
mediaSource: MediaSourceInfo | null | undefined,
jellyfinAudioIndex: number | undefined,
isTranscoding: boolean,
): number | undefined => {
if (jellyfinAudioIndex === undefined) {
return undefined;
}
// When transcoding, Jellyfin only includes the selected audio track in the HLS stream.
// So there's only 1 audio track - no need to specify an ID.
if (isTranscoding) {
return undefined;
}
const allAudio =
mediaSource?.MediaStreams?.filter((s) => s.Type === "Audio") || [];