]>
Commit | Line | Data |
---|---|---|
c55e3d72 C |
1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/no-floating-promises */ |
2 | ||
06aad801 | 3 | import { expect } from 'chai' |
c55e3d72 C |
4 | import { pathExists, readdir } from 'fs-extra' |
5 | import { basename, join } from 'path' | |
c77fdc60 | 6 | import { loadLanguages, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES } from '@server/initializers/constants' |
06aad801 | 7 | import { getLowercaseExtension, uuidRegex } from '@shared/core-utils' |
c55e3d72 C |
8 | import { HttpStatusCode, VideoCaption, VideoDetails } from '@shared/models' |
9 | import { makeRawRequest, PeerTubeServer, VideoEdit, waitJobs, webtorrentAdd } from '@shared/server-commands' | |
10 | import { dateIsValid, testImage } from './checks' | |
06aad801 | 11 | |
c77fdc60 C |
12 | loadLanguages() |
13 | ||
c55e3d72 | 14 | async function completeVideoCheck ( |
06aad801 | 15 | server: PeerTubeServer, |
16 | video: any, | |
17 | attributes: { | |
18 | name: string | |
19 | category: number | |
20 | licence: number | |
21 | language: string | |
22 | nsfw: boolean | |
23 | commentsEnabled: boolean | |
24 | downloadEnabled: boolean | |
25 | description: string | |
26 | publishedAt?: string | |
27 | support: string | |
28 | originallyPublishedAt?: string | |
29 | account: { | |
30 | name: string | |
31 | host: string | |
32 | } | |
33 | isLocal: boolean | |
34 | tags: string[] | |
35 | privacy: number | |
36 | likes?: number | |
37 | dislikes?: number | |
38 | duration: number | |
39 | channel: { | |
40 | displayName: string | |
41 | name: string | |
42 | description: string | |
43 | isLocal: boolean | |
44 | } | |
45 | fixture: string | |
46 | files: { | |
47 | resolution: number | |
48 | size: number | |
49 | }[] | |
50 | thumbnailfile?: string | |
51 | previewfile?: string | |
52 | } | |
53 | ) { | |
54 | if (!attributes.likes) attributes.likes = 0 | |
55 | if (!attributes.dislikes) attributes.dislikes = 0 | |
56 | ||
57 | const host = new URL(server.url).host | |
58 | const originHost = attributes.account.host | |
59 | ||
60 | expect(video.name).to.equal(attributes.name) | |
61 | expect(video.category.id).to.equal(attributes.category) | |
62 | expect(video.category.label).to.equal(attributes.category !== null ? VIDEO_CATEGORIES[attributes.category] : 'Misc') | |
63 | expect(video.licence.id).to.equal(attributes.licence) | |
64 | expect(video.licence.label).to.equal(attributes.licence !== null ? VIDEO_LICENCES[attributes.licence] : 'Unknown') | |
65 | expect(video.language.id).to.equal(attributes.language) | |
66 | expect(video.language.label).to.equal(attributes.language !== null ? VIDEO_LANGUAGES[attributes.language] : 'Unknown') | |
67 | expect(video.privacy.id).to.deep.equal(attributes.privacy) | |
68 | expect(video.privacy.label).to.deep.equal(VIDEO_PRIVACIES[attributes.privacy]) | |
69 | expect(video.nsfw).to.equal(attributes.nsfw) | |
70 | expect(video.description).to.equal(attributes.description) | |
71 | expect(video.account.id).to.be.a('number') | |
72 | expect(video.account.host).to.equal(attributes.account.host) | |
73 | expect(video.account.name).to.equal(attributes.account.name) | |
74 | expect(video.channel.displayName).to.equal(attributes.channel.displayName) | |
75 | expect(video.channel.name).to.equal(attributes.channel.name) | |
76 | expect(video.likes).to.equal(attributes.likes) | |
77 | expect(video.dislikes).to.equal(attributes.dislikes) | |
78 | expect(video.isLocal).to.equal(attributes.isLocal) | |
79 | expect(video.duration).to.equal(attributes.duration) | |
80 | expect(video.url).to.contain(originHost) | |
81 | expect(dateIsValid(video.createdAt)).to.be.true | |
82 | expect(dateIsValid(video.publishedAt)).to.be.true | |
83 | expect(dateIsValid(video.updatedAt)).to.be.true | |
84 | ||
85 | if (attributes.publishedAt) { | |
86 | expect(video.publishedAt).to.equal(attributes.publishedAt) | |
87 | } | |
88 | ||
89 | if (attributes.originallyPublishedAt) { | |
90 | expect(video.originallyPublishedAt).to.equal(attributes.originallyPublishedAt) | |
91 | } else { | |
92 | expect(video.originallyPublishedAt).to.be.null | |
93 | } | |
94 | ||
95 | const videoDetails = await server.videos.get({ id: video.uuid }) | |
96 | ||
97 | expect(videoDetails.files).to.have.lengthOf(attributes.files.length) | |
98 | expect(videoDetails.tags).to.deep.equal(attributes.tags) | |
99 | expect(videoDetails.account.name).to.equal(attributes.account.name) | |
100 | expect(videoDetails.account.host).to.equal(attributes.account.host) | |
101 | expect(video.channel.displayName).to.equal(attributes.channel.displayName) | |
102 | expect(video.channel.name).to.equal(attributes.channel.name) | |
103 | expect(videoDetails.channel.host).to.equal(attributes.account.host) | |
104 | expect(videoDetails.channel.isLocal).to.equal(attributes.channel.isLocal) | |
105 | expect(dateIsValid(videoDetails.channel.createdAt.toString())).to.be.true | |
106 | expect(dateIsValid(videoDetails.channel.updatedAt.toString())).to.be.true | |
107 | expect(videoDetails.commentsEnabled).to.equal(attributes.commentsEnabled) | |
108 | expect(videoDetails.downloadEnabled).to.equal(attributes.downloadEnabled) | |
109 | ||
110 | for (const attributeFile of attributes.files) { | |
111 | const file = videoDetails.files.find(f => f.resolution.id === attributeFile.resolution) | |
112 | expect(file).not.to.be.undefined | |
113 | ||
114 | let extension = getLowercaseExtension(attributes.fixture) | |
115 | // Transcoding enabled: extension will always be .mp4 | |
116 | if (attributes.files.length > 1) extension = '.mp4' | |
117 | ||
118 | expect(file.magnetUri).to.have.lengthOf.above(2) | |
119 | ||
120 | expect(file.torrentDownloadUrl).to.match(new RegExp(`http://${host}/download/torrents/${uuidRegex}-${file.resolution.id}.torrent`)) | |
121 | expect(file.torrentUrl).to.match(new RegExp(`http://${host}/lazy-static/torrents/${uuidRegex}-${file.resolution.id}.torrent`)) | |
122 | ||
123 | expect(file.fileUrl).to.match(new RegExp(`http://${originHost}/static/webseed/${uuidRegex}-${file.resolution.id}${extension}`)) | |
124 | expect(file.fileDownloadUrl).to.match(new RegExp(`http://${originHost}/download/videos/${uuidRegex}-${file.resolution.id}${extension}`)) | |
125 | ||
126 | await Promise.all([ | |
127 | makeRawRequest(file.torrentUrl, 200), | |
128 | makeRawRequest(file.torrentDownloadUrl, 200), | |
129 | makeRawRequest(file.metadataUrl, 200) | |
130 | ]) | |
131 | ||
132 | expect(file.resolution.id).to.equal(attributeFile.resolution) | |
133 | expect(file.resolution.label).to.equal(attributeFile.resolution + 'p') | |
134 | ||
135 | const minSize = attributeFile.size - ((10 * attributeFile.size) / 100) | |
136 | const maxSize = attributeFile.size + ((10 * attributeFile.size) / 100) | |
137 | expect( | |
138 | file.size, | |
139 | 'File size for resolution ' + file.resolution.label + ' outside confidence interval (' + minSize + '> size <' + maxSize + ')' | |
140 | ).to.be.above(minSize).and.below(maxSize) | |
141 | ||
142 | const torrent = await webtorrentAdd(file.magnetUri, true) | |
143 | expect(torrent.files).to.be.an('array') | |
144 | expect(torrent.files.length).to.equal(1) | |
145 | expect(torrent.files[0].path).to.exist.and.to.not.equal('') | |
146 | } | |
147 | ||
148 | expect(videoDetails.thumbnailPath).to.exist | |
149 | await testImage(server.url, attributes.thumbnailfile || attributes.fixture, videoDetails.thumbnailPath) | |
150 | ||
151 | if (attributes.previewfile) { | |
152 | expect(videoDetails.previewPath).to.exist | |
153 | await testImage(server.url, attributes.previewfile, videoDetails.previewPath) | |
154 | } | |
155 | } | |
c55e3d72 C |
156 | |
157 | async function checkVideoFilesWereRemoved (options: { | |
158 | server: PeerTubeServer | |
159 | video: VideoDetails | |
160 | captions?: VideoCaption[] | |
161 | onlyVideoFiles?: boolean // default false | |
162 | }) { | |
163 | const { video, server, captions = [], onlyVideoFiles = false } = options | |
164 | ||
165 | const webtorrentFiles = video.files || [] | |
166 | const hlsFiles = video.streamingPlaylists[0]?.files || [] | |
167 | ||
168 | const thumbnailName = basename(video.thumbnailPath) | |
169 | const previewName = basename(video.previewPath) | |
170 | ||
171 | const torrentNames = webtorrentFiles.concat(hlsFiles).map(f => basename(f.torrentUrl)) | |
172 | ||
173 | const captionNames = captions.map(c => basename(c.captionPath)) | |
174 | ||
175 | const webtorrentFilenames = webtorrentFiles.map(f => basename(f.fileUrl)) | |
176 | const hlsFilenames = hlsFiles.map(f => basename(f.fileUrl)) | |
177 | ||
178 | let directories: { [ directory: string ]: string[] } = { | |
179 | videos: webtorrentFilenames, | |
180 | redundancy: webtorrentFilenames, | |
181 | [join('playlists', 'hls')]: hlsFilenames, | |
182 | [join('redundancy', 'hls')]: hlsFilenames | |
183 | } | |
184 | ||
185 | if (onlyVideoFiles !== true) { | |
186 | directories = { | |
187 | ...directories, | |
188 | ||
189 | thumbnails: [ thumbnailName ], | |
190 | previews: [ previewName ], | |
191 | torrents: torrentNames, | |
192 | captions: captionNames | |
193 | } | |
194 | } | |
195 | ||
196 | for (const directory of Object.keys(directories)) { | |
197 | const directoryPath = server.servers.buildDirectory(directory) | |
198 | ||
199 | const directoryExists = await pathExists(directoryPath) | |
200 | if (directoryExists === false) continue | |
201 | ||
202 | const existingFiles = await readdir(directoryPath) | |
203 | for (const existingFile of existingFiles) { | |
204 | for (const shouldNotExist of directories[directory]) { | |
205 | expect(existingFile, `File ${existingFile} should not exist in ${directoryPath}`).to.not.contain(shouldNotExist) | |
206 | } | |
207 | } | |
208 | } | |
209 | } | |
210 | ||
211 | async function saveVideoInServers (servers: PeerTubeServer[], uuid: string) { | |
212 | for (const server of servers) { | |
213 | server.store.videoDetails = await server.videos.get({ id: uuid }) | |
214 | } | |
215 | } | |
216 | ||
217 | function checkUploadVideoParam ( | |
218 | server: PeerTubeServer, | |
219 | token: string, | |
220 | attributes: Partial<VideoEdit>, | |
221 | expectedStatus = HttpStatusCode.OK_200, | |
222 | mode: 'legacy' | 'resumable' = 'legacy' | |
223 | ) { | |
224 | return mode === 'legacy' | |
225 | ? server.videos.buildLegacyUpload({ token, attributes, expectedStatus }) | |
226 | : server.videos.buildResumeUpload({ token, attributes, expectedStatus }) | |
227 | } | |
228 | ||
229 | // serverNumber starts from 1 | |
230 | async function uploadRandomVideoOnServers ( | |
231 | servers: PeerTubeServer[], | |
232 | serverNumber: number, | |
233 | additionalParams?: VideoEdit & { prefixName?: string } | |
234 | ) { | |
235 | const server = servers.find(s => s.serverNumber === serverNumber) | |
236 | const res = await server.videos.randomUpload({ wait: false, additionalParams }) | |
237 | ||
238 | await waitJobs(servers) | |
239 | ||
240 | return res | |
241 | } | |
242 | ||
c729caf6 C |
243 | function getAllFiles (video: VideoDetails) { |
244 | const files = video.files | |
245 | ||
246 | if (video.streamingPlaylists[0]) { | |
247 | return files.concat(video.streamingPlaylists[0].files) | |
248 | } | |
249 | ||
250 | return files | |
251 | } | |
252 | ||
c55e3d72 C |
253 | // --------------------------------------------------------------------------- |
254 | ||
255 | export { | |
256 | completeVideoCheck, | |
257 | checkUploadVideoParam, | |
258 | uploadRandomVideoOnServers, | |
259 | checkVideoFilesWereRemoved, | |
c729caf6 C |
260 | saveVideoInServers, |
261 | getAllFiles | |
c55e3d72 | 262 | } |