Can't jump to specific track in playlist
Describe the bug I loaded up a playlist. Then I wanted to jump to the last track in the list to see how it would handle an "end of queue" event. I could click any track in the list, but it would only stop playback, not jump to that track.
To reproduce Steps to reproduce the behavior:
-
librespot -c . -name tescht --verbose &> test.log(yes, I know, got thenameparameter wrong, but anyway...) - Connect with Spotify for macOS (Apple Silicon) 1.2.45.454.gc16ec9f6
- Play https://open.spotify.com/playlist/37i9dQZF1DZ06evO2UhT7X
- Try to play a random track in the queue
Log Too long to embed here, I guess. I'm not sure what the relevant part of it is: test.log
Host (what you are running librespot on):
- OS: macOS 14.6.1
- Platform: Mac14,9 (MacBook Pro M2 Pro, 14")
-
librespot 0.5.0-dev e02e4ae (Built on 2024-09-26, Build ID: 0kQU3dVK, Profile: debug)
Additional context
The same interaction would work if playing from the application directly (not sending to librespot)
I can't see anything going wrong in the log, it seems to be doing what it's been told to. I also can't reproduce this, but I don't have the macOS client. Does it behave any better with a different audio backend i.e. passthrough?
I first encountered it with the pipe backend, yes. But as that was using my fork. I now tried again with passthrough and pipe on the dev branch and am seeing the same.
The problem really is that librespot seems to totally ignore the fact that I clicked a button. There's nothing logged, nor would playback jump. In pipe mode, though, playback paused. I'm not sure whether there's a timing related issue, as piping would simply download the full track and jump to the next within seconds anyway. But I see nothing logged in default logging mode. In my log file OTOH you'd see the following when playback of a track starts:
[2024-09-26T05:53:07Z DEBUG librespot_playback::player] command=Play
Then when I click a track:
[2024-09-26T05:53:11Z TRACE librespot_connect::spirc] Received update frame: Frame {
...
}
[2024-09-26T05:53:11Z TRACE librespot_connect::spirc] Sending status to server: [kPlayStatusPause]
[2024-09-26T05:53:11Z DEBUG librespot_playback::player] command=Pause
So I must assume there's something in that big Frame dump (900 lines?) which tells librespot to stop?
I should probably have dumped that frame here:
[2024-09-26T05:53:11Z TRACE librespot_connect::spirc] Received update frame: Frame {
version: Some(
1,
),
ident: Some(
"...",
),
protocol_version: Some(
"2.0.0",
),
seq_nr: Some(
753139219,
),
typ: Some(
kMessageTypePause,
),
device_state: MessageField(
Some(
DeviceState {
sw_version: Some(
"1.2.45.454.gc16ec9f6",
),
is_active: Some(
false,
),
can_play: Some(
true,
),
volume: Some(
28371,
),
name: Some(
"...",
),
error_code: None,
became_active_at: None,
error_message: None,
capabilities: [
Capability {
typ: Some(
kCanBePlayer,
),
intValue: [
1,
],
stringValue: [],
special_fields: SpecialFields {
unknown_fields: UnknownFields {
fields: None,
},
cached_size: CachedSize {
size: 0,
},
},
},
Capability {
typ: Some(
kRestrictToLocal,
),
intValue: [
0,
],
stringValue: [],
special_fields: SpecialFields {
unknown_fields: UnknownFields {
fields: None,
},
cached_size: CachedSize {
size: 0,
},
},
},
Capability {
typ: Some(
kGaiaEqConnectId,
),
intValue: [
1,
],
stringValue: [],
special_fields: SpecialFields {
unknown_fields: UnknownFields {
fields: None,
},
cached_size: CachedSize {
size: 0,
},
},
},
Capability {
typ: Some(
kSupportsLogout,
),
intValue: [
1,
],
stringValue: [],
special_fields: SpecialFields {
unknown_fields: UnknownFields {
fields: None,
},
cached_size: CachedSize {
size: 0,
},
},
},
Capability {
typ: Some(
kIsObservable,
),
intValue: [
1,
],
stringValue: [],
special_fields: SpecialFields {
unknown_fields: UnknownFields {
fields: None,
},
cached_size: CachedSize {
size: 0,
},
},
},
Capability {
typ: Some(
kCommandAcks,
),
intValue: [
1,
],
stringValue: [],
special_fields: SpecialFields {
unknown_fields: UnknownFields {
fields: None,
},
cached_size: CachedSize {
size: 0,
},
},
},
Capability {
typ: Some(
kSupportsRename,
),
intValue: [
1,
],
stringValue: [],
special_fields: SpecialFields {
unknown_fields: UnknownFields {
fields: None,
},
cached_size: CachedSize {
size: 0,
},
},
},
Capability {
typ: Some(
kHidden,
),
intValue: [
0,
],
stringValue: [],
special_fields: SpecialFields {
unknown_fields: UnknownFields {
fields: None,
},
cached_size: CachedSize {
size: 0,
},
},
},
Capability {
typ: Some(
kDeviceType,
),
intValue: [
1,
],
stringValue: [],
special_fields: SpecialFields {
unknown_fields: UnknownFields {
fields: None,
},
cached_size: CachedSize {
size: 0,
},
},
},
Capability {
typ: Some(
kVolumeSteps,
),
intValue: [
64,
],
stringValue: [],
special_fields: SpecialFields {
unknown_fields: UnknownFields {
fields: None,
},
cached_size: CachedSize {
size: 0,
},
},
},
Capability {
typ: Some(
kSupportsPlaylistV2,
),
intValue: [
1,
],
stringValue: [],
special_fields: SpecialFields {
unknown_fields: UnknownFields {
fields: None,
},
cached_size: CachedSize {
size: 0,
},
},
},
Capability {
typ: Some(
kSupportsExternalEpisodes,
),
intValue: [
1,
],
stringValue: [],
special_fields: SpecialFields {
unknown_fields: UnknownFields {
fields: None,
},
cached_size: CachedSize {
size: 0,
},
},
},
Capability {
typ: Some(
kSupportedTypes,
),
intValue: [],
stringValue: [
"audio/ad",
"audio/episode",
"audio/episode+track",
"audio/interruption",
"audio/local",
"audio/track",
"video/ad",
"video/episode",
"video/track",
],
special_fields: SpecialFields {
unknown_fields: UnknownFields {
fields: None,
},
cached_size: CachedSize {
size: 0,
},
},
},
],
context_player_error: None,
metadata: [
Metadata {
type_: Some(
"tier1_port",
),
metadata: Some(
"0",
),
special_fields: SpecialFields {
unknown_fields: UnknownFields {
fields: None,
},
cached_size: CachedSize {
size: 0,
},
},
},
Metadata {
type_: Some(
"device_address_mask",
),
metadata: Some(
"192.168.0.100/24",
),
special_fields: SpecialFields {
unknown_fields: UnknownFields {
fields: None,
},
cached_size: CachedSize {
size: 0,
},
},
},
Metadata {
type_: Some(
"debug_level",
),
metadata: Some(
"1",
),
special_fields: SpecialFields {
unknown_fields: UnknownFields {
fields: None,
},
cached_size: CachedSize {
size: 0,
},
},
},
Metadata {
type_: Some(
"client_id",
),
metadata: Some(
"....",
),
special_fields: SpecialFields {
unknown_fields: UnknownFields {
fields: None,
},
cached_size: CachedSize {
size: 0,
},
},
},
Metadata {
type_: Some(
"brand_display_name",
),
metadata: Some(
"spotify",
),
special_fields: SpecialFields {
unknown_fields: UnknownFields {
fields: None,
},
cached_size: CachedSize {
size: 0,
},
},
},
Metadata {
type_: Some(
"model_display_name",
),
metadata: Some(
"Mac14,9",
),
special_fields: SpecialFields {
unknown_fields: UnknownFields {
fields: None,
},
cached_size: CachedSize {
size: 0,
},
},
},
],
special_fields: SpecialFields {
unknown_fields: UnknownFields {
fields: None,
},
cached_size: CachedSize {
size: 0,
},
},
},
),
),
goodbye: MessageField(
None,
),
state: MessageField(
Some(
State {
context_uri: Some(
"spotify:playlist:37i9dQZF1DZ06evO2UhT7X",
),
index: Some(
10,
),
position_ms: Some(
57414,
),
status: Some(
kPlayStatusPlay,
),
position_measured_at: Some(
1727329987911,
),
context_description: Some(
"",
),
shuffle: Some(
false,
),
repeat: Some(
false,
),
last_command_ident: None,
last_command_msgid: None,
playing_from_fallback: Some(
true,
),
row: Some(
0,
),
playing_track_index: Some(
10,
),
track: [
TrackRef {
gid: Some(
[
171,
214,
93,
94,
119,
112,
79,
106,
191,
119,
101,
184,
200,
173,
140,
85,
],
),
uri: None,
queued: None,
context: None,
special_fields: SpecialFields {
unknown_fields: UnknownFields {
fields: None,
},
cached_size: CachedSize {
size: 0,
},
},
},
TrackRef {
gid: Some(
[
17,
200,
165,
122,
7,
143,
66,
91,
157,
16,
135,
24,
181,
170,
226,
102,
],
),
uri: None,
queued: None,
context: None,
special_fields: SpecialFields {
unknown_fields: UnknownFields {
fields: None,
},
cached_size: CachedSize {
size: 0,
},
},
},
TrackRef {
gid: Some(
[
225,
253,
245,
59,
173,
155,
72,
187,
158,
238,
187,
58,
125,
202,
52,
74,
],
),
uri: None,
queued: None,
context: None,
special_fields: SpecialFields {
unknown_fields: UnknownFields {
fields: None,
},
cached_size: CachedSize {
size: 0,
},
},
},
TrackRef {
gid: Some(
[
191,
202,
168,
182,
237,
111,
72,
18,
170,
128,
54,
220,
97,
172,
241,
148,
],
),
uri: None,
queued: None,
context: None,
special_fields: SpecialFields {
unknown_fields: UnknownFields {
fields: None,
},
cached_size: CachedSize {
size: 0,
},
},
},
TrackRef {
gid: Some(
[
147,
95,
166,
220,
117,
185,
65,
177,
149,
30,
85,
42,
74,
242,
246,
252,
],
),
uri: None,
queued: None,
context: None,
special_fields: SpecialFields {
unknown_fields: UnknownFields {
fields: None,
},
cached_size: CachedSize {
size: 0,
},
},
},
TrackRef {
gid: Some(
[
44,
203,
188,
252,
76,
3,
77,
21,
173,
8,
67,
216,
194,
100,
5,
159,
],
),
uri: None,
queued: None,
context: None,
special_fields: SpecialFields {
unknown_fields: UnknownFields {
fields: None,
},
cached_size: CachedSize {
size: 0,
},
},
},
TrackRef {
gid: Some(
[
20,
200,
15,
76,
49,
81,
75,
76,
187,
162,
150,
68,
0,
126,
232,
251,
],
),
uri: None,
queued: None,
context: None,
special_fields: SpecialFields {
unknown_fields: UnknownFields {
fields: None,
},
cached_size: CachedSize {
size: 0,
},
},
},
TrackRef {
gid: Some(
[
42,
207,
45,
30,
18,
203,
79,
242,
132,
142,
10,
155,
33,
81,
88,
206,
],
),
uri: None,
queued: None,
context: None,
special_fields: SpecialFields {
unknown_fields: UnknownFields {
fields: None,
},
cached_size: CachedSize {
size: 0,
},
},
},
TrackRef {
gid: Some(
[
98,
171,
228,
233,
109,
253,
71,
85,
186,
44,
252,
151,
201,
17,
234,
176,
],
),
uri: None,
queued: None,
context: None,
special_fields: SpecialFields {
unknown_fields: UnknownFields {
fields: None,
},
cached_size: CachedSize {
size: 0,
},
},
},
TrackRef {
gid: Some(
[
211,
62,
50,
218,
155,
116,
66,
152,
147,
14,
59,
87,
103,
237,
228,
91,
],
),
uri: None,
queued: None,
context: None,
special_fields: SpecialFields {
unknown_fields: UnknownFields {
fields: None,
},
cached_size: CachedSize {
size: 0,
},
},
},
TrackRef {
gid: Some(
[
231,
250,
60,
90,
170,
31,
79,
148,
191,
4,
165,
198,
114,
185,
8,
16,
],
),
uri: None,
queued: None,
context: None,
special_fields: SpecialFields {
unknown_fields: UnknownFields {
fields: None,
},
cached_size: CachedSize {
size: 0,
},
},
},
TrackRef {
gid: Some(
[
62,
124,
13,
101,
197,
52,
64,
0,
149,
231,
177,
4,
226,
236,
106,
49,
],
),
uri: None,
queued: None,
context: None,
special_fields: SpecialFields {
unknown_fields: UnknownFields {
fields: None,
},
cached_size: CachedSize {
size: 0,
},
},
},
TrackRef {
gid: Some(
[
76,
8,
197,
235,
90,
62,
65,
168,
152,
157,
88,
168,
211,
22,
113,
175,
],
),
uri: None,
queued: None,
context: None,
special_fields: SpecialFields {
unknown_fields: UnknownFields {
fields: None,
},
cached_size: CachedSize {
size: 0,
},
},
},
TrackRef {
gid: Some(
[
114,
248,
190,
30,
9,
198,
75,
125,
177,
18,
135,
180,
26,
4,
244,
229,
],
),
uri: None,
queued: None,
context: None,
special_fields: SpecialFields {
unknown_fields: UnknownFields {
fields: None,
},
cached_size: CachedSize {
size: 0,
},
},
},
],
ad: MessageField(
None,
),
special_fields: SpecialFields {
unknown_fields: UnknownFields {
fields: Some(
{
29: UnknownValues {
fixed32: [],
fixed64: [],
varint: [
1000,
],
length_delimited: [],
},
},
),
},
cached_size: CachedSize {
size: 0,
},
},
},
),
),
position: None,
volume: None,
state_update_id: Some(
1727329987917,
),
recipient: [
"0d04d7e2a0f68b7aa2459aba25e05ff8b2d61882",
],
context_player_state: None,
new_name: None,
metadata: MessageField(
None,
),
special_fields: SpecialFields {
unknown_fields: UnknownFields {
fields: None,
},
cached_size: CachedSize {
size: 0,
},
},
}
Oh, it's actually right there pretty much at the top... kMessageTypePause