|
@@ -1,7 +1,8 @@ |
|
|
import { |
|
|
import { |
|
|
createVideoClipper, |
|
|
createVideoClipper, |
|
|
|
|
|
YouTube, |
|
|
VideoType, |
|
|
VideoType, |
|
|
CreateVideoClipperParams, |
|
|
|
|
|
|
|
|
ClipVideoParams, |
|
|
} from '@modal/webvideo-clip-core'; |
|
|
} from '@modal/webvideo-clip-core'; |
|
|
import { constants } from 'http2'; |
|
|
import { constants } from 'http2'; |
|
|
import { RouteHandlerMethod } from 'fastify'; |
|
|
import { RouteHandlerMethod } from 'fastify'; |
|
@@ -23,18 +24,20 @@ const validateRequestBody = (body: ClipArgs) => { |
|
|
|
|
|
|
|
|
const typeofStart = typeof start; |
|
|
const typeofStart = typeof start; |
|
|
if (typeofStart !== 'undefined') { |
|
|
if (typeofStart !== 'undefined') { |
|
|
if (!['string', 'number'].includes(typeofStart)) { |
|
|
|
|
|
messages.push('Invalid end value.'); |
|
|
|
|
|
} else if (typeofStart === 'string' && !DURATION_STRING_REGEXP.test(start as string)) { |
|
|
|
|
|
|
|
|
if ( |
|
|
|
|
|
!['string', 'number'].includes(typeofStart) |
|
|
|
|
|
|| (typeofStart === 'string' && !DURATION_STRING_REGEXP.test(start as string)) |
|
|
|
|
|
) { |
|
|
messages.push('Invalid start value.'); |
|
|
messages.push('Invalid start value.'); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const typeofEnd = typeof end; |
|
|
const typeofEnd = typeof end; |
|
|
if (typeofEnd !== 'undefined') { |
|
|
if (typeofEnd !== 'undefined') { |
|
|
if (!['string', 'number'].includes(typeofEnd)) { |
|
|
|
|
|
messages.push('Invalid end value.'); |
|
|
|
|
|
} else if (typeofEnd === 'string' && !DURATION_STRING_REGEXP.test(end as string)) { |
|
|
|
|
|
|
|
|
if ( |
|
|
|
|
|
!['string', 'number'].includes(typeofEnd) |
|
|
|
|
|
|| (typeofEnd === 'string' && !DURATION_STRING_REGEXP.test(end as string)) |
|
|
|
|
|
) { |
|
|
messages.push('Invalid end value.'); |
|
|
messages.push('Invalid end value.'); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
@@ -44,7 +47,7 @@ const validateRequestBody = (body: ClipArgs) => { |
|
|
|
|
|
|
|
|
const getVideoType = (url: string) => { |
|
|
const getVideoType = (url: string) => { |
|
|
if (url.startsWith('https://www.youtube.com')) { |
|
|
if (url.startsWith('https://www.youtube.com')) { |
|
|
return VideoType.YOUTUBE; |
|
|
|
|
|
|
|
|
return YouTube.VIDEO_TYPE as VideoType; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
return null; |
|
|
return null; |
|
@@ -65,50 +68,23 @@ export const clip: RouteHandlerMethod = async (request, reply) => { |
|
|
reply |
|
|
reply |
|
|
.status(constants.HTTP_STATUS_UNPROCESSABLE_ENTITY) |
|
|
.status(constants.HTTP_STATUS_UNPROCESSABLE_ENTITY) |
|
|
.send({ |
|
|
.send({ |
|
|
message: 'Unsupported URL.', |
|
|
|
|
|
|
|
|
errors: ['Unsupported URL.'], |
|
|
}); |
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const { url, start, end } = request.body as ClipArgs; |
|
|
|
|
|
const videoClipperArgs = { |
|
|
|
|
|
type: videoType, |
|
|
|
|
|
|
|
|
const clipper = createVideoClipper({ |
|
|
|
|
|
type: videoType as VideoType, |
|
|
|
|
|
downloaderExecutablePath: process.env.YOUTUBE_DOWNLOADER_EXECUTABLE_PATH as string, |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
const { url, start, end } = request.body as ClipVideoParams; |
|
|
|
|
|
const clipResult = await clipper({ |
|
|
url, |
|
|
url, |
|
|
start, |
|
|
start, |
|
|
end, |
|
|
end, |
|
|
downloaderExecutablePath: process.env.YOUTUBE_DOWNLOADER_EXECUTABLE_PATH, |
|
|
|
|
|
} as CreateVideoClipperParams; |
|
|
|
|
|
const clipper = createVideoClipper(videoClipperArgs); |
|
|
|
|
|
clipper.on('process', (arg: Record<string, unknown>) => { |
|
|
|
|
|
request.server.log.info(`${arg.type as string}:${arg.phase as string}`); |
|
|
|
|
|
if (typeof arg.command === 'string') { |
|
|
|
|
|
request.server.log.debug(`> ${arg.command}`); |
|
|
|
|
|
} |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
let clipResult: Record<string, unknown>; |
|
|
|
|
|
clipper.on('success', (result: Record<string, unknown>) => { |
|
|
|
|
|
clipResult = result; |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
let theError: Error; |
|
|
|
|
|
clipper.on('error', (error: Error) => { |
|
|
|
|
|
theError = error; |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
clipper.on('end', () => { |
|
|
|
|
|
if (theError) { |
|
|
|
|
|
reply |
|
|
|
|
|
.status(constants.HTTP_STATUS_INTERNAL_SERVER_ERROR) |
|
|
|
|
|
.send({ |
|
|
|
|
|
message: theError.message, |
|
|
|
|
|
}); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
reply |
|
|
|
|
|
.header('Content-Type', clipResult.type as string) |
|
|
|
|
|
.send(clipResult.output as Buffer); |
|
|
|
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
clipper.process(); |
|
|
|
|
|
|
|
|
reply |
|
|
|
|
|
.header('Content-Type', clipResult.contentType) |
|
|
|
|
|
.send(clipResult.content); |
|
|
}; |
|
|
}; |