Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

添加禁止评论,禁止弹幕和精选评论的功能 #156

Merged
merged 7 commits into from
Jun 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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