transcoder.ts 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733
  1. /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
  2. import 'mocha'
  3. import * as chai from 'chai'
  4. import { omit } from 'lodash'
  5. import { canDoQuickTranscode } from '@server/helpers/ffmpeg'
  6. import { generateHighBitrateVideo, generateVideoWithFramerate, getAllFiles } from '@server/tests/shared'
  7. import { buildAbsoluteFixturePath, getMaxBitrate, getMinLimitBitrate } from '@shared/core-utils'
  8. import {
  9. getAudioStream,
  10. buildFileMetadata,
  11. getVideoStreamBitrate,
  12. getVideoStreamFPS,
  13. getVideoStreamDimensionsInfo,
  14. hasAudioStream
  15. } from '@shared/extra-utils'
  16. import { HttpStatusCode, VideoState } from '@shared/models'
  17. import {
  18. cleanupTests,
  19. createMultipleServers,
  20. doubleFollow,
  21. makeGetRequest,
  22. PeerTubeServer,
  23. setAccessTokensToServers,
  24. waitJobs,
  25. webtorrentAdd
  26. } from '@shared/server-commands'
  27. const expect = chai.expect
  28. function updateConfigForTranscoding (server: PeerTubeServer) {
  29. return server.config.updateCustomSubConfig({
  30. newConfig: {
  31. transcoding: {
  32. enabled: true,
  33. allowAdditionalExtensions: true,
  34. allowAudioFiles: true,
  35. hls: { enabled: true },
  36. webtorrent: { enabled: true },
  37. resolutions: {
  38. '0p': false,
  39. '144p': true,
  40. '240p': true,
  41. '360p': true,
  42. '480p': true,
  43. '720p': true,
  44. '1080p': true,
  45. '1440p': true,
  46. '2160p': true
  47. }
  48. }
  49. }
  50. })
  51. }
  52. describe('Test video transcoding', function () {
  53. let servers: PeerTubeServer[] = []
  54. let video4k: string
  55. before(async function () {
  56. this.timeout(30_000)
  57. // Run servers
  58. servers = await createMultipleServers(2)
  59. await setAccessTokensToServers(servers)
  60. await doubleFollow(servers[0], servers[1])
  61. await updateConfigForTranscoding(servers[1])
  62. })
  63. describe('Basic transcoding (or not)', function () {
  64. it('Should not transcode video on server 1', async function () {
  65. this.timeout(60_000)
  66. const attributes = {
  67. name: 'my super name for server 1',
  68. description: 'my super description for server 1',
  69. fixture: 'video_short.webm'
  70. }
  71. await servers[0].videos.upload({ attributes })
  72. await waitJobs(servers)
  73. for (const server of servers) {
  74. const { data } = await server.videos.list()
  75. const video = data[0]
  76. const videoDetails = await server.videos.get({ id: video.id })
  77. expect(videoDetails.files).to.have.lengthOf(1)
  78. const magnetUri = videoDetails.files[0].magnetUri
  79. expect(magnetUri).to.match(/\.webm/)
  80. const torrent = await webtorrentAdd(magnetUri, true)
  81. expect(torrent.files).to.be.an('array')
  82. expect(torrent.files.length).to.equal(1)
  83. expect(torrent.files[0].path).match(/\.webm$/)
  84. }
  85. })
  86. it('Should transcode video on server 2', async function () {
  87. this.timeout(120_000)
  88. const attributes = {
  89. name: 'my super name for server 2',
  90. description: 'my super description for server 2',
  91. fixture: 'video_short.webm'
  92. }
  93. await servers[1].videos.upload({ attributes })
  94. await waitJobs(servers)
  95. for (const server of servers) {
  96. const { data } = await server.videos.list()
  97. const video = data.find(v => v.name === attributes.name)
  98. const videoDetails = await server.videos.get({ id: video.id })
  99. expect(videoDetails.files).to.have.lengthOf(5)
  100. const magnetUri = videoDetails.files[0].magnetUri
  101. expect(magnetUri).to.match(/\.mp4/)
  102. const torrent = await webtorrentAdd(magnetUri, true)
  103. expect(torrent.files).to.be.an('array')
  104. expect(torrent.files.length).to.equal(1)
  105. expect(torrent.files[0].path).match(/\.mp4$/)
  106. }
  107. })
  108. it('Should wait for transcoding before publishing the video', async function () {
  109. this.timeout(160_000)
  110. {
  111. // Upload the video, but wait transcoding
  112. const attributes = {
  113. name: 'waiting video',
  114. fixture: 'video_short1.webm',
  115. waitTranscoding: true
  116. }
  117. const { uuid } = await servers[1].videos.upload({ attributes })
  118. const videoId = uuid
  119. // Should be in transcode state
  120. const body = await servers[1].videos.get({ id: videoId })
  121. expect(body.name).to.equal('waiting video')
  122. expect(body.state.id).to.equal(VideoState.TO_TRANSCODE)
  123. expect(body.state.label).to.equal('To transcode')
  124. expect(body.waitTranscoding).to.be.true
  125. {
  126. // Should have my video
  127. const { data } = await servers[1].videos.listMyVideos()
  128. const videoToFindInMine = data.find(v => v.name === attributes.name)
  129. expect(videoToFindInMine).not.to.be.undefined
  130. expect(videoToFindInMine.state.id).to.equal(VideoState.TO_TRANSCODE)
  131. expect(videoToFindInMine.state.label).to.equal('To transcode')
  132. expect(videoToFindInMine.waitTranscoding).to.be.true
  133. }
  134. {
  135. // Should not list this video
  136. const { data } = await servers[1].videos.list()
  137. const videoToFindInList = data.find(v => v.name === attributes.name)
  138. expect(videoToFindInList).to.be.undefined
  139. }
  140. // Server 1 should not have the video yet
  141. await servers[0].videos.get({ id: videoId, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
  142. }
  143. await waitJobs(servers)
  144. for (const server of servers) {
  145. const { data } = await server.videos.list()
  146. const videoToFind = data.find(v => v.name === 'waiting video')
  147. expect(videoToFind).not.to.be.undefined
  148. const videoDetails = await server.videos.get({ id: videoToFind.id })
  149. expect(videoDetails.state.id).to.equal(VideoState.PUBLISHED)
  150. expect(videoDetails.state.label).to.equal('Published')
  151. expect(videoDetails.waitTranscoding).to.be.true
  152. }
  153. })
  154. it('Should accept and transcode additional extensions', async function () {
  155. this.timeout(300_000)
  156. for (const fixture of [ 'video_short.mkv', 'video_short.avi' ]) {
  157. const attributes = {
  158. name: fixture,
  159. fixture
  160. }
  161. await servers[1].videos.upload({ attributes })
  162. await waitJobs(servers)
  163. for (const server of servers) {
  164. const { data } = await server.videos.list()
  165. const video = data.find(v => v.name === attributes.name)
  166. const videoDetails = await server.videos.get({ id: video.id })
  167. expect(videoDetails.files).to.have.lengthOf(5)
  168. const magnetUri = videoDetails.files[0].magnetUri
  169. expect(magnetUri).to.contain('.mp4')
  170. }
  171. }
  172. })
  173. it('Should transcode a 4k video', async function () {
  174. this.timeout(200_000)
  175. const attributes = {
  176. name: '4k video',
  177. fixture: 'video_short_4k.mp4'
  178. }
  179. const { uuid } = await servers[1].videos.upload({ attributes })
  180. video4k = uuid
  181. await waitJobs(servers)
  182. const resolutions = [ 144, 240, 360, 480, 720, 1080, 1440, 2160 ]
  183. for (const server of servers) {
  184. const videoDetails = await server.videos.get({ id: video4k })
  185. expect(videoDetails.files).to.have.lengthOf(resolutions.length)
  186. for (const r of resolutions) {
  187. expect(videoDetails.files.find(f => f.resolution.id === r)).to.not.be.undefined
  188. expect(videoDetails.streamingPlaylists[0].files.find(f => f.resolution.id === r)).to.not.be.undefined
  189. }
  190. }
  191. })
  192. })
  193. describe('Audio transcoding', function () {
  194. it('Should transcode high bit rate mp3 to proper bit rate', async function () {
  195. this.timeout(60_000)
  196. const attributes = {
  197. name: 'mp3_256k',
  198. fixture: 'video_short_mp3_256k.mp4'
  199. }
  200. await servers[1].videos.upload({ attributes })
  201. await waitJobs(servers)
  202. for (const server of servers) {
  203. const { data } = await server.videos.list()
  204. const video = data.find(v => v.name === attributes.name)
  205. const videoDetails = await server.videos.get({ id: video.id })
  206. expect(videoDetails.files).to.have.lengthOf(5)
  207. const file = videoDetails.files.find(f => f.resolution.id === 240)
  208. const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
  209. const probe = await getAudioStream(path)
  210. if (probe.audioStream) {
  211. expect(probe.audioStream['codec_name']).to.be.equal('aac')
  212. expect(probe.audioStream['bit_rate']).to.be.at.most(384 * 8000)
  213. } else {
  214. this.fail('Could not retrieve the audio stream on ' + probe.absolutePath)
  215. }
  216. }
  217. })
  218. it('Should transcode video with no audio and have no audio itself', async function () {
  219. this.timeout(60_000)
  220. const attributes = {
  221. name: 'no_audio',
  222. fixture: 'video_short_no_audio.mp4'
  223. }
  224. await servers[1].videos.upload({ attributes })
  225. await waitJobs(servers)
  226. for (const server of servers) {
  227. const { data } = await server.videos.list()
  228. const video = data.find(v => v.name === attributes.name)
  229. const videoDetails = await server.videos.get({ id: video.id })
  230. const file = videoDetails.files.find(f => f.resolution.id === 240)
  231. const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
  232. expect(await hasAudioStream(path)).to.be.false
  233. }
  234. })
  235. it('Should leave the audio untouched, but properly transcode the video', async function () {
  236. this.timeout(60_000)
  237. const attributes = {
  238. name: 'untouched_audio',
  239. fixture: 'video_short.mp4'
  240. }
  241. await servers[1].videos.upload({ attributes })
  242. await waitJobs(servers)
  243. for (const server of servers) {
  244. const { data } = await server.videos.list()
  245. const video = data.find(v => v.name === attributes.name)
  246. const videoDetails = await server.videos.get({ id: video.id })
  247. expect(videoDetails.files).to.have.lengthOf(5)
  248. const fixturePath = buildAbsoluteFixturePath(attributes.fixture)
  249. const fixtureVideoProbe = await getAudioStream(fixturePath)
  250. const file = videoDetails.files.find(f => f.resolution.id === 240)
  251. const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
  252. const videoProbe = await getAudioStream(path)
  253. if (videoProbe.audioStream && fixtureVideoProbe.audioStream) {
  254. const toOmit = [ 'max_bit_rate', 'duration', 'duration_ts', 'nb_frames', 'start_time', 'start_pts' ]
  255. expect(omit(videoProbe.audioStream, toOmit)).to.be.deep.equal(omit(fixtureVideoProbe.audioStream, toOmit))
  256. } else {
  257. this.fail('Could not retrieve the audio stream on ' + videoProbe.absolutePath)
  258. }
  259. }
  260. })
  261. })
  262. describe('Audio upload', function () {
  263. function runSuite (mode: 'legacy' | 'resumable') {
  264. before(async function () {
  265. await servers[1].config.updateCustomSubConfig({
  266. newConfig: {
  267. transcoding: {
  268. hls: { enabled: true },
  269. webtorrent: { enabled: true },
  270. resolutions: {
  271. '0p': false,
  272. '144p': false,
  273. '240p': false,
  274. '360p': false,
  275. '480p': false,
  276. '720p': false,
  277. '1080p': false,
  278. '1440p': false,
  279. '2160p': false
  280. }
  281. }
  282. }
  283. })
  284. })
  285. it('Should merge an audio file with the preview file', async function () {
  286. this.timeout(60_000)
  287. const attributes = { name: 'audio_with_preview', previewfile: 'preview.jpg', fixture: 'sample.ogg' }
  288. await servers[1].videos.upload({ attributes, mode })
  289. await waitJobs(servers)
  290. for (const server of servers) {
  291. const { data } = await server.videos.list()
  292. const video = data.find(v => v.name === 'audio_with_preview')
  293. const videoDetails = await server.videos.get({ id: video.id })
  294. expect(videoDetails.files).to.have.lengthOf(1)
  295. await makeGetRequest({ url: server.url, path: videoDetails.thumbnailPath, expectedStatus: HttpStatusCode.OK_200 })
  296. await makeGetRequest({ url: server.url, path: videoDetails.previewPath, expectedStatus: HttpStatusCode.OK_200 })
  297. const magnetUri = videoDetails.files[0].magnetUri
  298. expect(magnetUri).to.contain('.mp4')
  299. }
  300. })
  301. it('Should upload an audio file and choose a default background image', async function () {
  302. this.timeout(60_000)
  303. const attributes = { name: 'audio_without_preview', fixture: 'sample.ogg' }
  304. await servers[1].videos.upload({ attributes, mode })
  305. await waitJobs(servers)
  306. for (const server of servers) {
  307. const { data } = await server.videos.list()
  308. const video = data.find(v => v.name === 'audio_without_preview')
  309. const videoDetails = await server.videos.get({ id: video.id })
  310. expect(videoDetails.files).to.have.lengthOf(1)
  311. await makeGetRequest({ url: server.url, path: videoDetails.thumbnailPath, expectedStatus: HttpStatusCode.OK_200 })
  312. await makeGetRequest({ url: server.url, path: videoDetails.previewPath, expectedStatus: HttpStatusCode.OK_200 })
  313. const magnetUri = videoDetails.files[0].magnetUri
  314. expect(magnetUri).to.contain('.mp4')
  315. }
  316. })
  317. it('Should upload an audio file and create an audio version only', async function () {
  318. this.timeout(60_000)
  319. await servers[1].config.updateCustomSubConfig({
  320. newConfig: {
  321. transcoding: {
  322. hls: { enabled: true },
  323. webtorrent: { enabled: true },
  324. resolutions: {
  325. '0p': true,
  326. '144p': false,
  327. '240p': false,
  328. '360p': false
  329. }
  330. }
  331. }
  332. })
  333. const attributes = { name: 'audio_with_preview', previewfile: 'preview.jpg', fixture: 'sample.ogg' }
  334. const { id } = await servers[1].videos.upload({ attributes, mode })
  335. await waitJobs(servers)
  336. for (const server of servers) {
  337. const videoDetails = await server.videos.get({ id })
  338. for (const files of [ videoDetails.files, videoDetails.streamingPlaylists[0].files ]) {
  339. expect(files).to.have.lengthOf(2)
  340. expect(files.find(f => f.resolution.id === 0)).to.not.be.undefined
  341. }
  342. }
  343. await updateConfigForTranscoding(servers[1])
  344. })
  345. }
  346. describe('Legacy upload', function () {
  347. runSuite('legacy')
  348. })
  349. describe('Resumable upload', function () {
  350. runSuite('resumable')
  351. })
  352. })
  353. describe('Framerate', function () {
  354. it('Should transcode a 60 FPS video', async function () {
  355. this.timeout(60_000)
  356. const attributes = {
  357. name: 'my super 30fps name for server 2',
  358. description: 'my super 30fps description for server 2',
  359. fixture: '60fps_720p_small.mp4'
  360. }
  361. await servers[1].videos.upload({ attributes })
  362. await waitJobs(servers)
  363. for (const server of servers) {
  364. const { data } = await server.videos.list()
  365. const video = data.find(v => v.name === attributes.name)
  366. const videoDetails = await server.videos.get({ id: video.id })
  367. expect(videoDetails.files).to.have.lengthOf(5)
  368. expect(videoDetails.files[0].fps).to.be.above(58).and.below(62)
  369. expect(videoDetails.files[1].fps).to.be.below(31)
  370. expect(videoDetails.files[2].fps).to.be.below(31)
  371. expect(videoDetails.files[3].fps).to.be.below(31)
  372. expect(videoDetails.files[4].fps).to.be.below(31)
  373. for (const resolution of [ 144, 240, 360, 480 ]) {
  374. const file = videoDetails.files.find(f => f.resolution.id === resolution)
  375. const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
  376. const fps = await getVideoStreamFPS(path)
  377. expect(fps).to.be.below(31)
  378. }
  379. const file = videoDetails.files.find(f => f.resolution.id === 720)
  380. const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
  381. const fps = await getVideoStreamFPS(path)
  382. expect(fps).to.be.above(58).and.below(62)
  383. }
  384. })
  385. it('Should downscale to the closest divisor standard framerate', async function () {
  386. this.timeout(200_000)
  387. let tempFixturePath: string
  388. {
  389. tempFixturePath = await generateVideoWithFramerate(59)
  390. const fps = await getVideoStreamFPS(tempFixturePath)
  391. expect(fps).to.be.equal(59)
  392. }
  393. const attributes = {
  394. name: '59fps video',
  395. description: '59fps video',
  396. fixture: tempFixturePath
  397. }
  398. await servers[1].videos.upload({ attributes })
  399. await waitJobs(servers)
  400. for (const server of servers) {
  401. const { data } = await server.videos.list()
  402. const { id } = data.find(v => v.name === attributes.name)
  403. const video = await server.videos.get({ id })
  404. {
  405. const file = video.files.find(f => f.resolution.id === 240)
  406. const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
  407. const fps = await getVideoStreamFPS(path)
  408. expect(fps).to.be.equal(25)
  409. }
  410. {
  411. const file = video.files.find(f => f.resolution.id === 720)
  412. const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
  413. const fps = await getVideoStreamFPS(path)
  414. expect(fps).to.be.equal(59)
  415. }
  416. }
  417. })
  418. })
  419. describe('Bitrate control', function () {
  420. it('Should respect maximum bitrate values', async function () {
  421. this.timeout(160_000)
  422. const tempFixturePath = await generateHighBitrateVideo()
  423. const attributes = {
  424. name: 'high bitrate video',
  425. description: 'high bitrate video',
  426. fixture: tempFixturePath
  427. }
  428. await servers[1].videos.upload({ attributes })
  429. await waitJobs(servers)
  430. for (const server of servers) {
  431. const { data } = await server.videos.list()
  432. const { id } = data.find(v => v.name === attributes.name)
  433. const video = await server.videos.get({ id })
  434. for (const resolution of [ 240, 360, 480, 720, 1080 ]) {
  435. const file = video.files.find(f => f.resolution.id === resolution)
  436. const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
  437. const bitrate = await getVideoStreamBitrate(path)
  438. const fps = await getVideoStreamFPS(path)
  439. const dataResolution = await getVideoStreamDimensionsInfo(path)
  440. expect(resolution).to.equal(resolution)
  441. const maxBitrate = getMaxBitrate({ ...dataResolution, fps })
  442. expect(bitrate).to.be.below(maxBitrate)
  443. }
  444. }
  445. })
  446. it('Should not transcode to an higher bitrate than the original file but above our low limit', async function () {
  447. this.timeout(160_000)
  448. const newConfig = {
  449. transcoding: {
  450. enabled: true,
  451. resolutions: {
  452. '144p': true,
  453. '240p': true,
  454. '360p': true,
  455. '480p': true,
  456. '720p': true,
  457. '1080p': true,
  458. '1440p': true,
  459. '2160p': true
  460. },
  461. webtorrent: { enabled: true },
  462. hls: { enabled: true }
  463. }
  464. }
  465. await servers[1].config.updateCustomSubConfig({ newConfig })
  466. const attributes = {
  467. name: 'low bitrate',
  468. fixture: 'low-bitrate.mp4'
  469. }
  470. const { id } = await servers[1].videos.upload({ attributes })
  471. await waitJobs(servers)
  472. const video = await servers[1].videos.get({ id })
  473. const resolutions = [ 240, 360, 480, 720, 1080 ]
  474. for (const r of resolutions) {
  475. const file = video.files.find(f => f.resolution.id === r)
  476. const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
  477. const bitrate = await getVideoStreamBitrate(path)
  478. const inputBitrate = 60_000
  479. const limit = getMinLimitBitrate({ fps: 10, ratio: 1, resolution: r })
  480. let belowValue = Math.max(inputBitrate, limit)
  481. belowValue += belowValue * 0.20 // Apply 20% margin because bitrate control is not very precise
  482. expect(bitrate, `${path} not below ${limit}`).to.be.below(belowValue)
  483. }
  484. })
  485. })
  486. describe('FFprobe', function () {
  487. it('Should provide valid ffprobe data', async function () {
  488. this.timeout(160_000)
  489. const videoUUID = (await servers[1].videos.quickUpload({ name: 'ffprobe data' })).uuid
  490. await waitJobs(servers)
  491. {
  492. const video = await servers[1].videos.get({ id: videoUUID })
  493. const file = video.files.find(f => f.resolution.id === 240)
  494. const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
  495. const metadata = await buildFileMetadata(path)
  496. // expected format properties
  497. for (const p of [
  498. 'tags.encoder',
  499. 'format_long_name',
  500. 'size',
  501. 'bit_rate'
  502. ]) {
  503. expect(metadata.format).to.have.nested.property(p)
  504. }
  505. // expected stream properties
  506. for (const p of [
  507. 'codec_long_name',
  508. 'profile',
  509. 'width',
  510. 'height',
  511. 'display_aspect_ratio',
  512. 'avg_frame_rate',
  513. 'pix_fmt'
  514. ]) {
  515. expect(metadata.streams[0]).to.have.nested.property(p)
  516. }
  517. expect(metadata).to.not.have.nested.property('format.filename')
  518. }
  519. for (const server of servers) {
  520. const videoDetails = await server.videos.get({ id: videoUUID })
  521. const videoFiles = getAllFiles(videoDetails)
  522. expect(videoFiles).to.have.lengthOf(10)
  523. for (const file of videoFiles) {
  524. expect(file.metadata).to.be.undefined
  525. expect(file.metadataUrl).to.exist
  526. expect(file.metadataUrl).to.contain(servers[1].url)
  527. expect(file.metadataUrl).to.contain(videoUUID)
  528. const metadata = await server.videos.getFileMetadata({ url: file.metadataUrl })
  529. expect(metadata).to.have.nested.property('format.size')
  530. }
  531. }
  532. })
  533. it('Should correctly detect if quick transcode is possible', async function () {
  534. this.timeout(10_000)
  535. expect(await canDoQuickTranscode(buildAbsoluteFixturePath('video_short.mp4'))).to.be.true
  536. expect(await canDoQuickTranscode(buildAbsoluteFixturePath('video_short.webm'))).to.be.false
  537. })
  538. })
  539. describe('Transcoding job queue', function () {
  540. it('Should have the appropriate priorities for transcoding jobs', async function () {
  541. const body = await servers[1].jobs.list({
  542. start: 0,
  543. count: 100,
  544. sort: 'createdAt',
  545. jobType: 'video-transcoding'
  546. })
  547. const jobs = body.data
  548. const transcodingJobs = jobs.filter(j => j.data.videoUUID === video4k)
  549. expect(transcodingJobs).to.have.lengthOf(16)
  550. const hlsJobs = transcodingJobs.filter(j => j.data.type === 'new-resolution-to-hls')
  551. const webtorrentJobs = transcodingJobs.filter(j => j.data.type === 'new-resolution-to-webtorrent')
  552. const optimizeJobs = transcodingJobs.filter(j => j.data.type === 'optimize-to-webtorrent')
  553. expect(hlsJobs).to.have.lengthOf(8)
  554. expect(webtorrentJobs).to.have.lengthOf(7)
  555. expect(optimizeJobs).to.have.lengthOf(1)
  556. for (const j of optimizeJobs.concat(hlsJobs.concat(webtorrentJobs))) {
  557. expect(j.priority).to.be.greaterThan(100)
  558. expect(j.priority).to.be.lessThan(150)
  559. }
  560. })
  561. })
  562. after(async function () {
  563. await cleanupTests(servers)
  564. })
  565. })