Merge branch 'develop' into fix/vlc4

This commit is contained in:
Gauvain
2025-07-21 14:10:33 +02:00
committed by GitHub
210 changed files with 4785 additions and 2011 deletions

View File

@@ -1,17 +1,17 @@
import { useMemo } from "react";
import { useJellyseerr } from "@/hooks/useJellyseerr";
import {
MediaRequestStatus,
MediaStatus,
} from "@/utils/jellyseerr/server/constants/media";
import {
Permission,
hasPermission,
Permission,
} from "@/utils/jellyseerr/server/lib/permissions";
import type {
MovieResult,
TvResult,
} from "@/utils/jellyseerr/server/models/Search";
import { useMemo } from "react";
import type MediaRequest from "../jellyseerr/server/entity/MediaRequest";
import type { MovieDetails } from "../jellyseerr/server/models/Movie";
import type { TvDetails } from "../jellyseerr/server/models/Tv";

View File

@@ -1,5 +1,5 @@
import * as ScreenOrientation from "@/packages/expo-screen-orientation";
import { atom } from "jotai";
import * as ScreenOrientation from "@/packages/expo-screen-orientation";
export const orientationAtom = atom<number>(
ScreenOrientation.OrientationLock.PORTRAIT_UP,

View File

@@ -16,7 +16,7 @@ export const calculateTextColor = (backgroundColor: string): string => {
const brightness = (r * 299 + g * 587 + b * 114) / 1000;
// Calculate contrast ratio with white and black
const contrastWithWhite = calculateContrastRatio([255, 255, 255], [r, g, b]);
const _contrastWithWhite = calculateContrastRatio([255, 255, 255], [r, g, b]);
const contrastWithBlack = calculateContrastRatio([0, 0, 0], [r, g, b]);
// Use black text if the background is bright and has good contrast with black
@@ -55,7 +55,7 @@ export const isCloseToBlack = (color: string): boolean => {
return r < 20 && g < 20 && b < 20;
};
export const adjustToNearBlack = (color: string): string => {
export const adjustToNearBlack = (_color: string): string => {
return "#313131"; // A very dark gray, almost black
};

View File

@@ -1,9 +1,9 @@
import { processesAtom } from "@/providers/DownloadProvider";
import { useSettings } from "@/utils/atoms/settings";
import type { JobStatus } from "@/utils/optimize-server";
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
import { atom, useAtom } from "jotai";
import { useEffect } from "react";
import { processesAtom } from "@/providers/DownloadProvider";
import { useSettings } from "@/utils/atoms/settings";
import type { JobStatus } from "@/utils/optimize-server";
export interface Job {
id: string;

View File

@@ -1,4 +1,5 @@
import { Platform } from "react-native";
const BackgroundFetch = !Platform.isTV
? require("expo-background-fetch")
: null;

View File

@@ -1,9 +1,9 @@
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
import { useAtom } from "jotai";
import useImageStorage from "@/hooks/useImageStorage";
import { apiAtom } from "@/providers/JellyfinProvider";
import { getPrimaryImageUrlById } from "@/utils/jellyfin/image/getPrimaryImageUrlById";
import { storage } from "@/utils/mmkv";
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
import { useAtom } from "jotai";
const useDownloadHelper = () => {
const [api] = useAtom(apiAtom);

View File

@@ -1,10 +1,11 @@
// utils/getDefaultPlaySettings.ts
import { BITRATES } from "@/components/BitrateSelector";
import type {
BaseItemDto,
MediaSourceInfo,
} from "@jellyfin/sdk/lib/generated-client";
import { type Settings, useSettings } from "../atoms/settings";
import { BITRATES } from "@/components/BitrateSelector";
import { type Settings } from "../atoms/settings";
import {
AudioStreamRanker,
StreamRanker,
@@ -52,10 +53,10 @@ export function getDefaultPlaySettings(
// 2. Get default or preferred audio
const defaultAudioIndex = mediaSource?.DefaultAudioStreamIndex;
const preferedAudioIndex = mediaSource?.MediaStreams?.find(
const _preferedAudioIndex = mediaSource?.MediaStreams?.find(
(x) => x.Type === "Audio" && x.Language === settings?.defaultAudioLanguage,
)?.Index;
const firstAudioIndex = mediaSource?.MediaStreams?.find(
const _firstAudioIndex = mediaSource?.MediaStreams?.find(
(x) => x.Type === "Audio",
)?.Index;

View File

@@ -1,9 +1,5 @@
import type { Api } from "@jellyfin/sdk";
import {
type BaseItemDto,
BaseItemPerson,
} from "@jellyfin/sdk/lib/generated-client/models";
import { isBaseItemDto } from "../jellyfin";
import { type BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
/**
* Retrieves the primary image URL for a given item.

View File

@@ -1,9 +1,5 @@
import type { Api } from "@jellyfin/sdk";
import {
type BaseItemDto,
BaseItemPerson,
} from "@jellyfin/sdk/lib/generated-client/models";
import { isBaseItemDto } from "../jellyfin";
import { type BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
/**
* Retrieves the primary image URL for a given item.

View File

@@ -18,7 +18,7 @@ export const getAuthHeaders = (api: Api): Record<string, string> => ({
* @returns {string} - The bitrate as a human-readable string.
*/
export const bitrateToString = (bitrate: number): string => {
const kbps = bitrate / 1000;
const _kbps = bitrate / 1000;
const mbps = (bitrate / 1000000).toFixed(2);
return `${mbps} Mb/s`;

View File

@@ -31,7 +31,7 @@ export const markAsPlayed = async ({
});
return response.status === 200;
} catch (error) {
} catch (_error) {
return false;
}
};

View File

@@ -1,15 +1,6 @@
import { getOrSetDeviceId } from "@/providers/JellyfinProvider";
import type { Settings } from "@/utils/atoms/settings";
import old from "@/utils/profiles/old";
import type { Api } from "@jellyfin/sdk";
import { DeviceProfile } from "@jellyfin/sdk/lib/generated-client";
import {
getMediaInfoApi,
getPlaystateApi,
getSessionApi,
} from "@jellyfin/sdk/lib/utils/api";
import { getAuthHeaders } from "../jellyfin";
import { postCapabilities } from "../session/capabilities";
import { getPlaystateApi } from "@jellyfin/sdk/lib/utils/api";
import type { Settings } from "@/utils/atoms/settings";
interface ReportPlaybackProgressParams {
api?: Api | null;

View File

@@ -1,7 +1,7 @@
import type { Settings } from "@/utils/atoms/settings";
import generateDeviceProfile from "@/utils/profiles/native";
import type { Api } from "@jellyfin/sdk";
import type { AxiosResponse } from "axios";
import type { Settings } from "@/utils/atoms/settings";
import generateDeviceProfile from "@/utils/profiles/native";
import { getAuthHeaders } from "../jellyfin";
interface PostCapabilitiesParams {
@@ -50,7 +50,7 @@ export const postCapabilities = async ({
},
);
return d;
} catch (error) {
} catch (_error) {
throw new Error("Failed to mark as not played");
}
};

View File

@@ -1,6 +1,5 @@
import type { Api } from "@jellyfin/sdk";
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
import { AxiosError } from "axios";
import { getAuthHeaders } from "../jellyfin";
interface NextUpParams {
@@ -39,7 +38,7 @@ export const nextUp = async ({
);
return response.data.Items;
} catch (error) {
} catch (_error) {
return [];
}
};

View File

@@ -23,9 +23,9 @@ const logsAtom = atomWithStorage("logs", [], mmkvStorage);
const LogContext = createContext<ReturnType<typeof useLogProvider> | null>(
null,
);
const DownloadContext = createContext<ReturnType<typeof useLogProvider> | null>(
null,
);
const _DownloadContext = createContext<ReturnType<
typeof useLogProvider
> | null>(null);
function useLogProvider() {
const { data: logs } = useQuery({

View File

@@ -1,5 +1,3 @@
import { itemRouter } from "@/components/common/TouchableItemRouter";
import { DownloadedItem } from "@/providers/DownloadProvider";
import type {
BaseItemDto,
MediaSourceInfo,

View File

@@ -1,5 +1,3 @@
import { Platform } from "react-native";
import DeviceInfo from "react-native-device-info";
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -7,28 +5,7 @@ import DeviceInfo from "react-native-device-info";
*/
import MediaTypes from "../../constants/MediaTypes";
// Helper function to detect Dolby Vision support
const supportsDolbyVision = async () => {
if (Platform.OS === "ios") {
const deviceModel = await DeviceInfo.getModel();
// iPhone 12 and newer generally support Dolby Vision
const modelNumber = Number.parseInt(deviceModel.replace(/iPhone/, ""), 10);
return !Number.isNaN(modelNumber) && modelNumber >= 12;
}
if (Platform.OS === "android") {
const apiLevel = await DeviceInfo.getApiLevel();
const isHighEndDevice =
(await DeviceInfo.getTotalMemory()) > 4 * 1024 * 1024 * 1024; // >4GB RAM
// Very rough approximation - Android 10+ on higher-end devices may support it
return apiLevel >= 29 && isHighEndDevice;
}
return false;
};
export const generateDeviceProfile = async () => {
const dolbyVisionSupported = await supportsDolbyVision();
/**
* Device profile for Native video player
*/
@@ -51,7 +28,12 @@ export const generateDeviceProfile = async () => {
Value: "153",
IsRequired: false,
},
// We'll add Dolby Vision condition below if not supported
{
Condition: "NotEquals",
Property: "VideoRangeType",
Value: "DOVI", //no dolby vision at all
IsRequired: true,
},
],
},
{
@@ -172,22 +154,6 @@ export const generateDeviceProfile = async () => {
],
};
// Add Dolby Vision restriction if not supported
if (!dolbyVisionSupported) {
const hevcProfile = profile.CodecProfiles.find(
(p) => p.Type === MediaTypes.Video && p.Codec.includes("hevc"),
);
if (hevcProfile) {
hevcProfile.Conditions.push({
Condition: "NotEquals",
Property: "VideoRangeType",
Value: "DOVI", //no dolby vision at all
IsRequired: true,
});
}
}
return profile;
};