mirror of
https://github.com/TheFunny/TelegramTwitterMediaBot.git
synced 2026-09-24 23:42:18 +00:00
fix(pixiv): keep a frame-zip download hiccup retryable
download_media_to_file's non-transport errors all folded into PixivError::Api, and Api is permanent in pixiv_error_is_retryable — so one 429/5xx, stalled transfer or temp-file write failure while fetching the ugoira frame zip permanently failed the whole post, while the bot's own upload downloads classify the very same classes as retryable (classify_download_error). A new PixivError::Transient carries those classes into the existing retry policy (and into startup validation's 'stays enabled' branch); Http keeps its arm and everything else stays permanent. The retryable table and the startup-validation loop pin both halves.
This commit is contained in:
@@ -39,6 +39,11 @@ pub enum PixivError {
|
||||
Status(u16),
|
||||
#[error("pixiv api error: {0}")]
|
||||
Api(String),
|
||||
/// A bad moment while preparing media: a transient download status
|
||||
/// (429 / 5xx), a stalled transfer or a temp-file write failure. A retry
|
||||
/// can change the answer, so the pixiv retry policy re-fetches these.
|
||||
#[error("transient pixiv error: {0}")]
|
||||
Transient(String),
|
||||
}
|
||||
|
||||
/// Native pixiv app-API client.
|
||||
@@ -228,6 +233,14 @@ impl PixivAPI {
|
||||
.await
|
||||
.map_err(|e| match e {
|
||||
FetchError::Http(e) => PixivError::Http(e),
|
||||
// A bad moment (429/5xx, a stalled transfer, a temp-file
|
||||
// write failure) must stay retryable: folding it into Api
|
||||
// made one hiccup permanently fail the whole ugoira post,
|
||||
// while the bot's own upload downloads retry the same
|
||||
// classes.
|
||||
transient @ (FetchError::Transient(_) | FetchError::Io(_)) => {
|
||||
PixivError::Transient(format!("frame zip download failed: {transient}"))
|
||||
}
|
||||
other => PixivError::Api(format!("frame zip download failed: {other}")),
|
||||
})?;
|
||||
let frame_delays = metadata.frames.iter().map(|f| f.delay).collect::<Vec<_>>();
|
||||
|
||||
@@ -102,7 +102,7 @@ pub fn is_retryable(err: &FetchError) -> bool {
|
||||
/// rejected credential is not.
|
||||
fn pixiv_error_is_retryable(err: &PixivError) -> bool {
|
||||
match err {
|
||||
PixivError::Http(_) => true,
|
||||
PixivError::Http(_) | PixivError::Transient(_) => true,
|
||||
PixivError::Status(code) if *code == 429 || *code >= 500 => true,
|
||||
PixivError::Status(_) | PixivError::Api(_) | PixivError::Json(_) | PixivError::NoAuth => {
|
||||
false
|
||||
@@ -443,7 +443,11 @@ mod tests {
|
||||
// it calls `disable()`, a process-wide flag with no reset, so a test
|
||||
// touching it would order-couple every other pixiv test (the predicate
|
||||
// it keys on is covered by the table below).
|
||||
for err in [PixivError::Status(429), PixivError::Status(503)] {
|
||||
for err in [
|
||||
PixivError::Status(429),
|
||||
PixivError::Status(503),
|
||||
PixivError::Transient("frame zip download failed: transient".into()),
|
||||
] {
|
||||
let enabled_before = api::enabled();
|
||||
let message = startup_validation(Err(err)).unwrap_err();
|
||||
assert!(message.contains("stays enabled"), "{message}");
|
||||
@@ -458,11 +462,15 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn is_retryable_classifies_transient_and_permanent() {
|
||||
// Transient: network errors, explicit transient, pixiv 429/5xx.
|
||||
// Transient: network errors, explicit transient, pixiv 429/5xx, and a
|
||||
// failed media download (the frame zip's own bad moment).
|
||||
assert!(is_retryable(&FetchError::Transient("429".into())));
|
||||
assert!(is_retryable(&FetchError::Pixiv(PixivError::Status(429))));
|
||||
assert!(is_retryable(&FetchError::Pixiv(PixivError::Status(500))));
|
||||
assert!(is_retryable(&FetchError::Pixiv(PixivError::Status(503))));
|
||||
assert!(is_retryable(&FetchError::Pixiv(PixivError::Transient(
|
||||
"frame zip download failed: transient: media status 429".into()
|
||||
))));
|
||||
// Permanent: pixiv 4xx (bad/expired token, forbidden, not found),
|
||||
// api/auth errors, unparseable bodies, not-found/blocked/sensitive.
|
||||
assert!(!is_retryable(&FetchError::Pixiv(PixivError::Status(400))));
|
||||
|
||||
Reference in New Issue
Block a user