-
Notifications
You must be signed in to change notification settings - Fork 638
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Dealer: Rework context retrieval #1414
base: dev
Are you sure you want to change the base?
Conversation
- resolved contexts independently, by that we don't block our main loop - move resolve logic into own file - polish handling for play and transfer
When finishing a context it does seem to reset the current track correctly, but doesn't update the next tracks correclty. probably wrong:
|
Quickly seeking to beginning (just pressing prev) and pausing can update the state incorrectly. Log Snippet
|
- rename `handle_context` to `handle_next_context` - disconnect should only pause the playback - find_next should not exceed queue length
Copilot
AI
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copilot reviewed 6 out of 12 changed files in this pull request and generated 2 comments.
Files not reviewed (6)
- connect/src/state/options.rs: Evaluated as low risk
- connect/src/model.rs: Evaluated as low risk
- core/src/spclient.rs: Evaluated as low risk
- connect/src/state/transfer.rs: Evaluated as low risk
- connect/src/state/handle.rs: Evaluated as low risk
- connect/src/state/tracks.rs: Evaluated as low risk
Comments suppressed due to low confidence (1)
connect/src/state/context.rs:267
- Ensure that the behavior of returning
Ok(None)
whennext_contexts
is empty is covered by tests.
if next_contexts.is_empty() { return Ok(None); }
Currently the initial transfer is not as smooth as it was before these changes. I will fix that, and afterwards remove the draft state. Is probably be a small fix. |
- fix context_metadata and restriction incorrect reset - do some state updates earlier - add more logging
Hopefully last points:
|
just mimics official client behavior
# Conflicts: # connect/src/spirc.rs
Besides the incorrect tests, there seems to be a missing track update when starting a playback with shuffle and an specified track. Besides that the state feels quite stable again, so I will probably mark the PR as ready this evening. |
Hey @photovoltex. I tested the PR and it seems to work (at least that what I tested) :D Don't know if this belongs in this PR or if I should rather create an issue: I discovered that when playing a search result with autoplay, then sometimes the same song is played again when skipping to the next song. Sometimes it is not played directly again but after like 2-4 songs: Log after clicking on the search result:
No log after clicking skip. Btw: Congrats on becoming a maintainer :D |
Now, just as I have written the comment, I realized that it is no longer possible to select another song to play from the liked songs playlist if shuffle is enabled. xD If selecting a track, it always jumps to the beginning of the playlist or to the beginning of the currently played song... It works when shuffle is off. |
see here #1414 (comment) hehe
I also encountered that Problem and didn't think much about it. But seems to be a Problem of the current solution/branch. I think I already know where the problem lies heh. So that shouldn't be much of a problem to solve. As always thanks for testing this branch. Helps a lot :D |
Now I feel stupid, completely overlooked that somehow xD |
Huh... the autoplay endpoint seems to always provide the initial track again. That seems to be the "normal" behavior. I wonder why that didn't happen before... Well time to fix that. |
Wait I take that back... I have a track where it behaves like that and another's where it doesn't... the hell I suppose when it only affects autoplay we can filter out the track from which we requested the autoplay (so for singles/tracks just the track). Weird behavior tho... I wonder why it didn't seem to happen before. I checked the code that changed, but the relevant parts didn't change. |
Yup. Now to make it more confusing, it isn't even always the first track after skipping, as I described above (example: Feliz Navidad, at least for me). Will test your recent changes tomorrow. |
I look at "Feliz Navidad" and there is another "Feliz Navidad" in the autoplay context, but with a different |
- removes previously added workaround
This should "fix" the weird behavior, as we now provide all recently played songs, in which the current track will also be contained, and by that should be not included anymore in the autoplay tracks. But thats for spotify to decide in the end^^ |
@photovoltex I tested it and the tracks are no longer duplicated. I also was quite sure that when I tried playing Feliz Navidad, that there was the exact same track duplicated, now it's a track with even a different name... so issue fixed I guess :D Selecting a song when playing shuffled also works now. |
Copilot
AI
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copilot reviewed 6 out of 13 changed files in this pull request and generated 1 comment.
Files not reviewed (7)
- connect/src/state/options.rs: Evaluated as low risk
- connect/src/state/transfer.rs: Evaluated as low risk
- core/src/spclient.rs: Evaluated as low risk
- connect/src/state/handle.rs: Evaluated as low risk
- examples/play_connect.rs: Evaluated as low risk
- connect/src/lib.rs: Evaluated as low risk
- connect/src/state/tracks.rs: Evaluated as low risk
Comments suppressed due to low confidence (5)
connect/src/context_resolver.rs:170
- The debug message contains a minor grammar issue. It should be 'context was requested {} seconds ago'.
debug!("context was requested {}s ago, trying again to resolve the requested context", last_try.expect("checked by condition").as_secs());
connect/src/context_resolver.rs:232
- The behavior of
get_next_context
whenUpdateContext::Autoplay
is used should be covered by tests to ensure that it handles unsupported contexts for podcasts correctly.
pub async fn get_next_context(&self, recent_track_uri: impl Fn() -> Vec<String>) -> Result<Context, Error> {
connect/src/context_resolver.rs:33
- [nitpick] The name
ResolveContext
might be ambiguous. It could be renamed toContextResolution
to better reflect its purpose.
pub(super) struct ResolveContext {
core/src/dealer/protocol.rs:177
- The word "send" should be corrected to "sent".
trace!("message was send with {encoding} encoding ");
connect/src/state.rs:358
- The update_context_index method is called with new_index + 1, which might cause an off-by-one error. It should be called with new_index.
self.update_context_index(self.active_context, new_index + 1)?;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, some general feedback on code.
connect/src/context_resolver.rs
Outdated
|
||
fn find_next(&self) -> Option<(&ResolveContext, &str, usize)> { | ||
let mut idx = 0; | ||
loop { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not change into a for
loop? You can still break out of that.
Resolve::Uri(ref uri) => ConnectState::valid_resolve_uri(uri), | ||
Resolve::Context(ref ctx) => ConnectState::get_context_uri_from_context(ctx), | ||
} | ||
.or(self.fallback.as_deref()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this work correctly? I believe or
is eagerly evaluated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As it doesn't evaluated any values it shouldn't matter if it's eagerly or not. Unless there isn't any benefit to it, I would just keep it as is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just thinking that this will probably always evaluate to None
because of the eager evaluation at compile time, not sure if that's the behavior you intend?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But why would it always evaluate to None
, I think eagerly evaluated just means always/early evaluated? As self.fallback
is an Option<String>
it should only provide the value in place. I think the only difference would be the evaluation of as_deref()
. But this is only used here to convert Option<String>
into Option<&str>
.
Just want to understand where you are coming from, as from my perspective it shouldn't make a difference.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right.
connect/src/context_resolver.rs
Outdated
|
||
impl Eq for ResolveContext {} | ||
|
||
impl Hash for ResolveContext { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess deriving Hash
on the structs doesn't work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not quit easily as Context
doesn't implement Hash
. I could implement Hash
and Eq
in the protocol crate, then I could just derive the trait. Both ways seems fine to me. Just tell me what you would prefer :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No problem. Certainly if you need to patch up protobuf generated code, I'd say don't bother.
connect/src/context_resolver.rs
Outdated
|
||
pub fn remove_used_and_invalid(&mut self) { | ||
if let Some((_, _, remove)) = self.find_next() { | ||
for _ in 0..remove { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe nicer to use drain
: https://doc.rust-lang.org/std/collections/struct.VecDeque.html#method.drain
connect/src/model.rs
Outdated
impl TryFrom<SkipTo> for PlayingTrack { | ||
type Error = (); | ||
|
||
fn try_from(value: SkipTo) -> Result<Self, ()> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Most idiomatic to return Self::Error
.
connect/src/state/context.rs
Outdated
.first() | ||
.and_then(|p| p.tracks.first().map(|t| &t.uri)) | ||
pub fn get_context_uri_from_context(context: &Context) -> Option<&str> { | ||
match Self::valid_resolve_uri(&context.uri) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At your option (heh) you could also go for or_else
.
connect/src/state/context.rs
Outdated
let ctx = match self.get_context(&new_context) { | ||
let player = self.player_mut(); | ||
player.context_metadata.clear(); | ||
player.restrictions.clear(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How much can context_metadata
and restrictions
differ in size? Clearing keeps the memory allocated; does it make sense to free it (shrink it) or keep it around? I can imagine it makes sense to keep it, so just asking.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Never thought about that before. Anyhow, the metadata provided for context can be a big difference. For example an artists context is about two entries and the discovery weekly has more then 10 entries. For restrictions it's usually one to six, but it could be more.
For restrictions it probably doesn't make much sense to free it, but for the metadata could be. But could also be that for just ten entries it isn't worth it to free the instance.
Btw. do you mean by freeing dropping the current map and creating a new instance?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Two options:
-
Dropping then assigning a new object, like you say, or
-
Calling
shrink_to_fit
.
Honestly I don't know about the performance difference between dropping+creating versus clearing+shrinking, probably both just fine in this case.
- use drain instead of for with pop - use for instead of loop - use or_else instead of match - use Self::Error instead of the value - free memory for metadata and restrictions
I added another fix around |
And another small fix that adjusts the setting of the Last change as I will not add anything more. |
# Conflicts: # connect/src/model.rs # connect/src/spirc.rs # connect/src/state.rs # connect/src/state/context.rs # connect/src/state/transfer.rs # protocol/src/lib.rs
This resolves the issue that a context with multiple pages couldn't be completely shuffled. I had to rework most of the context resolving part, because resolving the entire context at once wasn't an option. Pretty much blocked the entire main loop for like ~1-30sec depending on the context. Which blocked all processing of commands and or events.
Each context is now requested independently so that commands can fit between requests :D
additional fixes:
match
blocksplayback_speed
according to previously mentioned here Spirc: Replace Mecury with Dealer #1356 (comment)I will let it stay in draft for some days, so I can still test it a bit more during the day. And maybe someone else even wants to try it^^