mirror of
https://github.com/Chocobozzz/PeerTube.git
synced 2025-02-25 18:55:32 -06:00
Add config option to keep original video file (basic first version) (#6157)
* testing not removing old file and adding columb to db * implement feature * remove unnecessary config changes * use only keptOriginalFileName, change keptOriginalFileName to keptOriginalFilename for consistency with with videoFile table, slight refactor with basename() * save original video files to dedicated directory original-video-files * begin implementing object storage (bucket) support --------- Co-authored-by: chagai.friedlander <chagai.friedlander@fairkom.eu> Co-authored-by: Ian <ian.kraft@hotmail.com> Co-authored-by: Chocobozzz <me@florianbigard.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
LiveVideoLatencyModeType,
|
||||
VideoFileMetadata,
|
||||
VideoPrivacyType,
|
||||
VideoStateType,
|
||||
VideoStreamingPlaylistType_Type
|
||||
@@ -85,7 +86,17 @@ export interface VideoExportJSON {
|
||||
}[]
|
||||
|
||||
source?: {
|
||||
filename: string
|
||||
inputFilename: string
|
||||
|
||||
resolution: number
|
||||
size: number
|
||||
|
||||
width: number
|
||||
height: number
|
||||
|
||||
fps: number
|
||||
|
||||
metadata: VideoFileMetadata
|
||||
}
|
||||
|
||||
archiveFiles: {
|
||||
|
||||
@@ -117,6 +117,10 @@ export interface CustomConfig {
|
||||
transcoding: {
|
||||
enabled: boolean
|
||||
|
||||
originalFile: {
|
||||
keep: boolean
|
||||
}
|
||||
|
||||
allowAdditionalExtensions: boolean
|
||||
allowAudioFiles: boolean
|
||||
|
||||
|
||||
@@ -1,4 +1,23 @@
|
||||
import { VideoFileMetadata } from './file/index.js'
|
||||
import { VideoConstant } from './video-constant.model.js'
|
||||
|
||||
export interface VideoSource {
|
||||
filename: string
|
||||
inputFilename: string
|
||||
|
||||
resolution?: VideoConstant<number>
|
||||
size?: number // Bytes
|
||||
|
||||
width?: number
|
||||
height?: number
|
||||
|
||||
fileDownloadUrl: string
|
||||
|
||||
fps?: number
|
||||
|
||||
metadata?: VideoFileMetadata
|
||||
|
||||
createdAt: string | Date
|
||||
|
||||
// TODO: remove, deprecated in 6.1
|
||||
filename: string
|
||||
}
|
||||
|
||||
@@ -106,6 +106,19 @@ export class ConfigCommand extends AbstractCommand {
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
keepSourceFile () {
|
||||
return this.updateExistingSubConfig({
|
||||
newConfig: {
|
||||
transcoding: {
|
||||
originalFile: {
|
||||
keep: true
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
enableChannelSync () {
|
||||
return this.setChannelSyncEnabled(true)
|
||||
}
|
||||
@@ -234,13 +247,17 @@ export class ConfigCommand extends AbstractCommand {
|
||||
webVideo?: boolean // default true
|
||||
hls?: boolean // default true
|
||||
with0p?: boolean // default false
|
||||
keepOriginal?: boolean // default false
|
||||
} = {}) {
|
||||
const { webVideo = true, hls = true, with0p = false } = options
|
||||
const { webVideo = true, hls = true, with0p = false, keepOriginal = false } = options
|
||||
|
||||
return this.updateExistingSubConfig({
|
||||
newConfig: {
|
||||
transcoding: {
|
||||
enabled: true,
|
||||
originalFile: {
|
||||
keep: keepOriginal
|
||||
},
|
||||
|
||||
allowAudioFiles: true,
|
||||
allowAdditionalExtensions: true,
|
||||
@@ -261,13 +278,17 @@ export class ConfigCommand extends AbstractCommand {
|
||||
enableMinimumTranscoding (options: {
|
||||
webVideo?: boolean // default true
|
||||
hls?: boolean // default true
|
||||
keepOriginal?: boolean // default false
|
||||
} = {}) {
|
||||
const { webVideo = true, hls = true } = options
|
||||
const { webVideo = true, hls = true, keepOriginal = false } = options
|
||||
|
||||
return this.updateExistingSubConfig({
|
||||
newConfig: {
|
||||
transcoding: {
|
||||
enabled: true,
|
||||
originalFile: {
|
||||
keep: keepOriginal
|
||||
},
|
||||
|
||||
allowAudioFiles: true,
|
||||
allowAdditionalExtensions: true,
|
||||
@@ -560,6 +581,9 @@ export class ConfigCommand extends AbstractCommand {
|
||||
},
|
||||
transcoding: {
|
||||
enabled: true,
|
||||
originalFile: {
|
||||
keep: false
|
||||
},
|
||||
remoteRunners: {
|
||||
enabled: false
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { randomInt } from 'crypto'
|
||||
import { HttpStatusCode } from '@peertube/peertube-models'
|
||||
import { randomInt } from 'crypto'
|
||||
import { makePostBodyRequest } from '../requests/index.js'
|
||||
|
||||
export class ObjectStorageCommand {
|
||||
@@ -50,6 +50,14 @@ export class ObjectStorageCommand {
|
||||
|
||||
web_videos: {
|
||||
bucket_name: this.getMockWebVideosBucketName()
|
||||
},
|
||||
|
||||
user_exports: {
|
||||
bucket_name: this.getMockUserExportBucketName()
|
||||
},
|
||||
|
||||
original_video_files: {
|
||||
bucket_name: this.getMockOriginalFileBucketName()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -63,6 +71,14 @@ export class ObjectStorageCommand {
|
||||
return `http://${this.getMockStreamingPlaylistsBucketName()}.${ObjectStorageCommand.getMockEndpointHost()}/`
|
||||
}
|
||||
|
||||
getMockUserExportBaseUrl () {
|
||||
return `http://${this.getMockUserExportBucketName()}.${ObjectStorageCommand.getMockEndpointHost()}/`
|
||||
}
|
||||
|
||||
getMockOriginalFileBaseUrl () {
|
||||
return `http://${this.getMockOriginalFileBucketName()}.${ObjectStorageCommand.getMockEndpointHost()}/`
|
||||
}
|
||||
|
||||
async prepareDefaultMockBuckets () {
|
||||
await this.createMockBucket(this.getMockStreamingPlaylistsBucketName())
|
||||
await this.createMockBucket(this.getMockWebVideosBucketName())
|
||||
@@ -100,6 +116,14 @@ export class ObjectStorageCommand {
|
||||
return this.getMockBucketName(name)
|
||||
}
|
||||
|
||||
getMockUserExportBucketName (name = 'user-exports') {
|
||||
return this.getMockBucketName(name)
|
||||
}
|
||||
|
||||
getMockOriginalFileBucketName (name = 'original-video-files') {
|
||||
return this.getMockBucketName(name)
|
||||
}
|
||||
|
||||
getMockBucketName (name: string) {
|
||||
return `${this.seed}-${name}`
|
||||
}
|
||||
|
||||
@@ -379,6 +379,7 @@ export class PeerTubeServer {
|
||||
avatars: this.getDirectoryPath('avatars') + '/',
|
||||
web_videos: this.getDirectoryPath('web-videos') + '/',
|
||||
streaming_playlists: this.getDirectoryPath('streaming-playlists') + '/',
|
||||
original_video_files: this.getDirectoryPath('original-video-files') + '/',
|
||||
redundancy: this.getDirectoryPath('redundancy') + '/',
|
||||
logs: this.getDirectoryPath('logs') + '/',
|
||||
previews: this.getDirectoryPath('previews') + '/',
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { exec } from 'child_process'
|
||||
import { copy, ensureDir, remove } from 'fs-extra/esm'
|
||||
import { readdir, readFile } from 'fs/promises'
|
||||
import { basename, join } from 'path'
|
||||
import { wait } from '@peertube/peertube-core-utils'
|
||||
import { HttpStatusCode } from '@peertube/peertube-models'
|
||||
import { getFileSize, isGithubCI, root } from '@peertube/peertube-node-utils'
|
||||
import { isGithubCI, root } from '@peertube/peertube-node-utils'
|
||||
import { exec } from 'child_process'
|
||||
import { copy, ensureDir, remove } from 'fs-extra/esm'
|
||||
import { readFile, readdir } from 'fs/promises'
|
||||
import { basename, join } from 'path'
|
||||
import { AbstractCommand, OverrideCommandOptions } from '../shared/index.js'
|
||||
|
||||
export class ServersCommand extends AbstractCommand {
|
||||
@@ -84,6 +84,8 @@ export class ServersCommand extends AbstractCommand {
|
||||
return files.length
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
buildWebVideoFilePath (fileUrl: string) {
|
||||
return this.buildDirectory(join('web-videos', basename(fileUrl)))
|
||||
}
|
||||
@@ -92,13 +94,9 @@ export class ServersCommand extends AbstractCommand {
|
||||
return this.buildDirectory(join('streaming-playlists', 'hls', videoUUID, basename(fileUrl)))
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
getLogContent () {
|
||||
return readFile(this.buildDirectory('logs/peertube.log'))
|
||||
}
|
||||
|
||||
async getServerFileSize (subPath: string) {
|
||||
const path = this.server.servers.buildDirectory(subPath)
|
||||
|
||||
return getFileSize(path)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { HttpStatusCode, ResultList, UserExport, UserExportRequestResult, UserExportState } from '@peertube/peertube-models'
|
||||
import { AbstractCommand, OverrideCommandOptions } from '../shared/index.js'
|
||||
import { wait } from '@peertube/peertube-core-utils'
|
||||
import { unwrapBody } from '../requests/requests.js'
|
||||
import { HttpStatusCode, ResultList, UserExport, UserExportRequestResult, UserExportState } from '@peertube/peertube-models'
|
||||
import { writeFile } from 'fs/promises'
|
||||
import { makeRawRequest, unwrapBody } from '../requests/requests.js'
|
||||
import { AbstractCommand, OverrideCommandOptions } from '../shared/index.js'
|
||||
|
||||
export class UserExportsCommand extends AbstractCommand {
|
||||
|
||||
@@ -49,6 +50,22 @@ export class UserExportsCommand extends AbstractCommand {
|
||||
})
|
||||
}
|
||||
|
||||
async downloadLatestArchive (options: OverrideCommandOptions & {
|
||||
userId: number
|
||||
destination: string
|
||||
}) {
|
||||
const { data } = await this.list(options)
|
||||
|
||||
const res = await makeRawRequest({
|
||||
url: data[0].privateDownloadUrl,
|
||||
responseType: 'arraybuffer',
|
||||
redirects: 1,
|
||||
expectedStatus: HttpStatusCode.OK_200
|
||||
})
|
||||
|
||||
await writeFile(options.destination, res.body)
|
||||
}
|
||||
|
||||
async deleteAllArchives (options: OverrideCommandOptions & {
|
||||
userId: number
|
||||
}) {
|
||||
|
||||
@@ -19,244 +19,7 @@ describe('Test config API validators', function () {
|
||||
let server: PeerTubeServer
|
||||
let userAccessToken: string
|
||||
|
||||
const updateParams: CustomConfig = {
|
||||
instance: {
|
||||
name: 'PeerTube updated',
|
||||
shortDescription: 'my short description',
|
||||
description: 'my super description',
|
||||
terms: 'my super terms',
|
||||
codeOfConduct: 'my super coc',
|
||||
|
||||
creationReason: 'my super reason',
|
||||
moderationInformation: 'my super moderation information',
|
||||
administrator: 'Kuja',
|
||||
maintenanceLifetime: 'forever',
|
||||
businessModel: 'my super business model',
|
||||
hardwareInformation: '2vCore 3GB RAM',
|
||||
|
||||
languages: [ 'en', 'es' ],
|
||||
categories: [ 1, 2 ],
|
||||
|
||||
isNSFW: true,
|
||||
defaultNSFWPolicy: 'blur',
|
||||
|
||||
defaultClientRoute: '/videos/recently-added',
|
||||
|
||||
customizations: {
|
||||
javascript: 'alert("coucou")',
|
||||
css: 'body { background-color: red; }'
|
||||
}
|
||||
},
|
||||
theme: {
|
||||
default: 'default'
|
||||
},
|
||||
services: {
|
||||
twitter: {
|
||||
username: '@MySuperUsername'
|
||||
}
|
||||
},
|
||||
client: {
|
||||
videos: {
|
||||
miniature: {
|
||||
preferAuthorDisplayName: false
|
||||
}
|
||||
},
|
||||
menu: {
|
||||
login: {
|
||||
redirectOnSingleExternalAuth: false
|
||||
}
|
||||
}
|
||||
},
|
||||
cache: {
|
||||
previews: {
|
||||
size: 2
|
||||
},
|
||||
captions: {
|
||||
size: 3
|
||||
},
|
||||
torrents: {
|
||||
size: 4
|
||||
},
|
||||
storyboards: {
|
||||
size: 5
|
||||
}
|
||||
},
|
||||
signup: {
|
||||
enabled: false,
|
||||
limit: 5,
|
||||
requiresApproval: false,
|
||||
requiresEmailVerification: false,
|
||||
minimumAge: 16
|
||||
},
|
||||
admin: {
|
||||
email: 'superadmin1@example.com'
|
||||
},
|
||||
contactForm: {
|
||||
enabled: false
|
||||
},
|
||||
user: {
|
||||
history: {
|
||||
videos: {
|
||||
enabled: true
|
||||
}
|
||||
},
|
||||
videoQuota: 5242881,
|
||||
videoQuotaDaily: 318742,
|
||||
defaultChannelName: 'Main $1 channel'
|
||||
},
|
||||
videoChannels: {
|
||||
maxPerUser: 20
|
||||
},
|
||||
transcoding: {
|
||||
enabled: true,
|
||||
remoteRunners: {
|
||||
enabled: true
|
||||
},
|
||||
allowAdditionalExtensions: true,
|
||||
allowAudioFiles: true,
|
||||
concurrency: 1,
|
||||
threads: 1,
|
||||
profile: 'vod_profile',
|
||||
resolutions: {
|
||||
'0p': false,
|
||||
'144p': false,
|
||||
'240p': false,
|
||||
'360p': true,
|
||||
'480p': true,
|
||||
'720p': false,
|
||||
'1080p': false,
|
||||
'1440p': false,
|
||||
'2160p': false
|
||||
},
|
||||
alwaysTranscodeOriginalResolution: false,
|
||||
webVideos: {
|
||||
enabled: true
|
||||
},
|
||||
hls: {
|
||||
enabled: false
|
||||
}
|
||||
},
|
||||
live: {
|
||||
enabled: true,
|
||||
|
||||
allowReplay: false,
|
||||
latencySetting: {
|
||||
enabled: false
|
||||
},
|
||||
maxDuration: 30,
|
||||
maxInstanceLives: -1,
|
||||
maxUserLives: 50,
|
||||
|
||||
transcoding: {
|
||||
enabled: true,
|
||||
remoteRunners: {
|
||||
enabled: true
|
||||
},
|
||||
threads: 4,
|
||||
profile: 'live_profile',
|
||||
resolutions: {
|
||||
'144p': true,
|
||||
'240p': true,
|
||||
'360p': true,
|
||||
'480p': true,
|
||||
'720p': true,
|
||||
'1080p': true,
|
||||
'1440p': true,
|
||||
'2160p': true
|
||||
},
|
||||
alwaysTranscodeOriginalResolution: false
|
||||
}
|
||||
},
|
||||
videoStudio: {
|
||||
enabled: true,
|
||||
remoteRunners: {
|
||||
enabled: true
|
||||
}
|
||||
},
|
||||
videoFile: {
|
||||
update: {
|
||||
enabled: true
|
||||
}
|
||||
},
|
||||
import: {
|
||||
videos: {
|
||||
concurrency: 1,
|
||||
http: {
|
||||
enabled: false
|
||||
},
|
||||
torrent: {
|
||||
enabled: false
|
||||
}
|
||||
},
|
||||
videoChannelSynchronization: {
|
||||
enabled: false,
|
||||
maxPerUser: 10
|
||||
},
|
||||
users: {
|
||||
enabled: false
|
||||
}
|
||||
},
|
||||
export: {
|
||||
users: {
|
||||
enabled: false,
|
||||
maxUserVideoQuota: 40,
|
||||
exportExpiration: 10
|
||||
}
|
||||
},
|
||||
trending: {
|
||||
videos: {
|
||||
algorithms: {
|
||||
enabled: [ 'hot', 'most-viewed', 'most-liked' ],
|
||||
default: 'most-viewed'
|
||||
}
|
||||
}
|
||||
},
|
||||
autoBlacklist: {
|
||||
videos: {
|
||||
ofUsers: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
},
|
||||
followers: {
|
||||
instance: {
|
||||
enabled: false,
|
||||
manualApproval: true
|
||||
}
|
||||
},
|
||||
followings: {
|
||||
instance: {
|
||||
autoFollowBack: {
|
||||
enabled: true
|
||||
},
|
||||
autoFollowIndex: {
|
||||
enabled: true,
|
||||
indexUrl: 'https://index.example.com'
|
||||
}
|
||||
}
|
||||
},
|
||||
broadcastMessage: {
|
||||
enabled: true,
|
||||
dismissable: true,
|
||||
message: 'super message',
|
||||
level: 'warning'
|
||||
},
|
||||
search: {
|
||||
remoteUri: {
|
||||
users: true,
|
||||
anonymous: true
|
||||
},
|
||||
searchIndex: {
|
||||
enabled: true,
|
||||
url: 'https://search.joinpeertube.org',
|
||||
disableLocalSearch: true,
|
||||
isDefaultSearch: true
|
||||
}
|
||||
},
|
||||
storyboards: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
let updateParams: CustomConfig
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
@@ -266,6 +29,7 @@ describe('Test config API validators', function () {
|
||||
server = await createSingleServer(1)
|
||||
|
||||
await setAccessTokensToServers([ server ])
|
||||
updateParams = await server.config.getCustomConfig()
|
||||
|
||||
const user = {
|
||||
username: 'user1',
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { HttpStatusCode } from '@peertube/peertube-models'
|
||||
import { HttpStatusCode, VideoSource } from '@peertube/peertube-models'
|
||||
import {
|
||||
PeerTubeServer,
|
||||
cleanupTests,
|
||||
createSingleServer,
|
||||
PeerTubeServer,
|
||||
makeRawRequest,
|
||||
setAccessTokensToServers,
|
||||
setDefaultVideoChannel,
|
||||
waitJobs
|
||||
@@ -148,6 +149,66 @@ describe('Test video sources API validator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
describe('When downloading the source file', function () {
|
||||
let videoFileToken: string
|
||||
let videoId: string
|
||||
let source: VideoSource
|
||||
let user3: string
|
||||
let user4: string
|
||||
|
||||
before(async function () {
|
||||
this.timeout(60000)
|
||||
|
||||
user3 = await server.users.generateUserAndToken('user3')
|
||||
user4 = await server.users.generateUserAndToken('user4')
|
||||
|
||||
await server.config.enableMinimumTranscoding({ hls: true, keepOriginal: true, webVideo: true })
|
||||
|
||||
const { uuid } = await server.videos.quickUpload({ name: 'video', token: user3 })
|
||||
|
||||
videoId = uuid
|
||||
videoFileToken = await server.videoToken.getVideoFileToken({ videoId: uuid, token: user3 })
|
||||
|
||||
await waitJobs([ server ])
|
||||
|
||||
source = await server.videos.getSource({ id: videoId, token: user3 })
|
||||
})
|
||||
|
||||
it('Should fail with an invalid filename', async function () {
|
||||
await makeRawRequest({ url: server.url + '/download/original-video-files/hello.mp4', expectedStatus: HttpStatusCode.NOT_FOUND_404 })
|
||||
})
|
||||
|
||||
it('Should fail without header token or video file token', async function () {
|
||||
await makeRawRequest({ url: source.fileDownloadUrl, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
|
||||
})
|
||||
|
||||
it('Should fail with an invalid header token', async function () {
|
||||
await makeRawRequest({ url: source.fileDownloadUrl, token: 'toto', expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
|
||||
})
|
||||
|
||||
it('Should fail with an invalid video file token', async function () {
|
||||
await makeRawRequest({ url: source.fileDownloadUrl, query: { videoFileToken: 'toto' }, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
|
||||
})
|
||||
|
||||
it('Should fail with header token of another user', async function () {
|
||||
await makeRawRequest({ url: source.fileDownloadUrl, token: user4, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
|
||||
})
|
||||
|
||||
it('Should fail with video file token of another user', async function () {
|
||||
const videoFileToken = await server.videoToken.getVideoFileToken({ videoId: uuid, token: user4 })
|
||||
|
||||
await makeRawRequest({ url: source.fileDownloadUrl, query: { videoFileToken }, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
|
||||
})
|
||||
|
||||
it('Should succeed with a valid header token', async function () {
|
||||
await makeRawRequest({ url: source.fileDownloadUrl, token: user3, expectedStatus: HttpStatusCode.OK_200 })
|
||||
})
|
||||
|
||||
it('Should succeed with a valid header token', async function () {
|
||||
await makeRawRequest({ url: source.fileDownloadUrl, query: { videoFileToken }, expectedStatus: HttpStatusCode.OK_200 })
|
||||
})
|
||||
})
|
||||
|
||||
after(async function () {
|
||||
await cleanupTests([ server ])
|
||||
})
|
||||
|
||||
@@ -84,6 +84,7 @@ function checkInitialConfig (server: PeerTubeServer, data: CustomConfig) {
|
||||
expect(data.transcoding.alwaysTranscodeOriginalResolution).to.be.true
|
||||
expect(data.transcoding.webVideos.enabled).to.be.true
|
||||
expect(data.transcoding.hls.enabled).to.be.true
|
||||
expect(data.transcoding.originalFile.keep).to.be.false
|
||||
|
||||
expect(data.live.enabled).to.be.false
|
||||
expect(data.live.allowReplay).to.be.false
|
||||
@@ -205,6 +206,7 @@ function checkUpdatedConfig (data: CustomConfig) {
|
||||
expect(data.transcoding.alwaysTranscodeOriginalResolution).to.be.false
|
||||
expect(data.transcoding.hls.enabled).to.be.false
|
||||
expect(data.transcoding.webVideos.enabled).to.be.true
|
||||
expect(data.transcoding.originalFile.keep).to.be.true
|
||||
|
||||
expect(data.live.enabled).to.be.true
|
||||
expect(data.live.allowReplay).to.be.true
|
||||
@@ -349,6 +351,9 @@ const newCustomConfig: CustomConfig = {
|
||||
remoteRunners: {
|
||||
enabled: true
|
||||
},
|
||||
originalFile: {
|
||||
keep: true
|
||||
},
|
||||
allowAdditionalExtensions: true,
|
||||
allowAudioFiles: true,
|
||||
threads: 1,
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
|
||||
|
||||
import { MockSmtpServer } from '@tests/shared/mock-servers/index.js'
|
||||
import {
|
||||
cleanupTests, getRedirectionUrl, makeActivityPubRawRequest,
|
||||
makeRawRequest,
|
||||
ObjectStorageCommand,
|
||||
PeerTubeServer,
|
||||
waitJobs
|
||||
} from '@peertube/peertube-server-commands'
|
||||
import { expect } from 'chai'
|
||||
import { wait } from '@peertube/peertube-core-utils'
|
||||
import {
|
||||
AccountExportJSON, ActivityPubActor,
|
||||
ActivityPubOrderedCollection,
|
||||
@@ -34,6 +26,15 @@ import {
|
||||
VideoPlaylistType,
|
||||
VideoPrivacy
|
||||
} from '@peertube/peertube-models'
|
||||
import { areMockObjectStorageTestsDisabled } from '@peertube/peertube-node-utils'
|
||||
import {
|
||||
cleanupTests, getRedirectionUrl, makeActivityPubRawRequest,
|
||||
makeRawRequest,
|
||||
ObjectStorageCommand,
|
||||
PeerTubeServer,
|
||||
waitJobs
|
||||
} from '@peertube/peertube-server-commands'
|
||||
import { expectStartWith } from '@tests/shared/checks.js'
|
||||
import {
|
||||
checkExportFileExists,
|
||||
checkFileExistsInZIP,
|
||||
@@ -44,8 +45,8 @@ import {
|
||||
prepareImportExportTests,
|
||||
regenerateExport
|
||||
} from '@tests/shared/import-export.js'
|
||||
import { areMockObjectStorageTestsDisabled } from '@peertube/peertube-node-utils'
|
||||
import { wait } from '@peertube/peertube-core-utils'
|
||||
import { MockSmtpServer } from '@tests/shared/mock-servers/index.js'
|
||||
import { expect } from 'chai'
|
||||
|
||||
function runTest (withObjectStorage: boolean) {
|
||||
let server: PeerTubeServer
|
||||
@@ -69,10 +70,12 @@ function runTest (withObjectStorage: boolean) {
|
||||
|
||||
let noahExportId: number
|
||||
|
||||
let objectStorage: ObjectStorageCommand
|
||||
|
||||
before(async function () {
|
||||
this.timeout(240000)
|
||||
|
||||
const objectStorage = withObjectStorage
|
||||
objectStorage = withObjectStorage
|
||||
? new ObjectStorageCommand()
|
||||
: undefined;
|
||||
|
||||
@@ -126,6 +129,10 @@ function runTest (withObjectStorage: boolean) {
|
||||
expect(data[0].size).to.be.greaterThan(0)
|
||||
expect(data[0].state.id).to.equal(UserExportState.COMPLETED)
|
||||
expect(data[0].state.label).to.equal('Completed')
|
||||
|
||||
if (objectStorage) {
|
||||
expectStartWith(await getRedirectionUrl(data[0].privateDownloadUrl), objectStorage.getMockUserExportBaseUrl())
|
||||
}
|
||||
}
|
||||
|
||||
await waitJobs([ server ])
|
||||
@@ -526,6 +533,14 @@ function runTest (withObjectStorage: boolean) {
|
||||
for (const url of urls) {
|
||||
await makeRawRequest({ url, expectedStatus: HttpStatusCode.OK_200 })
|
||||
}
|
||||
|
||||
expect(publicVideo.source.inputFilename).to.equal('video_short.webm')
|
||||
expect(publicVideo.source.fps).to.equal(25)
|
||||
expect(publicVideo.source.height).to.equal(720)
|
||||
expect(publicVideo.source.width).to.equal(1280)
|
||||
expect(publicVideo.source.metadata?.streams).to.exist
|
||||
expect(publicVideo.source.resolution).to.equal(720)
|
||||
expect(publicVideo.source.size).to.equal(218910)
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
|
||||
|
||||
import { MockSmtpServer } from '@tests/shared/mock-servers/index.js'
|
||||
import {
|
||||
cleanupTests, makeRawRequest,
|
||||
ObjectStorageCommand,
|
||||
PeerTubeServer, waitJobs
|
||||
} from '@peertube/peertube-server-commands'
|
||||
import {
|
||||
HttpStatusCode,
|
||||
LiveVideoLatencyMode,
|
||||
@@ -17,14 +11,20 @@ import {
|
||||
VideoPrivacy,
|
||||
VideoState
|
||||
} from '@peertube/peertube-models'
|
||||
import { prepareImportExportTests } from '@tests/shared/import-export.js'
|
||||
import { areMockObjectStorageTestsDisabled } from '@peertube/peertube-node-utils'
|
||||
import { writeFile } from 'fs/promises'
|
||||
import { join } from 'path'
|
||||
import { expect } from 'chai'
|
||||
import { testImage, testAvatarSize } from '@tests/shared/checks.js'
|
||||
import { completeVideoCheck } from '@tests/shared/videos.js'
|
||||
import {
|
||||
ObjectStorageCommand,
|
||||
PeerTubeServer,
|
||||
cleanupTests,
|
||||
waitJobs
|
||||
} from '@peertube/peertube-server-commands'
|
||||
import { testAvatarSize, testImage } from '@tests/shared/checks.js'
|
||||
import { prepareImportExportTests } from '@tests/shared/import-export.js'
|
||||
import { MockSmtpServer } from '@tests/shared/mock-servers/index.js'
|
||||
import { completeCheckHlsPlaylist } from '@tests/shared/streaming-playlists.js'
|
||||
import { completeVideoCheck } from '@tests/shared/videos.js'
|
||||
import { expect } from 'chai'
|
||||
import { join } from 'path'
|
||||
|
||||
function runTest (withObjectStorage: boolean) {
|
||||
let server: PeerTubeServer
|
||||
@@ -115,17 +115,8 @@ function runTest (withObjectStorage: boolean) {
|
||||
await server.userExports.request({ userId: noahId, withVideoFiles: true })
|
||||
await server.userExports.waitForCreation({ userId: noahId })
|
||||
|
||||
const { data } = await server.userExports.list({ userId: noahId })
|
||||
|
||||
const res = await makeRawRequest({
|
||||
url: data[0].privateDownloadUrl,
|
||||
responseType: 'arraybuffer',
|
||||
redirects: 1,
|
||||
expectedStatus: HttpStatusCode.OK_200
|
||||
})
|
||||
|
||||
archivePath = join(server.getDirectoryPath('tmp'), 'archive.zip')
|
||||
await writeFile(archivePath, res.body)
|
||||
await server.userExports.downloadLatestArchive({ userId: noahId, destination: archivePath })
|
||||
})
|
||||
|
||||
it('Should import an archive with video files', async function () {
|
||||
@@ -444,6 +435,11 @@ function runTest (withObjectStorage: boolean) {
|
||||
|
||||
const source = await remoteServer.videos.getSource({ id: otherVideo.uuid })
|
||||
expect(source.filename).to.equal('video_short.webm')
|
||||
expect(source.inputFilename).to.equal('video_short.webm')
|
||||
expect(source.fileDownloadUrl).to.not.exist
|
||||
|
||||
expect(source.metadata?.format).to.exist
|
||||
expect(source.metadata?.streams).to.be.an('array')
|
||||
}
|
||||
|
||||
{
|
||||
@@ -572,6 +568,57 @@ function runTest (withObjectStorage: boolean) {
|
||||
}
|
||||
})
|
||||
|
||||
it('Should import original file if included in the export', async function () {
|
||||
this.timeout(120000)
|
||||
|
||||
await server.config.enableMinimumTranscoding({ keepOriginal: true })
|
||||
await remoteServer.config.keepSourceFile()
|
||||
|
||||
const archivePath = join(server.getDirectoryPath('tmp'), 'archive2.zip')
|
||||
const fixture = 'video_short1.webm'
|
||||
|
||||
{
|
||||
const { token, userId } = await server.users.generate('claire')
|
||||
|
||||
await server.videos.quickUpload({ name: 'claire video', token, fixture })
|
||||
|
||||
await waitJobs([ server ])
|
||||
|
||||
await server.userExports.request({ userId, token, withVideoFiles: true })
|
||||
await server.userExports.waitForCreation({ userId, token })
|
||||
|
||||
await server.userExports.downloadLatestArchive({ userId, token, destination: archivePath })
|
||||
}
|
||||
|
||||
{
|
||||
const { token, userId } = await remoteServer.users.generate('external_claire')
|
||||
|
||||
await remoteServer.userImports.importArchive({ fixture: archivePath, userId, token })
|
||||
await waitJobs([ remoteServer ])
|
||||
|
||||
{
|
||||
const { data } = await remoteServer.videos.listMyVideos({ token })
|
||||
expect(data).to.have.lengthOf(1)
|
||||
|
||||
const source = await remoteServer.videos.getSource({ id: data[0].id })
|
||||
expect(source.filename).to.equal(fixture)
|
||||
expect(source.inputFilename).to.equal(fixture)
|
||||
expect(source.fileDownloadUrl).to.exist
|
||||
|
||||
expect(source.metadata?.format).to.exist
|
||||
expect(source.metadata?.streams).to.be.an('array')
|
||||
expect(source.metadata.format['format_name']).to.include('webm')
|
||||
|
||||
expect(source.createdAt).to.exist
|
||||
expect(source.fps).to.equal(25)
|
||||
expect(source.height).to.equal(720)
|
||||
expect(source.width).to.equal(1280)
|
||||
expect(source.resolution.id).to.equal(720)
|
||||
expect(source.size).to.equal(572456)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
after(async function () {
|
||||
MockSmtpServer.Instance.kill()
|
||||
|
||||
|
||||
@@ -1,24 +1,26 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
|
||||
import { expect } from 'chai'
|
||||
import { getAllFiles } from '@peertube/peertube-core-utils'
|
||||
import { HttpStatusCode } from '@peertube/peertube-models'
|
||||
import { expectStartWith } from '@tests/shared/checks.js'
|
||||
import { HttpStatusCode, VideoPrivacy } from '@peertube/peertube-models'
|
||||
import { areMockObjectStorageTestsDisabled } from '@peertube/peertube-node-utils'
|
||||
import {
|
||||
cleanupTests,
|
||||
createMultipleServers,
|
||||
doubleFollow,
|
||||
makeGetRequest,
|
||||
makeRawRequest,
|
||||
ObjectStorageCommand,
|
||||
PeerTubeServer,
|
||||
cleanupTests,
|
||||
createMultipleServers,
|
||||
doubleFollow, makeGetRequest,
|
||||
makeRawRequest,
|
||||
setAccessTokensToServers,
|
||||
setDefaultAccountAvatar,
|
||||
setDefaultVideoChannel,
|
||||
waitJobs
|
||||
} from '@peertube/peertube-server-commands'
|
||||
import { expectStartWith } from '@tests/shared/checks.js'
|
||||
import { checkDirectoryIsEmpty } from '@tests/shared/directories.js'
|
||||
import { FIXTURE_URLS } from '@tests/shared/tests.js'
|
||||
import { checkSourceFile } from '@tests/shared/videos.js'
|
||||
import { expect } from 'chai'
|
||||
|
||||
describe('Test a video file replacement', function () {
|
||||
describe('Test video source management', function () {
|
||||
let servers: PeerTubeServer[] = []
|
||||
|
||||
let replaceDate: Date
|
||||
@@ -36,6 +38,7 @@ describe('Test a video file replacement', function () {
|
||||
await setDefaultAccountAvatar(servers)
|
||||
|
||||
await servers[0].config.enableFileUpdate()
|
||||
await servers[0].config.enableMinimumTranscoding()
|
||||
|
||||
userToken = await servers[0].users.generateUserAndToken('user1')
|
||||
|
||||
@@ -44,30 +47,95 @@ describe('Test a video file replacement', function () {
|
||||
})
|
||||
|
||||
describe('Getting latest video source', () => {
|
||||
const fixture = 'video_short.webm'
|
||||
const fixture1 = 'video_short.webm'
|
||||
const fixture2 = 'video_short1.webm'
|
||||
|
||||
const uuids: string[] = []
|
||||
|
||||
it('Should get the source filename with legacy upload', async function () {
|
||||
it('Should get the source filename with legacy upload with disabled keep original file', async function () {
|
||||
this.timeout(30000)
|
||||
|
||||
const { uuid } = await servers[0].videos.upload({ attributes: { name: 'my video', fixture }, mode: 'legacy' })
|
||||
const { uuid } = await servers[0].videos.upload({ attributes: { name: 'my video', fixture: fixture1 }, mode: 'legacy' })
|
||||
uuids.push(uuid)
|
||||
|
||||
await waitJobs(servers)
|
||||
|
||||
const source = await servers[0].videos.getSource({ id: uuid })
|
||||
expect(source.filename).to.equal(fixture)
|
||||
expect(source.filename).to.equal(fixture1)
|
||||
expect(source.inputFilename).to.equal(fixture1)
|
||||
expect(source.fileDownloadUrl).to.be.null
|
||||
|
||||
expect(source.createdAt).to.exist
|
||||
expect(source.fps).to.equal(25)
|
||||
expect(source.height).to.equal(720)
|
||||
expect(source.width).to.equal(1280)
|
||||
expect(source.resolution.id).to.equal(720)
|
||||
expect(source.size).to.equal(218910)
|
||||
|
||||
expect(source.metadata?.format).to.exist
|
||||
expect(source.metadata?.streams).to.be.an('array')
|
||||
|
||||
await checkDirectoryIsEmpty(servers[0], 'original-video-files')
|
||||
})
|
||||
|
||||
it('Should get the source filename with resumable upload', async function () {
|
||||
it('Should get the source filename with resumable upload and enabled keep original file', async function () {
|
||||
this.timeout(30000)
|
||||
|
||||
const { uuid } = await servers[0].videos.upload({ attributes: { name: 'my video', fixture }, mode: 'resumable' })
|
||||
await servers[0].config.keepSourceFile()
|
||||
|
||||
const { uuid } = await servers[0].videos.upload({ attributes: { name: 'my video', fixture: fixture2 }, mode: 'resumable' })
|
||||
uuids.push(uuid)
|
||||
|
||||
await waitJobs(servers)
|
||||
|
||||
const source = await servers[0].videos.getSource({ id: uuid })
|
||||
expect(source.filename).to.equal(fixture)
|
||||
expect(source.filename).to.equal(fixture2)
|
||||
expect(source.inputFilename).to.equal(fixture2)
|
||||
expect(source.fileDownloadUrl).to.exist
|
||||
|
||||
expect(source.createdAt).to.exist
|
||||
expect(source.fps).to.equal(25)
|
||||
expect(source.height).to.equal(720)
|
||||
expect(source.width).to.equal(1280)
|
||||
expect(source.resolution.id).to.equal(720)
|
||||
expect(source.size).to.equal(572456)
|
||||
|
||||
expect(source.metadata?.format).to.exist
|
||||
expect(source.metadata?.streams).to.be.an('array')
|
||||
})
|
||||
|
||||
after(async function () {
|
||||
it('Should have kept original video file', async function () {
|
||||
await checkSourceFile({ server: servers[0], fsCount: 1, fixture: fixture2, uuid: uuids[uuids.length - 1] })
|
||||
})
|
||||
|
||||
it('Should transcode a file but do not replace original file', async function () {
|
||||
await servers[0].videos.runTranscoding({ transcodingType: 'web-video', videoId: uuids[0] })
|
||||
await servers[0].videos.runTranscoding({ transcodingType: 'web-video', videoId: uuids[1] })
|
||||
|
||||
await checkSourceFile({ server: servers[0], fsCount: 1, fixture: fixture2, uuid: uuids[uuids.length - 1] })
|
||||
})
|
||||
|
||||
it('Should also keep audio files', async function () {
|
||||
const fixture = 'sample.ogg'
|
||||
const { uuid } = await servers[0].videos.quickUpload({ name: 'audio', fixture })
|
||||
uuids.push(uuid)
|
||||
|
||||
await waitJobs(servers)
|
||||
const source = await checkSourceFile({ server: servers[0], fsCount: 2, fixture, uuid })
|
||||
|
||||
expect(source.createdAt).to.exist
|
||||
expect(source.fps).to.equal(0)
|
||||
expect(source.height).to.equal(0)
|
||||
expect(source.width).to.equal(0)
|
||||
expect(source.resolution.id).to.equal(0)
|
||||
expect(source.resolution.label).to.equal('Audio')
|
||||
expect(source.size).to.equal(105243)
|
||||
|
||||
expect(source.metadata?.format).to.exist
|
||||
expect(source.metadata?.streams).to.be.an('array')
|
||||
})
|
||||
|
||||
it('Should delete all videos and do not have original files anymore', async function () {
|
||||
this.timeout(60000)
|
||||
|
||||
for (const uuid of uuids) {
|
||||
@@ -75,6 +143,23 @@ describe('Test a video file replacement', function () {
|
||||
}
|
||||
|
||||
await waitJobs(servers)
|
||||
|
||||
await checkDirectoryIsEmpty(servers[0], 'original-video-files')
|
||||
})
|
||||
|
||||
it('Should not have source on import', async function () {
|
||||
const { video: { uuid } } = await servers[0].videoImports.importVideo({
|
||||
attributes: {
|
||||
channelId: servers[0].store.channel.id,
|
||||
targetUrl: FIXTURE_URLS.goodVideo,
|
||||
privacy: VideoPrivacy.PUBLIC
|
||||
}
|
||||
})
|
||||
|
||||
await waitJobs(servers)
|
||||
|
||||
await servers[0].videos.getSource({ id: uuid, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
|
||||
await checkDirectoryIsEmpty(servers[0], 'original-video-files')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -110,18 +195,25 @@ describe('Test a video file replacement', function () {
|
||||
}
|
||||
})
|
||||
|
||||
it('Should not have kept original video file', async function () {
|
||||
await checkDirectoryIsEmpty(servers[0], 'original-video-files')
|
||||
})
|
||||
|
||||
it('Should replace a video file with transcoding enabled', async function () {
|
||||
this.timeout(240000)
|
||||
|
||||
const previousPaths: string[] = []
|
||||
|
||||
await servers[0].config.enableTranscoding({ hls: true, webVideo: true, with0p: true })
|
||||
await servers[0].config.enableTranscoding({ hls: true, webVideo: true, with0p: true, keepOriginal: true })
|
||||
|
||||
const { uuid: videoUUID } = await servers[0].videos.quickUpload({ name: 'fs with transcoding', fixture: 'video_short_720p.mp4' })
|
||||
const uploadFixture = 'video_short_720p.mp4'
|
||||
const { uuid: videoUUID } = await servers[0].videos.quickUpload({ name: 'fs with transcoding', fixture: uploadFixture })
|
||||
uuid = videoUUID
|
||||
|
||||
await waitJobs(servers)
|
||||
|
||||
await checkSourceFile({ server: servers[0], fsCount: 1, uuid, fixture: uploadFixture })
|
||||
|
||||
for (const server of servers) {
|
||||
const video = await server.videos.get({ id: uuid })
|
||||
expect(video.inputFileUpdatedAt).to.be.null
|
||||
@@ -151,9 +243,23 @@ describe('Test a video file replacement', function () {
|
||||
|
||||
replaceDate = new Date()
|
||||
|
||||
await servers[0].videos.replaceSourceFile({ videoId: uuid, fixture: 'video_short_360p.mp4' })
|
||||
const replaceFixture = 'video_short_360p.mp4'
|
||||
await servers[0].videos.replaceSourceFile({ videoId: uuid, fixture: replaceFixture })
|
||||
await waitJobs(servers)
|
||||
|
||||
const source = await checkSourceFile({ server: servers[0], fsCount: 1, uuid, fixture: replaceFixture })
|
||||
|
||||
expect(source.createdAt).to.exist
|
||||
expect(source.fps).to.equal(25)
|
||||
expect(source.height).to.equal(360)
|
||||
expect(source.width).to.equal(640)
|
||||
expect(source.resolution.id).to.equal(360)
|
||||
expect(source.resolution.label).to.equal('360p')
|
||||
expect(source.size).to.equal(30620)
|
||||
|
||||
expect(source.metadata?.format).to.exist
|
||||
expect(source.metadata?.streams).to.be.an('array')
|
||||
|
||||
for (const server of servers) {
|
||||
const video = await server.videos.get({ id: uuid })
|
||||
|
||||
@@ -189,35 +295,36 @@ describe('Test a video file replacement', function () {
|
||||
}
|
||||
}
|
||||
|
||||
await servers[0].config.enableMinimumTranscoding()
|
||||
await servers[0].config.enableMinimumTranscoding({ keepOriginal: true })
|
||||
})
|
||||
|
||||
it('Should have cleaned up old files', async function () {
|
||||
{
|
||||
const count = await servers[0].servers.countFiles('storyboards')
|
||||
expect(count).to.equal(2)
|
||||
expect(count).to.equal(3)
|
||||
}
|
||||
|
||||
{
|
||||
const count = await servers[0].servers.countFiles('web-videos')
|
||||
expect(count).to.equal(5 + 1) // +1 for private directory
|
||||
expect(count).to.equal(6 + 1) // +1 for private directory
|
||||
}
|
||||
|
||||
{
|
||||
const count = await servers[0].servers.countFiles('streaming-playlists/hls')
|
||||
expect(count).to.equal(1 + 1) // +1 for private directory
|
||||
expect(count).to.equal(2 + 1) // +1 for private directory
|
||||
}
|
||||
|
||||
{
|
||||
const count = await servers[0].servers.countFiles('torrents')
|
||||
expect(count).to.equal(9)
|
||||
expect(count).to.equal(11)
|
||||
}
|
||||
})
|
||||
|
||||
it('Should have the correct source input', async function () {
|
||||
it('Should have the correct source input filename', async function () {
|
||||
const source = await servers[0].videos.getSource({ id: uuid })
|
||||
|
||||
expect(source.filename).to.equal('video_short_360p.mp4')
|
||||
expect(source.inputFilename).to.equal('video_short_360p.mp4')
|
||||
expect(new Date(source.createdAt)).to.be.above(replaceDate)
|
||||
})
|
||||
|
||||
@@ -367,6 +474,9 @@ describe('Test a video file replacement', function () {
|
||||
expect(files[0].resolution.id).to.equal(360)
|
||||
expectStartWith(files[0].fileUrl, objectStorage.getMockWebVideosBaseUrl())
|
||||
}
|
||||
|
||||
const source = await servers[0].videos.getSource({ id: uuid })
|
||||
expect(source.fileDownloadUrl).to.not.exist
|
||||
})
|
||||
|
||||
it('Should replace a video file with transcoding enabled', async function () {
|
||||
@@ -374,16 +484,25 @@ describe('Test a video file replacement', function () {
|
||||
|
||||
const previousPaths: string[] = []
|
||||
|
||||
await servers[0].config.enableTranscoding({ hls: true, webVideo: true, with0p: true })
|
||||
await servers[0].config.enableTranscoding({ hls: true, webVideo: true, with0p: true, keepOriginal: true })
|
||||
|
||||
const fixture1 = 'video_short_360p.mp4'
|
||||
const { uuid: videoUUID } = await servers[0].videos.quickUpload({
|
||||
name: 'object storage with transcoding',
|
||||
fixture: 'video_short_360p.mp4'
|
||||
fixture: fixture1
|
||||
})
|
||||
uuid = videoUUID
|
||||
|
||||
await waitJobs(servers)
|
||||
|
||||
await checkSourceFile({
|
||||
server: servers[0],
|
||||
fixture: fixture1,
|
||||
fsCount: 0,
|
||||
uuid,
|
||||
objectStorageBaseUrl: objectStorage?.getMockOriginalFileBaseUrl()
|
||||
})
|
||||
|
||||
for (const server of servers) {
|
||||
const video = await server.videos.get({ id: uuid })
|
||||
|
||||
@@ -403,9 +522,18 @@ describe('Test a video file replacement', function () {
|
||||
}
|
||||
}
|
||||
|
||||
await servers[0].videos.replaceSourceFile({ videoId: uuid, fixture: 'video_short_240p.mp4' })
|
||||
const fixture2 = 'video_short_240p.mp4'
|
||||
await servers[0].videos.replaceSourceFile({ videoId: uuid, fixture: fixture2 })
|
||||
await waitJobs(servers)
|
||||
|
||||
await checkSourceFile({
|
||||
server: servers[0],
|
||||
fixture: fixture2,
|
||||
fsCount: 0,
|
||||
uuid,
|
||||
objectStorageBaseUrl: objectStorage?.getMockOriginalFileBaseUrl()
|
||||
})
|
||||
|
||||
for (const server of servers) {
|
||||
const video = await server.videos.get({ id: uuid })
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export * from './client-cli.js'
|
||||
export * from './live-transcoding.js'
|
||||
export * from './replace-file.js'
|
||||
export * from './studio-transcoding.js'
|
||||
export * from './vod-transcoding.js'
|
||||
|
||||
86
packages/tests/src/peertube-runner/replace-file.ts
Normal file
86
packages/tests/src/peertube-runner/replace-file.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
|
||||
import { getAllFiles } from '@peertube/peertube-core-utils'
|
||||
import {
|
||||
cleanupTests,
|
||||
createSingleServer,
|
||||
PeerTubeServer,
|
||||
setAccessTokensToServers,
|
||||
setDefaultVideoChannel,
|
||||
waitJobs
|
||||
} from '@peertube/peertube-server-commands'
|
||||
import { PeerTubeRunnerProcess } from '@tests/shared/peertube-runner-process.js'
|
||||
import { checkSourceFile } from '@tests/shared/videos.js'
|
||||
import { expect } from 'chai'
|
||||
|
||||
describe('Test replace file using peertube-runner program', function () {
|
||||
let server: PeerTubeServer
|
||||
let peertubeRunner: PeerTubeRunnerProcess
|
||||
let uuid: string
|
||||
|
||||
before(async function () {
|
||||
this.timeout(120_000)
|
||||
|
||||
server = await createSingleServer(1)
|
||||
|
||||
await setAccessTokensToServers([ server ])
|
||||
await setDefaultVideoChannel([ server ])
|
||||
|
||||
await server.config.enableRemoteTranscoding()
|
||||
await server.config.enableFileUpdate()
|
||||
await server.config.enableMinimumTranscoding({ hls: true, keepOriginal: true, webVideo: true })
|
||||
|
||||
const registrationToken = await server.runnerRegistrationTokens.getFirstRegistrationToken()
|
||||
|
||||
peertubeRunner = new PeerTubeRunnerProcess(server)
|
||||
await peertubeRunner.runServer()
|
||||
await peertubeRunner.registerPeerTubeInstance({ registrationToken, runnerName: 'runner' })
|
||||
})
|
||||
|
||||
it('Should upload a webm video, transcode it and keep original file', async function () {
|
||||
this.timeout(240000)
|
||||
|
||||
const fixture = 'video_short.webm';
|
||||
({ uuid } = await server.videos.quickUpload({ name: 'video', fixture }))
|
||||
|
||||
await waitJobs(server, { runnerJobs: true })
|
||||
|
||||
const video = await server.videos.get({ id: uuid })
|
||||
|
||||
const files = getAllFiles(video)
|
||||
expect(files).to.have.lengthOf(4)
|
||||
expect(files[0].resolution.id).to.equal(720)
|
||||
|
||||
await checkSourceFile({ server, fsCount: 1, fixture, uuid })
|
||||
})
|
||||
|
||||
it('Should upload an audio file, transcode it and keep original file', async function () {
|
||||
const fixture = 'sample.ogg'
|
||||
const { uuid } = await server.videos.quickUpload({ name: 'audio', fixture })
|
||||
|
||||
await waitJobs([ server ], { runnerJobs: true })
|
||||
await checkSourceFile({ server, fsCount: 2, fixture, uuid })
|
||||
})
|
||||
|
||||
it('Should replace the video', async function () {
|
||||
const fixture = 'video_short_360p.mp4'
|
||||
await server.videos.replaceSourceFile({ videoId: uuid, fixture })
|
||||
await waitJobs(server, { runnerJobs: true })
|
||||
|
||||
const video = await server.videos.get({ id: uuid })
|
||||
|
||||
const files = getAllFiles(video)
|
||||
expect(files).to.have.lengthOf(4)
|
||||
expect(files[0].resolution.id).to.equal(360)
|
||||
|
||||
await checkSourceFile({ server, fsCount: 2, fixture, uuid })
|
||||
})
|
||||
|
||||
after(async function () {
|
||||
if (peertubeRunner) {
|
||||
await peertubeRunner.unregisterPeerTubeInstance({ runnerName: 'runner' })
|
||||
peertubeRunner.kill()
|
||||
}
|
||||
|
||||
await cleanupTests([ server ])
|
||||
})
|
||||
})
|
||||
@@ -1,23 +1,23 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/no-floating-promises */
|
||||
|
||||
import { uuidRegex } from '@peertube/peertube-core-utils'
|
||||
import { HttpStatusCode, HttpStatusCodeType, VideoCaption, VideoDetails, VideoPrivacy, VideoResolution } from '@peertube/peertube-models'
|
||||
import { buildAbsoluteFixturePath, getFileSize, getFilenameFromUrl, getLowercaseExtension } from '@peertube/peertube-node-utils'
|
||||
import { PeerTubeServer, VideoEdit, getRedirectionUrl, makeRawRequest, waitJobs } from '@peertube/peertube-server-commands'
|
||||
import {
|
||||
VIDEO_CATEGORIES,
|
||||
VIDEO_LANGUAGES,
|
||||
VIDEO_LICENCES,
|
||||
VIDEO_PRIVACIES,
|
||||
loadLanguages
|
||||
} from '@peertube/peertube-server/core/initializers/constants.js'
|
||||
import { expect } from 'chai'
|
||||
import { pathExists } from 'fs-extra/esm'
|
||||
import { readdir } from 'fs/promises'
|
||||
import { basename, join } from 'path'
|
||||
import { uuidRegex } from '@peertube/peertube-core-utils'
|
||||
import { HttpStatusCode, HttpStatusCodeType, VideoCaption, VideoDetails, VideoPrivacy, VideoResolution } from '@peertube/peertube-models'
|
||||
import {
|
||||
loadLanguages,
|
||||
VIDEO_CATEGORIES,
|
||||
VIDEO_LANGUAGES,
|
||||
VIDEO_LICENCES,
|
||||
VIDEO_PRIVACIES
|
||||
} from '@peertube/peertube-server/core/initializers/constants.js'
|
||||
import { getLowercaseExtension } from '@peertube/peertube-node-utils'
|
||||
import { makeRawRequest, PeerTubeServer, VideoEdit, waitJobs } from '@peertube/peertube-server-commands'
|
||||
import { dateIsValid, expectStartWith, testImageGeneratedByFFmpeg } from './checks.js'
|
||||
import { checkWebTorrentWorks } from './webtorrent.js'
|
||||
import { completeCheckHlsPlaylist } from './streaming-playlists.js'
|
||||
import { checkWebTorrentWorks } from './webtorrent.js'
|
||||
|
||||
export async function completeWebVideoFilesCheck (options: {
|
||||
server: PeerTubeServer
|
||||
@@ -369,3 +369,40 @@ export async function uploadRandomVideoOnServers (
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
export async function checkSourceFile (options: {
|
||||
server: PeerTubeServer
|
||||
fsCount: number
|
||||
uuid: string
|
||||
fixture: string
|
||||
objectStorageBaseUrl?: string // default false
|
||||
}) {
|
||||
const { server, fsCount, fixture, uuid, objectStorageBaseUrl } = options
|
||||
|
||||
const source = await server.videos.getSource({ id: uuid })
|
||||
const fixtureFileSize = await getFileSize(buildAbsoluteFixturePath(fixture))
|
||||
|
||||
if (fsCount > 0) {
|
||||
expect(await server.servers.countFiles('original-video-files')).to.equal(fsCount)
|
||||
|
||||
const keptFilePath = join(server.servers.buildDirectory('original-video-files'), getFilenameFromUrl(source.fileDownloadUrl))
|
||||
expect(await getFileSize(keptFilePath)).to.equal(fixtureFileSize)
|
||||
}
|
||||
|
||||
expect(source.fileDownloadUrl).to.exist
|
||||
if (objectStorageBaseUrl) {
|
||||
const token = await server.videoToken.getVideoFileToken({ videoId: uuid })
|
||||
expectStartWith(await getRedirectionUrl(source.fileDownloadUrl + '?videoFileToken=' + token), objectStorageBaseUrl)
|
||||
}
|
||||
|
||||
const { body } = await makeRawRequest({
|
||||
url: source.fileDownloadUrl,
|
||||
token: server.accessToken,
|
||||
redirects: 1,
|
||||
expectedStatus: HttpStatusCode.OK_200
|
||||
})
|
||||
|
||||
expect(body).to.have.lengthOf(fixtureFileSize)
|
||||
|
||||
return source
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user