mirror of
https://github.com/xuthus83/pigallery2.git
synced 2025-01-14 14:43:17 +08:00
implementing basic directory view
This commit is contained in:
parent
c86fbf3509
commit
ccd756f3b7
@ -14,7 +14,7 @@ import {ProjectPath} from "../ProjectPath";
|
||||
export class GalleryMWs {
|
||||
|
||||
|
||||
public static listDirectory(req:Request, res:Response, next:NextFunction) {
|
||||
public static listDirectory(req: Request, res: Response, next: NextFunction) {
|
||||
let directoryName = req.params.directory || "/";
|
||||
let absoluteDirectoryName = path.join(ProjectPath.ImageFolder, directoryName);
|
||||
|
||||
@ -27,7 +27,7 @@ export class GalleryMWs {
|
||||
console.error(err);
|
||||
return next(new Error(ErrorCodes.GENERAL_ERROR, err));
|
||||
}
|
||||
|
||||
|
||||
req.resultPipe = new ContentWrapper(directory, null);
|
||||
|
||||
return next();
|
||||
@ -35,26 +35,33 @@ export class GalleryMWs {
|
||||
}
|
||||
|
||||
|
||||
public static removeCyclicDirectoryReferences(req:Request, res:Response, next:NextFunction) {
|
||||
public static removeCyclicDirectoryReferences(req: Request, res: Response, next: NextFunction) {
|
||||
if (!req.resultPipe)
|
||||
return next();
|
||||
|
||||
let cw:ContentWrapper = req.resultPipe;
|
||||
if (cw.directory) {
|
||||
cw.directory.photos.forEach((photo: PhotoDTO) => {
|
||||
let cw: ContentWrapper = req.resultPipe;
|
||||
let removeDirs = (dir) => {
|
||||
dir.photos.forEach((photo: PhotoDTO) => {
|
||||
photo.directory = null;
|
||||
});
|
||||
|
||||
cw.directory.directories.forEach((photo: DirectoryDTO) => {
|
||||
photo.parent = null;
|
||||
dir.directories.forEach((directory: DirectoryDTO) => {
|
||||
removeDirs(directory);
|
||||
directory.parent = null;
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
if (cw.directory) {
|
||||
removeDirs(cw.directory);
|
||||
}
|
||||
|
||||
|
||||
return next();
|
||||
}
|
||||
|
||||
|
||||
public static loadImage(req:Request, res:Response, next:NextFunction) {
|
||||
public static loadImage(req: Request, res: Response, next: NextFunction) {
|
||||
if (!(req.params.imagePath)) {
|
||||
return next();
|
||||
}
|
||||
@ -74,7 +81,7 @@ export class GalleryMWs {
|
||||
}
|
||||
|
||||
|
||||
public static search(req:Request, res:Response, next:NextFunction) {
|
||||
public static search(req: Request, res: Response, next: NextFunction) {
|
||||
if (Config.Client.Search.searchEnabled === false) {
|
||||
return next();
|
||||
}
|
||||
@ -83,7 +90,7 @@ export class GalleryMWs {
|
||||
return next();
|
||||
}
|
||||
|
||||
let type:SearchTypes;
|
||||
let type: SearchTypes;
|
||||
if (req.query.type) {
|
||||
type = parseInt(req.query.type);
|
||||
}
|
||||
@ -98,11 +105,11 @@ export class GalleryMWs {
|
||||
}
|
||||
|
||||
|
||||
public static instantSearch(req:Request, res:Response, next:NextFunction) {
|
||||
public static instantSearch(req: Request, res: Response, next: NextFunction) {
|
||||
if (Config.Client.Search.instantSearchEnabled === false) {
|
||||
return next();
|
||||
}
|
||||
|
||||
|
||||
if (!(req.params.text)) {
|
||||
return next();
|
||||
}
|
||||
@ -117,7 +124,7 @@ export class GalleryMWs {
|
||||
});
|
||||
}
|
||||
|
||||
public static autocomplete(req:Request, res:Response, next:NextFunction) {
|
||||
public static autocomplete(req: Request, res: Response, next: NextFunction) {
|
||||
if (Config.Client.Search.autocompleteEnabled === false) {
|
||||
return next();
|
||||
}
|
||||
@ -125,7 +132,7 @@ export class GalleryMWs {
|
||||
return next();
|
||||
}
|
||||
|
||||
ObjectManagerRepository.getInstance().getSearchManager().autocomplete(req.params.text, (err, items:Array<AutoCompleteItem>) => {
|
||||
ObjectManagerRepository.getInstance().getSearchManager().autocomplete(req.params.text, (err, items: Array<AutoCompleteItem>) => {
|
||||
if (err || !items) {
|
||||
return next(new Error(ErrorCodes.GENERAL_ERROR, err));
|
||||
}
|
||||
|
@ -105,55 +105,77 @@ pool.run(
|
||||
});
|
||||
};
|
||||
|
||||
let parseDir = (directoryInfo: {
|
||||
relativeDirectoryName: string,
|
||||
directoryName: string,
|
||||
directoryParent: string,
|
||||
absoluteDirectoryName: string
|
||||
}, maxPhotos: number = null, photosOnly: boolean = false): Promise<DirectoryDTO> => {
|
||||
|
||||
let directory = <DirectoryDTO>{
|
||||
name: input.directoryName,
|
||||
path: input.directoryParent,
|
||||
lastUpdate: Date.now(),
|
||||
directories: [],
|
||||
photos: []
|
||||
return new Promise<DirectoryDTO>((resolve, reject) => {
|
||||
let promises: Array<Promise<any>> = [];
|
||||
let directory = <DirectoryDTO>{
|
||||
name: directoryInfo.directoryName,
|
||||
path: directoryInfo.directoryParent,
|
||||
lastUpdate: Date.now(),
|
||||
directories: [],
|
||||
photos: []
|
||||
};
|
||||
fs.readdir(directoryInfo.absoluteDirectoryName, (err, list) => {
|
||||
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
let file = list[i];
|
||||
let fullFilePath = path.normalize(path.resolve(directoryInfo.absoluteDirectoryName, file));
|
||||
if (photosOnly == false && fs.statSync(fullFilePath).isDirectory()) {
|
||||
let promise = parseDir({
|
||||
relativeDirectoryName: path.join(directoryInfo.relativeDirectoryName, path.sep),
|
||||
directoryName: file,
|
||||
directoryParent: path.join(directoryInfo.relativeDirectoryName, path.sep),
|
||||
absoluteDirectoryName: fullFilePath
|
||||
},
|
||||
5, true
|
||||
).then((dir) => {
|
||||
directory.directories.push(dir);
|
||||
});
|
||||
promises.push(promise);
|
||||
} else if (isImage(fullFilePath)) {
|
||||
|
||||
|
||||
let promise = loadPhotoMetadata(fullFilePath).then((photoMetadata) => {
|
||||
directory.photos.push(<PhotoDTO>{name: file, directory: null, metadata: photoMetadata});
|
||||
});
|
||||
|
||||
promises.push(promise);
|
||||
if (maxPhotos != null && promises.length > maxPhotos) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Promise.all(promises).then(() => {
|
||||
return resolve(directory);
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
let promises: Array< Promise<any> > = [];
|
||||
fs.readdir(input.absoluteDirectoryName, (err, list) => {
|
||||
|
||||
if (err) {
|
||||
return done(err, null);
|
||||
}
|
||||
|
||||
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
let file = list[i];
|
||||
let fullFilePath = path.normalize(path.resolve(input.absoluteDirectoryName, file));
|
||||
if (fs.statSync(fullFilePath).isDirectory()) {
|
||||
directory.directories.push(<DirectoryDTO>{
|
||||
name: file,
|
||||
path: path.join(input.relativeDirectoryName, path.sep),
|
||||
lastUpdate: Date.now(),
|
||||
directories: [],
|
||||
photos: []
|
||||
});
|
||||
}
|
||||
|
||||
if (isImage(fullFilePath)) {
|
||||
|
||||
|
||||
let promise = loadPhotoMetadata(fullFilePath).then((photoMetadata) => {
|
||||
directory.photos.push(<PhotoDTO>{name: file, directory: null, metadata: photoMetadata});
|
||||
});
|
||||
|
||||
promises.push(promise);
|
||||
}
|
||||
}
|
||||
|
||||
Promise.all(promises).then(() => {
|
||||
console.log("DiskManager: scanDirectory finished");
|
||||
return done(err, directory);
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
|
||||
parseDir(input).then((dir) => {
|
||||
console.log(dir);
|
||||
done(null, dir);
|
||||
}).catch((err) => {
|
||||
done(err, null);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
export class DiskManager {
|
||||
@ -169,9 +191,16 @@ export class DiskManager {
|
||||
directoryParent,
|
||||
absoluteDirectoryName
|
||||
}) .on('done', (error: any, result: DirectoryDTO) => {
|
||||
result.photos.forEach((ph) => {
|
||||
ph.directory = result;
|
||||
});
|
||||
let addDirs = (dir: DirectoryDTO) => {
|
||||
dir.photos.forEach((ph) => {
|
||||
ph.directory = dir;
|
||||
});
|
||||
dir.directories.forEach((d) => {
|
||||
addDirs(d);
|
||||
});
|
||||
};
|
||||
addDirs(result);
|
||||
|
||||
return cb(error, result);
|
||||
}).on('error', (job, error) => {
|
||||
return cb(error, null);
|
||||
|
80
frontend/app/gallery/Photo.ts
Normal file
80
frontend/app/gallery/Photo.ts
Normal file
@ -0,0 +1,80 @@
|
||||
import {PhotoDTO} from "../../../common/entities/PhotoDTO";
|
||||
import {Utils} from "../../../common/Utils";
|
||||
import {Config} from "../config/Config";
|
||||
export class Photo {
|
||||
|
||||
protected replacementSizeCache: boolean|number = false;
|
||||
|
||||
constructor(public photo: PhotoDTO, public renderWidth: number, public renderHeight: number) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
thumbnailLoaded() {
|
||||
if (!this.isThumbnailAvailable()) {
|
||||
this.photo.readyThumbnails = this.photo.readyThumbnails || [];
|
||||
this.photo.readyThumbnails.push(this.getThumbnailSize());
|
||||
}
|
||||
}
|
||||
|
||||
getThumbnailSize() {
|
||||
let renderSize = Math.sqrt(this.renderWidth * this.renderHeight);
|
||||
return Utils.findClosest(renderSize, Config.Client.thumbnailSizes);
|
||||
}
|
||||
|
||||
getReplacementThumbnailSize() {
|
||||
|
||||
if (this.replacementSizeCache === false) {
|
||||
this.replacementSizeCache = null;
|
||||
|
||||
let size = this.getThumbnailSize();
|
||||
if (!!this.photo.readyThumbnails) {
|
||||
for (let i = 0; i < this.photo.readyThumbnails.length; i++) {
|
||||
if (this.photo.readyThumbnails[i] < size) {
|
||||
this.replacementSizeCache = this.photo.readyThumbnails[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.replacementSizeCache;
|
||||
}
|
||||
|
||||
isReplacementThumbnailAvailable() {
|
||||
return this.getReplacementThumbnailSize() !== null;
|
||||
}
|
||||
|
||||
isThumbnailAvailable() {
|
||||
return this.photo.readyThumbnails && this.photo.readyThumbnails.indexOf(this.getThumbnailSize()) != -1;
|
||||
}
|
||||
|
||||
getReplacementThumbnailPath() {
|
||||
let size = this.getReplacementThumbnailSize();
|
||||
return Utils.concatUrls("/api/gallery/content/", this.photo.directory.path, this.photo.directory.name, this.photo.name, "thumbnail", size.toString());
|
||||
|
||||
}
|
||||
|
||||
getThumbnailPath() {
|
||||
let size = this.getThumbnailSize();
|
||||
return Utils.concatUrls("/api/gallery/content/", this.photo.directory.path, this.photo.directory.name, this.photo.name, "thumbnail", size.toString());
|
||||
}
|
||||
|
||||
getPhotoPath() {
|
||||
return Utils.concatUrls("/api/gallery/content/", this.photo.directory.path, this.photo.directory.name, this.photo.name);
|
||||
}
|
||||
|
||||
|
||||
equals(other: any) {
|
||||
//is gridphoto
|
||||
if (other.photo) {
|
||||
return this.photo.directory.path === other.photo.directory.path && this.photo.directory.name === other.photo.directory.name && this.photo.name === other.photo.name
|
||||
}
|
||||
|
||||
//is photo
|
||||
if (other.directory) {
|
||||
return this.photo.directory.path === other.directory.path && this.photo.directory.name === other.directory.name && this.photo.name === other.name
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
a {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.photo-container {
|
||||
border: 2px solid #333;
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
|
||||
background-color: #bbbbbb;
|
||||
}
|
||||
|
||||
.no-image {
|
||||
|
||||
color: #7f7f7f;
|
||||
font-size: 50px;
|
||||
top: calc(50% - 25px);
|
||||
left: calc(50% - 25px);
|
||||
}
|
||||
|
||||
.photo {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.button {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
text-align: left;
|
||||
|
||||
}
|
||||
|
||||
.info {
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
color: white;
|
||||
font-size: medium;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
padding: 5px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
a:hover .info {
|
||||
background-color: rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
a:hover .photo-container {
|
||||
border-color: #000;
|
||||
}
|
@ -1,5 +1,19 @@
|
||||
<a class="button btn btn-default" [routerLink]="['/gallery', getDirectoryPath()]"
|
||||
style="display: inline-block;">
|
||||
{{directory.name}}
|
||||
|
||||
|
||||
<div class="photo-container">
|
||||
|
||||
<div class="photo" *ngIf="photo" [style.background-image]="'url('+photo.getThumbnailPath()+')'"></div>
|
||||
|
||||
<span *ngIf="!photo" class="glyphicon glyphicon-folder-open no-image" aria-hidden="true">
|
||||
|
||||
</span>
|
||||
</div>
|
||||
<!--Info box -->
|
||||
<div #info class="info">
|
||||
<div class="photo-name">{{directory.name}}</div>
|
||||
|
||||
</div>
|
||||
</a>
|
||||
|
||||
|
@ -1,19 +1,31 @@
|
||||
import {Component, Input} from "@angular/core";
|
||||
import {Component, Input, OnChanges} from "@angular/core";
|
||||
import {DirectoryDTO} from "../../../../common/entities/DirectoryDTO";
|
||||
import {RouterLink} from "@angular/router";
|
||||
import {Utils} from "../../../../common/Utils";
|
||||
import {Photo} from "../Photo";
|
||||
|
||||
@Component({
|
||||
selector: 'gallery-directory',
|
||||
templateUrl: 'app/gallery/directory/directory.gallery.component.html',
|
||||
styleUrls: ['app/gallery/directory/directory.gallery.component.css'],
|
||||
providers: [RouterLink],
|
||||
})
|
||||
export class GalleryDirectoryComponent {
|
||||
export class GalleryDirectoryComponent implements OnChanges {
|
||||
@Input() directory: DirectoryDTO;
|
||||
photo: Photo = null;
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
ngOnChanges() {
|
||||
setImmediate(() => {
|
||||
if (this.directory.photos.length > 0) {
|
||||
this.photo = new Photo(this.directory.photos[0], 100, 100);
|
||||
console.log(this.photo);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getDirectoryPath() {
|
||||
return Utils.concatUrls(this.directory.path, this.directory.name);
|
||||
}
|
||||
|
@ -36,11 +36,21 @@ export class GalleryService {
|
||||
if (this.lastRequest.directory != directoryName) {
|
||||
return;
|
||||
}
|
||||
let addDir = (dir: DirectoryDTO) => {
|
||||
dir.photos.forEach((photo: PhotoDTO) => {
|
||||
photo.directory = dir;
|
||||
});
|
||||
|
||||
dir.directories.forEach((directory: DirectoryDTO) => {
|
||||
addDir(directory);
|
||||
directory.parent = dir;
|
||||
});
|
||||
|
||||
|
||||
};
|
||||
addDir(message.result.directory);
|
||||
|
||||
|
||||
message.result.directory.photos.forEach((photo: PhotoDTO) => {
|
||||
photo.directory = message.result.directory;
|
||||
});
|
||||
|
||||
this.lastDirectory = message.result.directory;
|
||||
this.content = message.result;
|
||||
|
@ -1,80 +1,11 @@
|
||||
import {PhotoDTO} from "../../../../common/entities/PhotoDTO";
|
||||
import {Config} from "../../config/Config";
|
||||
import {Utils} from "../../../../common/Utils";
|
||||
export class GridPhoto {
|
||||
import {Photo} from "../Photo";
|
||||
export class GridPhoto extends Photo {
|
||||
|
||||
private replacementSizeCache:boolean|number = false;
|
||||
|
||||
constructor(public photo: PhotoDTO, public renderWidth: number, public renderHeight: number, public rowId: number) {
|
||||
|
||||
constructor(photo: PhotoDTO, renderWidth: number, renderHeight: number, public rowId: number) {
|
||||
super(photo, renderWidth, renderHeight);
|
||||
}
|
||||
|
||||
|
||||
thumbnailLoaded() {
|
||||
if (!this.isThumbnailAvailable()) {
|
||||
this.photo.readyThumbnails = this.photo.readyThumbnails || [];
|
||||
this.photo.readyThumbnails.push(this.getThumbnailSize());
|
||||
}
|
||||
}
|
||||
|
||||
getThumbnailSize() {
|
||||
let renderSize = Math.sqrt(this.renderWidth * this.renderHeight);
|
||||
return Utils.findClosest(renderSize, Config.Client.thumbnailSizes);
|
||||
}
|
||||
|
||||
getReplacementThumbnailSize() {
|
||||
|
||||
if (this.replacementSizeCache === false) {
|
||||
this.replacementSizeCache = null;
|
||||
|
||||
let size = this.getThumbnailSize();
|
||||
if (!!this.photo.readyThumbnails) {
|
||||
for (let i = 0; i < this.photo.readyThumbnails.length; i++) {
|
||||
if (this.photo.readyThumbnails[i] < size) {
|
||||
this.replacementSizeCache = this.photo.readyThumbnails[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.replacementSizeCache;
|
||||
}
|
||||
|
||||
isReplacementThumbnailAvailable() {
|
||||
return this.getReplacementThumbnailSize() !== null;
|
||||
}
|
||||
|
||||
isThumbnailAvailable() {
|
||||
return this.photo.readyThumbnails && this.photo.readyThumbnails.indexOf(this.getThumbnailSize()) != -1;
|
||||
}
|
||||
|
||||
getReplacementThumbnailPath() {
|
||||
let size = this.getReplacementThumbnailSize();
|
||||
return Utils.concatUrls("/api/gallery/content/", this.photo.directory.path, this.photo.directory.name, this.photo.name, "thumbnail", size.toString());
|
||||
|
||||
}
|
||||
|
||||
getThumbnailPath() {
|
||||
let size = this.getThumbnailSize();
|
||||
return Utils.concatUrls("/api/gallery/content/", this.photo.directory.path, this.photo.directory.name, this.photo.name, "thumbnail", size.toString());
|
||||
}
|
||||
|
||||
getPhotoPath() {
|
||||
return Utils.concatUrls("/api/gallery/content/", this.photo.directory.path, this.photo.directory.name, this.photo.name);
|
||||
}
|
||||
|
||||
|
||||
equals(other:any) {
|
||||
//is gridphoto
|
||||
if (other.photo) {
|
||||
return this.photo.directory.path === other.photo.directory.path && this.photo.directory.name === other.photo.directory.name && this.photo.name === other.photo.name
|
||||
}
|
||||
|
||||
//is photo
|
||||
if (other.directory) {
|
||||
return this.photo.directory.path === other.directory.path && this.photo.directory.name === other.directory.name && this.photo.name === other.name
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -1,4 +1,7 @@
|
||||
<div class="static" *ngIf="animate == false"><span class="glyphicon glyphicon-picture" aria-hidden="true"></span></div>
|
||||
<div class="static" *ngIf="animate == false">
|
||||
<span class="glyphicon glyphicon-picture" aria-hidden="true">
|
||||
</span>
|
||||
</div>
|
||||
<div class="sk-cube-grid animate" *ngIf="animate == true">
|
||||
<div class="sk-cube sk-cube1"></div>
|
||||
<div class="sk-cube sk-cube2"></div>
|
||||
|
Loading…
x
Reference in New Issue
Block a user