]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blobdiff - server/controllers/api/videos/index.ts
Add `req` and `res` as controllers hooks parameters
[github/Chocobozzz/PeerTube.git] / server / controllers / api / videos / index.ts
index 6483d2e8a1bd94a2f94b37e3ee20317735bd94d4..61a030ba144e18835557279dc05678f50dafbedc 100644 (file)
@@ -1,20 +1,21 @@
-import * as express from 'express'
-import toInt from 'validator/lib/toInt'
-import { LiveManager } from '@server/lib/live-manager'
+import express from 'express'
+import { pickCommonVideoQuery } from '@server/helpers/query'
+import { doJSONRequest } from '@server/helpers/requests'
+import { VideoViews } from '@server/lib/video-views'
+import { openapiOperationDoc } from '@server/middlewares/doc'
 import { getServerActor } from '@server/models/application/application'
-import { VideosCommonQuery } from '../../../../shared'
-import { HttpStatusCode } from '../../../../shared/core-utils/miscs'
+import { guessAdditionalAttributesFromQuery } from '@server/models/video/formatter/video-format-utils'
+import { MVideoAccountLight } from '@server/types/models'
+import { HttpStatusCode } from '../../../../shared/models'
 import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger'
 import { buildNSFWFilter, getCountVideos } from '../../../helpers/express-utils'
 import { logger } from '../../../helpers/logger'
 import { getFormattedObjects } from '../../../helpers/utils'
-import { VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES } from '../../../initializers/constants'
+import { REMOTE_SCHEME, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES } from '../../../initializers/constants'
 import { sequelizeTypescript } from '../../../initializers/database'
 import { sendView } from '../../../lib/activitypub/send/send-view'
-import { fetchRemoteVideoDescription } from '../../../lib/activitypub/videos'
 import { JobQueue } from '../../../lib/job-queue'
 import { Hooks } from '../../../lib/plugins/hooks'
-import { Redis } from '../../../lib/redis'
 import {
   asyncMiddleware,
   asyncRetryTransactionMiddleware,
@@ -25,21 +26,21 @@ import {
   paginationValidator,
   setDefaultPagination,
   setDefaultVideosSort,
-  videoFileMetadataGetValidator,
   videosCustomGetValidator,
   videosGetValidator,
   videosRemoveValidator,
   videosSortValidator
 } from '../../../middlewares'
 import { VideoModel } from '../../../models/video/video'
-import { VideoFileModel } from '../../../models/video/video-file'
 import { blacklistRouter } from './blacklist'
 import { videoCaptionsRouter } from './captions'
 import { videoCommentRouter } from './comment'
+import { filesRouter } from './files'
 import { videoImportsRouter } from './import'
 import { liveRouter } from './live'
 import { ownershipVideoRouter } from './ownership'
 import { rateVideoRouter } from './rate'
+import { transcodingRouter } from './transcoding'
 import { updateRouter } from './update'
 import { uploadRouter } from './upload'
 import { watchingRouter } from './watching'
@@ -57,13 +58,28 @@ videosRouter.use('/', watchingRouter)
 videosRouter.use('/', liveRouter)
 videosRouter.use('/', uploadRouter)
 videosRouter.use('/', updateRouter)
+videosRouter.use('/', filesRouter)
+videosRouter.use('/', transcodingRouter)
 
-videosRouter.get('/categories', listVideoCategories)
-videosRouter.get('/licences', listVideoLicences)
-videosRouter.get('/languages', listVideoLanguages)
-videosRouter.get('/privacies', listVideoPrivacies)
+videosRouter.get('/categories',
+  openapiOperationDoc({ operationId: 'getCategories' }),
+  listVideoCategories
+)
+videosRouter.get('/licences',
+  openapiOperationDoc({ operationId: 'getLicences' }),
+  listVideoLicences
+)
+videosRouter.get('/languages',
+  openapiOperationDoc({ operationId: 'getLanguages' }),
+  listVideoLanguages
+)
+videosRouter.get('/privacies',
+  openapiOperationDoc({ operationId: 'getPrivacies' }),
+  listVideoPrivacies
+)
 
 videosRouter.get('/',
+  openapiOperationDoc({ operationId: 'getVideos' }),
   paginationValidator,
   videosSortValidator,
   setDefaultVideosSort,
@@ -74,25 +90,25 @@ videosRouter.get('/',
 )
 
 videosRouter.get('/:id/description',
+  openapiOperationDoc({ operationId: 'getVideoDesc' }),
   asyncMiddleware(videosGetValidator),
   asyncMiddleware(getVideoDescription)
 )
-videosRouter.get('/:id/metadata/:videoFileId',
-  asyncMiddleware(videoFileMetadataGetValidator),
-  asyncMiddleware(getVideoFileMetadata)
-)
 videosRouter.get('/:id',
+  openapiOperationDoc({ operationId: 'getVideo' }),
   optionalAuthenticate,
-  asyncMiddleware(videosCustomGetValidator('only-video-with-rights')),
+  asyncMiddleware(videosCustomGetValidator('for-api')),
   asyncMiddleware(checkVideoFollowConstraints),
-  asyncMiddleware(getVideo)
+  getVideo
 )
 videosRouter.post('/:id/views',
-  asyncMiddleware(videosCustomGetValidator('only-immutable-attributes')),
+  openapiOperationDoc({ operationId: 'addView' }),
+  asyncMiddleware(videosCustomGetValidator('only-video')),
   asyncMiddleware(viewVideo)
 )
 
 videosRouter.delete('/:id',
+  openapiOperationDoc({ operationId: 'delVideo' }),
   authenticate,
   asyncMiddleware(videosRemoveValidator),
   asyncRetryTransactionMiddleware(removeVideo)
@@ -122,15 +138,8 @@ function listVideoPrivacies (_req: express.Request, res: express.Response) {
   res.json(VIDEO_PRIVACIES)
 }
 
-async function getVideo (_req: express.Request, res: express.Response) {
-  // We need more attributes
-  const userId: number = res.locals.oauth ? res.locals.oauth.token.User.id : null
-
-  const video = await Hooks.wrapPromiseFun(
-    VideoModel.loadForGetAPI,
-    { id: res.locals.onlyVideoWithRights.id, userId },
-    'filter:api.video.get.result'
-  )
+function getVideo (_req: express.Request, res: express.Response) {
+  const video = res.locals.videoAPI
 
   if (video.isOutdated()) {
     JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'video', url: video.url } })
@@ -140,46 +149,19 @@ async function getVideo (_req: express.Request, res: express.Response) {
 }
 
 async function viewVideo (req: express.Request, res: express.Response) {
-  const immutableVideoAttrs = res.locals.onlyImmutableVideo
+  const video = res.locals.onlyVideo
 
   const ip = req.ip
-  const exists = await Redis.Instance.doesVideoIPViewExist(ip, immutableVideoAttrs.uuid)
-  if (exists) {
-    logger.debug('View for ip %s and video %s already exists.', ip, immutableVideoAttrs.uuid)
-    return res.sendStatus(HttpStatusCode.NO_CONTENT_204)
-  }
+  const success = await VideoViews.Instance.processView({ video, ip })
 
-  const video = await VideoModel.load(immutableVideoAttrs.id)
-
-  const promises: Promise<any>[] = [
-    Redis.Instance.setIPVideoView(ip, video.uuid, video.isLive)
-  ]
-
-  let federateView = true
-
-  // Increment our live manager
-  if (video.isLive && video.isOwned()) {
-    LiveManager.Instance.addViewTo(video.id)
-
-    // Views of our local live will be sent by our live manager
-    federateView = false
-  }
-
-  // Increment our video views cache counter
-  if (!video.isLive) {
-    promises.push(Redis.Instance.addVideoView(video.id))
-  }
-
-  if (federateView) {
+  if (success) {
     const serverActor = await getServerActor()
-    promises.push(sendView(serverActor, video, undefined))
-  }
+    await sendView(serverActor, video, undefined)
 
-  await Promise.all(promises)
-
-  Hooks.runAction('action:api.video.viewed', { video, ip })
+    Hooks.runAction('action:api.video.viewed', { video: video, ip, req, res })
+  }
 
-  return res.sendStatus(HttpStatusCode.NO_CONTENT_204)
+  return res.status(HttpStatusCode.NO_CONTENT_204).end()
 }
 
 async function getVideoDescription (req: express.Request, res: express.Response) {
@@ -192,30 +174,20 @@ async function getVideoDescription (req: express.Request, res: express.Response)
   return res.json({ description })
 }
 
-async function getVideoFileMetadata (req: express.Request, res: express.Response) {
-  const videoFile = await VideoFileModel.loadWithMetadata(toInt(req.params.videoFileId))
-
-  return res.json(videoFile.metadata)
-}
-
 async function listVideos (req: express.Request, res: express.Response) {
-  const query = req.query as VideosCommonQuery
+  const serverActor = await getServerActor()
+
+  const query = pickCommonVideoQuery(req.query)
   const countVideos = getCountVideos(req)
 
   const apiOptions = await Hooks.wrapObject({
-    start: query.start,
-    count: query.count,
-    sort: query.sort,
-    includeLocalVideos: true,
-    categoryOneOf: query.categoryOneOf,
-    licenceOneOf: query.licenceOneOf,
-    languageOneOf: query.languageOneOf,
-    tagsOneOf: query.tagsOneOf,
-    tagsAllOf: query.tagsAllOf,
+    ...query,
+
+    displayOnlyForFollower: {
+      actorId: serverActor.id,
+      orLocalVideos: true
+    },
     nsfw: buildNSFWFilter(res, query.nsfw),
-    isLive: query.isLive,
-    filter: query.filter,
-    withFiles: false,
     user: res.locals.oauth ? res.locals.oauth.token.User : undefined,
     countVideos
   }, 'filter:api.videos.list.params')
@@ -226,10 +198,10 @@ async function listVideos (req: express.Request, res: express.Response) {
     'filter:api.videos.list.result'
   )
 
-  return res.json(getFormattedObjects(resultList.data, resultList.total))
+  return res.json(getFormattedObjects(resultList.data, resultList.total, guessAdditionalAttributesFromQuery(query)))
 }
 
-async function removeVideo (_req: express.Request, res: express.Response) {
+async function removeVideo (req: express.Request, res: express.Response) {
   const videoInstance = res.locals.videoAll
 
   await sequelizeTypescript.transaction(async t => {
@@ -239,9 +211,21 @@ async function removeVideo (_req: express.Request, res: express.Response) {
   auditLogger.delete(getAuditIdFromRes(res), new VideoAuditView(videoInstance.toFormattedDetailsJSON()))
   logger.info('Video with name %s and uuid %s deleted.', videoInstance.name, videoInstance.uuid)
 
-  Hooks.runAction('action:api.video.deleted', { video: videoInstance })
+  Hooks.runAction('action:api.video.deleted', { video: videoInstance, req, res })
 
   return res.type('json')
             .status(HttpStatusCode.NO_CONTENT_204)
             .end()
 }
+
+// ---------------------------------------------------------------------------
+
+// FIXME: Should not exist, we rely on specific API
+async function fetchRemoteVideoDescription (video: MVideoAccountLight) {
+  const host = video.VideoChannel.Account.Actor.Server.host
+  const path = video.getDescriptionAPIPath()
+  const url = REMOTE_SCHEME.HTTP + '://' + host + path
+
+  const { body } = await doJSONRequest<any>(url)
+  return body.description || ''
+}