Add schema to retrieve file content and api to get a file by id

Signed-off-by: trivernis <trivernis@protonmail.com>
pull/4/head
trivernis 3 years ago
parent 3f667b8d72
commit 2a69c5b748

@ -1,6 +1,6 @@
[package]
name = "mediarepo-api"
version = "0.6.0"
version = "0.7.0"
edition = "2018"
license = "gpl-3"
@ -16,6 +16,8 @@ serde_json = {version="1.0.68", optional=true}
directories = {version="4.0.1", optional=true}
mime_guess = {version = "2.0.3", optional=true}
serde_piecewise_default = "0.2.0"
futures = {version = "0.3.17", optional=true}
url = {version = "2.2.2", optional=true }
[dependencies.serde]
version = "1.0.130"
@ -41,5 +43,5 @@ version = "0.5.8"
optional = true
[features]
tauri-plugin = ["client-api","tauri", "rmp-ipc", "parking_lot", "serde_json", "tokio", "toml", "directories", "mime_guess"]
tauri-plugin = ["client-api","tauri", "rmp-ipc", "parking_lot", "serde_json", "tokio", "toml", "directories", "mime_guess", "futures", "url"]
client-api = ["rmp-ipc", "async-trait", "tokio"]

@ -55,6 +55,11 @@ where
self.emit_and_get("all_files", ()).await
}
/// Returns a file by identifier
pub async fn get_file(&self, id: FileIdentifier) -> ApiResult<FileMetadataResponse> {
self.emit_and_get("get_file", id).await
}
/// Searches for a file by a list of tags
#[tracing::instrument(level = "debug", skip(self))]
pub async fn find_files(

@ -1,24 +1,104 @@
use crate::tauri_plugin::state::BufferState;
use tauri::http::ResponseBuilder;
use tauri::{Builder, Manager, Runtime};
use crate::tauri_plugin::state::{ApiState, BufferState};
use crate::types::identifier::FileIdentifier;
use std::borrow::Cow;
use std::collections::HashMap;
use tauri::http::{Request, Response, ResponseBuilder};
use tauri::{AppHandle, Builder, Manager, Runtime};
use url::Url;
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
pub fn register_custom_uri_schemes<R: Runtime>(builder: Builder<R>) -> Builder<R> {
builder.register_uri_scheme_protocol("once", |app, request| {
let buf_state = app.state::<BufferState>();
let resource_key = request.uri().trim_start_matches("once://");
let buffer = buf_state.get_entry(resource_key);
if let Some(buffer) = buffer {
ResponseBuilder::new()
.mimetype(&buffer.mime)
.status(200)
.body(buffer.buf)
} else {
ResponseBuilder::new()
.mimetype("text/plain")
.status(404)
.body("Resource not found".as_bytes().to_vec())
}
})
builder
.register_uri_scheme_protocol("once", once_scheme)
.register_uri_scheme_protocol("content", content_scheme)
.register_uri_scheme_protocol("thumb", thumb_scheme)
}
fn once_scheme<R: Runtime>(app: &AppHandle<R>, request: &Request) -> Result<Response> {
let buf_state = app.state::<BufferState>();
let resource_key = request.uri().trim_start_matches("once://");
let buffer = buf_state.get_entry(resource_key);
if let Some(buffer) = buffer {
ResponseBuilder::new()
.mimetype(&buffer.mime)
.status(200)
.body(buffer.buf)
} else {
ResponseBuilder::new()
.mimetype("text/plain")
.status(404)
.body("Resource not found".as_bytes().to_vec())
}
}
fn content_scheme<R: Runtime>(app: &AppHandle<R>, request: &Request) -> Result<Response> {
let api_state = app.state::<ApiState>();
let buf_state = app.state::<BufferState>();
let hash = request.uri().trim_start_matches("content://");
if let Some(buffer) = buf_state.get_entry(hash) {
ResponseBuilder::new()
.status(200)
.mimetype(&buffer.mime)
.body(buffer.buf)
} else {
let api = api_state.api_sync()?;
let file =
futures::executor::block_on(api.file.get_file(FileIdentifier::Hash(hash.to_string())))?;
let mime = file.mime_type.unwrap_or("image/png".to_string());
let bytes = futures::executor::block_on(
api.file
.read_file_by_hash(FileIdentifier::Hash(hash.to_string())),
)?;
buf_state.add_entry(hash.to_string(), mime.clone(), bytes.clone());
ResponseBuilder::new()
.mimetype(&mime)
.status(200)
.body(bytes)
}
}
fn thumb_scheme<R: Runtime>(app: &AppHandle<R>, request: &Request) -> Result<Response> {
let api_state = app.state::<ApiState>();
let buf_state = app.state::<BufferState>();
let url = Url::parse(request.uri())?;
let hash = url.path();
let query_pairs = url
.query_pairs()
.collect::<HashMap<Cow<'_, str>, Cow<'_, str>>>();
let height = query_pairs
.get("height")
.and_then(|h| h.parse::<u32>().ok())
.unwrap_or(250);
let width = query_pairs
.get("width")
.and_then(|w| w.parse::<u32>().ok())
.unwrap_or(250);
if let Some(buffer) = buf_state.get_entry(hash) {
ResponseBuilder::new()
.status(200)
.mimetype(&buffer.mime)
.body(buffer.buf)
} else {
let api = api_state.api_sync()?;
let (thumb, bytes) = futures::executor::block_on(api.file.get_thumbnail_of_size(
FileIdentifier::Hash(hash.to_string()),
((height as f32 * 0.8) as u32, (width as f32 * 0.8) as u32),
((height as f32 * 1.2) as u32, (width as f32 * 1.2) as u32),
))?;
let mime = thumb.mime_type.unwrap_or(String::from("image/png"));
buf_state.add_entry(hash.to_string(), mime.clone(), bytes.clone());
ResponseBuilder::new()
.mimetype(&mime)
.status(200)
.body(bytes)
}
}

@ -54,6 +54,10 @@ impl ApiState {
.clone()
.ok_or_else(|| PluginError::from("Not connected"))
}
pub fn api_sync(&self) -> PluginResult<ApiClient<ApiProtocolListener>> {
futures::executor::block_on(self.api())
}
}
#[derive(Clone)]
@ -79,6 +83,13 @@ pub struct BufferState {
}
impl BufferState {
/// Adds a cached buffer to the buffer state
pub fn add_entry(&self, key: String, mime: String, bytes: Vec<u8>) {
let mut buffers = self.buffer.write();
let buffer = VolatileBuffer::new(mime, bytes);
buffers.insert(key, Mutex::new(buffer));
}
/// Checks if an entry for the specific key exists and resets
/// its state so that it can safely be accessed again.
pub fn reserve_entry(&self, key: &String) -> bool {

Loading…
Cancel
Save