Where a Kick channel's picture is
The channel endpoint has carried a playback URL all along and this backend parsed past it: a signed Amazon IVS playlist, fetched anonymously because the token is already in the query string. It rides the stream event beside the title and the viewer count, so every frontend that can show video is told where the video is - and the refresh already running every minute is what replaces the token before it expires, rather than a fetch of its own at the moment somebody presses play. Empty is read as absent: Kick sends "" for a channel that has never streamed, and a player pointed at "" is a player pointed at this page. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -63,6 +63,12 @@ pub struct Channel {
|
||||
/// How many people follow the channel - the one number about a channel
|
||||
/// that means something while it is offline.
|
||||
pub followers: Option<u64>,
|
||||
/// Where the video is, when there is any: a signed Amazon IVS playlist,
|
||||
/// fetched anonymously because the token is already in the query string.
|
||||
/// Present on the channel whether or not it is live, and stale the moment
|
||||
/// the token expires - which is why it rides the refresh rather than
|
||||
/// being asked for once.
|
||||
pub playback_url: Option<String>,
|
||||
}
|
||||
|
||||
/// A stream in progress, as the channel endpoint describes it.
|
||||
@@ -88,6 +94,8 @@ struct ChannelJson {
|
||||
/// channels that happened to be sent that way would not connect at all.
|
||||
#[serde(default, deserialize_with = "loose_number")]
|
||||
followers_count: Option<u64>,
|
||||
#[serde(default)]
|
||||
playback_url: Option<String>,
|
||||
}
|
||||
|
||||
/// A count that may arrive as a number or as a string containing one.
|
||||
@@ -205,6 +213,10 @@ pub async fn channel(http: &reqwest::Client, slug: &str) -> Result<Channel> {
|
||||
followers_only: json.chatroom.followers_mode,
|
||||
live,
|
||||
followers: json.followers_count,
|
||||
// Empty is the same as absent here: Kick sends "" for a channel that
|
||||
// has never streamed, and a player pointed at "" is a player pointed
|
||||
// at this page.
|
||||
playback_url: json.playback_url.filter(|u| !u.trim().is_empty()),
|
||||
slug,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1065,6 +1065,11 @@ fn announce_stream(state: &AppState, buffer_id: &str, channel: &api::Channel, fo
|
||||
"viewers": channel.live.as_ref().and_then(|l| l.viewers),
|
||||
"startedTs": channel.live.as_ref().and_then(|l| l.started_ts),
|
||||
"followers": channel.followers,
|
||||
// Where the picture is. On the stream event rather than fetched by
|
||||
// whoever wants to watch, because every frontend that shows video
|
||||
// needs it and the refresh already running is what replaces the
|
||||
// signed token before it expires.
|
||||
"playbackUrl": channel.playback_url,
|
||||
// Absent rather than false for an account that is not signed in:
|
||||
// "not following" and "cannot say" are different, and a button that
|
||||
// offers to follow when it cannot is a button that fails when pressed.
|
||||
@@ -2177,6 +2182,7 @@ mod tests {
|
||||
followers_only: false,
|
||||
live: None,
|
||||
followers: None,
|
||||
playback_url: None,
|
||||
});
|
||||
w
|
||||
}
|
||||
@@ -2308,10 +2314,12 @@ mod tests {
|
||||
w.add(&api::Channel {
|
||||
id: 999, chatroom_id: 111, slug: "first".into(), username: "first".into(),
|
||||
avatar_url: None, subscribers_only: false, followers_only: false, live: None, followers: None,
|
||||
playback_url: None,
|
||||
});
|
||||
w.add(&api::Channel {
|
||||
id: 222, chatroom_id: 999, slug: "second".into(), username: "second".into(),
|
||||
avatar_url: None, subscribers_only: false, followers_only: false, live: None, followers: None,
|
||||
playback_url: None,
|
||||
});
|
||||
// 999 is first's channel and second's chatroom. Which one is meant is
|
||||
// never a guess - the subscription name says which kind it is.
|
||||
|
||||
Reference in New Issue
Block a user