aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/controllers/api/videos/transcoding.ts
blob: 8c9a5322b78a29b7f15efd3aa6cba59eec5950fc (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import Bluebird from 'bluebird'
import express from 'express'
import { computeResolutionsToTranscode } from '@server/helpers/ffmpeg'
import { logger, loggerTagsFactory } from '@server/helpers/logger'
import { JobQueue } from '@server/lib/job-queue'
import { Hooks } from '@server/lib/plugins/hooks'
import { buildTranscodingJob } from '@server/lib/video'
import { HttpStatusCode, UserRight, VideoState, VideoTranscodingCreate } from '@shared/models'
import { asyncMiddleware, authenticate, createTranscodingValidator, ensureUserHasRight } from '../../../middlewares'

const lTags = loggerTagsFactory('api', 'video')
const transcodingRouter = express.Router()

transcodingRouter.post('/:videoId/transcoding',
  authenticate,
  ensureUserHasRight(UserRight.RUN_VIDEO_TRANSCODING),
  asyncMiddleware(createTranscodingValidator),
  asyncMiddleware(createTranscoding)
)

// ---------------------------------------------------------------------------

export {
  transcodingRouter
}

// ---------------------------------------------------------------------------

async function createTranscoding (req: express.Request, res: express.Response) {
  const video = res.locals.videoAll
  logger.info('Creating %s transcoding job for %s.', req.body.transcodingType, video.url, lTags())

  const body: VideoTranscodingCreate = req.body

  const { resolution: maxResolution, hasAudio } = await video.probeMaxQualityFile()

  const resolutions = await Hooks.wrapObject(
    computeResolutionsToTranscode({ input: maxResolution, type: 'vod', includeInput: true, strictLower: false, hasAudio }),
    'filter:transcoding.manual.resolutions-to-transcode.result',
    body
  )

  if (resolutions.length === 0) {
    return res.sendStatus(HttpStatusCode.NO_CONTENT_204)
  }

  video.state = VideoState.TO_TRANSCODE
  await video.save()

  const childrenResolutions = resolutions.filter(r => r !== maxResolution)

  logger.info('Manually creating transcoding jobs for %s.', body.transcodingType, { childrenResolutions, maxResolution })

  const children = await Bluebird.mapSeries(childrenResolutions, resolution => {
    if (body.transcodingType === 'hls') {
      return buildHLSJobOption({
        videoUUID: video.uuid,
        hasAudio,
        resolution,
        isMaxQuality: false
      })
    }

    if (body.transcodingType === 'webtorrent') {
      return buildWebTorrentJobOption({
        videoUUID: video.uuid,
        hasAudio,
        resolution
      })
    }
  })

  const parent = body.transcodingType === 'hls'
    ? await buildHLSJobOption({
      videoUUID: video.uuid,
      hasAudio,
      resolution: maxResolution,
      isMaxQuality: false
    })
    : await buildWebTorrentJobOption({
      videoUUID: video.uuid,
      hasAudio,
      resolution: maxResolution
    })

  // Porcess the last resolution after the other ones to prevent concurrency issue
  // Because low resolutions use the biggest one as ffmpeg input
  await JobQueue.Instance.createJobWithChildren(parent, children)

  return res.sendStatus(HttpStatusCode.NO_CONTENT_204)
}

function buildHLSJobOption (options: {
  videoUUID: string
  hasAudio: boolean
  resolution: number
  isMaxQuality: boolean
}) {
  const { videoUUID, hasAudio, resolution, isMaxQuality } = options

  return buildTranscodingJob({
    type: 'new-resolution-to-hls',
    videoUUID,
    resolution,
    hasAudio,
    copyCodecs: false,
    isNewVideo: false,
    autoDeleteWebTorrentIfNeeded: false,
    isMaxQuality
  })
}

function buildWebTorrentJobOption (options: {
  videoUUID: string
  hasAudio: boolean
  resolution: number
}) {
  const { videoUUID, hasAudio, resolution } = options

  return buildTranscodingJob({
    type: 'new-resolution-to-webtorrent',
    videoUUID,
    isNewVideo: false,
    resolution,
    hasAudio,
    createHLSIfNeeded: false
  })
}