aboutsummaryrefslogblamecommitdiffhomepage
path: root/server/tests/api/videos/video-source.ts
blob: 8669f342e11ec35a5802a7ebd48ef4ef88a4e32d (plain) (tree)
1
                             
















                                                                                              
 





                                                       

                            









                                              
 



                                                                    

    








                                                                                                                    
 


                                                                    
 


















                                                                                                                       

    






























































































































































































































































































































































                                                                                                                                         
 


                                                                                  
 





                                                                                 


                           
                               

    
import { expect } from 'chai'
import { expectStartWith } from '@server/tests/shared'
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
import { areMockObjectStorageTestsDisabled, getAllFiles } from '@shared/core-utils'
import { HttpStatusCode } from '@shared/models'
import {
  cleanupTests,
  createMultipleServers,
  doubleFollow,
  makeGetRequest,
  makeRawRequest,
  ObjectStorageCommand,
  PeerTubeServer,
  setAccessTokensToServers,
  setDefaultAccountAvatar,
  setDefaultVideoChannel,
  waitJobs
} from '@shared/server-commands'

describe('Test a video file replacement', function () {
  let servers: PeerTubeServer[] = []

  let replaceDate: Date
  let userToken: string
  let uuid: string

  before(async function () {
    this.timeout(50000)

    servers = await createMultipleServers(2)

    // Get the access tokens
    await setAccessTokensToServers(servers)
    await setDefaultVideoChannel(servers)
    await setDefaultAccountAvatar(servers)

    await servers[0].config.enableFileUpdate()

    userToken = await servers[0].users.generateUserAndToken('user1')

    // Server 1 and server 2 follow each other
    await doubleFollow(servers[0], servers[1])
  })

  describe('Getting latest video source', () => {
    const fixture = 'video_short.webm'
    const uuids: string[] = []

    it('Should get the source filename with legacy upload', async function () {
      this.timeout(30000)

      const { uuid } = await servers[0].videos.upload({ attributes: { name: 'my video', fixture }, mode: 'legacy' })
      uuids.push(uuid)

      const source = await servers[0].videos.getSource({ id: uuid })
      expect(source.filename).to.equal(fixture)
    })

    it('Should get the source filename with resumable upload', async function () {
      this.timeout(30000)

      const { uuid } = await servers[0].videos.upload({ attributes: { name: 'my video', fixture }, mode: 'resumable' })
      uuids.push(uuid)

      const source = await servers[0].videos.getSource({ id: uuid })
      expect(source.filename).to.equal(fixture)
    })

    after(async function () {
      this.timeout(60000)

      for (const uuid of uuids) {
        await servers[0].videos.remove({ id: uuid })
      }

      await waitJobs(servers)
    })
  })

  describe('Updating video source', function () {

    describe('Filesystem', function () {

      it('Should replace a video file with transcoding disabled', async function () {
        this.timeout(120000)

        await servers[0].config.disableTranscoding()

        const { uuid } = await servers[0].videos.quickUpload({ name: 'fs without transcoding', fixture: 'video_short_720p.mp4' })
        await waitJobs(servers)

        for (const server of servers) {
          const video = await server.videos.get({ id: uuid })

          const files = getAllFiles(video)
          expect(files).to.have.lengthOf(1)
          expect(files[0].resolution.id).to.equal(720)
        }

        await servers[0].videos.replaceSourceFile({ videoId: uuid, fixture: 'video_short_360p.mp4' })
        await waitJobs(servers)

        for (const server of servers) {
          const video = await server.videos.get({ id: uuid })

          const files = getAllFiles(video)
          expect(files).to.have.lengthOf(1)
          expect(files[0].resolution.id).to.equal(360)
        }
      })

      it('Should replace a video file with transcoding enabled', async function () {
        this.timeout(120000)

        const previousPaths: string[] = []

        await servers[0].config.enableTranscoding(true, true, true)

        const { uuid: videoUUID } = await servers[0].videos.quickUpload({ name: 'fs with transcoding', fixture: 'video_short_720p.mp4' })
        uuid = videoUUID

        await waitJobs(servers)

        for (const server of servers) {
          const video = await server.videos.get({ id: uuid })
          expect(video.inputFileUpdatedAt).to.be.null

          const files = getAllFiles(video)
          expect(files).to.have.lengthOf(6 * 2)

          // Grab old paths to ensure we'll regenerate

          previousPaths.push(video.previewPath)
          previousPaths.push(video.thumbnailPath)

          for (const file of files) {
            previousPaths.push(file.fileUrl)
            previousPaths.push(file.torrentUrl)
            previousPaths.push(file.metadataUrl)

            const metadata = await server.videos.getFileMetadata({ url: file.metadataUrl })
            previousPaths.push(JSON.stringify(metadata))
          }

          const { storyboards } = await server.storyboard.list({ id: uuid })
          for (const s of storyboards) {
            previousPaths.push(s.storyboardPath)
          }
        }

        replaceDate = new Date()

        await servers[0].videos.replaceSourceFile({ videoId: uuid, fixture: 'video_short_360p.mp4' })
        await waitJobs(servers)

        for (const server of servers) {
          const video = await server.videos.get({ id: uuid })

          expect(video.inputFileUpdatedAt).to.not.be.null
          expect(new Date(video.inputFileUpdatedAt)).to.be.above(replaceDate)

          const files = getAllFiles(video)
          expect(files).to.have.lengthOf(4 * 2)

          expect(previousPaths).to.not.include(video.previewPath)
          expect(previousPaths).to.not.include(video.thumbnailPath)

          await makeGetRequest({ url: server.url, path: video.previewPath, expectedStatus: HttpStatusCode.OK_200 })
          await makeGetRequest({ url: server.url, path: video.thumbnailPath, expectedStatus: HttpStatusCode.OK_200 })

          for (const file of files) {
            expect(previousPaths).to.not.include(file.fileUrl)
            expect(previousPaths).to.not.include(file.torrentUrl)
            expect(previousPaths).to.not.include(file.metadataUrl)

            await makeRawRequest({ url: file.fileUrl, expectedStatus: HttpStatusCode.OK_200 })
            await makeRawRequest({ url: file.torrentUrl, expectedStatus: HttpStatusCode.OK_200 })

            const metadata = await server.videos.getFileMetadata({ url: file.metadataUrl })
            expect(previousPaths).to.not.include(JSON.stringify(metadata))
          }

          const { storyboards } = await server.storyboard.list({ id: uuid })
          for (const s of storyboards) {
            expect(previousPaths).to.not.include(s.storyboardPath)

            await makeGetRequest({ url: server.url, path: s.storyboardPath, expectedStatus: HttpStatusCode.OK_200 })
          }
        }

        await servers[0].config.enableMinimumTranscoding()
      })

      it('Should have cleaned up old files', async function () {
        {
          const count = await servers[0].servers.countFiles('storyboards')
          expect(count).to.equal(2)
        }

        {
          const count = await servers[0].servers.countFiles('web-videos')
          expect(count).to.equal(5 + 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
        }

        {
          const count = await servers[0].servers.countFiles('torrents')
          expect(count).to.equal(9)
        }
      })

      it('Should have the correct source input', async function () {
        const source = await servers[0].videos.getSource({ id: uuid })

        expect(source.filename).to.equal('video_short_360p.mp4')
        expect(new Date(source.createdAt)).to.be.above(replaceDate)
      })

      it('Should not have regenerated miniatures that were previously uploaded', async function () {
        this.timeout(120000)

        const { uuid } = await servers[0].videos.upload({
          attributes: {
            name: 'custom miniatures',
            thumbnailfile: 'custom-thumbnail.jpg',
            previewfile: 'custom-preview.jpg'
          }
        })

        await waitJobs(servers)

        const previousPaths: string[] = []

        for (const server of servers) {
          const video = await server.videos.get({ id: uuid })

          previousPaths.push(video.previewPath)
          previousPaths.push(video.thumbnailPath)

          await makeGetRequest({ url: server.url, path: video.previewPath, expectedStatus: HttpStatusCode.OK_200 })
          await makeGetRequest({ url: server.url, path: video.thumbnailPath, expectedStatus: HttpStatusCode.OK_200 })
        }

        await servers[0].videos.replaceSourceFile({ videoId: uuid, fixture: 'video_short_360p.mp4' })
        await waitJobs(servers)

        for (const server of servers) {
          const video = await server.videos.get({ id: uuid })

          expect(previousPaths).to.include(video.previewPath)
          expect(previousPaths).to.include(video.thumbnailPath)

          await makeGetRequest({ url: server.url, path: video.previewPath, expectedStatus: HttpStatusCode.OK_200 })
          await makeGetRequest({ url: server.url, path: video.thumbnailPath, expectedStatus: HttpStatusCode.OK_200 })
        }
      })
    })

    describe('Autoblacklist', function () {

      function updateAutoBlacklist (enabled: boolean) {
        return servers[0].config.updateExistingSubConfig({
          newConfig: {
            autoBlacklist: {
              videos: {
                ofUsers: {
                  enabled
                }
              }
            }
          }
        })
      }

      async function expectBlacklist (uuid: string, value: boolean) {
        const video = await servers[0].videos.getWithToken({ id: uuid })

        expect(video.blacklisted).to.equal(value)
      }

      before(async function () {
        await updateAutoBlacklist(true)
      })

      it('Should auto blacklist an unblacklisted video after file replacement', async function () {
        this.timeout(120000)

        const { uuid } = await servers[0].videos.quickUpload({ token: userToken, name: 'user video' })
        await waitJobs(servers)
        await expectBlacklist(uuid, true)

        await servers[0].blacklist.remove({ videoId: uuid })
        await expectBlacklist(uuid, false)

        await servers[0].videos.replaceSourceFile({ videoId: uuid, token: userToken, fixture: 'video_short_360p.mp4' })
        await waitJobs(servers)

        await expectBlacklist(uuid, true)
      })

      it('Should auto blacklist an already blacklisted video after file replacement', async function () {
        this.timeout(120000)

        const { uuid } = await servers[0].videos.quickUpload({ token: userToken, name: 'user video' })
        await waitJobs(servers)
        await expectBlacklist(uuid, true)

        await servers[0].videos.replaceSourceFile({ videoId: uuid, token: userToken, fixture: 'video_short_360p.mp4' })
        await waitJobs(servers)

        await expectBlacklist(uuid, true)
      })

      it('Should not auto blacklist if auto blacklist has been disabled between the upload and the replacement', async function () {
        this.timeout(120000)

        const { uuid } = await servers[0].videos.quickUpload({ token: userToken, name: 'user video' })
        await waitJobs(servers)
        await expectBlacklist(uuid, true)

        await servers[0].blacklist.remove({ videoId: uuid })
        await expectBlacklist(uuid, false)

        await updateAutoBlacklist(false)

        await servers[0].videos.replaceSourceFile({ videoId: uuid, token: userToken, fixture: 'video_short1.webm' })
        await waitJobs(servers)

        await expectBlacklist(uuid, false)
      })
    })

    describe('With object storage enabled', function () {
      if (areMockObjectStorageTestsDisabled()) return

      const objectStorage = new ObjectStorageCommand()

      before(async function () {
        this.timeout(120000)

        const configOverride = objectStorage.getDefaultMockConfig()
        await objectStorage.prepareDefaultMockBuckets()

        await servers[0].kill()
        await servers[0].run(configOverride)
      })

      it('Should replace a video file with transcoding disabled', async function () {
        this.timeout(120000)

        await servers[0].config.disableTranscoding()

        const { uuid } = await servers[0].videos.quickUpload({
          name: 'object storage without transcoding',
          fixture: 'video_short_720p.mp4'
        })
        await waitJobs(servers)

        for (const server of servers) {
          const video = await server.videos.get({ id: uuid })

          const files = getAllFiles(video)
          expect(files).to.have.lengthOf(1)
          expect(files[0].resolution.id).to.equal(720)
          expectStartWith(files[0].fileUrl, objectStorage.getMockWebVideosBaseUrl())
        }

        await servers[0].videos.replaceSourceFile({ videoId: uuid, fixture: 'video_short_360p.mp4' })
        await waitJobs(servers)

        for (const server of servers) {
          const video = await server.videos.get({ id: uuid })

          const files = getAllFiles(video)
          expect(files).to.have.lengthOf(1)
          expect(files[0].resolution.id).to.equal(360)
          expectStartWith(files[0].fileUrl, objectStorage.getMockWebVideosBaseUrl())
        }
      })

      it('Should replace a video file with transcoding enabled', async function () {
        this.timeout(120000)

        const previousPaths: string[] = []

        await servers[0].config.enableTranscoding(true, true, true)

        const { uuid: videoUUID } = await servers[0].videos.quickUpload({
          name: 'object storage with transcoding',
          fixture: 'video_short_360p.mp4'
        })
        uuid = videoUUID

        await waitJobs(servers)

        for (const server of servers) {
          const video = await server.videos.get({ id: uuid })

          const files = getAllFiles(video)
          expect(files).to.have.lengthOf(4 * 2)

          for (const file of files) {
            previousPaths.push(file.fileUrl)
          }

          for (const file of video.files) {
            expectStartWith(file.fileUrl, objectStorage.getMockWebVideosBaseUrl())
          }

          for (const file of video.streamingPlaylists[0].files) {
            expectStartWith(file.fileUrl, objectStorage.getMockPlaylistBaseUrl())
          }
        }

        await servers[0].videos.replaceSourceFile({ videoId: uuid, fixture: 'video_short_240p.mp4' })
        await waitJobs(servers)

        for (const server of servers) {
          const video = await server.videos.get({ id: uuid })

          const files = getAllFiles(video)
          expect(files).to.have.lengthOf(3 * 2)

          for (const file of files) {
            expect(previousPaths).to.not.include(file.fileUrl)
          }

          for (const file of video.files) {
            expectStartWith(file.fileUrl, objectStorage.getMockWebVideosBaseUrl())
          }

          for (const file of video.streamingPlaylists[0].files) {
            expectStartWith(file.fileUrl, objectStorage.getMockPlaylistBaseUrl())
          }
        }
      })
    })
  })

  after(async function () {
    await cleanupTests(servers)
  })
})