diff --git a/src/youtube/mod.rs b/src/youtube/mod.rs index a8a488b..a95d5e4 100644 --- a/src/youtube/mod.rs +++ b/src/youtube/mod.rs @@ -69,7 +69,8 @@ impl<'r> ActionChunk<'r> { let continuation_token = match &response.continuation_contents.live_chat_continuation.continuations[0] { Continuation::Invalidation { continuation, .. } => continuation.to_owned(), Continuation::Timed { continuation, .. } => continuation.to_owned(), - Continuation::Replay { continuation, .. } => continuation.to_owned() + Continuation::Replay { continuation, .. } => continuation.to_owned(), + Continuation::PlayerSeek { .. } => return Err(Error::EndOfContinuation) }; let signaler_topic = match &response.continuation_contents.live_chat_continuation.continuations[0] { Continuation::Invalidation { invalidation_id, .. } => Some(invalidation_id.topic.to_owned()), @@ -93,7 +94,7 @@ impl<'r> ActionChunk<'r> { .ok_or(Error::EndOfContinuation)? .into_iter() .flat_map(|f| match f.action { - Action::ReplayChat { actions, .. } => actions, + Action::ReplayChat { actions, .. } => actions.into_iter().map(|f| f.action).collect(), f => vec![f] }) .collect() @@ -159,8 +160,8 @@ pub async fn stream(options: &ChatContext) -> Result { for action in actions { - if let Action::AddChatItem { .. } = action { - yield_tx.send(Ok(action.to_owned())).await; + if let Action::AddChatItem { .. } = action.action { + yield_tx.send(Ok(action.action.to_owned())).await; } } } @@ -187,8 +188,8 @@ pub async fn stream(options: &ChatContext) -> Result { for action in actions { - if let Action::AddChatItem { .. } = action { - yield_tx.send(Ok(action.to_owned())).await; + if let Action::AddChatItem { .. } = action.action { + yield_tx.send(Ok(action.action.to_owned())).await; } } } @@ -232,8 +233,8 @@ pub async fn stream(options: &ChatContext) -> Result { for action in actions { - if let Action::AddChatItem { .. } = action { - yield_tx.send(Ok(action.to_owned())).await; + if let Action::AddChatItem { .. } = action.action { + yield_tx.send(Ok(action.action.to_owned())).await; } } } @@ -255,38 +256,17 @@ pub async fn stream(options: &ChatContext) -> Result { - let chunk = ActionChunk::new(initial_chat, options).unwrap(); - for action in chunk.iter() { - match action { - Action::AddChatItem { .. } => { - yield_tx.send(Ok(action.to_owned())).await; - } - Action::ReplayChat { actions, .. } => { - for action in actions { - if let Action::AddChatItem { .. } = action { - yield_tx.send(Ok(action.to_owned())).await; - } - } - } - action => { - yield_tx.send(Ok(action.to_owned())).await; - } - } - } - - while let Some(Ok(chunk)) = chunk.cont().await { + let mut chunk = ActionChunk::new(initial_chat, options).unwrap(); + loop { for action in chunk.iter() { match action { - Action::AddChatItem { item, .. } => { - if !seen_messages.contains(item.id()) { - yield_tx.send(Ok(action.to_owned())).await; - seen_messages.insert(item.id().to_owned()); - } + Action::AddChatItem { .. } => { + yield_tx.send(Ok(action.to_owned())).await; } Action::ReplayChat { actions, .. } => { for action in actions { - if let Action::AddChatItem { .. } = action { - yield_tx.send(Ok(action.to_owned())).await; + if let Action::AddChatItem { .. } = action.action { + yield_tx.send(Ok(action.action.to_owned())).await; } } } @@ -295,6 +275,10 @@ pub async fn stream(options: &ChatContext) -> Result chunk = e, + _ => break + } } } Continuation::Timed { timeout_ms, .. } => { @@ -311,8 +295,8 @@ pub async fn stream(options: &ChatContext) -> Result { for action in actions { - if let Action::AddChatItem { .. } = action { - yield_tx.send(Ok(action.to_owned())).await; + if let Action::AddChatItem { .. } = action.action { + yield_tx.send(Ok(action.action.to_owned())).await; } } } @@ -328,6 +312,7 @@ pub async fn stream(options: &ChatContext) -> Result panic!("player seek should not be first continuation") } }))) } diff --git a/src/youtube/types/get_live_chat.rs b/src/youtube/types/get_live_chat.rs index 8753e89..03d9cfc 100644 --- a/src/youtube/types/get_live_chat.rs +++ b/src/youtube/types/get_live_chat.rs @@ -92,7 +92,7 @@ pub struct LiveChatContinuation { pub actions: Option> } -#[derive(Deserialize, Debug)] +#[derive(Deserialize, Debug, Clone)] #[serde(rename_all = "camelCase")] pub struct ActionContainer { #[serde(flatten)] @@ -115,7 +115,10 @@ pub enum Continuation { Timed { timeout_ms: usize, continuation: String }, #[serde(rename = "liveChatReplayContinuationData")] #[serde(rename_all = "camelCase")] - Replay { time_until_last_message_msec: usize, continuation: String } + Replay { time_until_last_message_msec: usize, continuation: String }, + #[serde(rename = "playerSeekContinuationData")] + #[serde(rename_all = "camelCase")] + PlayerSeek { continuation: String } } #[derive(Deserialize, Debug)] @@ -150,7 +153,7 @@ pub enum Action { #[serde(rename = "replayChatItemAction")] #[serde(rename_all = "camelCase")] ReplayChat { - actions: Vec, + actions: Vec, #[serde(deserialize_with = "deserialize_number_from_string")] video_offset_time_msec: i64 }, @@ -243,6 +246,20 @@ pub enum ChatItem { background_color: isize, author_name_text_color: isize }, + #[serde(rename = "liveChatSponsorshipsGiftPurchaseAnnouncementRenderer")] + #[serde(rename_all = "camelCase")] + MembershipGift { + id: String, + #[serde(flatten)] + data: simd_json::OwnedValue + }, + #[serde(rename = "liveChatSponsorshipsGiftRedemptionAnnouncementRenderer")] + #[serde(rename_all = "camelCase")] + MembershipGiftRedemption { + id: String, + #[serde(flatten)] + data: simd_json::OwnedValue + }, #[serde(rename = "liveChatViewerEngagementMessageRenderer")] ViewerEngagement { id: String } } @@ -254,6 +271,8 @@ impl ChatItem { ChatItem::PaidSticker { message_renderer_base, .. } => &message_renderer_base.id, ChatItem::Superchat { message_renderer_base, .. } => &message_renderer_base.id, ChatItem::TextMessage { message_renderer_base, .. } => &message_renderer_base.id, + ChatItem::MembershipGift { id, .. } => id, + ChatItem::MembershipGiftRedemption { id, .. } => id, ChatItem::ViewerEngagement { id } => id } }