mirror of
https://github.com/narrrl/proton-drive-linux.git
synced 2026-11-10 17:17:03 +01:00
Compare commits
3
Commits
main
...
chore/sdk-0.11.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a0ab243be6 | ||
|
|
2930b457d4 | ||
|
|
d9da0e2263 |
No files matched your search
Generated
+6
-4
@@ -3540,6 +3540,7 @@ version = "3.7.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"base64 0.23.1",
|
"base64 0.23.1",
|
||||||
|
"chrono",
|
||||||
"dbus-secret-service-keyring-store",
|
"dbus-secret-service-keyring-store",
|
||||||
"directories",
|
"directories",
|
||||||
"ignore",
|
"ignore",
|
||||||
@@ -3825,13 +3826,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proton-drive-rs"
|
name = "proton-drive-rs"
|
||||||
version = "0.10.1"
|
version = "0.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a8fa2a88130883010b32035e208aaa36fb275f6d7d78ed44a1069b1d33d24481"
|
checksum = "14a22e621abbc1c61b4640728a7de969c16b66d5d983b59c638d501fda25a7b9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"base64 0.23.1",
|
"base64 0.23.1",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
"chrono",
|
||||||
"futures",
|
"futures",
|
||||||
"getrandom 0.4.3",
|
"getrandom 0.4.3",
|
||||||
"hex",
|
"hex",
|
||||||
@@ -3852,9 +3854,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proton-sdk"
|
name = "proton-sdk"
|
||||||
version = "0.10.1"
|
version = "0.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "af0bf8d50fa829d1a65b09c6855b606578d02734d4089874c971d1063c23c353"
|
checksum = "bd55a5e8b6600f3aa282ebbdf13ff366431ff9aae84d4eb20b0ef06d598919fe"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes-gcm 0.11.1",
|
"aes-gcm 0.11.1",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
|
|||||||
+3
-2
@@ -15,8 +15,9 @@ keywords = ["proton", "drive", "fuse", "sync", "linux"]
|
|||||||
categories = ["filesystem", "command-line-utilities"]
|
categories = ["filesystem", "command-line-utilities"]
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
proton-sdk = { version = "0.10.1" }
|
proton-sdk = { version = "0.11.0" }
|
||||||
proton-drive-rs = { version = "0.10.1" }
|
proton-drive-rs = { version = "0.11.0" }
|
||||||
|
chrono = { version = "0.4", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
# Internal crates.
|
# Internal crates.
|
||||||
pdfs-core = { path = "crates/pdfs-core" }
|
pdfs-core = { path = "crates/pdfs-core" }
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ description = "Core auth/session/config for the Proton Drive Linux client"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
proton-sdk.workspace = true
|
proton-sdk.workspace = true
|
||||||
proton-drive-rs.workspace = true
|
proton-drive-rs.workspace = true
|
||||||
|
chrono.workspace = true
|
||||||
tokio.workspace = true
|
tokio.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ pub mod similar;
|
|||||||
pub mod syncignore;
|
pub mod syncignore;
|
||||||
pub mod syncroot;
|
pub mod syncroot;
|
||||||
pub mod takeout;
|
pub mod takeout;
|
||||||
|
pub mod timestamp;
|
||||||
pub mod tray;
|
pub mod tray;
|
||||||
pub mod update;
|
pub mod update;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
//! Convert the client's stored epoch seconds at the SDK boundary.
|
||||||
|
|
||||||
|
use proton_drive_rs::ModificationTime;
|
||||||
|
|
||||||
|
/// A UTC modification time, or `None` when outside the SDK's supported range.
|
||||||
|
pub fn modification_time(seconds: i64) -> Option<ModificationTime> {
|
||||||
|
chrono::DateTime::from_timestamp(seconds, 0).map(|time| time.fixed_offset())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn epoch_seconds_preserve_the_instant_including_before_1970() {
|
||||||
|
for seconds in [-1, 0, 1_709_208_000, 1_782_482_460] {
|
||||||
|
let time = modification_time(seconds).unwrap();
|
||||||
|
assert_eq!(time.timestamp(), seconds);
|
||||||
|
assert_eq!(time.offset().local_minus_utc(), 0);
|
||||||
|
assert_eq!(time.timestamp_subsec_nanos(), 0);
|
||||||
|
}
|
||||||
|
assert_eq!(
|
||||||
|
modification_time(-1).unwrap().to_rfc3339(),
|
||||||
|
"1969-12-31T23:59:59+00:00"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn out_of_range_seconds_omit_the_timestamp() {
|
||||||
|
assert!(modification_time(i64::MIN).is_none());
|
||||||
|
assert!(modification_time(i64::MAX).is_none());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -939,6 +939,8 @@ fn handle_control_conn(core: &Core, username: &str, mountpoint: &Path, stream: U
|
|||||||
});
|
});
|
||||||
let metadata = proton_drive_rs::PhotoUploadMetadata {
|
let metadata = proton_drive_rs::PhotoUploadMetadata {
|
||||||
capture_time,
|
capture_time,
|
||||||
|
last_modification_time: capture_time
|
||||||
|
.and_then(pdfs_core::timestamp::modification_time),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let thumbnails = core.upload_thumbnails(Path::new(&source_path), &name);
|
let thumbnails = core.upload_thumbnails(Path::new(&source_path), &name);
|
||||||
|
|||||||
@@ -352,7 +352,13 @@ impl DriveApi for ProtonDriveClient {
|
|||||||
name: &str,
|
name: &str,
|
||||||
last_modification_time: Option<i64>,
|
last_modification_time: Option<i64>,
|
||||||
) -> Result<NodeUid> {
|
) -> Result<NodeUid> {
|
||||||
ProtonDriveClient::create_folder(self, parent_uid, name, last_modification_time).await
|
ProtonDriveClient::create_folder(
|
||||||
|
self,
|
||||||
|
parent_uid,
|
||||||
|
name,
|
||||||
|
last_modification_time.and_then(pdfs_core::timestamp::modification_time),
|
||||||
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn upload_file(
|
async fn upload_file(
|
||||||
@@ -384,7 +390,7 @@ impl DriveApi for ProtonDriveClient {
|
|||||||
reader,
|
reader,
|
||||||
intended_size,
|
intended_size,
|
||||||
thumbnails,
|
thumbnails,
|
||||||
last_modification_time,
|
last_modification_time.and_then(pdfs_core::timestamp::modification_time),
|
||||||
aead,
|
aead,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
@@ -409,7 +415,7 @@ impl DriveApi for ProtonDriveClient {
|
|||||||
reader,
|
reader,
|
||||||
intended_size,
|
intended_size,
|
||||||
thumbnails,
|
thumbnails,
|
||||||
last_modification_time,
|
last_modification_time.and_then(pdfs_core::timestamp::modification_time),
|
||||||
aead,
|
aead,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
@@ -429,7 +435,7 @@ impl DriveApi for ProtonDriveClient {
|
|||||||
reader,
|
reader,
|
||||||
intended_size,
|
intended_size,
|
||||||
thumbnails,
|
thumbnails,
|
||||||
last_modification_time,
|
last_modification_time.and_then(pdfs_core::timestamp::modification_time),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -200,6 +200,7 @@ impl Core {
|
|||||||
let reader = CountingReader::new(std::io::BufReader::new(file), &guard);
|
let reader = CountingReader::new(std::io::BufReader::new(file), &guard);
|
||||||
let metadata = PhotoUploadMetadata {
|
let metadata = PhotoUploadMetadata {
|
||||||
capture_time: Some(candidate.wanted),
|
capture_time: Some(candidate.wanted),
|
||||||
|
last_modification_time: pdfs_core::timestamp::modification_time(candidate.wanted),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let uploaded = self.rt.block_on(photos.upload_photo_from(
|
let uploaded = self.rt.block_on(photos.upload_photo_from(
|
||||||
|
|||||||
@@ -344,6 +344,9 @@ impl Core {
|
|||||||
);
|
);
|
||||||
let metadata = PhotoUploadMetadata {
|
let metadata = PhotoUploadMetadata {
|
||||||
capture_time: photo.capture_time,
|
capture_time: photo.capture_time,
|
||||||
|
last_modification_time: photo
|
||||||
|
.capture_time
|
||||||
|
.and_then(pdfs_core::timestamp::modification_time),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
// A thumbnail is made from a file, so a photo that can have one
|
// A thumbnail is made from a file, so a photo that can have one
|
||||||
@@ -365,10 +368,13 @@ impl Core {
|
|||||||
let thumbnails = self.upload_thumbnails(path, &photo.name);
|
let thumbnails = self.upload_thumbnails(path, &photo.name);
|
||||||
// No sidecar and no date in the name: the photo's own
|
// No sidecar and no date in the name: the photo's own
|
||||||
// EXIF, before Drive files it under today.
|
// EXIF, before Drive files it under today.
|
||||||
|
let capture_time = metadata
|
||||||
|
.capture_time
|
||||||
|
.or_else(|| pdfs_core::exiftime::capture_time_of(path));
|
||||||
let metadata = PhotoUploadMetadata {
|
let metadata = PhotoUploadMetadata {
|
||||||
capture_time: metadata
|
capture_time,
|
||||||
.capture_time
|
last_modification_time: capture_time
|
||||||
.or_else(|| pdfs_core::exiftime::capture_time_of(path)),
|
.and_then(pdfs_core::timestamp::modification_time),
|
||||||
..metadata
|
..metadata
|
||||||
};
|
};
|
||||||
std::fs::File::open(path)
|
std::fs::File::open(path)
|
||||||
|
|||||||
@@ -11,6 +11,12 @@ scratch (user data in `staging/` and `recovery/` is never touched by this).
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Update `proton-sdk` and `proton-drive-rs` to 0.11.0.
|
||||||
|
Convert epoch timestamps at the SDK boundary and explicitly preserve photo modification
|
||||||
|
times now that they are independent of capture times. See [the SDK review](SDK-0.11.0.md)
|
||||||
|
for features and breaking changes.
|
||||||
Schema: **45**. Synced folders now distinguish device backups from My files attachments.
|
Schema: **45**. Synced folders now distinguish device backups from My files attachments.
|
||||||
Existing folders remain device backups; older builds cannot open the upgraded database.
|
Existing folders remain device backups; older builds cannot open the upgraded database.
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
# SDK 0.11.0 review
|
||||||
|
|
||||||
|
Reviewed the sibling `../proton-sdk-rs` checkout at `84039a1` against 0.10.1
|
||||||
|
(`48dd410`), including its changelog and source diff.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Folder creation, streaming uploads, draft replacement, and revision uploads
|
||||||
|
preserve the supplied timestamp's UTC offset and fractional seconds.
|
||||||
|
- `ModificationTime` is exported as an alias for
|
||||||
|
`chrono::DateTime<chrono::FixedOffset>`.
|
||||||
|
- Photo modification time can be set independently of capture time. Downloaded
|
||||||
|
claimed modification timestamps remain verbatim strings.
|
||||||
|
- Neither SDK crate defines Cargo feature flags. No feature opt-in is needed.
|
||||||
|
The Drive crate now depends directly on `chrono` 0.4; the SDK already used it.
|
||||||
|
The minimum Rust version remains 1.96, matching this workspace.
|
||||||
|
|
||||||
|
## Breaking changes and adaptation
|
||||||
|
|
||||||
|
The modification-time argument changes from `Option<i64>` to
|
||||||
|
`Option<ModificationTime>` on `create_folder`, `upload_file_from`,
|
||||||
|
`upload_file_replacing_draft_from`, `upload_new_revision`, and
|
||||||
|
`upload_new_revision_from`. `None` remains valid.
|
||||||
|
|
||||||
|
The real `DriveApi` adapter converts the client's stored epoch seconds to UTC
|
||||||
|
through `pdfs_core::timestamp::modification_time`. Internal storage, the trait,
|
||||||
|
and the simulated Drive continue using epoch seconds. Out-of-range timestamps
|
||||||
|
are omitted rather than panicking. Existing upload paths still supply whole
|
||||||
|
seconds; this bump alone does not add subsecond storage or offset tracking.
|
||||||
|
|
||||||
|
`PhotoUploadMetadata` gains `last_modification_time`, and the SDK no longer
|
||||||
|
copies `capture_time` into it. Our literals already use `Default`, so this would
|
||||||
|
compile while silently changing uploaded metadata. Control uploads, Takeout
|
||||||
|
imports (including the EXIF fallback), and redating now set both explicitly to
|
||||||
|
preserve the previous behavior when a capture time is known. Without a known
|
||||||
|
capture time, modification time stays omitted as before.
|
||||||
|
|
||||||
|
No other public API changes were found in the reviewed diff. No database or
|
||||||
|
recovery migration is required by this SDK bump.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
The workspace requires the published 0.11.0 versions of both crates from
|
||||||
|
crates.io. No local patches or sibling checkout are needed to build it.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
The published crates' source files match the reviewed sibling checkout at
|
||||||
|
`84039a1`. On the final branch based on `main`, these checks passed:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cargo test --workspace --locked
|
||||||
|
cargo fmt --all -- --check
|
||||||
|
cargo clippy --workspace --all-targets --locked -- -D warnings
|
||||||
|
```
|
||||||
|
|
||||||
|
The test suite reported 1,135 passed and 67 ignored; ignored tests were not run.
|
||||||
|
Tests ran outside the sandbox because control-socket tests require socket
|
||||||
|
permissions. No live account or mounted-filesystem acceptance testing was
|
||||||
|
performed.
|
||||||
@@ -91,7 +91,7 @@ SECTIONS = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
# Too long or too internal for the wiki; links to them go to GitHub.
|
# Too long or too internal for the wiki; links to them go to GitHub.
|
||||||
EXCLUDED = {"BUGS.md", "CHANGELOG.md"}
|
EXCLUDED = {"BUGS.md", "CHANGELOG.md", "SDK-0.11.0.md"}
|
||||||
|
|
||||||
# Files outside docs/ whose links are checked but not published.
|
# Files outside docs/ whose links are checked but not published.
|
||||||
CHECKED = ["README.md", "CONTRIBUTING.md", "SECURITY.md"]
|
CHECKED = ["README.md", "CONTRIBUTING.md", "SECURITY.md"]
|
||||||
|
|||||||
Reference in new issue
Block a user