Improve create import file job
Fix federation of .ogv videos
This commit is contained in:
parent
0138af9237
commit
28be89161a
7 changed files with 48 additions and 34 deletions
|
@ -27,6 +27,7 @@ async function run () {
|
||||||
|
|
||||||
const video = await VideoModel.loadByUUID(program['video'])
|
const video = await VideoModel.loadByUUID(program['video'])
|
||||||
if (!video) throw new Error('Video not found.')
|
if (!video) throw new Error('Video not found.')
|
||||||
|
if (video.isOwned() === false) throw new Error('Cannot import files of a non owned video.')
|
||||||
|
|
||||||
const dataInput = {
|
const dataInput = {
|
||||||
videoUUID: video.uuid,
|
videoUUID: video.uuid,
|
||||||
|
|
|
@ -7,8 +7,8 @@ import { UserRight, VideoRateType } from '../../../shared'
|
||||||
import {
|
import {
|
||||||
CONSTRAINTS_FIELDS,
|
CONSTRAINTS_FIELDS,
|
||||||
VIDEO_CATEGORIES,
|
VIDEO_CATEGORIES,
|
||||||
VIDEO_LANGUAGES,
|
VIDEO_LICENCES,
|
||||||
VIDEO_LICENCES, VIDEO_MIMETYPE_EXT,
|
VIDEO_MIMETYPE_EXT,
|
||||||
VIDEO_PRIVACIES,
|
VIDEO_PRIVACIES,
|
||||||
VIDEO_RATE_TYPES
|
VIDEO_RATE_TYPES
|
||||||
} from '../../initializers'
|
} from '../../initializers'
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { VideoPrivacy } from '../../shared/models/videos'
|
||||||
// Do not use barrels, remain constants as independent as possible
|
// Do not use barrels, remain constants as independent as possible
|
||||||
import { buildPath, isTestInstance, root, sanitizeHost, sanitizeUrl } from '../helpers/core-utils'
|
import { buildPath, isTestInstance, root, sanitizeHost, sanitizeUrl } from '../helpers/core-utils'
|
||||||
import { NSFWPolicyType } from '../../shared/models/videos/nsfw-policy.type'
|
import { NSFWPolicyType } from '../../shared/models/videos/nsfw-policy.type'
|
||||||
|
import { invert } from 'lodash'
|
||||||
|
|
||||||
// Use a variable to reload the configuration if we need
|
// Use a variable to reload the configuration if we need
|
||||||
let config: IConfig = require('config')
|
let config: IConfig = require('config')
|
||||||
|
@ -330,6 +331,7 @@ const VIDEO_MIMETYPE_EXT = {
|
||||||
'video/ogg': '.ogv',
|
'video/ogg': '.ogv',
|
||||||
'video/mp4': '.mp4'
|
'video/mp4': '.mp4'
|
||||||
}
|
}
|
||||||
|
const VIDEO_EXT_MIMETYPE = invert(VIDEO_MIMETYPE_EXT)
|
||||||
|
|
||||||
const IMAGE_MIMETYPE_EXT = {
|
const IMAGE_MIMETYPE_EXT = {
|
||||||
'image/png': '.png',
|
'image/png': '.png',
|
||||||
|
@ -501,6 +503,7 @@ export {
|
||||||
SCHEDULER_INTERVAL,
|
SCHEDULER_INTERVAL,
|
||||||
STATIC_DOWNLOAD_PATHS,
|
STATIC_DOWNLOAD_PATHS,
|
||||||
RATES_LIMIT,
|
RATES_LIMIT,
|
||||||
|
VIDEO_EXT_MIMETYPE,
|
||||||
JOB_COMPLETED_LIFETIME,
|
JOB_COMPLETED_LIFETIME,
|
||||||
VIDEO_VIEW_LIFETIME,
|
VIDEO_VIEW_LIFETIME,
|
||||||
buildLanguages
|
buildLanguages
|
||||||
|
|
|
@ -16,14 +16,14 @@ export type VideoFilePayload = {
|
||||||
isPortraitMode?: boolean
|
isPortraitMode?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type VideoImportPayload = {
|
export type VideoFileImportPayload = {
|
||||||
videoUUID: string,
|
videoUUID: string,
|
||||||
filePath: string
|
filePath: string
|
||||||
}
|
}
|
||||||
|
|
||||||
async function processVideoImport (job: kue.Job) {
|
async function processVideoFileImport (job: kue.Job) {
|
||||||
const payload = job.data as VideoImportPayload
|
const payload = job.data as VideoFileImportPayload
|
||||||
logger.info('Processing video import in job %d.', job.id)
|
logger.info('Processing video file import in job %d.', job.id)
|
||||||
|
|
||||||
const video = await VideoModel.loadByUUIDAndPopulateAccountAndServerAndTags(payload.videoUUID)
|
const video = await VideoModel.loadByUUIDAndPopulateAccountAndServerAndTags(payload.videoUUID)
|
||||||
// No video, maybe deleted?
|
// No video, maybe deleted?
|
||||||
|
@ -132,5 +132,5 @@ async function onVideoFileOptimizerSuccess (video: VideoModel, isNewVideo: boole
|
||||||
|
|
||||||
export {
|
export {
|
||||||
processVideoFile,
|
processVideoFile,
|
||||||
processVideoImport
|
processVideoFileImport
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { ActivitypubHttpBroadcastPayload, processActivityPubHttpBroadcast } from
|
||||||
import { ActivitypubHttpFetcherPayload, processActivityPubHttpFetcher } from './handlers/activitypub-http-fetcher'
|
import { ActivitypubHttpFetcherPayload, processActivityPubHttpFetcher } from './handlers/activitypub-http-fetcher'
|
||||||
import { ActivitypubHttpUnicastPayload, processActivityPubHttpUnicast } from './handlers/activitypub-http-unicast'
|
import { ActivitypubHttpUnicastPayload, processActivityPubHttpUnicast } from './handlers/activitypub-http-unicast'
|
||||||
import { EmailPayload, processEmail } from './handlers/email'
|
import { EmailPayload, processEmail } from './handlers/email'
|
||||||
import { processVideoFile, processVideoImport, VideoFilePayload, VideoImportPayload } from './handlers/video-file'
|
import { processVideoFile, processVideoFileImport, VideoFilePayload, VideoFileImportPayload } from './handlers/video-file'
|
||||||
import { ActivitypubFollowPayload, processActivityPubFollow } from './handlers/activitypub-follow'
|
import { ActivitypubFollowPayload, processActivityPubFollow } from './handlers/activitypub-follow'
|
||||||
|
|
||||||
type CreateJobArgument =
|
type CreateJobArgument =
|
||||||
|
@ -15,7 +15,7 @@ type CreateJobArgument =
|
||||||
{ type: 'activitypub-http-unicast', payload: ActivitypubHttpUnicastPayload } |
|
{ type: 'activitypub-http-unicast', payload: ActivitypubHttpUnicastPayload } |
|
||||||
{ type: 'activitypub-http-fetcher', payload: ActivitypubHttpFetcherPayload } |
|
{ type: 'activitypub-http-fetcher', payload: ActivitypubHttpFetcherPayload } |
|
||||||
{ type: 'activitypub-follow', payload: ActivitypubFollowPayload } |
|
{ type: 'activitypub-follow', payload: ActivitypubFollowPayload } |
|
||||||
{ type: 'video-file-import', payload: VideoImportPayload } |
|
{ type: 'video-file-import', payload: VideoFileImportPayload } |
|
||||||
{ type: 'video-file', payload: VideoFilePayload } |
|
{ type: 'video-file', payload: VideoFilePayload } |
|
||||||
{ type: 'email', payload: EmailPayload }
|
{ type: 'email', payload: EmailPayload }
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ const handlers: { [ id in JobType ]: (job: kue.Job) => Promise<any>} = {
|
||||||
'activitypub-http-unicast': processActivityPubHttpUnicast,
|
'activitypub-http-unicast': processActivityPubHttpUnicast,
|
||||||
'activitypub-http-fetcher': processActivityPubHttpFetcher,
|
'activitypub-http-fetcher': processActivityPubHttpFetcher,
|
||||||
'activitypub-follow': processActivityPubFollow,
|
'activitypub-follow': processActivityPubFollow,
|
||||||
'video-file-import': processVideoImport,
|
'video-file-import': processVideoFileImport,
|
||||||
'video-file': processVideoFile,
|
'video-file': processVideoFile,
|
||||||
'email': processEmail
|
'email': processEmail
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import * as Bluebird from 'bluebird'
|
||||||
import { map, maxBy } from 'lodash'
|
import { map, maxBy } from 'lodash'
|
||||||
import * as magnetUtil from 'magnet-uri'
|
import * as magnetUtil from 'magnet-uri'
|
||||||
import * as parseTorrent from 'parse-torrent'
|
import * as parseTorrent from 'parse-torrent'
|
||||||
import { join, extname } from 'path'
|
import { extname, join } from 'path'
|
||||||
import * as Sequelize from 'sequelize'
|
import * as Sequelize from 'sequelize'
|
||||||
import {
|
import {
|
||||||
AllowNull,
|
AllowNull,
|
||||||
|
@ -30,9 +30,9 @@ import { VideoTorrentObject } from '../../../shared/models/activitypub/objects'
|
||||||
import { Video, VideoDetails, VideoFile } from '../../../shared/models/videos'
|
import { Video, VideoDetails, VideoFile } from '../../../shared/models/videos'
|
||||||
import { VideoFilter } from '../../../shared/models/videos/video-query.type'
|
import { VideoFilter } from '../../../shared/models/videos/video-query.type'
|
||||||
import {
|
import {
|
||||||
|
copyFilePromise,
|
||||||
createTorrentPromise,
|
createTorrentPromise,
|
||||||
peertubeTruncate,
|
peertubeTruncate,
|
||||||
copyFilePromise,
|
|
||||||
renamePromise,
|
renamePromise,
|
||||||
statPromise,
|
statPromise,
|
||||||
unlinkPromise,
|
unlinkPromise,
|
||||||
|
@ -63,6 +63,7 @@ import {
|
||||||
STATIC_PATHS,
|
STATIC_PATHS,
|
||||||
THUMBNAILS_SIZE,
|
THUMBNAILS_SIZE,
|
||||||
VIDEO_CATEGORIES,
|
VIDEO_CATEGORIES,
|
||||||
|
VIDEO_EXT_MIMETYPE,
|
||||||
VIDEO_LANGUAGES,
|
VIDEO_LANGUAGES,
|
||||||
VIDEO_LICENCES,
|
VIDEO_LICENCES,
|
||||||
VIDEO_PRIVACIES
|
VIDEO_PRIVACIES
|
||||||
|
@ -1166,7 +1167,7 @@ export class VideoModel extends Model<VideoModel> {
|
||||||
for (const file of this.VideoFiles) {
|
for (const file of this.VideoFiles) {
|
||||||
url.push({
|
url.push({
|
||||||
type: 'Link',
|
type: 'Link',
|
||||||
mimeType: 'video/' + file.extname.replace('.', ''),
|
mimeType: VIDEO_EXT_MIMETYPE[file.extname],
|
||||||
href: this.getVideoFileUrl(file, baseUrlHttp),
|
href: this.getVideoFileUrl(file, baseUrlHttp),
|
||||||
width: file.resolution,
|
width: file.resolution,
|
||||||
size: file.size
|
size: file.size
|
||||||
|
@ -1328,14 +1329,18 @@ export class VideoModel extends Model<VideoModel> {
|
||||||
await copyFilePromise(inputFilePath, outputPath)
|
await copyFilePromise(inputFilePath, outputPath)
|
||||||
|
|
||||||
const currentVideoFile = this.VideoFiles.find(videoFile => videoFile.resolution === updatedVideoFile.resolution)
|
const currentVideoFile = this.VideoFiles.find(videoFile => videoFile.resolution === updatedVideoFile.resolution)
|
||||||
const isNewVideoFile = !currentVideoFile
|
|
||||||
|
|
||||||
if (!isNewVideoFile) {
|
if (currentVideoFile) {
|
||||||
if (currentVideoFile.extname !== updatedVideoFile.extname) {
|
// Remove old file and old torrent
|
||||||
await this.removeFile(currentVideoFile)
|
await this.removeFile(currentVideoFile)
|
||||||
|
await this.removeTorrent(currentVideoFile)
|
||||||
|
// Remove the old video file from the array
|
||||||
|
this.VideoFiles = this.VideoFiles.filter(f => f !== currentVideoFile)
|
||||||
|
|
||||||
|
// Update the database
|
||||||
currentVideoFile.set('extname', updatedVideoFile.extname)
|
currentVideoFile.set('extname', updatedVideoFile.extname)
|
||||||
}
|
|
||||||
currentVideoFile.set('size', updatedVideoFile.size)
|
currentVideoFile.set('size', updatedVideoFile.size)
|
||||||
|
|
||||||
updatedVideoFile = currentVideoFile
|
updatedVideoFile = currentVideoFile
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1343,10 +1348,8 @@ export class VideoModel extends Model<VideoModel> {
|
||||||
|
|
||||||
await updatedVideoFile.save()
|
await updatedVideoFile.save()
|
||||||
|
|
||||||
if (isNewVideoFile) {
|
|
||||||
this.VideoFiles.push(updatedVideoFile)
|
this.VideoFiles.push(updatedVideoFile)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
getOriginalFileResolution () {
|
getOriginalFileResolution () {
|
||||||
const originalFilePath = this.getVideoFilePath(this.getOriginalFile())
|
const originalFilePath = this.getVideoFilePath(this.getOriginalFile())
|
||||||
|
|
|
@ -3,29 +3,31 @@
|
||||||
import 'mocha'
|
import 'mocha'
|
||||||
import * as chai from 'chai'
|
import * as chai from 'chai'
|
||||||
import { VideoDetails, VideoFile } from '../../../shared/models/videos'
|
import { VideoDetails, VideoFile } from '../../../shared/models/videos'
|
||||||
const expect = chai.expect
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
doubleFollow,
|
||||||
execCLI,
|
execCLI,
|
||||||
|
flushAndRunMultipleServers,
|
||||||
flushTests,
|
flushTests,
|
||||||
getEnvCli,
|
getEnvCli,
|
||||||
|
getVideo,
|
||||||
getVideosList,
|
getVideosList,
|
||||||
killallServers,
|
killallServers,
|
||||||
parseTorrentVideo,
|
|
||||||
runServer,
|
|
||||||
ServerInfo,
|
ServerInfo,
|
||||||
setAccessTokensToServers,
|
setAccessTokensToServers,
|
||||||
uploadVideo,
|
uploadVideo,
|
||||||
wait,
|
wait
|
||||||
getVideo, flushAndRunMultipleServers, doubleFollow
|
|
||||||
} from '../utils'
|
} from '../utils'
|
||||||
|
|
||||||
function assertVideoProperties (video: VideoFile, resolution: number, extname: string) {
|
const expect = chai.expect
|
||||||
|
|
||||||
|
function assertVideoProperties (video: VideoFile, resolution: number, extname: string, size?: number) {
|
||||||
expect(video).to.have.nested.property('resolution.id', resolution)
|
expect(video).to.have.nested.property('resolution.id', resolution)
|
||||||
expect(video).to.have.property('magnetUri').that.includes(`.${extname}`)
|
expect(video).to.have.property('magnetUri').that.includes(`.${extname}`)
|
||||||
expect(video).to.have.property('torrentUrl').that.includes(`-${resolution}.torrent`)
|
expect(video).to.have.property('torrentUrl').that.includes(`-${resolution}.torrent`)
|
||||||
expect(video).to.have.property('fileUrl').that.includes(`.${extname}`)
|
expect(video).to.have.property('fileUrl').that.includes(`.${extname}`)
|
||||||
expect(video).to.have.property('size').that.is.above(0)
|
expect(video).to.have.property('size').that.is.above(0)
|
||||||
|
|
||||||
|
if (size) expect(video.size).to.equal(size)
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('Test create import video jobs', function () {
|
describe('Test create import video jobs', function () {
|
||||||
|
@ -51,6 +53,7 @@ describe('Test create import video jobs', function () {
|
||||||
const res2 = await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video2' })
|
const res2 = await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video2' })
|
||||||
video2UUID = res2.body.video.uuid
|
video2UUID = res2.body.video.uuid
|
||||||
|
|
||||||
|
// Transcoding
|
||||||
await wait(40000)
|
await wait(40000)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -60,12 +63,11 @@ describe('Test create import video jobs', function () {
|
||||||
|
|
||||||
await wait(30000)
|
await wait(30000)
|
||||||
|
|
||||||
|
let magnetUri: string
|
||||||
for (const server of servers) {
|
for (const server of servers) {
|
||||||
const { data: videos } = (await getVideosList(server.url)).body
|
const { data: videos } = (await getVideosList(server.url)).body
|
||||||
expect(videos).to.have.lengthOf(2)
|
expect(videos).to.have.lengthOf(2)
|
||||||
|
|
||||||
let infoHashes: { [ id: number ]: string } = {}
|
|
||||||
|
|
||||||
const video = videos.find(({ uuid }) => uuid === video1UUID)
|
const video = videos.find(({ uuid }) => uuid === video1UUID)
|
||||||
const videoDetail: VideoDetails = (await getVideo(server.url, video.uuid)).body
|
const videoDetail: VideoDetails = (await getVideo(server.url, video.uuid)).body
|
||||||
|
|
||||||
|
@ -73,6 +75,9 @@ describe('Test create import video jobs', function () {
|
||||||
const [originalVideo, transcodedVideo] = videoDetail.files
|
const [originalVideo, transcodedVideo] = videoDetail.files
|
||||||
assertVideoProperties(originalVideo, 720, 'webm')
|
assertVideoProperties(originalVideo, 720, 'webm')
|
||||||
assertVideoProperties(transcodedVideo, 480, 'webm')
|
assertVideoProperties(transcodedVideo, 480, 'webm')
|
||||||
|
|
||||||
|
if (!magnetUri) magnetUri = transcodedVideo.magnetUri
|
||||||
|
else expect(transcodedVideo.magnetUri).to.equal(magnetUri)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -82,21 +87,23 @@ describe('Test create import video jobs', function () {
|
||||||
|
|
||||||
await wait(30000)
|
await wait(30000)
|
||||||
|
|
||||||
|
let magnetUri: string
|
||||||
for (const server of servers.reverse()) {
|
for (const server of servers.reverse()) {
|
||||||
const { data: videos } = (await getVideosList(server.url)).body
|
const { data: videos } = (await getVideosList(server.url)).body
|
||||||
expect(videos).to.have.lengthOf(2)
|
expect(videos).to.have.lengthOf(2)
|
||||||
|
|
||||||
let infoHashes: { [ id: number ]: string }
|
|
||||||
|
|
||||||
const video = videos.find(({ uuid }) => uuid === video2UUID)
|
const video = videos.find(({ uuid }) => uuid === video2UUID)
|
||||||
const videoDetail: VideoDetails = (await getVideo(server.url, video.uuid)).body
|
const videoDetail: VideoDetails = (await getVideo(server.url, video.uuid)).body
|
||||||
|
|
||||||
expect(videoDetail.files).to.have.lengthOf(4)
|
expect(videoDetail.files).to.have.lengthOf(4)
|
||||||
const [originalVideo, transcodedVideo420, transcodedVideo320, transcodedVideo240] = videoDetail.files
|
const [originalVideo, transcodedVideo420, transcodedVideo320, transcodedVideo240] = videoDetail.files
|
||||||
assertVideoProperties(originalVideo, 720, 'ogv')
|
assertVideoProperties(originalVideo, 720, 'ogv', 140849)
|
||||||
assertVideoProperties(transcodedVideo420, 480, 'mp4')
|
assertVideoProperties(transcodedVideo420, 480, 'mp4')
|
||||||
assertVideoProperties(transcodedVideo320, 360, 'mp4')
|
assertVideoProperties(transcodedVideo320, 360, 'mp4')
|
||||||
assertVideoProperties(transcodedVideo240, 240, 'mp4')
|
assertVideoProperties(transcodedVideo240, 240, 'mp4')
|
||||||
|
|
||||||
|
if (!magnetUri) magnetUri = originalVideo.magnetUri
|
||||||
|
else expect(originalVideo.magnetUri).to.equal(magnetUri)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue