mirror of
https://github.com/ankitects/anki.git
synced 2025-09-25 01:06:35 -04:00
defer errors on upload to media check
This commit is contained in:
parent
d38c2c12d5
commit
98279add15
6 changed files with 42 additions and 21 deletions
|
@ -97,6 +97,8 @@ message SyncError {
|
||||||
CLIENT_TOO_OLD = 3;
|
CLIENT_TOO_OLD = 3;
|
||||||
AUTH_FAILED = 4;
|
AUTH_FAILED = 4;
|
||||||
SERVER_MESSAGE = 5;
|
SERVER_MESSAGE = 5;
|
||||||
|
MEDIA_CHECK_REQUIRED = 6;
|
||||||
|
RESYNC_REQUIRED = 7;
|
||||||
}
|
}
|
||||||
SyncErrorKind kind = 2;
|
SyncErrorKind kind = 2;
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,20 +44,6 @@ class MediaSyncState:
|
||||||
removed_files: int = 0
|
removed_files: int = 0
|
||||||
|
|
||||||
|
|
||||||
# fixme: sync.rs fixmes
|
|
||||||
# fixme: maximum size when uploading
|
|
||||||
# fixme: concurrent modifications during upload step
|
|
||||||
# fixme: mediaSanity
|
|
||||||
# elif evt == "mediaSanity":
|
|
||||||
# showWarning(
|
|
||||||
# _(
|
|
||||||
# """\
|
|
||||||
# A problem occurred while syncing media. Please use Tools>Check Media, then \
|
|
||||||
# sync again to correct the issue."""
|
|
||||||
# )
|
|
||||||
# )
|
|
||||||
|
|
||||||
|
|
||||||
LogEntry = Union[MediaSyncState, str]
|
LogEntry = Union[MediaSyncState, str]
|
||||||
|
|
||||||
|
|
||||||
|
@ -170,6 +156,14 @@ class MediaSyncer:
|
||||||
"AnkiWeb encountered a problem. Please try again in a few minutes."
|
"AnkiWeb encountered a problem. Please try again in a few minutes."
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
elif kind == SyncErrorKind.MEDIA_CHECK_REQUIRED:
|
||||||
|
showWarning(_("Please use the Tools>Check Media menu option."))
|
||||||
|
elif kind == SyncErrorKind.RESYNC_REQUIRED:
|
||||||
|
showWarning(
|
||||||
|
_(
|
||||||
|
"Please sync again, and post on the support forum if this message keeps appearing."
|
||||||
|
)
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
showWarning(_("Unexpected error: {}").format(str(exc)))
|
showWarning(_("Unexpected error: {}").format(str(exc)))
|
||||||
elif isinstance(exc, NetworkError):
|
elif isinstance(exc, NetworkError):
|
||||||
|
|
|
@ -87,6 +87,8 @@ impl std::convert::From<SyncErrorKind> for i32 {
|
||||||
SyncErrorKind::ClientTooOld => V::ClientTooOld,
|
SyncErrorKind::ClientTooOld => V::ClientTooOld,
|
||||||
SyncErrorKind::AuthFailed => V::AuthFailed,
|
SyncErrorKind::AuthFailed => V::AuthFailed,
|
||||||
SyncErrorKind::ServerMessage => V::ServerMessage,
|
SyncErrorKind::ServerMessage => V::ServerMessage,
|
||||||
|
SyncErrorKind::MediaCheckRequired => V::MediaCheckRequired,
|
||||||
|
SyncErrorKind::ResyncRequired => V::ResyncRequired,
|
||||||
SyncErrorKind::Other => V::Other,
|
SyncErrorKind::Other => V::Other,
|
||||||
}) as i32
|
}) as i32
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,6 +129,8 @@ pub enum SyncErrorKind {
|
||||||
AuthFailed,
|
AuthFailed,
|
||||||
ServerMessage,
|
ServerMessage,
|
||||||
Other,
|
Other,
|
||||||
|
MediaCheckRequired,
|
||||||
|
ResyncRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn error_for_status_code(info: String, code: StatusCode) -> AnkiError {
|
fn error_for_status_code(info: String, code: StatusCode) -> AnkiError {
|
||||||
|
|
|
@ -244,6 +244,12 @@ delete from media where fname=?"
|
||||||
.collect();
|
.collect();
|
||||||
Ok(map?)
|
Ok(map?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn force_resync(&mut self) -> Result<()> {
|
||||||
|
self.db
|
||||||
|
.execute_batch("delete from media; update meta set lastUsn=0, usn=0")
|
||||||
|
.map_err(Into::into)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright: Ankitects Pty Ltd and contributors
|
// Copyright: Ankitects Pty Ltd and contributors
|
||||||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
use crate::err::{AnkiError, Result};
|
use crate::err::{AnkiError, Result, SyncErrorKind};
|
||||||
use crate::media::database::{MediaDatabaseContext, MediaDatabaseMetadata, MediaEntry};
|
use crate::media::database::{MediaDatabaseContext, MediaDatabaseMetadata, MediaEntry};
|
||||||
use crate::media::files::{
|
use crate::media::files::{
|
||||||
add_file_from_ankiweb, data_for_file, normalize_filename, remove_files, AddedFile,
|
add_file_from_ankiweb, data_for_file, normalize_filename, remove_files, AddedFile,
|
||||||
|
@ -22,6 +22,10 @@ use std::{io, time};
|
||||||
|
|
||||||
static SYNC_MAX_FILES: usize = 25;
|
static SYNC_MAX_FILES: usize = 25;
|
||||||
static SYNC_MAX_BYTES: usize = (2.5 * 1024.0 * 1024.0) as usize;
|
static SYNC_MAX_BYTES: usize = (2.5 * 1024.0 * 1024.0) as usize;
|
||||||
|
static SYNC_SINGLE_FILE_MAX_BYTES: usize = 100 * 1024 * 1024;
|
||||||
|
|
||||||
|
// fixme: non-normalized filenames on ankiweb
|
||||||
|
// fixme: concurrent modifications during upload step
|
||||||
|
|
||||||
/// The counts are not cumulative - the progress hook should accumulate them.
|
/// The counts are not cumulative - the progress hook should accumulate them.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -182,8 +186,11 @@ where
|
||||||
if data == "OK" {
|
if data == "OK" {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
// fixme: force resync, handle better
|
self.ctx.transact(|ctx| ctx.force_resync())?;
|
||||||
Err(AnkiError::server_message("resync required"))
|
Err(AnkiError::SyncError {
|
||||||
|
info: "".into(),
|
||||||
|
kind: SyncErrorKind::ResyncRequired,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(AnkiError::server_message(resp.err))
|
Err(AnkiError::server_message(resp.err))
|
||||||
|
@ -556,16 +563,17 @@ fn zip_files(media_folder: &Path, files: &[MediaEntry]) -> Result<Vec<u8>> {
|
||||||
|
|
||||||
let normalized = normalize_filename(&file.fname);
|
let normalized = normalize_filename(&file.fname);
|
||||||
if let Cow::Owned(_) = normalized {
|
if let Cow::Owned(_) = normalized {
|
||||||
// fixme: non-string err, or should ignore instead
|
return Err(media_check_required());
|
||||||
return Err(AnkiError::sync_misc("invalid file found"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let file_data = data_for_file(media_folder, &file.fname)?;
|
let file_data = data_for_file(media_folder, &file.fname)?;
|
||||||
|
|
||||||
if let Some(data) = &file_data {
|
if let Some(data) = &file_data {
|
||||||
if data.is_empty() {
|
if data.is_empty() {
|
||||||
// fixme: should ignore these, not error
|
return Err(media_check_required());
|
||||||
return Err(AnkiError::sync_misc("0 byte file found"));
|
}
|
||||||
|
if data.len() > SYNC_SINGLE_FILE_MAX_BYTES {
|
||||||
|
return Err(media_check_required());
|
||||||
}
|
}
|
||||||
accumulated_size += data.len();
|
accumulated_size += data.len();
|
||||||
zip.start_file(format!("{}", idx), options)?;
|
zip.start_file(format!("{}", idx), options)?;
|
||||||
|
@ -605,6 +613,13 @@ fn version_string() -> String {
|
||||||
format!("anki,{},{}", version(), std::env::consts::OS)
|
format!("anki,{},{}", version(), std::env::consts::OS)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn media_check_required() -> AnkiError {
|
||||||
|
AnkiError::SyncError {
|
||||||
|
info: "".into(),
|
||||||
|
kind: SyncErrorKind::MediaCheckRequired,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
struct FinalizeRequest {
|
struct FinalizeRequest {
|
||||||
local: u32,
|
local: u32,
|
||||||
|
|
Loading…
Reference in a new issue