2021-08-27 14:32:44 +02:00
|
|
|
import express from 'express'
|
2021-07-29 11:54:38 +02:00
|
|
|
import { pickCommonVideoQuery } from '@server/helpers/query'
|
2022-02-28 08:34:43 +01:00
|
|
|
import { getBiggestActorImage } from '@server/lib/actor-image'
|
2020-12-08 10:30:33 +01:00
|
|
|
import { Hooks } from '@server/lib/plugins/hooks'
|
2021-10-19 09:44:43 +02:00
|
|
|
import { ActorFollowModel } from '@server/models/actor/actor-follow'
|
2020-08-20 09:19:21 +02:00
|
|
|
import { getServerActor } from '@server/models/application/application'
|
2021-10-27 14:37:04 +02:00
|
|
|
import { guessAdditionalAttributesFromQuery } from '@server/models/video/formatter/video-format-utils'
|
2021-04-06 17:01:35 +02:00
|
|
|
import { MChannelBannerAccountDefault } from '@server/types/models'
|
2022-08-10 11:51:13 +02:00
|
|
|
import { ActorImageType, HttpStatusCode, VideoChannelCreate, VideoChannelUpdate, VideosImportInChannelCreate } from '@shared/models'
|
2020-08-20 09:19:21 +02:00
|
|
|
import { auditLoggerFactory, getAuditIdFromRes, VideoChannelAuditView } from '../../helpers/audit-logger'
|
|
|
|
import { resetSequelizeInstance } from '../../helpers/database-utils'
|
|
|
|
import { buildNSFWFilter, createReqFiles, getCountVideos, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils'
|
|
|
|
import { logger } from '../../helpers/logger'
|
2020-04-23 11:36:50 +02:00
|
|
|
import { getFormattedObjects } from '../../helpers/utils'
|
2020-08-20 09:19:21 +02:00
|
|
|
import { MIMETYPES } from '../../initializers/constants'
|
|
|
|
import { sequelizeTypescript } from '../../initializers/database'
|
|
|
|
import { sendUpdateActor } from '../../lib/activitypub/send'
|
|
|
|
import { JobQueue } from '../../lib/job-queue'
|
2022-02-28 08:34:43 +01:00
|
|
|
import { deleteLocalActorImageFile, updateLocalActorImageFiles } from '../../lib/local-actor'
|
2020-08-20 09:19:21 +02:00
|
|
|
import { createLocalVideoChannel, federateAllVideosOfChannel } from '../../lib/video-channel'
|
2018-04-24 17:05:32 +02:00
|
|
|
import {
|
2023-06-20 14:17:34 +02:00
|
|
|
apiRateLimiter,
|
2018-04-24 17:05:32 +02:00
|
|
|
asyncMiddleware,
|
2018-06-13 14:27:40 +02:00
|
|
|
asyncRetryTransactionMiddleware,
|
2018-08-14 15:28:30 +02:00
|
|
|
authenticate,
|
|
|
|
commonVideosFiltersValidator,
|
2022-09-07 17:18:29 +02:00
|
|
|
ensureCanManageChannelOrAccount,
|
2018-04-25 16:15:39 +02:00
|
|
|
optionalAuthenticate,
|
2018-04-24 17:05:32 +02:00
|
|
|
paginationValidator,
|
|
|
|
setDefaultPagination,
|
|
|
|
setDefaultSort,
|
2020-08-20 09:19:21 +02:00
|
|
|
setDefaultVideosSort,
|
2018-04-25 16:15:39 +02:00
|
|
|
videoChannelsAddValidator,
|
|
|
|
videoChannelsRemoveValidator,
|
|
|
|
videoChannelsSortValidator,
|
2019-02-26 10:55:40 +01:00
|
|
|
videoChannelsUpdateValidator,
|
|
|
|
videoPlaylistsSortValidator
|
2018-04-24 17:05:32 +02:00
|
|
|
} from '../../middlewares'
|
2021-10-19 09:44:43 +02:00
|
|
|
import {
|
2022-08-10 09:53:39 +02:00
|
|
|
ensureChannelOwnerCanUpload,
|
2021-12-13 15:29:13 +01:00
|
|
|
ensureIsLocalChannel,
|
2022-08-10 09:53:39 +02:00
|
|
|
videoChannelImportVideosValidator,
|
2021-10-19 09:44:43 +02:00
|
|
|
videoChannelsFollowersSortValidator,
|
|
|
|
videoChannelsListValidator,
|
|
|
|
videoChannelsNameWithHostValidator,
|
|
|
|
videosSortValidator
|
|
|
|
} from '../../middlewares/validators'
|
2021-04-07 10:36:13 +02:00
|
|
|
import { updateAvatarValidator, updateBannerValidator } from '../../middlewares/validators/actor-image'
|
2020-08-20 09:19:21 +02:00
|
|
|
import { commonVideoPlaylistFiltersValidator } from '../../middlewares/validators/videos/video-playlists'
|
2018-04-25 16:15:39 +02:00
|
|
|
import { AccountModel } from '../../models/account/account'
|
|
|
|
import { VideoModel } from '../../models/video/video'
|
2020-08-20 09:19:21 +02:00
|
|
|
import { VideoChannelModel } from '../../models/video/video-channel'
|
2019-02-26 10:55:40 +01:00
|
|
|
import { VideoPlaylistModel } from '../../models/video/video-playlist'
|
2018-06-29 11:29:23 +02:00
|
|
|
|
2018-07-31 14:04:26 +02:00
|
|
|
const auditLogger = auditLoggerFactory('channels')
|
2022-03-04 10:57:36 +01:00
|
|
|
const reqAvatarFile = createReqFiles([ 'avatarfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT)
|
|
|
|
const reqBannerFile = createReqFiles([ 'bannerfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT)
|
2018-04-24 17:05:32 +02:00
|
|
|
|
|
|
|
const videoChannelRouter = express.Router()
|
|
|
|
|
2023-06-20 14:17:34 +02:00
|
|
|
videoChannelRouter.use(apiRateLimiter)
|
|
|
|
|
2018-04-24 17:05:32 +02:00
|
|
|
videoChannelRouter.get('/',
|
|
|
|
paginationValidator,
|
|
|
|
videoChannelsSortValidator,
|
|
|
|
setDefaultSort,
|
|
|
|
setDefaultPagination,
|
2021-06-17 16:02:38 +02:00
|
|
|
videoChannelsListValidator,
|
2018-04-24 17:05:32 +02:00
|
|
|
asyncMiddleware(listVideoChannels)
|
|
|
|
)
|
|
|
|
|
2018-04-25 16:15:39 +02:00
|
|
|
videoChannelRouter.post('/',
|
|
|
|
authenticate,
|
2018-10-01 15:18:07 +02:00
|
|
|
asyncMiddleware(videoChannelsAddValidator),
|
2018-06-13 14:27:40 +02:00
|
|
|
asyncRetryTransactionMiddleware(addVideoChannel)
|
2018-04-25 16:15:39 +02:00
|
|
|
)
|
|
|
|
|
2018-08-17 15:45:42 +02:00
|
|
|
videoChannelRouter.post('/:nameWithHost/avatar/pick',
|
2018-06-29 11:29:23 +02:00
|
|
|
authenticate,
|
|
|
|
reqAvatarFile,
|
2021-10-19 09:44:43 +02:00
|
|
|
asyncMiddleware(videoChannelsNameWithHostValidator),
|
2021-12-13 15:29:13 +01:00
|
|
|
ensureIsLocalChannel,
|
2022-09-07 17:18:29 +02:00
|
|
|
ensureCanManageChannelOrAccount,
|
2018-06-29 11:29:23 +02:00
|
|
|
updateAvatarValidator,
|
2018-09-26 10:15:50 +02:00
|
|
|
asyncMiddleware(updateVideoChannelAvatar)
|
2018-06-29 11:29:23 +02:00
|
|
|
)
|
|
|
|
|
2021-04-06 17:01:35 +02:00
|
|
|
videoChannelRouter.post('/:nameWithHost/banner/pick',
|
|
|
|
authenticate,
|
|
|
|
reqBannerFile,
|
2021-10-19 09:44:43 +02:00
|
|
|
asyncMiddleware(videoChannelsNameWithHostValidator),
|
2021-12-13 15:29:13 +01:00
|
|
|
ensureIsLocalChannel,
|
2022-09-07 17:18:29 +02:00
|
|
|
ensureCanManageChannelOrAccount,
|
2021-04-06 17:01:35 +02:00
|
|
|
updateBannerValidator,
|
|
|
|
asyncMiddleware(updateVideoChannelBanner)
|
|
|
|
)
|
|
|
|
|
2021-01-13 09:12:55 +01:00
|
|
|
videoChannelRouter.delete('/:nameWithHost/avatar',
|
|
|
|
authenticate,
|
2021-10-19 09:44:43 +02:00
|
|
|
asyncMiddleware(videoChannelsNameWithHostValidator),
|
2021-12-13 15:29:13 +01:00
|
|
|
ensureIsLocalChannel,
|
2022-09-07 17:18:29 +02:00
|
|
|
ensureCanManageChannelOrAccount,
|
2021-01-13 09:12:55 +01:00
|
|
|
asyncMiddleware(deleteVideoChannelAvatar)
|
|
|
|
)
|
|
|
|
|
2021-04-06 17:01:35 +02:00
|
|
|
videoChannelRouter.delete('/:nameWithHost/banner',
|
|
|
|
authenticate,
|
2021-10-19 09:44:43 +02:00
|
|
|
asyncMiddleware(videoChannelsNameWithHostValidator),
|
2021-12-13 15:29:13 +01:00
|
|
|
ensureIsLocalChannel,
|
2022-09-07 17:18:29 +02:00
|
|
|
ensureCanManageChannelOrAccount,
|
2021-04-06 17:01:35 +02:00
|
|
|
asyncMiddleware(deleteVideoChannelBanner)
|
|
|
|
)
|
|
|
|
|
2018-08-17 15:45:42 +02:00
|
|
|
videoChannelRouter.put('/:nameWithHost',
|
2018-04-25 16:15:39 +02:00
|
|
|
authenticate,
|
2021-10-19 09:44:43 +02:00
|
|
|
asyncMiddleware(videoChannelsNameWithHostValidator),
|
2021-12-13 15:29:13 +01:00
|
|
|
ensureIsLocalChannel,
|
2022-09-07 17:18:29 +02:00
|
|
|
ensureCanManageChannelOrAccount,
|
2021-10-19 09:44:43 +02:00
|
|
|
videoChannelsUpdateValidator,
|
2018-06-13 14:27:40 +02:00
|
|
|
asyncRetryTransactionMiddleware(updateVideoChannel)
|
2018-04-25 16:15:39 +02:00
|
|
|
)
|
|
|
|
|
2018-08-17 15:45:42 +02:00
|
|
|
videoChannelRouter.delete('/:nameWithHost',
|
2018-04-25 16:15:39 +02:00
|
|
|
authenticate,
|
2021-12-13 15:29:13 +01:00
|
|
|
asyncMiddleware(videoChannelsNameWithHostValidator),
|
|
|
|
ensureIsLocalChannel,
|
2022-09-07 17:18:29 +02:00
|
|
|
ensureCanManageChannelOrAccount,
|
2018-04-25 16:15:39 +02:00
|
|
|
asyncMiddleware(videoChannelsRemoveValidator),
|
2018-06-13 14:27:40 +02:00
|
|
|
asyncRetryTransactionMiddleware(removeVideoChannel)
|
2018-04-25 16:15:39 +02:00
|
|
|
)
|
|
|
|
|
2018-08-17 15:45:42 +02:00
|
|
|
videoChannelRouter.get('/:nameWithHost',
|
|
|
|
asyncMiddleware(videoChannelsNameWithHostValidator),
|
2022-08-03 11:17:57 +02:00
|
|
|
asyncMiddleware(getVideoChannel)
|
2018-04-25 16:15:39 +02:00
|
|
|
)
|
|
|
|
|
2019-02-26 10:55:40 +01:00
|
|
|
videoChannelRouter.get('/:nameWithHost/video-playlists',
|
|
|
|
asyncMiddleware(videoChannelsNameWithHostValidator),
|
|
|
|
paginationValidator,
|
|
|
|
videoPlaylistsSortValidator,
|
|
|
|
setDefaultSort,
|
|
|
|
setDefaultPagination,
|
2019-03-05 10:58:44 +01:00
|
|
|
commonVideoPlaylistFiltersValidator,
|
2019-02-26 10:55:40 +01:00
|
|
|
asyncMiddleware(listVideoChannelPlaylists)
|
|
|
|
)
|
|
|
|
|
2018-08-17 15:45:42 +02:00
|
|
|
videoChannelRouter.get('/:nameWithHost/videos',
|
|
|
|
asyncMiddleware(videoChannelsNameWithHostValidator),
|
2018-04-25 16:15:39 +02:00
|
|
|
paginationValidator,
|
|
|
|
videosSortValidator,
|
2020-08-20 09:19:21 +02:00
|
|
|
setDefaultVideosSort,
|
2018-04-25 16:15:39 +02:00
|
|
|
setDefaultPagination,
|
|
|
|
optionalAuthenticate,
|
2018-07-20 14:35:18 +02:00
|
|
|
commonVideosFiltersValidator,
|
2018-04-25 16:15:39 +02:00
|
|
|
asyncMiddleware(listVideoChannelVideos)
|
|
|
|
)
|
|
|
|
|
2021-10-19 09:44:43 +02:00
|
|
|
videoChannelRouter.get('/:nameWithHost/followers',
|
|
|
|
authenticate,
|
|
|
|
asyncMiddleware(videoChannelsNameWithHostValidator),
|
2022-09-07 17:18:29 +02:00
|
|
|
ensureCanManageChannelOrAccount,
|
2021-10-19 09:44:43 +02:00
|
|
|
paginationValidator,
|
|
|
|
videoChannelsFollowersSortValidator,
|
|
|
|
setDefaultSort,
|
|
|
|
setDefaultPagination,
|
|
|
|
asyncMiddleware(listVideoChannelFollowers)
|
|
|
|
)
|
|
|
|
|
2022-08-10 09:53:39 +02:00
|
|
|
videoChannelRouter.post('/:nameWithHost/import-videos',
|
|
|
|
authenticate,
|
|
|
|
asyncMiddleware(videoChannelsNameWithHostValidator),
|
2022-08-10 11:51:13 +02:00
|
|
|
asyncMiddleware(videoChannelImportVideosValidator),
|
2022-08-10 09:53:39 +02:00
|
|
|
ensureIsLocalChannel,
|
2022-09-07 17:18:29 +02:00
|
|
|
ensureCanManageChannelOrAccount,
|
2022-08-10 09:53:39 +02:00
|
|
|
asyncMiddleware(ensureChannelOwnerCanUpload),
|
|
|
|
asyncMiddleware(importVideosInChannel)
|
|
|
|
)
|
|
|
|
|
2018-04-24 17:05:32 +02:00
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
export {
|
|
|
|
videoChannelRouter
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
2019-03-19 10:35:15 +01:00
|
|
|
async function listVideoChannels (req: express.Request, res: express.Response) {
|
2018-08-23 17:58:39 +02:00
|
|
|
const serverActor = await getServerActor()
|
2022-08-03 11:17:57 +02:00
|
|
|
|
|
|
|
const apiOptions = await Hooks.wrapObject({
|
2020-07-15 11:17:03 +02:00
|
|
|
actorId: serverActor.id,
|
|
|
|
start: req.query.start,
|
|
|
|
count: req.query.count,
|
2020-07-23 21:30:04 +02:00
|
|
|
sort: req.query.sort
|
2022-08-03 11:17:57 +02:00
|
|
|
}, 'filter:api.video-channels.list.params')
|
|
|
|
|
|
|
|
const resultList = await Hooks.wrapPromiseFun(
|
|
|
|
VideoChannelModel.listForApi,
|
|
|
|
apiOptions,
|
|
|
|
'filter:api.video-channels.list.result'
|
|
|
|
)
|
2018-04-24 17:05:32 +02:00
|
|
|
|
|
|
|
return res.json(getFormattedObjects(resultList.data, resultList.total))
|
|
|
|
}
|
2018-04-25 16:15:39 +02:00
|
|
|
|
2021-04-06 17:01:35 +02:00
|
|
|
async function updateVideoChannelBanner (req: express.Request, res: express.Response) {
|
|
|
|
const bannerPhysicalFile = req.files['bannerfile'][0]
|
|
|
|
const videoChannel = res.locals.videoChannel
|
|
|
|
const oldVideoChannelAuditKeys = new VideoChannelAuditView(videoChannel.toFormattedJSON())
|
|
|
|
|
2022-02-28 08:34:43 +01:00
|
|
|
const banners = await updateLocalActorImageFiles(videoChannel, bannerPhysicalFile, ActorImageType.BANNER)
|
2021-04-06 17:01:35 +02:00
|
|
|
|
|
|
|
auditLogger.update(getAuditIdFromRes(res), new VideoChannelAuditView(videoChannel.toFormattedJSON()), oldVideoChannelAuditKeys)
|
|
|
|
|
2022-02-28 08:34:43 +01:00
|
|
|
return res.json({
|
|
|
|
// TODO: remove, deprecated in 4.2
|
|
|
|
banner: getBiggestActorImage(banners).toFormattedJSON(),
|
|
|
|
banners: banners.map(b => b.toFormattedJSON())
|
|
|
|
})
|
2021-04-06 17:01:35 +02:00
|
|
|
}
|
2021-05-12 14:51:17 +02:00
|
|
|
|
2019-03-19 10:35:15 +01:00
|
|
|
async function updateVideoChannelAvatar (req: express.Request, res: express.Response) {
|
2020-01-31 16:56:52 +01:00
|
|
|
const avatarPhysicalFile = req.files['avatarfile'][0]
|
2019-03-19 10:35:15 +01:00
|
|
|
const videoChannel = res.locals.videoChannel
|
2018-07-31 14:04:26 +02:00
|
|
|
const oldVideoChannelAuditKeys = new VideoChannelAuditView(videoChannel.toFormattedJSON())
|
2018-06-29 11:29:23 +02:00
|
|
|
|
2022-02-28 08:34:43 +01:00
|
|
|
const avatars = await updateLocalActorImageFiles(videoChannel, avatarPhysicalFile, ActorImageType.AVATAR)
|
2018-09-20 11:31:48 +02:00
|
|
|
auditLogger.update(getAuditIdFromRes(res), new VideoChannelAuditView(videoChannel.toFormattedJSON()), oldVideoChannelAuditKeys)
|
2018-07-31 14:04:26 +02:00
|
|
|
|
2022-02-28 08:34:43 +01:00
|
|
|
return res.json({
|
|
|
|
// TODO: remove, deprecated in 4.2
|
|
|
|
avatar: getBiggestActorImage(avatars).toFormattedJSON(),
|
|
|
|
avatars: avatars.map(a => a.toFormattedJSON())
|
|
|
|
})
|
2018-06-29 11:29:23 +02:00
|
|
|
}
|
|
|
|
|
2021-01-13 09:12:55 +01:00
|
|
|
async function deleteVideoChannelAvatar (req: express.Request, res: express.Response) {
|
|
|
|
const videoChannel = res.locals.videoChannel
|
|
|
|
|
2021-04-06 17:01:35 +02:00
|
|
|
await deleteLocalActorImageFile(videoChannel, ActorImageType.AVATAR)
|
|
|
|
|
2021-06-01 01:36:53 +02:00
|
|
|
return res.status(HttpStatusCode.NO_CONTENT_204).end()
|
2021-04-06 17:01:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
async function deleteVideoChannelBanner (req: express.Request, res: express.Response) {
|
|
|
|
const videoChannel = res.locals.videoChannel
|
|
|
|
|
|
|
|
await deleteLocalActorImageFile(videoChannel, ActorImageType.BANNER)
|
2021-01-13 09:12:55 +01:00
|
|
|
|
2021-06-01 01:36:53 +02:00
|
|
|
return res.status(HttpStatusCode.NO_CONTENT_204).end()
|
2021-01-13 09:12:55 +01:00
|
|
|
}
|
|
|
|
|
2018-04-25 16:15:39 +02:00
|
|
|
async function addVideoChannel (req: express.Request, res: express.Response) {
|
|
|
|
const videoChannelInfo: VideoChannelCreate = req.body
|
|
|
|
|
2019-08-15 11:53:26 +02:00
|
|
|
const videoChannelCreated = await sequelizeTypescript.transaction(async t => {
|
2019-03-19 10:35:15 +01:00
|
|
|
const account = await AccountModel.load(res.locals.oauth.token.User.Account.id, t)
|
2018-09-20 10:13:13 +02:00
|
|
|
|
2019-08-20 19:05:31 +02:00
|
|
|
return createLocalVideoChannel(videoChannelInfo, account, t)
|
2018-04-25 16:15:39 +02:00
|
|
|
})
|
|
|
|
|
2021-02-26 14:22:25 +01:00
|
|
|
const payload = { actorId: videoChannelCreated.actorId }
|
2022-08-08 15:48:17 +02:00
|
|
|
await JobQueue.Instance.createJob({ type: 'actor-keys', payload })
|
2018-04-25 16:15:39 +02:00
|
|
|
|
2018-09-20 10:13:13 +02:00
|
|
|
auditLogger.create(getAuditIdFromRes(res), new VideoChannelAuditView(videoChannelCreated.toFormattedJSON()))
|
2019-05-31 14:02:26 +02:00
|
|
|
logger.info('Video channel %s created.', videoChannelCreated.Actor.url)
|
2018-04-25 16:15:39 +02:00
|
|
|
|
2022-08-03 11:17:57 +02:00
|
|
|
Hooks.runAction('action:api.video-channel.created', { videoChannel: videoChannelCreated, req, res })
|
|
|
|
|
2018-06-13 14:27:40 +02:00
|
|
|
return res.json({
|
|
|
|
videoChannel: {
|
2019-05-31 14:02:26 +02:00
|
|
|
id: videoChannelCreated.id
|
2018-06-13 14:27:40 +02:00
|
|
|
}
|
2021-04-06 17:01:35 +02:00
|
|
|
})
|
2018-04-25 16:15:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
async function updateVideoChannel (req: express.Request, res: express.Response) {
|
2019-03-19 10:35:15 +01:00
|
|
|
const videoChannelInstance = res.locals.videoChannel
|
2018-07-31 14:04:26 +02:00
|
|
|
const oldVideoChannelAuditKeys = new VideoChannelAuditView(videoChannelInstance.toFormattedJSON())
|
2018-04-25 16:15:39 +02:00
|
|
|
const videoChannelInfoToUpdate = req.body as VideoChannelUpdate
|
2019-05-31 16:30:11 +02:00
|
|
|
let doBulkVideoUpdate = false
|
2018-04-25 16:15:39 +02:00
|
|
|
|
|
|
|
try {
|
|
|
|
await sequelizeTypescript.transaction(async t => {
|
2019-05-31 16:30:11 +02:00
|
|
|
if (videoChannelInfoToUpdate.displayName !== undefined) videoChannelInstance.name = videoChannelInfoToUpdate.displayName
|
|
|
|
if (videoChannelInfoToUpdate.description !== undefined) videoChannelInstance.description = videoChannelInfoToUpdate.description
|
|
|
|
|
|
|
|
if (videoChannelInfoToUpdate.support !== undefined) {
|
|
|
|
const oldSupportField = videoChannelInstance.support
|
|
|
|
videoChannelInstance.support = videoChannelInfoToUpdate.support
|
|
|
|
|
|
|
|
if (videoChannelInfoToUpdate.bulkVideosSupportUpdate === true && oldSupportField !== videoChannelInfoToUpdate.support) {
|
|
|
|
doBulkVideoUpdate = true
|
|
|
|
await VideoModel.bulkUpdateSupportField(videoChannelInstance, t)
|
|
|
|
}
|
|
|
|
}
|
2018-04-25 16:15:39 +02:00
|
|
|
|
2021-05-12 14:51:17 +02:00
|
|
|
const videoChannelInstanceUpdated = await videoChannelInstance.save({ transaction: t }) as MChannelBannerAccountDefault
|
2018-04-25 16:15:39 +02:00
|
|
|
await sendUpdateActor(videoChannelInstanceUpdated, t)
|
|
|
|
|
2018-07-31 14:04:26 +02:00
|
|
|
auditLogger.update(
|
2018-09-19 17:02:16 +02:00
|
|
|
getAuditIdFromRes(res),
|
2018-07-31 14:04:26 +02:00
|
|
|
new VideoChannelAuditView(videoChannelInstanceUpdated.toFormattedJSON()),
|
|
|
|
oldVideoChannelAuditKeys
|
|
|
|
)
|
2019-05-31 16:30:11 +02:00
|
|
|
|
2022-08-03 11:17:57 +02:00
|
|
|
Hooks.runAction('action:api.video-channel.updated', { videoChannel: videoChannelInstanceUpdated, req, res })
|
|
|
|
|
2019-05-31 14:02:26 +02:00
|
|
|
logger.info('Video channel %s updated.', videoChannelInstance.Actor.url)
|
2018-07-31 14:04:26 +02:00
|
|
|
})
|
2018-04-25 16:15:39 +02:00
|
|
|
} catch (err) {
|
|
|
|
logger.debug('Cannot update the video channel.', { err })
|
|
|
|
|
|
|
|
// If the transaction is retried, sequelize will think the object has not changed
|
2023-02-14 08:59:27 +01:00
|
|
|
// So we need to restore the previous fields
|
2023-05-11 15:02:53 +02:00
|
|
|
await resetSequelizeInstance(videoChannelInstance)
|
2018-04-25 16:15:39 +02:00
|
|
|
|
|
|
|
throw err
|
|
|
|
}
|
|
|
|
|
2020-12-07 14:32:36 +01:00
|
|
|
res.type('json').status(HttpStatusCode.NO_CONTENT_204).end()
|
2019-05-31 16:30:11 +02:00
|
|
|
|
|
|
|
// Don't process in a transaction, and after the response because it could be long
|
|
|
|
if (doBulkVideoUpdate) {
|
|
|
|
await federateAllVideosOfChannel(videoChannelInstance)
|
|
|
|
}
|
2018-04-25 16:15:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
async function removeVideoChannel (req: express.Request, res: express.Response) {
|
2019-03-19 10:35:15 +01:00
|
|
|
const videoChannelInstance = res.locals.videoChannel
|
2018-04-25 16:15:39 +02:00
|
|
|
|
2018-06-13 14:27:40 +02:00
|
|
|
await sequelizeTypescript.transaction(async t => {
|
2019-03-05 10:58:44 +01:00
|
|
|
await VideoPlaylistModel.resetPlaylistsOfChannel(videoChannelInstance.id, t)
|
|
|
|
|
2018-04-25 16:15:39 +02:00
|
|
|
await videoChannelInstance.destroy({ transaction: t })
|
|
|
|
|
2022-08-03 11:17:57 +02:00
|
|
|
Hooks.runAction('action:api.video-channel.deleted', { videoChannel: videoChannelInstance, req, res })
|
|
|
|
|
2018-09-19 17:02:16 +02:00
|
|
|
auditLogger.delete(getAuditIdFromRes(res), new VideoChannelAuditView(videoChannelInstance.toFormattedJSON()))
|
2019-05-31 14:02:26 +02:00
|
|
|
logger.info('Video channel %s deleted.', videoChannelInstance.Actor.url)
|
2018-04-25 16:15:39 +02:00
|
|
|
})
|
|
|
|
|
2020-12-07 14:32:36 +01:00
|
|
|
return res.type('json').status(HttpStatusCode.NO_CONTENT_204).end()
|
2018-04-25 16:15:39 +02:00
|
|
|
}
|
|
|
|
|
2022-08-03 11:17:57 +02:00
|
|
|
async function getVideoChannel (req: express.Request, res: express.Response) {
|
|
|
|
const id = res.locals.videoChannel.id
|
|
|
|
const videoChannel = await Hooks.wrapObject(res.locals.videoChannel, 'filter:api.video-channel.get.result', { id })
|
2018-04-25 16:15:39 +02:00
|
|
|
|
2021-04-06 17:01:35 +02:00
|
|
|
if (videoChannel.isOutdated()) {
|
2022-08-08 15:48:17 +02:00
|
|
|
JobQueue.Instance.createJobAsync({ type: 'activitypub-refresher', payload: { type: 'actor', url: videoChannel.Actor.url } })
|
2019-01-14 11:30:15 +01:00
|
|
|
}
|
|
|
|
|
2021-04-06 17:01:35 +02:00
|
|
|
return res.json(videoChannel.toFormattedJSON())
|
2018-04-25 16:15:39 +02:00
|
|
|
}
|
|
|
|
|
2019-02-26 10:55:40 +01:00
|
|
|
async function listVideoChannelPlaylists (req: express.Request, res: express.Response) {
|
|
|
|
const serverActor = await getServerActor()
|
|
|
|
|
|
|
|
const resultList = await VideoPlaylistModel.listForApi({
|
|
|
|
followerActorId: serverActor.id,
|
|
|
|
start: req.query.start,
|
|
|
|
count: req.query.count,
|
|
|
|
sort: req.query.sort,
|
2019-03-05 10:58:44 +01:00
|
|
|
videoChannelId: res.locals.videoChannel.id,
|
|
|
|
type: req.query.playlistType
|
2019-02-26 10:55:40 +01:00
|
|
|
})
|
|
|
|
|
|
|
|
return res.json(getFormattedObjects(resultList.data, resultList.total))
|
|
|
|
}
|
|
|
|
|
2019-03-19 10:35:15 +01:00
|
|
|
async function listVideoChannelVideos (req: express.Request, res: express.Response) {
|
2021-10-27 14:37:04 +02:00
|
|
|
const serverActor = await getServerActor()
|
|
|
|
|
2019-03-19 10:35:15 +01:00
|
|
|
const videoChannelInstance = res.locals.videoChannel
|
2021-10-27 14:37:04 +02:00
|
|
|
|
|
|
|
const displayOnlyForFollower = isUserAbleToSearchRemoteURI(res)
|
|
|
|
? null
|
|
|
|
: {
|
|
|
|
actorId: serverActor.id,
|
|
|
|
orLocalVideos: true
|
|
|
|
}
|
|
|
|
|
2020-01-08 14:15:16 +01:00
|
|
|
const countVideos = getCountVideos(req)
|
2021-07-29 11:54:38 +02:00
|
|
|
const query = pickCommonVideoQuery(req.query)
|
2018-04-25 16:15:39 +02:00
|
|
|
|
2020-12-08 10:30:33 +01:00
|
|
|
const apiOptions = await Hooks.wrapObject({
|
2021-07-29 11:54:38 +02:00
|
|
|
...query,
|
|
|
|
|
2021-10-27 14:37:04 +02:00
|
|
|
displayOnlyForFollower,
|
2021-05-03 11:06:19 +02:00
|
|
|
nsfw: buildNSFWFilter(res, query.nsfw),
|
2018-10-10 11:46:50 +02:00
|
|
|
videoChannelId: videoChannelInstance.id,
|
2020-01-08 14:15:16 +01:00
|
|
|
user: res.locals.oauth ? res.locals.oauth.token.User : undefined,
|
|
|
|
countVideos
|
2020-12-08 10:30:33 +01:00
|
|
|
}, 'filter:api.video-channels.videos.list.params')
|
|
|
|
|
|
|
|
const resultList = await Hooks.wrapPromiseFun(
|
|
|
|
VideoModel.listForApi,
|
|
|
|
apiOptions,
|
|
|
|
'filter:api.video-channels.videos.list.result'
|
|
|
|
)
|
2018-04-25 16:15:39 +02:00
|
|
|
|
2021-10-27 14:37:04 +02:00
|
|
|
return res.json(getFormattedObjects(resultList.data, resultList.total, guessAdditionalAttributesFromQuery(query)))
|
2018-04-25 16:15:39 +02:00
|
|
|
}
|
2021-10-19 09:44:43 +02:00
|
|
|
|
|
|
|
async function listVideoChannelFollowers (req: express.Request, res: express.Response) {
|
|
|
|
const channel = res.locals.videoChannel
|
|
|
|
|
|
|
|
const resultList = await ActorFollowModel.listFollowersForApi({
|
|
|
|
actorIds: [ channel.actorId ],
|
|
|
|
start: req.query.start,
|
|
|
|
count: req.query.count,
|
|
|
|
sort: req.query.sort,
|
|
|
|
search: req.query.search,
|
2021-10-19 15:02:43 +02:00
|
|
|
state: 'accepted'
|
2021-10-19 09:44:43 +02:00
|
|
|
})
|
|
|
|
|
|
|
|
return res.json(getFormattedObjects(resultList.data, resultList.total))
|
|
|
|
}
|
2022-08-10 09:53:39 +02:00
|
|
|
|
|
|
|
async function importVideosInChannel (req: express.Request, res: express.Response) {
|
2022-08-10 11:51:13 +02:00
|
|
|
const { externalChannelUrl } = req.body as VideosImportInChannelCreate
|
2022-08-10 09:53:39 +02:00
|
|
|
|
|
|
|
await JobQueue.Instance.createJob({
|
|
|
|
type: 'video-channel-import',
|
|
|
|
payload: {
|
|
|
|
externalChannelUrl,
|
2022-08-10 11:51:13 +02:00
|
|
|
videoChannelId: res.locals.videoChannel.id,
|
|
|
|
partOfChannelSyncId: res.locals.videoChannelSync?.id
|
2022-08-10 09:53:39 +02:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
logger.info('Video import job for channel "%s" with url "%s" created.', res.locals.videoChannel.name, externalChannelUrl)
|
|
|
|
|
|
|
|
return res.type('json').status(HttpStatusCode.NO_CONTENT_204).end()
|
|
|
|
}
|