2018-03-31 03:30:30 +08:00
|
|
|
import {Injectable} from '@angular/core';
|
|
|
|
import {GalleryCacheService} from './cache.gallery.service';
|
2018-11-05 02:28:32 +08:00
|
|
|
import {Media} from './Media';
|
|
|
|
import {MediaIcon} from './MediaIcon';
|
2018-03-31 03:30:30 +08:00
|
|
|
import {PhotoDTO} from '../../../common/entities/PhotoDTO';
|
|
|
|
import {Config} from '../../../common/config/public/Config';
|
2018-11-05 02:28:32 +08:00
|
|
|
import {MediaDTO} from '../../../common/entities/MediaDTO';
|
2016-06-17 06:05:31 +08:00
|
|
|
|
2017-07-26 05:40:07 +08:00
|
|
|
export enum ThumbnailLoadingPriority {
|
|
|
|
extraHigh = 4, high = 3, medium = 2, low = 1
|
2016-06-25 05:44:05 +08:00
|
|
|
}
|
|
|
|
|
2016-06-17 06:05:31 +08:00
|
|
|
@Injectable()
|
|
|
|
export class ThumbnailLoaderService {
|
|
|
|
|
2017-06-11 04:32:56 +08:00
|
|
|
que: Array<ThumbnailTask> = [];
|
2018-05-13 00:19:51 +08:00
|
|
|
runningRequests = 0;
|
2016-06-17 06:05:31 +08:00
|
|
|
|
2017-06-28 05:00:28 +08:00
|
|
|
constructor(private galleryCacheService: GalleryCacheService) {
|
2017-06-11 04:32:56 +08:00
|
|
|
}
|
2016-06-17 06:05:31 +08:00
|
|
|
|
2018-05-13 00:19:51 +08:00
|
|
|
run = () => {
|
2018-11-02 23:24:37 +08:00
|
|
|
if (this.que.length === 0 || this.runningRequests >= Config.Client.Thumbnail.concurrentThumbnailGenerations) {
|
2018-05-13 00:19:51 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
const task = this.getNextTask();
|
|
|
|
|
|
|
|
if (task === null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.runningRequests++;
|
|
|
|
task.taskEntities.forEach(te => te.listener.onStartedLoading());
|
|
|
|
task.inProgress = true;
|
|
|
|
|
|
|
|
const curImg = new Image();
|
|
|
|
curImg.onload = () => {
|
|
|
|
task.onLoaded();
|
2018-11-05 02:28:32 +08:00
|
|
|
this.galleryCacheService.mediaUpdated(task.media);
|
2018-05-13 00:19:51 +08:00
|
|
|
task.taskEntities.forEach((te: ThumbnailTaskEntity) => te.listener.onLoad());
|
|
|
|
|
|
|
|
this.taskReady(task);
|
|
|
|
this.runningRequests--;
|
|
|
|
this.run();
|
|
|
|
};
|
|
|
|
|
|
|
|
curImg.onerror = (error) => {
|
|
|
|
task.taskEntities.forEach((te: ThumbnailTaskEntity) => te.listener.onError(error));
|
|
|
|
|
|
|
|
this.taskReady(task);
|
|
|
|
this.runningRequests--;
|
|
|
|
this.run();
|
|
|
|
};
|
|
|
|
|
|
|
|
curImg.src = task.path;
|
|
|
|
};
|
2016-06-24 04:25:16 +08:00
|
|
|
|
2017-06-11 04:32:56 +08:00
|
|
|
removeTask(taskEntry: ThumbnailTaskEntity) {
|
2016-06-25 05:44:05 +08:00
|
|
|
|
2018-05-13 00:19:51 +08:00
|
|
|
const index = taskEntry.parentTask.taskEntities.indexOf(taskEntry);
|
|
|
|
if (index === -1) {
|
2018-03-31 03:30:30 +08:00
|
|
|
throw new Error('ThumbnailTaskEntity not exist on Task');
|
2017-06-28 05:00:28 +08:00
|
|
|
}
|
|
|
|
taskEntry.parentTask.taskEntities.splice(index, 1);
|
2016-06-25 05:44:05 +08:00
|
|
|
|
2018-05-13 00:19:51 +08:00
|
|
|
if (taskEntry.parentTask.taskEntities.length === 0
|
|
|
|
&& taskEntry.parentTask.inProgress === false) {
|
|
|
|
const i = this.que.indexOf(taskEntry.parentTask);
|
|
|
|
if (i === -1) {
|
2018-03-31 03:30:30 +08:00
|
|
|
throw new Error('ThumbnailTask not exist');
|
2017-06-11 04:32:56 +08:00
|
|
|
}
|
2017-06-28 05:00:28 +08:00
|
|
|
this.que.splice(i, 1);
|
2016-06-25 05:44:05 +08:00
|
|
|
}
|
|
|
|
|
2017-06-11 04:32:56 +08:00
|
|
|
}
|
2017-03-21 04:37:23 +08:00
|
|
|
|
2018-11-05 02:28:32 +08:00
|
|
|
loadIcon(photo: MediaIcon, priority: ThumbnailLoadingPriority, listener: ThumbnailLoadingListener): ThumbnailTaskEntity {
|
2017-06-28 05:00:28 +08:00
|
|
|
let thTask: ThumbnailTask = null;
|
2018-05-13 00:19:51 +08:00
|
|
|
// is image already qued?
|
2017-06-11 04:32:56 +08:00
|
|
|
for (let i = 0; i < this.que.length; i++) {
|
2018-05-13 00:19:51 +08:00
|
|
|
if (this.que[i].path === photo.getIconPath()) {
|
2017-06-28 05:00:28 +08:00
|
|
|
thTask = this.que[i];
|
2017-06-11 04:32:56 +08:00
|
|
|
break;
|
|
|
|
}
|
2017-03-21 04:37:23 +08:00
|
|
|
}
|
2017-06-28 05:00:28 +08:00
|
|
|
if (thTask == null) {
|
|
|
|
thTask = {
|
2018-11-05 02:28:32 +08:00
|
|
|
media: photo.media,
|
2017-06-11 04:32:56 +08:00
|
|
|
inProgress: false,
|
2017-06-28 05:00:28 +08:00
|
|
|
taskEntities: [],
|
2017-06-11 04:32:56 +08:00
|
|
|
onLoaded: () => {
|
|
|
|
photo.iconLoaded();
|
|
|
|
},
|
|
|
|
path: photo.getIconPath()
|
2017-06-28 05:00:28 +08:00
|
|
|
};
|
|
|
|
this.que.push(thTask);
|
|
|
|
}
|
|
|
|
|
2018-05-13 00:19:51 +08:00
|
|
|
const thumbnailTaskEntity = {priority: priority, listener: listener, parentTask: thTask};
|
2017-06-28 05:00:28 +08:00
|
|
|
thTask.taskEntities.push(thumbnailTaskEntity);
|
2018-05-13 00:19:51 +08:00
|
|
|
if (thTask.inProgress === true) {
|
2017-06-28 05:00:28 +08:00
|
|
|
listener.onStartedLoading();
|
2017-06-11 04:32:56 +08:00
|
|
|
}
|
2017-06-28 05:00:28 +08:00
|
|
|
|
|
|
|
|
2017-06-11 04:56:23 +08:00
|
|
|
setTimeout(this.run, 0);
|
2017-06-11 04:32:56 +08:00
|
|
|
return thumbnailTaskEntity;
|
|
|
|
}
|
|
|
|
|
2018-11-05 02:28:32 +08:00
|
|
|
loadImage(media: Media, priority: ThumbnailLoadingPriority, listener: ThumbnailLoadingListener): ThumbnailTaskEntity {
|
2017-06-11 04:32:56 +08:00
|
|
|
|
2017-06-28 05:00:28 +08:00
|
|
|
let thTask: ThumbnailTask = null;
|
2018-05-13 00:19:51 +08:00
|
|
|
// is image already qued?
|
2017-06-11 04:32:56 +08:00
|
|
|
for (let i = 0; i < this.que.length; i++) {
|
2018-11-05 02:28:32 +08:00
|
|
|
if (this.que[i].path === media.getThumbnailPath()) {
|
2017-06-28 05:00:28 +08:00
|
|
|
thTask = this.que[i];
|
2017-06-11 04:32:56 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2017-06-28 05:00:28 +08:00
|
|
|
if (thTask == null) {
|
|
|
|
thTask = {
|
2018-11-05 02:28:32 +08:00
|
|
|
media: media.media,
|
2017-06-11 04:32:56 +08:00
|
|
|
inProgress: false,
|
2017-06-28 05:00:28 +08:00
|
|
|
taskEntities: [],
|
2017-06-11 04:32:56 +08:00
|
|
|
onLoaded: () => {
|
2018-11-05 02:28:32 +08:00
|
|
|
media.thumbnailLoaded();
|
2017-06-11 04:32:56 +08:00
|
|
|
},
|
2018-11-05 02:28:32 +08:00
|
|
|
path: media.getThumbnailPath()
|
2017-06-28 05:00:28 +08:00
|
|
|
};
|
|
|
|
this.que.push(thTask);
|
2016-06-25 05:44:05 +08:00
|
|
|
}
|
2017-06-28 05:00:28 +08:00
|
|
|
|
2018-05-13 00:19:51 +08:00
|
|
|
const thumbnailTaskEntity = {priority: priority, listener: listener, parentTask: thTask};
|
2017-06-28 05:00:28 +08:00
|
|
|
|
2018-05-13 00:19:51 +08:00
|
|
|
// add to poolTask
|
2017-06-28 05:00:28 +08:00
|
|
|
thTask.taskEntities.push(thumbnailTaskEntity);
|
2018-05-13 00:19:51 +08:00
|
|
|
if (thTask.inProgress === true) {
|
2017-06-28 05:00:28 +08:00
|
|
|
listener.onStartedLoading();
|
|
|
|
}
|
|
|
|
|
2017-06-11 04:56:23 +08:00
|
|
|
setTimeout(this.run, 0);
|
2017-06-11 04:32:56 +08:00
|
|
|
return thumbnailTaskEntity;
|
2016-06-25 05:44:05 +08:00
|
|
|
|
2017-06-11 04:32:56 +08:00
|
|
|
}
|
2016-06-25 05:44:05 +08:00
|
|
|
|
2017-06-11 04:32:56 +08:00
|
|
|
private getNextTask(): ThumbnailTask {
|
|
|
|
if (this.que.length === 0) {
|
|
|
|
return null;
|
|
|
|
}
|
2016-06-25 05:44:05 +08:00
|
|
|
|
2017-07-26 05:40:07 +08:00
|
|
|
let highestPriority: ThumbnailTask = null;
|
|
|
|
let currentPriority: ThumbnailLoadingPriority = Number.MAX_SAFE_INTEGER;
|
2017-06-11 04:32:56 +08:00
|
|
|
for (let i = 0; i < this.que.length; i++) {
|
|
|
|
for (let j = 0; j < this.que[i].taskEntities.length; j++) {
|
2018-05-13 00:19:51 +08:00
|
|
|
if (this.que[i].inProgress === false) {
|
2017-07-26 05:40:07 +08:00
|
|
|
if (highestPriority == null || currentPriority < this.que[i].taskEntities[j].priority) {
|
|
|
|
highestPriority = this.que[i];
|
|
|
|
currentPriority = this.que[i].taskEntities[j].priority;
|
2018-05-13 00:19:51 +08:00
|
|
|
if (currentPriority === ThumbnailLoadingPriority.extraHigh) {
|
2017-07-26 05:40:07 +08:00
|
|
|
return highestPriority;
|
|
|
|
}
|
|
|
|
}
|
2016-06-25 05:44:05 +08:00
|
|
|
}
|
2017-06-11 04:32:56 +08:00
|
|
|
}
|
|
|
|
}
|
2016-06-17 06:05:31 +08:00
|
|
|
|
2017-07-26 05:40:07 +08:00
|
|
|
return highestPriority;
|
2017-06-11 04:32:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
private taskReady(task: ThumbnailTask) {
|
2018-05-13 00:19:51 +08:00
|
|
|
const i = this.que.indexOf(task);
|
|
|
|
if (i === -1) {
|
2017-06-11 04:32:56 +08:00
|
|
|
if (task.taskEntities.length !== 0) {
|
2018-03-31 03:30:30 +08:00
|
|
|
console.error('ThumbnailLoader: can\'t find poolTask to remove');
|
2017-06-11 04:32:56 +08:00
|
|
|
}
|
|
|
|
return;
|
2016-06-25 05:44:05 +08:00
|
|
|
}
|
2017-06-11 04:32:56 +08:00
|
|
|
this.que.splice(i, 1);
|
|
|
|
}
|
2016-06-25 05:44:05 +08:00
|
|
|
}
|
|
|
|
|
2016-06-17 06:05:31 +08:00
|
|
|
|
2016-06-25 05:44:05 +08:00
|
|
|
export interface ThumbnailLoadingListener {
|
2017-06-11 04:32:56 +08:00
|
|
|
onStartedLoading: () => void;
|
|
|
|
onLoad: () => void;
|
|
|
|
onError: (error: any) => void;
|
2016-06-25 05:44:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export interface ThumbnailTaskEntity {
|
2017-06-11 04:32:56 +08:00
|
|
|
priority: ThumbnailLoadingPriority;
|
|
|
|
listener: ThumbnailLoadingListener;
|
2017-06-28 05:00:28 +08:00
|
|
|
parentTask: ThumbnailTask;
|
2016-06-17 06:05:31 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
interface ThumbnailTask {
|
2018-11-05 02:28:32 +08:00
|
|
|
media: MediaDTO;
|
2017-06-11 04:32:56 +08:00
|
|
|
inProgress: boolean;
|
|
|
|
taskEntities: Array<ThumbnailTaskEntity>;
|
|
|
|
path: string;
|
|
|
|
onLoaded: Function;
|
2016-06-17 06:05:31 +08:00
|
|
|
}
|
2017-03-21 04:37:23 +08:00
|
|
|
|