mirror of
https://github.com/streamyfin/streamyfin.git
synced 2025-08-20 18:37:18 +02:00
Fixed bug with dupe subtitle names for transcoded content
This commit is contained in:
@@ -20,7 +20,13 @@ import { useQuery } from "@tanstack/react-query";
|
||||
import * as Haptics from "expo-haptics";
|
||||
import { useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||
import { useAtomValue } from "jotai";
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import React, {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { Pressable, useWindowDimensions, View } from "react-native";
|
||||
import { useSharedValue } from "react-native-reanimated";
|
||||
import Video, {
|
||||
@@ -123,6 +129,7 @@ const Player = () => {
|
||||
return null;
|
||||
}
|
||||
|
||||
console.log("getting med");
|
||||
const res = await getStreamUrl({
|
||||
api,
|
||||
item,
|
||||
@@ -259,7 +266,7 @@ const Player = () => {
|
||||
|
||||
// TODO: Use this when streaming with HLS url, but NOT when direct playing
|
||||
// TODO: since playable duration is always 0 then.
|
||||
// setIsBuffering(data.playableDuration === 0);
|
||||
setIsBuffering(data.playableDuration === 0);
|
||||
|
||||
if (!item?.Id || data.currentTime === 0) {
|
||||
return;
|
||||
@@ -328,19 +335,25 @@ const Player = () => {
|
||||
SelectedTrack | undefined
|
||||
>(undefined);
|
||||
|
||||
|
||||
// Set intial Subtitle Track.
|
||||
// We will only select external tracks if they are are text based. Else it should be burned in already.
|
||||
const textSubs =
|
||||
stream?.mediaSource.MediaStreams?.filter((sub) => sub.Type === "Subtitle" && sub.IsTextSubtitleStream) || [];
|
||||
stream?.mediaSource.MediaStreams?.filter(
|
||||
(sub) => sub.Type === "Subtitle" && sub.IsTextSubtitleStream
|
||||
) || [];
|
||||
|
||||
const uniqueTextSubs = Array.from(
|
||||
new Set(textSubs.map((sub) => sub.DisplayTitle))
|
||||
).map((title) => textSubs.find((sub) => sub.DisplayTitle === title));
|
||||
const chosenSubtitleTrack = textSubs.find(
|
||||
(sub) => sub.Index === subtitleIndex
|
||||
);
|
||||
useEffect(() => {
|
||||
if (chosenSubtitleTrack && selectedTextTrack === undefined) {
|
||||
console.log("Setting selected text track", chosenSubtitleTrack);
|
||||
setSelectedTextTrack({
|
||||
type: SelectedTrackType.INDEX,
|
||||
value: textSubs.indexOf(chosenSubtitleTrack),
|
||||
value: uniqueTextSubs.indexOf(chosenSubtitleTrack),
|
||||
});
|
||||
}
|
||||
}, [embededTextTracks]);
|
||||
|
||||
@@ -65,9 +65,6 @@ export const PlayButton: React.FC<Props> = ({
|
||||
const colorChangeProgress = useSharedValue(0);
|
||||
const [settings] = useSettings();
|
||||
|
||||
|
||||
|
||||
// console.log(bitrateValue);
|
||||
const goToPlayer = useCallback(
|
||||
(q: string, bitrateValue: number | undefined) => {
|
||||
if (!bitrateValue) {
|
||||
|
||||
@@ -12,8 +12,6 @@ import {
|
||||
import { useAtomValue } from "jotai";
|
||||
import { apiAtom } from "@/providers/JellyfinProvider";
|
||||
import { useLocalSearchParams, useRouter } from "expo-router";
|
||||
import { parse } from "@babel/core";
|
||||
import { set } from "lodash";
|
||||
|
||||
interface DropdownViewProps {
|
||||
showControls: boolean;
|
||||
@@ -129,23 +127,26 @@ const DropdownView: React.FC<DropdownViewProps> = ({ showControls }) => {
|
||||
);
|
||||
|
||||
const textSubtitlesMap = new Map(textSubtitles.map((s) => [s.name, s]));
|
||||
|
||||
const imageSubtitlesMap = new Map(imageSubtitles.map((s) => [s.name, s]));
|
||||
|
||||
const sortedSubtitles = allSubs
|
||||
.map((sub) => {
|
||||
const displayTitle = sub.DisplayTitle ?? "";
|
||||
if (textSubtitlesMap.has(displayTitle)) {
|
||||
return textSubtitlesMap.get(displayTitle);
|
||||
}
|
||||
if (imageSubtitlesMap.has(displayTitle)) {
|
||||
return imageSubtitlesMap.get(displayTitle);
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.filter(
|
||||
(subtitle): subtitle is TranscodedSubtitle => subtitle !== null
|
||||
);
|
||||
const sortedSubtitles = Array.from(
|
||||
new Set(
|
||||
allSubs
|
||||
.map((sub) => {
|
||||
const displayTitle = sub.DisplayTitle ?? "";
|
||||
if (textSubtitlesMap.has(displayTitle)) {
|
||||
return textSubtitlesMap.get(displayTitle);
|
||||
}
|
||||
if (imageSubtitlesMap.has(displayTitle)) {
|
||||
return imageSubtitlesMap.get(displayTitle);
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.filter(
|
||||
(subtitle): subtitle is TranscodedSubtitle => subtitle !== null
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
return [disableSubtitle, ...sortedSubtitles];
|
||||
}
|
||||
@@ -270,7 +271,9 @@ const DropdownView: React.FC<DropdownViewProps> = ({ showControls }) => {
|
||||
value={selectedSubtitleIndex === sub.index}
|
||||
key={`subtitle-item-${idx}`}
|
||||
onValueChange={() => {
|
||||
if (subtitleIndex === sub?.index.toString()) return;
|
||||
console.log("Set sub index: ", sub.index);
|
||||
if (selectedSubtitleIndex === sub?.index.toString())
|
||||
return;
|
||||
setSelectedSubtitleIndex(sub.index);
|
||||
if (sub.IsTextSubtitleStream && isOnTextSubtitle) {
|
||||
setSubtitleTrack && setSubtitleTrack(sub.index);
|
||||
@@ -322,7 +325,7 @@ const DropdownView: React.FC<DropdownViewProps> = ({ showControls }) => {
|
||||
key={`audio-item-${idx}`}
|
||||
value={selectedAudioIndex === track.index}
|
||||
onValueChange={() => {
|
||||
if (audioIndex === track.index.toString()) return;
|
||||
if (selectedAudioIndex === track.index.toString()) return;
|
||||
setSelectedAudioIndex(track.index);
|
||||
ChangeTranscodingAudio(track.index);
|
||||
}}
|
||||
|
||||
@@ -1,53 +1,89 @@
|
||||
import { TrackInfo } from '@/modules/vlc-player';
|
||||
import { BaseItemDto, MediaSourceInfo } from '@jellyfin/sdk/lib/generated-client';
|
||||
import React, { createContext, useContext, useState, ReactNode, useEffect } from 'react';
|
||||
import { useControlContext } from './ControlContext';
|
||||
import { TrackInfo } from "@/modules/vlc-player";
|
||||
import {
|
||||
BaseItemDto,
|
||||
MediaSourceInfo,
|
||||
} from "@jellyfin/sdk/lib/generated-client";
|
||||
import React, {
|
||||
createContext,
|
||||
useContext,
|
||||
useState,
|
||||
ReactNode,
|
||||
useEffect,
|
||||
} from "react";
|
||||
import { useControlContext } from "./ControlContext";
|
||||
|
||||
interface VideoContextProps {
|
||||
audioTracks: TrackInfo[] | null;
|
||||
subtitleTracks: TrackInfo[] | null;
|
||||
setAudioTrack: ((index: number) => void) | undefined;
|
||||
setSubtitleTrack: ((index: number) => void) | undefined;
|
||||
setSubtitleURL: ((url: string, customName: string) => void) | undefined;
|
||||
audioTracks: TrackInfo[] | null;
|
||||
subtitleTracks: TrackInfo[] | null;
|
||||
setAudioTrack: ((index: number) => void) | undefined;
|
||||
setSubtitleTrack: ((index: number) => void) | undefined;
|
||||
setSubtitleURL: ((url: string, customName: string) => void) | undefined;
|
||||
}
|
||||
|
||||
const VideoContext = createContext<VideoContextProps | undefined>(undefined);
|
||||
|
||||
interface VideoProviderProps {
|
||||
children: ReactNode;
|
||||
getAudioTracks: (() => Promise<TrackInfo[] | null>) | (() => TrackInfo[]) | undefined;
|
||||
getSubtitleTracks: (() => Promise<TrackInfo[] | null>) | (() => TrackInfo[]) | undefined;
|
||||
getAudioTracks:
|
||||
| (() => Promise<TrackInfo[] | null>)
|
||||
| (() => TrackInfo[])
|
||||
| undefined;
|
||||
getSubtitleTracks:
|
||||
| (() => Promise<TrackInfo[] | null>)
|
||||
| (() => TrackInfo[])
|
||||
| undefined;
|
||||
setAudioTrack: ((index: number) => void) | undefined;
|
||||
setSubtitleTrack: ((index: number) => void) | undefined;
|
||||
setSubtitleURL: ((url: string, customName: string) => void) | undefined;
|
||||
}
|
||||
|
||||
export const VideoProvider: React.FC<VideoProviderProps> = ({ children, getSubtitleTracks, getAudioTracks, setSubtitleTrack, setSubtitleURL, setAudioTrack }) => {
|
||||
const [audioTracks, setAudioTracks] = useState<TrackInfo[] | null>(null);
|
||||
const [subtitleTracks, setSubtitleTracks] = useState<TrackInfo[] | null>(
|
||||
null
|
||||
);
|
||||
export const VideoProvider: React.FC<VideoProviderProps> = ({
|
||||
children,
|
||||
getSubtitleTracks,
|
||||
getAudioTracks,
|
||||
setSubtitleTrack,
|
||||
setSubtitleURL,
|
||||
setAudioTrack,
|
||||
}) => {
|
||||
const [audioTracks, setAudioTracks] = useState<TrackInfo[] | null>(null);
|
||||
const [subtitleTracks, setSubtitleTracks] = useState<TrackInfo[] | null>(
|
||||
null
|
||||
);
|
||||
|
||||
const ControlContext = useControlContext();
|
||||
const isVideoLoaded = ControlContext?.isVideoLoaded;
|
||||
const ControlContext = useControlContext();
|
||||
const isVideoLoaded = ControlContext?.isVideoLoaded;
|
||||
|
||||
useEffect(() => {
|
||||
const fetchTracks = async () => {
|
||||
if (getSubtitleTracks) {
|
||||
const subtitles = await getSubtitleTracks();
|
||||
console.log("Getting embeded subtitles...", subtitles);
|
||||
setSubtitleTracks(subtitles);
|
||||
}
|
||||
if (getAudioTracks) {
|
||||
const audio = await getAudioTracks();
|
||||
setAudioTracks(audio);
|
||||
}
|
||||
};
|
||||
fetchTracks();
|
||||
}, [isVideoLoaded, getAudioTracks, getSubtitleTracks]);
|
||||
useEffect(() => {
|
||||
const fetchTracks = async () => {
|
||||
if (
|
||||
getSubtitleTracks &&
|
||||
(subtitleTracks === null || subtitleTracks.length === 0)
|
||||
) {
|
||||
const subtitles = await getSubtitleTracks();
|
||||
console.log("Getting embeded subtitles...", subtitles);
|
||||
setSubtitleTracks(subtitles);
|
||||
}
|
||||
if (
|
||||
getAudioTracks &&
|
||||
(audioTracks === null || audioTracks.length === 0)
|
||||
) {
|
||||
const audio = await getAudioTracks();
|
||||
setAudioTracks(audio);
|
||||
}
|
||||
};
|
||||
fetchTracks();
|
||||
}, [isVideoLoaded, getAudioTracks, getSubtitleTracks]);
|
||||
|
||||
return (
|
||||
<VideoContext.Provider value={{ audioTracks, subtitleTracks, setSubtitleTrack, setSubtitleURL, setAudioTrack }}>
|
||||
<VideoContext.Provider
|
||||
value={{
|
||||
audioTracks,
|
||||
subtitleTracks,
|
||||
setSubtitleTrack,
|
||||
setSubtitleURL,
|
||||
setAudioTrack,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</VideoContext.Provider>
|
||||
);
|
||||
@@ -56,7 +92,7 @@ export const VideoProvider: React.FC<VideoProviderProps> = ({ children, getSubti
|
||||
export const useVideoContext = () => {
|
||||
const context = useContext(VideoContext);
|
||||
if (context === undefined) {
|
||||
throw new Error('useVideoContext must be used within a VideoProvider');
|
||||
throw new Error("useVideoContext must be used within a VideoProvider");
|
||||
}
|
||||
return context;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -580,10 +580,8 @@ extension VlcPlayerView: VLCMediaPlayerDelegate {
|
||||
"duration": player.media?.length.intValue ?? 0,
|
||||
"error": false,
|
||||
]
|
||||
// Playing and not transcoding, we can let it in no HLS issue.
|
||||
// We should also mark it as playing when the media is ready.
|
||||
// Fix HLS issue.
|
||||
if player.isPlaying && self.isMediaReady {
|
||||
|
||||
if player.isPlaying {
|
||||
stateInfo["isPlaying"] = true
|
||||
stateInfo["isBuffering"] = false
|
||||
stateInfo["state"] = "Playing"
|
||||
|
||||
Reference in New Issue
Block a user