From f1619ad984b4b212ac82e26b0cae6ed79d85a40f Mon Sep 17 00:00:00 2001 From: "Patrik J. Braun" Date: Sun, 25 Nov 2018 15:22:07 +0100 Subject: [PATCH] improving video support (adding partial ogg,ogv,webm support) --- README.md | 3 +- backend/model/threading/DiskMangerWorker.ts | 42 +++++++++++-------- backend/model/threading/ThumbnailWorker.ts | 15 ++++++- backend/routes/GalleryRouter.ts | 4 +- frontend/app/gallery/MediaIcon.ts | 4 ++ ...info-panel.lightbox.gallery.component.html | 4 +- .../media.lightbox.gallery.component.html | 2 +- .../media/media.lightbox.gallery.component.ts | 12 ++++++ 8 files changed, 61 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 7938da77..46061c8e 100644 --- a/README.md +++ b/README.md @@ -138,7 +138,8 @@ apt-get install build-essential libkrb5-dev gcc g++ * Random photo url * You can generate an url that returns a random photo from your gallery. You can use this feature to develop 3rd party applications, like: changing desktop background * video support - * uses ffmpeg and ffprobe to generate video thumbnails + * fully supports *.mp4 files and partially (might have errors with safari and IE) supports *.ogg, *.ogv, *.webm files + * uses ffmpeg and ffprobe to generate video thumbnails * **Markdown based blogging support** - `future plan` * you can write some note in the blog.md for every directory * bug free :) - `In progress` diff --git a/backend/model/threading/DiskMangerWorker.ts b/backend/model/threading/DiskMangerWorker.ts index b806c17c..e329e431 100644 --- a/backend/model/threading/DiskMangerWorker.ts +++ b/backend/model/threading/DiskMangerWorker.ts @@ -35,7 +35,10 @@ export class DiskMangerWorker { private static isVideo(fullPath: string) { const extensions = [ - '.mp4' + '.mp4', + '.webm', + '.ogv', + '.ogg' ]; const extension = path.extname(fullPath).toLowerCase(); @@ -112,8 +115,8 @@ export class DiskMangerWorker { return new Promise((resolve, reject) => { const metadata: VideoMetadata = { size: { - width: 0, - height: 0 + width: 1, + height: 1 }, bitRate: 0, duration: 0, @@ -135,15 +138,18 @@ export class DiskMangerWorker { } try { + for (let i = 0; i < data.streams.length; i++) { + if (data.streams[i].width) { + metadata.size.width = data.streams[i].width; + metadata.size.height = data.streams[i].height; - metadata.size = { - width: data.streams[0].width, - height: data.streams[0].height - }; + metadata.duration = Math.floor(data.streams[i].duration * 1000); + metadata.bitRate = parseInt(data.streams[i].bit_rate, 10) || null; + metadata.creationDate = Date.parse(data.streams[i].tags.creation_time); + break; + } + } - metadata.duration = Math.floor(data.streams[0].duration * 1000); - metadata.bitRate = data.streams[0].bit_rate; - metadata.creationDate = Date.parse(data.streams[0].tags.creation_time); } catch (err) { } @@ -177,7 +183,7 @@ export class DiskMangerWorker { try { const exif = ExifParserFactory.create(data).parse(); - metadata.cameraData = { + metadata.cameraData = { ISO: exif.tags.ISO, model: exif.tags.Model, make: exif.tags.Make, @@ -188,7 +194,7 @@ export class DiskMangerWorker { }; if (!isNaN(exif.tags.GPSLatitude) || exif.tags.GPSLongitude || exif.tags.GPSAltitude) { metadata.positionData = metadata.positionData || {}; - metadata.positionData.GPSData = { + metadata.positionData.GPSData = { latitude: exif.tags.GPSLatitude, longitude: exif.tags.GPSLongitude, altitude: exif.tags.GPSAltitude @@ -205,15 +211,15 @@ export class DiskMangerWorker { if (exif.imageSize) { - metadata.size = {width: exif.imageSize.width, height: exif.imageSize.height}; + metadata.size = {width: exif.imageSize.width, height: exif.imageSize.height}; } else if (exif.tags.RelatedImageWidth && exif.tags.RelatedImageHeight) { - metadata.size = {width: exif.tags.RelatedImageWidth, height: exif.tags.RelatedImageHeight}; + metadata.size = {width: exif.tags.RelatedImageWidth, height: exif.tags.RelatedImageHeight}; } else { - metadata.size = {width: 1, height: 1}; + metadata.size = {width: 1, height: 1}; } } catch (err) { Logger.debug(LOG_TAG, 'Error parsing exif', fullPath, err); - metadata.size = {width: 1, height: 1}; + metadata.size = {width: 1, height: 1}; } try { @@ -224,8 +230,8 @@ export class DiskMangerWorker { metadata.positionData.state = iptcData.province_or_state; metadata.positionData.city = iptcData.city; } - metadata.keywords = (iptcData.keywords || []); - metadata.creationDate = (iptcData.date_time ? iptcData.date_time.getTime() : metadata.creationDate); + metadata.keywords = (iptcData.keywords || []); + metadata.creationDate = (iptcData.date_time ? iptcData.date_time.getTime() : metadata.creationDate); } catch (err) { // Logger.debug(LOG_TAG, "Error parsing iptc data", fullPath, err); diff --git a/backend/model/threading/ThumbnailWorker.ts b/backend/model/threading/ThumbnailWorker.ts index 0d40fb33..3f28e87b 100644 --- a/backend/model/threading/ThumbnailWorker.ts +++ b/backend/model/threading/ThumbnailWorker.ts @@ -62,7 +62,20 @@ export class VideoRendererFactory { if (!!err || data === null) { return reject(err.toString()); } - const ratio = data.streams[0].height / data.streams[0].width; + /// console.log(data); + let width = null; + let height = null; + for (let i = 0; i < data.streams.length; i++) { + if (data.streams[i].width) { + width = data.streams[i].width; + height = data.streams[i].height; + break; + } + } + if (!width || !height) { + return reject('Can not read video dimension'); + } + const ratio = height / width; const command: FfmpegCommand = ffmpeg(input.mediaPath); const fileName = path.basename(input.thPath); const folder = path.dirname(input.thPath); diff --git a/backend/routes/GalleryRouter.ts b/backend/routes/GalleryRouter.ts index 4ccc6b68..e79fc8b4 100644 --- a/backend/routes/GalleryRouter.ts +++ b/backend/routes/GalleryRouter.ts @@ -43,7 +43,7 @@ export class GalleryRouter { } private static addGetVideo(app) { - app.get(['/api/gallery/content/:mediaPath(*\.(mp4))'], + app.get(['/api/gallery/content/:mediaPath(*\.(mp4|ogg|ogv|webm))'], AuthenticationMWs.authenticate, // TODO: authorize path GalleryMWs.loadMedia, @@ -73,7 +73,7 @@ export class GalleryRouter { } private static addGetVideoThumbnail(app) { - app.get('/api/gallery/content/:mediaPath(*\.(mp4))/thumbnail/:size?', + app.get('/api/gallery/content/:mediaPath(*\.(mp4|ogg|ogv|webm))/thumbnail/:size?', AuthenticationMWs.authenticate, // TODO: authorize path GalleryMWs.loadMedia, diff --git a/frontend/app/gallery/MediaIcon.ts b/frontend/app/gallery/MediaIcon.ts index f3043e36..d67a0cdd 100644 --- a/frontend/app/gallery/MediaIcon.ts +++ b/frontend/app/gallery/MediaIcon.ts @@ -12,6 +12,10 @@ export class MediaIcon { } + getExtension(): string { + return this.media.name.substr(this.media.name.lastIndexOf('.') + 1); + } + iconLoaded() { this.media.readyIcon = true; } diff --git a/frontend/app/gallery/lightbox/infopanel/info-panel.lightbox.gallery.component.html b/frontend/app/gallery/lightbox/infopanel/info-panel.lightbox.gallery.component.html index 78d94370..8a7ccd9c 100644 --- a/frontend/app/gallery/lightbox/infopanel/info-panel.lightbox.gallery.component.html +++ b/frontend/app/gallery/lightbox/infopanel/info-panel.lightbox.gallery.component.html @@ -21,7 +21,7 @@ -
+
@@ -36,7 +36,7 @@
-
+
diff --git a/frontend/app/gallery/lightbox/media/media.lightbox.gallery.component.html b/frontend/app/gallery/lightbox/media/media.lightbox.gallery.component.html index a2238892..760192b4 100644 --- a/frontend/app/gallery/lightbox/media/media.lightbox.gallery.component.html +++ b/frontend/app/gallery/lightbox/media/media.lightbox.gallery.component.html @@ -19,7 +19,7 @@ autoplay (error)="onImageError()" (timeupdate)="onVideoProgress()" > - +
diff --git a/frontend/app/gallery/lightbox/media/media.lightbox.gallery.component.ts b/frontend/app/gallery/lightbox/media/media.lightbox.gallery.component.ts index 133d9902..ed66c856 100644 --- a/frontend/app/gallery/lightbox/media/media.lightbox.gallery.component.ts +++ b/frontend/app/gallery/lightbox/media/media.lightbox.gallery.component.ts @@ -119,6 +119,18 @@ export class GalleryLightboxMediaComponent implements OnChanges { return this.video.nativeElement.paused; } + public getVideoType(): string { + switch (this.gridMedia.getExtension().toLowerCase()) { + case 'webm': + return 'video/webm'; + case 'ogv': + case 'ogg': + return 'video/ogg'; + default: + return 'video/mp4'; + } + } + onImageError() { // TODO:handle error this.imageLoadFinished = true;