mirror of
https://github.com/mickael-kerjean/filestash
synced 2025-12-30 12:16:07 +01:00
feature (chromecast): wip for chromecast support
This commit is contained in:
parent
2be99ac611
commit
07e77e969d
7 changed files with 304 additions and 188 deletions
|
|
@ -135,14 +135,6 @@ export function ViewerPageComponent({ error, subscribe, unsubscribe, match, loca
|
|||
return history.listen(() => {})
|
||||
}, [path]);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
const context = Chromecast.context();
|
||||
if (!context) return;
|
||||
context.endCurrentSession();
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="component_page_viewerpage">
|
||||
<BreadCrumb needSaving={state.needSaving} className="breadcrumb" path={path} />
|
||||
|
|
|
|||
|
|
@ -14,8 +14,11 @@ export function AudioPlayer({ filename, data }) {
|
|||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [purcentLoading, setPurcentLoading] = useState(0);
|
||||
const [volume, setVolume] = useState(settings_get("volume") === null ? 50 : settings_get("volume"));
|
||||
const [currentTime, setCurrentTime] = useState(0);
|
||||
const [duration, setDuration] = useState(0);
|
||||
const [isChromecast, setIsChromecast] = useState(false);
|
||||
const [error, setError] = useState(null);
|
||||
const [render, setRender] = useState(0);
|
||||
const wavesurfer = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -28,18 +31,18 @@ export function AudioPlayer({ filename, data }) {
|
|||
height: 200,
|
||||
barWidth: 1,
|
||||
});
|
||||
window.wavesurfer = wavesurfer.current; // TODO: remove this
|
||||
wavesurfer.current.load(data);
|
||||
|
||||
let $currentTime = document.getElementById("currentTime");
|
||||
let $totalDuration = document.getElementById("totalDuration");
|
||||
wavesurfer.current.on("ready", () => {
|
||||
setPurcentLoading(100);
|
||||
setIsLoading(false);
|
||||
wavesurfer.current.setVolume(volume / 100);
|
||||
$totalDuration.innerHTML = formatTimecode(wavesurfer.current.getDuration());
|
||||
setDuration(wavesurfer.current.getDuration());
|
||||
});
|
||||
wavesurfer.current.on("audioprocess", () => {
|
||||
$currentTime.innerHTML = formatTimecode(wavesurfer.current.getCurrentTime());
|
||||
const t = wavesurfer.current.getCurrentTime()
|
||||
_currentTime = t;
|
||||
setCurrentTime(t);
|
||||
});
|
||||
wavesurfer.current.on("loading", (n) => {
|
||||
setPurcentLoading(n);
|
||||
|
|
@ -48,106 +51,53 @@ export function AudioPlayer({ filename, data }) {
|
|||
setIsLoading(false);
|
||||
setError(err);
|
||||
});
|
||||
wavesurfer.current.on("seek", (s) => {
|
||||
const media = Chromecast.media();
|
||||
if (!media) return;
|
||||
const seekRequest = new chrome.cast.media.SeekRequest();
|
||||
seekRequest.currentTime = parseInt(s*wavesurfer.current.getDuration());
|
||||
media.seek(seekRequest);
|
||||
});
|
||||
|
||||
return () => wavesurfer.current.destroy();
|
||||
}, []);
|
||||
}, [data]);
|
||||
|
||||
useEffect(() => {
|
||||
const onKeyPressHandler = (e) => {
|
||||
if(e.code !== "Space") {
|
||||
return
|
||||
}
|
||||
// TODO: write shortcut
|
||||
isPlaying ? onPause(e) : onPlay(e);
|
||||
};
|
||||
|
||||
window.addEventListener("keypress", onKeyPressHandler);
|
||||
return () => window.removeEventListener("keypress", onKeyPressHandler);
|
||||
}, [isPlaying, isChromecast])
|
||||
|
||||
const chromecastSetup = (event) => {
|
||||
switch (event.sessionState) {
|
||||
case cast.framework.SessionState.SESSION_STARTING:
|
||||
setIsChromecast(true);
|
||||
setIsLoading(true);
|
||||
break;
|
||||
case cast.framework.SessionState.SESSION_START_FAILED:
|
||||
setIsChromecast(false);
|
||||
setIsLoading(false);
|
||||
break;
|
||||
case cast.framework.SessionState.SESSION_STARTED:
|
||||
chromecastHandler()
|
||||
const session = Chromecast.session();
|
||||
if (session) setVolume(session.getVolume() * 100);
|
||||
break;
|
||||
case cast.framework.SessionState.SESSION_ENDING:
|
||||
wavesurfer.current.setMute(false);
|
||||
setVolume(wavesurfer.current.getVolume() * 100);
|
||||
setIsChromecast(false);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const chromecastHandler = () => {
|
||||
setIsLoading(true);
|
||||
const link = Chromecast.createLink(data);
|
||||
const media = new chrome.cast.media.MediaInfo(
|
||||
link,
|
||||
getMimeType(data),
|
||||
);
|
||||
media.metadata = new chrome.cast.media.MusicTrackMediaMetadata()
|
||||
media.metadata.title = filename.substr(0, filename.lastIndexOf(filepath.extname(filename)));
|
||||
media.metadata.subtitle = CONFIG.name;
|
||||
media.metadata.albumName = CONFIG.name;
|
||||
media.metadata.images = [
|
||||
new chrome.cast.Image(origin + "/assets/icons/music.png"),
|
||||
];
|
||||
const session = Chromecast.session();
|
||||
if (!session) return;
|
||||
|
||||
Chromecast.createRequest(media)
|
||||
.then((req) => {
|
||||
req.currentTime = parseInt(wavesurfer.current.getCurrentTime());
|
||||
return session.loadMedia(req)
|
||||
})
|
||||
.then(() => {
|
||||
setIsPlaying(true);
|
||||
setIsLoading(false);
|
||||
wavesurfer.current.play();
|
||||
wavesurfer.current.setMute(true);
|
||||
|
||||
const media = Chromecast.media();
|
||||
if (!media) return;
|
||||
wavesurfer.current.seekTo(media.getEstimatedTime() / wavesurfer.current.getDuration());
|
||||
media.addUpdateListener(chromecastAlive);
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
notify.send(t("Cannot establish a connection"), "error");
|
||||
setIsChromecast(false);
|
||||
setIsLoading(false);
|
||||
});
|
||||
}
|
||||
|
||||
const chromecastAlive = (isAlive) => {
|
||||
if (isAlive) return;
|
||||
const session = Chromecast.session();
|
||||
if (session) {
|
||||
session.endSession();
|
||||
wavesurfer.current.setMute(false);
|
||||
}
|
||||
};
|
||||
}, [isPlaying, isChromecast]);
|
||||
|
||||
useEffect(() => {
|
||||
const context = Chromecast.context();
|
||||
if (!context) return;
|
||||
chromecastAlive(false);
|
||||
document.getElementById("chromecast-target").append(document.createElement("google-cast-launcher"));
|
||||
_currentTime = 0;
|
||||
|
||||
const chromecastSetup = (event) => {
|
||||
switch (event.sessionState) {
|
||||
case cast.framework.SessionState.SESSION_STARTING:
|
||||
setIsChromecast(true);
|
||||
setIsLoading(true);
|
||||
break;
|
||||
case cast.framework.SessionState.SESSION_START_FAILED:
|
||||
setIsChromecast(false);
|
||||
setIsLoading(false);
|
||||
break;
|
||||
case cast.framework.SessionState.SESSION_STARTED:
|
||||
chromecastLoader()
|
||||
break;
|
||||
case cast.framework.SessionState.SESSION_ENDING:
|
||||
setIsChromecast(false);
|
||||
// console.log("ENDING seekTo", _currentTime, duration, wavesurfer.current.getDuration(), _currentTime / wavesurfer.current.getDuration());
|
||||
wavesurfer.current.seekTo(_currentTime / wavesurfer.current.getDuration());
|
||||
// TODO: reset volume setVolume(wavesurfer.current.getVolume() * 100) --> not working
|
||||
wavesurfer.current.setMute(false);
|
||||
const media = Chromecast.media();
|
||||
if (media && media.playerState === "PLAYING") wavesurfer.current.play();
|
||||
else if (media && media.playerState === "PAUSED") wavesurfer.current.pause();
|
||||
break;
|
||||
}
|
||||
};
|
||||
context.addEventListener(
|
||||
cast.framework.CastContextEventType.SESSION_STATE_CHANGED,
|
||||
chromecastSetup,
|
||||
|
|
@ -157,13 +107,92 @@ export function AudioPlayer({ filename, data }) {
|
|||
cast.framework.CastContextEventType.SESSION_STATE_CHANGED,
|
||||
chromecastSetup,
|
||||
);
|
||||
const media = Chromecast.media();
|
||||
if (!media) return
|
||||
media.removeUpdateListener(chromecastAlive);
|
||||
chromecastAlive(false);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!wavesurfer) return;
|
||||
const onSeek = (s) => {
|
||||
// console.log("ON SEEK", isChromecast, isLoading);
|
||||
if (isChromecast === false) return
|
||||
else if (s * duration === _currentTime) {
|
||||
// wavesurfer trigger a seek event when trying to synchronise the remote to the local
|
||||
// which we want to ignore as we're only interested in user requested seek
|
||||
return;
|
||||
}
|
||||
const media = Chromecast.media();
|
||||
if (!media) return;
|
||||
|
||||
wavesurfer.current.pause();
|
||||
const seekRequest = new chrome.cast.media.SeekRequest();
|
||||
seekRequest.currentTime = s*duration;
|
||||
media.seek(seekRequest);
|
||||
}
|
||||
wavesurfer.current.on("seek", onSeek);
|
||||
return () => {
|
||||
wavesurfer.current.un("seek", onSeek);
|
||||
};
|
||||
}, [wavesurfer.current, isChromecast]);
|
||||
|
||||
useEffect(() => {
|
||||
const media = Chromecast.media();
|
||||
if (!media) return;
|
||||
|
||||
const remotePlayer = new cast.framework.RemotePlayer();
|
||||
const remotePlayerController = new cast.framework.RemotePlayerController(remotePlayer);
|
||||
const onPlayerStateChangeHandler = (event) => {
|
||||
// console.log("PLAYER STATE CHANGE", event)
|
||||
switch(event.value) {
|
||||
case "BUFFERING":
|
||||
wavesurfer.current.pause();
|
||||
break
|
||||
case "PLAYING":
|
||||
wavesurfer.current.play();
|
||||
break;
|
||||
}
|
||||
};
|
||||
const onPlayerCurrentTimeChangeHandler = (event) => {
|
||||
_currentTime = event.value;
|
||||
setCurrentTime(event.value);
|
||||
if (event.value > 0) wavesurfer.current.seekTo(event.value / wavesurfer.current.getDuration());
|
||||
// console.log("time change", event.value, wavesurfer.current.getDuration(), event.value / wavesurfer.current.getDuration())
|
||||
};
|
||||
const onMediaChange = (isAlive) => {
|
||||
if (media.playerState !== chrome.cast.media.PlayerState.IDLE) return;
|
||||
|
||||
switch(media.idleReason) {
|
||||
case chrome.cast.media.IdleReason.FINISHED:
|
||||
setIsPlaying(false);
|
||||
setIsChromecast(false);
|
||||
setVolume($video.current.volume * 100);
|
||||
$video.current.currentTime = _currentTime;
|
||||
$video.current.muted = false;
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
remotePlayerController.addEventListener(
|
||||
cast.framework.RemotePlayerEventType.PLAYER_STATE_CHANGED,
|
||||
onPlayerStateChangeHandler,
|
||||
);
|
||||
remotePlayerController.addEventListener(
|
||||
cast.framework.RemotePlayerEventType.CURRENT_TIME_CHANGED,
|
||||
onPlayerCurrentTimeChangeHandler,
|
||||
);
|
||||
return () => {
|
||||
remotePlayerController.removeEventListener(
|
||||
cast.framework.RemotePlayerEventType.PLAYER_STATE_CHANGED,
|
||||
onPlayerStateChangeHandler,
|
||||
);
|
||||
remotePlayerController.removeEventListener(
|
||||
cast.framework.RemotePlayerEventType.CURRENT_TIME_CHANGED,
|
||||
onPlayerCurrentTimeChangeHandler,
|
||||
);
|
||||
};
|
||||
}, [isChromecast, isLoading, render]);
|
||||
|
||||
const onPlay = (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
|
@ -187,13 +216,20 @@ export function AudioPlayer({ filename, data }) {
|
|||
};
|
||||
|
||||
const onVolumeChange = (v) => {
|
||||
settings_put("volume", v);
|
||||
setVolume(v);
|
||||
if (isChromecast) {
|
||||
const session = Chromecast.session()
|
||||
if (session) session.setVolume(v / 100);
|
||||
} else wavesurfer.current.setVolume(v / 100);
|
||||
else {
|
||||
setIsChromecast(false);
|
||||
notify.send(t("Cannot establish a connection"), "error");
|
||||
}
|
||||
} else {
|
||||
wavesurfer.current.setVolume(v / 100);
|
||||
settings_put("volume", v);
|
||||
}
|
||||
};
|
||||
|
||||
const onVolumeClick = () => {
|
||||
onVolumeChange(0);
|
||||
};
|
||||
|
|
@ -204,9 +240,52 @@ export function AudioPlayer({ filename, data }) {
|
|||
String(parseInt(seconds % 60)).padStart(2, "0");
|
||||
};
|
||||
|
||||
const chromecastLoader = () => {
|
||||
const link = Chromecast.createLink(data);
|
||||
const media = new chrome.cast.media.MediaInfo(
|
||||
link,
|
||||
getMimeType(data),
|
||||
);
|
||||
media.metadata = new chrome.cast.media.MusicTrackMediaMetadata()
|
||||
media.metadata.title = filename.substr(0, filename.lastIndexOf(filepath.extname(filename)));
|
||||
media.metadata.subtitle = CONFIG.name;
|
||||
media.metadata.albumName = CONFIG.name;
|
||||
media.metadata.images = [
|
||||
new chrome.cast.Image(origin + "/assets/icons/music.png"),
|
||||
];
|
||||
|
||||
setIsChromecast(true);
|
||||
setIsLoading(false);
|
||||
setIsPlaying(true);
|
||||
wavesurfer.current.setMute(true);
|
||||
wavesurfer.current.pause();
|
||||
|
||||
const session = Chromecast.session();
|
||||
if (!session) return;
|
||||
setVolume(session.getVolume() * 100);
|
||||
Chromecast.createRequest(media)
|
||||
.then((req) => {
|
||||
req.currentTime = _currentTime;
|
||||
return session.loadMedia(req)
|
||||
})
|
||||
.then(() => setRender(render + 1))
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
notify.send(t("Cannot establish a connection"), "error");
|
||||
setIsChromecast(false);
|
||||
setIsLoading(false);
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="component_audioplayer">
|
||||
<MenuBar title={filename} download={data} />
|
||||
<MenuBar title={filename} download={data}>
|
||||
{
|
||||
Chromecast.session() && (
|
||||
<Icon name="fullscreen" onClick={() => chromecastLoader()} />
|
||||
)
|
||||
}
|
||||
</MenuBar>
|
||||
<div className="audioplayer_container">
|
||||
<NgIf cond={error !== null} className="audioplayer_error">
|
||||
{error}
|
||||
|
|
@ -246,9 +325,9 @@ export function AudioPlayer({ filename, data }) {
|
|||
<input onChange={(e) => onVolumeChange(Number(e.target.value) || 0)} type="range" min="0" max="100" value={volume}/>
|
||||
</div>
|
||||
<div className="timecode">
|
||||
<span id="currentTime">{ formatTimecode(0) }</span>
|
||||
<span id="currentTime">{ formatTimecode(currentTime) }</span>
|
||||
<span id="separator" className="no-select">/</span>
|
||||
<span id="totalDuration">{ formatTimecode(0) }</span>
|
||||
<span id="totalDuration">{ formatTimecode(duration) }</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -257,3 +336,5 @@ export function AudioPlayer({ filename, data }) {
|
|||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
let _currentTime = 0; // trick to avoid making too many call to the chromecast SDK
|
||||
|
|
|
|||
|
|
@ -61,12 +61,12 @@ export function ImageViewerComponent({ filename, data, path, subscribe, unsubscr
|
|||
const chromecastSetup = (event) => {
|
||||
switch (event.sessionState) {
|
||||
case cast.framework.SessionState.SESSION_STARTED:
|
||||
chromecastHandler();
|
||||
chromecastLoader();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const chromecastHandler = (event) => {
|
||||
const chromecastLoader = () => {
|
||||
const session = Chromecast.session();
|
||||
if (!session) return;
|
||||
|
||||
|
|
@ -96,7 +96,8 @@ export function ImageViewerComponent({ filename, data, path, subscribe, unsubscr
|
|||
cast.framework.CastContextEventType.SESSION_STATE_CHANGED,
|
||||
chromecastSetup,
|
||||
);
|
||||
chromecastHandler();
|
||||
const media = Chromecast.media();
|
||||
if (media && media.media && media.media.mediaCategory === "IMAGE") chromecastLoader();
|
||||
return () => {
|
||||
context.removeEventListener(
|
||||
cast.framework.CastContextEventType.SESSION_STATE_CHANGED,
|
||||
|
|
@ -119,6 +120,7 @@ export function ImageViewerComponent({ filename, data, path, subscribe, unsubscr
|
|||
}
|
||||
};
|
||||
const requestFullScreen = () => {
|
||||
chromecastLoader();
|
||||
if ("webkitRequestFullscreen" in document.body) {
|
||||
$container.current.webkitRequestFullscreen();
|
||||
} else if ("mozRequestFullScreen" in document.body) {
|
||||
|
|
@ -168,10 +170,9 @@ export function ImageViewerComponent({ filename, data, path, subscribe, unsubscr
|
|||
</div>
|
||||
</div>
|
||||
<Pager
|
||||
type={["image"]}
|
||||
path={path}
|
||||
pageChange={(files) => setState({ draggable: files.length > 1 ? true : false })}
|
||||
next={(e) => setState({ preload: e })} />
|
||||
/>
|
||||
</div>
|
||||
|
||||
<NgIf cond={state.is_loaded}>
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ export const MenuBar = (props) => {
|
|||
<div className="titlebar" style={{ letterSpacing: "0.3px" }}>{props.title}</div>
|
||||
<div className="action-item no-select">
|
||||
<span className="specific">
|
||||
<span id="chromecast-target"></span>
|
||||
{props.children}
|
||||
<span id="chromecast-target"></span>
|
||||
</span>
|
||||
{
|
||||
props.download === null ?
|
||||
|
|
|
|||
|
|
@ -74,9 +74,15 @@ class PagerComponent extends React.Component {
|
|||
if (f === null) return Promise.reject({ code: "NO_DATA" });
|
||||
return Promise.resolve(f);
|
||||
})
|
||||
.then((f) => f.results
|
||||
.filter((file) => (isImage(file.name) || isVideo(file.name)) &&
|
||||
file.type === "file"))
|
||||
.then((f) => f.results.filter((file) => {
|
||||
if (file.type !== "file") return false;
|
||||
const mType = getMimeType(file.name);
|
||||
switch(mType.split("/")[0]) {
|
||||
case "video": return true;
|
||||
case "image": return true;
|
||||
}
|
||||
return false;
|
||||
}))
|
||||
.then((f) => sort(f, settings_get("filespage_sort") || "type"))
|
||||
.then((f) => findPosition(f, basename(props.path)))
|
||||
.then((res) => {
|
||||
|
|
@ -98,12 +104,6 @@ class PagerComponent extends React.Component {
|
|||
}
|
||||
return [files, i];
|
||||
};
|
||||
const isVideo = (filename) => {
|
||||
return getMimeType(filename).split("/")[0] === "video";
|
||||
};
|
||||
const isImage = (filename) => {
|
||||
return getMimeType(filename).split("/")[0] === "image";
|
||||
};
|
||||
}
|
||||
|
||||
onFormInputChange(e) {
|
||||
|
|
@ -125,6 +125,8 @@ class PagerComponent extends React.Component {
|
|||
}
|
||||
|
||||
onKeyPress(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (e.target.classList.contains("prevent")) return;
|
||||
if (e.keyCode === 39) {
|
||||
this.navigatePage(this.calculateNextPageNumber(this.state.n));
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ export function VideoPlayer({ filename, data, path }) {
|
|||
setIsLoading(false);
|
||||
};
|
||||
const finishHandler = () => {
|
||||
$video.current.currentTime = 0;
|
||||
setIsPlaying(false);
|
||||
};
|
||||
const errorHandler = (err) => {
|
||||
|
|
@ -94,8 +93,6 @@ export function VideoPlayer({ filename, data, path }) {
|
|||
case "KeyM": return onVolumeChange(0);
|
||||
case "ArrowUp": return onVolumeChange(Math.min(volume + 10, 100));
|
||||
case "ArrowDown": return onVolumeChange(Math.max(volume - 10, 0));
|
||||
case "ArrowLeft": return onSeek(_currentTime - 5);
|
||||
case "ArrowRight": return onSeek(_currentTime + 5);
|
||||
case "KeyL": return onSeek(_currentTime + 10);
|
||||
case "KeyJ": return onSeek(_currentTime - 10);
|
||||
case "KeyF": return onRequestFullscreen();
|
||||
|
|
@ -135,13 +132,22 @@ export function VideoPlayer({ filename, data, path }) {
|
|||
setIsLoading(false);
|
||||
break;
|
||||
case cast.framework.SessionState.SESSION_STARTED:
|
||||
chromecastLoader()
|
||||
chromecastLoader();
|
||||
break;
|
||||
case cast.framework.SessionState.SESSION_ENDING:
|
||||
setIsChromecast(false);
|
||||
setVolume($video.current.volume * 100);
|
||||
$video.current.currentTime = _currentTime;
|
||||
$video.current.muted = false;
|
||||
const media = Chromecast.media();
|
||||
if (media && media.playerState === "PLAYING") $video.current.play();
|
||||
else if (media && media.playerState === "PAUSED") $video.current.pause();
|
||||
break;
|
||||
case cast.framework.SessionState.SESSION_ENDED:
|
||||
setIsChromecast(false);
|
||||
setVolume($video.current.volume * 100);
|
||||
$video.current.currentTime = _currentTime;
|
||||
$video.current.muted = false;
|
||||
if (isPlaying) $video.current.play();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
|
@ -150,8 +156,6 @@ export function VideoPlayer({ filename, data, path }) {
|
|||
chromecastSetup,
|
||||
);
|
||||
return () => {
|
||||
const media = Chromecast.media();
|
||||
if (media) media.removeUpdateListener(chromecastMediaHandler);
|
||||
context.removeEventListener(
|
||||
cast.framework.CastContextEventType.SESSION_STATE_CHANGED,
|
||||
chromecastSetup,
|
||||
|
|
@ -160,20 +164,71 @@ export function VideoPlayer({ filename, data, path }) {
|
|||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
if (isLoading) return;
|
||||
if (isChromecast) {
|
||||
const media = Chromecast.media();
|
||||
if (!media) return;
|
||||
_currentTime = media.getEstimatedTime();
|
||||
setIsBuffering(media.playerState === "BUFFERING");
|
||||
} else _currentTime = $video.current.currentTime;
|
||||
setCurrentTime(_currentTime)
|
||||
}, 100);
|
||||
return () => {
|
||||
clearInterval(interval);
|
||||
if (isLoading === true) return;
|
||||
else if (isChromecast === false) {
|
||||
const interval = setInterval(() => {
|
||||
_currentTime = $video.current.currentTime;
|
||||
setCurrentTime(_currentTime);
|
||||
}, 100);
|
||||
return () => {
|
||||
clearInterval(interval);
|
||||
};
|
||||
}
|
||||
|
||||
const media = Chromecast.media();
|
||||
if (!media) return;
|
||||
|
||||
const remotePlayer = new cast.framework.RemotePlayer();
|
||||
const remotePlayerController = new cast.framework.RemotePlayerController(remotePlayer);
|
||||
const onPlayerStateChangeHandler = (event) => {
|
||||
switch(event.value) {
|
||||
case "BUFFERING":
|
||||
setIsBuffering(true);
|
||||
break
|
||||
case "PLAYING":
|
||||
setIsBuffering(false);
|
||||
break;
|
||||
}
|
||||
};
|
||||
}, [data, isChromecast, isLoading]);
|
||||
const onPlayerCurrentTimeChangeHandler = (event) => {
|
||||
_currentTime = event.value;
|
||||
setCurrentTime(event.value);
|
||||
};
|
||||
const onMediaChange = (isAlive) => {
|
||||
if (media.playerState !== chrome.cast.media.PlayerState.IDLE) return;
|
||||
|
||||
switch(media.idleReason) {
|
||||
case chrome.cast.media.IdleReason.FINISHED:
|
||||
setIsPlaying(false);
|
||||
setIsChromecast(false);
|
||||
setVolume($video.current.volume * 100);
|
||||
$video.current.currentTime = _currentTime;
|
||||
$video.current.muted = false;
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
media.addUpdateListener(onMediaChange);
|
||||
remotePlayerController.addEventListener(
|
||||
cast.framework.RemotePlayerEventType.PLAYER_STATE_CHANGED,
|
||||
onPlayerStateChangeHandler,
|
||||
);
|
||||
remotePlayerController.addEventListener(
|
||||
cast.framework.RemotePlayerEventType.CURRENT_TIME_CHANGED,
|
||||
onPlayerCurrentTimeChangeHandler,
|
||||
);
|
||||
return () => {
|
||||
media.removeUpdateListener(onMediaChange);
|
||||
remotePlayerController.removeEventListener(
|
||||
cast.framework.RemotePlayerEventType.PLAYER_STATE_CHANGED,
|
||||
onPlayerStateChangeHandler,
|
||||
);
|
||||
remotePlayerController.removeEventListener(
|
||||
cast.framework.RemotePlayerEventType.CURRENT_TIME_CHANGED,
|
||||
onPlayerCurrentTimeChangeHandler,
|
||||
);
|
||||
};
|
||||
}, [isChromecast, isLoading, render]);
|
||||
|
||||
const onPlay = () => {
|
||||
setIsPlaying(true);
|
||||
|
|
@ -194,11 +249,10 @@ export function VideoPlayer({ filename, data, path }) {
|
|||
if (isChromecast) {
|
||||
const media = Chromecast.media();
|
||||
if (!media) return;
|
||||
setIsLoading(true);
|
||||
setIsBuffering(true);
|
||||
const seekRequest = new chrome.cast.media.SeekRequest();
|
||||
seekRequest.currentTime = parseInt(newTime);
|
||||
media.seek(seekRequest);
|
||||
setTimeout(() => setIsLoading(false), 1000);
|
||||
} else $video.current.currentTime = newTime;
|
||||
};
|
||||
const onClickSeek = (e) => {
|
||||
|
|
@ -213,18 +267,24 @@ export function VideoPlayer({ filename, data, path }) {
|
|||
onPause();
|
||||
n = 0;
|
||||
}
|
||||
onSeek(n * duration);
|
||||
_currentTime = n * duration;
|
||||
setCurrentTime(_currentTime);
|
||||
onSeek(_currentTime);
|
||||
};
|
||||
|
||||
const onVolumeChange = (n) => {
|
||||
settings_put("volume", n);
|
||||
setVolume(n);
|
||||
if (isChromecast) {
|
||||
const session = Chromecast.session()
|
||||
if (session) session.setVolume(n / 100);
|
||||
else notify.send(t("Cannot establish a connection"), "error");
|
||||
else {
|
||||
setIsChromecast(false);
|
||||
notify.send(t("Cannot establish a connection"), "error");
|
||||
}
|
||||
} else {
|
||||
$video.current.volume = n / 100;
|
||||
settings_put("volume", n);
|
||||
}
|
||||
else $video.current.volume = n / 100;
|
||||
};
|
||||
|
||||
const onProgressHover = (e) => {
|
||||
|
|
@ -232,9 +292,9 @@ export function VideoPlayer({ filename, data, path }) {
|
|||
const width = e.clientX - rec.x;
|
||||
const time = duration * width / rec.width;
|
||||
let posX = width;
|
||||
posX = Math.max(posX, 30) // min boundary
|
||||
posX = Math.max(posX, 30);
|
||||
posX = Math.min(posX, e.target.clientWidth - 30);
|
||||
setHint({ x: `${posX}px`, time })
|
||||
setHint({ x: `${posX}px`, time });
|
||||
};
|
||||
|
||||
const onRequestFullscreen = () => {
|
||||
|
|
@ -242,9 +302,7 @@ export function VideoPlayer({ filename, data, path }) {
|
|||
if (!session) {
|
||||
document.querySelector(".video_screen").requestFullscreen();
|
||||
requestAnimationFrame(() => setRender(render + 1));
|
||||
} else {
|
||||
chromecastLoader();
|
||||
}
|
||||
} else chromecastLoader();
|
||||
};
|
||||
|
||||
const isFullscreen = () => {
|
||||
|
|
@ -290,47 +348,34 @@ export function VideoPlayer({ filename, data, path }) {
|
|||
];
|
||||
|
||||
setIsChromecast(true);
|
||||
setIsPlaying(true);
|
||||
setIsLoading(false);
|
||||
setIsPlaying(true);
|
||||
setIsBuffering(false);
|
||||
$video.current.muted = true;
|
||||
$video.current.pause();
|
||||
|
||||
const session = Chromecast.session();
|
||||
if (!session) return;
|
||||
$video.current.pause();
|
||||
setVolume(session.getVolume() * 100);
|
||||
Chromecast.createRequest(media)
|
||||
return Chromecast.createRequest(media)
|
||||
.then((req) => {
|
||||
req.currentTime = parseInt($video.current.currentTime);
|
||||
setCurrentTime($video.current.currentTime);
|
||||
req.currentTime = parseInt(_currentTime);
|
||||
return session.loadMedia(req);
|
||||
})
|
||||
.then(() => {
|
||||
const media = session.getMediaSession();
|
||||
if (!media) return;
|
||||
media.addUpdateListener(chromecastMediaHandler);
|
||||
})
|
||||
.then(() => setRender(render + 1))
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
notify.send(t("Cannot establish a connection"), "error");
|
||||
setIsChromecast(false);
|
||||
setIsLoading(false);
|
||||
});
|
||||
};
|
||||
const chromecastMediaHandler = (isAlive) => {
|
||||
if (isAlive) return;
|
||||
const session = Chromecast.session();
|
||||
if (session) {
|
||||
session.endSession();
|
||||
$video.current.muted = false;
|
||||
setVolume($video.current.volume * 100);
|
||||
setIsChromecast(false);
|
||||
onSeek(0);
|
||||
onPause();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="component_videoplayer">
|
||||
<MenuBar title={filename} download={data} />
|
||||
<div className="component_videoplayer" >
|
||||
<MenuBar title={filename} download={data}>
|
||||
<Icon name="fullscreen" onClick={onRequestFullscreen} />
|
||||
</MenuBar>
|
||||
<div className="video_container" ref={$container}>
|
||||
<ReactCSSTransitionGroup
|
||||
transitionName="video"
|
||||
|
|
@ -339,7 +384,11 @@ export function VideoPlayer({ filename, data, path }) {
|
|||
transitionEnter={true}
|
||||
transitionEnterTimeout={300}
|
||||
transitionAppearTimeout={300}>
|
||||
<div className={"video_screen" + (isPlaying ? " video-state-play" : " video-state-pause")}>
|
||||
<div className={
|
||||
"video_screen" +
|
||||
(isBuffering ? " video-state-buffer" : isPlaying ? " video-state-play" : " video-state-pause") +
|
||||
(isChromecast ? " is-casting-yes" : " is-casting-no")
|
||||
}>
|
||||
<div className="video_wrapper" style={isFullscreen() ? {
|
||||
maxHeight: "inherit",
|
||||
height: "inherit",
|
||||
|
|
@ -392,13 +441,6 @@ export function VideoPlayer({ filename, data, path }) {
|
|||
)
|
||||
}
|
||||
</span>
|
||||
<div className="pull-right">
|
||||
{
|
||||
<React.Fragment>
|
||||
<Icon name="fullscreen" onClick={onRequestFullscreen} />
|
||||
</React.Fragment>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,9 @@
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
&.video-state-pause .videoplayer_control { opacity: 1; transition: 0.1s opacity ease; }
|
||||
&.is-casting-yes .videoplayer_control,
|
||||
&.video-state-pause .videoplayer_control,
|
||||
&.video-state-buffer .videoplayer_control,
|
||||
&.video-state-play:hover .videoplayer_control { opacity: 1; transition: 0.1s opacity ease; }
|
||||
.videoplayer_control {
|
||||
transition: 0.5s opacity ease;
|
||||
|
|
@ -142,10 +144,6 @@
|
|||
width: 100%;
|
||||
}
|
||||
}
|
||||
.pull-right {
|
||||
margin-left: auto;
|
||||
padding-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
video {
|
||||
|
|
|
|||
Loading…
Reference in a new issue