mirror of
https://github.com/streamyfin/streamyfin.git
synced 2025-08-20 18:37:18 +02:00
Fixed trick play for VLC
This commit is contained in:
@@ -240,14 +240,18 @@ export default function page() {
|
||||
|
||||
const { currentTime } = data.nativeEvent;
|
||||
|
||||
if (isBuffering) {
|
||||
setIsBuffering(false);
|
||||
}
|
||||
|
||||
progress.value = currentTime;
|
||||
const currentTimeInTicks = msToTicks(currentTime);
|
||||
|
||||
console.log("onProgress ~", {
|
||||
currentTime,
|
||||
currentTimeInTicks,
|
||||
isPlaying,
|
||||
});
|
||||
// console.log("onProgress ~", {
|
||||
// currentTime,
|
||||
// currentTimeInTicks,
|
||||
// isPlaying,
|
||||
// });
|
||||
|
||||
await getPlaystateApi(api).onPlaybackProgress({
|
||||
itemId: item.Id,
|
||||
@@ -259,7 +263,6 @@ export default function page() {
|
||||
playMethod: stream?.url.includes("m3u8") ? "Transcode" : "DirectStream",
|
||||
playSessionId: stream.sessionId,
|
||||
});
|
||||
console.log("Progress", currentTime);
|
||||
},
|
||||
[item?.Id, isPlaying, api, isPlaybackStopped]
|
||||
);
|
||||
|
||||
@@ -12,6 +12,7 @@ import { getDefaultPlaySettings } from "@/utils/jellyfin/getDefaultPlaySettings"
|
||||
import { writeToLog } from "@/utils/log";
|
||||
import {
|
||||
formatTimeString,
|
||||
msToTicks,
|
||||
secondsToMs,
|
||||
ticksToMs,
|
||||
ticksToSeconds,
|
||||
@@ -234,8 +235,16 @@ export const Controls: React.FC<Props> = ({
|
||||
[isVlc]
|
||||
);
|
||||
|
||||
const [time, setTime] = useState({ minutes: 0, seconds: 0 });
|
||||
|
||||
const handleSliderChange = (value: number) => {
|
||||
calculateTrickplayUrl(value);
|
||||
const progressInTicks = isVlc ? msToTicks(value) : value;
|
||||
calculateTrickplayUrl(progressInTicks);
|
||||
|
||||
const progressInSeconds = Math.floor(progressInTicks / 10000000);
|
||||
const minutes = Math.floor(progressInSeconds / 60);
|
||||
const seconds = progressInSeconds % 60;
|
||||
setTime({ minutes, seconds });
|
||||
};
|
||||
|
||||
const handleSliderStart = useCallback(() => {
|
||||
@@ -351,7 +360,6 @@ export const Controls: React.FC<Props> = ({
|
||||
const uniqueExternalSubs = externalSubs.filter(
|
||||
(sub) => !embeddedSubNames.has(sub.name)
|
||||
);
|
||||
|
||||
// Combine embedded and unique external subs
|
||||
return [...embeddedSubs, ...uniqueExternalSubs] as (
|
||||
| EmbeddedSubtitle
|
||||
@@ -359,6 +367,11 @@ export const Controls: React.FC<Props> = ({
|
||||
)[];
|
||||
}, [item, isVideoLoaded, subtitleTracks, mediaSource]);
|
||||
|
||||
// useEffect(() => {
|
||||
|
||||
// }, [allSubtitleTracks, setSubtitleTrack]);
|
||||
const [subtitleTrackSet, setSubtitleTrackSet] = useState(false);
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
@@ -715,6 +728,21 @@ export const Controls: React.FC<Props> = ({
|
||||
source={{ uri: url }}
|
||||
contentFit="cover"
|
||||
/>
|
||||
<Text
|
||||
style={{
|
||||
position: "absolute",
|
||||
bottom: 5,
|
||||
left: 5,
|
||||
color: "white",
|
||||
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
||||
padding: 5,
|
||||
borderRadius: 5,
|
||||
}}
|
||||
>
|
||||
{`${time.minutes}:${
|
||||
time.seconds < 10 ? `0${time.seconds}` : time.seconds
|
||||
}`}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// hooks/useTrickplay.ts
|
||||
|
||||
import { apiAtom } from "@/providers/JellyfinProvider";
|
||||
import { ticksToMs } from "@/utils/time";
|
||||
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
|
||||
import { useAtom } from "jotai";
|
||||
import { useCallback, useMemo, useRef, useState } from "react";
|
||||
@@ -57,6 +58,7 @@ export const useTrickplay = (item: BaseItemDto, enabled = true) => {
|
||||
: null;
|
||||
}, [item, enabled]);
|
||||
|
||||
// Takes in ticks.
|
||||
const calculateTrickplayUrl = useCallback(
|
||||
(progress: number) => {
|
||||
if (!enabled) {
|
||||
@@ -74,28 +76,33 @@ export const useTrickplay = (item: BaseItemDto, enabled = true) => {
|
||||
}
|
||||
|
||||
const { data, resolution } = trickplayInfo;
|
||||
const { Interval, TileWidth, TileHeight } = data;
|
||||
const { Interval, TileWidth, TileHeight, Width, Height } = data;
|
||||
|
||||
if (!Interval || !TileWidth || !TileHeight || !resolution) {
|
||||
if (
|
||||
!Interval ||
|
||||
!TileWidth ||
|
||||
!TileHeight ||
|
||||
!resolution ||
|
||||
!Width ||
|
||||
!Height
|
||||
) {
|
||||
throw new Error("Invalid trickplay data");
|
||||
}
|
||||
|
||||
const currentSecond = Math.max(0, Math.floor(progress / 10000000));
|
||||
const currentTimeMs = Math.max(0, ticksToMs(progress));
|
||||
const currentTile = Math.floor(currentTimeMs / Interval);
|
||||
|
||||
const cols = TileWidth;
|
||||
const rows = TileHeight;
|
||||
const imagesPerTile = cols * rows;
|
||||
const imageIndex = Math.floor(currentSecond / (Interval / 1000));
|
||||
const tileIndex = Math.floor(imageIndex / imagesPerTile);
|
||||
const tileSize = TileWidth * TileHeight;
|
||||
const tileOffset = currentTile % tileSize;
|
||||
const index = Math.floor(currentTile / tileSize);
|
||||
|
||||
const positionInTile = imageIndex % imagesPerTile;
|
||||
const rowInTile = Math.floor(positionInTile / cols);
|
||||
const colInTile = positionInTile % cols;
|
||||
const tileOffsetX = tileOffset % TileWidth;
|
||||
const tileOffsetY = Math.floor(tileOffset / TileWidth);
|
||||
|
||||
const newTrickPlayUrl = {
|
||||
x: rowInTile,
|
||||
y: colInTile,
|
||||
url: `${api.basePath}/Videos/${item.Id}/Trickplay/${resolution}/${tileIndex}.jpg?api_key=${api.accessToken}`,
|
||||
x: tileOffsetX,
|
||||
y: tileOffsetY,
|
||||
url: `${api.basePath}/Videos/${item.Id}/Trickplay/${resolution}/${index}.jpg?api_key=${api.accessToken}`,
|
||||
};
|
||||
|
||||
setTrickPlayUrl(newTrickPlayUrl);
|
||||
|
||||
@@ -16,17 +16,17 @@ public class VlcPlayerModule: Module {
|
||||
}
|
||||
}
|
||||
|
||||
// Prop("muted") { (view: VlcPlayerView, muted: Bool) in
|
||||
// view.setMuted(muted)
|
||||
// }
|
||||
// Prop("muted") { (view: VlcPlayerView, muted: Bool) in
|
||||
// view.setMuted(muted)
|
||||
// }
|
||||
|
||||
// Prop("volume") { (view: VlcPlayerView, volume: Int) in
|
||||
// view.setVolume(volume)
|
||||
// }
|
||||
// Prop("volume") { (view: VlcPlayerView, volume: Int) in
|
||||
// view.setVolume(volume)
|
||||
// }
|
||||
|
||||
// Prop("videoAspectRatio") { (view: VlcPlayerView, ratio: String) in
|
||||
// view.setVideoAspectRatio(ratio)
|
||||
// }
|
||||
// Prop("videoAspectRatio") { (view: VlcPlayerView, ratio: String) in
|
||||
// view.setVideoAspectRatio(ratio)
|
||||
// }
|
||||
|
||||
Events(
|
||||
"onPlaybackStateChanged",
|
||||
@@ -69,13 +69,13 @@ public class VlcPlayerModule: Module {
|
||||
return view.getSubtitleTracks()
|
||||
}
|
||||
|
||||
// AsyncFunction("setVideoCropGeometry") { (view: VlcPlayerView, geometry: String?) in
|
||||
// view.setVideoCropGeometry(geometry)
|
||||
// }
|
||||
// AsyncFunction("setVideoCropGeometry") { (view: VlcPlayerView, geometry: String?) in
|
||||
// view.setVideoCropGeometry(geometry)
|
||||
// }
|
||||
|
||||
// AsyncFunction("getVideoCropGeometry") { (view: VlcPlayerView) -> String? in
|
||||
// return view.getVideoCropGeometry()
|
||||
// }
|
||||
// AsyncFunction("getVideoCropGeometry") { (view: VlcPlayerView) -> String? in
|
||||
// return view.getVideoCropGeometry()
|
||||
// }
|
||||
|
||||
AsyncFunction("setSubtitleURL") {
|
||||
(view: VlcPlayerView, url: String, name: String) in
|
||||
|
||||
Reference in New Issue
Block a user