20 video conversion for web view (#200)

* Added job for video conversion every 1 minute

* Handle get video as mp4 on the web

* Auto play video on web on hovered

* Added video player

* Added animation and video duration to thumbnail player

* Fixed issue with video not playing on hover

* Added animation when loading thumbnail
This commit is contained in:
Alex
2022-06-04 18:34:11 -05:00
committed by GitHub
parent 53c3c916a6
commit ab6909bfbd
17 changed files with 371 additions and 50 deletions
@@ -1,12 +1,30 @@
import { BullModule } from '@nestjs/bull';
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AssetModule } from '../../api-v1/asset/asset.module';
import { AssetEntity } from '../../api-v1/asset/entities/asset.entity';
import { ImageConversionService } from './image-conversion.service';
import { VideoConversionProcessor } from './video-conversion.processor';
import { VideoConversionService } from './video-conversion.service';
@Module({
imports: [
TypeOrmModule.forFeature([AssetEntity]),
BullModule.registerQueue({
settings: {},
name: 'video-conversion',
limiter: {
max: 1,
duration: 60000
},
defaultJobOptions: {
attempts: 3,
removeOnComplete: true,
removeOnFail: false,
},
}),
],
providers: [ImageConversionService],
providers: [ImageConversionService, VideoConversionService, VideoConversionProcessor,],
})
export class ScheduleTasksModule { }
@@ -0,0 +1,56 @@
import { Process, Processor } from '@nestjs/bull';
import { InjectRepository } from '@nestjs/typeorm';
import { Job } from 'bull';
import { Repository } from 'typeorm';
import { AssetEntity } from '../../api-v1/asset/entities/asset.entity';
import { existsSync, mkdirSync } from 'fs';
import { APP_UPLOAD_LOCATION } from '../../constants/upload_location.constant';
import ffmpeg from 'fluent-ffmpeg';
import { Logger } from '@nestjs/common';
@Processor('video-conversion')
export class VideoConversionProcessor {
constructor(
@InjectRepository(AssetEntity)
private assetRepository: Repository<AssetEntity>,
) { }
@Process('to-mp4')
async convertToMp4(job: Job) {
const { asset }: { asset: AssetEntity } = job.data;
const basePath = APP_UPLOAD_LOCATION;
const encodedVideoPath = `${basePath}/${asset.userId}/encoded-video`;
if (!existsSync(encodedVideoPath)) {
mkdirSync(encodedVideoPath, { recursive: true });
}
const latestAssetInfo = await this.assetRepository.findOne({ id: asset.id });
const savedEncodedPath = encodedVideoPath + "/" + latestAssetInfo.id + '.mp4'
if (latestAssetInfo.encodedVideoPath == '') {
ffmpeg(latestAssetInfo.originalPath)
.outputOptions([
'-crf 23',
'-preset ultrafast',
'-vcodec libx264',
'-acodec mp3',
'-vf scale=1280:-2'
])
.output(savedEncodedPath)
.on('start', () => Logger.log("Start Converting", 'VideoConversionMOV2MP4'))
.on('error', (a, b, c) => {
Logger.error('Cannot Convert Video', 'VideoConversionMOV2MP4')
console.log(a, b, c)
})
.on('end', async () => {
Logger.log(`Converting Success ${latestAssetInfo.id}`, 'VideoConversionMOV2MP4')
await this.assetRepository.update({ id: latestAssetInfo.id }, { encodedVideoPath: savedEncodedPath });
}).run();
}
return {}
}
}
@@ -0,0 +1,50 @@
import { Injectable, Logger } from '@nestjs/common';
import { Cron, CronExpression } from '@nestjs/schedule';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { AssetEntity } from '../../api-v1/asset/entities/asset.entity';
import sharp from 'sharp';
import ffmpeg from 'fluent-ffmpeg';
import { APP_UPLOAD_LOCATION } from '../../constants/upload_location.constant';
import { existsSync, mkdirSync } from 'fs';
import { InjectQueue } from '@nestjs/bull/dist/decorators';
import { Queue } from 'bull';
import { randomUUID } from 'crypto';
@Injectable()
export class VideoConversionService {
constructor(
@InjectRepository(AssetEntity)
private assetRepository: Repository<AssetEntity>,
@InjectQueue('video-conversion')
private videoEncodingQueue: Queue
) { }
// time ffmpeg -i 15065f4a-47ff-4aed-8c3e-c9fcf1840531.mov -crf 35 -preset ultrafast -vcodec libx264 -acodec mp3 -vf "scale=1280:-1" 15065f4a-47ff-4aed-8c3e-c9fcf1840531.mp4
@Cron(CronExpression.EVERY_MINUTE
, {
name: 'video-encoding'
})
async mp4Conversion() {
const assets = await this.assetRepository.find({
where: {
type: 'VIDEO',
mimeType: 'video/quicktime',
encodedVideoPath: ''
},
order: {
createdAt: 'DESC'
},
take: 1
});
if (assets.length > 0) {
const asset = assets[0];
await this.videoEncodingQueue.add('to-mp4', { asset }, { jobId: asset.id },)
}
}
}