Skip to content

Commit

Permalink
Harden YouTube playback a bit more when Spotify is not running
Browse files Browse the repository at this point in the history
  • Loading branch information
udoprog committed Mar 13, 2021
1 parent d2b969e commit cdb081f
Show file tree
Hide file tree
Showing 11 changed files with 1,744 additions and 837 deletions.
2 changes: 1 addition & 1 deletion bot-ui/dist/index.html
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<!doctype html><html lang="en"><head><base href="/"><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta http-equiv="X-UA-Compatible" content="ie=edge"><link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"><title>OxidizeBot - Local</title><link rel="icon" href="favicon.ico"><script defer="defer" src="190.c0fef7a89d3cb7fd6bfc.js"></script><script defer="defer" src="main.a97f53876457d888aacd.js"></script></head><body><section id="index"></section></body></html>
<!doctype html><html lang="en"><head><base href="/"><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta http-equiv="X-UA-Compatible" content="ie=edge"><link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"><title>OxidizeBot - Local</title><link rel="icon" href="favicon.ico"><script defer="defer" src="190.c0fef7a89d3cb7fd6bfc.js"></script><script defer="defer" src="main.24e1b447e76875d2869d.js"></script></head><body><section id="index"></section></body></html>
Original file line number Diff line number Diff line change
Expand Up @@ -7943,8 +7943,7 @@ var YouTube = /*#__PURE__*/function (_React$Component) {
_this.player = null;
_this.playerRef = /*#__PURE__*/react.createRef();
_this.state = {
stopped: true,
paused: true,
playing: false,
loading: true,
events: [],
api: null,
Expand All @@ -7969,46 +7968,48 @@ var YouTube = /*#__PURE__*/function (_React$Component) {
case "youtube/current":
switch (data.event.type) {
case "play":
var update = {
stopped: false,
paused: false
};
var update = {};

if (!this.state.playing) {
this.player.playVideo();
update.playing = true;
}

if (this.state.videoId !== data.event.video_id) {
var videoId = data.event.video_id;
this.player.loadVideoById({
videoId: videoId,
suggestedQuality: SUGGESTED_QUALITY
});
this.player.seekTo(data.event.elapsed);
this.player.playVideo();
this.player.seekTo(data.event.elapsed, true);
update.videoId = data.event.video_id;
} else {
if (this.state.paused) {
this.player.playVideo();
}

// We are a bit out of sync.
if (Math.abs(data.event.elapsed - this.player.getCurrentTime()) > 2) {
this.player.seekTo(data.event.elapsed);
this.player.seekTo(data.event.elapsed, true);
}
}

this.setState(update);
break;

case "pause":
this.player.pauseVideo();
if (this.state.playing) {
this.player.pauseVideo();
}

this.setState({
stopped: false,
paused: true
playing: false
});
break;

case "stop":
this.player.pauseVideo();
if (this.state.playing) {
this.player.stopVideo();
}

this.setState({
stopped: true,
paused: false,
playing: false,
videoId: null
});
break;
Expand Down Expand Up @@ -8045,12 +8046,22 @@ var YouTube = /*#__PURE__*/function (_React$Component) {
autoplay: false,
events: {
onReady: function onReady() {
if (_this2.state.playing) {
_this2.player.playVideo();
}

_this2.setState({
loading: false
});
},
onPlaybackQualityChange: function onPlaybackQualityChange(e) {},
onStateChange: function onStateChange(e) {}
onStateChange: function onStateChange(event) {
if (event.data === -1) {
if (_this2.state.playing) {
_this2.player.playVideo();
}
}
}
}
});
}
Expand Down
53 changes: 28 additions & 25 deletions bot-ui/src/components/YouTube.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ export default class YouTube extends React.Component {
this.playerRef = React.createRef();

this.state = {
stopped: true,
paused: true,
playing: false,
loading: true,
events: [],
api: null,
Expand All @@ -48,45 +47,40 @@ export default class YouTube extends React.Component {
case "youtube/current":
switch (data.event.type) {
case "play":
let update = {
stopped: false,
paused: false,
};
let update = {};

if (!this.state.playing) {
this.player.playVideo();
update.playing = true;
}

if (this.state.videoId !== data.event.video_id) {
let videoId = data.event.video_id;
this.player.loadVideoById({videoId, suggestedQuality: SUGGESTED_QUALITY});
this.player.seekTo(data.event.elapsed);
this.player.playVideo();
this.player.seekTo(data.event.elapsed, true);
update.videoId = data.event.video_id;
} else {
if (this.state.paused) {
this.player.playVideo();
}

// We are a bit out of sync.
if (Math.abs(data.event.elapsed - this.player.getCurrentTime()) > 2) {
this.player.seekTo(data.event.elapsed);
this.player.seekTo(data.event.elapsed, true);
}
}

this.setState(update);
break;
case "pause":
this.player.pauseVideo();
if (this.state.playing) {
this.player.pauseVideo();
}

this.setState({
stopped: false,
paused: true,
});
this.setState({ playing: false });
break;
case "stop":
this.player.pauseVideo();
if (this.state.playing) {
this.player.stopVideo();
}

this.setState({
stopped: true,
paused: false,
videoId: null,
});
this.setState({ playing: false, videoId: null });
break;
default:
break;
Expand Down Expand Up @@ -114,13 +108,22 @@ export default class YouTube extends React.Component {
autoplay: false,
events: {
onReady: () => {
if (this.state.playing) {
this.player.playVideo();
}

this.setState({
loading: false,
});
},
onPlaybackQualityChange: e => {
},
onStateChange: e => {
onStateChange: event => {
if (event.data === -1) {
if (this.state.playing) {
this.player.playVideo();
}
}
},
}
});
Expand Down
2 changes: 1 addition & 1 deletion bot/src/module/song/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ impl command::Handler for Handler {
None => player::ModifyVolume::Set(argument),
};

match player.volume(volume).await? {
match player.volume(volume).await {
Some(volume) => {
respond!(ctx, format!("Updated volume to {}.", volume));
}
Expand Down
112 changes: 62 additions & 50 deletions bot/src/player/connect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ pub(super) async fn setup(
let returned_player = player.clone();

let future = async move {
player.volume_update_log(scaled_volume).await;
warn_on_error(player.volume_update(scaled_volume).await);

loop {
tokio::select! {
Expand All @@ -65,12 +65,12 @@ pub(super) async fn setup(
update = volume_scale_stream.recv() => {
*volume_scale.write().await = update;
scaled_volume = (volume.load().await * update) / 100u32;
player.volume_update_log(scaled_volume).await;
warn_on_error(player.volume_update(scaled_volume).await);
}
update = volume_stream.recv() => {
*volume.write().await = update;
scaled_volume = (update * volume_scale.load().await) / 100u32;
player.volume_update_log(scaled_volume).await;
warn_on_error(player.volume_update(scaled_volume).await);
}
}
}
Expand Down Expand Up @@ -111,12 +111,9 @@ pub(super) struct ConnectPlayer {
}

impl ConnectPlayer {
/// Play the specified song.
pub(super) async fn play(
&self,
id: Option<SpotifyId>,
elapsed: Option<Duration>,
) -> Result<(), ConnectError> {
/// Play the specified song. Or just starting playing if the id of the song
/// is unspecified.
pub(super) async fn play(&self, id: Option<SpotifyId>, elapsed: Option<Duration>) {
let track_uri = id.map(|id| format!("spotify:track:{}", id.to_base62()));
let elapsed = elapsed.map(|elapsed| elapsed.as_millis() as u64);
let device_id = self.device.load().await;
Expand All @@ -126,65 +123,77 @@ impl ConnectPlayer {
.me_player_play(device_id.as_deref(), track_uri.as_deref(), elapsed)
.await;

ConnectError::handle(result, "play")
warn_on_error(ConnectError::handle(result, "play"));
}

/// Enqueue the specified song to play next.
pub(super) async fn queue(&self, id: SpotifyId) -> Result<(), ConnectError> {
let track_uri = format!("spotify:track:{}", id.to_base62());
let device_id = self.device.load().await;

let result = self
.spotify
.me_player_queue(device_id.as_deref(), &track_uri)
.await;

ConnectError::handle(result, "queue")
}

pub(super) async fn next(&self) -> Result<(), ConnectError> {
/// Play the next song.
pub(super) async fn next(&self) {
let device_id = self.device.load().await;
let result = self.spotify.me_player_next(device_id.as_deref()).await;
ConnectError::handle(result, "skip")
warn_on_error(ConnectError::handle(result, "skip"));
}

pub(super) async fn pause(&self) -> Result<(), ConnectError> {
/// Pause playback.
pub(super) async fn pause(&self) {
let device_id = self.device.load().await;
ConnectError::handle(

warn_on_error(ConnectError::handle(
self.spotify.me_player_pause(device_id.as_deref()).await,
"pause",
)
));
}

pub(super) async fn stop(&self) -> Result<(), ConnectError> {
/// Stop playback.
pub(super) async fn stop(&self) {
let device_id = self.device.load().await;
ConnectError::handle(

warn_on_error(ConnectError::handle(
self.spotify.me_player_pause(device_id.as_deref()).await,
"stop",
)
));
}

/// Update an unscaled volume.
pub(super) async fn set_scaled_volume(&self, scaled_volume: u32) -> Result<u32, ConnectError> {
/// Update a scaled volume.
pub(super) async fn set_scaled_volume(&self, scaled_volume: u32) {
let volume_scale = self.volume_scale.load().await;
let update = u32::min((scaled_volume * 100) / volume_scale, 100);
self.volume(player::ModifyVolume::Set(update)).await
self.volume(player::ModifyVolume::Set(update)).await;
}

/// Modify the volume of the player.
pub(super) async fn volume(&self, modify: player::ModifyVolume) -> Result<u32, ConnectError> {
/// Get the current volume of the player.
pub(super) async fn current_volume(&self) -> u32 {
self.volume.load().await
}

/// Enqueue the specified song to play next.
pub(super) async fn queue(&self, id: SpotifyId) -> Result<(), ConnectError> {
let track_uri = format!("spotify:track:{}", id.to_base62());
let device_id = self.device.load().await;

let result = self
.spotify
.me_player_queue(device_id.as_deref(), &track_uri)
.await;

ConnectError::handle(result, "queue")
}

/// Internal function to modify the volume of the player.
pub(super) async fn volume(&self, modify: player::ModifyVolume) -> u32 {
let volume = self.volume.load().await;
let update = modify.apply(volume);
self.settings

let result = self
.settings
.set("volume", update)
.await
.map_err(|e| ConnectError::Error("update volume settings", e.into()))?;
Ok(update)
}
.map_err(|e| ConnectError::Error("update volume settings", e.into()));

/// Get the current volume of the player.
pub(super) async fn current_volume(&self) -> u32 {
self.volume.load().await
if let Err(e) = result {
log_error!(e, "failed to store updated volume in settings");
}

update
}

async fn volume_update(&self, volume: u32) -> Result<(), ConnectError> {
Expand All @@ -197,13 +206,6 @@ impl ConnectPlayer {
"volume",
)
}

/// Same as volume update, but logs instead of errors.
async fn volume_update_log(&self, volume: u32) {
if let Err(e) = self.volume_update(volume).await {
log_warn!(e, "Failed to update volume");
}
}
}

pub(super) struct ConnectStream {
Expand Down Expand Up @@ -264,3 +266,13 @@ impl ConnectDevice {
Ok(())
}
}

/// Discards a result and log a warning on errors.
fn warn_on_error<T, E>(result: Result<T, E>)
where
Error: From<E>,
{
if let Err(e) = result {
log_warn!(e, "failed to issue connect command");
}
}
Loading

0 comments on commit cdb081f

Please sign in to comment.