aboutsummaryrefslogtreecommitdiffhomepage
path: root/packages/tests/src/shared/videos.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/tests/src/shared/videos.ts')
-rw-r--r--packages/tests/src/shared/videos.ts323
1 files changed, 323 insertions, 0 deletions
diff --git a/packages/tests/src/shared/videos.ts b/packages/tests/src/shared/videos.ts
new file mode 100644
index 000000000..9bdcbf058
--- /dev/null
+++ b/packages/tests/src/shared/videos.ts
@@ -0,0 +1,323 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/no-floating-promises */
2
3import { expect } from 'chai'
4import { pathExists } from 'fs-extra/esm'
5import { readdir } from 'fs/promises'
6import { basename, join } from 'path'
7import { pick, uuidRegex } from '@peertube/peertube-core-utils'
8import { HttpStatusCode, HttpStatusCodeType, VideoCaption, VideoDetails, VideoPrivacy, VideoResolution } from '@peertube/peertube-models'
9import {
10 loadLanguages,
11 VIDEO_CATEGORIES,
12 VIDEO_LANGUAGES,
13 VIDEO_LICENCES,
14 VIDEO_PRIVACIES
15} from '@peertube/peertube-server/server/initializers/constants.js'
16import { getLowercaseExtension } from '@peertube/peertube-node-utils'
17import { makeRawRequest, PeerTubeServer, VideoEdit, waitJobs } from '@peertube/peertube-server-commands'
18import { dateIsValid, expectStartWith, testImageGeneratedByFFmpeg } from './checks.js'
19import { checkWebTorrentWorks } from './webtorrent.js'
20
21async function completeWebVideoFilesCheck (options: {
22 server: PeerTubeServer
23 originServer: PeerTubeServer
24 videoUUID: string
25 fixture: string
26 files: {
27 resolution: number
28 size?: number
29 }[]
30 objectStorageBaseUrl?: string
31}) {
32 const { originServer, server, videoUUID, files, fixture, objectStorageBaseUrl } = options
33 const video = await server.videos.getWithToken({ id: videoUUID })
34 const serverConfig = await originServer.config.getConfig()
35 const requiresAuth = video.privacy.id === VideoPrivacy.PRIVATE || video.privacy.id === VideoPrivacy.INTERNAL
36
37 const transcodingEnabled = serverConfig.transcoding.web_videos.enabled
38
39 for (const attributeFile of files) {
40 const file = video.files.find(f => f.resolution.id === attributeFile.resolution)
41 expect(file, `resolution ${attributeFile.resolution} does not exist`).not.to.be.undefined
42
43 let extension = getLowercaseExtension(fixture)
44 // Transcoding enabled: extension will always be .mp4
45 if (transcodingEnabled) extension = '.mp4'
46
47 expect(file.id).to.exist
48 expect(file.magnetUri).to.have.lengthOf.above(2)
49
50 {
51 const privatePath = requiresAuth
52 ? 'private/'
53 : ''
54 const nameReg = `${uuidRegex}-${file.resolution.id}`
55
56 expect(file.torrentDownloadUrl).to.match(new RegExp(`${server.url}/download/torrents/${nameReg}.torrent`))
57 expect(file.torrentUrl).to.match(new RegExp(`${server.url}/lazy-static/torrents/${nameReg}.torrent`))
58
59 if (objectStorageBaseUrl && requiresAuth) {
60 const regexp = new RegExp(`${originServer.url}/object-storage-proxy/web-videos/${privatePath}${nameReg}${extension}`)
61 expect(file.fileUrl).to.match(regexp)
62 } else if (objectStorageBaseUrl) {
63 expectStartWith(file.fileUrl, objectStorageBaseUrl)
64 } else {
65 expect(file.fileUrl).to.match(new RegExp(`${originServer.url}/static/web-videos/${privatePath}${nameReg}${extension}`))
66 }
67
68 expect(file.fileDownloadUrl).to.match(new RegExp(`${originServer.url}/download/videos/${nameReg}${extension}`))
69 }
70
71 {
72 const token = requiresAuth
73 ? server.accessToken
74 : undefined
75
76 await Promise.all([
77 makeRawRequest({ url: file.torrentUrl, token, expectedStatus: HttpStatusCode.OK_200 }),
78 makeRawRequest({ url: file.torrentDownloadUrl, token, expectedStatus: HttpStatusCode.OK_200 }),
79 makeRawRequest({ url: file.metadataUrl, token, expectedStatus: HttpStatusCode.OK_200 }),
80 makeRawRequest({ url: file.fileUrl, token, expectedStatus: HttpStatusCode.OK_200 }),
81 makeRawRequest({
82 url: file.fileDownloadUrl,
83 token,
84 expectedStatus: objectStorageBaseUrl ? HttpStatusCode.FOUND_302 : HttpStatusCode.OK_200
85 })
86 ])
87 }
88
89 expect(file.resolution.id).to.equal(attributeFile.resolution)
90
91 if (file.resolution.id === VideoResolution.H_NOVIDEO) {
92 expect(file.resolution.label).to.equal('Audio')
93 } else {
94 expect(file.resolution.label).to.equal(attributeFile.resolution + 'p')
95 }
96
97 if (attributeFile.size) {
98 const minSize = attributeFile.size - ((10 * attributeFile.size) / 100)
99 const maxSize = attributeFile.size + ((10 * attributeFile.size) / 100)
100 expect(
101 file.size,
102 'File size for resolution ' + file.resolution.label + ' outside confidence interval (' + minSize + '> size <' + maxSize + ')'
103 ).to.be.above(minSize).and.below(maxSize)
104 }
105
106 await checkWebTorrentWorks(file.magnetUri)
107 }
108}
109
110async function completeVideoCheck (options: {
111 server: PeerTubeServer
112 originServer: PeerTubeServer
113 videoUUID: string
114 attributes: {
115 name: string
116 category: number
117 licence: number
118 language: string
119 nsfw: boolean
120 commentsEnabled: boolean
121 downloadEnabled: boolean
122 description: string
123 publishedAt?: string
124 support: string
125 originallyPublishedAt?: string
126 account: {
127 name: string
128 host: string
129 }
130 isLocal: boolean
131 tags: string[]
132 privacy: number
133 likes?: number
134 dislikes?: number
135 duration: number
136 channel: {
137 displayName: string
138 name: string
139 description: string
140 isLocal: boolean
141 }
142 fixture: string
143 files: {
144 resolution: number
145 size: number
146 }[]
147 thumbnailfile?: string
148 previewfile?: string
149 }
150}) {
151 const { attributes, originServer, server, videoUUID } = options
152
153 await loadLanguages()
154
155 const video = await server.videos.get({ id: videoUUID })
156
157 if (!attributes.likes) attributes.likes = 0
158 if (!attributes.dislikes) attributes.dislikes = 0
159
160 expect(video.name).to.equal(attributes.name)
161 expect(video.category.id).to.equal(attributes.category)
162 expect(video.category.label).to.equal(attributes.category !== null ? VIDEO_CATEGORIES[attributes.category] : 'Unknown')
163 expect(video.licence.id).to.equal(attributes.licence)
164 expect(video.licence.label).to.equal(attributes.licence !== null ? VIDEO_LICENCES[attributes.licence] : 'Unknown')
165 expect(video.language.id).to.equal(attributes.language)
166 expect(video.language.label).to.equal(attributes.language !== null ? VIDEO_LANGUAGES[attributes.language] : 'Unknown')
167 expect(video.privacy.id).to.deep.equal(attributes.privacy)
168 expect(video.privacy.label).to.deep.equal(VIDEO_PRIVACIES[attributes.privacy])
169 expect(video.nsfw).to.equal(attributes.nsfw)
170 expect(video.description).to.equal(attributes.description)
171 expect(video.account.id).to.be.a('number')
172 expect(video.account.host).to.equal(attributes.account.host)
173 expect(video.account.name).to.equal(attributes.account.name)
174 expect(video.channel.displayName).to.equal(attributes.channel.displayName)
175 expect(video.channel.name).to.equal(attributes.channel.name)
176 expect(video.likes).to.equal(attributes.likes)
177 expect(video.dislikes).to.equal(attributes.dislikes)
178 expect(video.isLocal).to.equal(attributes.isLocal)
179 expect(video.duration).to.equal(attributes.duration)
180 expect(video.url).to.contain(originServer.host)
181 expect(dateIsValid(video.createdAt)).to.be.true
182 expect(dateIsValid(video.publishedAt)).to.be.true
183 expect(dateIsValid(video.updatedAt)).to.be.true
184
185 if (attributes.publishedAt) {
186 expect(video.publishedAt).to.equal(attributes.publishedAt)
187 }
188
189 if (attributes.originallyPublishedAt) {
190 expect(video.originallyPublishedAt).to.equal(attributes.originallyPublishedAt)
191 } else {
192 expect(video.originallyPublishedAt).to.be.null
193 }
194
195 expect(video.files).to.have.lengthOf(attributes.files.length)
196 expect(video.tags).to.deep.equal(attributes.tags)
197 expect(video.account.name).to.equal(attributes.account.name)
198 expect(video.account.host).to.equal(attributes.account.host)
199 expect(video.channel.displayName).to.equal(attributes.channel.displayName)
200 expect(video.channel.name).to.equal(attributes.channel.name)
201 expect(video.channel.host).to.equal(attributes.account.host)
202 expect(video.channel.isLocal).to.equal(attributes.channel.isLocal)
203 expect(video.channel.createdAt).to.exist
204 expect(dateIsValid(video.channel.updatedAt.toString())).to.be.true
205 expect(video.commentsEnabled).to.equal(attributes.commentsEnabled)
206 expect(video.downloadEnabled).to.equal(attributes.downloadEnabled)
207
208 expect(video.thumbnailPath).to.exist
209 await testImageGeneratedByFFmpeg(server.url, attributes.thumbnailfile || attributes.fixture, video.thumbnailPath)
210
211 if (attributes.previewfile) {
212 expect(video.previewPath).to.exist
213 await testImageGeneratedByFFmpeg(server.url, attributes.previewfile, video.previewPath)
214 }
215
216 await completeWebVideoFilesCheck({ server, originServer, videoUUID: video.uuid, ...pick(attributes, [ 'fixture', 'files' ]) })
217}
218
219async function checkVideoFilesWereRemoved (options: {
220 server: PeerTubeServer
221 video: VideoDetails
222 captions?: VideoCaption[]
223 onlyVideoFiles?: boolean // default false
224}) {
225 const { video, server, captions = [], onlyVideoFiles = false } = options
226
227 const webVideoFiles = video.files || []
228 const hlsFiles = video.streamingPlaylists[0]?.files || []
229
230 const thumbnailName = basename(video.thumbnailPath)
231 const previewName = basename(video.previewPath)
232
233 const torrentNames = webVideoFiles.concat(hlsFiles).map(f => basename(f.torrentUrl))
234
235 const captionNames = captions.map(c => basename(c.captionPath))
236
237 const webVideoFilenames = webVideoFiles.map(f => basename(f.fileUrl))
238 const hlsFilenames = hlsFiles.map(f => basename(f.fileUrl))
239
240 let directories: { [ directory: string ]: string[] } = {
241 videos: webVideoFilenames,
242 redundancy: webVideoFilenames,
243 [join('playlists', 'hls')]: hlsFilenames,
244 [join('redundancy', 'hls')]: hlsFilenames
245 }
246
247 if (onlyVideoFiles !== true) {
248 directories = {
249 ...directories,
250
251 thumbnails: [ thumbnailName ],
252 previews: [ previewName ],
253 torrents: torrentNames,
254 captions: captionNames
255 }
256 }
257
258 for (const directory of Object.keys(directories)) {
259 const directoryPath = server.servers.buildDirectory(directory)
260
261 const directoryExists = await pathExists(directoryPath)
262 if (directoryExists === false) continue
263
264 const existingFiles = await readdir(directoryPath)
265 for (const existingFile of existingFiles) {
266 for (const shouldNotExist of directories[directory]) {
267 expect(existingFile, `File ${existingFile} should not exist in ${directoryPath}`).to.not.contain(shouldNotExist)
268 }
269 }
270 }
271}
272
273async function saveVideoInServers (servers: PeerTubeServer[], uuid: string) {
274 for (const server of servers) {
275 server.store.videoDetails = await server.videos.get({ id: uuid })
276 }
277}
278
279function checkUploadVideoParam (options: {
280 server: PeerTubeServer
281 token: string
282 attributes: Partial<VideoEdit>
283 expectedStatus?: HttpStatusCodeType
284 completedExpectedStatus?: HttpStatusCodeType
285 mode?: 'legacy' | 'resumable'
286}) {
287 const { server, token, attributes, completedExpectedStatus, expectedStatus, mode = 'legacy' } = options
288
289 return mode === 'legacy'
290 ? server.videos.buildLegacyUpload({ token, attributes, expectedStatus: expectedStatus || completedExpectedStatus })
291 : server.videos.buildResumeUpload({
292 token,
293 attributes,
294 expectedStatus,
295 completedExpectedStatus,
296 path: '/api/v1/videos/upload-resumable'
297 })
298}
299
300// serverNumber starts from 1
301async function uploadRandomVideoOnServers (
302 servers: PeerTubeServer[],
303 serverNumber: number,
304 additionalParams?: VideoEdit & { prefixName?: string }
305) {
306 const server = servers.find(s => s.serverNumber === serverNumber)
307 const res = await server.videos.randomUpload({ wait: false, additionalParams })
308
309 await waitJobs(servers)
310
311 return res
312}
313
314// ---------------------------------------------------------------------------
315
316export {
317 completeVideoCheck,
318 completeWebVideoFilesCheck,
319 checkUploadVideoParam,
320 uploadRandomVideoOnServers,
321 checkVideoFilesWereRemoved,
322 saveVideoInServers
323}