Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New filmstrip #44

Merged
merged 10 commits into from Dec 28, 2018
3 changes: 2 additions & 1 deletion main-filenames.ts
Expand Up @@ -19,9 +19,10 @@ export const acceptableFiles = [
'mpe',
'mpeg',
'mpg',
'mxf',
'ogg',
'rm',
'vob',
'webm',
'wmv'
];
];
2 changes: 1 addition & 1 deletion main-misc.ts
Expand Up @@ -14,4 +14,4 @@ if (!timestamps.length) {
t = t + 1;
}
}
*/
*/
43 changes: 26 additions & 17 deletions main-support.ts
Expand Up @@ -225,15 +225,15 @@ const exec = require('child_process').exec;
* save as particular fileNumber
* @param pathToVideo -- full path to the video file
* @param fileHash -- hash of the video file
* @param screensize -- resolution in pixels (defaul is 100)
* @param screenshotHeight -- height of screenshot in pixels (defaul is 100)
* @param saveLocation -- folder where to save jpg files
* @param done -- callback when done
*/
export function takeTenScreenshots(
pathToVideo: string,
fileHash: string,
duration: number,
screenSize: number,
screenshotHeight: number,
saveLocation: string,
done: any
) {
Expand All @@ -243,27 +243,37 @@ export function takeTenScreenshots(
takeTenClips(pathToVideo,
fileHash,
duration,
screenSize,
screenshotHeight,
saveLocation,
done);
return;
}

let current = 1;
const totalCount = 11;
const step: number = duration / totalCount;
let current = 0;
const totalCount = 10;
const step: number = duration / (totalCount + 1);
const args = [];
let concat = '';
let allFramesFiltered = '';
let outputFrames = '';

// Hardcode ~16:9 ratio
const ssWidth: number = Math.ceil(screenshotHeight * 16 / 9);
const ratioString: string = ssWidth + ':' + screenshotHeight;

// sweet thanks to StackExchange!
// https://superuser.com/questions/547296/resizing-videos-with-ffmpeg-avconv-to-fit-into-static-sized-player
const fancyScaleFilter = 'scale=' + ratioString + ':force_original_aspect_ratio=decrease,pad=' + ratioString + ':(ow-iw)/2:(oh-ih)/2';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm getting an issue when I try to use a thumbnail size of 50px:

grep stderr: Stream mapping:
  Stream #0:0 (h264) -> scale
  Stream #1:0 (h264) -> scale
  Stream #2:0 (h264) -> scale
  Stream #3:0 (h264) -> scale
  Stream #4:0 (h264) -> scale
  Stream #5:0 (h264) -> scale
  Stream #6:0 (h264) -> scale
  Stream #7:0 (h264) -> scale
  Stream #8:0 (h264) -> scale
  Stream #9:0 (h264) -> scale
  hstack -> Stream #0:0 (mjpeg)
Press [q] to stop, [?] for help

grep stderr: [swscaler @ 0x7fd12ada9a00] deprecated pixel format used, make sure you did set range correctly

grep stderr: [Parsed_pad_1 @ 0x7fd129099800] Input area 0:0:89:50 not within the padded area 0:0:88:50 or zero-sized
[Parsed_pad_1 @ 0x7fd129099800] Failed to configure input pad on Parsed_pad_1

grep stderr: Error reinitializing filters!
Failed to inject frame into filter network: Invalid argument

grep stderr: Error while processing the decoded data for stream #9:0

grep stderr: Conversion failed!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Input area 0:0:89:50 not within the padded area 0:0:88:50

Looks like rounding error!


// make the magic filter
while (current < totalCount) {
const time = current * step;
const time = (current + 1) * step; // +1 so we don't pick the 0th frame
args.push('-ss', time, '-i', pathToVideo);
concat += '[' + (current - 1) + ':v]';
allFramesFiltered += '[' + current + ':v]' + fancyScaleFilter + '[' + current + '];';
outputFrames += '[' + current + ']';
current++;
}
args.push('-frames', 1,
'-filter_complex', concat + 'vstack=inputs=' + (totalCount - 1),
'-filter_complex', allFramesFiltered + outputFrames + 'hstack=inputs=' + totalCount,
saveLocation + '/' + fileHash + '.jpg'
);

Expand All @@ -278,7 +288,7 @@ export function takeTenScreenshots(
takeTenClips(pathToVideo,
fileHash,
duration,
screenSize,
screenshotHeight,
saveLocation,
done);
});
Expand All @@ -290,15 +300,15 @@ export function takeTenScreenshots(
* save as particular fileNumber
* @param pathToVideo -- full path to the video file
* @param fileHash -- hash of the video file
* @param screensize -- resolution in pixels (defaul is 100)
* @param screenshotHeight -- resolution in pixels (defaul is 100)
* @param saveLocation -- folder where to save jpg files
* @param done -- callback when done
*/
export function takeTenClips(
pathToVideo: string,
fileHash: string,
duration: number,
screenSize: number,
screenshotHeight: number,
saveLocation: string,
done: any
) {
Expand All @@ -323,8 +333,8 @@ export function takeTenClips(
concat += '[' + (current - 1) + ':v]' + '[' + (current - 1) + ':a]';
current++;
}
concat += 'concat=n=' + (totalCount - 1) + ':v=1:a=1';
args.push('-filter_complex', concat, saveLocation + '/' + fileHash + '.mp4');
concat += 'concat=n=' + (totalCount - 1) + ':v=1:a=1[v][a];[v]scale=' + screenshotHeight + ':-2[v2]';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just realised, this is now backwards (generates 300px wide, not high)!

Should be -2:screenshotHeight!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll also confirm later whether we need -2 or -1!

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I copied this change from your other branch without looking into what it does -- feel free to edit anything in takeTenClips method as you see fit 👍

args.push('-filter_complex', concat, '-map', '[v2]', '-map', '[a]', saveLocation + '/' + fileHash + '.mp4');
// phfff glad that's over

// now make it all worth it!
Expand Down Expand Up @@ -566,12 +576,11 @@ function extractMetadataForThisONEFile(
const origWidth = metadata.streams[0].width;
const origHeight = metadata.streams[0].height;
const sizeLabel = labelVideo(origWidth, origHeight);
const width = Math.round(100 * origWidth / origHeight) || 169;
const fileSize = metadata.format.size;
imageElement[3] = hashFile(imageElement[1], fileSize);
imageElement[4] = duration; // 4th item is duration
imageElement[5] = sizeLabel; // 5th item is the label, e.g. 'HD'
imageElement[6] = width; // 6th item is width of screenshot in px (e.g. 150);
imageElement[6] = 0; // 6th item WILL BE DEPRECATED AND REMOVED !!!
cal2195 marked this conversation as resolved.
Show resolved Hide resolved
imageElement[7] = fileSize; // 7th item is file size

extractMetaCallback(imageElement);
Expand Down
2 changes: 1 addition & 1 deletion src/app/components/common/final-object.interface.ts
Expand Up @@ -22,7 +22,7 @@ index type description more info
identifier
4 number duration <-- duration of film as number
5 string depicting size <-- e.g. `720`, `1080`, `SD`, `HD`
6 number width of screenshot <-- e.g 124 etc
6 number width of screenshot <-- e.g 124 etc !!! DEPRECATED -- TO BE REMOVED !!!
7 number fileSize in megabytes <-- rounded to nearest MB

*/
8 changes: 4 additions & 4 deletions src/app/components/common/settings-buttons.ts
Expand Up @@ -116,10 +116,10 @@ export let SettingsButtons: { [s: string]: SettingsButton } = {
},
'showClips': {
hidden: false,
toggled: true,
iconName: 'icon-show-thumbnails',
title: 'Show clips',
description: 'Switches to the clips view',
toggled: false,
iconName: 'icon-video-blank',
title: 'BUTTONS.showClipsHint',
description: 'BUTTONS.showClipsDescription',
},
'showTags': {
hidden: false,
Expand Down
35 changes: 8 additions & 27 deletions src/app/components/home/filmstrip/filmstrip.component.html
Expand Up @@ -4,37 +4,18 @@
>

<div
#filmstripHolder
(mousemove)="updateFilmXoffset($event)"
class="filmstrip-container"
[ngStyle]="{ height: imgHeight + 'px'}"
[ngStyle]="{
height: imgHeight + 'px',
'background-image': 'url(' + fullFilePath + ')',
'background-position-x': '-' + filmXoffset + 'px',
'background-size': 'auto ' + imgHeight + 'px'
}"
>

<span *ngIf="showMeta" class="time" >{{ time | lengthPipe }}</span>
<span *ngIf="showMeta" class="rez" >{{ rez }}</span>

<ng-container *ngIf="hoverScrub && hover && noError">
<div
*ngFor="let item of indexArray; let i = index"
(mouseover)="showThisOne(i)"
[ngStyle]="{'left': 10 * i + '%' }"
class="hoverable"
>
</div>
</ng-container>

<ng-container *ngIf="noError">
<img
*ngFor="let item of indexArray; let i = index"
[ngStyle]="{
'right': 'calc(' + (9 - i) + ' * (100% - ' + imgWidth + 'px)/9)',
'z-index': indexArray[i],
'height': imgHeight * 10 + 'px',
'margin-top': -i * imgHeight + 'px'
}"
[src]="sanitizer.bypassSecurityTrustUrl('file://' + folderPath + '/' + imgId)"
class="screencap"
>
</ng-container>

</div>

<span
Expand Down
30 changes: 4 additions & 26 deletions src/app/components/home/filmstrip/filmstrip.component.scss
Expand Up @@ -6,36 +6,14 @@
}

.filmstrip-container {
background: black;
background-color: black;
background-repeat: no-repeat;
cursor: pointer;
height: 100px;
outline: 3px solid black;
overflow-y: hidden;
position: relative;
width: 100%;
outline: 3px solid black;
}

.hoverable {
border: 0;
height: 100%;
position: absolute;
width: 10%;
z-index: 100;
cursor: pointer;
}

.screencap {
box-shadow: 0px 0px 10px 5px rgba(0, 0, 0, 0.4);
box-sizing: border-box;
height: 100px;
outline: 3px solid black;
position: absolute;
}

img:before {
content: 'lol u mad?'; /* to prevent broken img icon from showing up */
display: block;
font-size: 20px;
margin-top: 1000px;
}

.title {
Expand Down
45 changes: 15 additions & 30 deletions src/app/components/home/filmstrip/filmstrip.component.ts
@@ -1,4 +1,4 @@
import { Component, Input, OnInit, HostListener } from '@angular/core';
import { Component, Input, OnInit, ViewChild, ElementRef } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
import { galleryItemAppear, metaAppear, textAppear } from '../../common/animations';

Expand All @@ -12,6 +12,8 @@ import { galleryItemAppear, metaAppear, textAppear } from '../../common/animatio
})
export class FilmstripComponent implements OnInit {

@ViewChild('filmstripHolder') filmstripHolder: ElementRef;

@Input() darkMode: boolean;
@Input() elHeight: number;
@Input() fileSize: number;
Expand All @@ -20,49 +22,32 @@ export class FilmstripComponent implements OnInit {
@Input() hubName: string;
@Input() imgHeight: number;
@Input() imgId: any;
@Input() imgWidth: number;
@Input() largerFont: boolean;
@Input() rez: string;
@Input() showMeta: boolean;
@Input() time: string;
@Input() title: string;

indexArray: Array<number> = []; // to set z-index on css

hover = false;
noError = true;
fullFilePath: string = '';
filmXoffset: number = 0;

constructor(
public sanitizer: DomSanitizer
) { }

@HostListener('mouseenter') onMouseEnter() {
this.hover = true;
}
@HostListener('mouseleave') onMouseLeave() {
this.hover = false;
}

ngOnInit() {
// this.imgId is `undefined` when no screenshot taken -- because of ffmpeg extraction error
if (this.imgId === undefined) {
this.noError = false;
}
this.fullFilePath = 'file://' + this.folderPath + '/' + 'vha-' + this.hubName + '/' + this.imgId + '.jpg';
}

// hack -- populate hardcoded values -- fix later
const fileHash = this.imgId;
this.imgId = 'vha-' + this.hubName + '/' + fileHash + '.jpg';
updateFilmXoffset($event) {
if (this.hoverScrub) {
const imgWidth = this.imgHeight * 1.78; // 1.78 is the hardcoded aspect ratio
const containerWidth = this.filmstripHolder.nativeElement.getBoundingClientRect().width;
const howManyScreensOutsideCutoff = 11 - Math.floor(containerWidth / imgWidth);
// 11 is just 1 more than number of screenshots (currently hardcoded as 10)

for (let i = 0; i < 10; i++) {
this.indexArray[i] = 10 - i;
const cursorX = $event.layerX; // cursor's X position inside the filmstrip element
this.filmXoffset = imgWidth * Math.floor(cursorX / (containerWidth / howManyScreensOutsideCutoff));
}
}

showThisOne(screen: number): void {
this.indexArray.forEach((element, index) => {
const distance = Math.abs(index - screen);
this.indexArray[index] = 10 - distance;
});
}

}
2 changes: 0 additions & 2 deletions src/app/components/home/home.component.html
Expand Up @@ -141,7 +141,6 @@
[hubName]="appState.hubName"
[imgHeight]="100"
[imgId]="itemToRename[3]"
[imgWidth]="(imgHeight / 100) * itemToRename[6] || (imgHeight / 100) * 171"
[largerFont]="true"
[rez]="itemToRename[5]"
[showMeta]="true"
Expand Down Expand Up @@ -700,7 +699,6 @@ <h2>{{ 'SETTINGS.changeLanguage' | translate }}</h2>
[rez]="item[5]"
[fileSize]="item[7]"
[elHeight]="imgHeight + textPaddingHeight"
[imgWidth]="(imgHeight / 100) * item[6] || (imgHeight / 100) * 171"
[imgHeight]="imgHeight"
[hubName]="appState.hubName"
[folderPath]="appState.selectedOutputFolder"
Expand Down
2 changes: 1 addition & 1 deletion src/app/components/home/home.component.ts
Expand Up @@ -934,7 +934,7 @@ export class HomeComponent implements OnInit, AfterViewInit {
* Computes the preview width for thumbnails view
*/
public computePreviewWidth(): void {
this.previewWidth = Math.ceil((this.imgHeight / 100) * 174);
this.previewWidth = Math.ceil((this.imgHeight / 100) * 178);
}

/**
Expand Down
6 changes: 3 additions & 3 deletions src/app/components/home/tags/tags.component.ts
@@ -1,4 +1,4 @@
import { Component, EventEmitter, Input, Output, OnDestroy, ViewChild, ElementRef } from '@angular/core';
import { Component, EventEmitter, Input, Output, OnDestroy, ViewChild, ElementRef, OnInit } from '@angular/core';

import { TagsService, WordAndFreq } from './tags.service';
import { TagsSaveService } from './tags-save.service';
Expand All @@ -16,7 +16,7 @@ import { slowFadeIn, tagDeleteButton, donutAppear } from '../../../components/co
'tags.component.scss'],
animations: [slowFadeIn, tagDeleteButton, donutAppear]
})
export class TagsComponent implements OnDestroy {
export class TagsComponent implements OnInit, OnDestroy {

@Input() finalArray: ImageElement[];
@Input() hubName: string; // if hubName changes, tagsService will recalculate, otherwise it will show cached
Expand Down Expand Up @@ -117,4 +117,4 @@ export class TagsComponent implements OnDestroy {
this.showEdit = false;
}

}
}