diff --git a/mediarepo-ui/README.md b/mediarepo-ui/README.md index c04b2ee..b66efa2 100644 --- a/mediarepo-ui/README.md +++ b/mediarepo-ui/README.md @@ -1,27 +1,26 @@ -# MediarepoUi +

+mediarepo-ui +

+

This repository is a work in progress

-This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 12.2.9. +- - - -## Development server +This repository contains a frontend client to connect to the [mediarepo-daemon](https://github.com/Trivernis/mediarepo-daemon). +It is written in tauri (yay). -Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. +## Usage -## Code scaffolding +Refer to [the tauri documentation](https://tauri.studio/en/docs/getting-started) for information about setting up your environment to build this project. +With the `cargo-tauri` tooling installed you can run +``` +cargo tauri dev +``` +to start the application in development mode or +``` +cargo tauri build +``` +to bundle the application. -Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. +## License -## Build - -Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. - -## Running unit tests - -Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). - -## Running end-to-end tests - -Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities. - -## Further help - -To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page. +GPL-3 diff --git a/mediarepo-ui/src-tauri/src/commands/files.rs b/mediarepo-ui/src-tauri/src/commands/files.rs index 8d908bd..877ff10 100644 --- a/mediarepo-ui/src-tauri/src/commands/files.rs +++ b/mediarepo-ui/src-tauri/src/commands/files.rs @@ -1,13 +1,13 @@ -use crate::commands::get_ipc; +use crate::commands::{add_once_buffer, get_ipc}; use mediarepo::requests::{FindFilesByTagsRequest, GetFileThumbnailsRequest, ReadFileRequest}; use mediarepo::responses::{FileResponse, ThumbnailResponse}; use crate::context::Context; -use crate::error::{AppError, AppResult}; +use crate::error::AppResult; #[tauri::command] pub async fn get_all_files(context: tauri::State<'_, Context>) -> AppResult> { - let ipc = get_ipc(context).await?; + let ipc = get_ipc(&context).await?; let response = ipc .emitter .emit_to("files", "all_files", ()) @@ -23,7 +23,7 @@ pub async fn find_files( tags: Vec, context: tauri::State<'_, Context>, ) -> AppResult> { - let ipc = get_ipc(context).await?; + let ipc = get_ipc(&context).await?; let response = ipc .emitter .emit_to("files", "find_files", FindFilesByTagsRequest { tags }) @@ -36,17 +36,20 @@ pub async fn find_files( #[tauri::command] pub async fn read_file_by_hash( hash: String, + mime: String, context: tauri::State<'_, Context>, -) -> AppResult> { - let ipc = get_ipc(context).await?; +) -> AppResult { + let ipc = get_ipc(&context).await?; let response = ipc .emitter - .emit_to("files", "read_file", ReadFileRequest::Hash(hash)) + .emit_to("files", "read_file", ReadFileRequest::Hash(hash.clone())) .await? .await_reply(&ipc) .await?; + let raw_data = response.data_raw().to_vec(); + let uri = add_once_buffer(&context, hash, mime, raw_data); - Ok(response.data_raw().to_vec()) + Ok(uri) } #[tauri::command] @@ -54,7 +57,7 @@ pub async fn get_thumbnails( hash: String, context: tauri::State<'_, Context>, ) -> AppResult> { - let ipc = get_ipc(context).await?; + let ipc = get_ipc(&context).await?; let response = ipc .emitter .emit_to( @@ -72,19 +75,18 @@ pub async fn get_thumbnails( #[tauri::command] pub async fn read_thumbnail( hash: String, + mime: String, context: tauri::State<'_, Context>, -) -> AppResult> { - let ipc = context.ipc.read().await; - if let Some(ipc) = &*ipc { - let response = ipc - .emitter - .emit_to("files", "read_thumbnail", hash) - .await? - .await_reply(&ipc) - .await?; +) -> AppResult { + let ipc = get_ipc(&context).await?; + let response = ipc + .emitter + .emit_to("files", "read_thumbnail", hash.clone()) + .await? + .await_reply(&ipc) + .await?; + let raw_data = response.data_raw().to_vec(); + let uri = add_once_buffer(&context, hash, mime, raw_data); - Ok(response.data_raw().to_vec()) - } else { - Err(AppError::new("No ipc connection.")) - } + Ok(uri) } diff --git a/mediarepo-ui/src-tauri/src/commands/mod.rs b/mediarepo-ui/src-tauri/src/commands/mod.rs index 594b24c..91aa347 100644 --- a/mediarepo-ui/src-tauri/src/commands/mod.rs +++ b/mediarepo-ui/src-tauri/src/commands/mod.rs @@ -1,6 +1,6 @@ use rmp_ipc::ipc::context::Context as IPCContext; -use crate::context::Context; +use crate::context::{Context, OnceBuffer}; use crate::error::{AppError, AppResult}; pub mod files; @@ -20,7 +20,22 @@ pub async fn emit_info(context: tauri::State<'_, Context>) -> AppResult<()> { Ok(()) } -pub async fn get_ipc(context: tauri::State<'_, Context>) -> AppResult { +pub async fn get_ipc(context: &tauri::State<'_, Context>) -> AppResult { let ipc = context.ipc.read().await; (ipc.clone()).ok_or(AppError::new("No ipc connection.")) } + +/// Adds a once-buffer to the buffer store +pub fn add_once_buffer( + context: &tauri::State<'_, Context>, + key: String, + mime: String, + buf: Vec, +) -> String { + let uri = format!("once://{}", key); + let once_buffer = OnceBuffer { mime, buf }; + let mut once_buffers = context.once_buffers.lock().unwrap(); + once_buffers.insert(key, once_buffer); + + uri +} diff --git a/mediarepo-ui/src-tauri/src/commands/tags.rs b/mediarepo-ui/src-tauri/src/commands/tags.rs index 1770e30..8d696ba 100644 --- a/mediarepo-ui/src-tauri/src/commands/tags.rs +++ b/mediarepo-ui/src-tauri/src/commands/tags.rs @@ -9,7 +9,7 @@ pub async fn get_tags_for_file( hash: String, context: tauri::State<'_, Context>, ) -> AppResult> { - let ipc = get_ipc(context).await?; + let ipc = get_ipc(&context).await?; let response = ipc .emitter .emit_to("tags", "tags_for_file", FileIdentifier::Hash(hash)) diff --git a/mediarepo-ui/src-tauri/src/context.rs b/mediarepo-ui/src-tauri/src/context.rs index 8e04277..34ef383 100644 --- a/mediarepo-ui/src-tauri/src/context.rs +++ b/mediarepo-ui/src-tauri/src/context.rs @@ -1,14 +1,22 @@ use crate::commands::repo::Repository; use crate::settings::Settings; use rmp_ipc::ipc::context::Context as IPCContext; +use std::collections::HashMap; use std::sync::Arc; +use std::sync::Mutex as StdMutex; use tokio::sync::RwLock; +pub struct OnceBuffer { + pub mime: String, + pub buf: Vec, +} + #[derive(Clone)] pub struct Context { pub active_repository: Arc>>, pub ipc: Arc>>, pub settings: Arc>, + pub once_buffers: Arc>>, } impl Context { @@ -17,6 +25,7 @@ impl Context { ipc: Arc::new(RwLock::new(None)), active_repository: Arc::new(RwLock::new(None)), settings: Arc::new(RwLock::new(settings)), + once_buffers: Arc::new(StdMutex::new(HashMap::new())), } } } diff --git a/mediarepo-ui/src-tauri/src/main.rs b/mediarepo-ui/src-tauri/src/main.rs index 189a6de..9ec99f2 100644 --- a/mediarepo-ui/src-tauri/src/main.rs +++ b/mediarepo-ui/src-tauri/src/main.rs @@ -11,6 +11,8 @@ use crate::commands::repo::{ use crate::commands::tags::*; use crate::context::Context; use crate::settings::load_settings; +use tauri::http::ResponseBuilder; +use tauri::Manager; mod commands; pub mod context; @@ -24,6 +26,25 @@ fn main() { tauri::Builder::default() .manage(context) + .register_uri_scheme_protocol("once", |app, request| { + let context = app.state::(); + let resource_key = request.uri().trim_start_matches("once://"); + let buffer = { + let mut buffers = context.once_buffers.lock().unwrap(); + buffers.remove(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()) + } + }) .invoke_handler(tauri::generate_handler![ get_repositories, add_repository, diff --git a/mediarepo-ui/src/app/services/file/file.service.ts b/mediarepo-ui/src/app/services/file/file.service.ts index b01cb3b..dd4d4a7 100644 --- a/mediarepo-ui/src/app/services/file/file.service.ts +++ b/mediarepo-ui/src/app/services/file/file.service.ts @@ -26,18 +26,14 @@ export class FileService { this.displayedFiles.next(files); } - public async readFile(hash: string, mime_type: string): Promise { - const data = await invoke("read_file_by_hash", {hash}); - const blob = new Blob([new Uint8Array(data)], {type: mime_type}); - - return this.createSafeObjectUrl(blob); + public async readFile(hash: string, mime: string): Promise { + const once_uri = await invoke("read_file_by_hash", {hash, mime}); + return this.sanitizer.bypassSecurityTrustResourceUrl(once_uri); } public async readThumbnail(thumbnail: Thumbnail): Promise { - let data = await invoke("read_thumbnail", {hash: thumbnail.hash}); - const blob = new Blob([new Uint8Array(data)], {type: thumbnail.mime}); - - return this.createSafeObjectUrl(blob); + let once_uri = await invoke("read_thumbnail", {hash: thumbnail.hash, mime: thumbnail.mime}); + return this.sanitizer.bypassSecurityTrustResourceUrl(once_uri); } public async getThumbnails(hash: string): Promise {