mirror of
https://github.com/Jellify-Music/App.git
synced 2026-04-27 22:20:00 -05:00
Add Seeking ability
clean up some backend player handlers scaling fixes
This commit is contained in:
@@ -63,7 +63,7 @@ export default function Artist(props: ArtistProps): React.JSX.Element {
|
||||
<Card
|
||||
caption={album.Name}
|
||||
subCaption={album.ProductionYear?.toString()}
|
||||
width={width / columns}
|
||||
width={(width / 1.25) / columns}
|
||||
cornered
|
||||
itemId={album.Id!}
|
||||
onPress={() => {
|
||||
|
||||
@@ -21,7 +21,7 @@ import Icon from "../Global/helpers/icon";
|
||||
export default function Player({ navigation }: { navigation : NavigationHelpers<ParamListBase, BottomTabNavigationEventMap> }): React.JSX.Element {
|
||||
|
||||
const { apiClient } = useApiClientContext();
|
||||
const { nowPlaying, progress } = usePlayerContext();
|
||||
const { nowPlaying, progress, useSeekTo } = usePlayerContext();
|
||||
|
||||
const { width } = useSafeAreaFrame();
|
||||
|
||||
@@ -63,8 +63,8 @@ export default function Player({ navigation }: { navigation : NavigationHelpers<
|
||||
imageStyle={{
|
||||
position: "relative",
|
||||
alignSelf: "center",
|
||||
width: width / 1.25,
|
||||
height: width / 1.25,
|
||||
width: width / 1.5,
|
||||
height: width / 1.5,
|
||||
borderRadius: 2
|
||||
}}
|
||||
/>
|
||||
@@ -92,12 +92,19 @@ export default function Player({ navigation }: { navigation : NavigationHelpers<
|
||||
</XStack>
|
||||
</XStack>
|
||||
|
||||
<XStack justifyContent="center" margin={15}>
|
||||
<XStack justifyContent="center" marginHorizontal={20}>
|
||||
{/* playback progress goes here */}
|
||||
<HorizontalSlider
|
||||
value={progress!.position}
|
||||
max={progress!.duration}
|
||||
width={width / 1.25}
|
||||
width={width / 1.5}
|
||||
props={{
|
||||
onValueChange: (value) => {
|
||||
const position = value[0];
|
||||
|
||||
useSeekTo.mutate(position);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
</XStack>
|
||||
|
||||
@@ -50,8 +50,8 @@ export function Miniplayer({ navigation }: { navigation : NavigationHelpers<Para
|
||||
}
|
||||
imageStyle={{
|
||||
position: "relative",
|
||||
width: width / 6,
|
||||
height: width / 6,
|
||||
width: width / 8,
|
||||
height: width / 8,
|
||||
borderRadius: 2,
|
||||
}}
|
||||
/>
|
||||
|
||||
+17
-30
@@ -3,14 +3,15 @@ import { JellifyTrack } from "../types/JellifyTrack";
|
||||
import { PlaystateApi } from "@jellyfin/sdk/lib/generated-client/api/playstate-api";
|
||||
import { convertSecondsToRunTimeTicks } from "@/helpers/runtimeticks";
|
||||
|
||||
export async function handlePlaybackState(sessionId: string, playstateApi: PlaystateApi, track: JellifyTrack, state: State) {
|
||||
export async function handlePlaybackState(sessionId: string, playstateApi: PlaystateApi, track: JellifyTrack, state: State, progress: Progress) {
|
||||
switch (state) {
|
||||
case (State.Playing) : {
|
||||
console.debug("Report playback started")
|
||||
await playstateApi.reportPlaybackStart({
|
||||
playbackStartInfo: {
|
||||
SessionId: sessionId,
|
||||
ItemId: track.ItemId
|
||||
ItemId: track.ItemId,
|
||||
PositionTicks: convertSecondsToRunTimeTicks(progress.position)
|
||||
}
|
||||
});
|
||||
break;
|
||||
@@ -23,9 +24,10 @@ export async function handlePlaybackState(sessionId: string, playstateApi: Plays
|
||||
await playstateApi.reportPlaybackStopped({
|
||||
playbackStopInfo: {
|
||||
SessionId: sessionId,
|
||||
ItemId: track.ItemId
|
||||
ItemId: track.ItemId,
|
||||
PositionTicks: convertSecondsToRunTimeTicks(progress.position)
|
||||
}
|
||||
})
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -35,28 +37,6 @@ export async function handlePlaybackState(sessionId: string, playstateApi: Plays
|
||||
}
|
||||
}
|
||||
|
||||
export async function handlePlaybackStopped(sessionId: string, playstateApi: PlaystateApi, track: JellifyTrack) {
|
||||
console.debug("Stopping playback for session");
|
||||
|
||||
await playstateApi.reportPlaybackStopped({
|
||||
playbackStopInfo: {
|
||||
SessionId: sessionId,
|
||||
ItemId: track.ItemId
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export async function handlePlaybackStarted(sessionId: string, playstateApi: PlaystateApi, track: JellifyTrack) {
|
||||
console.debug("Starting playback for session");
|
||||
|
||||
await playstateApi.reportPlaybackStart({
|
||||
playbackStartInfo: {
|
||||
SessionId: sessionId,
|
||||
ItemId: track.ItemId
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export async function handlePlaybackProgressUpdated(sessionId: string, playstateApi: PlaystateApi, track: JellifyTrack, progress: Progress) {
|
||||
if (Math.floor(progress.duration - progress.position) === 5) {
|
||||
console.debug("Track finished, scrobbling...");
|
||||
@@ -66,8 +46,15 @@ export async function handlePlaybackProgressUpdated(sessionId: string, playstate
|
||||
ItemId: track.ItemId,
|
||||
PositionTicks: convertSecondsToRunTimeTicks(track.duration!)
|
||||
}
|
||||
})
|
||||
}
|
||||
else
|
||||
return;
|
||||
});
|
||||
} else {
|
||||
console.debug("Reporting playback position");
|
||||
await playstateApi.reportPlaybackProgress({
|
||||
playbackProgressInfo: {
|
||||
SessionId: sessionId,
|
||||
ItemId: track.ItemId,
|
||||
PositionTicks: convertSecondsToRunTimeTicks(progress.position)
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
+42
-6
@@ -8,7 +8,7 @@ import _ from "lodash";
|
||||
import { buildNewQueue } from "./helpers/queue";
|
||||
import { useApiClientContext } from "../components/jellyfin-api-provider";
|
||||
import { getPlaystateApi } from "@jellyfin/sdk/lib/utils/api";
|
||||
import { handlePlaybackProgressUpdated, handlePlaybackStarted, handlePlaybackState, handlePlaybackStopped } from "./handlers";
|
||||
import { handlePlaybackProgressUpdated, handlePlaybackState } from "./handlers";
|
||||
import { useSetupPlayer } from "@/player/hooks";
|
||||
import { UPDATE_INTERVAL } from "./config";
|
||||
import { sleep } from "@/helpers/sleep";
|
||||
@@ -17,7 +17,8 @@ import { QueueMutation } from "./interfaces";
|
||||
import { mapDtoToTrack } from "@/helpers/mappings";
|
||||
import { QueuingType } from "@/enums/queuing-type";
|
||||
import { trigger } from "react-native-haptic-feedback";
|
||||
import { pause } from "react-native-track-player/lib/src/trackPlayer";
|
||||
import { pause, seekTo } from "react-native-track-player/lib/src/trackPlayer";
|
||||
import { convertRunTimeTicksToSeconds } from "@/helpers/runtimeticks";
|
||||
|
||||
interface PlayerContext {
|
||||
showPlayer: boolean;
|
||||
@@ -27,6 +28,7 @@ interface PlayerContext {
|
||||
nowPlaying: JellifyTrack | undefined;
|
||||
queue: JellifyTrack[];
|
||||
useTogglePlayback: UseMutationResult<void, Error, number | undefined, unknown>;
|
||||
useSeekTo: UseMutationResult<void, Error, number, unknown>;
|
||||
playNewQueue: UseMutationResult<void, Error, QueueMutation, unknown>;
|
||||
playbackState: State | undefined;
|
||||
progress: Progress | undefined;
|
||||
@@ -86,7 +88,20 @@ const PlayerContextInitializer = () => {
|
||||
else
|
||||
await play(index);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
const useSeekTo = useMutation({
|
||||
mutationFn: async (position: number) => {
|
||||
trigger('impactLight');
|
||||
await seekTo(position);
|
||||
|
||||
await handlePlaybackProgressUpdated(sessionId, playStateApi, nowPlaying!, {
|
||||
buffered: 0,
|
||||
position,
|
||||
duration: convertRunTimeTicksToSeconds(nowPlaying!.duration!)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const playNewQueue = useMutation({
|
||||
mutationFn: async (mutation: QueueMutation) => {
|
||||
@@ -104,6 +119,8 @@ const PlayerContextInitializer = () => {
|
||||
|
||||
//#region RNTP Setup
|
||||
const isPlayerReady = useSetupPlayer().isSuccess;
|
||||
const { state: playbackState } = usePlaybackState();
|
||||
const progress = useProgress(UPDATE_INTERVAL);
|
||||
|
||||
useTrackPlayerEvents([
|
||||
Event.PlaybackProgressUpdated,
|
||||
@@ -113,7 +130,7 @@ const PlayerContextInitializer = () => {
|
||||
switch (event.type) {
|
||||
|
||||
case (Event.PlaybackState) : {
|
||||
handlePlaybackState(sessionId, playStateApi, await TrackPlayer.getActiveTrack() as JellifyTrack, event.state);
|
||||
handlePlaybackState(sessionId, playStateApi, await TrackPlayer.getActiveTrack() as JellifyTrack, event.state, progress);
|
||||
break;
|
||||
}
|
||||
case (Event.PlaybackProgressUpdated) : {
|
||||
@@ -135,8 +152,6 @@ const PlayerContextInitializer = () => {
|
||||
}
|
||||
})
|
||||
|
||||
const { state: playbackState } = usePlaybackState();
|
||||
const progress = useProgress(UPDATE_INTERVAL);
|
||||
|
||||
useEffect(() => {
|
||||
if (!showMiniplayer)
|
||||
@@ -164,6 +179,7 @@ const PlayerContextInitializer = () => {
|
||||
nowPlaying,
|
||||
queue,
|
||||
useTogglePlayback,
|
||||
useSeekTo,
|
||||
playNewQueue,
|
||||
playbackState,
|
||||
progress,
|
||||
@@ -197,6 +213,24 @@ export const PlayerContext = createContext<PlayerContext>({
|
||||
failureReason: null,
|
||||
submittedAt: 0
|
||||
},
|
||||
useSeekTo: {
|
||||
mutate: () => {},
|
||||
mutateAsync: async () => {},
|
||||
data: undefined,
|
||||
error: null,
|
||||
variables: undefined,
|
||||
isError: false,
|
||||
isIdle: true,
|
||||
isPaused: false,
|
||||
isPending: false,
|
||||
isSuccess: false,
|
||||
status: "idle",
|
||||
reset: () => {},
|
||||
context: {},
|
||||
failureCount: 0,
|
||||
failureReason: null,
|
||||
submittedAt: 0
|
||||
},
|
||||
playNewQueue: {
|
||||
mutate: () => {},
|
||||
mutateAsync: async () => {},
|
||||
@@ -229,6 +263,7 @@ export const PlayerProvider: ({ children }: { children: ReactNode }) => React.JS
|
||||
nowPlaying,
|
||||
queue,
|
||||
useTogglePlayback,
|
||||
useSeekTo,
|
||||
playNewQueue,
|
||||
playbackState,
|
||||
progress
|
||||
@@ -242,6 +277,7 @@ export const PlayerProvider: ({ children }: { children: ReactNode }) => React.JS
|
||||
nowPlaying,
|
||||
queue,
|
||||
useTogglePlayback,
|
||||
useSeekTo,
|
||||
playNewQueue,
|
||||
playbackState,
|
||||
progress
|
||||
|
||||
@@ -22,4 +22,8 @@ export async function PlaybackService() {
|
||||
TrackPlayer.addEventListener(Event.RemotePrevious, async () => {
|
||||
await TrackPlayer.skipToPrevious();
|
||||
});
|
||||
|
||||
TrackPlayer.addEventListener(Event.RemoteSeek, async (event) => {
|
||||
await TrackPlayer.seekTo(event.position);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user