Improve thumbnail handling

Signed-off-by: trivernis <trivernis@protonmail.com>
pull/4/head
trivernis 3 years ago
parent 1d0ca6bb8e
commit 785f5853d8

@ -1580,8 +1580,8 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
[[package]] [[package]]
name = "mediarepo-api" name = "mediarepo-api"
version = "0.4.2" version = "0.5.1"
source = "git+https://github.com/Trivernis/mediarepo-api.git?rev=28b25e94eb2cdb8cec86e3e452081a649b8cd64e#28b25e94eb2cdb8cec86e3e452081a649b8cd64e" source = "git+https://github.com/Trivernis/mediarepo-api.git?rev=29131ac96de51d2362562aeb16fbfe4e8b0224ff#29131ac96de51d2362562aeb16fbfe4e8b0224ff"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"chrono", "chrono",
@ -2507,10 +2507,12 @@ dependencies = [
[[package]] [[package]]
name = "rmp-ipc" name = "rmp-ipc"
version = "0.8.1" version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17b727984a9179fbacb650930ae09537e06d50ce4fd767b0ce8b5a605f332199" checksum = "f6e9a9202fb951b3ca3088a4edd351774ef154efabb759d6aac2911cc1ae60c1"
dependencies = [ dependencies = [
"async-trait",
"byteorder",
"lazy_static", "lazy_static",
"rmp-serde", "rmp-serde",
"serde", "serde",

@ -30,7 +30,7 @@ features = ["env-filter"]
[dependencies.mediarepo-api] [dependencies.mediarepo-api]
git = "https://github.com/Trivernis/mediarepo-api.git" git = "https://github.com/Trivernis/mediarepo-api.git"
rev = "28b25e94eb2cdb8cec86e3e452081a649b8cd64e" rev = "29131ac96de51d2362562aeb16fbfe4e8b0224ff"
features = ["tauri-plugin"] features = ["tauri-plugin"]
[features] [features]

@ -34,8 +34,6 @@ export class FileGalleryEntryComponent implements OnInit, OnChanges {
this.cachedFile = this.file.data; this.cachedFile = this.file.data;
this.contentUrl = undefined; this.contentUrl = undefined;
await this.loadImage(); await this.loadImage();
} else if (!this.contentUrl) {
await this.loadImage();
} }
} }
@ -47,17 +45,10 @@ export class FileGalleryEntryComponent implements OnInit, OnChanges {
async loadImage() { async loadImage() {
try { try {
const hash = this.file.data.hash; const hash = this.file.data.hash;
const thumbnails = await this.fileService.getThumbnails(this.file.data); const contentUrl = await this.fileService.getFileThumbnail(this.file.data, 250, 250);
let thumbnail = thumbnails.find(
t => (t.height > 250 || t.width > 250) && (t.height < 500 && t.width < 500));
thumbnail = thumbnail ?? thumbnails[0];
if (!thumbnail) { if (this.file.data.hash === hash) { // avoid issues with changed files
console.log("Thumbnail is empty?!", thumbnails); this.contentUrl = contentUrl;
} else if (this.file.data.hash === hash) {
this.contentUrl = await this.fileService.readThumbnail(thumbnail!!);
} else {
console.warn("Grid file updated while loading thumbnail.")
} }
} catch (err) { } catch (err) {
this.errorBroker.showError(err); this.errorBroker.showError(err);

@ -1,12 +1,12 @@
import { import {
Component, Component,
ElementRef, ElementRef,
EventEmitter, EventEmitter,
Input, Input,
OnChanges, OnChanges,
OnInit, OnInit,
Output, Output, SimpleChanges,
ViewChild ViewChild
} from '@angular/core'; } from '@angular/core';
import {File} from "../../../models/File"; import {File} from "../../../models/File";
import {FileService} from "../../../services/file/file.service"; import {FileService} from "../../../services/file/file.service";
@ -37,8 +37,8 @@ export class FileGridEntryComponent implements OnInit, OnChanges {
await this.loadImage(); await this.loadImage();
} }
async ngOnChanges() { async ngOnChanges(changes: SimpleChanges) {
if (!this.cachedFile || this.gridEntry.file.hash !== this.cachedFile.hash) { if (changes["file"] && (!this.cachedFile || this.gridEntry.file.hash !== this.cachedFile.hash)) {
this.cachedFile = this.gridEntry.file; this.cachedFile = this.gridEntry.file;
await this.loadImage(); await this.loadImage();
} }
@ -46,18 +46,12 @@ export class FileGridEntryComponent implements OnInit, OnChanges {
async loadImage() { async loadImage() {
try { try {
const thumbnails = await this.fileService.getThumbnails( const hash = this.gridEntry.file.hash;
this.gridEntry.file); const contentUrl = await this.fileService.getFileThumbnail(this.gridEntry.file, 250, 250);
let thumbnail = thumbnails.find(
t => (t.height > 250 || t.width > 250) && (t.height < 500 && t.width < 500));
thumbnail = thumbnail ?? thumbnails[0];
if (!thumbnail) { if (this.gridEntry.file.hash === hash) { // avoid issues with changed files
console.log("Thumbnail is empty?!", thumbnails); this.contentUrl = contentUrl;
} else { }
this.contentUrl = await this.fileService.readThumbnail(
thumbnail!!);
}
} catch (err) { } catch (err) {
this.errorBroker.showError(err); this.errorBroker.showError(err);
} }

@ -13,6 +13,8 @@ import {SortKey} from "../../models/SortKey";
export class FileService { export class FileService {
displayedFiles = new BehaviorSubject<File[]>([]); displayedFiles = new BehaviorSubject<File[]>([]);
pendingThumbnails: {[key:number]: BehaviorSubject<boolean>} = {};
thumbnailCache: {[key: number]: Thumbnail[]} = {};
constructor(@Inject(DomSanitizer) private sanitizer: DomSanitizer) { constructor(@Inject(DomSanitizer) private sanitizer: DomSanitizer) {
@ -35,15 +37,59 @@ export class FileService {
return this.sanitizer.bypassSecurityTrustResourceUrl(once_uri); return this.sanitizer.bypassSecurityTrustResourceUrl(once_uri);
} }
/**
* Returns the thumbnail for a file with a specific size (allowing +-10%)
* If none can be found it asks the backend if it has one or generates one of that size
* @param {File} file
* @param {number} width
* @param {number} height
* @returns {Promise<SafeResourceUrl>}
*/
public async getFileThumbnail(file: File, width: number, height: number): Promise<SafeResourceUrl> {
let subject = this.pendingThumbnails[file.id];
if (subject && !await subject.toPromise()) { // avoid calling for the same thumbnail multiple times
await subject.toPromise();
}
subject = new BehaviorSubject<boolean>(false);
this.pendingThumbnails[file.id] = subject;
setTimeout(() => subject.next(true), 5000); // allow new request after 5 seconds max
const thumbnails = await this.getThumbnails(file);
const thumbnail = thumbnails.find(t => t.height >= height * 0.7 && t.width >= width * 0.7 && t.height <= height * 1.3 && t.width <= width * 1.3);
let url;
if (thumbnail) {
url = await this.readThumbnail(thumbnail);
} else {
url = await this.getThumbnailOfSize(file, height * 0.9, width * 0.9, height * 1.1, width * 1.1);
delete this.thumbnailCache[file.id];
}
this.pendingThumbnails[file.id].next(true);
delete this.pendingThumbnails[file.id];
return url;
}
public async readThumbnail(thumbnail: Thumbnail): Promise<SafeResourceUrl> { public async readThumbnail(thumbnail: Thumbnail): Promise<SafeResourceUrl> {
let once_uri = await invoke<string>("plugin:mediarepo|read_thumbnail", let once_uri = await invoke<string>("plugin:mediarepo|read_thumbnail",
{hash: thumbnail.hash, mimeType: thumbnail.mime_type}); {hash: thumbnail.hash, mimeType: thumbnail.mime_type});
return this.sanitizer.bypassSecurityTrustResourceUrl(once_uri); return this.sanitizer.bypassSecurityTrustResourceUrl(once_uri);
} }
public async getThumbnailOfSize(file: File, minHeight: number, minWidth: number, maxHeight: number, maxWidth: number): Promise<SafeResourceUrl> {
let once_uri = await invoke<string>("plugin:mediarepo|get_thumbnail_of_size", {fileId: file.id, minSize: [minHeight, minWidth], maxSize: [maxHeight, maxWidth]});
return this.sanitizer.bypassSecurityTrustResourceUrl(once_uri);
}
public async getThumbnails(file: File): Promise<Thumbnail[]> { public async getThumbnails(file: File): Promise<Thumbnail[]> {
return await invoke<Thumbnail[]>("plugin:mediarepo|get_file_thumbnails", const cachedThumbnails = this.thumbnailCache[file.id];
if (cachedThumbnails) {
return cachedThumbnails;
}
const thumbnails = await invoke<Thumbnail[]>("plugin:mediarepo|get_file_thumbnails",
{id: file.id}); {id: file.id});
this.thumbnailCache[file.id] = thumbnails;
return thumbnails;
} }
public async updateFileName(file: File, name: string): Promise<File> { public async updateFileName(file: File, name: string): Promise<File> {

Loading…
Cancel
Save