Compare commits

...

3 Commits

Author SHA1 Message Date
Xinrea
ed0bd88e3b fix: douyin segment with params 2025-10-29 00:28:00 +08:00
Xinrea
b4fb2d058a fix: update interval setting 2025-10-29 00:16:06 +08:00
Xinrea
4058b425c8 feat: only query userinfo once 2025-10-28 23:31:15 +08:00
12 changed files with 99 additions and 28 deletions

View File

@@ -1,7 +1,7 @@
{
"name": "bili-shadowreplay",
"private": true,
"version": "2.15.4",
"version": "2.15.5",
"type": "module",
"scripts": {
"dev": "vite",

2
src-tauri/Cargo.lock generated
View File

@@ -541,7 +541,7 @@ checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba"
[[package]]
name = "bili-shadowreplay"
version = "2.15.4"
version = "2.15.5"
dependencies = [
"async-ffmpeg-sidecar 0.0.1",
"async-std",

View File

@@ -4,7 +4,7 @@ resolver = "2"
[package]
name = "bili-shadowreplay"
version = "2.15.4"
version = "2.15.5"
description = "BiliBili ShadowReplay"
authors = ["Xinrea"]
license = ""

View File

@@ -412,6 +412,7 @@ mod tests {
).await.unwrap();
assert_eq!(stream.index(), "https://hs.hls.huya.com/huyalive/156976698-156976698-674209784144068608-314076852-10057-A-0-1.m3u8?ratio=2000&wsSecret=7abc7dec8809146f31f92046eb044e3b&wsTime=68fa41ba&fm=RFdxOEJjSjNoNkRKdDZUWV8kMF8kMV8kMl8kMw%3D%3D&ctype=tars_mobile&fs=bgct&t=103");
assert_eq!(stream.ts_url("1.ts"), "https://hs.hls.huya.com/huyalive/1.ts?ratio=2000&wsSecret=7abc7dec8809146f31f92046eb044e3b&wsTime=68fa41ba&fm=RFdxOEJjSjNoNkRKdDZUWV8kMF8kMV8kMl8kMw%3D%3D&ctype=tars_mobile&fs=bgct&t=103");
assert_eq!(stream.ts_url("1.ts?expires=1760808243"), "https://hs.hls.huya.com/huyalive/1.ts?expires=1760808243&ratio=2000&wsSecret=7abc7dec8809146f31f92046eb044e3b&wsTime=68fa41ba&fm=RFdxOEJjSjNoNkRKdDZUWV8kMF8kMV8kMl8kMw%3D%3D&ctype=tars_mobile&fs=bgct&t=103");
assert_eq!(stream.host, "https://hs.hls.huya.com");
assert_eq!(
stream.base,

View File

@@ -80,9 +80,18 @@ impl HlsStream {
if self.extra.is_empty() {
format!("{}{}", self.host, base_url)
} else {
// Remove the trailing '?' from base_url and add it properly
let base_without_query = base_url.trim_end_matches('?');
format!("{}{}?{}", self.host, base_without_query, self.extra)
// Check if base_url already contains query parameters
if base_url.contains('?') {
// If seg_name already has query params, append extra with '&'
// Remove trailing '?' or '&' before appending
let base_trimmed = base_url.trim_end_matches('?').trim_end_matches('&');
format!("{}{}&{}", self.host, base_trimmed, self.extra)
} else {
// If no query params, add them with '?'
// Remove trailing '?' from base_url if present
let base_without_query = base_url.trim_end_matches('?');
format!("{}{}?{}", self.host, base_without_query, self.extra)
}
}
}
}

View File

@@ -77,6 +77,8 @@ where
room_info: Arc<RwLock<RoomInfo>>,
/// The user info for the recorder
user_info: Arc<RwLock<UserInfo>>,
/// The update interval for room status
update_interval: Arc<atomic::AtomicU64>,
/// The platform live id for the current recording
platform_live_id: Arc<RwLock<String>>,

View File

@@ -42,6 +42,7 @@ impl BiliRecorder {
account: &Account,
cache_dir: PathBuf,
event_channel: broadcast::Sender<RecorderEvent>,
update_interval: Arc<atomic::AtomicU64>,
enabled: bool,
) -> Result<Self, crate::errors::RecorderError> {
let client = reqwest::Client::new();
@@ -59,6 +60,7 @@ impl BiliRecorder {
cache_dir,
quit: Arc::new(atomic::AtomicBool::new(false)),
enabled: Arc::new(atomic::AtomicBool::new(enabled)),
update_interval,
is_recording: Arc::new(atomic::AtomicBool::new(false)),
room_info: Arc::new(RwLock::new(RoomInfo::default())),
user_info: Arc::new(RwLock::new(UserInfo::default())),
@@ -109,19 +111,22 @@ impl BiliRecorder {
room_cover: room_info.room_cover_url.clone(),
status: room_info.live_status == 1,
};
let user_id = room_info.user_id;
let user_info = api::get_user_info(&self.client, &self.account, user_id).await;
if let Ok(user_info) = user_info {
*self.user_info.write().await = UserInfo {
user_id: user_id.to_string(),
user_name: user_info.user_name,
user_avatar: user_info.user_avatar_url,
// Only update user info once
if self.user_info.read().await.user_id != room_info.user_id.to_string() {
let user_id = room_info.user_id;
let user_info = api::get_user_info(&self.client, &self.account, user_id).await;
if let Ok(user_info) = user_info {
*self.user_info.write().await = UserInfo {
user_id: user_id.to_string(),
user_name: user_info.user_name,
user_avatar: user_info.user_avatar_url,
}
} else {
self.log_error(&format!(
"Failed to get user info: {}",
user_info.err().unwrap()
));
}
} else {
self.log_error(&format!(
"Failed to get user info: {}",
user_info.err().unwrap()
));
}
let live_status = room_info.live_status == 1;
@@ -370,7 +375,10 @@ impl crate::traits::RecorderTrait<BiliExtra> for BiliRecorder {
continue;
}
tokio::time::sleep(Duration::from_secs(15)).await;
tokio::time::sleep(Duration::from_secs(
self_clone.update_interval.load(atomic::Ordering::Relaxed),
))
.await;
}
}));
}

View File

@@ -48,6 +48,7 @@ impl DouyinRecorder {
account: &Account,
cache_dir: PathBuf,
channel: broadcast::Sender<RecorderEvent>,
update_interval: Arc<atomic::AtomicU64>,
enabled: bool,
) -> Result<Self, crate::errors::RecorderError> {
Ok(Self {
@@ -69,6 +70,7 @@ impl DouyinRecorder {
last_sequence: Arc::new(atomic::AtomicU64::new(0)),
danmu_task: Arc::new(Mutex::new(None)),
record_task: Arc::new(Mutex::new(None)),
update_interval,
total_duration: Arc::new(atomic::AtomicU64::new(0)),
total_size: Arc::new(atomic::AtomicU64::new(0)),
extra: DouyinExtra {
@@ -327,7 +329,10 @@ impl crate::traits::RecorderTrait<DouyinExtra> for DouyinRecorder {
continue;
}
tokio::time::sleep(Duration::from_secs(15)).await;
tokio::time::sleep(Duration::from_secs(
self_clone.update_interval.load(atomic::Ordering::Relaxed),
))
.await;
}
log::info!("[{}]Recording thread quit.", self_clone.room_id);
}));

View File

@@ -34,6 +34,7 @@ impl HuyaRecorder {
account: &Account,
cache_dir: PathBuf,
channel: broadcast::Sender<RecorderEvent>,
update_interval: Arc<atomic::AtomicU64>,
enabled: bool,
) -> Result<Self, crate::errors::RecorderError> {
Ok(Self {
@@ -55,6 +56,7 @@ impl HuyaRecorder {
last_sequence: Arc::new(atomic::AtomicU64::new(0)),
danmu_task: Arc::new(Mutex::new(None)),
record_task: Arc::new(Mutex::new(None)),
update_interval,
total_duration: Arc::new(atomic::AtomicU64::new(0)),
total_size: Arc::new(atomic::AtomicU64::new(0)),
extra: HuyaExtra {
@@ -224,7 +226,10 @@ impl crate::traits::RecorderTrait<HuyaExtra> for HuyaRecorder {
continue;
}
tokio::time::sleep(Duration::from_secs(15)).await;
tokio::time::sleep(Duration::from_secs(
self_clone.update_interval.load(atomic::Ordering::Relaxed),
))
.await;
}
log::info!("[{}]Recording thread quit.", self_clone.room_id);
}));

View File

@@ -1,7 +1,8 @@
use std::path::{Path, PathBuf};
use chrono::Local;
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
use std::sync::atomic::{self, AtomicU64};
use std::sync::Arc;
use crate::{danmu2ass::Danmu2AssOptions, recorder_manager::ClipRangeParams};
@@ -39,6 +40,8 @@ pub struct Config {
pub webhook_url: String,
#[serde(default = "default_danmu_ass_options")]
pub danmu_ass_options: Danmu2AssOptions,
#[serde(skip)]
pub update_interval: Arc<AtomicU64>,
}
#[derive(Deserialize, Serialize, Clone)]
@@ -107,6 +110,7 @@ impl Config {
if let Ok(content) = std::fs::read_to_string(config_path) {
if let Ok(mut config) = toml::from_str::<Config>(&content) {
config.config_path = config_path.to_str().unwrap().into();
config.update_interval = Arc::new(AtomicU64::new(config.status_check_interval));
return Ok(config);
}
}
@@ -137,6 +141,7 @@ impl Config {
whisper_language: default_whisper_language(),
webhook_url: default_webhook_url(),
danmu_ass_options: default_danmu_ass_options(),
update_interval: Arc::new(AtomicU64::new(default_status_check_interval())),
};
config.save();
@@ -220,4 +225,11 @@ impl Config {
Path::new(&output).join(&sanitized)
}
pub fn set_status_check_interval(&mut self, interval: u64) {
self.status_check_interval = interval;
self.update_interval
.store(interval, atomic::Ordering::Relaxed);
self.save();
}
}

View File

@@ -251,8 +251,11 @@ pub async fn update_status_check_interval(
interval = 10; // Minimum interval of 10 seconds
}
log::info!("Updating status check interval to {interval} seconds");
state.config.write().await.status_check_interval = interval;
state.config.write().await.save();
state
.config
.write()
.await
.set_status_check_interval(interval);
Ok(())
}

View File

@@ -497,15 +497,41 @@ impl RecorderManager {
let cache_dir = PathBuf::from(&cache_dir);
let event_tx = self.get_event_sender();
let update_interval = self.config.read().await.update_interval.clone();
let recorder: RecorderType = match platform {
PlatformType::BiliBili => RecorderType::BiliBili(
BiliRecorder::new(room_id, account, cache_dir, event_tx, enabled).await?,
BiliRecorder::new(
room_id,
account,
cache_dir,
event_tx,
update_interval,
enabled,
)
.await?,
),
PlatformType::Douyin => RecorderType::Douyin(
DouyinRecorder::new(room_id, extra, account, cache_dir, event_tx, enabled).await?,
DouyinRecorder::new(
room_id,
extra,
account,
cache_dir,
event_tx,
update_interval,
enabled,
)
.await?,
),
PlatformType::Huya => RecorderType::Huya(
HuyaRecorder::new(room_id, account, cache_dir, event_tx, enabled).await?,
HuyaRecorder::new(
room_id,
account,
cache_dir,
event_tx,
update_interval,
enabled,
)
.await?,
),
_ => {
return Err(RecorderManagerError::InvalidPlatformType {