Skip to content

Instantly share code, notes, and snippets.

@TSB1999
Created August 18, 2025 13:12
Show Gist options
  • Select an option

  • Save TSB1999/08c589c15a1f147412df66672d40c99d to your computer and use it in GitHub Desktop.

Select an option

Save TSB1999/08c589c15a1f147412df66672d40c99d to your computer and use it in GitHub Desktop.
The next Spotify won’t be a music platform — it’ll be a better player for YouTube.
import React, {
createContext,
useState,
useRef,
useEffect,
useContext,
} from 'react';
import WebView from 'react-native-webview';
import {Trak} from '../types/Trak';
import {useSelector} from 'react-redux';
import {useEffectAsync} from '../hooks/useEffectAsync';
import {Modalize} from 'react-native-modalize';
import {useAsyncStorage} from '../hooks/useAsyncStorage';
import {asyncStorageKeys} from '../core/asyncStorageKeys';
import {handleRetrieveRadio} from '../app/utils/retreiveRadio';
import PushNotificationIOS from '@react-native-community/push-notification-ios';
import {nowPlaying} from '../app/utils/nowPlaying';
import {useTrakstar} from '../hooks/useTrakstar';
import {HeaderContext} from '../stores/context/HeaderProvider';
import {SPINBROKER, TRAKLIST} from '../app/utils/meta';
export interface Track {
track: Trak;
trace: Trace;
buffer: Trak[];
protocolId?: string;
uuid?: string;
theme?: string;
}
export interface Trace {
type:
| 'track'
| 'album'
| 'artist'
| 'playlist'
| 'category'
| 'user'
| 'request'
| 'edit'
| 'web';
source: string;
}
interface Player {
index: number;
radio: Trak[] | null;
radioIndex: number;
bufferIndex: number;
queue: Track[];
isLive: boolean;
setIsLive: React.Dispatch<React.SetStateAction<boolean>>;
isPaused: boolean;
isShuffle: boolean;
isPrimaryPlayer: boolean;
isPrimaryPlayerInitialized: boolean;
isSecondaryPlayerInitialized: boolean;
isPrimaryWebViewLoaded: boolean;
isSecondaryWebViewLoaded: boolean;
currentTrack: Track | null;
buffer1: React.RefObject<WebView>;
buffer2: React.RefObject<WebView>;
handleStop: () => void;
handlePause: () => void;
handleRepeatOptions: () => void;
handlePlayPrevious: () => void;
handleBuffer: (options: {isPrimary: boolean}) => void;
handlePlayNext: () => void;
handleQueue: (queue: Track[]) => void;
handleSeek: (seekTo: number) => void;
handlePlayNow: (track: Track) => void;
handleQueueNext: (queue: Track[]) => void;
handleNext: (options: {isPrimary: boolean}) => void;
handleForcePiP: () => void;
handleShuffleOptions: () => void;
handleReleaseKeys: () => void;
setRadio: React.Dispatch<React.SetStateAction<boolean>>;
setIsPaused: React.Dispatch<React.SetStateAction<boolean>>;
setPrimaryWebViewLoaded: React.Dispatch<React.SetStateAction<boolean>>;
setSecondaryWebViewLoaded: React.Dispatch<React.SetStateAction<boolean>>;
setSecondaryPlayerInitialized: React.Dispatch<React.SetStateAction<boolean>>;
setPrimaryPlayerInitialized: React.Dispatch<React.SetStateAction<boolean>>;
setBufferIndex: React.Dispatch<React.SetStateAction<number>>;
setRadioIndex: React.Dispatch<React.SetStateAction<number>>;
primaryRunnerKey: string | null;
setPrimaryRunnerKey: React.Dispatch<React.SetStateAction<string | null>>;
secondaryRunnerKey: string | null;
setSecondaryRunnerKey: React.Dispatch<React.SetStateAction<string | null>>;
primaryPreloadKey: string | null;
setPrimaryPreloadKey: React.Dispatch<React.SetStateAction<string | null>>;
secondaryPreloadKey: string | null;
setSecondaryPreloadKey: React.Dispatch<React.SetStateAction<string | null>>;
repeatOptions: 'repeat' | 'repeat-once' | 'off';
setRepeatOptions: React.Dispatch<
React.SetStateAction<'repeat' | 'repeat-once' | 'off'>
>;
isPlayingFromQueue: boolean;
isPlayingFromQueueLoop: boolean;
setIsPlayingFromQueue: React.Dispatch<React.SetStateAction<boolean>>;
setIsPlayingFromQueueLoop: React.Dispatch<React.SetStateAction<boolean>>;
setIndex: React.Dispatch<React.SetStateAction<Number>>;
setQueue: React.Dispatch<React.SetStateAction<Track[]>>;
initializingPiP: boolean;
setInitializingPiP: React.Dispatch<React.SetStateAction<boolean>>;
}
export const PlayerContext = createContext<Player>({
radioIndex: 0,
radio: null,
index: -1,
bufferIndex: -1,
queue: [],
isLive: true,
isPaused: false,
isShuffle: false,
isPrimaryWebViewLoaded: false,
isSecondaryWebViewLoaded: false,
isPrimaryPlayerInitialized: false,
isSecondaryPlayerInitialized: false,
isPrimaryPlayer: true,
currentTrack: null,
buffer1: {current: null},
buffer2: {current: null},
primaryRunnerKey: null,
setPrimaryRunnerKey: () => {},
secondaryRunnerKey: null,
setSecondaryRunnerKey: () => {},
primaryPreloadKey: null,
setPrimaryPreloadKey: () => {},
secondaryPreloadKey: null,
setSecondaryPreloadKey: () => {},
setIsLive: () => {},
setRadio: () => {},
setRepeatOptions: () => {},
setIsPlayingFromQueue: () => {},
setPrimaryWebViewLoaded: () => {},
setSecondaryWebViewLoaded: () => {},
setPrimaryPlayerInitialized: () => {},
setSecondaryPlayerInitialized: () => {},
setIsPlayingFromQueueLoop: () => {},
setBufferIndex: () => {},
setIsPaused: () => {},
setRadioIndex: () => {},
handleStop: () => {},
handlePause: () => {},
handlePlayPrevious: () => {},
handleBuffer: () => {},
handleQueue: () => {},
handlePlayNow: () => {},
handleQueueNext: () => {},
handleNext: () => {},
handlePlayNext: () => {},
handleShuffleOptions: () => {},
handleReleaseKeys: () => {},
handleSeek: (seekTo: number) => {},
repeatOptions: 'off',
handleRepeatOptions: () => {},
isPlayingFromQueue: false,
isPlayingFromQueueLoop: false,
setIndex: () => {},
setQueue: () => {},
initializingPiP: false,
setInitializingPiP: () => {},
handleForcePiP: () => {},
});
export const PlayerProvider = ({children}: {children: React.ReactChild}) => {
const {setIsLoading} = useContext(HeaderContext);
const initializedPlayer = useSelector(
(state: any) => state.streaming.initializedPlayer,
);
const [index, setIndex] = useState(initializedPlayer.index ?? -1);
const [bufferIndex, setBufferIndex] = useState(
initializedPlayer.bufferIndex ?? -1,
);
const [isPaused, setIsPaused] = useState(true);
const [queue, setQueue] = useState<Track[]>(initializedPlayer.queue ?? []);
const [isPrimaryPlayer, setIsPrimaryPlayer] = useState(true);
const [initializingPiP, setInitializingPiP] = useState(false);
const [isLive, setIsLive] = useState(true);
const [currentTrack, setCurrentTrack] = useState<Track | null>(
initializedPlayer.currentTrack ?? null,
);
const [isShuffle, setIsShuffle] = useState(false);
const [radio, setRadio] = useState(initializedPlayer.radio);
const [radioIndex, setRadioIndex] = useState(initializedPlayer.radioId);
const [isPrimaryWebViewLoaded, setPrimaryWebViewLoaded] = useState(true);
const [isSecondaryWebViewLoaded, setSecondaryWebViewLoaded] = useState(false);
const [isPrimaryPlayerInitialized, setPrimaryPlayerInitialized] =
useState(false);
const [isSecondaryPlayerInitialized, setSecondaryPlayerInitialized] =
useState(false);
const [repeatOptions, setRepeatOptions] = useState<
'repeat-once' | 'repeat' | 'off'
>(initializedPlayer.repeatOptions ?? 'repeat');
const [primaryRunnerKey, setPrimaryRunnerKey] = useState<string | null>(null);
const [secondaryRunnerKey, setSecondaryRunnerKey] = useState<string | null>(
null,
);
const [primaryPreloadKey, setPrimaryPreloadKey] = useState<string | null>(
null,
);
const [secondaryPreloadKey, setSecondaryPreloadKey] = useState<string | null>(
null,
);
const [isPlayingFromQueue, setIsPlayingFromQueue] = useState<boolean>(
initializedPlayer.isPlayingFromQueue,
);
const [isPlayingFromQueueLoop, setIsPlayingFromQueueLoop] = useState<boolean>(
initializedPlayer.isPlayingFromQueueLoop,
);
const {handleStoreData} = useAsyncStorage();
const networkToken = useSelector(
(state: any) => state.keys.jukerstone.networkToken,
);
useEffectAsync(async () => {
if (radioIndex > radio.length - 4) {
const newRadio = await handleRetrieveRadio({token: networkToken});
if (newRadio.radio) {
setRadio([...radio, ...newRadio.radio]);
PushNotificationIOS.addNotificationRequest({
id: '1',
title: 'TrakStar™ Music: Radio',
body: 'Generating new tracks for ya!',
});
if (!queue.length && currentTrack) {
currentTrack.buffer = [...currentTrack.buffer, ...newRadio.radio];
} else if (queue.length) {
const oldBuffer = queue[index + 1].buffer;
const newBuffer = [...oldBuffer, ...newRadio.radio];
queue[index + 1].buffer = newBuffer;
}
}
}
await handleStoreData(asyncStorageKeys.RADIO_ID, radioIndex);
}, [radioIndex]);
useEffectAsync(async () => {
await handleStoreData(asyncStorageKeys.INDEX, index);
await handleStoreData(asyncStorageKeys.BUFFER_ID, bufferIndex);
await handleStoreData(asyncStorageKeys.QUEUE, queue);
await handleStoreData(asyncStorageKeys.REPEAT_OPTIONS, repeatOptions);
await handleStoreData(
asyncStorageKeys.IS_PLAYING_FROM_QUEUE,
isPlayingFromQueue,
);
await handleStoreData(
asyncStorageKeys.IS_PLAYING_FROM_QUEUE_LOOP,
isPlayingFromQueueLoop,
);
}, [index, bufferIndex, queue, currentTrack]);
const buffer1 = useRef(null);
const buffer2 = useRef(null);
const handleStop = async () => {
setQueue([]);
setIndex(-1);
setBufferIndex(-1);
setCurrentTrack(null);
setIsPaused(true);
setRadio(null);
setIsPlayingFromQueue(false);
setInitializingPiP(false);
setIsLoading(false);
isPrimaryPlayer && setPrimaryWebViewLoaded(false);
!isPrimaryPlayer && setSecondaryWebViewLoaded(false);
isPrimaryPlayer && setPrimaryPlayerInitialized(false);
!isPrimaryPlayer && setSecondaryPlayerInitialized(false);
await handleStoreData(asyncStorageKeys.RADIO_ID, -1);
await handleStoreData(asyncStorageKeys.RADIO, null);
await handleStoreData(asyncStorageKeys.INDEX, -1);
await handleStoreData(asyncStorageKeys.BUFFER_ID, -1);
await handleStoreData(asyncStorageKeys.QUEUE, []);
await handleStoreData(asyncStorageKeys.CURRENT_TRACK, null);
};
const handleBuffer = ({isPrimary}: {isPrimary: boolean}) => {
setIsPlayingFromQueue(false);
setBufferIndex(prevIndex => {
if (!queue.length && currentTrack) {
if (prevIndex + 1 < currentTrack.buffer.length) {
if (currentTrack.buffer[bufferIndex + 1]?.isRadio) {
setRadioIndex(radioIndex + 1);
}
return prevIndex + 1;
} else {
if (repeatOptions == 'repeat') {
setIsPlayingFromQueueLoop(true);
} else {
handleStop();
}
return -1;
}
} else if (queue.length) {
if (prevIndex + 1 < queue[index].buffer.length) {
if (queue[index].buffer[bufferIndex + 1]?.isRadio) {
setRadioIndex(radioIndex + 1);
}
return prevIndex + 1;
} else {
if (repeatOptions == 'repeat') {
setIsPlayingFromQueueLoop(true);
} else {
handleStop();
}
return -1;
}
} else {
if (repeatOptions == 'repeat') {
setIsPlayingFromQueueLoop(true);
} else {
handleStop();
}
return -1;
}
});
setIsPrimaryPlayer(isPrimary);
};
const handleForcePiP = () => {
const ref = isPrimaryPlayer ? buffer1 : buffer2;
ref.current?.injectJavaScript(`
if (!window.trakStarVideo) {
window.trakStarVideo = document.getElementsByTagName('video')[0];
}
if (window.trakStarVideo) {
window.trakStarVideo.requestPictureInPicture().then(() => {
const message = {
eventType: 'enablePiP',
data: 'PiP initiated successfully.'
};
window.ReactNativeWebView.postMessage(JSON.stringify(message));
}).catch(error => {
const message = {
eventType: 'enablePiP',
data: 'PiP initiation failed: ' + error.message
};
window.ReactNativeWebView.postMessage(JSON.stringify(message));
});
}
true;
`);
};
const handlePause = async () => {
if (!currentTrack) {
if (radio && radio.length) {
setBufferIndex(-1);
setCurrentTrack({
buffer: radio.slice(1),
trace: {
source: `juk:11:${radio[0].trak.youtube.url.split('=')[1]}`,
type: 'track',
},
track: radio[0],
});
}
// else {
// const {radio, radioId} = await handleRetrieveRadio({
// token: networkToken,
// });
// setBufferIndex(-1);
// setRadioIndex(radioId);
// setCurrentTrack({
// buffer: radio.slice(1),
// trace: {
// source: `juk:11:${radio[0].trak.youtube.url.split('=')[1]}`,
// type: 'track',
// },
// track: radio[0],
// });
// }
}
if (isPrimaryPlayer && isPaused) {
buffer1.current?.injectJavaScript(`
if (window.trakStarVideo) {
window.trakStarVideo.play();
}
true;
`);
} else if (isPrimaryPlayer && !isPaused) {
buffer1.current?.injectJavaScript(`
if (window.trakStarVideo) {
window.trakStarVideo.pause();
}
true;
`);
} else if (!isPrimaryPlayer && !isPaused) {
buffer2.current?.injectJavaScript(`
if (window.trakStarVideo) {
window.trakStarVideo.pause();
}
true;
`);
} else if (!isPrimaryPlayer && isPaused) {
buffer2.current?.injectJavaScript(`
if (window.trakStarVideo) {
window.trakStarVideo.play();
}
true;
`);
}
};
const handlePlayNow = (track: Track) => {
if (repeatOptions == 'repeat-once') {
setRepeatOptions('off');
}
setPrimaryPreloadKey(null);
setPrimaryRunnerKey(null);
setSecondaryRunnerKey(null);
setSecondaryPreloadKey(null);
setCurrentTrack(track);
setIsLoading(false);
};
const handleQueue = (queue: Track[]) => {
setQueue(prevQueue => [...prevQueue, ...queue]);
};
const handleQueueNext = (queue: Track[]) => {
setQueue(prevQueue => [...queue, ...prevQueue]);
};
const handleNext = ({isPrimary}: {isPrimary: boolean}) => {
setIsPlayingFromQueue(true);
setIndex(prevIndex => {
if (prevIndex < queue.length - 1) {
return prevIndex + 1;
} else {
handleStop();
return prevIndex;
}
});
setIsPrimaryPlayer(isPrimary);
};
const handleShuffleOptions = () => {
setIsShuffle(!isShuffle);
};
const handlePlayPrevious = () => {
alert('coming soon');
};
const handlePlayNext = () => {
setIsLoading(true);
if (currentTrack && !queue.length) {
setIsPlayingFromQueue(false);
setIsPlayingFromQueueLoop(false);
if (bufferIndex + 1 < currentTrack.buffer.length) {
// not at end of buffer
handlePlayNow({
track: currentTrack.buffer[bufferIndex + 1],
buffer: currentTrack.buffer,
trace: {
source: `juk:11:${
currentTrack.buffer[bufferIndex + 1].trak.youtube.url.split(
'=',
)[1]
}`,
type: 'track',
},
});
setBufferIndex(bufferIndex + 1);
} else {
// buffer end
if (repeatOptions == 'repeat') {
setBufferIndex(-1);
handlePlayNow({
track: currentTrack.buffer[0],
buffer: currentTrack.buffer,
trace: {
source: `juk:11:${
currentTrack.track.trak.youtube.url.split('=')[1]
}`,
type: 'track',
},
});
} else {
handleStop();
}
}
} else if (currentTrack && queue.length) {
if (index + 1 < queue.length) {
setIsPlayingFromQueue(true);
setIsPlayingFromQueueLoop(false);
// not at end of queue
handlePlayNow({
track: queue[index + 1].track,
buffer: currentTrack.buffer,
trace: {
source: `juk:11:${
currentTrack.track.trak.youtube.url.split('=')[1]
}`,
type: 'track',
},
});
setIndex(index + 1);
} else {
// index end
if (bufferIndex + 1 < currentTrack.buffer.length) {
setIsPlayingFromQueue(false);
setIsPlayingFromQueueLoop(false);
// if not at buffer end
handlePlayNow({
track: currentTrack.buffer[bufferIndex + 1],
buffer: queue[index].buffer,
trace: {
source: `juk:11:${
currentTrack.track.trak.youtube.url.split('=')[1]
}`,
type: 'track',
},
});
setBufferIndex(bufferIndex + 1);
} else {
// buffer end
if (repeatOptions == 'repeat') {
setIsPlayingFromQueue(true);
setIsPlayingFromQueueLoop(true);
setBufferIndex(-1);
handlePlayNow({
track: currentTrack.buffer[0],
buffer: currentTrack.buffer,
trace: {
source: `juk:11:${
currentTrack.track.trak.youtube.url.split('=')[1]
}`,
type: 'track',
},
});
} else {
handleStop();
}
}
}
}
};
const handleReleaseKeys = () => {
if (isPrimaryPlayer && isSecondaryWebViewLoaded && secondaryPreloadKey) {
setSecondaryWebViewLoaded(false);
setSecondaryPlayerInitialized(false);
setSecondaryPreloadKey(null);
// setTRXUrl1(null);
} else if (
!isPrimaryPlayer &&
isPrimaryWebViewLoaded &&
primaryPreloadKey
) {
// setTRXUrl2(null);
setPrimaryWebViewLoaded(false);
setPrimaryPlayerInitialized(false);
setPrimaryPreloadKey(null);
}
};
const handleSeek = (seekTo: number) => {
if (isPrimaryPlayer) {
buffer1.current.injectJavaScript(`
if (window.trakStarVideo) {
window.trakStarVideo.currentTime = ${seekTo};
}
true;
`);
} else {
buffer2.current.injectJavaScript(`
if (window.trakStarVideo) {
window.trakStarVideo.currentTime = ${seekTo};
}
true;
`);
}
};
const handleRepeatOptions = () => {
if (repeatOptions == 'off') {
setRepeatOptions('repeat');
} else if (repeatOptions == 'repeat') {
setRepeatOptions('repeat-once');
} else setRepeatOptions('off');
};
return (
<PlayerContext.Provider
value={{
index,
bufferIndex,
setRadioIndex,
queue,
buffer1,
buffer2,
isPaused,
handleStop,
handleNext,
handleQueue,
handlePause,
currentTrack,
handlePlayNow,
handleQueueNext,
isPrimaryPlayer,
handlePlayNext,
handleSeek,
handleBuffer,
handleShuffleOptions,
isShuffle,
setIsPaused,
radio,
radioIndex,
isPrimaryWebViewLoaded,
setPrimaryWebViewLoaded,
isSecondaryWebViewLoaded,
setSecondaryWebViewLoaded,
isPrimaryPlayerInitialized,
setPrimaryPlayerInitialized,
isSecondaryPlayerInitialized,
setSecondaryPlayerInitialized,
setRadio,
handlePlayPrevious,
setBufferIndex,
primaryRunnerKey,
setPrimaryRunnerKey,
secondaryRunnerKey,
setSecondaryRunnerKey,
primaryPreloadKey,
setPrimaryPreloadKey,
secondaryPreloadKey,
setSecondaryPreloadKey,
repeatOptions,
handleRepeatOptions,
setRepeatOptions,
isPlayingFromQueue,
setIsPlayingFromQueue,
setIndex,
isPlayingFromQueueLoop,
setIsPlayingFromQueueLoop,
setQueue,
initializingPiP,
setInitializingPiP,
handleForcePiP,
handleReleaseKeys,
isLive,
setIsLive,
}}>
{children}
</PlayerContext.Provider>
);
};
export interface Progress {
duration: number;
currentTime: number;
percent: number;
}
interface ProgressContextType {
modalRef: React.RefObject<Modalize>;
progress: Progress | null;
setProgress: React.Dispatch<React.SetStateAction<Progress | null>>;
progressSnapshot: () => Progress | null;
}
export const ProgressContext = createContext<ProgressContextType>({
progress: null,
setProgress: () => {},
modalRef: {current: null}, // migrate away
progressSnapshot: () => null,
});
export const ProgressProvider = ({children}: {children: React.ReactChild}) => {
const [progress, setProgress] = useState<Progress | null>(null);
const modalRef = useRef(null);
const progressSnapshot = () => {
return progress;
};
return (
<ProgressContext.Provider
value={{
progress,
setProgress,
modalRef,
progressSnapshot,
}}>
{children}
</ProgressContext.Provider>
);
};
import {useContext} from 'react';
import WebView from 'react-native-webview';
import {StyleSheet, View} from 'react-native';
import {useJukePod} from './useJukePod';
import {PlayerContext, Track} from './JukePodProvider';
import {Dimensions} from 'react-native';
export const JukePodSDK = ({
handleStream,
}: {
handleStream: ({
TRACK,
IS_LIVE,
PROTOCOL_ID,
}: {
TRACK: Track;
IS_LIVE: boolean;
PROTOCOL_ID: string;
}) => void;
}) => {
const {
picture1,
picture2,
handleMessage,
isPrimaryPlayer,
fetchVideoTimeJS,
isPrimaryPlayerInitialized,
isSecondaryPlayerInitialized,
} = useJukePod({handleStream});
const context = useContext(PlayerContext);
const {buffer1, buffer2} = context;
return (
<View>
<WebView
ref={buffer1}
style={styles.container}
source={
{
uri: isPrimaryPlayerInitialized ? picture1 : null,
} as {uri: string}
}
onMessage={event => handleMessage(event, true)}
injectedJavaScript={fetchVideoTimeJS(isPrimaryPlayer)}
allowsInlineMediaPlayback={true}
/>
<WebView
ref={buffer2}
style={styles.container}
source={
{
uri: isSecondaryPlayerInitialized ? picture2 : null,
} as {uri: string}
}
onMessage={event => handleMessage(event, false)}
injectedJavaScript={fetchVideoTimeJS(!isPrimaryPlayer)}
allowsInlineMediaPlayback={true}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
height: 240,
width: Dimensions.get('screen').width,
margin: 10,
},
});
import {useContext, useEffect, useState, useRef, act} from 'react';
import {
PlayerContext,
Progress,
ProgressContext,
Track,
} from './JukePodProvider';
import {useAppState} from '@react-native-community/hooks';
import {WebViewMessageEvent} from 'react-native-webview';
import PushNotificationIOS from '@react-native-community/push-notification-ios';
import {useEffectAsync} from '../hooks/useEffectAsync';
import {useAsyncStorage} from '../hooks/useAsyncStorage';
import {asyncStorageKeys} from '../core/asyncStorageKeys';
import uuid from 'react-native-uuid';
import {nowPlaying} from '../app/utils/nowPlaying';
import {TRAKLIST} from '../app/utils/meta';
import {API} from '../api';
export const useJukePod = ({
handleStream,
}: {
handleStream: ({
TRACK,
IS_LIVE,
PROTOCOL_ID,
}: {
TRACK: Track;
IS_LIVE: boolean;
PROTOCOL_ID: string;
}) => void;
}) => {
const [hasStreamed, setHasStreamed] = useState(false);
const {handleStoreData} = useAsyncStorage();
const intervalRef = useRef<any>(null);
const {
queue,
index,
buffer1,
buffer2,
isPaused,
handleStop,
handleNext,
handleSeek,
setIsPaused,
bufferIndex,
currentTrack,
handleBuffer,
repeatOptions,
isPrimaryPlayer,
setRepeatOptions,
isPrimaryWebViewLoaded,
setPrimaryWebViewLoaded,
isSecondaryWebViewLoaded,
setSecondaryWebViewLoaded,
setPrimaryPlayerInitialized,
setSecondaryPlayerInitialized,
isPrimaryPlayerInitialized,
isSecondaryPlayerInitialized,
primaryRunnerKey,
setPrimaryRunnerKey,
secondaryRunnerKey,
setSecondaryRunnerKey,
primaryPreloadKey,
setPrimaryPreloadKey,
secondaryPreloadKey,
setSecondaryPreloadKey,
handlePlayNext,
initializingPiP,
setInitializingPiP,
isLive,
} = useContext(PlayerContext);
const {setProgress} = useContext(ProgressContext);
const appState = useAppState();
const [isPiPEnabled, setIsPiPEnabled] = useState(false);
const [isProcessing, setIsProcessing] = useState(false);
const [trxUrl1, setTRXUrl1] = useState<string | null>(null);
const [trxUrl2, setTRXUrl2] = useState<string | null>(null);
const [userActionPause, setUserActionPause] = useState<boolean>(false);
const {track, trace, protocolId} = nowPlaying();
const startErrorCheckInterval = (isRunner: boolean) => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
}
const isRunnerBuffer = isPrimaryPlayer ? buffer1.current : buffer2.current;
const isPreloadBuffer = isPrimaryPlayer ? buffer2.current : buffer1.current;
intervalRef.current = setInterval(() => {
const ref = isRunner ? isRunnerBuffer : isPreloadBuffer;
ref?.injectJavaScript(`
if (!window.trakStarVideo) {
window.trakStarVideo = document.getElementsByTagName('video')[0];
}
if (window.trakStarVideo && window.trakStarVideo.readyState < 3) {
// Video is not playable, trigger error
window.ReactNativeWebView.postMessage(JSON.stringify({
eventType: 'videoError',
data: '${isRunner}'
}));
}
`);
}, 4000); // Check every 2 seconds
};
const inactiveTime = useRef(0);
const backgroundTime = useRef(0);
const previousAppState = useRef(appState);
useEffect(() => {
if (appState === 'inactive') {
inactiveTime.current = new Date();
}
if (appState === 'background') {
backgroundTime.current = new Date();
const timeDiff = backgroundTime.current - inactiveTime.current;
console.log(
`Time difference between inactive and background: ${timeDiff}ms`,
);
if (TRAKLIST) {
PushNotificationIOS.addNotificationRequest({
id: 'traklist-bk',
title: 'Please return to the Stage Manager',
body: 'Stage Manager allows you to multitask effectively. Please return to avoid issues.',
});
} else if (timeDiff < 10 && !isPaused) {
PushNotificationIOS.addNotificationRequest({
id: '1',
title: 'TrakStar™ Music: Screen Locked',
body: 'Playback may pause when your screen is locked. To avoid this, minimise the app before locking the screen.',
});
if (isPrimaryPlayer) {
buffer1.current?.injectJavaScript(`
if (window.trakStarVideo) {
window.trakStarVideo.play();
window.trakStarVideo.currentTime = window.trakStarVideo.currentTime;
}
true;
`);
} else {
buffer2.current?.injectJavaScript(`
if (window.trakStarVideo) {
window.trakStarVideo.play();
window.trakStarVideo.currentTime = window.trakStarVideo.currentTime;
}
true;
`);
}
} else if (timeDiff > 100 && !isPaused) {
PushNotificationIOS.addNotificationRequest({
id: '0',
title: 'TrakStar™ Music: Background Play',
body: initializingPiP
? 'Please allow the Picture in Picture module to load before attempting background play.'
: 'Playback may be interrupted by other media. Reopen the app to resume if needed.',
});
}
inactiveTime.current = 0;
backgroundTime.current = 0;
}
if (appState === 'active') {
inactiveTime.current = 0;
backgroundTime.current = 0;
}
}, [appState]);
useEffect(() => {
if (TRAKLIST) return;
if (appState === 'active' || appState === 'background') {
if (isPrimaryPlayer) {
if (appState === 'background' && !isPiPEnabled) {
buffer1.current?.injectJavaScript(`
if (window.trakStarVideo) {
window.trakStarVideo.play();
};
true;
`);
} else if (
appState === 'active' &&
previousAppState.current == 'background' &&
!isPiPEnabled &&
currentTrack
) {
setInitializingPiP(true);
}
setTRXUrl2(null);
setSecondaryWebViewLoaded(false);
setSecondaryPlayerInitialized(false);
setSecondaryPreloadKey(null);
} else {
if (appState === 'background' && !isPiPEnabled) {
buffer2.current?.injectJavaScript(`
if (window.trakStarVideo) {
window.trakStarVideo.play();
};
true;
`);
} else if (
appState === 'active' &&
previousAppState.current == 'background' &&
!isPiPEnabled &&
currentTrack
) {
setInitializingPiP(true);
}
setTRXUrl1(null);
setPrimaryWebViewLoaded(false);
setPrimaryPlayerInitialized(false);
setPrimaryPreloadKey(null);
}
if (isPrimaryPlayerInitialized || isSecondaryPlayerInitialized) {
const ref = isPrimaryPlayer ? buffer1.current : buffer2.current;
setTimeout(() => {
ref?.injectJavaScript(`
if (!window.trakStarVideo) {
window.trakStarVideo = document.getElementsByTagName('video')[0];
}
if (window.trakStarVideo) {
window.trakStarVideo.requestPictureInPicture().then(() => {
const message = {
eventType: 'enablePiP',
data: 'PiP initiated successfully.'
};
window.ReactNativeWebView.postMessage(JSON.stringify(message));
}).catch(error => {
const message = {
eventType: 'enablePiP',
data: 'PiP initiation failed: ' + error.message
};
window.ReactNativeWebView.postMessage(JSON.stringify(message));
});
} else {
const message = {
eventType: 'enablePiP',
data: 'No video element found.'
};
window.ReactNativeWebView.postMessage(JSON.stringify(message));
}
true;
`);
}, 500);
}
}
previousAppState.current = appState;
}, [appState]);
useEffectAsync(async () => {
setHasStreamed(false);
if (!currentTrack || !trace) {
return;
}
setInitializingPiP(true);
if (isPrimaryPlayer) {
setTRXUrl2(null);
setSecondaryWebViewLoaded(false);
setSecondaryPlayerInitialized(false);
setPrimaryPlayerInitialized(true);
} else {
setTRXUrl1(null);
setPrimaryWebViewLoaded(false);
setPrimaryPlayerInitialized(false);
setSecondaryPlayerInitialized(true);
}
const key = uuid.v4();
if (isPrimaryPlayer) {
setPrimaryRunnerKey(String(key));
const id = `${
currentTrack.track.trak.youtube.url.split('=')[1]
}?playsinline=1&fs=0&key=${key}`;
const url = API({id}).jukerstone.security.youtube;
setTRXUrl1(url);
} else {
setSecondaryRunnerKey(String(key));
const id = `${
currentTrack.track.trak.youtube.url.split('=')[1]
}?playsinline=1&fs=0&key=${key}`;
const url = API({id}).jukerstone.security.youtube;
setTRXUrl2(url);
}
await handleStoreData(asyncStorageKeys.CURRENT_TRACK, currentTrack);
setTimeout(() => startErrorCheckInterval(true), 1500);
}, [currentTrack]);
useEffectAsync(async () => {
if (isPrimaryPlayerInitialized) setSecondaryPlayerInitialized(true);
if (isSecondaryPlayerInitialized) setPrimaryPlayerInitialized(true);
const storedTrack: Track = {
track: track!,
buffer: currentTrack?.buffer ?? [],
trace: currentTrack!.trace ?? {
source: '',
type: '',
},
};
// alert(currentTrack.trace.source);
await handleStoreData(
asyncStorageKeys.CURRENT_TRACK,
currentTrack ? storedTrack : null,
);
if (!isPrimaryPlayer && appState === 'active') {
if (TRAKLIST) {
buffer2.current?.injectJavaScript(`
if (!window.trakStarVideo) {
window.trakStarVideo = document.getElementsByTagName('video')[0];
}
if (window.trakStarVideo) {
window.trakStarVideo.currentTime = window.trakStarVideo.currentTime;
window.trakStarVideo.muted = false;
window.trakStarVideo.play();
}
true;
`);
} else {
buffer2.current?.injectJavaScript(`
if (!window.trakStarVideo) {
window.trakStarVideo = document.getElementsByTagName('video')[0];
}
if (window.trakStarVideo) {
window.trakStarVideo.requestPictureInPicture().then(() => {
const message = {
eventType: 'enablePiP',
data: 'PiP initiated successfully.'
};
window.ReactNativeWebView.postMessage(JSON.stringify(message));
}).catch(error => {
const message = {
eventType: 'enablePiP',
data: 'PiP initiation failed: ' + error.message
};
window.ReactNativeWebView.postMessage(JSON.stringify(message));
});
window.trakStarVideo.currentTime = window.trakStarVideo.currentTime;
window.trakStarVideo.muted = false;
window.trakStarVideo.play();
}
true;
`);
}
} else if (!isPrimaryPlayer && appState !== 'active') {
setIsPiPEnabled(false);
buffer2.current?.injectJavaScript(`
if (!window.trakStarVideo) {
window.trakStarVideo = document.getElementsByTagName('video')[0];
}
if (window.trakStarVideo) {
window.trakStarVideo.currentTime = window.trakStarVideo.currentTime;
window.trakStarVideo.muted = false;
window.trakStarVideo.play();
}
true;
`);
} else if (isPrimaryPlayer && appState === 'active') {
if (TRAKLIST) {
buffer1.current?.injectJavaScript(`
if (!window.trakStarVideo) {
window.trakStarVideo = document.getElementsByTagName('video')[0];
}
if (window.trakStarVideo) {
window.trakStarVideo.currentTime = window.trakStarVideo.currentTime;
window.trakStarVideo.muted = false;
window.trakStarVideo.play();
}
true;
`);
} else {
buffer1.current?.injectJavaScript(`
if (!window.trakStarVideo) {
window.trakStarVideo = document.getElementsByTagName('video')[0];
}
if (window.trakStarVideo) {
window.trakStarVideo.requestPictureInPicture().then(() => {
const message = {
eventType: 'enablePiP',
data: 'PiP initiated successfully.'
};
window.ReactNativeWebView.postMessage(JSON.stringify(message));
}).catch(error => {
const message = {
eventType: 'enablePiP',
data: 'PiP initiation failed: ' + error.message
};
window.ReactNativeWebView.postMessage(JSON.stringify(message));
});
window.trakStarVideo.currentTime = window.trakStarVideo.currentTime;
window.trakStarVideo.muted = false;
window.trakStarVideo.play();
}
true;
`);
}
} else if (isPrimaryPlayer && appState !== 'active') {
setIsPiPEnabled(false);
buffer1.current?.injectJavaScript(`
if (!window.trakStarVideo) {
window.trakStarVideo = document.getElementsByTagName('video')[0];
}
if (window.trakStarVideo) {
window.trakStarVideo.currentTime = window.trakStarVideo.currentTime;
window.trakStarVideo.muted = false;
window.trakStarVideo.play();
}
true;
`);
}
}, [isPrimaryPlayer]);
useEffect(() => {
if (isPrimaryPlayer) {
setTRXUrl2(null);
setSecondaryWebViewLoaded(false);
setSecondaryPlayerInitialized(false);
setSecondaryPreloadKey(null);
setSecondaryRunnerKey(null);
} else {
setTRXUrl1(null);
setPrimaryWebViewLoaded(false);
setPrimaryPlayerInitialized(false);
setPrimaryPreloadKey(null);
setPrimaryRunnerKey(null);
}
}, [queue]);
useEffect(() => {
if (repeatOptions == 'repeat') {
if (isPrimaryPlayer) {
setSecondaryPreloadKey(null);
setSecondaryRunnerKey(null);
} else {
setPrimaryPreloadKey(null);
setPrimaryRunnerKey(null);
}
}
}, [repeatOptions]);
const fetchVideoTimeJS = (active: boolean) => `
if (!window.trakStarVideo) {
window.trakStarVideo = document.getElementsByTagName('video')[0];
var urlParams = new URLSearchParams(window.location.search);
var requestId = urlParams.get('key');
window.trakStarVideo.addEventListener('canplay', () => {
window.ReactNativeWebView.postMessage(JSON.stringify({
eventType: 'videoReady',
data: {
ready: true,
requestId: requestId // Send the requestId back in the message
}
}));
clearInterval(window.errorCheckInterval); // Stop the error checking
});
window.trakStarVideo.addEventListener('enterpictureinpicture', function() {
window.ReactNativeWebView.postMessage(JSON.stringify({
eventType: 'enteredPiP',
data: 'Picture-in-Picture mode entered'
}));
});
window.trakStarVideo.addEventListener('leavepictureinpicture', function() {
window.ReactNativeWebView.postMessage(JSON.stringify({
eventType: 'exitedPiP',
data: 'Picture-in-Picture mode exited'
}));
});
window.trakStarVideo.addEventListener('ended', function() {
window.ReactNativeWebView.postMessage(JSON.stringify({
eventType: 'videoEnded',
data: 100
}));
});
window.trakStarVideo.addEventListener('pause', function() {
window.ReactNativeWebView.postMessage(JSON.stringify({
eventType: 'videoPaused',
data: true
}));
});
window.trakStarVideo.addEventListener('play', function() {
window.ReactNativeWebView.postMessage(JSON.stringify({
eventType: 'videoPlaying',
data: true
}));
});
let lastUpdateTime = 0;
window.trakStarVideo.addEventListener('timeupdate', () => {
const now = Date.now();
// Throttle updates to once per second
if (now - lastUpdateTime > 750) {
window.ReactNativeWebView.postMessage(JSON.stringify({
eventType: 'videoCurrentTime',
data: {
currentTime: window.trakStarVideo.currentTime,
duration: window.trakStarVideo.duration,
percent: (window.trakStarVideo.currentTime / window.trakStarVideo.duration)
}
}));
lastUpdateTime = now;
}
});
window.trakStarVideo.addEventListener('error', function() {
window.ReactNativeWebView.postMessage(JSON.stringify({
eventType: 'videoError',
data: 'An error occurred while trying to load the video.'
}));
});
window.trakStarVideo.preload = 'auto'
window.trakStarVideo.muted = ${!active};
if (${active}) {
window.trakStarVideo.play();
} else {
window.trakStarVideo.pause();
};
true;
}
`;
const handleMessage = async (
event: WebViewMessageEvent,
isPrimaryMessage: boolean,
) => {
const message = JSON.parse(event.nativeEvent.data);
switch (message.eventType) {
case 'videoReady':
if (intervalRef.current) {
clearInterval(intervalRef.current); // Stop error check interval if video is ready
}
const requestId = message.data.requestId;
if (isPrimaryPlayer) {
if (requestId === secondaryPreloadKey)
setSecondaryWebViewLoaded(true);
} else {
if (requestId === primaryPreloadKey) setPrimaryWebViewLoaded(true);
}
if (appState === 'active') {
if (isPrimaryPlayer && isPrimaryMessage) {
buffer1.current?.injectJavaScript(`
if (!window.trakStarVideo) {
window.trakStarVideo = document.getElementsByTagName('video')[0];
}
var urlParams = new URLSearchParams(window.location.search);
var requestId = urlParams.get('key');
if (window.trakStarVideo) {
if (window.trakStarVideo) {
window.trakStarVideo.play();
window.trakStarVideo.currentTime = window.trakStarVideo.currentTime;
};
window.trakStarVideo.requestPictureInPicture().then(() => {
const message = {
eventType: 'enablePiP',
data: 'PiP initiated successfully.',
key: requestId
};
window.ReactNativeWebView.postMessage(JSON.stringify(message));
}).catch(error => {
const message = {
eventType: 'enablePiP',
data: 'PiP initiation failed: ' + error.message
};
window.ReactNativeWebView.postMessage(JSON.stringify(message));
});
}
true;
`);
} else if (!isPrimaryPlayer && !isPrimaryMessage) {
buffer2.current?.injectJavaScript(`
if (!window.trakStarVideo) {
window.trakStarVideo = document.getElementsByTagName('video')[0];
}
if (window.trakStarVideo) {
if (window.trakStarVideo) {
window.trakStarVideo.play();
window.trakStarVideo.currentTime = window.trakStarVideo.currentTime;
};
window.trakStarVideo.requestPictureInPicture().then(() => {
const message = {
eventType: 'enablePiP',
data: 'PiP initiated successfully.'
};
window.ReactNativeWebView.postMessage(JSON.stringify(message));
}).catch(error => {
const message = {
eventType: 'enablePiP',
data: 'PiP initiation failed: ' + error.message
};
window.ReactNativeWebView.postMessage(JSON.stringify(message));
});
}
true;
`);
}
}
break;
case 'enteredPiP':
setIsPiPEnabled(true);
break;
case 'exitedPiP':
setIsPiPEnabled(false);
if (isPrimaryPlayer && appState === 'active') {
buffer1.current?.injectJavaScript(`
if (!window.trakStarVideo) {
window.trakStarVideo = document.getElementsByTagName('video')[0];
}
if (window.trakStarVideo) {
window.trakStarVideo.requestPictureInPicture().then(() => {
const message = {
eventType: 'enablePiP',
data: 'PiP initiated successfully.'
};
window.ReactNativeWebView.postMessage(JSON.stringify(message));
}).catch(error => {
const message = {
eventType: 'enablePiP',
data: 'PiP initiation failed: ' + error.message
};
window.ReactNativeWebView.postMessage(JSON.stringify(message));
});
window.trakStarVideo.currentTime = window.trakStarVideo.currentTime;
window.trakStarVideo.muted = false;
window.trakStarVideo.play();
// Use requestAnimationFrame to trigger pause if userActionPause is true
if (${userActionPause}) {
requestAnimationFrame(() => window.trakStarVideo.pause());
}
}
true;
`);
} else if (!isPrimaryPlayer && appState === 'active') {
buffer2.current?.injectJavaScript(`
if (!window.trakStarVideo) {
window.trakStarVideo = document.getElementsByTagName('video')[0];
}
if (window.trakStarVideo) {
window.trakStarVideo.requestPictureInPicture().then(() => {
const message = {
eventType: 'enablePiP',
data: 'PiP initiated successfully.'
};
window.ReactNativeWebView.postMessage(JSON.stringify(message));
}).catch(error => {
const message = {
eventType: 'enablePiP',
data: 'PiP initiation failed: ' + error.message
};
window.ReactNativeWebView.postMessage(JSON.stringify(message));
});
window.trakStarVideo.currentTime = window.trakStarVideo.currentTime;
window.trakStarVideo.muted = false;
window.trakStarVideo.play();
// Use requestAnimationFrame to trigger pause if userActionPause is true
if (${userActionPause}) {
requestAnimationFrame(() => window.trakStarVideo.pause());
}
}
true;
`);
} else if (isPrimaryPlayer && appState === 'background') {
buffer1.current?.injectJavaScript(`
if (!window.trakStarVideo) {
window.trakStarVideo = document.getElementsByTagName('video')[0];
}
if (window.trakStarVideo) {
window.trakStarVideo.currentTime = window.trakStarVideo.currentTime;
window.trakStarVideo.muted = false;
window.trakStarVideo.play();
// Use requestAnimationFrame to trigger pause if userActionPause is true
if (${userActionPause}) {
requestAnimationFrame(() => window.trakStarVideo.pause());
}
}
true;
`);
} else if (!isPrimaryPlayer && appState === 'background') {
buffer2.current?.injectJavaScript(`
if (!window.trakStarVideo) {
window.trakStarVideo = document.getElementsByTagName('video')[0];
}
if (window.trakStarVideo) {
window.trakStarVideo.currentTime = window.trakStarVideo.currentTime;
window.trakStarVideo.muted = false;
window.trakStarVideo.play();
// Use requestAnimationFrame to trigger pause if userActionPause is true
if (${userActionPause}) {
requestAnimationFrame(() => window.trakStarVideo.pause());
}
}
true;
`);
}
break;
case 'remotePause':
setUserActionPause(true);
if (isPrimaryPlayer) {
buffer1.current?.injectJavaScript(`
if (document.pictureInPictureElement) {
document.exitPictureInPicture().then(() => {
const message = {
eventType: 'exitPiP',
data: 'Exited PiP successfully.'
};
window.ReactNativeWebView.postMessage(JSON.stringify(message));
}).catch(error => {
const message = {
eventType: 'exitPiP',
data: 'Failed to exit PiP: ' + error.message
};
window.ReactNativeWebView.postMessage(JSON.stringify(message));
});
} else {
if (window.trakStarVideo) {
window.trakStarVideo.pause();
}
};
true;
`);
} else {
buffer2.current?.injectJavaScript(`
if (document.pictureInPictureElement) {
document.exitPictureInPicture().then(() => {
const message = {
eventType: 'exitPiP',
data: 'Exited PiP successfully.'
};
window.ReactNativeWebView.postMessage(JSON.stringify(message));
}).catch(error => {
const message = {
eventType: 'exitPiP',
data: 'Failed to exit PiP: ' + error.message
};
window.ReactNativeWebView.postMessage(JSON.stringify(message));
});
} else {
if (window.trakStarVideo) {
window.trakStarVideo.pause();
}
};
true;
`);
}
setTimeout(() => {
setUserActionPause(false);
}, 3000);
break;
case 'videoPaused':
if (isPrimaryPlayer && !isPrimaryMessage) return;
if (!isPrimaryPlayer && isPrimaryMessage) return;
if (TRAKLIST && appState == 'background') return;
// Start the requestAnimationFrame loop
if (!userActionPause && appState == 'background') {
if (isPrimaryPlayer) {
buffer1.current?.injectJavaScript(`
if (window.trakStarVideo) {
window.trakStarVideo.play();
}
true;
`);
} else {
buffer2.current?.injectJavaScript(`
if (window.trakStarVideo) {
window.trakStarVideo.play();
}
true;
`);
}
}
setIsPaused(true);
break;
case 'remotePlay':
if (isPrimaryPlayer) {
buffer1.current?.injectJavaScript(`
if (window.trakStarVideo) {
window.trakStarVideo.play();
window.trakStarVideo.currentTime = window.trakStarVideo.currentTime;
};
true;
`);
} else {
buffer2.current?.injectJavaScript(`
if (window.trakStarVideo) {
window.trakStarVideo.play();
window.trakStarVideo.currentTime = window.trakStarVideo.currentTime;
};
true;
`);
}
setIsPaused(false);
break;
case 'videoPlaying':
if (isPrimaryPlayer && !isPrimaryMessage) return;
if (!isPrimaryPlayer && isPrimaryMessage) return;
setIsPaused(false);
break;
case 'videoError':
if (repeatOptions == 'repeat-once') return;
if (intervalRef.current) {
clearInterval(intervalRef.current);
}
if (message.data == 'true') {
PushNotificationIOS.addNotificationRequest({
id: '0',
title: 'TrakStar™ Music: Unavailable Video',
body: 'Moving onto the next track.',
});
handlePlayNext();
} else {
if (!queue.length && currentTrack) {
const bufferCopy = [...currentTrack.buffer]; // Create a copy of the buffer array
bufferCopy.splice(bufferIndex == -1 ? 1 : bufferIndex + 1, 1); // Modify the copy
currentTrack.buffer = bufferCopy; //
} else if (index + 1 < queue.length) {
queue.splice(index + 1, 1);
} else if (index + 1 >= queue.length) {
const bufferCopy = [...currentTrack.buffer]; // Create a copy of the buffer array
bufferCopy.splice(bufferIndex == -1 ? 1 : bufferIndex + 1, 1); // Modify the copy
currentTrack.buffer = bufferCopy; //
}
if (isPrimaryPlayer) {
setTRXUrl2(null);
setSecondaryPreloadKey(null);
} else {
setTRXUrl1(null);
setPrimaryPreloadKey(null);
}
}
break;
case 'enablePiP':
const data = message.data;
if (data == 'PiP initiated successfully.') {
setInitializingPiP(false);
if (appState == 'active') {
if (isPrimaryPlayer) {
if (message.key === primaryRunnerKey)
setPrimaryWebViewLoaded(true);
} else {
if (message.key === secondaryRunnerKey)
setSecondaryWebViewLoaded(true);
}
}
}
break;
case 'videoCurrentTime':
const progress: Progress = message.data;
const ref = isPrimaryPlayer ? buffer1 : buffer2;
ref.current?.injectJavaScript(`
if (!window.trakStarVideo) {
window.trakStarVideo = document.getElementsByTagName('video')[0];
}
if (window.trakStarVideo && navigator.mediaSession) {
navigator.mediaSession.metadata = new MediaMetadata({
title: "${track?.trak.title ?? 'TrakStar™ Music'}",
artist: "${track?.trak.artist ?? 'JUKERSTONE LTD.'}",
album: "${Date.now()}", // Customize or fetch this as needed
artwork: [
{ src: "${
track?.trak.thumbnail ??
'https://firebasestorage.googleapis.com/v0/b/traklist-7b38a.appspot.com/o/sonar.png?alt=media&token=f81e029a-3f3c-481d-997c-715815bc7598'
}", sizes: "512x512", type: "image/png" }
]
});
// Remove the existing handlers if any
navigator.mediaSession.setActionHandler('seekbackward', null);
navigator.mediaSession.setActionHandler('seekforward', null);
// Define the previous track action
navigator.mediaSession.setActionHandler('previoustrack', () => {
window.ReactNativeWebView.postMessage(JSON.stringify({
eventType: 'previousTrack',
}));
});
// Define the next track action
navigator.mediaSession.setActionHandler('nexttrack', () => {
window.ReactNativeWebView.postMessage(JSON.stringify({
eventType: 'nextTrack',
}));
});
// Handle the pause action
navigator.mediaSession.setActionHandler('pause', () => {
window.ReactNativeWebView.postMessage(JSON.stringify({
eventType: 'remotePause',
}));
});
// Handle the play action
navigator.mediaSession.setActionHandler('play', () => {
window.ReactNativeWebView.postMessage(JSON.stringify({
eventType: 'remotePlay',
}));
});
}
`);
setProgress(progress); // Update progress every 1 seconds
setIsPaused(false);
if (30 <= progress.currentTime && !hasStreamed && track && trace) {
setHasStreamed(true);
handleStream({
TRACK: {track, trace, buffer: []},
IS_LIVE: isLive,
PROTOCOL_ID: protocolId ? protocolId : '',
});
}
if (
repeatOptions == 'repeat-once' &&
progress.currentTime > progress.duration - 2
) {
setHasStreamed(false);
if (isPrimaryPlayer) {
buffer1.current.injectJavaScript(`
if (!window.trakStarVideo) {
window.trakStarVideo = document.getElementsByTagName('video')[0];
}
window.trakStarVideo.currentTime = 0;
true;
`);
} else {
buffer2.current.injectJavaScript(`
if (!window.trakStarVideo) {
window.trakStarVideo = document.getElementsByTagName('video')[0];
}
window.trakStarVideo.currentTime = 0;
true;
`);
}
}
const key = uuid.v4();
if (
isPrimaryPlayer &&
!isSecondaryWebViewLoaded &&
!secondaryPreloadKey &&
queue.length &&
index + 1 < queue.length // Ensure index + 1 is within bounds
) {
setSecondaryPlayerInitialized(true);
setSecondaryPreloadKey(String(key));
const id = `${
queue[index + 1].track.trak.youtube.url.split('=')[1]
}?playsinline=1&fs=0&key=${key}`;
const url = API({id}).jukerstone.security.youtube;
setTRXUrl2(url);
startErrorCheckInterval(false);
} else if (
!isPrimaryPlayer &&
!isPrimaryWebViewLoaded &&
!primaryPreloadKey &&
queue.length &&
index + 1 < queue.length // Ensure index + 1 is within bounds
) {
setPrimaryPlayerInitialized(true);
setPrimaryPreloadKey(String(key));
const id = `${
queue[index + 1].track.trak.youtube.url.split('=')[1]
}?playsinline=1&fs=0&key=${key}`;
const url = API({id}).jukerstone.security.youtube;
setTRXUrl1(url);
startErrorCheckInterval(false);
} else if (
isPrimaryPlayer &&
!isSecondaryWebViewLoaded &&
!secondaryPreloadKey &&
queue.length &&
index + 1 == queue.length
) {
if (
queue[index].buffer.length &&
bufferIndex + 1 < queue[index].buffer.length
) {
setSecondaryPlayerInitialized(true);
setSecondaryPreloadKey(String(key));
const id = `${
queue[index].buffer[
bufferIndex == -1 ? 1 : bufferIndex + 1
].trak.youtube.url.split('=')[1]
}?playsinline=1&fs=0&key=${key}`;
const url = API({id}).jukerstone.security.youtube;
setTRXUrl2(url);
startErrorCheckInterval(false);
} else {
if (repeatOptions == 'repeat') {
setSecondaryPlayerInitialized(true);
setSecondaryPreloadKey(String(key));
const id = `${
queue[index].buffer[0].trak.youtube.url.split('=')[1]
}?playsinline=1&fs=0&key=${key}`;
const url = API({id}).jukerstone.security.youtube;
setTRXUrl2(url);
startErrorCheckInterval(false);
} else {
setTRXUrl2(null);
setSecondaryPlayerInitialized(false);
}
}
} else if (
!isPrimaryPlayer &&
!isPrimaryWebViewLoaded &&
!primaryPreloadKey &&
queue.length &&
index + 1 == queue.length
) {
if (
queue[index].buffer.length &&
bufferIndex + 1 < queue[index].buffer.length
) {
setPrimaryPlayerInitialized(true);
setPrimaryPreloadKey(String(key));
const id = `${
queue[index].buffer[
bufferIndex == -1 ? 1 : bufferIndex + 1
].trak.youtube.url.split('=')[1]
}?playsinline=1&fs=0&key=${key}`;
const url = API({id}).jukerstone.security.youtube;
setTRXUrl1(url);
startErrorCheckInterval(false);
} else {
if (repeatOptions == 'repeat') {
setPrimaryPlayerInitialized(true);
setPrimaryPreloadKey(String(key));
const id = `${
queue[index].buffer[0].trak.youtube.url.split('=')[1]
}?playsinline=1&fs=0&key=${key}`;
const url = API({id}).jukerstone.security.youtube;
setTRXUrl1(url);
startErrorCheckInterval(false);
} else {
setTRXUrl1(null);
setPrimaryPlayerInitialized(false);
}
}
} else if (
isPrimaryPlayer &&
!isSecondaryWebViewLoaded &&
!secondaryPreloadKey &&
!queue.length &&
currentTrack
) {
if (
currentTrack.buffer.length &&
bufferIndex + 1 < currentTrack.buffer.length
) {
setSecondaryPlayerInitialized(true);
setSecondaryPreloadKey(String(key));
const id = `${
currentTrack.buffer[bufferIndex + 1].trak.youtube.url.split(
'=',
)[1]
}?playsinline=1&fs=0&key=${key}`;
const url = API({id}).jukerstone.security.youtube;
setTRXUrl2(url);
startErrorCheckInterval(false);
} else {
if (repeatOptions == 'repeat') {
setSecondaryPlayerInitialized(true);
setSecondaryPreloadKey(String(key));
const id = `${
currentTrack.buffer[0].trak.youtube.url.split('=')[1]
}?playsinline=1&fs=0&key=${key}`;
const url = API({id}).jukerstone.security.youtube;
setTRXUrl2(url);
startErrorCheckInterval(false);
} else {
setTRXUrl2(null);
setSecondaryPlayerInitialized(false);
}
}
} else if (
!isPrimaryPlayer &&
!isPrimaryWebViewLoaded &&
!primaryPreloadKey &&
!queue.length &&
currentTrack
) {
if (
currentTrack.buffer.length &&
bufferIndex + 1 < currentTrack.buffer.length
) {
setPrimaryPlayerInitialized(true);
setPrimaryPreloadKey(String(key));
const id = `${
currentTrack.buffer[bufferIndex + 1].trak.youtube.url.split(
'=',
)[1]
}?playsinline=1&fs=0&key=${key}`;
const url = API({id}).jukerstone.security.youtube;
setTRXUrl1(url);
startErrorCheckInterval(false);
} else {
if (repeatOptions == 'repeat') {
setPrimaryPlayerInitialized(true);
setPrimaryPreloadKey(String(key));
const id = `${
currentTrack.buffer[0].trak.youtube.url.split('=')[1]
}?playsinline=1&fs=0&key=${key}`;
const url = API({id}).jukerstone.security.youtube;
setTRXUrl1(url);
startErrorCheckInterval(false);
} else {
setTRXUrl1(null);
setPrimaryPlayerInitialized(false);
}
}
}
break;
case 'videoEnded':
setHasStreamed(false);
if (isPrimaryPlayer) {
setPrimaryWebViewLoaded(false);
setPrimaryPreloadKey(null);
if (index + 1 < queue.length) {
handleNext({isPrimary: false});
} else {
handleBuffer({isPrimary: false});
}
} else if (!isPrimaryPlayer) {
setSecondaryWebViewLoaded(false);
setSecondaryPreloadKey(null);
if (index + 1 < queue.length) {
handleNext({isPrimary: true});
} else {
handleBuffer({isPrimary: true});
}
}
break;
case 'nextTrack':
if (isProcessing) {
return; // Ignore if already processing the next track
}
setIsProcessing(true);
if (
isPrimaryPlayer &&
isSecondaryWebViewLoaded &&
secondaryPreloadKey
) {
setTRXUrl1(null);
setPrimaryWebViewLoaded(false);
setPrimaryPlayerInitialized(false);
setPrimaryPreloadKey(null);
if (index + 1 < queue.length) {
handleNext({isPrimary: false});
} else {
handleBuffer({isPrimary: false});
}
if (repeatOptions == 'repeat-once') setRepeatOptions('off');
} else if (
!isPrimaryPlayer &&
isPrimaryWebViewLoaded &&
primaryPreloadKey
) {
setTRXUrl2(null);
setSecondaryWebViewLoaded(false);
setSecondaryPlayerInitialized(false);
setSecondaryPreloadKey(null);
if (index + 1 < queue.length) {
handleNext({isPrimary: true});
} else {
handleBuffer({isPrimary: true});
}
if (repeatOptions == 'repeat-once') setRepeatOptions('off');
} else if (
(isPrimaryPlayer && !isSecondaryPlayerInitialized) ||
(!isPrimaryPlayer && !isPrimaryPlayerInitialized)
) {
setTRXUrl1(null);
setPrimaryWebViewLoaded(false);
setPrimaryPlayerInitialized(false);
setPrimaryPreloadKey(null);
setTRXUrl2(null);
setSecondaryWebViewLoaded(false);
setSecondaryPlayerInitialized(false);
setSecondaryPreloadKey(null);
PushNotificationIOS.addNotificationRequest({
id: '1',
title: "That's all folks",
body: 'Come back into the app and queue some new music',
});
handleStop();
} else if (
(isPrimaryPlayer && !isSecondaryWebViewLoaded) ||
(!isPrimaryPlayer && !isPrimaryWebViewLoaded)
) {
PushNotificationIOS.addNotificationRequest({
id: '1',
title: 'TrakStar™ Music: Buffering',
body: 'Your queue is buffering. Please try again in a few seconds.',
});
}
setIsProcessing(false);
break;
case 'previousTrack':
handleSeek(0);
break;
default:
console.warn(`Unhandled event type: ${message.eventType}`);
break;
}
};
return {
picture1: trxUrl1,
picture2: trxUrl2,
handleMessage,
isPrimaryPlayer,
isPrimaryPlayerInitialized,
isSecondaryPlayerInitialized,
fetchVideoTimeJS,
};
};
@TSB1999
Copy link
Author

TSB1999 commented Aug 18, 2025

The next Spotify won’t be a music platform — it’ll be a better player for YouTube.

JukePod turns YouTube into a true music app with a dual-buffered RN WebView player, PiP, and background-safe playback.

Most people miss this: YouTube already has the world’s biggest music library.
Official tracks. Remixes. Live cuts. The weird, wonderful versions DSPs ignore. It’s all there — global, instant.

The gap: YouTube doesn’t feel like a music app.

So I built a player that does.

🎧 Introducing JukePod — a new kind of music playback experience

  • Connect your playlist (Spotify, Apple Music, etc.)
  • We match each track to the best YouTube source
  • Dual-buffered queue → instant, gapless handoffs
  • Picture-in-Picture works out of the box
  • Designed for distraction-free playback

🧠 Why it’s different (architecture, not a hack)

JukePod uses a two-WebView “runner + preload” model keyed per track, with Media Session remote controls and background-safe behavior. There’s no SDK lock-in; everything is orchestrated at the playback surface with strict user-action gating, so it feels like a real music app — fast, smooth, and stable.

🛠️ What’s in this gist

  • Provider — state, queueing, repeat modes, persistence
  • Hook — keyed preloading, PiP orchestration, self-healing error checks
  • SDK — two WebViews, message routing, minimal surface area

If you’re curious about the React Native stack, the dual-buffer design, or PiP + background quirks, AMA.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment