2018-03-31 03:30:30 +08:00
|
|
|
import {Injectable} from '@angular/core';
|
|
|
|
import {GalleryCacheService} from './cache.gallery.service';
|
|
|
|
import {Photo} from './Photo';
|
|
|
|
import {IconPhoto} from './IconPhoto';
|
|
|
|
import {PhotoDTO} from '../../../common/entities/PhotoDTO';
|
|
|
|
import {Config} from '../../../common/config/public/Config';
|
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> = [];
|
|
|
|
runningRequests: number = 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
|
|
|
|
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
|
|
|
|
2017-06-28 05:00:28 +08:00
|
|
|
let 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
|
|
|
|
2017-06-28 05:00:28 +08:00
|
|
|
if (taskEntry.parentTask.taskEntities.length == 0
|
|
|
|
&& taskEntry.parentTask.inProgress == false) {
|
|
|
|
let 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
|
|
|
|
2017-06-11 04:32:56 +08:00
|
|
|
loadIcon(photo: IconPhoto, priority: ThumbnailLoadingPriority, listener: ThumbnailLoadingListener): ThumbnailTaskEntity {
|
2017-06-28 05:00:28 +08:00
|
|
|
let thTask: ThumbnailTask = null;
|
2017-06-11 04:32:56 +08:00
|
|
|
//is image already qued?
|
|
|
|
for (let i = 0; i < this.que.length; i++) {
|
|
|
|
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 = {
|
2017-06-11 04:32:56 +08:00
|
|
|
photo: photo.photo,
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
let thumbnailTaskEntity = {priority: priority, listener: listener, parentTask: thTask};
|
|
|
|
thTask.taskEntities.push(thumbnailTaskEntity);
|
|
|
|
if (thTask.inProgress == true) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
loadImage(photo: Photo, priority: ThumbnailLoadingPriority, listener: ThumbnailLoadingListener): ThumbnailTaskEntity {
|
|
|
|
|
2017-06-28 05:00:28 +08:00
|
|
|
let thTask: ThumbnailTask = null;
|
2017-06-11 04:32:56 +08:00
|
|
|
//is image already qued?
|
|
|
|
for (let i = 0; i < this.que.length; i++) {
|
|
|
|
if (this.que[i].path == photo.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 = {
|
2017-06-11 04:32:56 +08:00
|
|
|
photo: photo.photo,
|
|
|
|
inProgress: false,
|
2017-06-28 05:00:28 +08:00
|
|
|
taskEntities: [],
|
2017-06-11 04:32:56 +08:00
|
|
|
onLoaded: () => {
|
|
|
|
photo.thumbnailLoaded();
|
|
|
|
},
|
|
|
|
path: photo.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
|
|
|
|
|
|
|
let thumbnailTaskEntity = {priority: priority, listener: listener, parentTask: thTask};
|
|
|
|
|
2017-07-04 16:24:20 +08:00
|
|
|
//add to poolTask
|
2017-06-28 05:00:28 +08:00
|
|
|
thTask.taskEntities.push(thumbnailTaskEntity);
|
|
|
|
if (thTask.inProgress == true) {
|
|
|
|
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++) {
|
2017-07-26 05:40:07 +08:00
|
|
|
if (this.que[i].inProgress == false) {
|
|
|
|
if (highestPriority == null || currentPriority < this.que[i].taskEntities[j].priority) {
|
|
|
|
highestPriority = this.que[i];
|
|
|
|
currentPriority = this.que[i].taskEntities[j].priority;
|
|
|
|
if (currentPriority == ThumbnailLoadingPriority.extraHigh) {
|
|
|
|
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) {
|
|
|
|
let i = this.que.indexOf(task);
|
|
|
|
if (i == -1) {
|
|
|
|
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-17 06:05:31 +08:00
|
|
|
|
2016-06-25 05:44:05 +08:00
|
|
|
|
2017-06-11 04:32:56 +08:00
|
|
|
run = () => {
|
|
|
|
if (this.que.length === 0 || this.runningRequests >= Config.Client.concurrentThumbnailGenerations) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
let task = this.getNextTask();
|
2016-06-25 05:44:05 +08:00
|
|
|
|
2017-06-11 04:32:56 +08:00
|
|
|
if (task === null) {
|
|
|
|
return;
|
|
|
|
}
|
2016-06-25 05:44:05 +08:00
|
|
|
|
2017-06-11 04:32:56 +08:00
|
|
|
this.runningRequests++;
|
|
|
|
task.taskEntities.forEach(te => te.listener.onStartedLoading());
|
|
|
|
task.inProgress = true;
|
2016-06-24 04:25:16 +08:00
|
|
|
|
2017-06-11 04:32:56 +08:00
|
|
|
let curImg = new Image();
|
|
|
|
curImg.onload = () => {
|
|
|
|
task.onLoaded();
|
2017-06-28 05:00:28 +08:00
|
|
|
this.galleryCacheService.photoUpdated(task.photo);
|
2017-06-11 04:32:56 +08:00
|
|
|
task.taskEntities.forEach((te: ThumbnailTaskEntity) => te.listener.onLoad());
|
2016-06-17 06:05:31 +08:00
|
|
|
|
2017-06-11 04:32:56 +08:00
|
|
|
this.taskReady(task);
|
|
|
|
this.runningRequests--;
|
|
|
|
this.run();
|
|
|
|
};
|
2016-06-24 04:25:16 +08:00
|
|
|
|
2017-06-11 04:32:56 +08:00
|
|
|
curImg.onerror = (error) => {
|
|
|
|
task.taskEntities.forEach((te: ThumbnailTaskEntity) => te.listener.onError(error));
|
2017-01-23 03:30:23 +08:00
|
|
|
|
2017-06-11 04:32:56 +08:00
|
|
|
this.taskReady(task);
|
|
|
|
this.runningRequests--;
|
|
|
|
this.run();
|
2016-06-25 05:44:05 +08:00
|
|
|
};
|
2017-06-11 04:32:56 +08:00
|
|
|
|
|
|
|
curImg.src = task.path;
|
|
|
|
};
|
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 {
|
2017-06-11 04:32:56 +08:00
|
|
|
photo: PhotoDTO;
|
|
|
|
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
|
|
|
|