Unifi asset download and thumbnail download response type
Some checks failed
Integration Tests / integration-test (push) Failing after 19s
Some checks failed
Integration Tests / integration-test (push) Failing after 19s
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -1016,6 +1016,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"chrono",
|
||||
"image",
|
||||
"mime",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
||||
@@ -24,6 +24,7 @@ image = "0.25"
|
||||
url = "2.5"
|
||||
bytes = "1.10"
|
||||
async-trait = "0.1"
|
||||
mime = "0.3.17"
|
||||
|
||||
[dev-dependencies]
|
||||
tokio-test = "0.4"
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
//! Assets API - Manage photos and videos
|
||||
|
||||
use std::io::Cursor;
|
||||
use std::path::Path;
|
||||
use std::{io::Cursor, str::FromStr};
|
||||
|
||||
use mime::Mime;
|
||||
use reqwest::header::CONTENT_TYPE;
|
||||
|
||||
use crate::{
|
||||
Client,
|
||||
@@ -14,17 +17,17 @@ use crate::{
|
||||
|
||||
/// Response from downloading a thumbnail containing image data and metadata
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ThumbnailResponse {
|
||||
/// The image data as bytes
|
||||
pub struct AssetDownload {
|
||||
/// The asset data as bytes
|
||||
pub data: bytes::Bytes,
|
||||
/// The MIME type of the image (e.g., "image/jpeg", "image/png", "image/webp")
|
||||
pub content_type: String,
|
||||
/// The MIME type of the asset (e.g., "image/jpeg", "image/png", "image/webp")
|
||||
pub mime: Mime,
|
||||
}
|
||||
|
||||
impl ThumbnailResponse {
|
||||
impl AssetDownload {
|
||||
/// Get the file extension based on content type
|
||||
pub fn extension(&self) -> Option<&str> {
|
||||
match self.content_type.as_str() {
|
||||
match self.mime.essence_str() {
|
||||
"image/jpeg" | "image/jpg" => Some("jpg"),
|
||||
"image/png" => Some("png"),
|
||||
"image/webp" => Some("webp"),
|
||||
@@ -35,8 +38,8 @@ impl ThumbnailResponse {
|
||||
}
|
||||
|
||||
/// Get the content type
|
||||
pub fn content_type(&self) -> &str {
|
||||
&self.content_type
|
||||
pub fn content_type(&self) -> &Mime {
|
||||
&self.mime
|
||||
}
|
||||
|
||||
/// Get the data
|
||||
@@ -52,7 +55,7 @@ impl ThumbnailResponse {
|
||||
/// Decode the image data into a DynamicImage
|
||||
///
|
||||
/// # Errors
|
||||
/// Returns an error if the content type is not supported or if decoding fails
|
||||
/// Returns an error if the asset is not an image, content type is not supported, or if decoding fails
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust,ignore
|
||||
@@ -69,8 +72,8 @@ impl ThumbnailResponse {
|
||||
/// ```
|
||||
pub fn decode(&self) -> Result<image::DynamicImage> {
|
||||
// Get image format from content type
|
||||
let format = image::ImageFormat::from_mime_type(&self.content_type).ok_or_else(|| {
|
||||
ImmichError::Image(format!("Unsupported content type: {}", self.content_type))
|
||||
let format = image::ImageFormat::from_mime_type(&self.mime).ok_or_else(|| {
|
||||
ImmichError::Image(format!("Unsupported content type: {}", self.mime))
|
||||
})?;
|
||||
|
||||
// Create reader with the format and decode
|
||||
@@ -303,7 +306,10 @@ impl UploadAssetBuilder {
|
||||
|
||||
// Add file timestamps (required by Immich API v2)
|
||||
let now = chrono::Utc::now().to_rfc3339();
|
||||
form = form.text("fileCreatedAt", self.file_created_at.unwrap_or_else(|| now.clone()));
|
||||
form = form.text(
|
||||
"fileCreatedAt",
|
||||
self.file_created_at.unwrap_or_else(|| now.clone()),
|
||||
);
|
||||
form = form.text("fileModifiedAt", self.file_modified_at.unwrap_or(now));
|
||||
|
||||
let req = self.client.post("/assets").multipart(form);
|
||||
@@ -386,7 +392,7 @@ impl DownloadAssetBuilder {
|
||||
}
|
||||
|
||||
/// Execute the download
|
||||
pub async fn execute(self) -> Result<bytes::Bytes> {
|
||||
pub async fn execute(self) -> Result<AssetDownload> {
|
||||
let path = format!("/assets/{}/original", self.id);
|
||||
let mut req = self.client.get(&path);
|
||||
|
||||
@@ -395,8 +401,18 @@ impl DownloadAssetBuilder {
|
||||
}
|
||||
|
||||
let response = self.client.execute(req.build()?).await?;
|
||||
let bytes = response.bytes().await?;
|
||||
Ok(bytes)
|
||||
|
||||
// Get content type from response headers
|
||||
let mime = response
|
||||
.headers()
|
||||
.get(CONTENT_TYPE)
|
||||
.and_then(|ct| ct.to_str().ok())
|
||||
.and_then(|ct| Mime::from_str(ct).ok())
|
||||
.ok_or(ImmichError::Image("Missing or invalid content-type".into()))?;
|
||||
|
||||
let data = response.bytes().await?;
|
||||
|
||||
Ok(AssetDownload { data, mime })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -433,7 +449,7 @@ impl ThumbnailBuilder {
|
||||
}
|
||||
|
||||
/// Execute the request
|
||||
pub async fn execute(self) -> Result<ThumbnailResponse> {
|
||||
pub async fn execute(self) -> Result<AssetDownload> {
|
||||
let path = format!("/assets/{}/thumbnail", self.id);
|
||||
let mut req = self.client.get(&path);
|
||||
|
||||
@@ -454,15 +470,15 @@ impl ThumbnailBuilder {
|
||||
let response = self.client.execute(req.build()?).await?;
|
||||
|
||||
// Get content type from response headers
|
||||
let content_type = response
|
||||
let mime = response
|
||||
.headers()
|
||||
.get("content-type")
|
||||
.get(CONTENT_TYPE)
|
||||
.and_then(|ct| ct.to_str().ok())
|
||||
.map(String::from)
|
||||
.unwrap_or_else(|| "application/octet-stream".to_string());
|
||||
.and_then(|ct| Mime::from_str(ct).ok())
|
||||
.ok_or(ImmichError::Image("Missing or invalid content-type".into()))?;
|
||||
|
||||
let data = response.bytes().await?;
|
||||
|
||||
Ok(ThumbnailResponse { data, content_type })
|
||||
Ok(AssetDownload { data, mime })
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user