mirror of
https://github.com/streamyfin/streamyfin.git
synced 2025-08-20 18:37:18 +02:00
wip
This commit is contained in:
@@ -49,6 +49,12 @@ import { VideoRef } from "react-native-video";
|
||||
import * as DropdownMenu from "zeego/dropdown-menu";
|
||||
import { Text } from "../common/Text";
|
||||
import { Loader } from "../Loader";
|
||||
import BottomSheet, {
|
||||
BottomSheetBackdrop,
|
||||
BottomSheetBackdropProps,
|
||||
BottomSheetModal,
|
||||
BottomSheetView,
|
||||
} from "@gorhom/bottom-sheet";
|
||||
|
||||
interface Props {
|
||||
item: BaseItemDto;
|
||||
@@ -391,6 +397,32 @@ export const Controls: React.FC<Props> = ({
|
||||
)[];
|
||||
}, [item, isVideoLoaded, subtitleTracks, mediaSource]);
|
||||
|
||||
/**
|
||||
* Bottom sheet
|
||||
*/
|
||||
const bottomSheetModalRef = useRef<BottomSheetModal>(null);
|
||||
|
||||
const handlePresentModalPress = useCallback(() => {
|
||||
bottomSheetModalRef.current?.present();
|
||||
}, []);
|
||||
|
||||
const handleSheetChanges = useCallback((index: number) => {}, []);
|
||||
|
||||
const closeModal = useCallback(() => {
|
||||
bottomSheetModalRef.current?.dismiss();
|
||||
}, []);
|
||||
|
||||
const renderBackdrop = useCallback(
|
||||
(props: BottomSheetBackdropProps) => (
|
||||
<BottomSheetBackdrop
|
||||
{...props}
|
||||
disappearsOnIndex={-1}
|
||||
appearsOnIndex={0}
|
||||
/>
|
||||
),
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
@@ -402,7 +434,58 @@ export const Controls: React.FC<Props> = ({
|
||||
height: "100%",
|
||||
}}
|
||||
>
|
||||
{/* <VideoDebugInfo playerRef={videoRef} /> */}
|
||||
<BottomSheetModal
|
||||
ref={bottomSheetModalRef}
|
||||
index={0}
|
||||
snapPoints={["50%"]}
|
||||
handleIndicatorStyle={{
|
||||
backgroundColor: "white",
|
||||
}}
|
||||
backgroundStyle={{
|
||||
backgroundColor: "#171717",
|
||||
}}
|
||||
onChange={handleSheetChanges}
|
||||
backdropComponent={renderBackdrop}
|
||||
>
|
||||
<BottomSheetView style={{ padding: 16 }}>
|
||||
<Text className="font-bold text-white mb-2 text-lg">
|
||||
Audio Tracks
|
||||
</Text>
|
||||
{audioTracks?.map((track, index) => (
|
||||
<TouchableOpacity
|
||||
key={`audio-${index}`}
|
||||
onPress={() => {
|
||||
setAudioTrack && setAudioTrack(track.index);
|
||||
closeModal();
|
||||
}}
|
||||
className="border border-neutral-700 rounded-lg p-4 mb-2 bg-neutral-800"
|
||||
>
|
||||
<Text style={{ color: "white" }}>
|
||||
{track.name} {track.language ? `(${track.language})` : null}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
|
||||
<Text className="font-bold text-white mb-2 text-lg">Subtitles</Text>
|
||||
{allSubtitleTracks?.map((sub, index) => (
|
||||
<TouchableOpacity
|
||||
key={`subtitle-${index}`}
|
||||
onPress={() => {
|
||||
if (sub.isExternal) {
|
||||
setSubtitleURL &&
|
||||
setSubtitleURL(api?.basePath + sub.deliveryUrl, sub.name);
|
||||
} else {
|
||||
setSubtitleTrack && setSubtitleTrack(sub.index);
|
||||
}
|
||||
closeModal();
|
||||
}}
|
||||
className="border border-neutral-700 rounded-lg p-4 mb-2 bg-neutral-800"
|
||||
>
|
||||
<Text style={{ color: "white" }}>{sub.name}</Text>
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
</BottomSheetView>
|
||||
</BottomSheetModal>
|
||||
<View
|
||||
style={{
|
||||
position: "absolute",
|
||||
@@ -411,14 +494,16 @@ export const Controls: React.FC<Props> = ({
|
||||
}}
|
||||
className="p-4"
|
||||
>
|
||||
<DropdownMenu.Root>
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
handlePresentModalPress();
|
||||
}}
|
||||
className="aspect-square flex flex-col bg-neutral-800/90 rounded-xl items-center justify-center p-2"
|
||||
>
|
||||
<Ionicons name="ellipsis-horizontal" size={24} color={"white"} />
|
||||
</TouchableOpacity>
|
||||
{/* <DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger>
|
||||
<TouchableOpacity
|
||||
onPress={() => console.log("open settings")}
|
||||
className="aspect-square flex flex-col bg-neutral-800/90 rounded-xl items-center justify-center p-2"
|
||||
>
|
||||
<Ionicons name="ellipsis-horizontal" size={24} color={"white"} />
|
||||
</TouchableOpacity>
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content
|
||||
loop={true}
|
||||
@@ -495,7 +580,7 @@ export const Controls: React.FC<Props> = ({
|
||||
</DropdownMenu.SubContent>
|
||||
</DropdownMenu.Sub>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
</DropdownMenu.Root> */}
|
||||
</View>
|
||||
|
||||
<View
|
||||
|
||||
@@ -11,6 +11,7 @@ import { getBackdropUrl } from "@/utils/jellyfin/image/getBackdropUrl";
|
||||
import { getAuthHeaders } from "@/utils/jellyfin/jellyfin";
|
||||
import { getStreamUrl } from "@/utils/jellyfin/media/getStreamUrl";
|
||||
import native from "@/utils/profiles/native";
|
||||
import android from "@/utils/profiles/android";
|
||||
import { secondsToTicks } from "@/utils/secondsToTicks";
|
||||
import { Api } from "@jellyfin/sdk";
|
||||
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
|
||||
@@ -22,17 +23,9 @@ 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 { Pressable, StatusBar, useWindowDimensions, View } from "react-native";
|
||||
import { SystemBars } from "react-native-edge-to-edge";
|
||||
import React, { useCallback, useMemo, useRef, useState } from "react";
|
||||
import { Pressable, useWindowDimensions, View } from "react-native";
|
||||
import { useSharedValue } from "react-native-reanimated";
|
||||
import { SafeAreaView } from "react-native-safe-area-context";
|
||||
import Video, {
|
||||
OnProgressData,
|
||||
SelectedTrack,
|
||||
@@ -140,7 +133,7 @@ const Player = () => {
|
||||
maxStreamingBitrate: bitrateValue,
|
||||
mediaSourceId: mediaSourceId,
|
||||
subtitleStreamIndex: subtitleIndex,
|
||||
deviceProfile: native,
|
||||
deviceProfile: android,
|
||||
});
|
||||
|
||||
if (!res) return null;
|
||||
@@ -383,6 +376,8 @@ const Player = () => {
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
zIndex: 0,
|
||||
}}
|
||||
>
|
||||
@@ -396,7 +391,9 @@ const Player = () => {
|
||||
}}
|
||||
resizeMode={ignoreSafeAreas ? "cover" : "contain"}
|
||||
onProgress={onProgress}
|
||||
onError={() => {}}
|
||||
onError={(e) => {
|
||||
console.error("Error playing video", e);
|
||||
}}
|
||||
onLoad={() => {
|
||||
if (firstTime.current === true) {
|
||||
play();
|
||||
@@ -468,7 +465,7 @@ const Player = () => {
|
||||
console.log("setAudioTrack ~", i);
|
||||
setSelectedAudioTrack({
|
||||
type: SelectedTrackType.INDEX,
|
||||
value: 10,
|
||||
value: i,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
|
||||
143
utils/profiles/android.js
Normal file
143
utils/profiles/android.js
Normal file
@@ -0,0 +1,143 @@
|
||||
/**
|
||||
* 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
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
import MediaTypes from "../../constants/MediaTypes";
|
||||
|
||||
/**
|
||||
* Device profile for Native video player
|
||||
*/
|
||||
export default {
|
||||
Name: "1. Native iOS Video Profile",
|
||||
MaxStaticBitrate: 100000000,
|
||||
MaxStreamingBitrate: 120000000,
|
||||
MusicStreamingTranscodingBitrate: 384000,
|
||||
CodecProfiles: [
|
||||
{
|
||||
Type: MediaTypes.Video,
|
||||
Codec: "h264,h265,hevc,mpeg4,divx,xvid,wmv,vc1,vp8,vp9,av1",
|
||||
},
|
||||
{
|
||||
Type: MediaTypes.Audio,
|
||||
Codec: "aac,mp3,flac,alac,opus,vorbis,pcm,wma",
|
||||
},
|
||||
],
|
||||
DirectPlayProfiles: [
|
||||
{
|
||||
Type: MediaTypes.Video,
|
||||
Container: "mp4,mkv,avi,mov,flv,ts,m2ts,webm,ogv,3gp",
|
||||
VideoCodec: "h264,h265,hevc,mpeg4,divx,xvid,wmv,vc1,vp8,vp9,av1",
|
||||
AudioCodec: "aac,mp3,flac,alac,opus,vorbis,wma",
|
||||
},
|
||||
{
|
||||
Type: MediaTypes.Audio,
|
||||
Container: "mp3,aac,flac,alac,wav,ogg,wma",
|
||||
AudioCodec: "mp3,aac,flac,alac,opus,vorbis,wma,pcm",
|
||||
},
|
||||
],
|
||||
TranscodingProfiles: [
|
||||
{
|
||||
Type: MediaTypes.Video,
|
||||
Context: "Streaming",
|
||||
Protocol: "hls",
|
||||
Container: "ts",
|
||||
VideoCodec: "h264",
|
||||
AudioCodec: "aac,mp3,ac3",
|
||||
MaxAudioChannels: "8",
|
||||
MinSegments: "2",
|
||||
BreakOnNonKeyFrames: true,
|
||||
},
|
||||
{
|
||||
Type: MediaTypes.Audio,
|
||||
Context: "Streaming",
|
||||
Protocol: "http",
|
||||
Container: "mp3",
|
||||
AudioCodec: "mp3",
|
||||
MaxAudioChannels: "2",
|
||||
},
|
||||
],
|
||||
ResponseProfiles: [
|
||||
{
|
||||
Container: "mkv",
|
||||
MimeType: "video/x-matroska",
|
||||
Type: MediaTypes.Video,
|
||||
},
|
||||
{
|
||||
Container: "mp4",
|
||||
MimeType: "video/mp4",
|
||||
Type: MediaTypes.Video,
|
||||
},
|
||||
],
|
||||
SubtitleProfiles: [
|
||||
{ Format: "srt", Method: "Embed" },
|
||||
{ Format: "srt", Method: "External" },
|
||||
{ Format: "srt", Method: "Encode" },
|
||||
{ Format: "ass", Method: "Embed" },
|
||||
{ Format: "ass", Method: "External" },
|
||||
{ Format: "ass", Method: "Encode" },
|
||||
{ Format: "ssa", Method: "Embed" },
|
||||
{ Format: "ssa", Method: "External" },
|
||||
{ Format: "ssa", Method: "Encode" },
|
||||
{ Format: "sub", Method: "Embed" },
|
||||
{ Format: "sub", Method: "External" },
|
||||
{ Format: "sub", Method: "Encode" },
|
||||
{ Format: "vtt", Method: "Embed" },
|
||||
{ Format: "vtt", Method: "External" },
|
||||
{ Format: "vtt", Method: "Encode" },
|
||||
{ Format: "ttml", Method: "Embed" },
|
||||
{ Format: "ttml", Method: "External" },
|
||||
{ Format: "ttml", Method: "Encode" },
|
||||
{ Format: "pgs", Method: "Embed" },
|
||||
{ Format: "pgs", Method: "External" },
|
||||
{ Format: "pgs", Method: "Encode" },
|
||||
{ Format: "dvdsub", Method: "Embed" },
|
||||
{ Format: "dvdsub", Method: "External" },
|
||||
{ Format: "dvdsub", Method: "Encode" },
|
||||
{ Format: "dvbsub", Method: "Embed" },
|
||||
{ Format: "dvbsub", Method: "External" },
|
||||
{ Format: "dvbsub", Method: "Encode" },
|
||||
{ Format: "xsub", Method: "Embed" },
|
||||
{ Format: "xsub", Method: "External" },
|
||||
{ Format: "xsub", Method: "Encode" },
|
||||
{ Format: "mov_text", Method: "Embed" },
|
||||
{ Format: "mov_text", Method: "External" },
|
||||
{ Format: "mov_text", Method: "Encode" },
|
||||
{ Format: "scc", Method: "Embed" },
|
||||
{ Format: "scc", Method: "External" },
|
||||
{ Format: "scc", Method: "Encode" },
|
||||
{ Format: "smi", Method: "Embed" },
|
||||
{ Format: "smi", Method: "External" },
|
||||
{ Format: "smi", Method: "Encode" },
|
||||
{ Format: "teletext", Method: "Embed" },
|
||||
{ Format: "teletext", Method: "External" },
|
||||
{ Format: "teletext", Method: "Encode" },
|
||||
{ Format: "microdvd", Method: "Embed" },
|
||||
{ Format: "microdvd", Method: "External" },
|
||||
{ Format: "microdvd", Method: "Encode" },
|
||||
{ Format: "mpl2", Method: "Embed" },
|
||||
{ Format: "mpl2", Method: "External" },
|
||||
{ Format: "mpl2", Method: "Encode" },
|
||||
{ Format: "pjs", Method: "Embed" },
|
||||
{ Format: "pjs", Method: "External" },
|
||||
{ Format: "pjs", Method: "Encode" },
|
||||
{ Format: "realtext", Method: "Embed" },
|
||||
{ Format: "realtext", Method: "External" },
|
||||
{ Format: "realtext", Method: "Encode" },
|
||||
{ Format: "stl", Method: "Embed" },
|
||||
{ Format: "stl", Method: "External" },
|
||||
{ Format: "stl", Method: "Encode" },
|
||||
{ Format: "subrip", Method: "Embed" },
|
||||
{ Format: "subrip", Method: "External" },
|
||||
{ Format: "subrip", Method: "Encode" },
|
||||
{ Format: "subviewer", Method: "Embed" },
|
||||
{ Format: "subviewer", Method: "External" },
|
||||
{ Format: "subviewer", Method: "Encode" },
|
||||
{ Format: "text", Method: "Embed" },
|
||||
{ Format: "text", Method: "External" },
|
||||
{ Format: "text", Method: "Encode" },
|
||||
{ Format: "vplayer", Method: "Embed" },
|
||||
{ Format: "vplayer", Method: "External" },
|
||||
{ Format: "vplayer", Method: "Encode" },
|
||||
],
|
||||
};
|
||||
Reference in New Issue
Block a user