mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-04-22 16:54:42 +01:00
Merge branch 'develop' into fix/vlc4
This commit is contained in:
@@ -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";
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Platform } from "react-native";
|
||||
|
||||
const BackgroundFetch = !Platform.isTV
|
||||
? require("expo-background-fetch")
|
||||
: null;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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`;
|
||||
|
||||
@@ -31,7 +31,7 @@ export const markAsPlayed = async ({
|
||||
});
|
||||
|
||||
return response.status === 200;
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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 [];
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { itemRouter } from "@/components/common/TouchableItemRouter";
|
||||
import { DownloadedItem } from "@/providers/DownloadProvider";
|
||||
import type {
|
||||
BaseItemDto,
|
||||
MediaSourceInfo,
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user