Follow client setting to filter explicit tracks

- Don't load explicit tracks when the client setting forbids them

 - When a client switches explicit filtering on *while* playing
   an explicit track, immediately skip to the next track
This commit is contained in:
Roderick van Domburg 2021-12-30 23:50:28 +01:00
parent 2af34fc674
commit 0fdff0d3fd
No known key found for this signature in database
GPG key ID: A9EF5222A26F0451
5 changed files with 63 additions and 0 deletions

View file

@ -746,12 +746,17 @@ impl SpircTask {
_ => old_value, _ => old_value,
}; };
self.session.set_user_attribute(key, new_value); self.session.set_user_attribute(key, new_value);
trace!( trace!(
"Received attribute mutation, {} was {} is now {}", "Received attribute mutation, {} was {} is now {}",
key, key,
old_value, old_value,
new_value new_value
); );
if key == "filter-explicit-content" && new_value == "1" {
self.player.skip_explicit_content();
}
} else { } else {
trace!( trace!(
"Received attribute mutation for {} but key was not found!", "Received attribute mutation for {} but key was not found!",

View file

@ -26,6 +26,7 @@ pub struct AudioItem {
pub duration: i32, pub duration: i32,
pub availability: AudioItemAvailability, pub availability: AudioItemAvailability,
pub alternatives: Option<Tracks>, pub alternatives: Option<Tracks>,
pub is_explicit: bool,
} }
impl AudioItem { impl AudioItem {

View file

@ -80,6 +80,7 @@ impl InnerAudioItem for Episode {
duration: episode.duration, duration: episode.duration,
availability, availability,
alternatives: None, alternatives: None,
is_explicit: episode.is_explicit,
}) })
} }
} }

View file

@ -95,6 +95,7 @@ impl InnerAudioItem for Track {
duration: track.duration, duration: track.duration,
availability, availability,
alternatives, alternatives,
is_explicit: track.is_explicit,
}) })
} }
} }

View file

@ -98,6 +98,7 @@ enum PlayerCommand {
SetSinkEventCallback(Option<SinkEventCallback>), SetSinkEventCallback(Option<SinkEventCallback>),
EmitVolumeSetEvent(u16), EmitVolumeSetEvent(u16),
SetAutoNormaliseAsAlbum(bool), SetAutoNormaliseAsAlbum(bool),
SkipExplicitContent(),
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -456,6 +457,10 @@ impl Player {
pub fn set_auto_normalise_as_album(&self, setting: bool) { pub fn set_auto_normalise_as_album(&self, setting: bool) {
self.command(PlayerCommand::SetAutoNormaliseAsAlbum(setting)); self.command(PlayerCommand::SetAutoNormaliseAsAlbum(setting));
} }
pub fn skip_explicit_content(&self) {
self.command(PlayerCommand::SkipExplicitContent());
}
} }
impl Drop for Player { impl Drop for Player {
@ -478,6 +483,7 @@ struct PlayerLoadedTrackData {
bytes_per_second: usize, bytes_per_second: usize,
duration_ms: u32, duration_ms: u32,
stream_position_pcm: u64, stream_position_pcm: u64,
is_explicit: bool,
} }
enum PlayerPreload { enum PlayerPreload {
@ -513,6 +519,7 @@ enum PlayerState {
duration_ms: u32, duration_ms: u32,
stream_position_pcm: u64, stream_position_pcm: u64,
suggested_to_preload_next_track: bool, suggested_to_preload_next_track: bool,
is_explicit: bool,
}, },
Playing { Playing {
track_id: SpotifyId, track_id: SpotifyId,
@ -526,6 +533,7 @@ enum PlayerState {
stream_position_pcm: u64, stream_position_pcm: u64,
reported_nominal_start_time: Option<Instant>, reported_nominal_start_time: Option<Instant>,
suggested_to_preload_next_track: bool, suggested_to_preload_next_track: bool,
is_explicit: bool,
}, },
EndOfTrack { EndOfTrack {
track_id: SpotifyId, track_id: SpotifyId,
@ -608,6 +616,7 @@ impl PlayerState {
normalisation_data, normalisation_data,
stream_loader_controller, stream_loader_controller,
stream_position_pcm, stream_position_pcm,
is_explicit,
.. ..
} => { } => {
*self = EndOfTrack { *self = EndOfTrack {
@ -620,6 +629,7 @@ impl PlayerState {
bytes_per_second, bytes_per_second,
duration_ms, duration_ms,
stream_position_pcm, stream_position_pcm,
is_explicit,
}, },
}; };
} }
@ -648,6 +658,7 @@ impl PlayerState {
bytes_per_second, bytes_per_second,
stream_position_pcm, stream_position_pcm,
suggested_to_preload_next_track, suggested_to_preload_next_track,
is_explicit,
} => { } => {
*self = Playing { *self = Playing {
track_id, track_id,
@ -661,6 +672,7 @@ impl PlayerState {
stream_position_pcm, stream_position_pcm,
reported_nominal_start_time: None, reported_nominal_start_time: None,
suggested_to_preload_next_track, suggested_to_preload_next_track,
is_explicit,
}; };
} }
_ => { _ => {
@ -689,6 +701,7 @@ impl PlayerState {
stream_position_pcm, stream_position_pcm,
reported_nominal_start_time: _, reported_nominal_start_time: _,
suggested_to_preload_next_track, suggested_to_preload_next_track,
is_explicit,
} => { } => {
*self = Paused { *self = Paused {
track_id, track_id,
@ -701,6 +714,7 @@ impl PlayerState {
bytes_per_second, bytes_per_second,
stream_position_pcm, stream_position_pcm,
suggested_to_preload_next_track, suggested_to_preload_next_track,
is_explicit,
}; };
} }
_ => { _ => {
@ -778,6 +792,16 @@ impl PlayerTrackLoader {
audio.name, audio.spotify_uri audio.name, audio.spotify_uri
); );
let is_explicit = audio.is_explicit;
if is_explicit {
if let Some(value) = self.session.get_user_attribute("filter-explicit-content") {
if &value == "1" {
warn!("Track is marked as explicit, which client setting forbids.");
return None;
}
}
}
let audio = match self.find_available_alternative(audio).await { let audio = match self.find_available_alternative(audio).await {
Some(audio) => audio, Some(audio) => audio,
None => { None => {
@ -951,6 +975,7 @@ impl PlayerTrackLoader {
bytes_per_second, bytes_per_second,
duration_ms, duration_ms,
stream_position_pcm, stream_position_pcm,
is_explicit,
}); });
} }
} }
@ -1518,6 +1543,7 @@ impl PlayerInternal {
Instant::now() - Duration::from_millis(position_ms as u64), Instant::now() - Duration::from_millis(position_ms as u64),
), ),
suggested_to_preload_next_track: false, suggested_to_preload_next_track: false,
is_explicit: loaded_track.is_explicit,
}; };
} else { } else {
self.ensure_sink_stopped(false); self.ensure_sink_stopped(false);
@ -1533,6 +1559,7 @@ impl PlayerInternal {
bytes_per_second: loaded_track.bytes_per_second, bytes_per_second: loaded_track.bytes_per_second,
stream_position_pcm: loaded_track.stream_position_pcm, stream_position_pcm: loaded_track.stream_position_pcm,
suggested_to_preload_next_track: false, suggested_to_preload_next_track: false,
is_explicit: loaded_track.is_explicit,
}; };
self.send_event(PlayerEvent::Paused { self.send_event(PlayerEvent::Paused {
@ -1674,6 +1701,7 @@ impl PlayerInternal {
bytes_per_second, bytes_per_second,
duration_ms, duration_ms,
normalisation_data, normalisation_data,
is_explicit,
.. ..
} }
| PlayerState::Paused { | PlayerState::Paused {
@ -1683,6 +1711,7 @@ impl PlayerInternal {
bytes_per_second, bytes_per_second,
duration_ms, duration_ms,
normalisation_data, normalisation_data,
is_explicit,
.. ..
} = old_state } = old_state
{ {
@ -1693,6 +1722,7 @@ impl PlayerInternal {
bytes_per_second, bytes_per_second,
duration_ms, duration_ms,
stream_position_pcm, stream_position_pcm,
is_explicit,
}; };
self.preload = PlayerPreload::None; self.preload = PlayerPreload::None;
@ -1943,6 +1973,30 @@ impl PlayerInternal {
PlayerCommand::SetAutoNormaliseAsAlbum(setting) => { PlayerCommand::SetAutoNormaliseAsAlbum(setting) => {
self.auto_normalise_as_album = setting self.auto_normalise_as_album = setting
} }
PlayerCommand::SkipExplicitContent() => {
if let PlayerState::Playing {
track_id,
play_request_id,
is_explicit,
..
}
| PlayerState::Paused {
track_id,
play_request_id,
is_explicit,
..
} = self.state
{
if is_explicit {
warn!("Currently loaded track is explicit, which client setting forbids -- skipping to next track.");
self.send_event(PlayerEvent::EndOfTrack {
track_id,
play_request_id,
})
}
}
}
}; };
Ok(result) Ok(result)
@ -2080,6 +2134,7 @@ impl fmt::Debug for PlayerCommand {
.debug_tuple("SetAutoNormaliseAsAlbum") .debug_tuple("SetAutoNormaliseAsAlbum")
.field(&setting) .field(&setting)
.finish(), .finish(),
PlayerCommand::SkipExplicitContent() => f.debug_tuple("SkipExplicitContent").finish(),
} }
} }
} }