Add thumbnails and improve performance

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

@ -23,6 +23,8 @@
"@angular/platform-browser-dynamic": "~12.2.0",
"@angular/router": "~12.2.0",
"@tauri-apps/api": "^1.0.0-beta.8",
"primeicons": "^4.1.0",
"primeng": "^12.2.1",
"rxjs": "~6.6.0",
"tslib": "^2.3.0",
"zone.js": "~0.11.4"
@ -50,4 +52,4 @@
"karma-jasmine-html-reporter": "~1.7.0",
"typescript": "~4.3.5"
}
}
}

@ -55,7 +55,7 @@ version = "0.1.0"
dependencies = [
"directories",
"mediarepo",
"rmp-ipc",
"rmp-ipc 0.4.3",
"serde",
"serde_json",
"tauri",
@ -306,6 +306,12 @@ version = "3.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9df67f7bf9ef8498769f994239c45613ef0c5899415fb58e9add412d2c1a538"
[[package]]
name = "bytemuck"
version = "1.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72957246c41db82b8ef88a5486143830adeb8227ef9837740bdec67724cf2c5b"
[[package]]
name = "byteorder"
version = "1.4.3"
@ -460,6 +466,12 @@ dependencies = [
"objc",
]
[[package]]
name = "color_quant"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
[[package]]
name = "com"
version = "0.2.0"
@ -1256,6 +1268,16 @@ dependencies = [
"wasi 0.10.0+wasi-snapshot-preview1",
]
[[package]]
name = "gif"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3a7187e78088aead22ceedeee99779455b23fc231fe13ec443f99bb71694e5b"
dependencies = [
"color_quant",
"weezl",
]
[[package]]
name = "gio"
version = "0.14.8"
@ -1562,6 +1584,25 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "image"
version = "0.23.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1"
dependencies = [
"bytemuck",
"byteorder",
"color_quant",
"gif",
"jpeg-decoder",
"num-iter",
"num-rational",
"num-traits",
"png 0.16.8",
"scoped_threadpool",
"tiff",
]
[[package]]
name = "indexmap"
version = "1.7.0"
@ -1648,6 +1689,15 @@ dependencies = [
"libc",
]
[[package]]
name = "jpeg-decoder"
version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "229d53d58899083193af11e15917b5640cd40b29ff475a1fe4ef725deb02d0f2"
dependencies = [
"rayon",
]
[[package]]
name = "js-sys"
version = "0.3.55"
@ -1790,7 +1840,7 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
[[package]]
name = "mediarepo"
version = "0.1.0"
source = "git+https://github.com/Trivernis/mediarepo-daemon?rev=a928f5e42eb7a8ce524d3722a4f5ae05889e52ba#a928f5e42eb7a8ce524d3722a4f5ae05889e52ba"
source = "git+https://github.com/Trivernis/mediarepo-daemon?rev=57308089d58706ee1438f6b55d53691e9e08ce6b#57308089d58706ee1438f6b55d53691e9e08ce6b"
dependencies = [
"log",
"mediarepo-core",
@ -1801,12 +1851,14 @@ dependencies = [
[[package]]
name = "mediarepo-core"
version = "0.1.0"
source = "git+https://github.com/Trivernis/mediarepo-daemon?rev=a928f5e42eb7a8ce524d3722a4f5ae05889e52ba#a928f5e42eb7a8ce524d3722a4f5ae05889e52ba"
source = "git+https://github.com/Trivernis/mediarepo-daemon?rev=57308089d58706ee1438f6b55d53691e9e08ce6b#57308089d58706ee1438f6b55d53691e9e08ce6b"
dependencies = [
"base64",
"futures",
"image",
"multibase",
"multihash",
"rmp-ipc",
"rmp-ipc 0.6.0",
"sea-orm",
"serde",
"sqlx",
@ -1818,8 +1870,8 @@ dependencies = [
[[package]]
name = "mediarepo-database"
version = "0.1.0"
source = "git+https://github.com/Trivernis/mediarepo-daemon?rev=a928f5e42eb7a8ce524d3722a4f5ae05889e52ba#a928f5e42eb7a8ce524d3722a4f5ae05889e52ba"
version = "0.2.0"
source = "git+https://github.com/Trivernis/mediarepo-daemon?rev=57308089d58706ee1438f6b55d53691e9e08ce6b#57308089d58706ee1438f6b55d53691e9e08ce6b"
dependencies = [
"chrono",
"mediarepo-core",
@ -1830,7 +1882,7 @@ dependencies = [
[[package]]
name = "mediarepo-model"
version = "0.1.0"
source = "git+https://github.com/Trivernis/mediarepo-daemon?rev=a928f5e42eb7a8ce524d3722a4f5ae05889e52ba#a928f5e42eb7a8ce524d3722a4f5ae05889e52ba"
source = "git+https://github.com/Trivernis/mediarepo-daemon?rev=57308089d58706ee1438f6b55d53691e9e08ce6b#57308089d58706ee1438f6b55d53691e9e08ce6b"
dependencies = [
"chrono",
"mediarepo-core",
@ -1846,12 +1898,11 @@ dependencies = [
[[package]]
name = "mediarepo-socket"
version = "0.1.0"
source = "git+https://github.com/Trivernis/mediarepo-daemon?rev=a928f5e42eb7a8ce524d3722a4f5ae05889e52ba#a928f5e42eb7a8ce524d3722a4f5ae05889e52ba"
source = "git+https://github.com/Trivernis/mediarepo-daemon?rev=57308089d58706ee1438f6b55d53691e9e08ce6b#57308089d58706ee1438f6b55d53691e9e08ce6b"
dependencies = [
"chrono",
"mediarepo-core",
"mediarepo-model",
"rmp-ipc",
"serde",
"tokio",
]
@ -2147,6 +2198,17 @@ dependencies = [
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.14"
@ -2878,6 +2940,21 @@ dependencies = [
"typemap_rev",
]
[[package]]
name = "rmp-ipc"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbd5082cefa9a4407327087d75dfadb5bf2d6f07168d0b609a2509f54fa3b243"
dependencies = [
"lazy_static",
"log",
"rmp-serde",
"serde",
"thiserror",
"tokio",
"typemap_rev",
]
[[package]]
name = "rmp-serde"
version = "0.15.5"
@ -2958,6 +3035,12 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2"
[[package]]
name = "scoped_threadpool"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8"
[[package]]
name = "scopeguard"
version = "1.1.0"
@ -3791,6 +3874,17 @@ dependencies = [
"once_cell",
]
[[package]]
name = "tiff"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a53f4706d65497df0c4349241deddf35f84cee19c87ed86ea8ca590f4464437"
dependencies = [
"jpeg-decoder",
"miniz_oxide 0.4.4",
"weezl",
]
[[package]]
name = "time"
version = "0.1.44"
@ -4249,6 +4343,12 @@ dependencies = [
"winapi",
]
[[package]]
name = "weezl"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8b77fdfd5a253be4ab714e4ffa3c49caf146b4de743e97510c0656cf90f1e8e"
[[package]]
name = "wepoll-ffi"
version = "0.1.2"

@ -30,7 +30,7 @@ features = ["fs", "io-std", "io-util"]
[dependencies.mediarepo]
git = "https://github.com/Trivernis/mediarepo-daemon"
rev = "a928f5e42eb7a8ce524d3722a4f5ae05889e52ba"
rev = "57308089d58706ee1438f6b55d53691e9e08ce6b"
features = ["library"]
default-features=false

@ -1,5 +1,6 @@
use mediarepo::requests::ReadFileRequest;
use mediarepo::responses::FileResponse;
use mediarepo::requests::{GetFileThumbnailsRequest, ReadFileRequest};
use mediarepo::responses::{FileResponse, ThumbnailResponse};
use crate::context::Context;
use crate::error::{AppError, AppResult};
@ -25,5 +26,28 @@ pub async fn read_file_by_hash(hash: String, context: tauri::State<'_, Context>)
} else {
Err(AppError::new("No ipc connection."))
}
}
#[tauri::command]
pub async fn get_thumbnails(hash: String, context: tauri::State<'_, Context>) -> AppResult<Vec<ThumbnailResponse>> {
let ipc = context.ipc.read().await;
if let Some(ipc) = &*ipc {
let response = ipc.emitter.emit_to("files", "get_thumbnails", GetFileThumbnailsRequest::Hash(hash)).await?.await_reply(&ipc).await?;
Ok(response.data::<Vec<ThumbnailResponse>>()?)
} else {
Err(AppError::new("No ipc connection."))
}
}
#[tauri::command]
pub async fn read_thumbnail(hash: String, context: tauri::State<'_, Context>) -> AppResult<Vec<u8>> {
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?;
Ok(response.data::<Vec<u8>>()?)
} else {
Err(AppError::new("No ipc connection."))
}
}

@ -1 +0,0 @@
use mediarepo::requests::AddFileRequest;

@ -4,7 +4,7 @@
)]
use crate::commands::repo::{get_repositories, add_repository, select_repository, get_active_repository};
use crate::commands::files::{get_all_files, read_file_by_hash};
use crate::commands::files::*;
use crate::commands::emit_info;
use crate::context::Context;
use crate::settings::load_settings;
@ -21,7 +21,7 @@ fn main() {
tauri::Builder::default()
.manage(context)
.invoke_handler(tauri::generate_handler![get_repositories, add_repository, select_repository, get_active_repository, emit_info, get_all_files, read_file_by_hash])
.invoke_handler(tauri::generate_handler![get_repositories, add_repository, select_repository, get_active_repository, emit_info, get_all_files, read_file_by_hash, get_thumbnails, read_thumbnail])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}

@ -21,6 +21,8 @@ import {MatSidenavModule} from "@angular/material/sidenav";
import {MatGridListModule} from "@angular/material/grid-list";
import { FileGridEntryComponent } from './components/file-grid/file-grid-entry/file-grid-entry.component';
import {MatProgressBarModule} from "@angular/material/progress-bar";
import {MatPaginatorModule} from "@angular/material/paginator";
import {ScrollingModule} from "@angular/cdk/scrolling";
@NgModule({
declarations: [
@ -47,6 +49,8 @@ import {MatProgressBarModule} from "@angular/material/progress-bar";
MatSidenavModule,
MatGridListModule,
MatProgressBarModule,
MatPaginatorModule,
ScrollingModule,
],
providers: [],
bootstrap: [AppComponent]

@ -1,7 +1,7 @@
<mat-card>
<mat-card #card>
<mat-card-title *ngIf="!!file?.name">{{file?.name}}</mat-card-title>
<mat-card-content *ngIf="contentUrl !== undefined">
<img *ngIf="contentUrl !== undefined" class="entry-image" decoding="async" [src]="contentUrl" alt="File Image">
<img *ngIf="contentUrl !== undefined" class="entry-image" loading="lazy" decoding="async" [src]="contentUrl" alt="File Image">
</mat-card-content>
<mat-card-footer>
<mat-progress-bar *ngIf="contentUrl === undefined" mode="indeterminate"></mat-progress-bar>

@ -1,7 +1,6 @@
mat-card {
height: calc(100% - 32px);
width: calc(100% - 32px);
padding: 16px;
}
mat-card-content {
@ -14,5 +13,6 @@ mat-card-content {
.entry-image {
max-width: 100%;
max-height: 100%;
height: auto;
margin: auto;
}

@ -1,8 +1,16 @@
import {Component, Input, OnInit} from '@angular/core';
import {
AfterContentChecked,
Component,
Input,
OnInit,
ViewChild,
ElementRef, AfterViewInit
} from '@angular/core';
import {File} from "../../../models/File";
import {FileService} from "../../../services/file/file.service";
import {ErrorBrokerService} from "../../../services/error-broker/error-broker.service";
import {SafeResourceUrl} from "@angular/platform-browser";
import {MatCard} from "@angular/material/card";
@Component({
selector: 'app-file-grid-entry',
@ -11,19 +19,23 @@ import {SafeResourceUrl} from "@angular/platform-browser";
})
export class FileGridEntryComponent implements OnInit {
@Input() file: File | undefined;
@ViewChild("card") card!: ElementRef;
@Input() file!: File;
contentUrl: SafeResourceUrl | undefined;
constructor(private fileService: FileService, private errorBroker: ErrorBrokerService) { }
async ngOnInit(): Promise<void> {
if (this.file) {
console.log(this.file);
try {
this.contentUrl = await this.fileService.readFile(this.file.hash, this.file.mime_type ?? "image/png");
} catch (err) {
this.errorBroker.showError(err);
}
}
async ngOnInit() {
await this.loadImage();
}
async loadImage() {
try {
const thumbnails = await this.fileService.getThumbnails(this.file.hash);
let thumbnail = thumbnails.find(t => (t.height > 250 || t.width > 250) && (t.height < 500 && t.width < 500));
this.contentUrl = await this.fileService.readThumbnail(thumbnail!!);
} catch (err) {
this.errorBroker.showError(err);
}
}
}

@ -1,5 +1,7 @@
<mat-grid-list gutterSize="1em" cols="5" rowHeight="1:1">
<mat-grid-tile *ngFor="let file of files">
<app-file-grid-entry [file]="file"></app-file-grid-entry>
</mat-grid-tile>
</mat-grid-list>
<cdk-virtual-scroll-viewport class="file-scroll" maxBufferPx="500" minBufferPx="500" itemSize="250">
<div *cdkVirtualFor="let fileRow of fileRows">
<div class="file-row">
<app-file-grid-entry *ngFor="let file of fileRow" [file]="file"></app-file-grid-entry>
</div>
</div>
</cdk-virtual-scroll-viewport>

@ -1,4 +1,13 @@
app-file-grid-entry {
height: 250px;
width: 100%;
padding: 5px;
}
.file-scroll {
height: 100%;
width: 100%
}
.file-row {
display: flex;
}

@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core';
import {Component, Input, OnInit} from '@angular/core';
import {File} from "../../models/File";
import {FileService} from "../../services/file/file.service";
@ -7,14 +7,9 @@ import {FileService} from "../../services/file/file.service";
templateUrl: './file-grid.component.html',
styleUrls: ['./file-grid.component.scss']
})
export class FileGridComponent implements OnInit {
export class FileGridComponent {
files: File[] = [];
constructor(private fileService: FileService) { }
ngOnInit(): void {
this.fileService.displayedFiles.subscribe((files) => this.files = files);
}
@Input() fileRows: File[][] = [];
constructor() { }
}

@ -0,0 +1,6 @@
export type Thumbnail = {
hash: string,
height: number,
width: number,
mime: string | undefined
}

@ -7,7 +7,7 @@
<p>Drawer</p>
</mat-drawer>
<mat-drawer-content>
<app-file-grid></app-file-grid>
<app-file-grid [fileRows]="fileRows"></app-file-grid>
</mat-drawer-content>
</mat-drawer-container>

@ -10,6 +10,7 @@
mat-drawer {
height: 100%;
width: 25%;
}
mat-drawer-content {
@ -23,5 +24,5 @@ mat-drawer-container {
}
app-file-grid {
padding: 1em;
padding: 0;
}

@ -1,5 +1,7 @@
import { Component, OnInit } from '@angular/core';
import {FileService} from "../../services/file/file.service";
import {File} from "../../models/File";
import {PageEvent} from "@angular/material/paginator";
@Component({
selector: 'app-home',
@ -8,10 +10,23 @@ import {FileService} from "../../services/file/file.service";
})
export class HomeComponent implements OnInit {
fileRows: File[][] = [];
page: number = 0;
pageSize: number = 25;
constructor(private fileService: FileService) { }
async ngOnInit() {
this.fileService.displayedFiles.subscribe((files) => this.setFileRows(files));
await this.fileService.getFiles();
}
setFileRows(files: File[]) {
this.fileRows = [];
const filesPerRow = 6;
for (let i = 0; i < (Math.ceil(files.length /filesPerRow )); i++) {
this.fileRows.push(files.slice(i * filesPerRow, Math.min(files.length, (i + 1) * filesPerRow)))
}
console.log(this.fileRows);
}
}

@ -4,6 +4,7 @@ import {File} from "../../models/File";
import {invoke} from "@tauri-apps/api/tauri";
import {DOCUMENT} from "@angular/common";
import {DomSanitizer, SafeResourceUrl} from "@angular/platform-browser";
import {Thumbnail} from "../../models/Thumbnail";
@Injectable({
providedIn: 'root'
@ -18,7 +19,7 @@ export class FileService {
public async getFiles() {
let all_files = await invoke<File[]>("get_all_files");
this.displayedFiles.next(all_files.slice(70, 100));
this.displayedFiles.next(all_files);
}
public async readFile(hash: string, mime_type: string): Promise<SafeResourceUrl> {
@ -28,4 +29,16 @@ export class FileService {
const url = URL?.createObjectURL(blob);
return this.sanitizer.bypassSecurityTrustResourceUrl(url);
}
public async readThumbnail(thumbnail: Thumbnail): Promise<SafeResourceUrl> {
let data = await invoke<number[]>("read_thumbnail", {hash: thumbnail.hash});
const blob = new Blob([new Uint8Array(data)], {type: thumbnail.mime});
const url = URL?.createObjectURL(blob);
return this.sanitizer.bypassSecurityTrustResourceUrl(url);
}
public async getThumbnails(hash: string): Promise<Thumbnail[]> {
return await invoke<Thumbnail[]>("get_thumbnails", {hash});
}
}

@ -8272,6 +8272,18 @@ pretty-bytes@^5.3.0:
resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb"
integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==
primeicons@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/primeicons/-/primeicons-4.1.0.tgz#19eaef8ef5594b0006358ae64e738d03e167c9bb"
integrity sha512-uEv2pSPk1zQCfaB2VgnUfnUxxlGryYi+5rbdxmZBBt5v9S/pscIQYS5YDLxsQZ7D9jn5c76+Tx5wX/2J1nK6sA==
primeng@^12.2.1:
version "12.2.1"
resolved "https://registry.yarnpkg.com/primeng/-/primeng-12.2.1.tgz#a01f6207b6e65dc22dbafc7ffa97ae6d0a43cf86"
integrity sha512-2Nr/ASCq7SsLutFm/mBjudqCr31pO596qDs8zA6VcNMw0FmXjM85YqeHWB8hc4hlDzky4BHAuIKphvT4ykA+xw==
dependencies:
tslib "^2.1.0"
process-nextick-args@~2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
@ -9938,7 +9950,7 @@ tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
tslib@^2.0.0, tslib@^2.2.0, tslib@^2.3.0:
tslib@^2.0.0, tslib@^2.1.0, tslib@^2.2.0, tslib@^2.3.0:
version "2.3.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01"
integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==

Loading…
Cancel
Save