Skip to content

Commit

Permalink
添加禁止评论,禁止弹幕和精选评论的功能 (#156)
Browse files Browse the repository at this point in the history
* Add up close reply (#8)

* 增加对up_close_reply(禁止评论)的支持

* 更新readme的OPTIONS排版

* fix error

* stream_gears增加默认值

* try to fix

* try to fix

* try to fix

* try to fix

* try to fix

* 修改stream-gears版本号

* Update release.yml

* 修复禁止评论不生效

* 恢复 release.yml

* 更新cargo.lock

* 兼容原始upload方法

---------

Co-authored-by: Kataick <24969684+Kataick@users.noreply.github.com>
  • Loading branch information
zzc10086 and Kataick authored Jun 6, 2024
1 parent 8b3debc commit 9288716
Show file tree
Hide file tree
Showing 6 changed files with 352 additions and 16 deletions.
53 changes: 47 additions & 6 deletions crates/biliup/src/uploader/bilibili.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,22 +221,63 @@ pub struct BiliBili {

impl BiliBili {
pub async fn submit(&self, studio: &Studio) -> Result<ResponseData> {
let ret: ResponseData = reqwest::Client::builder()
.user_agent("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/63.0.3239.108")
.timeout(Duration::new(60, 0))
.build()?
.post(format!(
"http://member.bilibili.com/x/vu/client/add?access_key={}",
self.login_info.token_info.access_token
))
.json(studio)
.send()
.await?
.json()
.await?;
info!("{:?}", ret);
if ret.code == 0 {
info!("投稿成功");
Ok(ret)
} else {
Err(Kind::Custom(format!("{:?}", ret)))
}
}

pub async fn submit_by_app(&self, studio: &Studio) -> Result<ResponseData> {
let payload = {
let mut payload = json!({
"access_key": self.login_info.token_info.access_token,
"appkey": crate::credential::AppKeyStore::BiliTV.app_key(),
"build": 7800300,
"c_locale": "zh-Hans_CN",
"channel": "bili",
"disable_rcmd": 0,
"mobi_app": "android",
"platform": "android",
"s_locale": "zh-Hans_CN",
"statistics": "\"appId\":1,\"platform\":3,\"version\":\"7.80.0\",\"abtest\":\"\"",
"ts": std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs(),
});

let urlencoded = serde_urlencoded::to_string(&payload)?;
let sign = crate::credential::Credential::sign(&urlencoded, crate::credential::AppKeyStore::BiliTV.appsec());
payload["sign"] = Value::from(sign);
payload
};

let ret: ResponseData = reqwest::Client::builder()
.user_agent("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/63.0.3239.108")
.user_agent("Mozilla/5.0 BiliDroid/7.80.0 (bbcallen@gmail.com) os/android model/MI 6 mobi_app/android build/7800300 channel/bili innerVer/7800310 osVer/13 network/2")
.timeout(Duration::new(60, 0))
.build()?
.post(format!(
"http://member.bilibili.com/x/vu/client/add?access_key={}",
self.login_info.token_info.access_token
))
.post("https://member.bilibili.com/x/vu/app/add")
.query(&payload)
.json(studio)
.send()
.await?
.json()
.await?;
info!("{:?}", ret);
if ret.code == 0 {
info!("投稿成功");
Ok(ret)
} else {
Err(Kind::Custom(format!("{:?}", ret)))
Expand Down
6 changes: 3 additions & 3 deletions crates/biliup/src/uploader/credential.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,14 @@ pub(crate) enum AppKeyStore {
}

impl AppKeyStore {
fn app_key(&self) -> &'static str {
pub fn app_key(&self) -> &'static str {
match self {
AppKeyStore::BiliTV => "4409e2ce8ffd12b8",
AppKeyStore::Android => "783bbb7264451d82",
}
}

fn appsec(&self) -> &'static str {
pub fn appsec(&self) -> &'static str {
match self {
AppKeyStore::BiliTV => "59b43e04ad6965f34319062b478f83dd",
AppKeyStore::Android => "2653583c8873dea268ab9386918b1d65",
Expand Down Expand Up @@ -571,7 +571,7 @@ impl Credential {
Ok(())
}

fn sign(param: &str, app_sec: &str) -> String {
pub fn sign(param: &str, app_sec: &str) -> String {
let mut hasher = Md5::new();
// process input message
hasher.update(format!("{}{}", param, app_sec));
Expand Down
91 changes: 91 additions & 0 deletions crates/stream-gears/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ fn login_by_web_qrcode(sess_data: String, dede_user_id: String) -> PyResult<bool

#[allow(clippy::too_many_arguments)]
#[pyfunction]
//#[pyo3(signature = (video_path, cookie_file, title, tid=171, tag="".to_string(), copyright=2, source="".to_string(), desc="".to_string(), dynamic="".to_string(), cover="".to_string(), dolby=0, lossless_music=0, no_reprint=0, open_elec=0, up_close_reply=false, up_selection_reply=false, limit=3, desc_v2=vec![], dtime=None, line=None))]
fn upload(
py: Python<'_>,
video_path: Vec<PathBuf>,
Expand Down Expand Up @@ -273,6 +274,95 @@ fn upload(
})
}

#[allow(clippy::too_many_arguments)]
#[pyfunction]
fn upload_by_app(
py: Python<'_>,
video_path: Vec<PathBuf>,
cookie_file: PathBuf,
title: String,
tid: u16,
tag: String,
copyright: u8,
source: String,
desc: String,
dynamic: String,
cover: String,
dolby: u8,
lossless_music: u8,
no_reprint: u8,
open_elec: u8,
up_close_reply: bool,
up_selection_reply: bool,
up_close_danmu: bool,
limit: usize,
desc_v2: Vec<PyCredit>,
dtime: Option<u32>,
line: Option<UploadLine>,
) -> PyResult<()> {
py.allow_threads(|| {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()?;
// 输出到控制台中
unsafe {
time::util::local_offset::set_soundness(time::util::local_offset::Soundness::Unsound);
}
let local_time = tracing_subscriber::fmt::time::LocalTime::new(format_description!(
"[year]-[month]-[day] [hour]:[minute]:[second]"
));
let formatting_layer = tracing_subscriber::FmtSubscriber::builder()
// will be written to stdout.
// builds the subscriber.
.with_timer(local_time.clone())
.finish();
let file_appender = tracing_appender::rolling::never("", "upload.log");
let (non_blocking, _guard) = tracing_appender::non_blocking(file_appender);
let file_layer = tracing_subscriber::fmt::layer()
.with_ansi(false)
.with_timer(local_time)
.with_writer(non_blocking);

let collector = formatting_layer.with(file_layer);

tracing::subscriber::with_default(collector, || -> PyResult<()> {
let studio_pre = StudioPre::builder()
.video_path(video_path)
.cookie_file(cookie_file)
.line(line)
.limit(limit)
.title(title)
.tid(tid)
.tag(tag)
.copyright(copyright)
.source(source)
.desc(desc)
.dynamic(dynamic)
.cover(cover)
.dtime(dtime)
.dolby(dolby)
.lossless_music(lossless_music)
.no_reprint(no_reprint)
.open_elec(open_elec)
.up_close_reply(up_close_reply)
.up_selection_reply(up_selection_reply)
.up_close_danmu(up_close_danmu)
.desc_v2_credit(desc_v2)
.build();

match rt.block_on(uploader::upload_by_app(studio_pre)) {
Ok(_) => Ok(()),
// Ok(_) => { },
Err(err) => Err(pyo3::exceptions::PyRuntimeError::new_err(format!(
"{}, {}",
err.root_cause(),
err
))),
}
})
})
}

/// A Python module implemented in Rust.
#[pymodule]
fn stream_gears(m: &Bound<'_, PyModule>) -> PyResult<()> {
Expand All @@ -282,6 +372,7 @@ fn stream_gears(m: &Bound<'_, PyModule>) -> PyResult<()> {
// .with_writer(non_blocking)
// .init();
m.add_function(wrap_pyfunction!(upload, m)?)?;
m.add_function(wrap_pyfunction!(upload_by_app, m)?)?;
m.add_function(wrap_pyfunction!(download, m)?)?;
m.add_function(wrap_pyfunction!(download_with_callback, m)?)?;
m.add_function(wrap_pyfunction!(login_by_cookies, m)?)?;
Expand Down
131 changes: 131 additions & 0 deletions crates/stream-gears/src/uploader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ pub struct StudioPre {
lossless_music: u8,
no_reprint: u8,
open_elec: u8,
#[builder(default=false)]
up_close_reply: bool,
#[builder(default=false)]
up_selection_reply: bool,
#[builder(default=false)]
up_close_danmu: bool,
desc_v2_credit: Vec<PyCredit>,
}

Expand Down Expand Up @@ -86,6 +92,7 @@ pub async fn upload(studio_pre: StudioPre) -> Result<ResponseData> {
no_reprint,
open_elec,
desc_v2_credit,
..
} = studio_pre;

let bilibili = login_by_cookies(&cookie_file).await;
Expand Down Expand Up @@ -179,3 +186,127 @@ pub async fn upload(studio_pre: StudioPre) -> Result<ResponseData> {

Ok(bilibili.submit(&studio).await?)
}

pub async fn upload_by_app(studio_pre: StudioPre) -> Result<ResponseData> {
// let file = std::fs::File::options()
// .read(true)
// .write(true)
// .open(&cookie_file);
let StudioPre {
video_path,
cookie_file,
line,
limit,
title,
tid,
tag,
copyright,
source,
desc,
dynamic,
cover,
dtime,
dolby,
lossless_music,
no_reprint,
open_elec,
up_close_reply,
up_selection_reply,
up_close_danmu,
desc_v2_credit,
} = studio_pre;

let bilibili = login_by_cookies(&cookie_file).await;
let bilibili = if let Err(Kind::IO(_)) = bilibili {
bilibili
.with_context(|| String::from("open cookies file: ") + &cookie_file.to_string_lossy())?
} else {
bilibili?
};

let client = StatelessClient::default();
let mut videos = Vec::new();
let line = match line {
Some(UploadLine::Bda2) => line::bda2(),
Some(UploadLine::Ws) => line::ws(),
Some(UploadLine::Qn) => line::qn(),
// Some(UploadLine::Kodo) => line::kodo(),
// Some(UploadLine::Cos) => line::cos(),
// Some(UploadLine::CosInternal) => line::cos_internal(),
Some(UploadLine::Bda) => line::bda(),
Some(UploadLine::Tx) => line::tx(),
Some(UploadLine::Txa) => line::txa(),
Some(UploadLine::Bldsa) => line::bldsa(),
None => Probe::probe(&client.client).await.unwrap_or_default(),
};
for video_path in video_path {
println!("{:?}", video_path.canonicalize()?.to_str());
info!("{line:?}");
let video_file = VideoFile::new(&video_path)?;
let total_size = video_file.total_size;
let file_name = video_file.file_name.clone();
let uploader = line.pre_upload(&bilibili, video_file).await?;

let instant = Instant::now();

let video = uploader
.upload(client.clone(), limit, |vs| {
vs.map(|vs| {
let chunk = vs?;
let len = chunk.len();
Ok((chunk, len))
})
})
.await?;
let t = instant.elapsed().as_millis();
info!(
"Upload completed: {file_name} => cost {:.2}s, {:.2} MB/s.",
t as f64 / 1000.,
total_size as f64 / 1000. / t as f64
);
videos.push(video);
}

let mut desc_v2 = Vec::new();
for credit in desc_v2_credit {
desc_v2.push(Credit {
type_id: credit.type_id,
raw_text: credit.raw_text,
biz_id: credit.biz_id,
});
}

let mut studio: Studio = Studio::builder()
.desc(desc)
.dtime(dtime)
.copyright(copyright)
.cover(cover)
.dynamic(dynamic)
.source(source)
.tag(tag)
.tid(tid)
.title(title)
.videos(videos)
.dolby(dolby)
.lossless_music(lossless_music)
.no_reprint(no_reprint)
.open_elec(open_elec)
.up_close_reply(up_close_reply)
.up_selection_reply(up_selection_reply)
.up_close_danmu(up_close_danmu)
.desc_v2(Some(desc_v2))
.build();

if !studio.cover.is_empty() {
let url = bilibili
.cover_up(
&std::fs::read(&studio.cover)
.with_context(|| format!("cover: {}", studio.cover))?,
)
.await?;
println!("{url}");
studio.cover = url;
}

Ok(bilibili.submit_by_app(&studio).await?)
}
Loading

0 comments on commit 9288716

Please sign in to comment.