Skip to content

Commit

Permalink
feat: generate screenshots
Browse files Browse the repository at this point in the history
  • Loading branch information
Leonardo committed Mar 20, 2023
1 parent d56050c commit cfee81c
Show file tree
Hide file tree
Showing 9 changed files with 181 additions and 76 deletions.
37 changes: 18 additions & 19 deletions angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,18 @@
"outputPath": "dist/clips",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": [
"zone.js"
],
"polyfills": ["zone.js"],
"tsConfig": "tsconfig.app.json",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.css"
"src/assets",
{
"input": "node_modules/@ffmpeg/core/dist",
"output": "node_modules/@ffmpeg/core/dist",
"glob": "*"
}
],
"styles": ["src/styles.css"],
"scripts": []
},
"configurations": {
Expand Down Expand Up @@ -66,7 +67,13 @@
"browserTarget": "clips:build:development"
}
},
"defaultConfiguration": "development"
"defaultConfiguration": "development",
"options": {
"headers": {
"Cross-Origin-Opener-Policy": "same-origin",
"Cross-Origin-Embedder-Policy": "require-corp"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
Expand All @@ -77,18 +84,10 @@
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"polyfills": [
"zone.js",
"zone.js/testing"
],
"polyfills": ["zone.js", "zone.js/testing"],
"tsConfig": "tsconfig.spec.json",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.css"
],
"assets": ["src/favicon.ico", "src/assets"],
"styles": ["src/styles.css"],
"scripts": []
}
}
Expand Down
35 changes: 30 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
"@angular/platform-browser": "^15.0.0",
"@angular/platform-browser-dynamic": "^15.0.0",
"@angular/router": "^15.0.0",
"@ffmpeg/core": "^0.11.0",
"@ffmpeg/ffmpeg": "^0.11.6",
"@types/node": "^18.15.3",
"@types/uuid": "^9.0.1",
"firebase": "^9.17.2",
"ngx-mask": "^14.2.4",
Expand Down
62 changes: 62 additions & 0 deletions src/app/services/ffmpeg.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { Injectable } from '@angular/core';
import { createFFmpeg, fetchFile } from '@ffmpeg/ffmpeg';

@Injectable({
providedIn: 'root',
})
export class FfmpegService {
public isReady: boolean = false;
private ffmpeg;
constructor() {
this.ffmpeg = createFFmpeg({ log: true });
}

async init(): Promise<void> {
if (this.isReady) {
return;
}
await this.ffmpeg.load();
this.isReady = true;
}

async getScreenshots(file: File): Promise<string[]> {
const data = await fetchFile(file);
this.ffmpeg.FS('writeFile', file.name, data);
const seconds = [1, 2, 3];
const commands: string[] = [];
seconds.forEach((second) => {
commands.push(
'-i',
file.name,
'-ss',
`00:00:0${second}`,
'-frames:v',
'1',
'-filter:v',
'scale=510:-1',
`output_0${second}.png`
);
});

await this.ffmpeg.run(...commands);

const screenshots: string[] = [];

seconds.forEach((second) => {
const screenshotFile = this.ffmpeg.FS(
'readFile',
`output_0${second}.png`
);
const screenshotBlob = new Blob([screenshotFile.buffer], {
type: 'image/png',
});

const screenshotUrl = URL.createObjectURL(screenshotBlob);
console.log(screenshotUrl);
screenshots.push(screenshotUrl);
});
return screenshots;
}

// makerScreenshotCommander(second) {}
}
13 changes: 13 additions & 0 deletions src/app/video/pipes/safe-url.pipe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Pipe, PipeTransform } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';

@Pipe({
name: 'safeURL',
})
export class SafeURLPipe implements PipeTransform {
constructor(private sanitizer: DomSanitizer) {}

transform(value: string) {
return this.sanitizer.bypassSecurityTrustUrl(value);
}
}
87 changes: 44 additions & 43 deletions src/app/video/upload/upload.component.html
Original file line number Diff line number Diff line change
@@ -1,55 +1,56 @@
<section class="container mx-auto my-8 bg-secondary p-6">
<div class="rounded relative flex flex-col">
<div class="font-bold mb-6">Upload Video</div>

<!-- Upload Dropbox -->
<ng-container *ngIf="!nextStep; else uploadFormContainer">
<div (dragend)="isDragOver = false" (dragover)="isDragOver = true" (dragenter)="isDragOver = true"
(dragleave)="isDragOver = false" (mouseleave)="isDragOver = false" (drop)="storeFile($event)"
[ngClass]="{'bg-indigo-400 border-indigo-400 border-solid': isDragOver}" app-event-blocker class="w-full px-10 py-40 rounded text-center cursor-pointer border border-dashed
<ng-container *ngIf="ffmpegService.isReady; else loading">
<!-- Upload Dropbox -->
<ng-container *ngIf="!nextStep; else uploadFormContainer">
<div (dragend)="isDragOver = false" (dragover)="isDragOver = true" (dragenter)="isDragOver = true"
(dragleave)="isDragOver = false" (mouseleave)="isDragOver = false" (drop)="storeFile($event)"
[ngClass]="{'bg-indigo-400 border-indigo-400 border-solid': isDragOver}" app-event-blocker class="w-full px-10 py-40 rounded text-center cursor-pointer border border-dashed
border-gray-400 transition duration-500 hover:text-white
hover:bg-indigo-400 hover:border-indigo-400 hover:border-solid text-xl">
<h5>Drop your file here (mp4 only!)</h5>
</div>
<input type="file" class='mt-4' (change)="storeFile($event)">
</ng-container>
<h5>Drop your file here (mp4 only!)</h5>
</div>
<input type="file" class='mt-4' (change)="storeFile($event)">
</ng-container>

<!-- Video Editor -->
<ng-template #uploadFormContainer>
<app-alert *ngIf="showAlert" [color]="alertColor">
<p>{{alertMessage}}</p>
<p *ngIf="showPercentage">{{ percentage | percent}}</p>
</app-alert>
<!-- Form -->
<form [hidden]="!nextStep" [formGroup]="uploadFrom" (ngSubmit)="uploadFile()">
<!-- Screenshots -->
<h2 class="mb-4 text-xl">Select a Thumbnail</h2>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-4">
<div class="border-8 cursor-pointer border-green-400">
<img src="assets/img/1.jpg">
<!-- Video Editor -->
<ng-template #uploadFormContainer>
<app-alert *ngIf="showAlert" [color]="alertColor">
<p>{{alertMessage}}</p>
<p *ngIf="showPercentage">{{ percentage | percent}}</p>
</app-alert>
<!-- Form -->
<form [hidden]="!nextStep" [formGroup]="uploadFrom" (ngSubmit)="uploadFile()">
<!-- Screenshots -->
<h2 class="mb-4 text-xl">Select a Thumbnail</h2>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-4">
<div *ngFor="let screenshot of screenshots" class="border-8 cursor-pointer border-green-400">
<img [src]="screenshot | safeURL">
</div>
</div>
<div class="border-8 cursor-pointer border-transparent">
<img src="assets/img/2.jpg">
</div>
<div class="border-8 cursor-pointer border-transparent">
<img src="assets/img/3.jpg">

<!-- Title -->
<div class="mt-4">
<label class="block text-xl mb-4">Title</label>
<app-input [control]="title" placeholder="Enter title"></app-input>
</div>
</div>

<!-- Title -->
<div class="mt-4">
<label class="block text-xl mb-4">Title</label>
<app-input [control]="title" placeholder="Enter title"></app-input>
</div>
<div class="mt-4 text-right">
<button type="submit" [disabled]="inSubmission"
[ngClass]="{'opacity-50': inSubmission, 'hover:bg-indigo-700': !inSubmission}"
class="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm rounded-md text-white bg-indigo-600 focus:outline-none">
Publish
</button>
</div>
</form>
</ng-template>
</ng-container>

<div class="mt-4 text-right">
<button type="submit" [disabled]="inSubmission"
[ngClass]="{'opacity-50': inSubmission, 'hover:bg-indigo-700': !inSubmission}"
class="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm rounded-md text-white bg-indigo-600 focus:outline-none">
Publish
</button>
</div>
</form>
<ng-template #loading>
<span class="material-icon text-center text-6xl p-8 animate-spin">
|
</span>
</ng-template>
</div>
</section>
</section>
7 changes: 6 additions & 1 deletion src/app/video/upload/upload.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import firebase from 'firebase/compat/app';
import { ClipService } from 'src/app/services/clip.service';
import IClip from 'src/app/models/clips.model';
import { Router } from '@angular/router';
import { FfmpegService } from 'src/app/services/ffmpeg.service';

@Component({
selector: 'app-upload',
Expand All @@ -28,6 +29,7 @@ export class UploadComponent implements OnDestroy {
percentage: number = 0;
user: firebase.User | null = null;
task?: AngularFireUploadTask;
screenshots: string[] = [];

alertColor: string = 'blue';
alertMessage: string = 'Please wait! your clip is being uploaded';
Expand All @@ -40,22 +42,25 @@ export class UploadComponent implements OnDestroy {
constructor(
private storage: AngularFireStorage,
private clipService: ClipService,
public ffmpegService: FfmpegService,
private auth: AngularFireAuth,
private router: Router
) {
this.auth.user.subscribe((user) => (this.user = user));
this.ffmpegService.init();
}

ngOnDestroy(): void {
this.task?.cancel();
}

storeFile(event: Event): void {
async storeFile(event: Event): Promise<void> {
this.isDragOver = false;
this.file = (event as DragEvent).dataTransfer
? (event as DragEvent).dataTransfer?.files.item(0) ?? null
: (event.target as HTMLInputElement).files?.item(0) ?? null;
if (!this.file || this.file.type !== 'video/mp4') return;
this.screenshots = await this.ffmpegService.getScreenshots(this.file);
this.title.setValue(this.file.name.replace(/\.[^/.]+$/, ''));
this.nextStep = true;
}
Expand Down
3 changes: 2 additions & 1 deletion src/app/video/video.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ import { UploadComponent } from './upload/upload.component';
import { SharedModule } from '../shared/shared.module';
import { ReactiveFormsModule } from '@angular/forms';
import { EditComponent } from './edit/edit.component';
import { SafeURLPipe } from './pipes/safe-url.pipe';

@NgModule({
declarations: [ManageComponent, UploadComponent, EditComponent],
declarations: [ManageComponent, UploadComponent, EditComponent, SafeURLPipe],
imports: [
CommonModule,
VideoRoutingModule,
Expand Down
10 changes: 3 additions & 7 deletions tsconfig.app.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,8 @@
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/app",
"types": []
"types": ["node"]
},
"files": [
"src/main.ts"
],
"include": [
"src/**/*.d.ts"
]
"files": ["src/main.ts"],
"include": ["src/**/*.d.ts"]
}

0 comments on commit cfee81c

Please sign in to comment.