mirror of
https://github.com/streamyfin/streamyfin.git
synced 2025-08-20 18:37:18 +02:00
wip
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { atom, useAtom } from "jotai";
|
||||
import AsyncStorage from "@react-native-async-storage/async-storage";
|
||||
import { useEffect } from "react";
|
||||
import * as ScreenOrientation from "expo-screen-orientation";
|
||||
|
||||
export type DownloadQuality = "original" | "high" | "low";
|
||||
|
||||
@@ -9,6 +10,22 @@ export type DownloadOption = {
|
||||
value: DownloadQuality;
|
||||
};
|
||||
|
||||
export const ScreenOrientationEnum: Record<
|
||||
ScreenOrientation.OrientationLock,
|
||||
string
|
||||
> = {
|
||||
[ScreenOrientation.OrientationLock.DEFAULT]: "Default",
|
||||
[ScreenOrientation.OrientationLock.ALL]: "All",
|
||||
[ScreenOrientation.OrientationLock.PORTRAIT]: "Portrait",
|
||||
[ScreenOrientation.OrientationLock.PORTRAIT_UP]: "Portrait Up",
|
||||
[ScreenOrientation.OrientationLock.PORTRAIT_DOWN]: "Portrait Down",
|
||||
[ScreenOrientation.OrientationLock.LANDSCAPE]: "Landscape",
|
||||
[ScreenOrientation.OrientationLock.LANDSCAPE_LEFT]: "Landscape Left",
|
||||
[ScreenOrientation.OrientationLock.LANDSCAPE_RIGHT]: "Landscape Right",
|
||||
[ScreenOrientation.OrientationLock.OTHER]: "Other",
|
||||
[ScreenOrientation.OrientationLock.UNKNOWN]: "Unknown",
|
||||
};
|
||||
|
||||
export const DownloadOptions: DownloadOption[] = [
|
||||
{
|
||||
label: "Original quality",
|
||||
@@ -53,6 +70,7 @@ type Settings = {
|
||||
defaultSubtitleLanguage: DefaultLanguageOption | null;
|
||||
defaultAudioLanguage: DefaultLanguageOption | null;
|
||||
showHomeTitles: boolean;
|
||||
defaultVideoOrientation: ScreenOrientation.OrientationLock;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -86,6 +104,7 @@ const loadSettings = async (): Promise<Settings> => {
|
||||
defaultAudioLanguage: null,
|
||||
defaultSubtitleLanguage: null,
|
||||
showHomeTitles: true,
|
||||
defaultVideoOrientation: ScreenOrientation.OrientationLock.DEFAULT,
|
||||
};
|
||||
|
||||
try {
|
||||
|
||||
55
utils/hls/parseM3U8ForSubtitles.ts
Normal file
55
utils/hls/parseM3U8ForSubtitles.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import axios from "axios";
|
||||
|
||||
export interface SubtitleTrack {
|
||||
index: number;
|
||||
name: string;
|
||||
uri: string;
|
||||
language: string;
|
||||
default: boolean;
|
||||
forced: boolean;
|
||||
autoSelect: boolean;
|
||||
}
|
||||
|
||||
export async function parseM3U8ForSubtitles(
|
||||
url: string
|
||||
): Promise<SubtitleTrack[]> {
|
||||
try {
|
||||
const response = await axios.get(url, { responseType: "text" });
|
||||
const lines = response.data.split(/\r?\n/);
|
||||
const subtitleTracks: SubtitleTrack[] = [];
|
||||
let index = 0;
|
||||
|
||||
lines.forEach((line: string) => {
|
||||
if (line.startsWith("#EXT-X-MEDIA:TYPE=SUBTITLES")) {
|
||||
const attributes = parseAttributes(line);
|
||||
const track: SubtitleTrack = {
|
||||
index: index++,
|
||||
name: attributes["NAME"] || "",
|
||||
uri: attributes["URI"] || "",
|
||||
language: attributes["LANGUAGE"] || "",
|
||||
default: attributes["DEFAULT"] === "YES",
|
||||
forced: attributes["FORCED"] === "YES",
|
||||
autoSelect: attributes["AUTOSELECT"] === "YES",
|
||||
};
|
||||
subtitleTracks.push(track);
|
||||
}
|
||||
});
|
||||
|
||||
return subtitleTracks;
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch or parse the M3U8 file:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
function parseAttributes(line: string): { [key: string]: string } {
|
||||
const attributes: { [key: string]: string } = {};
|
||||
const parts = line.split(",");
|
||||
parts.forEach((part) => {
|
||||
const [key, value] = part.split("=");
|
||||
if (key && value) {
|
||||
attributes[key.trim()] = value.replace(/"/g, "").trim();
|
||||
}
|
||||
});
|
||||
return attributes;
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
MediaSourceInfo,
|
||||
PlaybackInfoResponse,
|
||||
} from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import { getAuthHeaders } from "../jellyfin";
|
||||
|
||||
export const getStreamUrl = async ({
|
||||
api,
|
||||
@@ -15,7 +16,7 @@ export const getStreamUrl = async ({
|
||||
sessionData,
|
||||
deviceProfile = ios,
|
||||
audioStreamIndex = 0,
|
||||
subtitleStreamIndex = 0,
|
||||
subtitleStreamIndex = undefined,
|
||||
forceDirectPlay = false,
|
||||
height,
|
||||
mediaSourceId,
|
||||
@@ -39,6 +40,9 @@ export const getStreamUrl = async ({
|
||||
|
||||
const itemId = item.Id;
|
||||
|
||||
/**
|
||||
* Build the stream URL for videos
|
||||
*/
|
||||
const response = await api.axiosInstance.post(
|
||||
`${api.basePath}/Items/${itemId}/PlaybackInfo`,
|
||||
{
|
||||
@@ -58,9 +62,7 @@ export const getStreamUrl = async ({
|
||||
EnableMpegtsM2TsMode: false,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: `MediaBrowser DeviceId="${api.deviceInfo.id}", Token="${api.accessToken}"`,
|
||||
},
|
||||
headers: getAuthHeaders(api),
|
||||
}
|
||||
);
|
||||
|
||||
@@ -80,10 +82,8 @@ export const getStreamUrl = async ({
|
||||
|
||||
if (mediaSource.SupportsDirectPlay || forceDirectPlay === true) {
|
||||
if (item.MediaType === "Video") {
|
||||
console.log("Using direct stream for video!");
|
||||
url = `${api.basePath}/Videos/${itemId}/stream.mp4?playSessionId=${sessionData.PlaySessionId}&mediaSourceId=${mediaSource.Id}&static=true&subtitleStreamIndex=${subtitleStreamIndex}&audioStreamIndex=${audioStreamIndex}&deviceId=${api.deviceInfo.id}&api_key=${api.accessToken}`;
|
||||
} else if (item.MediaType === "Audio") {
|
||||
console.log("Using direct stream for audio!");
|
||||
const searchParams = new URLSearchParams({
|
||||
UserId: userId,
|
||||
DeviceId: api.deviceInfo.id,
|
||||
|
||||
Reference in New Issue
Block a user