} from '../../middlewares'
import { getAccountVideoRateValidator, videoCommentGetValidator } from '../../middlewares/validators'
import { AccountModel } from '../../models/account/account'
-import { ActorModel } from '../../models/activitypub/actor'
import { ActorFollowModel } from '../../models/activitypub/actor-follow'
import { VideoModel } from '../../models/video/video'
import { VideoCommentModel } from '../../models/video/video-comment'
import { videoPlaylistElementAPGetValidator, videoPlaylistsGetValidator } from '../../middlewares/validators/videos/video-playlists'
import { VideoPlaylistModel } from '../../models/video/video-playlist'
import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model'
+import { MAccountId, MActorId, MVideo, MVideoAPWithoutCaption } from '@server/typings/models'
const activityPubClientRouter = express.Router()
activityPubClientRouter.get('/video-playlists/:playlistId',
executeIfActivityPub,
- asyncMiddleware(videoPlaylistsGetValidator),
+ asyncMiddleware(videoPlaylistsGetValidator('all')),
asyncMiddleware(videoPlaylistController)
)
activityPubClientRouter.get('/video-playlists/:playlistId/:videoId',
async function videoController (req: express.Request, res: express.Response) {
// We need more attributes
- const video = await VideoModel.loadForGetAPI({ id: res.locals.video.id })
+ const video = await VideoModel.loadForGetAPI({ id: res.locals.onlyVideoWithRights.id }) as MVideoAPWithoutCaption
if (video.url.startsWith(WEBSERVER.URL) === false) return res.redirect(video.url)
// We need captions to render AP object
- video.VideoCaptions = await VideoCaptionModel.listVideoCaptions(video.id)
+ const captions = await VideoCaptionModel.listVideoCaptions(video.id)
+ const videoWithCaptions: MVideoAPWithoutCaption = Object.assign(video, { VideoCaptions: captions })
- const audience = getAudience(video.VideoChannel.Account.Actor, video.privacy === VideoPrivacy.PUBLIC)
- const videoObject = audiencify(video.toActivityPubObject(), audience)
+ const audience = getAudience(videoWithCaptions.VideoChannel.Account.Actor, videoWithCaptions.privacy === VideoPrivacy.PUBLIC)
+ const videoObject = audiencify(videoWithCaptions.toActivityPubObject(), audience)
if (req.path.endsWith('/activity')) {
- const data = buildCreateActivity(video.url, video.VideoChannel.Account.Actor, videoObject, audience)
+ const data = buildCreateActivity(videoWithCaptions.url, video.VideoChannel.Account.Actor, videoObject, audience)
return activityPubResponse(activityPubContextify(data), res)
}
if (share.url.startsWith(WEBSERVER.URL) === false) return res.redirect(share.url)
- const { activity } = await buildAnnounceWithVideoAudience(share.Actor, share, res.locals.video, undefined)
+ const { activity } = await buildAnnounceWithVideoAudience(share.Actor, share, res.locals.videoAll, undefined)
return activityPubResponse(activityPubContextify(activity), res)
}
async function videoAnnouncesController (req: express.Request, res: express.Response) {
- const video = res.locals.video
+ const video = res.locals.onlyVideo
const handler = async (start: number, count: number) => {
const result = await VideoShareModel.listAndCountByVideoId(video.id, start, count)
}
async function videoLikesController (req: express.Request, res: express.Response) {
- const video = res.locals.video
+ const video = res.locals.onlyVideo
const json = await videoRates(req, 'like', video, getVideoLikesActivityPubUrl(video))
return activityPubResponse(activityPubContextify(json), res)
}
async function videoDislikesController (req: express.Request, res: express.Response) {
- const video = res.locals.video
+ const video = res.locals.onlyVideo
const json = await videoRates(req, 'dislike', video, getVideoDislikesActivityPubUrl(video))
return activityPubResponse(activityPubContextify(json), res)
}
async function videoCommentsController (req: express.Request, res: express.Response) {
- const video = res.locals.video
+ const video = res.locals.onlyVideo
const handler = async (start: number, count: number) => {
const result = await VideoCommentModel.listAndCountByVideoId(video.id, start, count)
}
async function videoCommentController (req: express.Request, res: express.Response) {
- const videoComment = res.locals.videoComment
+ const videoComment = res.locals.videoCommentFull
if (videoComment.url.startsWith(WEBSERVER.URL) === false) return res.redirect(videoComment.url)
}
async function videoPlaylistController (req: express.Request, res: express.Response) {
- const playlist = res.locals.videoPlaylist
+ const playlist = res.locals.videoPlaylistFull
// We need more attributes
playlist.OwnerAccount = await AccountModel.load(playlist.ownerAccountId)
// ---------------------------------------------------------------------------
-async function actorFollowing (req: express.Request, actor: ActorModel) {
+async function actorFollowing (req: express.Request, actor: MActorId) {
const handler = (start: number, count: number) => {
return ActorFollowModel.listAcceptedFollowingUrlsForApi([ actor.id ], undefined, start, count)
}
return activityPubCollectionPagination(WEBSERVER.URL + req.path, handler, req.query.page)
}
-async function actorFollowers (req: express.Request, actor: ActorModel) {
+async function actorFollowers (req: express.Request, actor: MActorId) {
const handler = (start: number, count: number) => {
return ActorFollowModel.listAcceptedFollowerUrlsForAP([ actor.id ], undefined, start, count)
}
return activityPubCollectionPagination(WEBSERVER.URL + req.path, handler, req.query.page)
}
-async function actorPlaylists (req: express.Request, account: AccountModel) {
+async function actorPlaylists (req: express.Request, account: MAccountId) {
const handler = (start: number, count: number) => {
return VideoPlaylistModel.listPublicUrlsOfForAP(account.id, start, count)
}
return activityPubCollectionPagination(WEBSERVER.URL + req.path, handler, req.query.page)
}
-function videoRates (req: express.Request, rateType: VideoRateType, video: VideoModel, url: string) {
+function videoRates (req: express.Request, rateType: VideoRateType, video: MVideo, url: string) {
const handler = async (start: number, count: number) => {
const result = await AccountVideoRateModel.listAndCountAccountUrlsByVideoId(rateType, video.id, start, count)
return {
import { activityPubValidator } from '../../middlewares/validators/activitypub/activity'
import { queue } from 'async'
import { ActorModel } from '../../models/activitypub/actor'
-import { SignatureActorModel } from '../../typings/models'
+import { MActorDefault, MActorSignature } from '../../typings/models'
const inboxRouter = express.Router()
// ---------------------------------------------------------------------------
-const inboxQueue = queue<{ activities: Activity[], signatureActor?: SignatureActorModel, inboxActor?: ActorModel }, Error>((task, cb) => {
+type QueueParam = { activities: Activity[], signatureActor?: MActorSignature, inboxActor?: MActorDefault }
+const inboxQueue = queue<QueueParam, Error>((task, cb) => {
const options = { signatureActor: task.signatureActor, inboxActor: task.inboxActor }
processActivities(task.activities, options)
import { buildAnnounceActivity, buildCreateActivity } from '../../lib/activitypub/send'
import { buildAudience } from '../../lib/activitypub/audience'
import { asyncMiddleware, localAccountValidator, localVideoChannelValidator } from '../../middlewares'
-import { AccountModel } from '../../models/account/account'
-import { ActorModel } from '../../models/activitypub/actor'
import { VideoModel } from '../../models/video/video'
import { activityPubResponse } from './utils'
-import { VideoChannelModel } from '../../models/video/video-channel'
+import { MActorLight } from '@server/typings/models'
const outboxRouter = express.Router()
return activityPubResponse(activityPubContextify(json), res)
}
-async function buildActivities (actor: ActorModel, start: number, count: number) {
+async function buildActivities (actor: MActorLight, start: number, count: number) {
const data = await VideoModel.listAllAndSharedByActorForOutbox(actor.id, start, count)
const activities: Activity[] = []
- // Avoid too many SQL requests
- const actors = data.data.map(v => v.VideoChannel.Account.Actor)
- actors.push(actor)
-
for (const video of data.data) {
const byActor = video.VideoChannel.Account.Actor
const createActivityAudience = buildAudience([ byActor.followersUrl ], video.privacy === VideoPrivacy.PUBLIC)
import { logger } from '../../helpers/logger'
import { VideoChannelModel } from '../../models/video/video-channel'
import { loadActorUrlOrGetFromWebfinger } from '../../helpers/webfinger'
+import { MChannelAccountDefault, MVideoAccountAllFiles } from '../../typings/models'
const searchRouter = express.Router()
}
async function searchVideoChannelURI (search: string, isWebfingerSearch: boolean, res: express.Response) {
- let videoChannel: VideoChannelModel
+ let videoChannel: MChannelAccountDefault
let uri = search
if (isWebfingerSearch) {
}
async function searchVideoURI (url: string, res: express.Response) {
- let video: VideoModel
+ let video: MVideoAccountAllFiles
// Check if we can fetch a remote video with the URL
if (isUserAbleToSearchRemoteURI(res)) {
import { sequelizeTypescript } from '../../../initializers/database'
import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model'
import { UserRegister } from '../../../../shared/models/users/user-register.model'
+import { MUser, MUserAccountDefault } from '@server/typings/models'
const auditLogger = auditLoggerFactory('users')
res.end()
}
-async function changeUserBlock (res: express.Response, user: UserModel, block: boolean, reason?: string) {
+async function changeUserBlock (res: express.Response, user: MUserAccountDefault, block: boolean, reason?: string) {
const oldUserAuditView = new UserAuditView(user.toFormattedJSON())
user.blocked = block
}
async function getUserVideoRating (req: express.Request, res: express.Response) {
- const videoId = res.locals.video.id
+ const videoId = res.locals.videoId.id
const accountId = +res.locals.oauth.token.User.Account.id
const ratingObj = await AccountVideoRateModel.load(accountId, videoId, null)
setDefaultPagination,
userHistoryRemoveValidator
} from '../../../middlewares'
-import { UserModel } from '../../../models/account/user'
import { getFormattedObjects } from '../../../helpers/utils'
import { UserVideoHistoryModel } from '../../../models/account/user-video-history'
import { sequelizeTypescript } from '../../../initializers'
async function addVideoChannel (req: express.Request, res: express.Response) {
const videoChannelInfo: VideoChannelCreate = req.body
- const videoChannelCreated: VideoChannelModel = await sequelizeTypescript.transaction(async t => {
+ const videoChannelCreated = await sequelizeTypescript.transaction(async t => {
const account = await AccountModel.load(res.locals.oauth.token.User.Account.id, t)
return createVideoChannel(videoChannelInfo, account, t)
import { CONFIG } from '../../initializers/config'
import { sequelizeTypescript } from '../../initializers/database'
import { createPlaylistMiniatureFromExisting } from '../../lib/thumbnail'
-import { VideoModel } from '../../models/video/video'
+import { MVideoPlaylistFull, MVideoPlaylistThumbnail, MVideoThumbnail } from '@server/typings/models'
const reqThumbnailFile = createReqFiles([ 'thumbnailfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT, { thumbnailfile: CONFIG.STORAGE.TMP_DIR })
)
videoPlaylistRouter.get('/:playlistId',
- asyncMiddleware(videoPlaylistsGetValidator),
+ asyncMiddleware(videoPlaylistsGetValidator('summary')),
getVideoPlaylist
)
)
videoPlaylistRouter.get('/:playlistId/videos',
- asyncMiddleware(videoPlaylistsGetValidator),
+ asyncMiddleware(videoPlaylistsGetValidator('summary')),
paginationValidator,
setDefaultPagination,
optionalAuthenticate,
}
function getVideoPlaylist (req: express.Request, res: express.Response) {
- const videoPlaylist = res.locals.videoPlaylist
+ const videoPlaylist = res.locals.videoPlaylistSummary
if (videoPlaylist.isOutdated()) {
JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'video-playlist', url: videoPlaylist.url } })
description: videoPlaylistInfo.description,
privacy: videoPlaylistInfo.privacy || VideoPlaylistPrivacy.PRIVATE,
ownerAccountId: user.Account.id
- })
+ }) as MVideoPlaylistFull
videoPlaylist.url = getVideoPlaylistActivityPubUrl(videoPlaylist) // We use the UUID, so set the URL after building the object
? await createPlaylistMiniatureFromExisting(thumbnailField[0].path, videoPlaylist, false)
: undefined
- const videoPlaylistCreated: VideoPlaylistModel = await sequelizeTypescript.transaction(async t => {
- const videoPlaylistCreated = await videoPlaylist.save({ transaction: t })
+ const videoPlaylistCreated = await sequelizeTypescript.transaction(async t => {
+ const videoPlaylistCreated = await videoPlaylist.save({ transaction: t }) as MVideoPlaylistFull
if (thumbnailModel) {
thumbnailModel.automaticallyGenerated = false
}
async function updateVideoPlaylist (req: express.Request, res: express.Response) {
- const videoPlaylistInstance = res.locals.videoPlaylist
+ const videoPlaylistInstance = res.locals.videoPlaylistFull
const videoPlaylistFieldsSave = videoPlaylistInstance.toJSON()
const videoPlaylistInfoToUpdate = req.body as VideoPlaylistUpdate
}
async function removeVideoPlaylist (req: express.Request, res: express.Response) {
- const videoPlaylistInstance = res.locals.videoPlaylist
+ const videoPlaylistInstance = res.locals.videoPlaylistSummary
await sequelizeTypescript.transaction(async t => {
await videoPlaylistInstance.destroy({ transaction: t })
async function addVideoInPlaylist (req: express.Request, res: express.Response) {
const body: VideoPlaylistElementCreate = req.body
- const videoPlaylist = res.locals.videoPlaylist
- const video = res.locals.video
+ const videoPlaylist = res.locals.videoPlaylistFull
+ const video = res.locals.onlyVideo
- const playlistElement: VideoPlaylistElementModel = await sequelizeTypescript.transaction(async t => {
+ const playlistElement = await sequelizeTypescript.transaction(async t => {
const position = await VideoPlaylistElementModel.getNextPositionOf(videoPlaylist.id, t)
const playlistElement = await VideoPlaylistElementModel.create({
async function updateVideoPlaylistElement (req: express.Request, res: express.Response) {
const body: VideoPlaylistElementUpdate = req.body
- const videoPlaylist = res.locals.videoPlaylist
+ const videoPlaylist = res.locals.videoPlaylistFull
const videoPlaylistElement = res.locals.videoPlaylistElement
const playlistElement: VideoPlaylistElementModel = await sequelizeTypescript.transaction(async t => {
async function removeVideoFromPlaylist (req: express.Request, res: express.Response) {
const videoPlaylistElement = res.locals.videoPlaylistElement
- const videoPlaylist = res.locals.videoPlaylist
+ const videoPlaylist = res.locals.videoPlaylistFull
const positionToDelete = videoPlaylistElement.position
await sequelizeTypescript.transaction(async t => {
}
async function reorderVideosPlaylist (req: express.Request, res: express.Response) {
- const videoPlaylist = res.locals.videoPlaylist
+ const videoPlaylist = res.locals.videoPlaylistFull
const body: VideoPlaylistReorder = req.body
const start: number = body.startPosition
}
async function getVideoPlaylistVideos (req: express.Request, res: express.Response) {
- const videoPlaylistInstance = res.locals.videoPlaylist
+ const videoPlaylistInstance = res.locals.videoPlaylistSummary
const user = res.locals.oauth ? res.locals.oauth.token.User : undefined
const server = await getServerActor()
return res.json(getFormattedObjects(resultList.data, resultList.total, options))
}
-async function regeneratePlaylistThumbnail (videoPlaylist: VideoPlaylistModel) {
+async function regeneratePlaylistThumbnail (videoPlaylist: MVideoPlaylistThumbnail) {
await videoPlaylist.Thumbnail.destroy()
videoPlaylist.Thumbnail = null
if (firstElement) await generateThumbnailForPlaylist(videoPlaylist, firstElement.Video)
}
-async function generateThumbnailForPlaylist (videoPlaylist: VideoPlaylistModel, video: VideoModel) {
+async function generateThumbnailForPlaylist (videoPlaylist: MVideoPlaylistThumbnail, video: MVideoThumbnail) {
logger.info('Generating default thumbnail to playlist %s.', videoPlaylist.url)
const inputPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, video.getMiniature().filename)
import { auditLoggerFactory, VideoAbuseAuditView } from '../../../helpers/audit-logger'
import { Notifier } from '../../../lib/notifier'
import { sendVideoAbuse } from '../../../lib/activitypub/send/send-flag'
+import { MVideoAbuseAccountVideo } from '../../../typings/models/video'
const auditLogger = auditLoggerFactory('abuse')
const abuseVideoRouter = express.Router()
}
async function reportVideoAbuse (req: express.Request, res: express.Response) {
- const videoInstance = res.locals.video
+ const videoInstance = res.locals.videoAll
const body: VideoAbuseCreate = req.body
- const videoAbuse: VideoAbuseModel = await sequelizeTypescript.transaction(async t => {
+ const videoAbuse = await sequelizeTypescript.transaction(async t => {
const reporterAccount = await AccountModel.load(res.locals.oauth.token.User.Account.id, t)
const abuseToCreate = {
state: VideoAbuseState.PENDING
}
- const videoAbuseInstance = await VideoAbuseModel.create(abuseToCreate, { transaction: t })
+ const videoAbuseInstance: MVideoAbuseAccountVideo = await VideoAbuseModel.create(abuseToCreate, { transaction: t })
videoAbuseInstance.Video = videoInstance
videoAbuseInstance.Account = reporterAccount
import * as express from 'express'
-import { VideoBlacklist, UserRight, VideoBlacklistCreate, VideoBlacklistType } from '../../../../shared'
+import { UserRight, VideoBlacklistCreate, VideoBlacklistType } from '../../../../shared'
import { logger } from '../../../helpers/logger'
import { getFormattedObjects } from '../../../helpers/utils'
import {
setBlacklistSort,
setDefaultPagination,
videosBlacklistAddValidator,
+ videosBlacklistFiltersValidator,
videosBlacklistRemoveValidator,
- videosBlacklistUpdateValidator,
- videosBlacklistFiltersValidator
+ videosBlacklistUpdateValidator
} from '../../../middlewares'
import { VideoBlacklistModel } from '../../../models/video/video-blacklist'
import { sequelizeTypescript } from '../../../initializers'
import { Notifier } from '../../../lib/notifier'
import { sendDeleteVideo } from '../../../lib/activitypub/send'
import { federateVideoIfNeeded } from '../../../lib/activitypub'
+import { MVideoBlacklistVideo } from '@server/typings/models'
const blacklistRouter = express.Router()
// ---------------------------------------------------------------------------
async function addVideoToBlacklist (req: express.Request, res: express.Response) {
- const videoInstance = res.locals.video
+ const videoInstance = res.locals.videoAll
const body: VideoBlacklistCreate = req.body
const toCreate = {
type: VideoBlacklistType.MANUAL
}
- const blacklist = await VideoBlacklistModel.create(toCreate)
+ const blacklist: MVideoBlacklistVideo = await VideoBlacklistModel.create(toCreate)
blacklist.Video = videoInstance
if (body.unfederate === true) {
Notifier.Instance.notifyOnVideoBlacklist(blacklist)
- logger.info('Video %s blacklisted.', res.locals.video.uuid)
+ logger.info('Video %s blacklisted.', videoInstance.uuid)
return res.type('json').status(204).end()
}
async function removeVideoFromBlacklistController (req: express.Request, res: express.Response) {
const videoBlacklist = res.locals.videoBlacklist
- const video = res.locals.video
+ const video = res.locals.videoAll
const videoBlacklistType = await sequelizeTypescript.transaction(async t => {
const unfederated = videoBlacklist.unfederated
Notifier.Instance.notifyOnNewVideoIfNeeded(video)
}
- logger.info('Video %s removed from blacklist.', res.locals.video.uuid)
+ logger.info('Video %s removed from blacklist.', video.uuid)
return res.type('json').status(204).end()
}
import { moveAndProcessCaptionFile } from '../../../helpers/captions-utils'
import { CONFIG } from '../../../initializers/config'
import { sequelizeTypescript } from '../../../initializers/database'
+import { MVideoCaptionVideo } from '@server/typings/models'
const reqVideoCaptionAdd = createReqFiles(
[ 'captionfile' ],
// ---------------------------------------------------------------------------
async function listVideoCaptions (req: express.Request, res: express.Response) {
- const data = await VideoCaptionModel.listVideoCaptions(res.locals.video.id)
+ const data = await VideoCaptionModel.listVideoCaptions(res.locals.videoId.id)
return res.json(getFormattedObjects(data, data.length))
}
async function addVideoCaption (req: express.Request, res: express.Response) {
const videoCaptionPhysicalFile = req.files['captionfile'][0]
- const video = res.locals.video
+ const video = res.locals.videoAll
const videoCaption = new VideoCaptionModel({
videoId: video.id,
language: req.params.captionLanguage
- })
+ }) as MVideoCaptionVideo
videoCaption.Video = video
// Move physical file
}
async function deleteVideoCaption (req: express.Request, res: express.Response) {
- const video = res.locals.video
+ const video = res.locals.videoAll
const videoCaption = res.locals.videoCaption
await sequelizeTypescript.transaction(async t => {
import { AccountModel } from '../../../models/account/account'
import { Notifier } from '../../../lib/notifier'
import { Hooks } from '../../../lib/plugins/hooks'
-import { ActorModel } from '../../../models/activitypub/actor'
-import { VideoChannelModel } from '../../../models/video/video-channel'
-import { VideoModel } from '../../../models/video/video'
import { sendDeleteVideoComment } from '../../../lib/activitypub/send'
const auditLogger = auditLoggerFactory('comments')
// ---------------------------------------------------------------------------
async function listVideoThreads (req: express.Request, res: express.Response) {
- const video = res.locals.video
+ const video = res.locals.onlyVideo
const user = res.locals.oauth ? res.locals.oauth.token.User : undefined
let resultList: ResultList<VideoCommentModel>
start: req.query.start,
count: req.query.count,
sort: req.query.sort,
- user: user
+ user
}, 'filter:api.video-threads.list.params')
resultList = await Hooks.wrapPromiseFun(
}
async function listVideoThreadComments (req: express.Request, res: express.Response) {
- const video = res.locals.video
+ const video = res.locals.onlyVideo
const user = res.locals.oauth ? res.locals.oauth.token.User : undefined
let resultList: ResultList<VideoCommentModel>
return createVideoComment({
text: videoCommentInfo.text,
inReplyToComment: null,
- video: res.locals.video,
+ video: res.locals.videoAll,
account
}, t)
})
return createVideoComment({
text: videoCommentInfo.text,
- inReplyToComment: res.locals.videoComment,
- video: res.locals.video,
+ inReplyToComment: res.locals.videoCommentFull,
+ video: res.locals.videoAll,
account
}, t)
})
}
async function removeVideoComment (req: express.Request, res: express.Response) {
- const videoCommentInstance = res.locals.videoComment
+ const videoCommentInstance = res.locals.videoCommentFull
await sequelizeTypescript.transaction(async t => {
await videoCommentInstance.destroy({ transaction: t })
import { JobQueue } from '../../../lib/job-queue/job-queue'
import { join } from 'path'
import { isArray } from '../../../helpers/custom-validators/misc'
-import { VideoChannelModel } from '../../../models/video/video-channel'
import * as Bluebird from 'bluebird'
import * as parseTorrent from 'parse-torrent'
import { getSecureTorrentName } from '../../../helpers/utils'
import { sequelizeTypescript } from '../../../initializers/database'
import { createVideoMiniatureFromExisting } from '../../../lib/thumbnail'
import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type'
-import { ThumbnailModel } from '../../../models/video/thumbnail'
-import { UserModel } from '../../../models/account/user'
+import {
+ MChannelActorAccountDefault,
+ MThumbnail,
+ MUser,
+ MVideoThumbnailAccountDefault,
+ MVideoWithBlacklistLight
+} from '@server/typings/models'
+import { MVideoImport, MVideoImportVideo } from '@server/typings/models/video/video-import'
const auditLogger = auditLoggerFactory('video-imports')
const videoImportsRouter = express.Router()
}
function insertIntoDB (parameters: {
- video: VideoModel,
- thumbnailModel: ThumbnailModel,
- previewModel: ThumbnailModel,
- videoChannel: VideoChannelModel,
+ video: MVideoThumbnailAccountDefault,
+ thumbnailModel: MThumbnail,
+ previewModel: MThumbnail,
+ videoChannel: MChannelActorAccountDefault,
tags: string[],
- videoImportAttributes: Partial<VideoImportModel>,
- user: UserModel
-}): Bluebird<VideoImportModel> {
+ videoImportAttributes: Partial<MVideoImport>,
+ user: MUser
+}): Bluebird<MVideoImportVideo> {
const { video, thumbnailModel, previewModel, videoChannel, tags, videoImportAttributes, user } = parameters
return sequelizeTypescript.transaction(async t => {
const sequelizeOptions = { transaction: t }
// Save video object in database
- const videoCreated = await video.save(sequelizeOptions)
+ const videoCreated = await video.save(sequelizeOptions) as (MVideoThumbnailAccountDefault & MVideoWithBlacklistLight)
videoCreated.VideoChannel = videoChannel
if (thumbnailModel) await videoCreated.addAndSaveThumbnail(thumbnailModel, t)
if (previewModel) await videoCreated.addAndSaveThumbnail(previewModel, t)
await autoBlacklistVideoIfNeeded({
- video,
+ video: videoCreated,
user,
notify: false,
isRemote: false,
const tagInstances = await TagModel.findOrCreateTags(tags, t)
await videoCreated.$set('Tags', tagInstances, sequelizeOptions)
- videoCreated.Tags = tagInstances
- } else {
- videoCreated.Tags = []
}
// Create video import object in database
const videoImport = await VideoImportModel.create(
Object.assign({ videoId: videoCreated.id }, videoImportAttributes),
sequelizeOptions
- )
+ ) as MVideoImportVideo
videoImport.Video = videoCreated
return videoImport
import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type'
import { VideoTranscodingPayload } from '../../../lib/job-queue/handlers/video-transcoding'
import { Hooks } from '../../../lib/plugins/hooks'
+import { MVideoFullLight } from '@server/typings/models'
const auditLogger = auditLoggerFactory('videos')
const videosRouter = express.Router()
const { videoCreated } = await sequelizeTypescript.transaction(async t => {
const sequelizeOptions = { transaction: t }
- const videoCreated = await video.save(sequelizeOptions)
+ const videoCreated = await video.save(sequelizeOptions) as MVideoFullLight
await videoCreated.addAndSaveThumbnail(thumbnailModel, t)
await videoCreated.addAndSaveThumbnail(previewModel, t)
}
async function updateVideo (req: express.Request, res: express.Response) {
- const videoInstance = res.locals.video
+ const videoInstance = res.locals.videoAll
const videoFieldsSave = videoInstance.toJSON()
const oldVideoAuditView = new VideoAuditView(videoInstance.toFormattedDetailsJSON())
const videoInfoToUpdate: VideoUpdate = req.body
}
}
- const videoInstanceUpdated = await videoInstance.save(sequelizeOptions)
+ const videoInstanceUpdated = await videoInstance.save(sequelizeOptions) as MVideoFullLight
if (thumbnailModel) await videoInstanceUpdated.addAndSaveThumbnail(thumbnailModel, t)
if (previewModel) await videoInstanceUpdated.addAndSaveThumbnail(previewModel, t)
const video = await Hooks.wrapPromiseFun(
VideoModel.loadForGetAPI,
- { id: res.locals.video.id, userId },
+ { id: res.locals.onlyVideoWithRights.id, userId },
'filter:api.video.get.result'
)
}
async function viewVideo (req: express.Request, res: express.Response) {
- const videoInstance = res.locals.video
+ const videoInstance = res.locals.videoAll
const ip = req.ip
const exists = await Redis.Instance.doesVideoIPViewExist(ip, videoInstance.uuid)
}
async function getVideoDescription (req: express.Request, res: express.Response) {
- const videoInstance = res.locals.video
+ const videoInstance = res.locals.videoAll
let description = ''
if (videoInstance.isOwned()) {
}
async function removeVideo (req: express.Request, res: express.Response) {
- const videoInstance = res.locals.video
+ const videoInstance = res.locals.videoAll
await sequelizeTypescript.transaction(async t => {
await videoInstance.destroy({ transaction: t })
import { changeVideoChannelShare } from '../../../lib/activitypub'
import { sendUpdateVideo } from '../../../lib/activitypub/send'
import { VideoModel } from '../../../models/video/video'
+import { MVideoFullLight } from '@server/typings/models'
const ownershipVideoRouter = express.Router()
// ---------------------------------------------------------------------------
async function giveVideoOwnership (req: express.Request, res: express.Response) {
- const videoInstance = res.locals.video
+ const videoInstance = res.locals.videoAll
const initiatorAccountId = res.locals.oauth.token.User.Account.id
const nextOwner = res.locals.nextOwner
targetVideo.channelId = channel.id
- const targetVideoUpdated = await targetVideo.save({ transaction: t })
+ const targetVideoUpdated = await targetVideo.save({ transaction: t }) as MVideoFullLight
targetVideoUpdated.VideoChannel = channel
if (targetVideoUpdated.privacy !== VideoPrivacy.PRIVATE && targetVideoUpdated.state === VideoState.PUBLISHED) {
async function rateVideo (req: express.Request, res: express.Response) {
const body: UserVideoRateUpdate = req.body
const rateType = body.rating
- const videoInstance = res.locals.video
+ const videoInstance = res.locals.videoAll
const userAccount = res.locals.oauth.token.User.Account
await sequelizeTypescript.transaction(async t => {
const user = res.locals.oauth.token.User
const body: UserWatchingVideo = req.body
- const { id: videoId } = res.locals.video as { id: number }
+ const { id: videoId } = res.locals.videoId
await UserVideoHistoryModel.upsert({
videoId,
async function generateVideoCommentsFeed (req: express.Request, res: express.Response) {
const start = 0
- const video = res.locals.video
+ const video = res.locals.videoAll
const videoId: number = video ? video.id : undefined
const comments = await VideoCommentModel.listForFeed(start, FEEDS.COUNT, videoId)
// ---------------------------------------------------------------------------
function generateOEmbed (req: express.Request, res: express.Response) {
- const video = res.locals.video
+ const video = res.locals.videoAll
const webserverUrl = WEBSERVER.URL
const maxHeight = parseInt(req.query.maxheight, 10)
const maxWidth = parseInt(req.query.maxwidth, 10)
return res.send(json).end()
}
-async function downloadTorrent (req: express.Request, res: express.Response, next: express.NextFunction) {
+async function downloadTorrent (req: express.Request, res: express.Response) {
const { video, videoFile } = getVideoAndFile(req, res)
if (!videoFile) return res.status(404).end()
return res.download(video.getTorrentFilePath(videoFile), `${video.name}-${videoFile.resolution}p.torrent`)
}
-async function downloadVideoFile (req: express.Request, res: express.Response, next: express.NextFunction) {
+async function downloadVideoFile (req: express.Request, res: express.Response) {
const { video, videoFile } = getVideoAndFile(req, res)
if (!videoFile) return res.status(404).end()
function getVideoAndFile (req: express.Request, res: express.Response) {
const resolution = parseInt(req.params.resolution, 10)
- const video = res.locals.video
+ const video = res.locals.videoAll
const videoFile = video.VideoFiles.find(f => f.resolution === resolution)
// ---------------------------------------------------------------------------
function webfingerController (req: express.Request, res: express.Response) {
- const actor = res.locals.actor
+ const actor = res.locals.actorFull
const json = {
subject: req.query.resource,
import { signJsonLDObject } from './peertube-crypto'
import { pageToStartAndCount } from './core-utils'
import { parse } from 'url'
+import { MActor } from '../typings/models'
function activityPubContextify <T> (data: T) {
return Object.assign(data, {
}
-function buildSignedActivity (byActor: ActorModel, data: Object) {
+function buildSignedActivity (byActor: MActor, data: Object) {
const activity = activityPubContextify(data)
return signJsonLDObject(byActor, activity) as Promise<Activity>
import { ActorModel } from '../models/activitypub/actor'
+import * as Bluebird from 'bluebird'
+import { MActorFull, MActorAccountChannelId } from '../typings/models'
-type ActorFetchByUrlType = 'all' | 'actor-and-association-ids'
-function fetchActorByUrl (url: string, fetchType: ActorFetchByUrlType) {
+type ActorFetchByUrlType = 'all' | 'association-ids'
+
+function fetchActorByUrl (url: string, fetchType: ActorFetchByUrlType): Bluebird<MActorFull | MActorAccountChannelId> {
if (fetchType === 'all') return ActorModel.loadByUrlAndPopulateAccountAndChannel(url)
- if (fetchType === 'actor-and-association-ids') return ActorModel.loadByUrl(url)
+ if (fetchType === 'association-ids') return ActorModel.loadByUrl(url)
}
export {
import { join } from 'path'
import { CONFIG } from '../initializers/config'
-import { VideoCaptionModel } from '../models/video/video-caption'
import * as srt2vtt from 'srt-to-vtt'
-import { createReadStream, createWriteStream, remove, move } from 'fs-extra'
+import { createReadStream, createWriteStream, move, remove } from 'fs-extra'
+import { MVideoCaption } from '@server/typings/models'
-async function moveAndProcessCaptionFile (physicalFile: { filename: string, path: string }, videoCaption: VideoCaptionModel) {
+async function moveAndProcessCaptionFile (physicalFile: { filename: string, path: string }, videoCaption: MVideoCaption) {
const videoCaptionsDir = CONFIG.STORAGE.CAPTIONS_DIR
const destination = join(videoCaptionsDir, videoCaption.getCaptionName())
import { Response } from 'express'
-import * as validator from 'validator'
import { VideoChangeOwnershipModel } from '../../models/video/video-change-ownership'
-import { UserModel } from '../../models/account/user'
+import { MVideoChangeOwnershipFull } from '@server/typings/models/video/video-change-ownership'
+import { MUserId } from '@server/typings/models'
-export async function doesChangeVideoOwnershipExist (id: string, res: Response): Promise<boolean> {
- const videoChangeOwnership = await loadVideoChangeOwnership(id)
+export async function doesChangeVideoOwnershipExist (id: number, res: Response) {
+ const videoChangeOwnership = await VideoChangeOwnershipModel.load(id)
if (!videoChangeOwnership) {
res.status(404)
return true
}
-async function loadVideoChangeOwnership (id: string): Promise<VideoChangeOwnershipModel | undefined> {
- if (validator.isInt(id)) {
- return VideoChangeOwnershipModel.load(parseInt(id, 10))
- }
-
- return undefined
-}
-
-export function checkUserCanTerminateOwnershipChange (
- user: UserModel,
- videoChangeOwnership: VideoChangeOwnershipModel,
- res: Response
-): boolean {
+export function checkUserCanTerminateOwnershipChange (user: MUserId, videoChangeOwnership: MVideoChangeOwnershipFull, res: Response) {
if (videoChangeOwnership.NextOwner.userId === user.id) {
return true
}
import { Response } from 'express'
import { AccountModel } from '../../models/account/account'
import * as Bluebird from 'bluebird'
+import { MAccountDefault } from '../../typings/models'
function doesAccountIdExist (id: number, res: Response, sendNotFound = true) {
const promise = AccountModel.load(id)
}
function doesAccountNameWithHostExist (nameWithDomain: string, res: Response, sendNotFound = true) {
- return doesAccountExist(AccountModel.loadByNameWithHost(nameWithDomain), res, sendNotFound)
+ const promise = AccountModel.loadByNameWithHost(nameWithDomain)
+
+ return doesAccountExist(promise, res, sendNotFound)
}
-async function doesAccountExist (p: Bluebird<AccountModel>, res: Response, sendNotFound: boolean) {
+async function doesAccountExist (p: Bluebird<MAccountDefault>, res: Response, sendNotFound: boolean) {
const account = await p
if (!account) {
-import * as express from 'express'
-import { VideoChannelModel } from '../../models/video/video-channel'
+import { Response } from 'express'
+import { VideoAbuseModel } from '../../models/video/video-abuse'
-async function doesLocalVideoChannelNameExist (name: string, res: express.Response) {
- const videoChannel = await VideoChannelModel.loadLocalByNameAndPopulateAccount(name)
+async function doesVideoAbuseExist (abuseId: number, videoId: number, res: Response) {
+ const videoAbuse = await VideoAbuseModel.loadByIdAndVideoId(abuseId, videoId)
- return processVideoChannelExist(videoChannel, res)
-}
-
-async function doesVideoChannelIdExist (id: number, res: express.Response) {
- const videoChannel = await VideoChannelModel.loadAndPopulateAccount(+id)
-
- return processVideoChannelExist(videoChannel, res)
-}
-
-async function doesVideoChannelNameWithHostExist (nameWithDomain: string, res: express.Response) {
- const videoChannel = await VideoChannelModel.loadByNameWithHostAndPopulateAccount(nameWithDomain)
-
- return processVideoChannelExist(videoChannel, res)
-}
-
-// ---------------------------------------------------------------------------
-
-export {
- doesLocalVideoChannelNameExist,
- doesVideoChannelIdExist,
- doesVideoChannelNameWithHostExist
-}
-
-function processVideoChannelExist (videoChannel: VideoChannelModel, res: express.Response) {
- if (!videoChannel) {
+ if (videoAbuse === null) {
res.status(404)
- .json({ error: 'Video channel not found' })
+ .json({ error: 'Video abuse not found' })
.end()
return false
}
- res.locals.videoChannel = videoChannel
+ res.locals.videoAbuse = videoAbuse
return true
}
+
+// ---------------------------------------------------------------------------
+
+export {
+ doesVideoAbuseExist
+}
-import { VideoModel } from '../../models/video/video'
import { Response } from 'express'
import { VideoCaptionModel } from '../../models/video/video-caption'
+import { MVideoId } from '@server/typings/models'
-async function doesVideoCaptionExist (video: VideoModel, language: string, res: Response) {
+async function doesVideoCaptionExist (video: MVideoId, language: string, res: Response) {
const videoCaption = await VideoCaptionModel.loadByVideoIdAndLanguage(video.id, language)
if (!videoCaption) {
-import { Response } from 'express'
-import { VideoAbuseModel } from '../../models/video/video-abuse'
+import * as express from 'express'
+import { VideoChannelModel } from '../../models/video/video-channel'
+import { MChannelActorAccountDefault } from '../../typings/models'
-async function doesVideoAbuseExist (abuseId: number, videoId: number, res: Response) {
- const videoAbuse = await VideoAbuseModel.loadByIdAndVideoId(abuseId, videoId)
+async function doesLocalVideoChannelNameExist (name: string, res: express.Response) {
+ const videoChannel = await VideoChannelModel.loadLocalByNameAndPopulateAccount(name)
- if (videoAbuse === null) {
- res.status(404)
- .json({ error: 'Video abuse not found' })
- .end()
+ return processVideoChannelExist(videoChannel, res)
+}
- return false
- }
+async function doesVideoChannelIdExist (id: number, res: express.Response) {
+ const videoChannel = await VideoChannelModel.loadAndPopulateAccount(+id)
- res.locals.videoAbuse = videoAbuse
- return true
+ return processVideoChannelExist(videoChannel, res)
+}
+
+async function doesVideoChannelNameWithHostExist (nameWithDomain: string, res: express.Response) {
+ const videoChannel = await VideoChannelModel.loadByNameWithHostAndPopulateAccount(nameWithDomain)
+
+ return processVideoChannelExist(videoChannel, res)
}
// ---------------------------------------------------------------------------
export {
- doesVideoAbuseExist
+ doesLocalVideoChannelNameExist,
+ doesVideoChannelIdExist,
+ doesVideoChannelNameWithHostExist
+}
+
+function processVideoChannelExist (videoChannel: MChannelActorAccountDefault, res: express.Response) {
+ if (!videoChannel) {
+ res.status(404)
+ .json({ error: 'Video channel not found' })
+ .end()
+
+ return false
+ }
+
+ res.locals.videoChannel = videoChannel
+ return true
}
import * as express from 'express'
import { VideoPlaylistModel } from '../../models/video/video-playlist'
+import { MVideoPlaylist } from '../../typings/models/video/video-playlist'
-async function doesVideoPlaylistExist (id: number | string, res: express.Response, fetchType: 'summary' | 'all' = 'summary') {
- const videoPlaylist = fetchType === 'summary'
- ? await VideoPlaylistModel.loadWithAccountAndChannelSummary(id, undefined)
- : await VideoPlaylistModel.loadWithAccountAndChannel(id, undefined)
+export type VideoPlaylistFetchType = 'summary' | 'all'
+async function doesVideoPlaylistExist (id: number | string, res: express.Response, fetchType: VideoPlaylistFetchType = 'summary') {
+ if (fetchType === 'summary') {
+ const videoPlaylist = await VideoPlaylistModel.loadWithAccountAndChannelSummary(id, undefined)
+ res.locals.videoPlaylistSummary = videoPlaylist
+ return handleVideoPlaylist(videoPlaylist, res)
+ }
+
+ const videoPlaylist = await VideoPlaylistModel.loadWithAccountAndChannel(id, undefined)
+ res.locals.videoPlaylistFull = videoPlaylist
+
+ return handleVideoPlaylist(videoPlaylist, res)
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+ doesVideoPlaylistExist
+}
+
+// ---------------------------------------------------------------------------
+
+function handleVideoPlaylist (videoPlaylist: MVideoPlaylist, res: express.Response) {
if (!videoPlaylist) {
res.status(404)
.json({ error: 'Video playlist not found' })
return false
}
- res.locals.videoPlaylist = videoPlaylist
return true
}
-
-// ---------------------------------------------------------------------------
-
-export {
- doesVideoPlaylistExist
-}
import { Response } from 'express'
import { fetchVideo, VideoFetchType } from '../video'
-import { UserModel } from '../../models/account/user'
import { UserRight } from '../../../shared/models/users'
import { VideoChannelModel } from '../../models/video/video-channel'
-import { VideoModel } from '../../models/video/video'
+import { MUser, MUserAccountId, MVideoAccountLight, MVideoFullLight, MVideoWithRights } from '@server/typings/models'
async function doesVideoExist (id: number | string, res: Response, fetchType: VideoFetchType = 'all') {
const userId = res.locals.oauth ? res.locals.oauth.token.User.id : undefined
return false
}
- if (fetchType !== 'none') res.locals.video = video
+ switch (fetchType) {
+ case 'all':
+ res.locals.videoAll = video as MVideoFullLight
+ break
+
+ case 'id':
+ res.locals.videoId = video
+ break
+
+ case 'only-video':
+ res.locals.onlyVideo = video
+ break
+
+ case 'only-video-with-rights':
+ res.locals.onlyVideoWithRights = video as MVideoWithRights
+ break
+ }
+
return true
}
-async function doesVideoChannelOfAccountExist (channelId: number, user: UserModel, res: Response) {
+async function doesVideoChannelOfAccountExist (channelId: number, user: MUserAccountId, res: Response) {
if (user.hasRight(UserRight.UPDATE_ANY_VIDEO) === true) {
const videoChannel = await VideoChannelModel.loadAndPopulateAccount(channelId)
if (videoChannel === null) {
return true
}
-function checkUserCanManageVideo (user: UserModel, video: VideoModel, right: UserRight, res: Response) {
+function checkUserCanManageVideo (user: MUser, video: MVideoAccountLight, right: UserRight, res: Response) {
// Retrieve the user who did the request
if (video.isOwned() === false) {
res.status(403)
import { createVerify } from 'crypto'
import { buildDigest } from '../lib/job-queue/handlers/utils/activitypub-http-utils'
import * as bcrypt from 'bcrypt'
+import { MActor } from '../typings/models'
const bcryptComparePromise = promisify2<any, string, boolean>(bcrypt.compare)
const bcryptGenSaltPromise = promisify1<number, string>(bcrypt.genSalt)
return true
}
-function isHTTPSignatureVerified (httpSignatureParsed: any, actor: ActorModel): boolean {
+function isHTTPSignatureVerified (httpSignatureParsed: any, actor: MActor): boolean {
return httpSignature.verifySignature(httpSignatureParsed, actor.publicKey) === true
}
// JSONLD
-async function isJsonLDSignatureVerified (fromActor: ActorModel, signedDocument: any): Promise<boolean> {
+async function isJsonLDSignatureVerified (fromActor: MActor, signedDocument: any): Promise<boolean> {
if (signedDocument.signature.type === 'RsaSignature2017') {
// Mastodon algorithm
const res = await isJsonLDRSA2017Verified(fromActor, signedDocument)
}
// Backward compatibility with "other" implementations
-async function isJsonLDRSA2017Verified (fromActor: ActorModel, signedDocument: any) {
+async function isJsonLDRSA2017Verified (fromActor: MActor, signedDocument: any) {
function hash (obj: any): Promise<any> {
return jsonld.promises
.normalize(obj, {
return verify.verify(fromActor.publicKey, signedDocument.signature.signatureValue, 'base64')
}
-function signJsonLDObject (byActor: ActorModel, data: any) {
+function signJsonLDObject (byActor: MActor, data: any) {
const options = {
privateKeyPem: byActor.privateKey,
creator: byActor.url,
import { VideoModel } from '../models/video/video'
+import * as Bluebird from 'bluebird'
+import { MVideoAccountAllFiles, MVideoFullLight, MVideoThumbnail, MVideoWithRights, MVideoIdThumbnail } from '@server/typings/models'
+import { Response } from 'express'
type VideoFetchType = 'all' | 'only-video' | 'only-video-with-rights' | 'id' | 'none'
-function fetchVideo (id: number | string, fetchType: VideoFetchType, userId?: number) {
+function fetchVideo (id: number | string, fetchType: 'all', userId?: number): Bluebird<MVideoFullLight>
+function fetchVideo (id: number | string, fetchType: 'only-video', userId?: number): Bluebird<MVideoThumbnail>
+function fetchVideo (id: number | string, fetchType: 'only-video-with-rights', userId?: number): Bluebird<MVideoWithRights>
+function fetchVideo (id: number | string, fetchType: 'id' | 'none', userId?: number): Bluebird<MVideoIdThumbnail>
+function fetchVideo (
+ id: number | string,
+ fetchType: VideoFetchType,
+ userId?: number
+): Bluebird<MVideoFullLight | MVideoThumbnail | MVideoWithRights | MVideoIdThumbnail>
+function fetchVideo (
+ id: number | string,
+ fetchType: VideoFetchType,
+ userId?: number
+): Bluebird<MVideoFullLight | MVideoThumbnail | MVideoWithRights | MVideoIdThumbnail> {
if (fetchType === 'all') return VideoModel.loadAndPopulateAccountAndServerAndTags(id, undefined, userId)
if (fetchType === 'only-video-with-rights') return VideoModel.loadWithRights(id)
}
type VideoFetchByUrlType = 'all' | 'only-video'
-function fetchVideoByUrl (url: string, fetchType: VideoFetchByUrlType) {
+
+function fetchVideoByUrl (url: string, fetchType: 'all'): Bluebird<MVideoAccountAllFiles>
+function fetchVideoByUrl (url: string, fetchType: 'only-video'): Bluebird<MVideoThumbnail>
+function fetchVideoByUrl (url: string, fetchType: VideoFetchByUrlType): Bluebird<MVideoAccountAllFiles> | Bluebird<MVideoThumbnail>
+function fetchVideoByUrl (url: string, fetchType: VideoFetchByUrlType): Bluebird<MVideoAccountAllFiles> | Bluebird<MVideoThumbnail> {
if (fetchType === 'all') return VideoModel.loadByUrlAndPopulateAccount(url)
if (fetchType === 'only-video') return VideoModel.loadByUrl(url)
}
+function getVideo (res: Response) {
+ return res.locals.videoAll || res.locals.onlyVideo || res.locals.onlyVideoWithRights || res.locals.videoId
+}
+
export {
VideoFetchType,
VideoFetchByUrlType,
fetchVideo,
+ getVideo,
fetchVideoByUrl
}
import { isTestInstance } from './core-utils'
import { isActivityPubUrlValid } from './custom-validators/activitypub/misc'
import { WEBSERVER } from '../initializers/constants'
+import { MActorFull } from '../typings/models'
const webfinger = new WebFinger({
webfist_fallback: false,
const uri = uriArg.startsWith('@') ? uriArg.slice(1) : uriArg
const [ name, host ] = uri.split('@')
- let actor: ActorModel
+ let actor: MActorFull
if (!host || host === WEBSERVER.HOST) {
actor = await ActorModel.loadLocalByName(name)
import { getServerActor } from '../../helpers/utils'
import { ActorFetchByUrlType, fetchActorByUrl } from '../../helpers/actor'
import { sequelizeTypescript } from '../../initializers/database'
+import {
+ MAccount,
+ MActor,
+ MActorAccountChannelId,
+ MActorAccountId,
+ MActorDefault,
+ MActorFull,
+ MActorId,
+ MActorAccountChannelIdActor,
+ MChannel,
+ MActorFullActor, MAccountActorDefault, MChannelActorDefault, MChannelActorAccountDefault
+} from '../../typings/models'
// Set account keys, this could be long so process after the account creation and do not block the client
-function setAsyncActorKeys (actor: ActorModel) {
+function setAsyncActorKeys (actor: MActor) {
return createPrivateAndPublicKeys()
.then(({ publicKey, privateKey }) => {
- actor.set('publicKey', publicKey)
- actor.set('privateKey', privateKey)
+ actor.publicKey = publicKey
+ actor.privateKey = privateKey
return actor.save()
})
.catch(err => {
})
}
+function getOrCreateActorAndServerAndModel (
+ activityActor: string | ActivityPubActor,
+ fetchType: 'all',
+ recurseIfNeeded?: boolean,
+ updateCollections?: boolean
+): Promise<MActorFullActor>
+
+function getOrCreateActorAndServerAndModel (
+ activityActor: string | ActivityPubActor,
+ fetchType?: 'association-ids',
+ recurseIfNeeded?: boolean,
+ updateCollections?: boolean
+): Promise<MActorAccountChannelId>
+
async function getOrCreateActorAndServerAndModel (
activityActor: string | ActivityPubActor,
- fetchType: ActorFetchByUrlType = 'actor-and-association-ids',
+ fetchType: ActorFetchByUrlType = 'association-ids',
recurseIfNeeded = true,
updateCollections = false
-) {
+): Promise<MActorFullActor | MActorAccountChannelId> {
const actorUrl = getAPId(activityActor)
let created = false
let accountPlaylistsUrl: string
// Create the attributed to actor
// In PeerTube a video channel is owned by an account
- let ownerActor: ActorModel = undefined
+ let ownerActor: MActorFullActor
if (recurseIfNeeded === true && result.actor.type === 'Group') {
const accountAttributedTo = result.attributedTo.find(a => a.type === 'Person')
if (!accountAttributedTo) throw new Error('Cannot find account attributed to video channel ' + actor.url)
accountPlaylistsUrl = result.playlists
}
- if (actor.Account) actor.Account.Actor = actor
- if (actor.VideoChannel) actor.VideoChannel.Actor = actor
+ if (actor.Account) (actor as MActorAccountChannelIdActor).Account.Actor = actor
+ if (actor.VideoChannel) (actor as MActorAccountChannelIdActor).VideoChannel.Actor = actor
const { actor: actorRefreshed, refreshed } = await retryTransactionWrapper(refreshActorIfNeeded, actor, fetchType)
if (!actorRefreshed) throw new Error('Actor ' + actorRefreshed.url + ' does not exist anymore.')
actorInstance.followingUrl = attributes.following
}
-async function updateActorAvatarInstance (actor: ActorModel, info: { name: string, onDisk: boolean, fileUrl: string }, t: Transaction) {
+type AvatarInfo = { name: string, onDisk: boolean, fileUrl: string }
+async function updateActorAvatarInstance (actor: MActorDefault, info: AvatarInfo, t: Transaction) {
if (info.name !== undefined) {
if (actor.avatarId) {
try {
return JobQueue.Instance.createJob({ type: 'activitypub-http-fetcher', payload })
}
-async function refreshActorIfNeeded (
- actorArg: ActorModel,
+async function refreshActorIfNeeded <T extends MActorFull | MActorAccountChannelId> (
+ actorArg: T,
fetchedType: ActorFetchByUrlType
-): Promise<{ actor: ActorModel, refreshed: boolean }> {
+): Promise<{ actor: T | MActorFull, refreshed: boolean }> {
if (!actorArg.isOutdated()) return { actor: actorArg, refreshed: false }
// We need more attributes
- const actor = fetchedType === 'all' ? actorArg : await ActorModel.loadByUrlAndPopulateAccountAndChannel(actorArg.url)
+ const actor = fetchedType === 'all'
+ ? actorArg as MActorFull
+ : await ActorModel.loadByUrlAndPopulateAccountAndChannel(actorArg.url)
try {
let actorUrl: string
function saveActorAndServerAndModelIfNotExist (
result: FetchRemoteActorResult,
- ownerActor?: ActorModel,
+ ownerActor?: MActorFullActor,
t?: Transaction
-): Bluebird<ActorModel> | Promise<ActorModel> {
+): Bluebird<MActorFullActor> | Promise<MActorFullActor> {
let actor = result.actor
if (t !== undefined) return save(t)
// Force the actor creation, sometimes Sequelize skips the save() when it thinks the instance already exists
// (which could be false in a retried query)
- const [ actorCreated ] = await ActorModel.findOrCreate({
+ const [ actorCreated ] = await ActorModel.findOrCreate<MActorFullActor>({
defaults: actor.toJSON(),
where: {
url: actor.url
})
if (actorCreated.type === 'Person' || actorCreated.type === 'Application') {
- actorCreated.Account = await saveAccount(actorCreated, result, t)
+ actorCreated.Account = await saveAccount(actorCreated, result, t) as MAccountActorDefault
actorCreated.Account.Actor = actorCreated
} else if (actorCreated.type === 'Group') { // Video channel
- actorCreated.VideoChannel = await saveVideoChannel(actorCreated, result, ownerActor, t)
+ actorCreated.VideoChannel = await saveVideoChannel(actorCreated, result, ownerActor, t) as MChannelActorAccountDefault
actorCreated.VideoChannel.Actor = actorCreated
actorCreated.VideoChannel.Account = ownerActor.Account
}
}
type FetchRemoteActorResult = {
- actor: ActorModel
+ actor: MActor
name: string
summary: string
support?: string
}
}
-async function saveAccount (actor: ActorModel, result: FetchRemoteActorResult, t: Transaction) {
+async function saveAccount (actor: MActorId, result: FetchRemoteActorResult, t: Transaction) {
const [ accountCreated ] = await AccountModel.findOrCreate({
defaults: {
name: result.name,
transaction: t
})
- return accountCreated
+ return accountCreated as MAccount
}
-async function saveVideoChannel (actor: ActorModel, result: FetchRemoteActorResult, ownerActor: ActorModel, t: Transaction) {
+async function saveVideoChannel (actor: MActorId, result: FetchRemoteActorResult, ownerActor: MActorAccountId, t: Transaction) {
const [ videoChannelCreated ] = await VideoChannelModel.findOrCreate({
defaults: {
name: result.name,
transaction: t
})
- return videoChannelCreated
+ return videoChannelCreated as MChannel
}
import { ACTIVITY_PUB } from '../../initializers/constants'
import { ActorModel } from '../../models/activitypub/actor'
import { VideoModel } from '../../models/video/video'
-import { VideoCommentModel } from '../../models/video/video-comment'
import { VideoShareModel } from '../../models/video/video-share'
-import { ActorModelOnly } from '../../typings/models'
+import { MActorFollowersUrl, MActorLight, MCommentOwner, MCommentOwnerVideo, MVideo, MVideoAccountLight } from '../../typings/models'
-function getRemoteVideoAudience (video: VideoModel, actorsInvolvedInVideo: ActorModel[]): ActivityAudience {
+function getRemoteVideoAudience (video: MVideoAccountLight, actorsInvolvedInVideo: MActorFollowersUrl[]): ActivityAudience {
return {
to: [ video.VideoChannel.Account.Actor.url ],
cc: actorsInvolvedInVideo.map(a => a.followersUrl)
}
function getVideoCommentAudience (
- videoComment: VideoCommentModel,
- threadParentComments: VideoCommentModel[],
- actorsInvolvedInVideo: ActorModel[],
+ videoComment: MCommentOwnerVideo,
+ threadParentComments: MCommentOwner[],
+ actorsInvolvedInVideo: MActorFollowersUrl[],
isOrigin = false
): ActivityAudience {
const to = [ ACTIVITY_PUB.PUBLIC ]
}
}
-function getAudienceFromFollowersOf (actorsInvolvedInObject: ActorModel[]): ActivityAudience {
+function getAudienceFromFollowersOf (actorsInvolvedInObject: MActorFollowersUrl[]): ActivityAudience {
return {
to: [ ACTIVITY_PUB.PUBLIC ].concat(actorsInvolvedInObject.map(a => a.followersUrl)),
cc: []
}
}
-async function getActorsInvolvedInVideo (video: VideoModel, t: Transaction) {
- const actors = await VideoShareModel.loadActorsByShare(video.id, t)
+async function getActorsInvolvedInVideo (video: MVideo, t: Transaction) {
+ const actors: MActorLight[] = await VideoShareModel.loadActorsByShare(video.id, t)
- const videoActor = video.VideoChannel && video.VideoChannel.Account
- ? video.VideoChannel.Account.Actor
- : await ActorModel.loadAccountActorByVideoId(video.id, t)
+ const videoAll = video as VideoModel
+
+ const videoActor = videoAll.VideoChannel && videoAll.VideoChannel.Account
+ ? videoAll.VideoChannel.Account.Actor
+ : await ActorModel.loadFromAccountByVideoId(video.id, t)
actors.push(videoActor)
return actors
}
-function getAudience (actorSender: ActorModelOnly, isPublic = true) {
+function getAudience (actorSender: MActorFollowersUrl, isPublic = true) {
return buildAudience([ actorSender.followersUrl ], isPublic)
}
import { CacheFileObject } from '../../../shared/index'
-import { VideoModel } from '../../models/video/video'
import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy'
import { Transaction } from 'sequelize'
import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type'
+import { MActorId, MVideoRedundancy, MVideoWithAllFiles } from '@server/typings/models'
-function cacheFileActivityObjectToDBAttributes (cacheFileObject: CacheFileObject, video: VideoModel, byActor: { id?: number }) {
+function cacheFileActivityObjectToDBAttributes (cacheFileObject: CacheFileObject, video: MVideoWithAllFiles, byActor: MActorId) {
if (cacheFileObject.url.mediaType === 'application/x-mpegURL') {
const url = cacheFileObject.url
}
}
-async function createOrUpdateCacheFile (cacheFileObject: CacheFileObject, video: VideoModel, byActor: { id?: number }, t: Transaction) {
+async function createOrUpdateCacheFile (cacheFileObject: CacheFileObject, video: MVideoWithAllFiles, byActor: MActorId, t: Transaction) {
const redundancyModel = await VideoRedundancyModel.loadByUrl(cacheFileObject.id, t)
if (!redundancyModel) {
}
}
-function createCacheFile (cacheFileObject: CacheFileObject, video: VideoModel, byActor: { id?: number }, t: Transaction) {
+function createCacheFile (cacheFileObject: CacheFileObject, video: MVideoWithAllFiles, byActor: MActorId, t: Transaction) {
const attributes = cacheFileActivityObjectToDBAttributes(cacheFileObject, video, byActor)
return VideoRedundancyModel.create(attributes, { transaction: t })
function updateCacheFile (
cacheFileObject: CacheFileObject,
- redundancyModel: VideoRedundancyModel,
- video: VideoModel,
- byActor: { id?: number },
+ redundancyModel: MVideoRedundancy,
+ video: MVideoWithAllFiles,
+ byActor: MActorId,
t: Transaction
) {
if (redundancyModel.actorId !== byActor.id) {
import { PlaylistObject } from '../../../shared/models/activitypub/objects/playlist-object'
import { crawlCollectionPage } from './crawl'
import { ACTIVITY_PUB, CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants'
-import { AccountModel } from '../../models/account/account'
import { isArray } from '../../helpers/custom-validators/misc'
import { getOrCreateActorAndServerAndModel } from './actor'
import { logger } from '../../helpers/logger'
import { getOrCreateVideoAndAccountAndChannel } from './videos'
import { isPlaylistElementObjectValid, isPlaylistObjectValid } from '../../helpers/custom-validators/activitypub/playlist'
import { VideoPlaylistElementModel } from '../../models/video/video-playlist-element'
-import { VideoModel } from '../../models/video/video'
import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model'
import { sequelizeTypescript } from '../../initializers/database'
import { createPlaylistMiniatureFromUrl } from '../thumbnail'
import { FilteredModelAttributes } from '../../typings/sequelize'
-import { AccountModelId } from '../../typings/models'
+import { MAccountDefault, MAccountId, MVideoId } from '../../typings/models'
+import { MVideoPlaylist, MVideoPlaylistId, MVideoPlaylistOwner } from '../../typings/models/video/video-playlist'
-function playlistObjectToDBAttributes (playlistObject: PlaylistObject, byAccount: AccountModelId, to: string[]) {
+function playlistObjectToDBAttributes (playlistObject: PlaylistObject, byAccount: MAccountId, to: string[]) {
const privacy = to.indexOf(ACTIVITY_PUB.PUBLIC) !== -1 ? VideoPlaylistPrivacy.PUBLIC : VideoPlaylistPrivacy.UNLISTED
return {
}
}
-function playlistElementObjectToDBAttributes (elementObject: PlaylistElementObject, videoPlaylist: VideoPlaylistModel, video: VideoModel) {
+function playlistElementObjectToDBAttributes (elementObject: PlaylistElementObject, videoPlaylist: MVideoPlaylistId, video: MVideoId) {
return {
position: elementObject.position,
url: elementObject.id,
}
}
-async function createAccountPlaylists (playlistUrls: string[], account: AccountModel) {
+async function createAccountPlaylists (playlistUrls: string[], account: MAccountDefault) {
await Bluebird.map(playlistUrls, async playlistUrl => {
try {
const exists = await VideoPlaylistModel.doesPlaylistExist(playlistUrl)
}, { concurrency: CRAWL_REQUEST_CONCURRENCY })
}
-async function createOrUpdateVideoPlaylist (playlistObject: PlaylistObject, byAccount: AccountModelId, to: string[]) {
+async function createOrUpdateVideoPlaylist (playlistObject: PlaylistObject, byAccount: MAccountId, to: string[]) {
const playlistAttributes = playlistObjectToDBAttributes(playlistObject, byAccount, to)
if (isArray(playlistObject.attributedTo) && playlistObject.attributedTo.length === 1) {
}
}
- const [ playlist ] = await VideoPlaylistModel.upsert<VideoPlaylistModel>(playlistAttributes, { returning: true })
+ const [ playlist ] = await VideoPlaylistModel.upsert<MVideoPlaylist>(playlistAttributes, { returning: true })
let accItems: string[] = []
await crawlCollectionPage<string>(playlistObject.id, items => {
return resetVideoPlaylistElements(accItems, refreshedPlaylist)
}
-async function refreshVideoPlaylistIfNeeded (videoPlaylist: VideoPlaylistModel): Promise<VideoPlaylistModel> {
+async function refreshVideoPlaylistIfNeeded (videoPlaylist: MVideoPlaylistOwner): Promise<MVideoPlaylistOwner> {
if (!videoPlaylist.isOutdated()) return videoPlaylist
try {
// ---------------------------------------------------------------------------
-async function resetVideoPlaylistElements (elementUrls: string[], playlist: VideoPlaylistModel) {
+async function resetVideoPlaylistElements (elementUrls: string[], playlist: MVideoPlaylist) {
const elementsToCreate: FilteredModelAttributes<VideoPlaylistElementModel>[] = []
await Bluebird.map(elementUrls, async elementUrl => {
import { ActivityAccept } from '../../../../shared/models/activitypub'
-import { ActorModel } from '../../../models/activitypub/actor'
import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
import { addFetchOutboxJob } from '../actor'
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
-import { SignatureActorModel } from '../../../typings/models'
+import { MActorDefault, MActorSignature } from '../../../typings/models'
async function processAcceptActivity (options: APProcessorOptions<ActivityAccept>) {
const { byActor: targetActor, inboxActor } = options
// ---------------------------------------------------------------------------
-async function processAccept (actor: ActorModel, targetActor: SignatureActorModel) {
+async function processAccept (actor: MActorDefault, targetActor: MActorSignature) {
const follow = await ActorFollowModel.loadByActorAndTarget(actor.id, targetActor.id)
if (!follow) throw new Error('Cannot find associated follow.')
import { forwardVideoRelatedActivity } from '../send/utils'
import { getOrCreateVideoAndAccountAndChannel } from '../videos'
import { Notifier } from '../../notifier'
-import { VideoModel } from '../../../models/video/video'
import { logger } from '../../../helpers/logger'
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
-import { SignatureActorModel } from '../../../typings/models'
+import { MActorSignature, MVideoAccountAllFiles } from '../../../typings/models'
async function processAnnounceActivity (options: APProcessorOptions<ActivityAnnounce>) {
const { activity, byActor: actorAnnouncer } = options
// ---------------------------------------------------------------------------
-async function processVideoShare (actorAnnouncer: SignatureActorModel, activity: ActivityAnnounce, notify: boolean) {
+async function processVideoShare (actorAnnouncer: MActorSignature, activity: ActivityAnnounce, notify: boolean) {
const objectUri = typeof activity.object === 'string' ? activity.object : activity.object.id
- let video: VideoModel
+ let video: MVideoAccountAllFiles
let videoCreated: boolean
try {
import { Notifier } from '../../notifier'
import { PlaylistObject } from '../../../../shared/models/activitypub/objects/playlist-object'
import { createOrUpdateVideoPlaylist } from '../playlist'
-import { VideoModel } from '../../../models/video/video'
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
-import { VideoCommentModel } from '../../../models/video/video-comment'
-import { SignatureActorModel } from '../../../typings/models'
+import { MActorSignature, MCommentOwnerVideo, MVideoAccountAllFiles } from '../../../typings/models'
async function processCreateActivity (options: APProcessorOptions<ActivityCreate>) {
const { activity, byActor } = options
return video
}
-async function processCreateCacheFile (activity: ActivityCreate, byActor: SignatureActorModel) {
+async function processCreateCacheFile (activity: ActivityCreate, byActor: MActorSignature) {
const cacheFile = activity.object as CacheFileObject
const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: cacheFile.object })
}
}
-async function processCreateVideoComment (activity: ActivityCreate, byActor: SignatureActorModel, notify: boolean) {
+async function processCreateVideoComment (activity: ActivityCreate, byActor: MActorSignature, notify: boolean) {
const commentObject = activity.object as VideoCommentObject
const byAccount = byActor.Account
if (!byAccount) throw new Error('Cannot create video comment with the non account actor ' + byActor.url)
- let video: VideoModel
+ let video: MVideoAccountAllFiles
let created: boolean
- let comment: VideoCommentModel
+ let comment: MCommentOwnerVideo
try {
const resolveThreadResult = await resolveThread({ url: commentObject.id, isVideo: false })
video = resolveThreadResult.video
if (created && notify) Notifier.Instance.notifyOnNewComment(comment)
}
-async function processCreatePlaylist (activity: ActivityCreate, byActor: SignatureActorModel) {
+async function processCreatePlaylist (activity: ActivityCreate, byActor: MActorSignature) {
const playlistObject = activity.object as PlaylistObject
const byAccount = byActor.Account
import { retryTransactionWrapper } from '../../../helpers/database-utils'
import { logger } from '../../../helpers/logger'
import { sequelizeTypescript } from '../../../initializers'
-import { AccountModel } from '../../../models/account/account'
import { ActorModel } from '../../../models/activitypub/actor'
import { VideoModel } from '../../../models/video/video'
-import { VideoChannelModel } from '../../../models/video/video-channel'
import { VideoCommentModel } from '../../../models/video/video-comment'
import { forwardVideoRelatedActivity } from '../send/utils'
import { VideoPlaylistModel } from '../../../models/video/video-playlist'
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
-import { SignatureActorModel } from '../../../typings/models'
+import { MAccountActor, MActor, MActorSignature, MChannelActor, MChannelActorAccountActor } from '../../../typings/models'
async function processDeleteActivity (options: APProcessorOptions<ActivityDelete>) {
const { activity, byActor } = options
if (byActorFull.type === 'Person') {
if (!byActorFull.Account) throw new Error('Actor ' + byActorFull.url + ' is a person but we cannot find it in database.')
- byActorFull.Account.Actor = await byActorFull.Account.$get('Actor') as ActorModel
- return retryTransactionWrapper(processDeleteAccount, byActorFull.Account)
+ const accountToDelete = byActorFull.Account as MAccountActor
+ accountToDelete.Actor = byActorFull
+
+ return retryTransactionWrapper(processDeleteAccount, accountToDelete)
} else if (byActorFull.type === 'Group') {
if (!byActorFull.VideoChannel) throw new Error('Actor ' + byActorFull.url + ' is a group but we cannot find it in database.')
- byActorFull.VideoChannel.Actor = await byActorFull.VideoChannel.$get('Actor') as ActorModel
- return retryTransactionWrapper(processDeleteVideoChannel, byActorFull.VideoChannel)
+ const channelToDelete = byActorFull.VideoChannel as MChannelActorAccountActor
+ channelToDelete.Actor = byActorFull
+
+ return retryTransactionWrapper(processDeleteVideoChannel, channelToDelete)
}
}
// ---------------------------------------------------------------------------
-async function processDeleteVideo (actor: ActorModel, videoToDelete: VideoModel) {
+async function processDeleteVideo (actor: MActor, videoToDelete: VideoModel) {
logger.debug('Removing remote video "%s".', videoToDelete.uuid)
await sequelizeTypescript.transaction(async t => {
logger.info('Remote video with uuid %s removed.', videoToDelete.uuid)
}
-async function processDeleteVideoPlaylist (actor: ActorModel, playlistToDelete: VideoPlaylistModel) {
+async function processDeleteVideoPlaylist (actor: MActor, playlistToDelete: VideoPlaylistModel) {
logger.debug('Removing remote video playlist "%s".', playlistToDelete.uuid)
await sequelizeTypescript.transaction(async t => {
logger.info('Remote video playlist with uuid %s removed.', playlistToDelete.uuid)
}
-async function processDeleteAccount (accountToRemove: AccountModel) {
+async function processDeleteAccount (accountToRemove: MAccountActor) {
logger.debug('Removing remote account "%s".', accountToRemove.Actor.url)
await sequelizeTypescript.transaction(async t => {
logger.info('Remote account %s removed.', accountToRemove.Actor.url)
}
-async function processDeleteVideoChannel (videoChannelToRemove: VideoChannelModel) {
+async function processDeleteVideoChannel (videoChannelToRemove: MChannelActor) {
logger.debug('Removing remote video channel "%s".', videoChannelToRemove.Actor.url)
await sequelizeTypescript.transaction(async t => {
logger.info('Remote video channel %s removed.', videoChannelToRemove.Actor.url)
}
-function processDeleteVideoComment (byActor: SignatureActorModel, videoComment: VideoCommentModel, activity: ActivityDelete) {
+function processDeleteVideoComment (byActor: MActorSignature, videoComment: VideoCommentModel, activity: ActivityDelete) {
logger.debug('Removing remote video comment "%s".', videoComment.url)
return sequelizeTypescript.transaction(async t => {
import { forwardVideoRelatedActivity } from '../send/utils'
import { getVideoDislikeActivityPubUrl } from '../url'
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
-import { SignatureActorModel } from '../../../typings/models'
+import { MActorSignature } from '../../../typings/models'
async function processDislikeActivity (options: APProcessorOptions<ActivityCreate | ActivityDislike>) {
const { activity, byActor } = options
// ---------------------------------------------------------------------------
-async function processDislike (activity: ActivityCreate | ActivityDislike, byActor: SignatureActorModel) {
+async function processDislike (activity: ActivityCreate | ActivityDislike, byActor: MActorSignature) {
const dislikeObject = activity.type === 'Dislike' ? activity.object : (activity.object as DislikeObject).object
const byAccount = byActor.Account
import { Notifier } from '../../notifier'
import { getAPId } from '../../../helpers/activitypub'
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
-import { SignatureActorModel } from '../../../typings/models'
+import { MActorSignature, MVideoAbuseVideo } from '../../../typings/models'
async function processFlagActivity (options: APProcessorOptions<ActivityCreate | ActivityFlag>) {
const { activity, byActor } = options
// ---------------------------------------------------------------------------
-async function processCreateVideoAbuse (activity: ActivityCreate | ActivityFlag, byActor: SignatureActorModel) {
+async function processCreateVideoAbuse (activity: ActivityCreate | ActivityFlag, byActor: MActorSignature) {
const flag = activity.type === 'Flag' ? activity : (activity.object as VideoAbuseObject)
logger.debug('Reporting remote abuse for video %s.', getAPId(flag.object))
state: VideoAbuseState.PENDING
}
- const videoAbuseInstance = await VideoAbuseModel.create(videoAbuseData, { transaction: t })
+ const videoAbuseInstance = await VideoAbuseModel.create(videoAbuseData, { transaction: t }) as MVideoAbuseVideo
videoAbuseInstance.Video = video
logger.info('Remote abuse for video uuid %s created', flag.object)
import { getServerActor } from '../../../helpers/utils'
import { CONFIG } from '../../../initializers/config'
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
-import { SignatureActorModel } from '../../../typings/models'
-import { ActorFollowModelLight } from '../../../typings/models/actor-follow'
+import { MAccount, MActorFollowActors, MActorFollowFull, MActorSignature } from '../../../typings/models'
async function processFollowActivity (options: APProcessorOptions<ActivityFollow>) {
const { activity, byActor } = options
// ---------------------------------------------------------------------------
-async function processFollow (byActor: SignatureActorModel, targetActorURL: string) {
+async function processFollow (byActor: MActorSignature, targetActorURL: string) {
const { actorFollow, created, isFollowingInstance } = await sequelizeTypescript.transaction(async t => {
const targetActor = await ActorModel.loadByUrlAndPopulateAccountAndChannel(targetActorURL, t)
await sendReject(byActor, targetActor)
- return { actorFollow: undefined }
+ return { actorFollow: undefined as MActorFollowActors }
}
- const [ actorFollow, created ] = await ActorFollowModel.findOrCreate({
+ const [ actorFollow, created ] = await ActorFollowModel.findOrCreate<MActorFollowActors>({
where: {
actorId: byActor.id,
targetActorId: targetActor.id
state: CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL ? 'pending' : 'accepted'
},
transaction: t
- }) as [ ActorFollowModelLight, boolean ]
+ })
if (actorFollow.state !== 'accepted' && CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL === false) {
actorFollow.state = 'accepted'
if (!actorFollow) return
if (created) {
- if (isFollowingInstance) Notifier.Instance.notifyOfNewInstanceFollow(actorFollow)
- else Notifier.Instance.notifyOfNewUserFollow(actorFollow)
+ if (isFollowingInstance) {
+ Notifier.Instance.notifyOfNewInstanceFollow(actorFollow)
+ } else {
+ const actorFollowFull = actorFollow as MActorFollowFull
+ actorFollowFull.ActorFollower.Account = await actorFollow.ActorFollower.$get('Account') as MAccount
+
+ Notifier.Instance.notifyOfNewUserFollow(actorFollowFull)
+ }
}
logger.info('Actor %s is followed by actor %s.', targetActorURL, byActor.url)
import { getVideoLikeActivityPubUrl } from '../url'
import { getAPId } from '../../../helpers/activitypub'
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
-import { SignatureActorModel } from '../../../typings/models'
+import { MActorSignature } from '../../../typings/models'
async function processLikeActivity (options: APProcessorOptions<ActivityLike>) {
const { activity, byActor } = options
// ---------------------------------------------------------------------------
-async function processLikeVideo (byActor: SignatureActorModel, activity: ActivityLike) {
+async function processLikeVideo (byActor: MActorSignature, activity: ActivityLike) {
const videoUrl = getAPId(activity.object)
const byAccount = byActor.Account
import { sequelizeTypescript } from '../../../initializers'
import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
-import { ActorModelOnly } from '../../../typings/models'
+import { MActor } from '../../../typings/models'
async function processRejectActivity (options: APProcessorOptions<ActivityReject>) {
const { byActor: targetActor, inboxActor } = options
// ---------------------------------------------------------------------------
-async function processReject (follower: ActorModelOnly, targetActor: ActorModelOnly) {
+async function processReject (follower: MActor, targetActor: MActor) {
return sequelizeTypescript.transaction(async t => {
const actorFollow = await ActorFollowModel.loadByActorAndTarget(follower.id, targetActor.id, t)
import { VideoShareModel } from '../../../models/video/video-share'
import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy'
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
-import { SignatureActorModel } from '../../../typings/models'
+import { MActorSignature } from '../../../typings/models'
async function processUndoActivity (options: APProcessorOptions<ActivityUndo>) {
const { activity, byActor } = options
// ---------------------------------------------------------------------------
-async function processUndoLike (byActor: SignatureActorModel, activity: ActivityUndo) {
+async function processUndoLike (byActor: MActorSignature, activity: ActivityUndo) {
const likeActivity = activity.object as ActivityLike
const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: likeActivity.object })
})
}
-async function processUndoDislike (byActor: SignatureActorModel, activity: ActivityUndo) {
+async function processUndoDislike (byActor: MActorSignature, activity: ActivityUndo) {
const dislike = activity.object.type === 'Dislike'
? activity.object
: activity.object.object as DislikeObject
})
}
-async function processUndoCacheFile (byActor: SignatureActorModel, activity: ActivityUndo) {
+async function processUndoCacheFile (byActor: MActorSignature, activity: ActivityUndo) {
const cacheFileObject = activity.object.object as CacheFileObject
const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: cacheFileObject.object })
})
}
-function processUndoFollow (follower: SignatureActorModel, followActivity: ActivityFollow) {
+function processUndoFollow (follower: MActorSignature, followActivity: ActivityFollow) {
return sequelizeTypescript.transaction(async t => {
const following = await ActorModel.loadByUrlAndPopulateAccountAndChannel(followActivity.object, t)
const actorFollow = await ActorFollowModel.loadByActorAndTarget(follower.id, following.id, t)
})
}
-function processUndoAnnounce (byActor: SignatureActorModel, announceActivity: ActivityAnnounce) {
+function processUndoAnnounce (byActor: MActorSignature, announceActivity: ActivityAnnounce) {
return sequelizeTypescript.transaction(async t => {
const share = await VideoShareModel.loadByUrl(announceActivity.id, t)
if (!share) throw new Error(`Unknown video share ${announceActivity.id}.`)
import { PlaylistObject } from '../../../../shared/models/activitypub/objects/playlist-object'
import { createOrUpdateVideoPlaylist } from '../playlist'
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
-import { SignatureActorModel } from '../../../typings/models'
+import { MActorSignature } from '../../../typings/models'
async function processUpdateActivity (options: APProcessorOptions<ActivityUpdate>) {
const { activity, byActor } = options
// ---------------------------------------------------------------------------
-async function processUpdateVideo (actor: SignatureActorModel, activity: ActivityUpdate) {
+async function processUpdateVideo (actor: MActorSignature, activity: ActivityUpdate) {
const videoObject = activity.object as VideoTorrentObject
if (sanitizeAndCheckVideoTorrentObject(videoObject) === false) {
return undefined
}
- const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: videoObject.id, allowRefresh: false })
+ const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: videoObject.id, allowRefresh: false, fetchType: 'all' })
const channelActor = await getOrCreateVideoChannelFromVideoObject(videoObject)
const updateOptions = {
video,
videoObject,
- account: actor.Account,
+ account: channelActor.VideoChannel.Account,
channel: channelActor.VideoChannel,
overrideTo: activity.to
}
return updateVideoFromAP(updateOptions)
}
-async function processUpdateCacheFile (byActor: SignatureActorModel, activity: ActivityUpdate) {
+async function processUpdateCacheFile (byActor: MActorSignature, activity: ActivityUpdate) {
const cacheFileObject = activity.object as CacheFileObject
if (!isCacheFileObjectValid(cacheFileObject)) {
}
}
-async function processUpdatePlaylist (byActor: SignatureActorModel, activity: ActivityUpdate) {
+async function processUpdatePlaylist (byActor: MActorSignature, activity: ActivityUpdate) {
const playlistObject = activity.object as PlaylistObject
const byAccount = byActor.Account
import { Redis } from '../../redis'
import { ActivityCreate, ActivityView, ViewObject } from '../../../../shared/models/activitypub'
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
-import { SignatureActorModel } from '../../../typings/models'
+import { MActorSignature } from '../../../typings/models'
async function processViewActivity (options: APProcessorOptions<ActivityCreate | ActivityView>) {
const { activity, byActor } = options
// ---------------------------------------------------------------------------
-async function processCreateView (activity: ActivityView | ActivityCreate, byActor: SignatureActorModel) {
+async function processCreateView (activity: ActivityView | ActivityCreate, byActor: MActorSignature) {
const videoObject = activity.type === 'View' ? activity.object : (activity.object as ViewObject).object
const options = {
- videoObject: videoObject,
+ videoObject,
fetchType: 'only-video' as 'only-video'
}
const { video } = await getOrCreateVideoAndAccountAndChannel(options)
import { Activity, ActivityType } from '../../../../shared/models/activitypub'
import { checkUrlsSameHost, getAPId } from '../../../helpers/activitypub'
import { logger } from '../../../helpers/logger'
-import { ActorModel } from '../../../models/activitypub/actor'
import { processAcceptActivity } from './process-accept'
import { processAnnounceActivity } from './process-announce'
import { processCreateActivity } from './process-create'
import { processFlagActivity } from './process-flag'
import { processViewActivity } from './process-view'
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
-import { SignatureActorModel } from '../../../typings/models'
+import { MActorDefault, MActorSignature } from '../../../typings/models'
const processActivity: { [ P in ActivityType ]: (options: APProcessorOptions<Activity>) => Promise<any> } = {
Create: processCreateActivity,
async function processActivities (
activities: Activity[],
options: {
- signatureActor?: SignatureActorModel
- inboxActor?: ActorModel
+ signatureActor?: MActorSignature
+ inboxActor?: MActorDefault
outboxUrl?: string
fromFetch?: boolean
} = {}
) {
const { outboxUrl, signatureActor, inboxActor, fromFetch = false } = options
- const actorsCache: { [ url: string ]: SignatureActorModel } = {}
+ const actorsCache: { [ url: string ]: MActorSignature } = {}
for (const activity of activities) {
if (!signatureActor && [ 'Create', 'Announce', 'Like' ].includes(activity.type) === false) {
}
try {
- await activityProcessor({ activity, byActor, inboxActor: inboxActor, fromFetch })
+ await activityProcessor({ activity, byActor, inboxActor, fromFetch })
} catch (err) {
logger.warn('Cannot process activity %s.', activity.type, { err })
}
import { unicastTo } from './utils'
import { buildFollowActivity } from './send-follow'
import { logger } from '../../../helpers/logger'
-import { ActorFollowModelLight } from '../../../typings/models/actor-follow'
-import { ActorModelOnly } from '../../../typings/models'
+import { MActor, MActorFollowActors } from '../../../typings/models'
-async function sendAccept (actorFollow: ActorFollowModelLight) {
+async function sendAccept (actorFollow: MActorFollowActors) {
const follower = actorFollow.ActorFollower
const me = actorFollow.ActorFollowing
// ---------------------------------------------------------------------------
-function buildAcceptActivity (url: string, byActor: ActorModelOnly, followActivityData: ActivityFollow): ActivityAccept {
+function buildAcceptActivity (url: string, byActor: MActor, followActivityData: ActivityFollow): ActivityAccept {
return {
type: 'Accept',
id: url,
import { Transaction } from 'sequelize'
import { ActivityAnnounce, ActivityAudience } from '../../../../shared/models/activitypub'
-import { VideoModel } from '../../../models/video/video'
import { broadcastToFollowers } from './utils'
import { audiencify, getActorsInvolvedInVideo, getAudience, getAudienceFromFollowersOf } from '../audience'
import { logger } from '../../../helpers/logger'
-import { ActorModelOnly } from '../../../typings/models'
-import { VideoShareModelOnly } from '../../../typings/models/video-share'
+import { MActorLight, MVideo } from '../../../typings/models'
+import { MVideoShare } from '../../../typings/models/video'
async function buildAnnounceWithVideoAudience (
- byActor: ActorModelOnly,
- videoShare: VideoShareModelOnly,
- video: VideoModel,
+ byActor: MActorLight,
+ videoShare: MVideoShare,
+ video: MVideo,
t: Transaction
) {
const announcedObject = video.url
return { activity, actorsInvolvedInVideo }
}
-async function sendVideoAnnounce (byActor: ActorModelOnly, videoShare: VideoShareModelOnly, video: VideoModel, t: Transaction) {
+async function sendVideoAnnounce (byActor: MActorLight, videoShare: MVideoShare, video: MVideo, t: Transaction) {
const { activity, actorsInvolvedInVideo } = await buildAnnounceWithVideoAudience(byActor, videoShare, video, t)
logger.info('Creating job to send announce %s.', videoShare.url)
return broadcastToFollowers(activity, byActor, actorsInvolvedInVideo, t, followersException)
}
-function buildAnnounceActivity (url: string, byActor: ActorModelOnly, object: string, audience?: ActivityAudience): ActivityAnnounce {
+function buildAnnounceActivity (url: string, byActor: MActorLight, object: string, audience?: ActivityAudience): ActivityAnnounce {
if (!audience) audience = getAudience(byActor)
return audiencify({
import { Transaction } from 'sequelize'
import { ActivityAudience, ActivityCreate } from '../../../../shared/models/activitypub'
import { VideoPrivacy } from '../../../../shared/models/videos'
-import { ActorModel } from '../../../models/activitypub/actor'
-import { VideoModel } from '../../../models/video/video'
import { VideoCommentModel } from '../../../models/video/video-comment'
import { broadcastToActors, broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils'
import { audiencify, getActorsInvolvedInVideo, getAudience, getAudienceFromFollowersOf, getVideoCommentAudience } from '../audience'
import { logger } from '../../../helpers/logger'
-import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy'
-import { VideoPlaylistModel } from '../../../models/video/video-playlist'
import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model'
import { getServerActor } from '../../../helpers/utils'
-import * as Bluebird from 'bluebird'
-
-async function sendCreateVideo (video: VideoModel, t: Transaction) {
+import {
+ MActorLight,
+ MCommentOwnerVideo,
+ MVideoAccountLight,
+ MVideoAP,
+ MVideoPlaylistFull,
+ MVideoRedundancyFileVideo,
+ MVideoRedundancyStreamingPlaylistVideo
+} from '../../../typings/models'
+
+async function sendCreateVideo (video: MVideoAP, t: Transaction) {
if (video.privacy === VideoPrivacy.PRIVATE) return undefined
logger.info('Creating job to send video creation of %s.', video.url)
return broadcastToFollowers(createActivity, byActor, [ byActor ], t)
}
-async function sendCreateCacheFile (byActor: ActorModel, video: VideoModel, fileRedundancy: VideoRedundancyModel) {
+async function sendCreateCacheFile (
+ byActor: MActorLight,
+ video: MVideoAccountLight,
+ fileRedundancy: MVideoRedundancyStreamingPlaylistVideo | MVideoRedundancyFileVideo
+) {
logger.info('Creating job to send file cache of %s.', fileRedundancy.url)
return sendVideoRelatedCreateActivity({
})
}
-async function sendCreateVideoPlaylist (playlist: VideoPlaylistModel, t: Transaction) {
+async function sendCreateVideoPlaylist (playlist: MVideoPlaylistFull, t: Transaction) {
if (playlist.privacy === VideoPlaylistPrivacy.PRIVATE) return undefined
logger.info('Creating job to send create video playlist of %s.', playlist.url)
return broadcastToFollowers(createActivity, byActor, toFollowersOf, t)
}
-async function sendCreateVideoComment (comment: VideoCommentModel, t: Transaction) {
+async function sendCreateVideoComment (comment: MCommentOwnerVideo, t: Transaction) {
logger.info('Creating job to send comment %s.', comment.url)
const isOrigin = comment.Video.isOwned()
t.afterCommit(() => unicastTo(createActivity, byActor, comment.Video.VideoChannel.Account.Actor.sharedInboxUrl))
}
-function buildCreateActivity (url: string, byActor: ActorModel, object: any, audience?: ActivityAudience): ActivityCreate {
+function buildCreateActivity (url: string, byActor: MActorLight, object: any, audience?: ActivityAudience): ActivityCreate {
if (!audience) audience = getAudience(byActor)
return audiencify(
// ---------------------------------------------------------------------------
async function sendVideoRelatedCreateActivity (options: {
- byActor: ActorModel,
- video: VideoModel,
+ byActor: MActorLight,
+ video: MVideoAccountLight,
url: string,
object: any,
transaction?: Transaction
import { Transaction } from 'sequelize'
import { ActivityAudience, ActivityDelete } from '../../../../shared/models/activitypub'
import { ActorModel } from '../../../models/activitypub/actor'
-import { VideoModel } from '../../../models/video/video'
import { VideoCommentModel } from '../../../models/video/video-comment'
import { VideoShareModel } from '../../../models/video/video-share'
import { getDeleteActivityPubUrl } from '../url'
import { broadcastToActors, broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils'
import { audiencify, getActorsInvolvedInVideo, getVideoCommentAudience } from '../audience'
import { logger } from '../../../helpers/logger'
-import { VideoPlaylistModel } from '../../../models/video/video-playlist'
import { getServerActor } from '../../../helpers/utils'
+import { MCommentOwnerVideoReply, MVideoAccountLight, MVideoPlaylistFullSummary } from '../../../typings/models/video'
+import { MActorUrl } from '../../../typings/models'
-async function sendDeleteVideo (video: VideoModel, transaction: Transaction) {
+async function sendDeleteVideo (video: MVideoAccountLight, transaction: Transaction) {
logger.info('Creating job to broadcast delete of video %s.', video.url)
const byActor = video.VideoChannel.Account.Actor
return broadcastToFollowers(activity, byActor, actorsInvolved, t)
}
-async function sendDeleteVideoComment (videoComment: VideoCommentModel, t: Transaction) {
+async function sendDeleteVideoComment (videoComment: MCommentOwnerVideoReply, t: Transaction) {
logger.info('Creating job to send delete of comment %s.', videoComment.url)
const isVideoOrigin = videoComment.Video.isOwned()
t.afterCommit(() => unicastTo(activity, byActor, videoComment.Video.VideoChannel.Account.Actor.sharedInboxUrl))
}
-async function sendDeleteVideoPlaylist (videoPlaylist: VideoPlaylistModel, t: Transaction) {
+async function sendDeleteVideoPlaylist (videoPlaylist: MVideoPlaylistFullSummary, t: Transaction) {
logger.info('Creating job to send delete of playlist %s.', videoPlaylist.url)
const byActor = videoPlaylist.OwnerAccount.Actor
// ---------------------------------------------------------------------------
-function buildDeleteActivity (url: string, object: string, byActor: ActorModel, audience?: ActivityAudience): ActivityDelete {
+function buildDeleteActivity (url: string, object: string, byActor: MActorUrl, audience?: ActivityAudience): ActivityDelete {
const activity = {
type: 'Delete' as 'Delete',
id: url,
import { Transaction } from 'sequelize'
-import { ActorModel } from '../../../models/activitypub/actor'
-import { VideoModel } from '../../../models/video/video'
import { getVideoDislikeActivityPubUrl } from '../url'
import { logger } from '../../../helpers/logger'
import { ActivityAudience, ActivityDislike } from '../../../../shared/models/activitypub'
import { sendVideoRelatedActivity } from './utils'
import { audiencify, getAudience } from '../audience'
+import { MActor, MActorAudience, MVideoAccountLight, MVideoUrl } from '../../../typings/models'
-async function sendDislike (byActor: ActorModel, video: VideoModel, t: Transaction) {
+async function sendDislike (byActor: MActor, video: MVideoAccountLight, t: Transaction) {
logger.info('Creating job to dislike %s.', video.url)
const activityBuilder = (audience: ActivityAudience) => {
return sendVideoRelatedActivity(activityBuilder, { byActor, video, transaction: t })
}
-function buildDislikeActivity (url: string, byActor: ActorModel, video: VideoModel, audience?: ActivityAudience): ActivityDislike {
+function buildDislikeActivity (url: string, byActor: MActorAudience, video: MVideoUrl, audience?: ActivityAudience): ActivityDislike {
if (!audience) audience = getAudience(byActor)
return audiencify(
-import { ActorModel } from '../../../models/activitypub/actor'
-import { VideoModel } from '../../../models/video/video'
-import { VideoAbuseModel } from '../../../models/video/video-abuse'
import { getVideoAbuseActivityPubUrl } from '../url'
import { unicastTo } from './utils'
import { logger } from '../../../helpers/logger'
import { ActivityAudience, ActivityFlag } from '../../../../shared/models/activitypub'
import { audiencify, getAudience } from '../audience'
import { Transaction } from 'sequelize'
+import { MActor, MVideoFullLight } from '../../../typings/models'
+import { MVideoAbuseVideo } from '../../../typings/models/video'
-async function sendVideoAbuse (byActor: ActorModel, videoAbuse: VideoAbuseModel, video: VideoModel, t: Transaction) {
+async function sendVideoAbuse (byActor: MActor, videoAbuse: MVideoAbuseVideo, video: MVideoFullLight, t: Transaction) {
if (!video.VideoChannel.Account.Actor.serverId) return // Local user
const url = getVideoAbuseActivityPubUrl(videoAbuse)
t.afterCommit(() => unicastTo(flagActivity, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl))
}
-function buildFlagActivity (url: string, byActor: ActorModel, videoAbuse: VideoAbuseModel, audience: ActivityAudience): ActivityFlag {
+function buildFlagActivity (url: string, byActor: MActor, videoAbuse: MVideoAbuseVideo, audience: ActivityAudience): ActivityFlag {
if (!audience) audience = getAudience(byActor)
const activity = Object.assign(
import { unicastTo } from './utils'
import { logger } from '../../../helpers/logger'
import { Transaction } from 'sequelize'
-import { ActorModelOnly } from '../../../typings/models'
+import { MActor, MActorFollowActors } from '../../../typings/models'
-function sendFollow (actorFollow: ActorFollowModel, t: Transaction) {
+function sendFollow (actorFollow: MActorFollowActors, t: Transaction) {
const me = actorFollow.ActorFollower
const following = actorFollow.ActorFollowing
t.afterCommit(() => unicastTo(data, me, following.inboxUrl))
}
-function buildFollowActivity (url: string, byActor: ActorModelOnly, targetActor: ActorModelOnly): ActivityFollow {
+function buildFollowActivity (url: string, byActor: MActor, targetActor: MActor): ActivityFollow {
return {
type: 'Follow',
id: url,
import { Transaction } from 'sequelize'
import { ActivityAudience, ActivityLike } from '../../../../shared/models/activitypub'
-import { ActorModel } from '../../../models/activitypub/actor'
-import { VideoModel } from '../../../models/video/video'
import { getVideoLikeActivityPubUrl } from '../url'
import { sendVideoRelatedActivity } from './utils'
import { audiencify, getAudience } from '../audience'
import { logger } from '../../../helpers/logger'
+import { MActor, MActorAudience, MVideoAccountLight, MVideoUrl } from '../../../typings/models'
-async function sendLike (byActor: ActorModel, video: VideoModel, t: Transaction) {
+async function sendLike (byActor: MActor, video: MVideoAccountLight, t: Transaction) {
logger.info('Creating job to like %s.', video.url)
const activityBuilder = (audience: ActivityAudience) => {
return sendVideoRelatedActivity(activityBuilder, { byActor, video, transaction: t })
}
-function buildLikeActivity (url: string, byActor: ActorModel, video: VideoModel, audience?: ActivityAudience): ActivityLike {
+function buildLikeActivity (url: string, byActor: MActorAudience, video: MVideoUrl, audience?: ActivityAudience): ActivityLike {
if (!audience) audience = getAudience(byActor)
return audiencify(
import { ActivityFollow, ActivityReject } from '../../../../shared/models/activitypub'
-import { ActorModel } from '../../../models/activitypub/actor'
import { getActorFollowActivityPubUrl, getActorFollowRejectActivityPubUrl } from '../url'
import { unicastTo } from './utils'
import { buildFollowActivity } from './send-follow'
import { logger } from '../../../helpers/logger'
-import { SignatureActorModel } from '../../../typings/models'
+import { MActor } from '../../../typings/models'
-async function sendReject (follower: SignatureActorModel, following: ActorModel) {
+async function sendReject (follower: MActor, following: MActor) {
if (!follower.serverId) { // This should never happen
logger.warn('Do not sending reject to local follower.')
return
// ---------------------------------------------------------------------------
-function buildRejectActivity (url: string, byActor: ActorModel, followActivityData: ActivityFollow): ActivityReject {
+function buildRejectActivity (url: string, byActor: MActor, followActivityData: ActivityFollow): ActivityReject {
return {
type: 'Reject',
id: url,
import {
ActivityAnnounce,
ActivityAudience,
- ActivityCreate, ActivityDislike,
+ ActivityCreate,
+ ActivityDislike,
ActivityFollow,
ActivityLike,
ActivityUndo
} from '../../../../shared/models/activitypub'
-import { ActorModel } from '../../../models/activitypub/actor'
-import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
import { VideoModel } from '../../../models/video/video'
import { getActorFollowActivityPubUrl, getUndoActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from '../url'
import { broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils'
import { buildCreateActivity } from './send-create'
import { buildFollowActivity } from './send-follow'
import { buildLikeActivity } from './send-like'
-import { VideoShareModel } from '../../../models/video/video-share'
import { buildAnnounceWithVideoAudience } from './send-announce'
import { logger } from '../../../helpers/logger'
-import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy'
import { buildDislikeActivity } from './send-dislike'
-
-async function sendUndoFollow (actorFollow: ActorFollowModel, t: Transaction) {
+import {
+ MActor, MActorAudience,
+ MActorFollowActors,
+ MActorLight,
+ MVideo,
+ MVideoAccountLight,
+ MVideoRedundancyVideo,
+ MVideoShare
+} from '../../../typings/models'
+
+async function sendUndoFollow (actorFollow: MActorFollowActors, t: Transaction) {
const me = actorFollow.ActorFollower
const following = actorFollow.ActorFollowing
t.afterCommit(() => unicastTo(undoActivity, me, following.inboxUrl))
}
-async function sendUndoAnnounce (byActor: ActorModel, videoShare: VideoShareModel, video: VideoModel, t: Transaction) {
+async function sendUndoAnnounce (byActor: MActorLight, videoShare: MVideoShare, video: MVideo, t: Transaction) {
logger.info('Creating job to undo announce %s.', videoShare.url)
const undoUrl = getUndoActivityPubUrl(videoShare.url)
return broadcastToFollowers(undoActivity, byActor, actorsInvolvedInVideo, t, followersException)
}
-async function sendUndoLike (byActor: ActorModel, video: VideoModel, t: Transaction) {
+async function sendUndoLike (byActor: MActor, video: MVideoAccountLight, t: Transaction) {
logger.info('Creating job to undo a like of video %s.', video.url)
const likeUrl = getVideoLikeActivityPubUrl(byActor, video)
return sendUndoVideoRelatedActivity({ byActor, video, url: likeUrl, activity: likeActivity, transaction: t })
}
-async function sendUndoDislike (byActor: ActorModel, video: VideoModel, t: Transaction) {
+async function sendUndoDislike (byActor: MActor, video: MVideoAccountLight, t: Transaction) {
logger.info('Creating job to undo a dislike of video %s.', video.url)
const dislikeUrl = getVideoDislikeActivityPubUrl(byActor, video)
return sendUndoVideoRelatedActivity({ byActor, video, url: dislikeUrl, activity: dislikeActivity, transaction: t })
}
-async function sendUndoCacheFile (byActor: ActorModel, redundancyModel: VideoRedundancyModel, t: Transaction) {
+async function sendUndoCacheFile (byActor: MActor, redundancyModel: MVideoRedundancyVideo, t: Transaction) {
logger.info('Creating job to undo cache file %s.', redundancyModel.url)
const videoId = redundancyModel.getVideo().id
function undoActivityData (
url: string,
- byActor: ActorModel,
+ byActor: MActorAudience,
object: ActivityFollow | ActivityLike | ActivityDislike | ActivityCreate | ActivityAnnounce,
audience?: ActivityAudience
): ActivityUndo {
}
async function sendUndoVideoRelatedActivity (options: {
- byActor: ActorModel,
- video: VideoModel,
+ byActor: MActor,
+ video: MVideoAccountLight,
url: string,
activity: ActivityFollow | ActivityLike | ActivityDislike | ActivityCreate | ActivityAnnounce,
transaction: Transaction
import { ActivityAudience, ActivityUpdate } from '../../../../shared/models/activitypub'
import { VideoPrivacy } from '../../../../shared/models/videos'
import { AccountModel } from '../../../models/account/account'
-import { ActorModel } from '../../../models/activitypub/actor'
import { VideoModel } from '../../../models/video/video'
-import { VideoChannelModel } from '../../../models/video/video-channel'
import { VideoShareModel } from '../../../models/video/video-share'
import { getUpdateActivityPubUrl } from '../url'
import { broadcastToFollowers, sendVideoRelatedActivity } from './utils'
import { audiencify, getActorsInvolvedInVideo, getAudience } from '../audience'
import { logger } from '../../../helpers/logger'
import { VideoCaptionModel } from '../../../models/video/video-caption'
-import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy'
-import { VideoPlaylistModel } from '../../../models/video/video-playlist'
import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model'
import { getServerActor } from '../../../helpers/utils'
+import {
+ MAccountActor,
+ MActor,
+ MActorLight,
+ MChannelActor,
+ MVideoAP,
+ MVideoAPWithoutCaption,
+ MVideoPlaylistFull,
+ MVideoRedundancyVideo
+} from '../../../typings/models'
+
+async function sendUpdateVideo (videoArg: MVideoAPWithoutCaption, t: Transaction, overrodeByActor?: MActor) {
+ const video = videoArg as MVideoAP
-async function sendUpdateVideo (video: VideoModel, t: Transaction, overrodeByActor?: ActorModel) {
if (video.privacy === VideoPrivacy.PRIVATE) return undefined
logger.info('Creating job to update video %s.', video.url)
return broadcastToFollowers(updateActivity, byActor, actorsInvolved, t)
}
-async function sendUpdateActor (accountOrChannel: AccountModel | VideoChannelModel, t: Transaction) {
+async function sendUpdateActor (accountOrChannel: MAccountActor | MChannelActor, t: Transaction) {
const byActor = accountOrChannel.Actor
logger.info('Creating job to update actor %s.', byActor.url)
const audience = getAudience(byActor)
const updateActivity = buildUpdateActivity(url, byActor, accountOrChannelObject, audience)
- let actorsInvolved: ActorModel[]
+ let actorsInvolved: MActor[]
if (accountOrChannel instanceof AccountModel) {
// Actors that shared my videos are involved too
actorsInvolved = await VideoShareModel.loadActorsWhoSharedVideosOf(byActor.id, t)
return broadcastToFollowers(updateActivity, byActor, actorsInvolved, t)
}
-async function sendUpdateCacheFile (byActor: ActorModel, redundancyModel: VideoRedundancyModel) {
+async function sendUpdateCacheFile (byActor: MActorLight, redundancyModel: MVideoRedundancyVideo) {
logger.info('Creating job to update cache file %s.', redundancyModel.url)
const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(redundancyModel.getVideo().id)
return sendVideoRelatedActivity(activityBuilder, { byActor, video })
}
-async function sendUpdateVideoPlaylist (videoPlaylist: VideoPlaylistModel, t: Transaction) {
+async function sendUpdateVideoPlaylist (videoPlaylist: MVideoPlaylistFull, t: Transaction) {
if (videoPlaylist.privacy === VideoPlaylistPrivacy.PRIVATE) return undefined
const byActor = videoPlaylist.OwnerAccount.Actor
// ---------------------------------------------------------------------------
-function buildUpdateActivity (url: string, byActor: ActorModel, object: any, audience?: ActivityAudience): ActivityUpdate {
+function buildUpdateActivity (url: string, byActor: MActorLight, object: any, audience?: ActivityAudience): ActivityUpdate {
if (!audience) audience = getAudience(byActor)
return audiencify(
type: 'Update' as 'Update',
id: url,
actor: byActor.url,
- object: audiencify(object, audience
- )
+ object: audiencify(object, audience)
},
audience
)
import { Transaction } from 'sequelize'
import { ActivityAudience, ActivityView } from '../../../../shared/models/activitypub'
import { ActorModel } from '../../../models/activitypub/actor'
-import { VideoModel } from '../../../models/video/video'
import { getVideoLikeActivityPubUrl } from '../url'
import { sendVideoRelatedActivity } from './utils'
import { audiencify, getAudience } from '../audience'
import { logger } from '../../../helpers/logger'
+import { MActorAudience, MVideoAccountLight, MVideoUrl } from '@server/typings/models'
-async function sendView (byActor: ActorModel, video: VideoModel, t: Transaction) {
+async function sendView (byActor: ActorModel, video: MVideoAccountLight, t: Transaction) {
logger.info('Creating job to send view of %s.', video.url)
const activityBuilder = (audience: ActivityAudience) => {
return sendVideoRelatedActivity(activityBuilder, { byActor, video, transaction: t })
}
-function buildViewActivity (url: string, byActor: ActorModel, video: VideoModel, audience?: ActivityAudience): ActivityView {
+function buildViewActivity (url: string, byActor: MActorAudience, video: MVideoUrl, audience?: ActivityAudience): ActivityView {
if (!audience) audience = getAudience(byActor)
return audiencify(
import { ActorModel } from '../../../models/activitypub/actor'
import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
import { JobQueue } from '../../job-queue'
-import { VideoModel } from '../../../models/video/video'
import { getActorsInvolvedInVideo, getAudienceFromFollowersOf, getRemoteVideoAudience } from '../audience'
import { getServerActor } from '../../../helpers/utils'
import { afterCommitIfTransaction } from '../../../helpers/database-utils'
-import { ActorFollowerException, ActorModelId, ActorModelOnly } from '../../../typings/models'
+import { MActorFollowerException, MActor, MActorId, MActorLight, MVideo, MVideoAccountLight } from '../../../typings/models'
async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAudience) => Activity, options: {
- byActor: ActorModelOnly,
- video: VideoModel,
+ byActor: MActorLight,
+ video: MVideoAccountLight,
transaction?: Transaction
}) {
const { byActor, video, transaction } = options
async function forwardVideoRelatedActivity (
activity: Activity,
t: Transaction,
- followersException: ActorFollowerException[] = [],
- video: VideoModel
+ followersException: MActorFollowerException[] = [],
+ video: MVideo
) {
// Mastodon does not add our announces in audience, so we forward to them manually
const additionalActors = await getActorsInvolvedInVideo(video, t)
async function forwardActivity (
activity: Activity,
t: Transaction,
- followersException: ActorFollowerException[] = [],
+ followersException: MActorFollowerException[] = [],
additionalFollowerUrls: string[] = []
) {
logger.info('Forwarding activity %s.', activity.id)
async function broadcastToFollowers (
data: any,
- byActor: ActorModelId,
- toFollowersOf: ActorModelId[],
+ byActor: MActorId,
+ toFollowersOf: MActorId[],
t: Transaction,
- actorsException: ActorFollowerException[] = []
+ actorsException: MActorFollowerException[] = []
) {
const uris = await computeFollowerUris(toFollowersOf, actorsException, t)
async function broadcastToActors (
data: any,
- byActor: ActorModelId,
- toActors: ActorModelOnly[],
+ byActor: MActorId,
+ toActors: MActor[],
t?: Transaction,
- actorsException: ActorFollowerException[] = []
+ actorsException: MActorFollowerException[] = []
) {
const uris = await computeUris(toActors, actorsException)
return afterCommitIfTransaction(t, () => broadcastTo(uris, data, byActor))
}
-function broadcastTo (uris: string[], data: any, byActor: ActorModelId) {
+function broadcastTo (uris: string[], data: any, byActor: MActorId) {
if (uris.length === 0) return undefined
logger.debug('Creating broadcast job.', { uris })
return JobQueue.Instance.createJob({ type: 'activitypub-http-broadcast', payload })
}
-function unicastTo (data: any, byActor: ActorModelId, toActorUrl: string) {
+function unicastTo (data: any, byActor: MActorId, toActorUrl: string) {
logger.debug('Creating unicast job.', { uri: toActorUrl })
const payload = {
// ---------------------------------------------------------------------------
-async function computeFollowerUris (toFollowersOf: ActorModelId[], actorsException: ActorFollowerException[], t: Transaction) {
+async function computeFollowerUris (toFollowersOf: MActorId[], actorsException: MActorFollowerException[], t: Transaction) {
const toActorFollowerIds = toFollowersOf.map(a => a.id)
const result = await ActorFollowModel.listAcceptedFollowerSharedInboxUrls(toActorFollowerIds, t)
return result.data.filter(sharedInbox => sharedInboxesException.indexOf(sharedInbox) === -1)
}
-async function computeUris (toActors: ActorModelOnly[], actorsException: ActorFollowerException[] = []) {
+async function computeUris (toActors: MActor[], actorsException: MActorFollowerException[] = []) {
const serverActor = await getServerActor()
const targetUrls = toActors
.filter(a => a.id !== serverActor.id) // Don't send to ourselves
.filter(sharedInbox => sharedInboxesException.indexOf(sharedInbox) === -1)
}
-async function buildSharedInboxesException (actorsException: ActorFollowerException[]) {
+async function buildSharedInboxesException (actorsException: MActorFollowerException[]) {
const serverActor = await getServerActor()
return actorsException
import { Transaction } from 'sequelize'
import { VideoPrivacy } from '../../../shared/models/videos'
import { getServerActor } from '../../helpers/utils'
-import { VideoModel } from '../../models/video/video'
import { VideoShareModel } from '../../models/video/video-share'
import { sendUndoAnnounce, sendVideoAnnounce } from './send'
import { getVideoAnnounceActivityPubUrl } from './url'
-import { VideoChannelModel } from '../../models/video/video-channel'
import * as Bluebird from 'bluebird'
import { doRequest } from '../../helpers/requests'
import { getOrCreateActorAndServerAndModel } from './actor'
import { logger } from '../../helpers/logger'
import { CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants'
import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub'
+import { MChannelActor, MChannelActorLight, MVideo, MVideoAccountLight, MVideoId } from '../../typings/models/video'
-async function shareVideoByServerAndChannel (video: VideoModel, t: Transaction) {
+async function shareVideoByServerAndChannel (video: MVideoAccountLight, t: Transaction) {
if (video.privacy === VideoPrivacy.PRIVATE) return undefined
return Promise.all([
])
}
-async function changeVideoChannelShare (video: VideoModel, oldVideoChannel: VideoChannelModel, t: Transaction) {
+async function changeVideoChannelShare (
+ video: MVideoAccountLight,
+ oldVideoChannel: MChannelActorLight,
+ t: Transaction
+) {
logger.info('Updating video channel of video %s: %s -> %s.', video.uuid, oldVideoChannel.name, video.VideoChannel.name)
await undoShareByVideoChannel(video, oldVideoChannel, t)
await shareByVideoChannel(video, t)
}
-async function addVideoShares (shareUrls: string[], instance: VideoModel) {
+async function addVideoShares (shareUrls: string[], video: MVideoId) {
await Bluebird.map(shareUrls, async shareUrl => {
try {
// Fetch url
const entry = {
actorId: actor.id,
- videoId: instance.id,
+ videoId: video.id,
url: shareUrl
}
// ---------------------------------------------------------------------------
-async function shareByServer (video: VideoModel, t: Transaction) {
+async function shareByServer (video: MVideo, t: Transaction) {
const serverActor = await getServerActor()
const serverShareUrl = getVideoAnnounceActivityPubUrl(serverActor, video)
return sendVideoAnnounce(serverActor, serverShare, video, t)
}
-async function shareByVideoChannel (video: VideoModel, t: Transaction) {
+async function shareByVideoChannel (video: MVideoAccountLight, t: Transaction) {
const videoChannelShareUrl = getVideoAnnounceActivityPubUrl(video.VideoChannel.Actor, video)
const [ videoChannelShare ] = await VideoShareModel.findOrCreate({
defaults: {
return sendVideoAnnounce(video.VideoChannel.Actor, videoChannelShare, video, t)
}
-async function undoShareByVideoChannel (video: VideoModel, oldVideoChannel: VideoChannelModel, t: Transaction) {
+async function undoShareByVideoChannel (video: MVideo, oldVideoChannel: MChannelActorLight, t: Transaction) {
// Load old share
const oldShare = await VideoShareModel.load(oldVideoChannel.actorId, video.id, t)
if (!oldShare) return new Error('Cannot find old video channel share ' + oldVideoChannel.actorId + ' for video ' + video.id)
import { WEBSERVER } from '../../initializers/constants'
-import { VideoModel } from '../../models/video/video'
-import { VideoAbuseModel } from '../../models/video/video-abuse'
-import { VideoCommentModel } from '../../models/video/video-comment'
-import { VideoFileModel } from '../../models/video/video-file'
-import { VideoStreamingPlaylistModel } from '../../models/video/video-streaming-playlist'
-import { VideoPlaylistModel } from '../../models/video/video-playlist'
-import { ActorModelOnly, ActorModelUrl } from '../../typings/models'
-import { ActorFollowModelLight } from '../../typings/models/actor-follow'
-
-function getVideoActivityPubUrl (video: VideoModel) {
+import {
+ MActor,
+ MActorFollowActors,
+ MActorId,
+ MActorUrl,
+ MCommentId,
+ MVideoAbuseId,
+ MVideoId,
+ MVideoUrl,
+ MVideoUUID
+} from '../../typings/models'
+import { MVideoPlaylist, MVideoPlaylistUUID } from '../../typings/models/video/video-playlist'
+import { MVideoFileVideoUUID } from '../../typings/models/video/video-file'
+import { MStreamingPlaylist } from '../../typings/models/video/video-streaming-playlist'
+
+function getVideoActivityPubUrl (video: MVideoUUID) {
return WEBSERVER.URL + '/videos/watch/' + video.uuid
}
-function getVideoPlaylistActivityPubUrl (videoPlaylist: VideoPlaylistModel) {
+function getVideoPlaylistActivityPubUrl (videoPlaylist: MVideoPlaylist) {
return WEBSERVER.URL + '/video-playlists/' + videoPlaylist.uuid
}
-function getVideoPlaylistElementActivityPubUrl (videoPlaylist: VideoPlaylistModel, video: VideoModel) {
+function getVideoPlaylistElementActivityPubUrl (videoPlaylist: MVideoPlaylistUUID, video: MVideoUUID) {
return WEBSERVER.URL + '/video-playlists/' + videoPlaylist.uuid + '/' + video.uuid
}
-function getVideoCacheFileActivityPubUrl (videoFile: VideoFileModel) {
+function getVideoCacheFileActivityPubUrl (videoFile: MVideoFileVideoUUID) {
const suffixFPS = videoFile.fps && videoFile.fps !== -1 ? '-' + videoFile.fps : ''
return `${WEBSERVER.URL}/redundancy/videos/${videoFile.Video.uuid}/${videoFile.resolution}${suffixFPS}`
}
-function getVideoCacheStreamingPlaylistActivityPubUrl (video: VideoModel, playlist: VideoStreamingPlaylistModel) {
+function getVideoCacheStreamingPlaylistActivityPubUrl (video: MVideoUUID, playlist: MStreamingPlaylist) {
return `${WEBSERVER.URL}/redundancy/streaming-playlists/${playlist.getStringType()}/${video.uuid}`
}
-function getVideoCommentActivityPubUrl (video: VideoModel, videoComment: VideoCommentModel) {
+function getVideoCommentActivityPubUrl (video: MVideoUUID, videoComment: MCommentId) {
return WEBSERVER.URL + '/videos/watch/' + video.uuid + '/comments/' + videoComment.id
}
return WEBSERVER.URL + '/accounts/' + accountName
}
-function getVideoAbuseActivityPubUrl (videoAbuse: VideoAbuseModel) {
+function getVideoAbuseActivityPubUrl (videoAbuse: MVideoAbuseId) {
return WEBSERVER.URL + '/admin/video-abuses/' + videoAbuse.id
}
-function getVideoViewActivityPubUrl (byActor: ActorModelUrl, video: VideoModel) {
+function getVideoViewActivityPubUrl (byActor: MActorUrl, video: MVideoId) {
return byActor.url + '/views/videos/' + video.id + '/' + new Date().toISOString()
}
-function getVideoLikeActivityPubUrl (byActor: ActorModelUrl, video: VideoModel | { id: number }) {
+function getVideoLikeActivityPubUrl (byActor: MActorUrl, video: MVideoId) {
return byActor.url + '/likes/' + video.id
}
-function getVideoDislikeActivityPubUrl (byActor: ActorModelUrl, video: VideoModel | { id: number }) {
+function getVideoDislikeActivityPubUrl (byActor: MActorUrl, video: MVideoId) {
return byActor.url + '/dislikes/' + video.id
}
-function getVideoSharesActivityPubUrl (video: VideoModel) {
+function getVideoSharesActivityPubUrl (video: MVideoUrl) {
return video.url + '/announces'
}
-function getVideoCommentsActivityPubUrl (video: VideoModel) {
+function getVideoCommentsActivityPubUrl (video: MVideoUrl) {
return video.url + '/comments'
}
-function getVideoLikesActivityPubUrl (video: VideoModel) {
+function getVideoLikesActivityPubUrl (video: MVideoUrl) {
return video.url + '/likes'
}
-function getVideoDislikesActivityPubUrl (video: VideoModel) {
+function getVideoDislikesActivityPubUrl (video: MVideoUrl) {
return video.url + '/dislikes'
}
-function getActorFollowActivityPubUrl (follower: ActorModelOnly, following: ActorModelOnly) {
+function getActorFollowActivityPubUrl (follower: MActor, following: MActorId) {
return follower.url + '/follows/' + following.id
}
-function getActorFollowAcceptActivityPubUrl (actorFollow: ActorFollowModelLight) {
+function getActorFollowAcceptActivityPubUrl (actorFollow: MActorFollowActors) {
const follower = actorFollow.ActorFollower
const me = actorFollow.ActorFollowing
return follower.url + '/accepts/follows/' + me.id
}
-function getActorFollowRejectActivityPubUrl (follower: ActorModelOnly, following: ActorModelOnly) {
+function getActorFollowRejectActivityPubUrl (follower: MActorUrl, following: MActorId) {
return follower.url + '/rejects/follows/' + following.id
}
-function getVideoAnnounceActivityPubUrl (byActor: ActorModelOnly, video: VideoModel) {
+function getVideoAnnounceActivityPubUrl (byActor: MActorId, video: MVideoUrl) {
return video.url + '/announces/' + byActor.id
}
import { logger } from '../../helpers/logger'
import { doRequest } from '../../helpers/requests'
import { ACTIVITY_PUB, CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants'
-import { VideoModel } from '../../models/video/video'
import { VideoCommentModel } from '../../models/video/video-comment'
import { getOrCreateActorAndServerAndModel } from './actor'
import { getOrCreateVideoAndAccountAndChannel } from './videos'
import * as Bluebird from 'bluebird'
import { checkUrlsSameHost } from '../../helpers/activitypub'
+import { MCommentOwner, MCommentOwnerVideo, MVideoAccountAllFiles } from '../../typings/models/video'
type ResolveThreadParams = {
url: string,
- comments?: VideoCommentModel[],
+ comments?: MCommentOwner[],
isVideo?: boolean,
commentCreated?: boolean
}
-type ResolveThreadResult = Promise<{ video: VideoModel, comment: VideoCommentModel, commentCreated: boolean }>
+type ResolveThreadResult = Promise<{ video: MVideoAccountAllFiles, comment: MCommentOwnerVideo, commentCreated: boolean }>
async function addVideoComments (commentUrls: string[]) {
return Bluebird.map(commentUrls, commentUrl => {
const syncParam = { likes: true, dislikes: true, shares: true, comments: false, thumbnail: true, refreshVideo: false }
const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: url, syncParam })
- let resultComment: VideoCommentModel
+ let resultComment: MCommentOwnerVideo
if (comments.length !== 0) {
- const firstReply = comments[ comments.length - 1 ]
+ const firstReply = comments[ comments.length - 1 ] as MCommentOwnerVideo
firstReply.inReplyToCommentId = null
firstReply.originCommentId = null
firstReply.videoId = video.id
comments[comments.length - 1] = await firstReply.save()
for (let i = comments.length - 2; i >= 0; i--) {
- const comment = comments[ i ]
+ const comment = comments[ i ] as MCommentOwnerVideo
comment.originCommentId = firstReply.id
comment.inReplyToCommentId = comments[ i + 1 ].id
comment.videoId = video.id
comments[i] = await comment.save()
}
- resultComment = comments[0]
+ resultComment = comments[0] as MCommentOwnerVideo
}
return { video, comment: resultComment, commentCreated }
originCommentId: null,
createdAt: new Date(body.published),
updatedAt: new Date(body.updated)
- })
+ }) as MCommentOwner
comment.Account = actor.Account
return resolveThread({
import { Transaction } from 'sequelize'
-import { AccountModel } from '../../models/account/account'
-import { VideoModel } from '../../models/video/video'
import { sendLike, sendUndoDislike, sendUndoLike } from './send'
import { VideoRateType } from '../../../shared/models/videos'
import * as Bluebird from 'bluebird'
import { CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants'
import { doRequest } from '../../helpers/requests'
import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub'
-import { ActorModel } from '../../models/activitypub/actor'
import { getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from './url'
import { sendDislike } from './send/send-dislike'
+import { MAccountActor, MActorUrl, MVideo, MVideoAccountLight, MVideoId } from '../../typings/models'
-async function createRates (ratesUrl: string[], video: VideoModel, rate: VideoRateType) {
+async function createRates (ratesUrl: string[], video: MVideo, rate: VideoRateType) {
let rateCounts = 0
await Bluebird.map(ratesUrl, async rateUrl => {
return
}
-async function sendVideoRateChange (account: AccountModel,
- video: VideoModel,
- likes: number,
- dislikes: number,
- t: Transaction) {
+async function sendVideoRateChange (
+ account: MAccountActor,
+ video: MVideoAccountLight,
+ likes: number,
+ dislikes: number,
+ t: Transaction
+) {
const actor = account.Actor
// Keep the order: first we undo and then we create
if (dislikes > 0) await sendDislike(actor, video, t)
}
-function getRateUrl (rateType: VideoRateType, actor: ActorModel, video: VideoModel) {
- return rateType === 'like' ? getVideoLikeActivityPubUrl(actor, video) : getVideoDislikeActivityPubUrl(actor, video)
+function getRateUrl (rateType: VideoRateType, actor: MActorUrl, video: MVideoId) {
+ return rateType === 'like'
+ ? getVideoLikeActivityPubUrl(actor, video)
+ : getVideoDislikeActivityPubUrl(actor, video)
}
export {
REMOTE_SCHEME,
STATIC_PATHS
} from '../../initializers/constants'
-import { ActorModel } from '../../models/activitypub/actor'
import { TagModel } from '../../models/video/tag'
import { VideoModel } from '../../models/video/video'
import { VideoFileModel } from '../../models/video/video-file'
import { ActivitypubHttpFetcherPayload } from '../job-queue/handlers/activitypub-http-fetcher'
import { createRates } from './video-rates'
import { addVideoShares, shareVideoByServerAndChannel } from './share'
-import { AccountModel } from '../../models/account/account'
import { fetchVideoByUrl, VideoFetchByUrlType } from '../../helpers/video'
import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub'
import { Notifier } from '../notifier'
import { VideoCommentModel } from '../../models/video/video-comment'
import { sequelizeTypescript } from '../../initializers/database'
import { createPlaceholderThumbnail, createVideoMiniatureFromUrl } from '../thumbnail'
-import { ThumbnailModel } from '../../models/video/thumbnail'
import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type'
import { join } from 'path'
import { FilteredModelAttributes } from '../../typings/sequelize'
import { autoBlacklistVideoIfNeeded } from '../video-blacklist'
import { ActorFollowScoreCache } from '../files-cache'
-import { AccountModelIdActor, VideoChannelModelId, VideoChannelModelIdActor } from '../../typings/models'
+import {
+ MAccountActor,
+ MChannelAccountLight,
+ MChannelDefault,
+ MChannelId,
+ MVideo,
+ MVideoAccountAllFiles,
+ MVideoAccountLight,
+ MVideoAP,
+ MVideoAPWithoutCaption,
+ MVideoFile,
+ MVideoFullLight,
+ MVideoId,
+ MVideoTag,
+ MVideoThumbnail,
+ MVideoWithAllFiles
+} from '../../typings/models'
+import { MThumbnail } from '../../typings/models/video/thumbnail'
+
+async function federateVideoIfNeeded (videoArg: MVideoAPWithoutCaption, isNewVideo: boolean, transaction?: sequelize.Transaction) {
+ const video = videoArg as MVideoAP
-async function federateVideoIfNeeded (video: VideoModel, isNewVideo: boolean, transaction?: sequelize.Transaction) {
if (
// Check this is not a blacklisted video, or unfederated blacklisted video
(video.isBlacklisted() === false || (isNewVideo === false && video.VideoBlacklist.unfederated === false)) &&
return { response, videoObject: body }
}
-async function fetchRemoteVideoDescription (video: VideoModel) {
+async function fetchRemoteVideoDescription (video: MVideoAccountLight) {
const host = video.VideoChannel.Account.Actor.Server.host
const path = video.getDescriptionAPIPath()
const options = {
return body.description ? body.description : ''
}
-function fetchRemoteVideoStaticFile (video: VideoModel, path: string, destPath: string) {
+function fetchRemoteVideoStaticFile (video: MVideoAccountLight, path: string, destPath: string) {
const url = buildRemoteBaseUrl(video, path)
// We need to provide a callback, if no we could have an uncaught exception
return doRequestAndSaveToFile({ uri: url }, destPath)
}
-function buildRemoteBaseUrl (video: VideoModel, path: string) {
+function buildRemoteBaseUrl (video: MVideoAccountLight, path: string) {
const host = video.VideoChannel.Account.Actor.Server.host
return REMOTE_SCHEME.HTTP + '://' + host + path
thumbnail: boolean
refreshVideo?: boolean
}
-async function syncVideoExternalAttributes (video: VideoModel, fetchedVideo: VideoTorrentObject, syncParam: SyncParam) {
+async function syncVideoExternalAttributes (video: MVideo, fetchedVideo: VideoTorrentObject, syncParam: SyncParam) {
logger.info('Adding likes/dislikes/shares/comments of video %s.', video.uuid)
const jobPayloads: ActivitypubHttpFetcherPayload[] = []
await Bluebird.map(jobPayloads, payload => JobQueue.Instance.createJob({ type: 'activitypub-http-fetcher', payload }))
}
+function getOrCreateVideoAndAccountAndChannel (options: {
+ videoObject: { id: string } | string,
+ syncParam?: SyncParam,
+ fetchType?: 'all',
+ allowRefresh?: boolean
+}): Promise<{ video: MVideoAccountAllFiles, created: boolean, autoBlacklisted?: boolean }>
+function getOrCreateVideoAndAccountAndChannel (options: {
+ videoObject: { id: string } | string,
+ syncParam?: SyncParam,
+ fetchType?: VideoFetchByUrlType,
+ allowRefresh?: boolean
+}): Promise<{ video: MVideoAccountAllFiles | MVideoThumbnail, created: boolean, autoBlacklisted?: boolean }>
async function getOrCreateVideoAndAccountAndChannel (options: {
videoObject: { id: string } | string,
syncParam?: SyncParam,
fetchType?: VideoFetchByUrlType,
allowRefresh?: boolean // true by default
-}) {
+}): Promise<{ video: MVideoAccountAllFiles | MVideoThumbnail, created: boolean, autoBlacklisted?: boolean }> {
// Default params
const syncParam = options.syncParam || { likes: true, dislikes: true, shares: true, comments: true, thumbnail: true, refreshVideo: false }
const fetchType = options.fetchType || 'all'
const { videoObject: fetchedVideo } = await fetchRemoteVideo(videoUrl)
if (!fetchedVideo) throw new Error('Cannot fetch remote video with url: ' + videoUrl)
- const channelActor = await getOrCreateVideoChannelFromVideoObject(fetchedVideo)
- const { autoBlacklisted, videoCreated } = await retryTransactionWrapper(createVideo, fetchedVideo, channelActor, syncParam.thumbnail)
+ const actor = await getOrCreateVideoChannelFromVideoObject(fetchedVideo)
+ const videoChannel = actor.VideoChannel
+ const { autoBlacklisted, videoCreated } = await retryTransactionWrapper(createVideo, fetchedVideo, videoChannel, syncParam.thumbnail)
await syncVideoExternalAttributes(videoCreated, fetchedVideo, syncParam)
}
async function updateVideoFromAP (options: {
- video: VideoModel,
+ video: MVideoAccountAllFiles,
videoObject: VideoTorrentObject,
- account: AccountModelIdActor,
- channel: VideoChannelModelIdActor,
+ account: MAccountActor,
+ channel: MChannelDefault,
overrideTo?: string[]
}) {
const { video, videoObject, account, channel, overrideTo } = options
- logger.debug('Updating remote video "%s".', options.videoObject.uuid)
+ logger.debug('Updating remote video "%s".', options.videoObject.uuid, { account, channel })
let videoFieldsSave: any
const wasPrivateVideo = video.privacy === VideoPrivacy.PRIVATE
const wasUnlistedVideo = video.privacy === VideoPrivacy.UNLISTED
try {
- let thumbnailModel: ThumbnailModel
+ let thumbnailModel: MThumbnail
try {
thumbnailModel = await createVideoMiniatureFromUrl(videoObject.icon.url, video, ThumbnailType.MINIATURE)
logger.warn('Cannot generate thumbnail of %s.', videoObject.id, { err })
}
- await sequelizeTypescript.transaction(async t => {
+ const videoUpdated = await sequelizeTypescript.transaction(async t => {
const sequelizeOptions = { transaction: t }
videoFieldsSave = video.toJSON()
video.channelId = videoData.channelId
video.views = videoData.views
- await video.save(sequelizeOptions)
+ const videoUpdated = await video.save(sequelizeOptions) as MVideoFullLight
- if (thumbnailModel) await video.addAndSaveThumbnail(thumbnailModel, t)
+ if (thumbnailModel) await videoUpdated.addAndSaveThumbnail(thumbnailModel, t)
// FIXME: use icon URL instead
- const previewUrl = buildRemoteBaseUrl(video, join(STATIC_PATHS.PREVIEWS, video.getPreview().filename))
+ const previewUrl = buildRemoteBaseUrl(videoUpdated, join(STATIC_PATHS.PREVIEWS, videoUpdated.getPreview().filename))
const previewModel = createPlaceholderThumbnail(previewUrl, video, ThumbnailType.PREVIEW, PREVIEWS_SIZE)
- await video.addAndSaveThumbnail(previewModel, t)
+ await videoUpdated.addAndSaveThumbnail(previewModel, t)
{
- const videoFileAttributes = videoFileActivityUrlToDBAttributes(video, videoObject)
+ const videoFileAttributes = videoFileActivityUrlToDBAttributes(videoUpdated, videoObject)
const newVideoFiles = videoFileAttributes.map(a => new VideoFileModel(a))
// Remove video files that do not exist anymore
- const destroyTasks = video.VideoFiles
+ const destroyTasks = videoUpdated.VideoFiles
.filter(f => !newVideoFiles.find(newFile => newFile.hasSameUniqueKeysThan(f)))
.map(f => f.destroy(sequelizeOptions))
await Promise.all(destroyTasks)
.then(([ file ]) => file)
})
- video.VideoFiles = await Promise.all(upsertTasks)
+ videoUpdated.VideoFiles = await Promise.all(upsertTasks)
}
{
- const streamingPlaylistAttributes = streamingPlaylistActivityUrlToDBAttributes(video, videoObject, video.VideoFiles)
+ const streamingPlaylistAttributes = streamingPlaylistActivityUrlToDBAttributes(videoUpdated, videoObject, videoUpdated.VideoFiles)
const newStreamingPlaylists = streamingPlaylistAttributes.map(a => new VideoStreamingPlaylistModel(a))
// Remove video files that do not exist anymore
- const destroyTasks = video.VideoStreamingPlaylists
+ const destroyTasks = videoUpdated.VideoStreamingPlaylists
.filter(f => !newStreamingPlaylists.find(newPlaylist => newPlaylist.hasSameUniqueKeysThan(f)))
.map(f => f.destroy(sequelizeOptions))
await Promise.all(destroyTasks)
.then(([ streamingPlaylist ]) => streamingPlaylist)
})
- video.VideoStreamingPlaylists = await Promise.all(upsertTasks)
+ videoUpdated.VideoStreamingPlaylists = await Promise.all(upsertTasks)
}
{
// Update Tags
const tags = videoObject.tag.map(tag => tag.name)
const tagInstances = await TagModel.findOrCreateTags(tags, t)
- await video.$set('Tags', tagInstances, sequelizeOptions)
+ await videoUpdated.$set('Tags', tagInstances, sequelizeOptions)
}
{
// Update captions
- await VideoCaptionModel.deleteAllCaptionsOfRemoteVideo(video.id, t)
+ await VideoCaptionModel.deleteAllCaptionsOfRemoteVideo(videoUpdated.id, t)
const videoCaptionsPromises = videoObject.subtitleLanguage.map(c => {
- return VideoCaptionModel.insertOrReplaceLanguage(video.id, c.identifier, t)
+ return VideoCaptionModel.insertOrReplaceLanguage(videoUpdated.id, c.identifier, t)
})
- video.VideoCaptions = await Promise.all(videoCaptionsPromises)
+ await Promise.all(videoCaptionsPromises)
}
+
+ return videoUpdated
})
await autoBlacklistVideoIfNeeded({
- video,
+ video: videoUpdated,
user: undefined,
isRemote: true,
isNew: false,
transaction: undefined
})
- if (wasPrivateVideo || wasUnlistedVideo) Notifier.Instance.notifyOnNewVideoIfNeeded(video) // Notify our users?
+ if (wasPrivateVideo || wasUnlistedVideo) Notifier.Instance.notifyOnNewVideoIfNeeded(videoUpdated) // Notify our users?
logger.info('Remote video with uuid %s updated', videoObject.uuid)
+
+ return videoUpdated
} catch (err) {
if (video !== undefined && videoFieldsSave !== undefined) {
resetSequelizeInstance(video, videoFieldsSave)
}
async function refreshVideoIfNeeded (options: {
- video: VideoModel,
+ video: MVideoThumbnail,
fetchedType: VideoFetchByUrlType,
syncParam: SyncParam
-}): Promise<VideoModel> {
+}): Promise<MVideoThumbnail> {
if (!options.video.isOutdated()) return options.video
// We need more attributes if the argument video was fetched with not enough joints
const video = options.fetchedType === 'all'
- ? options.video
+ ? options.video as MVideoAccountAllFiles
: await VideoModel.loadByUrlAndPopulateAccount(options.video.url)
try {
}
const channelActor = await getOrCreateVideoChannelFromVideoObject(videoObject)
- const account = await AccountModel.load(channelActor.VideoChannel.accountId)
const updateOptions = {
video,
videoObject,
- account,
+ account: channelActor.VideoChannel.Account,
channel: channelActor.VideoChannel
}
await retryTransactionWrapper(updateVideoFromAP, updateOptions)
return tag.name === 'sha256' && tag.type === 'Link' && urlMediaType === 'application/json'
}
-async function createVideo (videoObject: VideoTorrentObject, channelActor: ActorModel, waitThumbnail = false) {
+async function createVideo (videoObject: VideoTorrentObject, channel: MChannelAccountLight, waitThumbnail = false) {
logger.debug('Adding remote video %s.', videoObject.id)
- const videoData = await videoActivityObjectToDBAttributes(channelActor.VideoChannel, videoObject, videoObject.to)
- const video = VideoModel.build(videoData)
+ const videoData = await videoActivityObjectToDBAttributes(channel, videoObject, videoObject.to)
+ const video = VideoModel.build(videoData) as MVideoThumbnail
const promiseThumbnail = createVideoMiniatureFromUrl(videoObject.icon.url, video, ThumbnailType.MINIATURE)
- let thumbnailModel: ThumbnailModel
+ let thumbnailModel: MThumbnail
if (waitThumbnail === true) {
thumbnailModel = await promiseThumbnail
}
const { autoBlacklisted, videoCreated } = await sequelizeTypescript.transaction(async t => {
const sequelizeOptions = { transaction: t }
- const videoCreated = await video.save(sequelizeOptions)
- videoCreated.VideoChannel = channelActor.VideoChannel
+ const videoCreated = await video.save(sequelizeOptions) as MVideoFullLight
+ videoCreated.VideoChannel = channel
if (thumbnailModel) await videoCreated.addAndSaveThumbnail(thumbnailModel, t)
const videoCaptionsPromises = videoObject.subtitleLanguage.map(c => {
return VideoCaptionModel.insertOrReplaceLanguage(videoCreated.id, c.identifier, t)
})
- const captions = await Promise.all(videoCaptionsPromises)
+ await Promise.all(videoCaptionsPromises)
- video.VideoFiles = videoFiles
- video.VideoStreamingPlaylists = streamingPlaylists
- video.Tags = tagInstances
- video.VideoCaptions = captions
+ videoCreated.VideoFiles = videoFiles
+ videoCreated.VideoStreamingPlaylists = streamingPlaylists
+ videoCreated.Tags = tagInstances
const autoBlacklisted = await autoBlacklistVideoIfNeeded({
- video,
+ video: videoCreated,
user: undefined,
isRemote: true,
isNew: true,
return { autoBlacklisted, videoCreated }
}
-async function videoActivityObjectToDBAttributes (
- videoChannel: VideoChannelModelId,
- videoObject: VideoTorrentObject,
- to: string[] = []
-) {
+async function videoActivityObjectToDBAttributes (videoChannel: MChannelId, videoObject: VideoTorrentObject, to: string[] = []) {
const privacy = to.indexOf(ACTIVITY_PUB.PUBLIC) !== -1 ? VideoPrivacy.PUBLIC : VideoPrivacy.UNLISTED
const duration = videoObject.duration.replace(/[^\d]+/, '')
}
}
-function videoFileActivityUrlToDBAttributes (video: VideoModel, videoObject: VideoTorrentObject) {
+function videoFileActivityUrlToDBAttributes (video: MVideo, videoObject: VideoTorrentObject) {
const fileUrls = videoObject.url.filter(u => isAPVideoUrlObject(u)) as ActivityVideoUrlObject[]
if (fileUrls.length === 0) {
return attributes
}
-function streamingPlaylistActivityUrlToDBAttributes (video: VideoModel, videoObject: VideoTorrentObject, videoFiles: VideoFileModel[]) {
+function streamingPlaylistActivityUrlToDBAttributes (video: MVideoId, videoObject: VideoTorrentObject, videoFiles: MVideoFile[]) {
const playlistUrls = videoObject.url.filter(u => isAPStreamingPlaylistUrlObject(u)) as ActivityPlaylistUrlObject[]
if (playlistUrls.length === 0) return []
import { AVATARS_SIZE, LRU_CACHE, QUEUE_CONCURRENCY } from '../initializers/constants'
import { updateActorAvatarInstance } from './activitypub'
import { processImage } from '../helpers/image-utils'
-import { AccountModel } from '../models/account/account'
-import { VideoChannelModel } from '../models/video/video-channel'
import { extname, join } from 'path'
import { retryTransactionWrapper } from '../helpers/database-utils'
import * as uuidv4 from 'uuid/v4'
import * as LRUCache from 'lru-cache'
import { queue } from 'async'
import { downloadImage } from '../helpers/requests'
+import { MAccountActorDefault, MChannelActorDefault } from '../typings/models'
-async function updateActorAvatarFile (avatarPhysicalFile: Express.Multer.File, accountOrChannel: AccountModel | VideoChannelModel) {
+async function updateActorAvatarFile (
+ avatarPhysicalFile: Express.Multer.File,
+ accountOrChannel: MAccountActorDefault | MChannelActorDefault
+) {
const extension = extname(avatarPhysicalFile.filename)
const avatarName = uuidv4() + extension
const destination = join(CONFIG.STORAGE.AVATARS_DIR, avatarName)
import { sequelizeTypescript } from '../initializers'
import { AccountBlocklistModel } from '../models/account/account-blocklist'
import { ServerBlocklistModel } from '../models/server/server-blocklist'
+import { MAccountBlocklist, MServerBlocklist } from '@server/typings/models'
function addAccountInBlocklist (byAccountId: number, targetAccountId: number) {
return sequelizeTypescript.transaction(async t => {
})
}
-function removeAccountFromBlocklist (accountBlock: AccountBlocklistModel) {
+function removeAccountFromBlocklist (accountBlock: MAccountBlocklist) {
return sequelizeTypescript.transaction(async t => {
return accountBlock.destroy({ transaction: t })
})
}
-function removeServerFromBlocklist (serverBlock: ServerBlocklistModel) {
+function removeServerFromBlocklist (serverBlock: MServerBlocklist) {
return sequelizeTypescript.transaction(async t => {
return serverBlock.destroy({ transaction: t })
})
import * as Bluebird from 'bluebird'
import { CONFIG } from '../initializers/config'
import { logger } from '../helpers/logger'
+import { MAccountActor, MChannelActor, MVideo } from '../typings/models'
export class ClientHtml {
}
private static async getAccountOrChannelHTMLPage (
- loader: () => Bluebird<AccountModel | VideoChannelModel>,
+ loader: () => Bluebird<MAccountActor | MChannelActor>,
req: express.Request,
res: express.Response
) {
return htmlStringPage.replace('</head>', linkTag + '</head>')
}
- private static addVideoOpenGraphAndOEmbedTags (htmlStringPage: string, video: VideoModel) {
+ private static addVideoOpenGraphAndOEmbedTags (htmlStringPage: string, video: MVideo) {
const previewUrl = WEBSERVER.URL + video.getPreviewStaticPath()
const videoUrl = WEBSERVER.URL + video.getWatchStaticPath()
return this.addOpenGraphAndOEmbedTags(htmlStringPage, tagsString)
}
- private static addAccountOrChannelMetaTags (htmlStringPage: string, entity: AccountModel | VideoChannelModel) {
+ private static addAccountOrChannelMetaTags (htmlStringPage: string, entity: MAccountActor | MChannelActor) {
// SEO, use origin account or channel URL
const metaTags = `<link rel="canonical" href="${entity.Actor.url}" />`
import { bunyanLogger, logger } from '../helpers/logger'
import { CONFIG } from '../initializers/config'
import { UserModel } from '../models/account/user'
-import { VideoModel } from '../models/video/video'
import { JobQueue } from './job-queue'
import { EmailPayload } from './job-queue/handlers/email'
import { readFileSync } from 'fs-extra'
-import { VideoCommentModel } from '../models/video/video-comment'
-import { VideoAbuseModel } from '../models/video/video-abuse'
import { VideoBlacklistModel } from '../models/video/video-blacklist'
-import { VideoImportModel } from '../models/video/video-import'
-import { ActorFollowModel } from '../models/activitypub/actor-follow'
import { WEBSERVER } from '../initializers/constants'
+import { MCommentOwnerVideo, MVideo, MVideoAbuseVideo, MVideoAccountLight, MVideoBlacklistVideo } from '../typings/models/video'
+import { MActorFollowActors, MActorFollowFull, MUser } from '../typings/models'
+import { MVideoImport, MVideoImportVideo } from '@server/typings/models/video/video-import'
type SendEmailOptions = {
to: string[]
}
}
- addNewVideoFromSubscriberNotification (to: string[], video: VideoModel) {
+ addNewVideoFromSubscriberNotification (to: string[], video: MVideoAccountLight) {
const channelName = video.VideoChannel.getDisplayName()
const videoUrl = WEBSERVER.URL + video.getWatchStaticPath()
return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
}
- addNewFollowNotification (to: string[], actorFollow: ActorFollowModel, followType: 'account' | 'channel') {
+ addNewFollowNotification (to: string[], actorFollow: MActorFollowFull, followType: 'account' | 'channel') {
const followerName = actorFollow.ActorFollower.Account.getDisplayName()
const followingName = (actorFollow.ActorFollowing.VideoChannel || actorFollow.ActorFollowing.Account).getDisplayName()
return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
}
- addNewInstanceFollowerNotification (to: string[], actorFollow: ActorFollowModel) {
+ addNewInstanceFollowerNotification (to: string[], actorFollow: MActorFollowActors) {
const awaitingApproval = actorFollow.state === 'pending' ? ' awaiting manual approval.' : ''
const text = `Hi dear admin,\n\n` +
return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
}
- myVideoPublishedNotification (to: string[], video: VideoModel) {
+ myVideoPublishedNotification (to: string[], video: MVideo) {
const videoUrl = WEBSERVER.URL + video.getWatchStaticPath()
const text = `Hi dear user,\n\n` +
return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
}
- myVideoImportSuccessNotification (to: string[], videoImport: VideoImportModel) {
+ myVideoImportSuccessNotification (to: string[], videoImport: MVideoImportVideo) {
const videoUrl = WEBSERVER.URL + videoImport.Video.getWatchStaticPath()
const text = `Hi dear user,\n\n` +
return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
}
- myVideoImportErrorNotification (to: string[], videoImport: VideoImportModel) {
+ myVideoImportErrorNotification (to: string[], videoImport: MVideoImport) {
const importUrl = WEBSERVER.URL + '/my-account/video-imports'
const text = `Hi dear user,\n\n` +
return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
}
- addNewCommentOnMyVideoNotification (to: string[], comment: VideoCommentModel) {
+ addNewCommentOnMyVideoNotification (to: string[], comment: MCommentOwnerVideo) {
const accountName = comment.Account.getDisplayName()
const video = comment.Video
const commentUrl = WEBSERVER.URL + comment.getCommentStaticPath()
return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
}
- addNewCommentMentionNotification (to: string[], comment: VideoCommentModel) {
+ addNewCommentMentionNotification (to: string[], comment: MCommentOwnerVideo) {
const accountName = comment.Account.getDisplayName()
const video = comment.Video
const commentUrl = WEBSERVER.URL + comment.getCommentStaticPath()
return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
}
- addVideoAbuseModeratorsNotification (to: string[], videoAbuse: VideoAbuseModel) {
+ addVideoAbuseModeratorsNotification (to: string[], videoAbuse: MVideoAbuseVideo) {
const videoUrl = WEBSERVER.URL + videoAbuse.Video.getWatchStaticPath()
const text = `Hi,\n\n` +
return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
}
- addVideoAutoBlacklistModeratorsNotification (to: string[], video: VideoModel) {
+ addVideoAutoBlacklistModeratorsNotification (to: string[], video: MVideo) {
const VIDEO_AUTO_BLACKLIST_URL = WEBSERVER.URL + '/admin/moderation/video-auto-blacklist/list'
const videoUrl = WEBSERVER.URL + video.getWatchStaticPath()
return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
}
- addNewUserRegistrationNotification (to: string[], user: UserModel) {
+ addNewUserRegistrationNotification (to: string[], user: MUser) {
const text = `Hi,\n\n` +
`User ${user.username} just registered on ${WEBSERVER.HOST} PeerTube instance.\n\n` +
`Cheers,\n` +
return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
}
- addVideoBlacklistNotification (to: string[], videoBlacklist: VideoBlacklistModel) {
+ addVideoBlacklistNotification (to: string[], videoBlacklist: MVideoBlacklistVideo) {
const videoName = videoBlacklist.Video.name
const videoUrl = WEBSERVER.URL + videoBlacklist.Video.getWatchStaticPath()
return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
}
- addVideoUnblacklistNotification (to: string[], video: VideoModel) {
+ addVideoUnblacklistNotification (to: string[], video: MVideo) {
const videoUrl = WEBSERVER.URL + video.getWatchStaticPath()
const text = 'Hi,\n\n' +
return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
}
- addUserBlockJob (user: UserModel, blocked: boolean, reason?: string) {
+ addUserBlockJob (user: MUser, blocked: boolean, reason?: string) {
const reasonString = reason ? ` for the following reason: ${reason}` : ''
const blockedWord = blocked ? 'blocked' : 'unblocked'
const blockedString = `Your account ${user.username} on ${WEBSERVER.HOST} has been ${blockedWord}${reasonString}.`
-import { VideoModel } from '../models/video/video'
import { basename, dirname, join } from 'path'
import { HLS_STREAMING_PLAYLIST_DIRECTORY, P2P_MEDIA_LOADER_PEER_VERSION } from '../initializers/constants'
import { close, ensureDir, move, open, outputJSON, pathExists, read, readFile, remove, writeFile } from 'fs-extra'
import { VideoFileModel } from '../models/video/video-file'
import { CONFIG } from '../initializers/config'
import { sequelizeTypescript } from '../initializers/database'
+import { MVideoWithFile } from '@server/typings/models'
async function updateStreamingPlaylistsInfohashesIfNeeded () {
const playlistsToUpdate = await VideoStreamingPlaylistModel.listByIncorrectPeerVersion()
}
}
-async function updateMasterHLSPlaylist (video: VideoModel) {
+async function updateMasterHLSPlaylist (video: MVideoWithFile) {
const directory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid)
const masterPlaylists: string[] = [ '#EXTM3U', '#EXT-X-VERSION:3' ]
const masterPlaylistPath = join(directory, VideoStreamingPlaylistModel.getMasterHlsPlaylistFilename())
await writeFile(masterPlaylistPath, masterPlaylists.join('\n') + '\n')
}
-async function updateSha256Segments (video: VideoModel) {
+async function updateSha256Segments (video: MVideoWithFile) {
const json: { [filename: string]: { [range: string]: string } } = {}
const playlistDirectory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid)
import { ActorModel } from '../../../models/activitypub/actor'
import { Notifier } from '../../notifier'
import { sequelizeTypescript } from '../../../initializers/database'
+import { MActorFollowFull, MActorFull } from '../../../typings/models'
export type ActivitypubFollowPayload = {
followerActorId: number
logger.info('Processing ActivityPub follow in job %d.', job.id)
- let targetActor: ActorModel
+ let targetActor: MActorFull
if (!host || host === WEBSERVER.HOST) {
targetActor = await ActorModel.loadLocalByName(payload.name)
} else {
const sanitizedHost = sanitizeHost(host, REMOTE_SCHEME.HTTP)
const actorUrl = await loadActorUrlOrGetFromWebfinger(payload.name + '@' + sanitizedHost)
- targetActor = await getOrCreateActorAndServerAndModel(actorUrl)
+ targetActor = await getOrCreateActorAndServerAndModel(actorUrl, 'all')
}
const fromActor = await ActorModel.load(payload.followerActorId)
// ---------------------------------------------------------------------------
-async function follow (fromActor: ActorModel, targetActor: ActorModel) {
+async function follow (fromActor: MActorFull, targetActor: MActorFull) {
if (fromActor.id === targetActor.id) {
throw new Error('Follower is the same than target actor.')
}
const state = !fromActor.serverId && !targetActor.serverId ? 'accepted' : 'pending'
const actorFollow = await sequelizeTypescript.transaction(async t => {
- const [ actorFollow ] = await ActorFollowModel.findOrCreate({
+ const [ actorFollow ] = await ActorFollowModel.findOrCreate<MActorFollowFull>({
where: {
actorId: fromActor.id,
targetActorId: targetActor.id
import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
import { VideoShareModel } from '../../../models/video/video-share'
import { VideoCommentModel } from '../../../models/video/video-comment'
+import { MAccountDefault, MVideoFullLight } from '../../../typings/models'
type FetchType = 'activity' | 'video-likes' | 'video-dislikes' | 'video-shares' | 'video-comments' | 'account-playlists'
const payload = job.data as ActivitypubHttpFetcherPayload
- let video: VideoModel
+ let video: MVideoFullLight
if (payload.videoId) video = await VideoModel.loadAndPopulateAccountAndServerAndTags(payload.videoId)
- let account: AccountModel
+ let account: MAccountDefault
if (payload.accountId) account = await AccountModel.load(payload.accountId)
const fetcherType: { [ id in FetchType ]: (items: any[]) => Promise<any> } = {
import { ActorModel } from '../../../../models/activitypub/actor'
import { sha256 } from '../../../../helpers/core-utils'
import { HTTP_SIGNATURE } from '../../../../initializers/constants'
+import { MActor } from '../../../../typings/models'
type Payload = { body: any, signatureActorId?: number }
}
async function buildSignedRequestOptions (payload: Payload) {
- let actor: ActorModel | null
+ let actor: MActor | null
+
if (payload.signatureActorId) {
actor = await ActorModel.load(payload.signatureActorId)
if (!actor) throw new Error('Unknown signature actor id.')
import { copy, stat } from 'fs-extra'
import { VideoFileModel } from '../../../models/video/video-file'
import { extname } from 'path'
+import { MVideoFile, MVideoWithFile } from '@server/typings/models'
export type VideoFileImportPayload = {
videoUUID: string,
// ---------------------------------------------------------------------------
-async function updateVideoFile (video: VideoModel, inputFilePath: string) {
+async function updateVideoFile (video: MVideoWithFile, inputFilePath: string) {
const { videoFileResolution } = await getVideoFileResolution(inputFilePath)
const { size } = await stat(inputFilePath)
const fps = await getVideoFileFPS(inputFilePath)
size,
fps,
videoId: video.id
- })
+ }) as MVideoFile
const currentVideoFile = video.VideoFiles.find(videoFile => videoFile.resolution === updatedVideoFile.resolution)
video.VideoFiles = video.VideoFiles.filter(f => f !== currentVideoFile)
// Update the database
- currentVideoFile.set('extname', updatedVideoFile.extname)
- currentVideoFile.set('size', updatedVideoFile.size)
- currentVideoFile.set('fps', updatedVideoFile.fps)
+ currentVideoFile.extname = updatedVideoFile.extname
+ currentVideoFile.size = updatedVideoFile.size
+ currentVideoFile.fps = updatedVideoFile.fps
updatedVideoFile = currentVideoFile
}
import { Notifier } from '../../notifier'
import { CONFIG } from '../../../initializers/config'
import { sequelizeTypescript } from '../../../initializers/database'
-import { ThumbnailModel } from '../../../models/video/thumbnail'
import { createVideoMiniatureFromUrl, generateVideoMiniature } from '../../thumbnail'
import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type'
+import { MThumbnail } from '../../../typings/models/video/thumbnail'
+import { MVideoImportDefault, MVideoImportDefaultFiles, MVideoImportVideo } from '@server/typings/models/video/video-import'
type VideoImportYoutubeDLPayload = {
type: 'youtube-dl'
generateThumbnail: boolean
generatePreview: boolean
}
-async function processFile (downloader: () => Promise<string>, videoImport: VideoImportModel, options: ProcessFileOptions) {
+async function processFile (downloader: () => Promise<string>, videoImportArg: MVideoImportDefault, options: ProcessFileOptions) {
let tempVideoPath: string
let videoDestFile: string
let videoFile: VideoFileModel
+ const videoImport = videoImportArg as MVideoImportDefaultFiles
+
try {
// Download video from youtubeDL
tempVideoPath = await downloader()
tempVideoPath = null // This path is not used anymore
// Process thumbnail
- let thumbnailModel: ThumbnailModel
+ let thumbnailModel: MThumbnail
if (options.downloadThumbnail && options.thumbnailUrl) {
thumbnailModel = await createVideoMiniatureFromUrl(options.thumbnailUrl, videoImport.Video, ThumbnailType.MINIATURE)
} else if (options.generateThumbnail || options.downloadThumbnail) {
}
// Process preview
- let previewModel: ThumbnailModel
+ let previewModel: MThumbnail
if (options.downloadPreview && options.thumbnailUrl) {
previewModel = await createVideoMiniatureFromUrl(options.thumbnailUrl, videoImport.Video, ThumbnailType.PREVIEW)
} else if (options.generatePreview || options.downloadPreview) {
// Create torrent
await videoImport.Video.createTorrentAndSetInfoHash(videoFile)
- const videoImportUpdated: VideoImportModel = await sequelizeTypescript.transaction(async t => {
+ const { videoImportUpdated, video } = await sequelizeTypescript.transaction(async t => {
+ const videoImportToUpdate = videoImport as MVideoImportVideo
+
// Refresh video
- const video = await VideoModel.load(videoImport.videoId, t)
- if (!video) throw new Error('Video linked to import ' + videoImport.videoId + ' does not exist anymore.')
- videoImport.Video = video
+ const video = await VideoModel.load(videoImportToUpdate.videoId, t)
+ if (!video) throw new Error('Video linked to import ' + videoImportToUpdate.videoId + ' does not exist anymore.')
const videoFileCreated = await videoFile.save({ transaction: t })
- video.VideoFiles = [ videoFileCreated ]
+ videoImportToUpdate.Video = Object.assign(video, { VideoFiles: [ videoFileCreated ] })
// Update video DB object
video.duration = duration
await federateVideoIfNeeded(videoForFederation, true, t)
// Update video import object
- videoImport.state = VideoImportState.SUCCESS
- const videoImportUpdated = await videoImport.save({ transaction: t })
+ videoImportToUpdate.state = VideoImportState.SUCCESS
+ const videoImportUpdated = await videoImportToUpdate.save({ transaction: t }) as MVideoImportVideo
+ videoImportUpdated.Video = video
logger.info('Video %s imported.', video.uuid)
- videoImportUpdated.Video = videoForFederation
- return videoImportUpdated
+ return { videoImportUpdated, video: videoForFederation }
})
Notifier.Instance.notifyOnFinishedVideoImport(videoImportUpdated, true)
- if (videoImportUpdated.Video.isBlacklisted()) {
- Notifier.Instance.notifyOnVideoAutoBlacklist(videoImportUpdated.Video)
+ if (video.isBlacklisted()) {
+ Notifier.Instance.notifyOnVideoAutoBlacklist(video)
} else {
- Notifier.Instance.notifyOnNewVideoIfNeeded(videoImportUpdated.Video)
+ Notifier.Instance.notifyOnNewVideoIfNeeded(video)
}
// Create transcoding jobs?
- if (videoImportUpdated.Video.state === VideoState.TO_TRANSCODE) {
+ if (video.state === VideoState.TO_TRANSCODE) {
// Put uuid because we don't have id auto incremented for now
const dataInput = {
type: 'optimize' as 'optimize',
import { generateHlsPlaylist, optimizeVideofile, transcodeOriginalVideofile, mergeAudioVideofile } from '../../video-transcoding'
import { Notifier } from '../../notifier'
import { CONFIG } from '../../../initializers/config'
+import { MVideoUUID, MVideoWithFile } from '@server/typings/models'
interface BaseTranscodingPayload {
videoUUID: string
return video
}
-async function onHlsPlaylistGenerationSuccess (video: VideoModel) {
+async function onHlsPlaylistGenerationSuccess (video: MVideoUUID) {
if (video === undefined) return undefined
await sequelizeTypescript.transaction(async t => {
})
}
-async function publishNewResolutionIfNeeded (video: VideoModel, payload?: NewResolutionTranscodingPayload | MergeAudioTranscodingPayload) {
+async function publishNewResolutionIfNeeded (video: MVideoUUID, payload?: NewResolutionTranscodingPayload | MergeAudioTranscodingPayload) {
const { videoDatabase, videoPublished } = await sequelizeTypescript.transaction(async t => {
// Maybe the video changed in database, refresh it
let videoDatabase = await VideoModel.loadAndPopulateAccountAndServerAndTags(video.uuid, t)
await createHlsJobIfEnabled(payload)
}
-async function onVideoFileOptimizerSuccess (videoArg: VideoModel, payload: OptimizeTranscodingPayload) {
+async function onVideoFileOptimizerSuccess (videoArg: MVideoWithFile, payload: OptimizeTranscodingPayload) {
if (videoArg === undefined) return undefined
// Outside the transaction (IO on disk)
import { PeerTubeSocket } from './peertube-socket'
import { CONFIG } from '../initializers/config'
import { VideoPrivacy, VideoState } from '../../shared/models/videos'
-import { VideoAbuseModel } from '../models/video/video-abuse'
import { VideoBlacklistModel } from '../models/video/video-blacklist'
import * as Bluebird from 'bluebird'
import { VideoImportModel } from '../models/video/video-import'
import { AccountBlocklistModel } from '../models/account/account-blocklist'
+import {
+ MCommentOwnerVideo,
+ MVideo,
+ MVideoAbuseVideo,
+ MVideoAccountLight,
+ MVideoBlacklistVideo,
+ MVideoFullLight
+} from '../typings/models/video'
+import { MUser, MUserAccount, MUserWithNotificationSetting, UserNotificationModelForApi } from '@server/typings/models/user'
+import { MActorFollowActors, MActorFollowFull } from '../typings/models'
import { ActorFollowModel } from '../models/activitypub/actor-follow'
-import { AccountModel } from '../models/account/account'
+import { MVideoImportVideo } from '@server/typings/models/video/video-import'
+import { AccountModel } from '@server/models/account/account'
class Notifier {
private constructor () {}
- notifyOnNewVideoIfNeeded (video: VideoModel): void {
+ notifyOnNewVideoIfNeeded (video: MVideoAccountLight): void {
// Only notify on public and published videos which are not blacklisted
if (video.privacy !== VideoPrivacy.PUBLIC || video.state !== VideoState.PUBLISHED || video.isBlacklisted()) return
.catch(err => logger.error('Cannot notify subscribers of new video %s.', video.url, { err }))
}
- notifyOnVideoPublishedAfterTranscoding (video: VideoModel): void {
+ notifyOnVideoPublishedAfterTranscoding (video: MVideoFullLight): void {
// don't notify if didn't wait for transcoding or video is still blacklisted/waiting for scheduled update
if (!video.waitTranscoding || video.VideoBlacklist || video.ScheduleVideoUpdate) return
.catch(err => logger.error('Cannot notify owner that its video %s has been published after transcoding.', video.url, { err }))
}
- notifyOnVideoPublishedAfterScheduledUpdate (video: VideoModel): void {
+ notifyOnVideoPublishedAfterScheduledUpdate (video: MVideoFullLight): void {
// don't notify if video is still blacklisted or waiting for transcoding
if (video.VideoBlacklist || (video.waitTranscoding && video.state !== VideoState.PUBLISHED)) return
.catch(err => logger.error('Cannot notify owner that its video %s has been published after scheduled update.', video.url, { err }))
}
- notifyOnVideoPublishedAfterRemovedFromAutoBlacklist (video: VideoModel): void {
+ notifyOnVideoPublishedAfterRemovedFromAutoBlacklist (video: MVideoFullLight): void {
// don't notify if video is still waiting for transcoding or scheduled update
if (video.ScheduleVideoUpdate || (video.waitTranscoding && video.state !== VideoState.PUBLISHED)) return
.catch(err => logger.error('Cannot notify owner that its video %s has been published after removed from auto-blacklist.', video.url, { err })) // tslint:disable-line:max-line-length
}
- notifyOnNewComment (comment: VideoCommentModel): void {
+ notifyOnNewComment (comment: MCommentOwnerVideo): void {
this.notifyVideoOwnerOfNewComment(comment)
.catch(err => logger.error('Cannot notify video owner of new comment %s.', comment.url, { err }))
.catch(err => logger.error('Cannot notify mentions of comment %s.', comment.url, { err }))
}
- notifyOnNewVideoAbuse (videoAbuse: VideoAbuseModel): void {
+ notifyOnNewVideoAbuse (videoAbuse: MVideoAbuseVideo): void {
this.notifyModeratorsOfNewVideoAbuse(videoAbuse)
.catch(err => logger.error('Cannot notify of new video abuse of video %s.', videoAbuse.Video.url, { err }))
}
- notifyOnVideoAutoBlacklist (video: VideoModel): void {
+ notifyOnVideoAutoBlacklist (video: MVideo): void {
this.notifyModeratorsOfVideoAutoBlacklist(video)
.catch(err => logger.error('Cannot notify of auto-blacklist of video %s.', video.url, { err }))
}
- notifyOnVideoBlacklist (videoBlacklist: VideoBlacklistModel): void {
+ notifyOnVideoBlacklist (videoBlacklist: MVideoBlacklistVideo): void {
this.notifyVideoOwnerOfBlacklist(videoBlacklist)
.catch(err => logger.error('Cannot notify video owner of new video blacklist of %s.', videoBlacklist.Video.url, { err }))
}
- notifyOnVideoUnblacklist (video: VideoModel): void {
+ notifyOnVideoUnblacklist (video: MVideo): void {
this.notifyVideoOwnerOfUnblacklist(video)
.catch(err => logger.error('Cannot notify video owner of unblacklist of %s.', video.url, { err }))
}
- notifyOnFinishedVideoImport (videoImport: VideoImportModel, success: boolean): void {
+ notifyOnFinishedVideoImport (videoImport: MVideoImportVideo, success: boolean): void {
this.notifyOwnerVideoImportIsFinished(videoImport, success)
.catch(err => logger.error('Cannot notify owner that its video import %s is finished.', videoImport.getTargetIdentifier(), { err }))
}
- notifyOnNewUserRegistration (user: UserModel): void {
+ notifyOnNewUserRegistration (user: MUserAccount): void {
this.notifyModeratorsOfNewUserRegistration(user)
.catch(err => logger.error('Cannot notify moderators of new user registration (%s).', user.username, { err }))
}
- notifyOfNewUserFollow (actorFollow: ActorFollowModel): void {
+ notifyOfNewUserFollow (actorFollow: MActorFollowFull): void {
this.notifyUserOfNewActorFollow(actorFollow)
.catch(err => {
logger.error(
})
}
- notifyOfNewInstanceFollow (actorFollow: ActorFollowModel): void {
+ notifyOfNewInstanceFollow (actorFollow: MActorFollowActors): void {
this.notifyAdminsOfNewInstanceFollow(actorFollow)
.catch(err => {
logger.error('Cannot notify administrators of new follower %s.', actorFollow.ActorFollower.url, { err })
})
}
- private async notifySubscribersOfNewVideo (video: VideoModel) {
+ private async notifySubscribersOfNewVideo (video: MVideoAccountLight) {
// List all followers that are users
const users = await UserModel.listUserSubscribersOf(video.VideoChannel.actorId)
userId: user.id,
videoId: video.id
})
- notification.Video = video
+ notification.Video = video as VideoModel
return notification
}
return this.notify({ users, settingGetter, notificationCreator, emailSender })
}
- private async notifyVideoOwnerOfNewComment (comment: VideoCommentModel) {
+ private async notifyVideoOwnerOfNewComment (comment: MCommentOwnerVideo) {
if (comment.Video.isOwned() === false) return
const user = await UserModel.loadByVideoId(comment.videoId)
userId: user.id,
commentId: comment.id
})
- notification.Comment = comment
+ notification.Comment = comment as VideoCommentModel
return notification
}
return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender })
}
- private async notifyOfCommentMention (comment: VideoCommentModel) {
+ private async notifyOfCommentMention (comment: MCommentOwnerVideo) {
const extractedUsernames = comment.extractMentions()
logger.debug(
'Extracted %d username from comment %s.', extractedUsernames.length, comment.url,
userId: user.id,
commentId: comment.id
})
- notification.Comment = comment
+ notification.Comment = comment as VideoCommentModel
return notification
}
return this.notify({ users, settingGetter, notificationCreator, emailSender })
}
- private async notifyUserOfNewActorFollow (actorFollow: ActorFollowModel) {
+ private async notifyUserOfNewActorFollow (actorFollow: MActorFollowFull) {
if (actorFollow.ActorFollowing.isOwned() === false) return
// Account follows one of our account?
if (!user) return
- if (!actorFollow.ActorFollower.Account || !actorFollow.ActorFollower.Account.name) {
- actorFollow.ActorFollower.Account = await actorFollow.ActorFollower.$get('Account') as AccountModel
- }
const followerAccount = actorFollow.ActorFollower.Account
const accountMuted = await AccountBlocklistModel.isAccountMutedBy(user.Account.id, followerAccount.id)
userId: user.id,
actorFollowId: actorFollow.id
})
- notification.ActorFollow = actorFollow
+ notification.ActorFollow = actorFollow as ActorFollowModel
return notification
}
return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender })
}
- private async notifyAdminsOfNewInstanceFollow (actorFollow: ActorFollowModel) {
+ private async notifyAdminsOfNewInstanceFollow (actorFollow: MActorFollowActors) {
const admins = await UserModel.listWithRight(UserRight.MANAGE_SERVER_FOLLOW)
logger.info('Notifying %d administrators of new instance follower: %s.', admins.length, actorFollow.ActorFollower.url)
userId: user.id,
actorFollowId: actorFollow.id
})
- notification.ActorFollow = actorFollow
+ notification.ActorFollow = actorFollow as ActorFollowModel
return notification
}
return this.notify({ users: admins, settingGetter, notificationCreator, emailSender })
}
- private async notifyModeratorsOfNewVideoAbuse (videoAbuse: VideoAbuseModel) {
+ private async notifyModeratorsOfNewVideoAbuse (videoAbuse: MVideoAbuseVideo) {
const moderators = await UserModel.listWithRight(UserRight.MANAGE_VIDEO_ABUSES)
if (moderators.length === 0) return
}
async function notificationCreator (user: UserModel) {
- const notification = await UserNotificationModel.create({
+ const notification: UserNotificationModelForApi = await UserNotificationModel.create({
type: UserNotificationType.NEW_VIDEO_ABUSE_FOR_MODERATORS,
userId: user.id,
videoAbuseId: videoAbuse.id
return this.notify({ users: moderators, settingGetter, notificationCreator, emailSender })
}
- private async notifyModeratorsOfVideoAutoBlacklist (video: VideoModel) {
+ private async notifyModeratorsOfVideoAutoBlacklist (video: MVideo) {
const moderators = await UserModel.listWithRight(UserRight.MANAGE_VIDEO_BLACKLIST)
if (moderators.length === 0) return
userId: user.id,
videoId: video.id
})
- notification.Video = video
+ notification.Video = video as VideoModel
return notification
}
return this.notify({ users: moderators, settingGetter, notificationCreator, emailSender })
}
- private async notifyVideoOwnerOfBlacklist (videoBlacklist: VideoBlacklistModel) {
+ private async notifyVideoOwnerOfBlacklist (videoBlacklist: MVideoBlacklistVideo) {
const user = await UserModel.loadByVideoId(videoBlacklist.videoId)
if (!user) return
userId: user.id,
videoBlacklistId: videoBlacklist.id
})
- notification.VideoBlacklist = videoBlacklist
+ notification.VideoBlacklist = videoBlacklist as VideoBlacklistModel
return notification
}
return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender })
}
- private async notifyVideoOwnerOfUnblacklist (video: VideoModel) {
+ private async notifyVideoOwnerOfUnblacklist (video: MVideo) {
const user = await UserModel.loadByVideoId(video.id)
if (!user) return
userId: user.id,
videoId: video.id
})
- notification.Video = video
+ notification.Video = video as VideoModel
return notification
}
return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender })
}
- private async notifyOwnedVideoHasBeenPublished (video: VideoModel) {
+ private async notifyOwnedVideoHasBeenPublished (video: MVideoFullLight) {
const user = await UserModel.loadByVideoId(video.id)
if (!user) return
userId: user.id,
videoId: video.id
})
- notification.Video = video
+ notification.Video = video as VideoModel
return notification
}
return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender })
}
- private async notifyOwnerVideoImportIsFinished (videoImport: VideoImportModel, success: boolean) {
+ private async notifyOwnerVideoImportIsFinished (videoImport: MVideoImportVideo, success: boolean) {
const user = await UserModel.loadByVideoImportId(videoImport.id)
if (!user) return
userId: user.id,
videoImportId: videoImport.id
})
- notification.VideoImport = videoImport
+ notification.VideoImport = videoImport as VideoImportModel
return notification
}
return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender })
}
- private async notifyModeratorsOfNewUserRegistration (registeredUser: UserModel) {
+ private async notifyModeratorsOfNewUserRegistration (registeredUser: MUserAccount) {
const moderators = await UserModel.listWithRight(UserRight.MANAGE_USERS)
if (moderators.length === 0) return
logger.info(
'Notifying %s moderators of new user registration of %s.',
- moderators.length, registeredUser.Account.Actor.preferredUsername
+ moderators.length, registeredUser.username
)
function settingGetter (user: UserModel) {
userId: user.id,
accountId: registeredUser.Account.id
})
- notification.Account = registeredUser.Account
+ notification.Account = registeredUser.Account as AccountModel
return notification
}
}
private async notify (options: {
- users: UserModel[],
- notificationCreator: (user: UserModel) => Promise<UserNotificationModel>,
+ users: MUserWithNotificationSetting[],
+ notificationCreator: (user: MUserWithNotificationSetting) => Promise<UserNotificationModelForApi>,
emailSender: (emails: string[]) => Promise<any> | Bluebird<any>,
- settingGetter: (user: UserModel) => UserNotificationSettingValue
+ settingGetter: (user: MUserWithNotificationSetting) => UserNotificationSettingValue
}) {
const emails: string[] = []
}
}
- private isEmailEnabled (user: UserModel, value: UserNotificationSettingValue) {
+ private isEmailEnabled (user: MUser, value: UserNotificationSettingValue) {
if (CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION === true && user.emailVerified === false) return false
return value & UserNotificationSettingValue.EMAIL
import { Transaction } from 'sequelize'
import { CONFIG } from '../initializers/config'
import * as LRUCache from 'lru-cache'
+import { MOAuthTokenUser } from '@server/typings/models/oauth/oauth-token'
type TokenInfo = { accessToken: string, refreshToken: string, accessTokenExpiresAt: Date, refreshTokenExpiresAt: Date }
-const accessTokenCache = new LRUCache<string, OAuthTokenModel>({ max: LRU_CACHE.USER_TOKENS.MAX_SIZE })
+const accessTokenCache = new LRUCache<string, MOAuthTokenUser>({ max: LRU_CACHE.USER_TOKENS.MAX_SIZE })
const userHavingToken = new LRUCache<number, string>({ max: LRU_CACHE.USER_TOKENS.MAX_SIZE })
// ---------------------------------------------------------------------------
import * as SocketIO from 'socket.io'
import { authenticateSocket } from '../middlewares'
-import { UserNotificationModel } from '../models/account/user-notification'
import { logger } from '../helpers/logger'
import { Server } from 'http'
+import { UserNotificationModelForApi } from '@server/typings/models/user'
class PeerTubeSocket {
})
}
- sendNotification (userId: number, notification: UserNotificationModel) {
+ sendNotification (userId: number, notification: UserNotificationModelForApi) {
const socket = this.userNotificationSockets[userId]
if (!socket) return
import { sendUndoCacheFile } from './activitypub/send'
import { Transaction } from 'sequelize'
import { getServerActor } from '../helpers/utils'
+import { MVideoRedundancyVideo } from '@server/typings/models'
-async function removeVideoRedundancy (videoRedundancy: VideoRedundancyModel, t?: Transaction) {
+async function removeVideoRedundancy (videoRedundancy: MVideoRedundancyVideo, t?: Transaction) {
const serverActor = await getServerActor()
// Local cache, send undo to remote instances
import { logger } from '../../helpers/logger'
import { VideosRedundancy } from '../../../shared/models/redundancy'
import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy'
-import { VideoFileModel } from '../../models/video/video-file'
import { downloadWebTorrentVideo } from '../../helpers/webtorrent'
import { join } from 'path'
import { move } from 'fs-extra'
import { getVideoCacheFileActivityPubUrl, getVideoCacheStreamingPlaylistActivityPubUrl } from '../activitypub/url'
import { removeVideoRedundancy } from '../redundancy'
import { getOrCreateVideoAndAccountAndChannel } from '../activitypub'
-import { VideoStreamingPlaylistModel } from '../../models/video/video-streaming-playlist'
-import { VideoModel } from '../../models/video/video'
import { downloadPlaylistSegments } from '../hls'
import { CONFIG } from '../../initializers/config'
+import {
+ MStreamingPlaylist,
+ MStreamingPlaylistVideo,
+ MVideoAccountLight,
+ MVideoFile,
+ MVideoFileVideo,
+ MVideoRedundancyFileVideo,
+ MVideoRedundancyStreamingPlaylistVideo,
+ MVideoRedundancyVideo,
+ MVideoWithAllFiles
+} from '@server/typings/models'
type CandidateToDuplicate = {
redundancy: VideosRedundancy,
- video: VideoModel,
- files: VideoFileModel[],
- streamingPlaylists: VideoStreamingPlaylistModel[]
+ video: MVideoWithAllFiles,
+ files: MVideoFile[],
+ streamingPlaylists: MStreamingPlaylist[]
+}
+
+function isMVideoRedundancyFileVideo (o: MVideoRedundancyVideo): o is MVideoRedundancyFileVideo {
+ return !!(o as MVideoRedundancyFileVideo).VideoFile
}
export class VideosRedundancyScheduler extends AbstractScheduler {
}
}
- private async extendsRedundancy (redundancyModel: VideoRedundancyModel) {
+ private async extendsRedundancy (redundancyModel: MVideoRedundancyVideo) {
const redundancy = CONFIG.REDUNDANCY.VIDEOS.STRATEGIES.find(s => s.strategy === redundancyModel.strategy)
// Redundancy strategy disabled, remove our redundancy instead of extending expiration
if (!redundancy) {
}
}
- private async createVideoFileRedundancy (redundancy: VideosRedundancy, video: VideoModel, file: VideoFileModel) {
+ private async createVideoFileRedundancy (redundancy: VideosRedundancy, video: MVideoAccountLight, fileArg: MVideoFile) {
+ const file = fileArg as MVideoFileVideo
file.Video = video
const serverActor = await getServerActor()
const destPath = join(CONFIG.STORAGE.REDUNDANCY_DIR, video.getVideoFilename(file))
await move(tmpPath, destPath)
- const createdModel = await VideoRedundancyModel.create({
+ const createdModel: MVideoRedundancyFileVideo = await VideoRedundancyModel.create({
expiresOn: this.buildNewExpiration(redundancy.minLifetime),
url: getVideoCacheFileActivityPubUrl(file),
fileUrl: video.getVideoRedundancyUrl(file, WEBSERVER.URL),
logger.info('Duplicated %s - %d -> %s.', video.url, file.resolution, createdModel.url)
}
- private async createStreamingPlaylistRedundancy (redundancy: VideosRedundancy, video: VideoModel, playlist: VideoStreamingPlaylistModel) {
+ private async createStreamingPlaylistRedundancy (
+ redundancy: VideosRedundancy,
+ video: MVideoAccountLight,
+ playlistArg: MStreamingPlaylist
+ ) {
+ const playlist = playlistArg as MStreamingPlaylistVideo
playlist.Video = video
const serverActor = await getServerActor()
const destDirectory = join(HLS_REDUNDANCY_DIRECTORY, video.uuid)
await downloadPlaylistSegments(playlist.playlistUrl, destDirectory, VIDEO_IMPORT_TIMEOUT)
- const createdModel = await VideoRedundancyModel.create({
+ const createdModel: MVideoRedundancyStreamingPlaylistVideo = await VideoRedundancyModel.create({
expiresOn: this.buildNewExpiration(redundancy.minLifetime),
url: getVideoCacheStreamingPlaylistActivityPubUrl(video, playlist),
fileUrl: playlist.getVideoRedundancyUrl(WEBSERVER.URL),
logger.info('Duplicated playlist %s -> %s.', playlist.playlistUrl, createdModel.url)
}
- private async extendsExpirationOf (redundancy: VideoRedundancyModel, expiresAfterMs: number) {
+ private async extendsExpirationOf (redundancy: MVideoRedundancyVideo, expiresAfterMs: number) {
logger.info('Extending expiration of %s.', redundancy.url)
const serverActor = await getServerActor()
private async purgeCacheIfNeeded (candidateToDuplicate: CandidateToDuplicate) {
while (this.isTooHeavy(candidateToDuplicate)) {
const redundancy = candidateToDuplicate.redundancy
- const toDelete = await VideoRedundancyModel.loadOldestLocalThatAlreadyExpired(redundancy.strategy, redundancy.minLifetime)
+ const toDelete = await VideoRedundancyModel.loadOldestLocalExpired(redundancy.strategy, redundancy.minLifetime)
if (!toDelete) return
await removeVideoRedundancy(toDelete)
return new Date(Date.now() + expiresAfterMs)
}
- private buildEntryLogId (object: VideoRedundancyModel) {
- if (object.VideoFile) return `${object.VideoFile.Video.url}-${object.VideoFile.resolution}`
+ private buildEntryLogId (object: MVideoRedundancyFileVideo | MVideoRedundancyStreamingPlaylistVideo) {
+ if (isMVideoRedundancyFileVideo(object)) return `${object.VideoFile.Video.url}-${object.VideoFile.resolution}`
return `${object.VideoStreamingPlaylist.playlistUrl}`
}
- private getTotalFileSizes (files: VideoFileModel[], playlists: VideoStreamingPlaylistModel[]) {
- const fileReducer = (previous: number, current: VideoFileModel) => previous + current.size
+ private getTotalFileSizes (files: MVideoFile[], playlists: MStreamingPlaylist[]) {
+ const fileReducer = (previous: number, current: MVideoFile) => previous + current.size
const totalSize = files.reduce(fileReducer, 0)
if (playlists.length === 0) return totalSize
-import { VideoFileModel } from '../models/video/video-file'
import { generateImageFromVideoFile } from '../helpers/ffmpeg-utils'
import { CONFIG } from '../initializers/config'
-import { PREVIEWS_SIZE, THUMBNAILS_SIZE, ASSETS_PATH } from '../initializers/constants'
-import { VideoModel } from '../models/video/video'
+import { ASSETS_PATH, PREVIEWS_SIZE, THUMBNAILS_SIZE } from '../initializers/constants'
import { ThumbnailModel } from '../models/video/thumbnail'
import { ThumbnailType } from '../../shared/models/videos/thumbnail.type'
import { processImage } from '../helpers/image-utils'
import { join } from 'path'
import { downloadImage } from '../helpers/requests'
-import { VideoPlaylistModel } from '../models/video/video-playlist'
+import { MVideoPlaylistThumbnail } from '../typings/models/video/video-playlist'
+import { MVideoFile, MVideoThumbnail } from '../typings/models'
+import { MThumbnail } from '../typings/models/video/thumbnail'
type ImageSize = { height: number, width: number }
function createPlaylistMiniatureFromExisting (
inputPath: string,
- playlist: VideoPlaylistModel,
+ playlist: MVideoPlaylistThumbnail,
automaticallyGenerated: boolean,
keepOriginal = false,
size?: ImageSize
return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, automaticallyGenerated, existingThumbnail })
}
-function createPlaylistMiniatureFromUrl (fileUrl: string, playlist: VideoPlaylistModel, size?: ImageSize) {
+function createPlaylistMiniatureFromUrl (fileUrl: string, playlist: MVideoPlaylistThumbnail, size?: ImageSize) {
const { filename, basePath, height, width, existingThumbnail } = buildMetadataFromPlaylist(playlist, size)
const type = ThumbnailType.MINIATURE
return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, existingThumbnail, fileUrl })
}
-function createVideoMiniatureFromUrl (fileUrl: string, video: VideoModel, type: ThumbnailType, size?: ImageSize) {
+function createVideoMiniatureFromUrl (fileUrl: string, video: MVideoThumbnail, type: ThumbnailType, size?: ImageSize) {
const { filename, basePath, height, width, existingThumbnail } = buildMetadataFromVideo(video, type, size)
const thumbnailCreator = () => downloadImage(fileUrl, basePath, filename, { width, height })
function createVideoMiniatureFromExisting (
inputPath: string,
- video: VideoModel,
+ video: MVideoThumbnail,
type: ThumbnailType,
automaticallyGenerated: boolean,
size?: ImageSize
return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, automaticallyGenerated, existingThumbnail })
}
-function generateVideoMiniature (video: VideoModel, videoFile: VideoFileModel, type: ThumbnailType) {
+function generateVideoMiniature (video: MVideoThumbnail, videoFile: MVideoFile, type: ThumbnailType) {
const input = video.getVideoFilePath(videoFile)
const { filename, basePath, height, width, existingThumbnail, outputPath } = buildMetadataFromVideo(video, type)
return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, automaticallyGenerated: true, existingThumbnail })
}
-function createPlaceholderThumbnail (fileUrl: string, video: VideoModel, type: ThumbnailType, size: ImageSize) {
+function createPlaceholderThumbnail (fileUrl: string, video: MVideoThumbnail, type: ThumbnailType, size: ImageSize) {
const { filename, height, width, existingThumbnail } = buildMetadataFromVideo(video, type, size)
const thumbnail = existingThumbnail ? existingThumbnail : new ThumbnailModel()
createPlaylistMiniatureFromExisting
}
-function buildMetadataFromPlaylist (playlist: VideoPlaylistModel, size: ImageSize) {
+function buildMetadataFromPlaylist (playlist: MVideoPlaylistThumbnail, size: ImageSize) {
const filename = playlist.generateThumbnailName()
const basePath = CONFIG.STORAGE.THUMBNAILS_DIR
}
}
-function buildMetadataFromVideo (video: VideoModel, type: ThumbnailType, size?: ImageSize) {
+function buildMetadataFromVideo (video: MVideoThumbnail, type: ThumbnailType, size?: ImageSize) {
const existingThumbnail = Array.isArray(video.Thumbnails)
? video.Thumbnails.find(t => t.type === type)
: undefined
type: ThumbnailType,
automaticallyGenerated?: boolean,
fileUrl?: string,
- existingThumbnail?: ThumbnailModel
+ existingThumbnail?: MThumbnail
}) {
const { thumbnailCreator, filename, width, height, type, existingThumbnail, automaticallyGenerated = null, fileUrl = null } = parameters
import { UserModel } from '../models/account/user'
import { buildActorInstance, getAccountActivityPubUrl, setAsyncActorKeys } from './activitypub'
import { createVideoChannel } from './video-channel'
-import { VideoChannelModel } from '../models/video/video-channel'
import { ActorModel } from '../models/activitypub/actor'
import { UserNotificationSettingModel } from '../models/account/user-notification-setting'
import { UserNotificationSetting, UserNotificationSettingValue } from '../../shared/models/users'
import { Transaction } from 'sequelize/types'
import { Redis } from './redis'
import { Emailer } from './emailer'
+import { MAccountActor, MActor, MChannelActor } from '../typings/models'
+import { MUser, MUserId, MUserNotifSettingAccount } from '../typings/models/user'
type ChannelNames = { name: string, displayName: string }
+
async function createUserAccountAndChannelAndPlaylist (parameters: {
userToCreate: UserModel,
userDisplayName?: string,
channelNames?: ChannelNames,
validateUser?: boolean
-}) {
+}): Promise<{ user: MUserNotifSettingAccount, account: MAccountActor, videoChannel: MChannelActor }> {
const { userToCreate, userDisplayName, channelNames, validateUser = true } = parameters
const { user, account, videoChannel } = await sequelizeTypescript.transaction(async t => {
validate: validateUser
}
- const userCreated = await userToCreate.save(userOptions)
+ const userCreated: MUserNotifSettingAccount = await userToCreate.save(userOptions)
userCreated.NotificationSetting = await createDefaultUserNotificationSettings(userCreated, t)
const accountCreated = await createLocalAccountWithoutKeys({
return { user: userCreated, account: accountCreated, videoChannel, videoPlaylist }
})
- const [ accountKeys, channelKeys ] = await Promise.all([
+ const [ accountActorWithKeys, channelActorWithKeys ] = await Promise.all([
setAsyncActorKeys(account.Actor),
setAsyncActorKeys(videoChannel.Actor)
])
- account.Actor = accountKeys
- videoChannel.Actor = channelKeys
+ account.Actor = accountActorWithKeys
+ videoChannel.Actor = channelActorWithKeys
- return { user, account, videoChannel } as { user: UserModel, account: AccountModel, videoChannel: VideoChannelModel }
+ return { user, account, videoChannel }
}
async function createLocalAccountWithoutKeys (parameters: {
const url = getAccountActivityPubUrl(name)
const actorInstance = buildActorInstance(type, url, name)
- const actorInstanceCreated = await actorInstance.save({ transaction: t })
+ const actorInstanceCreated: MActor = await actorInstance.save({ transaction: t })
const accountInstance = new AccountModel({
name: displayName || name,
actorId: actorInstanceCreated.id
})
- const accountInstanceCreated = await accountInstance.save({ transaction: t })
+ const accountInstanceCreated: MAccountActor = await accountInstance.save({ transaction: t })
accountInstanceCreated.Actor = actorInstanceCreated
return accountInstanceCreated
return accountCreated
}
-async function sendVerifyUserEmail (user: UserModel, isPendingEmail = false) {
+async function sendVerifyUserEmail (user: MUser, isPendingEmail = false) {
const verificationString = await Redis.Instance.setVerifyEmailVerificationString(user.id)
let url = WEBSERVER.URL + '/verify-account/email?userId=' + user.id + '&verificationString=' + verificationString
// ---------------------------------------------------------------------------
-function createDefaultUserNotificationSettings (user: UserModel, t: Transaction | undefined) {
+function createDefaultUserNotificationSettings (user: MUserId, t: Transaction | undefined) {
const values: UserNotificationSetting & { userId: number } = {
userId: user.id,
newVideoFromSubscription: UserNotificationSettingValue.WEB,
return UserNotificationSettingModel.create(values, { transaction: t })
}
-async function buildChannelAttributes (user: UserModel, channelNames?: ChannelNames) {
+async function buildChannelAttributes (user: MUser, channelNames?: ChannelNames) {
if (channelNames) return channelNames
let channelName = user.username + '_channel'
import { CONFIG } from '../initializers/config'
import { UserRight, VideoBlacklistType } from '../../shared/models'
import { VideoBlacklistModel } from '../models/video/video-blacklist'
-import { UserModel } from '../models/account/user'
-import { VideoModel } from '../models/video/video'
import { logger } from '../helpers/logger'
import { UserAdminFlag } from '../../shared/models/users/user-flag.model'
import { Hooks } from './plugins/hooks'
import { Notifier } from './notifier'
+import { MUser, MVideoBlacklist, MVideoWithBlacklistLight } from '@server/typings/models'
async function autoBlacklistVideoIfNeeded (parameters: {
- video: VideoModel,
- user?: UserModel,
+ video: MVideoWithBlacklistLight,
+ user?: MUser,
isRemote: boolean,
isNew: boolean,
notify?: boolean,
reason: 'Auto-blacklisted. Moderator review required.',
type: VideoBlacklistType.AUTO_BEFORE_PUBLISHED
}
- const [ videoBlacklist ] = await VideoBlacklistModel.findOrCreate({
+ const [ videoBlacklist ] = await VideoBlacklistModel.findOrCreate<MVideoBlacklist>({
where: {
videoId: video.id
},
}
async function autoBlacklistNeeded (parameters: {
- video: VideoModel,
+ video: MVideoWithBlacklistLight,
isRemote: boolean,
isNew: boolean,
- user?: UserModel
+ user?: MUser
}) {
const { user, video, isRemote, isNew } = parameters
import * as Sequelize from 'sequelize'
import * as uuidv4 from 'uuid/v4'
import { VideoChannelCreate } from '../../shared/models'
-import { AccountModel } from '../models/account/account'
import { VideoChannelModel } from '../models/video/video-channel'
import { buildActorInstance, federateVideoIfNeeded, getVideoChannelActivityPubUrl } from './activitypub'
import { VideoModel } from '../models/video/video'
+import { MAccountId, MChannelActor, MChannelId } from '../typings/models'
-async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account: AccountModel, t: Sequelize.Transaction) {
+type CustomVideoChannelModelAccount <T extends MAccountId> = MChannelActor &
+ { Account?: T }
+
+async function createVideoChannel <T extends MAccountId> (
+ videoChannelInfo: VideoChannelCreate,
+ account: T,
+ t: Sequelize.Transaction
+): Promise<CustomVideoChannelModelAccount<T>> {
const uuid = uuidv4()
const url = getVideoChannelActivityPubUrl(videoChannelInfo.name)
const actorInstance = buildActorInstance('Group', url, videoChannelInfo.name, uuid)
actorId: actorInstanceCreated.id
}
- const videoChannel = VideoChannelModel.build(videoChannelData)
+ const videoChannel = new VideoChannelModel(videoChannelData)
const options = { transaction: t }
- const videoChannelCreated = await videoChannel.save(options)
+ const videoChannelCreated: CustomVideoChannelModelAccount<T> = await videoChannel.save(options) as MChannelActor
// Do not forget to add Account/Actor information to the created video channel
videoChannelCreated.Account = account
return videoChannelCreated
}
-async function federateAllVideosOfChannel (videoChannel: VideoChannelModel) {
+async function federateAllVideosOfChannel (videoChannel: MChannelId) {
const videoIds = await VideoModel.getAllIdsFromChannel(videoChannel)
for (const videoId of videoIds) {
import * as Sequelize from 'sequelize'
import { ResultList } from '../../shared/models'
import { VideoCommentThreadTree } from '../../shared/models/videos/video-comment.model'
-import { AccountModel } from '../models/account/account'
-import { VideoModel } from '../models/video/video'
import { VideoCommentModel } from '../models/video/video-comment'
import { getVideoCommentActivityPubUrl } from './activitypub'
import { sendCreateVideoComment } from './activitypub/send'
+import { MAccountDefault, MComment, MCommentOwnerVideoReply, MVideoFullLight } from '../typings/models'
async function createVideoComment (obj: {
text: string,
- inReplyToComment: VideoCommentModel | null,
- video: VideoModel
- account: AccountModel
+ inReplyToComment: MComment | null,
+ video: MVideoFullLight,
+ account: MAccountDefault
}, t: Sequelize.Transaction) {
let originCommentId: number | null = null
let inReplyToCommentId: number | null = null
comment.url = getVideoCommentActivityPubUrl(obj.video, comment)
- const savedComment = await comment.save({ transaction: t })
+ const savedComment: MCommentOwnerVideoReply = await comment.save({ transaction: t })
savedComment.InReplyToVideoComment = obj.inReplyToComment
savedComment.Video = obj.video
savedComment.Account = obj.account
import * as Sequelize from 'sequelize'
-import { AccountModel } from '../models/account/account'
import { VideoPlaylistModel } from '../models/video/video-playlist'
import { VideoPlaylistPrivacy } from '../../shared/models/videos/playlist/video-playlist-privacy.model'
import { getVideoPlaylistActivityPubUrl } from './activitypub'
import { VideoPlaylistType } from '../../shared/models/videos/playlist/video-playlist-type.model'
+import { MAccount } from '../typings/models'
+import { MVideoPlaylistOwner } from '../typings/models/video/video-playlist'
-async function createWatchLaterPlaylist (account: AccountModel, t: Sequelize.Transaction) {
- const videoPlaylist = new VideoPlaylistModel({
+async function createWatchLaterPlaylist (account: MAccount, t: Sequelize.Transaction) {
+ const videoPlaylist: MVideoPlaylistOwner = new VideoPlaylistModel({
name: 'Watch later',
privacy: VideoPlaylistPrivacy.PRIVATE,
type: VideoPlaylistType.WATCH_LATER,
import { logger } from '../helpers/logger'
import { VideoResolution } from '../../shared/models/videos'
import { VideoFileModel } from '../models/video/video-file'
-import { VideoModel } from '../models/video/video'
import { updateMasterHLSPlaylist, updateSha256Segments } from './hls'
import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist'
import { VideoStreamingPlaylistType } from '../../shared/models/videos/video-streaming-playlist.type'
import { CONFIG } from '../initializers/config'
+import { MVideoFile, MVideoWithFile, MVideoWithFileThumbnail } from '@server/typings/models'
/**
* Optimize the original video file and replace it. The resolution is not changed.
*/
-async function optimizeVideofile (video: VideoModel, inputVideoFileArg?: VideoFileModel) {
+async function optimizeVideofile (video: MVideoWithFile, inputVideoFileArg?: MVideoFile) {
const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR
const transcodeDirectory = CONFIG.STORAGE.TMP_DIR
const newExtname = '.mp4'
/**
* Transcode the original video file to a lower resolution.
*/
-async function transcodeOriginalVideofile (video: VideoModel, resolution: VideoResolution, isPortrait: boolean) {
+async function transcodeOriginalVideofile (video: MVideoWithFile, resolution: VideoResolution, isPortrait: boolean) {
const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR
const transcodeDirectory = CONFIG.STORAGE.TMP_DIR
const extname = '.mp4'
return onVideoFileTranscoding(video, newVideoFile, videoTranscodedPath, videoOutputPath)
}
-async function mergeAudioVideofile (video: VideoModel, resolution: VideoResolution) {
+async function mergeAudioVideofile (video: MVideoWithFileThumbnail, resolution: VideoResolution) {
const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR
const transcodeDirectory = CONFIG.STORAGE.TMP_DIR
const newExtname = '.mp4'
return onVideoFileTranscoding(video, inputVideoFile, videoTranscodedPath, videoOutputPath)
}
-async function generateHlsPlaylist (video: VideoModel, resolution: VideoResolution, isPortraitMode: boolean) {
+async function generateHlsPlaylist (video: MVideoWithFile, resolution: VideoResolution, isPortraitMode: boolean) {
const baseHlsDirectory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid)
await ensureDir(join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid))
// ---------------------------------------------------------------------------
-async function onVideoFileTranscoding (video: VideoModel, videoFile: VideoFileModel, transcodingPath: string, outputPath: string) {
+async function onVideoFileTranscoding (video: MVideoWithFile, videoFile: MVideoFile, transcodingPath: string, outputPath: string) {
const stats = await stat(transcodingPath)
const fps = await getVideoFileFPS(transcodingPath)
await move(transcodingPath, outputPath)
- videoFile.set('size', stats.size)
- videoFile.set('fps', fps)
+ videoFile.size = stats.size
+ videoFile.fps = fps
await video.createTorrentAndSetInfoHash(videoFile)
import { ActorModel } from '../../models/activitypub/actor'
import { loadActorUrlOrGetFromWebfinger } from '../../helpers/webfinger'
import { isValidActorHandle } from '../../helpers/custom-validators/activitypub/actor'
+import { MActorFollowActorsDefault } from '@server/typings/models'
const followValidator = [
body('hosts').custom(isEachUniqueHostValid).withMessage('Should have an array of unique hosts'),
if (areValidationErrors(req, res)) return
- let follow: ActorFollowModel
+ let follow: MActorFollowActorsDefault
try {
const actorUrl = await loadActorUrlOrGetFromWebfinger(req.params.nameWithHost)
const actor = await ActorModel.loadByUrl(actorUrl)
if (areValidationErrors(req, res)) return
if (!await doesVideoExist(req.params.videoId, res)) return
- const video = res.locals.video
+ const video = res.locals.videoAll
const videoFile = video.VideoFiles.find(f => {
return f.resolution === req.params.resolution && (!req.params.fps || f.fps === req.params.fps)
})
if (areValidationErrors(req, res)) return
if (!await doesVideoExist(req.params.videoId, res)) return
- const video = res.locals.video
+ const video = res.locals.videoAll
const videoStreamingPlaylist = video.VideoStreamingPlaylists.find(p => p === req.params.streamingPlaylistType)
if (!videoStreamingPlaylist) return res.status(404).json({ error: 'Video playlist not found.' })
import * as express from 'express'
import { body, param } from 'express-validator'
import { omit } from 'lodash'
-import { isIdOrUUIDValid, toBooleanOrNull, toIntOrNull } from '../../helpers/custom-validators/misc'
+import { isIdOrUUIDValid, toBooleanOrNull } from '../../helpers/custom-validators/misc'
import {
isUserAdminFlagsValid,
isUserAutoPlayVideoValid,
import { isThemeRegistered } from '../../lib/plugins/theme-utils'
import { doesVideoExist } from '../../helpers/middlewares'
import { UserRole } from '../../../shared/models/users'
+import { MUserDefault } from '@server/typings/models'
const usersAddValidator = [
body('username').custom(isUserUsernameValid).withMessage('Should have a valid username (lowercase alphanumeric characters)'),
return true
}
-async function checkUserExist (finder: () => Bluebird<UserModel>, res: express.Response, abortResponse = true) {
+async function checkUserExist (finder: () => Bluebird<MUserDefault>, res: express.Response, abortResponse = true) {
const user = await finder()
if (!user) {
if (areValidationErrors(req, res)) return
if (!await doesVideoExist(req.params.videoId, res)) return
- if (!await doesVideoAbuseExist(req.params.id, res.locals.video.id, res)) return
+ if (!await doesVideoAbuseExist(req.params.id, res.locals.videoAll.id, res)) return
return next()
}
if (areValidationErrors(req, res)) return
if (!await doesVideoExist(req.params.videoId, res)) return
- if (!await doesVideoAbuseExist(req.params.id, res.locals.video.id, res)) return
+ if (!await doesVideoAbuseExist(req.params.id, res.locals.videoAll.id, res)) return
return next()
}
if (areValidationErrors(req, res)) return
if (!await doesVideoExist(req.params.videoId, res)) return
- if (!await doesVideoBlacklistExist(res.locals.video.id, res)) return
+ if (!await doesVideoBlacklistExist(res.locals.videoAll.id, res)) return
return next()
}
if (areValidationErrors(req, res)) return
if (!await doesVideoExist(req.params.videoId, res)) return
- const video = res.locals.video
+ const video = res.locals.videoAll
if (req.body.unfederate === true && video.remote === true) {
return res
.status(409)
if (areValidationErrors(req, res)) return
if (!await doesVideoExist(req.params.videoId, res)) return
- if (!await doesVideoBlacklistExist(res.locals.video.id, res)) return
+ if (!await doesVideoBlacklistExist(res.locals.videoAll.id, res)) return
return next()
}
// Check if the user who did the request is able to update the video
const user = res.locals.oauth.token.User
- if (!checkUserCanManageVideo(user, res.locals.video, UserRight.UPDATE_ANY_VIDEO, res)) return cleanUpReqFiles(req)
+ if (!checkUserCanManageVideo(user, res.locals.videoAll, UserRight.UPDATE_ANY_VIDEO, res)) return cleanUpReqFiles(req)
return next()
}
if (areValidationErrors(req, res)) return
if (!await doesVideoExist(req.params.videoId, res)) return
- if (!await doesVideoCaptionExist(res.locals.video, req.params.captionLanguage, res)) return
+ if (!await doesVideoCaptionExist(res.locals.videoAll, req.params.captionLanguage, res)) return
// Check if the user who did the request is able to update the video
const user = res.locals.oauth.token.User
- if (!checkUserCanManageVideo(user, res.locals.video, UserRight.UPDATE_ANY_VIDEO, res)) return
+ if (!checkUserCanManageVideo(user, res.locals.videoAll, UserRight.UPDATE_ANY_VIDEO, res)) return
return next()
}
isVideoChannelSupportValid
} from '../../../helpers/custom-validators/video-channels'
import { logger } from '../../../helpers/logger'
-import { UserModel } from '../../../models/account/user'
import { VideoChannelModel } from '../../../models/video/video-channel'
import { areValidationErrors } from '../utils'
import { isActorPreferredUsernameValid } from '../../../helpers/custom-validators/activitypub/actor'
import { ActorModel } from '../../../models/activitypub/actor'
import { isBooleanValid } from '../../../helpers/custom-validators/misc'
import { doesLocalVideoChannelNameExist, doesVideoChannelNameWithHostExist } from '../../../helpers/middlewares'
+import { MChannelActorAccountDefault } from '../../../typings/models/video'
+import { MUser } from '@server/typings/models'
const videoChannelsAddValidator = [
body('name').custom(isActorPreferredUsernameValid).withMessage('Should have a valid channel name'),
// ---------------------------------------------------------------------------
-function checkUserCanDeleteVideoChannel (user: UserModel, videoChannel: VideoChannelModel, res: express.Response) {
+function checkUserCanDeleteVideoChannel (user: MUser, videoChannel: MChannelActorAccountDefault, res: express.Response) {
if (videoChannel.Actor.isOwned() === false) {
res.status(403)
.json({ error: 'Cannot remove video channel of another server.' })
import { isIdOrUUIDValid, isIdValid } from '../../../helpers/custom-validators/misc'
import { isValidVideoCommentText } from '../../../helpers/custom-validators/video-comments'
import { logger } from '../../../helpers/logger'
-import { UserModel } from '../../../models/account/user'
-import { VideoModel } from '../../../models/video/video'
import { VideoCommentModel } from '../../../models/video/video-comment'
import { areValidationErrors } from '../utils'
import { Hooks } from '../../../lib/plugins/hooks'
-import { isLocalVideoThreadAccepted, isLocalVideoCommentReplyAccepted, AcceptResult } from '../../../lib/moderation'
+import { AcceptResult, isLocalVideoCommentReplyAccepted, isLocalVideoThreadAccepted } from '../../../lib/moderation'
import { doesVideoExist } from '../../../helpers/middlewares'
+import { MCommentOwner, MVideo, MVideoFullLight, MVideoId } from '../../../typings/models/video'
+import { MUser } from '@server/typings/models'
const listVideoCommentThreadsValidator = [
param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'),
if (areValidationErrors(req, res)) return
if (!await doesVideoExist(req.params.videoId, res, 'only-video')) return
- if (!await doesVideoCommentThreadExist(req.params.threadId, res.locals.video, res)) return
+ if (!await doesVideoCommentThreadExist(req.params.threadId, res.locals.onlyVideo, res)) return
return next()
}
if (areValidationErrors(req, res)) return
if (!await doesVideoExist(req.params.videoId, res)) return
- if (!isVideoCommentsEnabled(res.locals.video, res)) return
- if (!await isVideoCommentAccepted(req, res, false)) return
+ if (!isVideoCommentsEnabled(res.locals.videoAll, res)) return
+ if (!await isVideoCommentAccepted(req, res, res.locals.videoAll,false)) return
return next()
}
if (areValidationErrors(req, res)) return
if (!await doesVideoExist(req.params.videoId, res)) return
- if (!isVideoCommentsEnabled(res.locals.video, res)) return
- if (!await doesVideoCommentExist(req.params.commentId, res.locals.video, res)) return
- if (!await isVideoCommentAccepted(req, res, true)) return
+ if (!isVideoCommentsEnabled(res.locals.videoAll, res)) return
+ if (!await doesVideoCommentExist(req.params.commentId, res.locals.videoAll, res)) return
+ if (!await isVideoCommentAccepted(req, res, res.locals.videoAll, true)) return
return next()
}
if (areValidationErrors(req, res)) return
if (!await doesVideoExist(req.params.videoId, res, 'id')) return
- if (!await doesVideoCommentExist(req.params.commentId, res.locals.video, res)) return
+ if (!await doesVideoCommentExist(req.params.commentId, res.locals.videoId, res)) return
return next()
}
if (areValidationErrors(req, res)) return
if (!await doesVideoExist(req.params.videoId, res)) return
- if (!await doesVideoCommentExist(req.params.commentId, res.locals.video, res)) return
+ if (!await doesVideoCommentExist(req.params.commentId, res.locals.videoAll, res)) return
// Check if the user who did the request is able to delete the video
- if (!checkUserCanDeleteVideoComment(res.locals.oauth.token.User, res.locals.videoComment, res)) return
+ if (!checkUserCanDeleteVideoComment(res.locals.oauth.token.User, res.locals.videoCommentFull, res)) return
return next()
}
// ---------------------------------------------------------------------------
-async function doesVideoCommentThreadExist (id: number, video: VideoModel, res: express.Response) {
+async function doesVideoCommentThreadExist (id: number, video: MVideoId, res: express.Response) {
const videoComment = await VideoCommentModel.loadById(id)
if (!videoComment) {
return true
}
-async function doesVideoCommentExist (id: number, video: VideoModel, res: express.Response) {
+async function doesVideoCommentExist (id: number, video: MVideoId, res: express.Response) {
const videoComment = await VideoCommentModel.loadByIdAndPopulateVideoAndAccountAndReply(id)
if (!videoComment) {
return false
}
- res.locals.videoComment = videoComment
+ res.locals.videoCommentFull = videoComment
return true
}
-function isVideoCommentsEnabled (video: VideoModel, res: express.Response) {
+function isVideoCommentsEnabled (video: MVideo, res: express.Response) {
if (video.commentsEnabled !== true) {
res.status(409)
.json({ error: 'Video comments are disabled for this video.' })
return true
}
-function checkUserCanDeleteVideoComment (user: UserModel, videoComment: VideoCommentModel, res: express.Response) {
+function checkUserCanDeleteVideoComment (user: MUser, videoComment: MCommentOwner, res: express.Response) {
const account = videoComment.Account
if (user.hasRight(UserRight.REMOVE_ANY_VIDEO_COMMENT) === false && account.userId !== user.id) {
res.status(403)
return true
}
-async function isVideoCommentAccepted (req: express.Request, res: express.Response, isReply: boolean) {
+async function isVideoCommentAccepted (req: express.Request, res: express.Response, video: MVideoFullLight, isReply: boolean) {
const acceptParameters = {
- video: res.locals.video,
+ video,
commentBody: req.body,
user: res.locals.oauth.token.User
}
let acceptedResult: AcceptResult
if (isReply) {
- const acceptReplyParameters = Object.assign(acceptParameters, { parentComment: res.locals.videoComment })
+ const acceptReplyParameters = Object.assign(acceptParameters, { parentComment: res.locals.videoCommentFull })
acceptedResult = await Hooks.wrapFun(
isLocalVideoCommentReplyAccepted,
import { body, param, query, ValidationChain } from 'express-validator'
import { UserRight, VideoPlaylistCreate, VideoPlaylistUpdate } from '../../../../shared'
import { logger } from '../../../helpers/logger'
-import { UserModel } from '../../../models/account/user'
import { areValidationErrors } from '../utils'
import { isVideoImage } from '../../../helpers/custom-validators/videos'
import { CONSTRAINTS_FIELDS } from '../../../initializers/constants'
isVideoPlaylistTimestampValid,
isVideoPlaylistTypeValid
} from '../../../helpers/custom-validators/video-playlists'
-import { VideoPlaylistModel } from '../../../models/video/video-playlist'
import { cleanUpReqFiles } from '../../../helpers/express-utils'
import { VideoPlaylistElementModel } from '../../../models/video/video-playlist-element'
import { authenticatePromiseIfNeeded } from '../../oauth'
import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model'
import { VideoPlaylistType } from '../../../../shared/models/videos/playlist/video-playlist-type.model'
-import { doesVideoChannelIdExist, doesVideoExist, doesVideoPlaylistExist } from '../../../helpers/middlewares'
+import { doesVideoChannelIdExist, doesVideoExist, doesVideoPlaylistExist, VideoPlaylistFetchType } from '../../../helpers/middlewares'
+import { MVideoPlaylist } from '../../../typings/models/video/video-playlist'
+import { MUserAccountId } from '@server/typings/models'
const videoPlaylistsAddValidator = getCommonPlaylistEditAttributes().concat([
body('displayName')
if (!await doesVideoPlaylistExist(req.params.playlistId, res, 'all')) return cleanUpReqFiles(req)
- const videoPlaylist = res.locals.videoPlaylist
+ const videoPlaylist = getPlaylist(res)
- if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, res.locals.videoPlaylist, UserRight.REMOVE_ANY_VIDEO_PLAYLIST, res)) {
+ if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, videoPlaylist, UserRight.REMOVE_ANY_VIDEO_PLAYLIST, res)) {
return cleanUpReqFiles(req)
}
if (!await doesVideoPlaylistExist(req.params.playlistId, res)) return
- const videoPlaylist = res.locals.videoPlaylist
+ const videoPlaylist = getPlaylist(res)
if (videoPlaylist.type === VideoPlaylistType.WATCH_LATER) {
return res.status(400)
.json({ error: 'Cannot delete a watch later playlist.' })
}
- if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, res.locals.videoPlaylist, UserRight.REMOVE_ANY_VIDEO_PLAYLIST, res)) {
+ if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, videoPlaylist, UserRight.REMOVE_ANY_VIDEO_PLAYLIST, res)) {
return
}
}
]
-const videoPlaylistsGetValidator = [
- param('playlistId')
- .custom(isIdOrUUIDValid).withMessage('Should have a valid playlist id/uuid'),
+const videoPlaylistsGetValidator = (fetchType: VideoPlaylistFetchType) => {
+ return [
+ param('playlistId')
+ .custom(isIdOrUUIDValid).withMessage('Should have a valid playlist id/uuid'),
- async (req: express.Request, res: express.Response, next: express.NextFunction) => {
- logger.debug('Checking videoPlaylistsGetValidator parameters', { parameters: req.params })
+ async (req: express.Request, res: express.Response, next: express.NextFunction) => {
+ logger.debug('Checking videoPlaylistsGetValidator parameters', { parameters: req.params })
- if (areValidationErrors(req, res)) return
+ if (areValidationErrors(req, res)) return
- if (!await doesVideoPlaylistExist(req.params.playlistId, res)) return
+ if (!await doesVideoPlaylistExist(req.params.playlistId, res, fetchType)) return
- const videoPlaylist = res.locals.videoPlaylist
+ const videoPlaylist = res.locals.videoPlaylistFull || res.locals.videoPlaylistSummary
- // Video is unlisted, check we used the uuid to fetch it
- if (videoPlaylist.privacy === VideoPlaylistPrivacy.UNLISTED) {
- if (isUUIDValid(req.params.playlistId)) return next()
+ // Video is unlisted, check we used the uuid to fetch it
+ if (videoPlaylist.privacy === VideoPlaylistPrivacy.UNLISTED) {
+ if (isUUIDValid(req.params.playlistId)) return next()
- return res.status(404).end()
- }
+ return res.status(404).end()
+ }
- if (videoPlaylist.privacy === VideoPlaylistPrivacy.PRIVATE) {
- await authenticatePromiseIfNeeded(req, res)
+ if (videoPlaylist.privacy === VideoPlaylistPrivacy.PRIVATE) {
+ await authenticatePromiseIfNeeded(req, res)
- const user = res.locals.oauth ? res.locals.oauth.token.User : null
+ const user = res.locals.oauth ? res.locals.oauth.token.User : null
- if (
- !user ||
- (videoPlaylist.OwnerAccount.id !== user.Account.id && !user.hasRight(UserRight.UPDATE_ANY_VIDEO_PLAYLIST))
- ) {
- return res.status(403)
- .json({ error: 'Cannot get this private video playlist.' })
+ if (
+ !user ||
+ (videoPlaylist.OwnerAccount.id !== user.Account.id && !user.hasRight(UserRight.UPDATE_ANY_VIDEO_PLAYLIST))
+ ) {
+ return res.status(403)
+ .json({ error: 'Cannot get this private video playlist.' })
+ }
+
+ return next()
}
return next()
}
-
- return next()
- }
-]
+ ]
+}
const videoPlaylistsAddVideoValidator = [
param('playlistId')
if (!await doesVideoPlaylistExist(req.params.playlistId, res, 'all')) return
if (!await doesVideoExist(req.body.videoId, res, 'only-video')) return
- const videoPlaylist = res.locals.videoPlaylist
- const video = res.locals.video
+ const videoPlaylist = getPlaylist(res)
+ const video = res.locals.onlyVideo
const videoPlaylistElement = await VideoPlaylistElementModel.loadByPlaylistAndVideo(videoPlaylist.id, video.id)
if (videoPlaylistElement) {
return
}
- if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, res.locals.videoPlaylist, UserRight.UPDATE_ANY_VIDEO_PLAYLIST, res)) {
+ if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, videoPlaylist, UserRight.UPDATE_ANY_VIDEO_PLAYLIST, res)) {
return
}
if (!await doesVideoPlaylistExist(req.params.playlistId, res, 'all')) return
- const videoPlaylist = res.locals.videoPlaylist
+ const videoPlaylist = getPlaylist(res)
const videoPlaylistElement = await VideoPlaylistElementModel.loadById(req.params.playlistElementId)
if (!videoPlaylistElement) {
if (!await doesVideoPlaylistExist(req.params.playlistId, res, 'all')) return
- const videoPlaylist = res.locals.videoPlaylist
+ const videoPlaylist = getPlaylist(res)
if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, videoPlaylist, UserRight.UPDATE_ANY_VIDEO_PLAYLIST, res)) return
const nextPosition = await VideoPlaylistElementModel.getNextPositionOf(videoPlaylist.id)
] as (ValidationChain | express.Handler)[]
}
-function checkUserCanManageVideoPlaylist (user: UserModel, videoPlaylist: VideoPlaylistModel, right: UserRight, res: express.Response) {
+function checkUserCanManageVideoPlaylist (user: MUserAccountId, videoPlaylist: MVideoPlaylist, right: UserRight, res: express.Response) {
if (videoPlaylist.isOwned() === false) {
res.status(403)
.json({ error: 'Cannot manage video playlist of another server.' })
return true
}
+
+function getPlaylist (res: express.Response) {
+ return res.locals.videoPlaylistFull || res.locals.videoPlaylistSummary
+}
if (areValidationErrors(req, res)) return
if (!await doesVideoExist(req.params.id, res)) return
- const video = res.locals.video
+ const video = res.locals.videoAll
const share = await VideoShareModel.load(req.params.actorId, video.id)
if (!share) {
import { checkUserCanTerminateOwnershipChange, doesChangeVideoOwnershipExist } from '../../../helpers/custom-validators/video-ownership'
import { VideoChangeOwnershipAccept } from '../../../../shared/models/videos/video-change-ownership-accept.model'
import { AccountModel } from '../../../models/account/account'
-import { VideoFetchType } from '../../../helpers/video'
import { isNSFWQueryValid, isNumberArray, isStringArray } from '../../../helpers/custom-validators/search'
import { getServerActor } from '../../../helpers/utils'
import { CONFIG } from '../../../initializers/config'
import { isLocalVideoAccepted } from '../../../lib/moderation'
import { Hooks } from '../../../lib/plugins/hooks'
import { checkUserCanManageVideo, doesVideoChannelOfAccountExist, doesVideoExist } from '../../../helpers/middlewares'
+import { MVideoFullLight } from '@server/typings/models'
+import { getVideo } from '../../../helpers/video'
const videosAddValidator = getCommonVideoEditAttributes().concat([
body('videofile')
// Check if the user who did the request is able to update the video
const user = res.locals.oauth.token.User
- if (!checkUserCanManageVideo(user, res.locals.video, UserRight.UPDATE_ANY_VIDEO, res)) return cleanUpReqFiles(req)
+ if (!checkUserCanManageVideo(user, res.locals.videoAll, UserRight.UPDATE_ANY_VIDEO, res)) return cleanUpReqFiles(req)
if (req.body.channelId && !await doesVideoChannelOfAccountExist(req.body.channelId, user, res)) return cleanUpReqFiles(req)
])
async function checkVideoFollowConstraints (req: express.Request, res: express.Response, next: express.NextFunction) {
- const video = res.locals.video
+ const video = getVideo(res)
// Anybody can watch local videos
if (video.isOwned() === true) return next()
})
}
-const videosCustomGetValidator = (fetchType: VideoFetchType) => {
+const videosCustomGetValidator = (fetchType: 'all' | 'only-video' | 'only-video-with-rights') => {
return [
param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
if (areValidationErrors(req, res)) return
if (!await doesVideoExist(req.params.id, res, fetchType)) return
- const video = res.locals.video
+ const video = getVideo(res)
+ const videoAll = video as MVideoFullLight
// Video private or blacklisted
- if (video.privacy === VideoPrivacy.PRIVATE || video.VideoBlacklist) {
+ if (video.privacy === VideoPrivacy.PRIVATE || videoAll.VideoBlacklist) {
await authenticatePromiseIfNeeded(req, res)
const user = res.locals.oauth ? res.locals.oauth.token.User : null
// Only the owner or a user that have blacklist rights can see the video
if (
!user ||
- (video.VideoChannel.Account.userId !== user.id && !user.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST))
+ (videoAll.VideoChannel && videoAll.VideoChannel.Account.userId !== user.id && !user.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST))
) {
return res.status(403)
.json({ error: 'Cannot get this private or blacklisted video.' })
if (!await doesVideoExist(req.params.id, res)) return
// Check if the user who did the request is able to delete the video
- if (!checkUserCanManageVideo(res.locals.oauth.token.User, res.locals.video, UserRight.REMOVE_ANY_VIDEO, res)) return
+ if (!checkUserCanManageVideo(res.locals.oauth.token.User, res.locals.videoAll, UserRight.REMOVE_ANY_VIDEO, res)) return
return next()
}
if (!await doesVideoExist(req.params.videoId, res)) return
// Check if the user who did the request is able to change the ownership of the video
- if (!checkUserCanManageVideo(res.locals.oauth.token.User, res.locals.video, UserRight.CHANGE_VIDEO_OWNERSHIP, res)) return
+ if (!checkUserCanManageVideo(res.locals.oauth.token.User, res.locals.videoAll, UserRight.CHANGE_VIDEO_OWNERSHIP, res)) return
const nextOwner = await AccountModel.loadLocalByName(req.body.username)
if (!nextOwner) {
const nameWithHost = getHostWithPort(req.query.resource.substr(5))
const [ name ] = nameWithHost.split('@')
+ // FIXME: we don't need the full actor
const actor = await ActorModel.loadLocalByName(name)
if (!actor) {
return res.status(404)
.end()
}
- res.locals.actor = actor
+ res.locals.actorFull = actor
return next()
}
]
import { getSort } from '../utils'
import { AccountBlock } from '../../../shared/models/blocklist'
import { Op } from 'sequelize'
+import * as Bluebird from 'bluebird'
+import { MAccountBlocklist, MAccountBlocklistAccounts } from '@server/typings/models'
enum ScopeNames {
WITH_ACCOUNTS = 'WITH_ACCOUNTS'
})
}
- static loadByAccountAndTarget (accountId: number, targetAccountId: number) {
+ static loadByAccountAndTarget (accountId: number, targetAccountId: number): Bluebird<MAccountBlocklist> {
const query = {
where: {
accountId,
return AccountBlocklistModel
.scope([ ScopeNames.WITH_ACCOUNTS ])
- .findAndCountAll(query)
+ .findAndCountAll<MAccountBlocklistAccounts>(query)
.then(({ rows, count }) => {
return { total: count, data: rows }
})
import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
import { AccountVideoRate } from '../../../shared'
import { ScopeNames as VideoChannelScopeNames, SummaryOptions, VideoChannelModel } from '../video/video-channel'
+import * as Bluebird from 'bluebird'
+import { MAccountVideoRate, MAccountVideoRateAccountUrl, MAccountVideoRateAccountVideo } from '@server/typings/models/video/video-rate'
/*
Account rates per video.
})
Account: AccountModel
- static load (accountId: number, videoId: number, transaction?: Transaction) {
+ static load (accountId: number, videoId: number, transaction?: Transaction): Bluebird<MAccountVideoRate> {
const options: FindOptions = {
where: {
accountId,
return AccountVideoRateModel.findOne(options)
}
- static loadByAccountAndVideoOrUrl (accountId: number, videoId: number, url: string, transaction?: Transaction) {
+ static loadByAccountAndVideoOrUrl (accountId: number, videoId: number, url: string, t?: Transaction): Bluebird<MAccountVideoRate> {
const options: FindOptions = {
where: {
[ Op.or]: [
]
}
}
- if (transaction) options.transaction = transaction
+ if (t) options.transaction = t
return AccountVideoRateModel.findOne(options)
}
return AccountVideoRateModel.findAndCountAll(query)
}
- static loadLocalAndPopulateVideo (rateType: VideoRateType, accountName: string, videoId: number, transaction?: Transaction) {
+ static loadLocalAndPopulateVideo (
+ rateType: VideoRateType,
+ accountName: string,
+ videoId: number,
+ t?: Transaction
+ ): Bluebird<MAccountVideoRateAccountVideo> {
const options: FindOptions = {
where: {
videoId,
required: true,
include: [
{
- attributes: [ 'id', 'url', 'preferredUsername' ],
+ attributes: [ 'id', 'url', 'followersUrl', 'preferredUsername' ],
model: ActorModel.unscoped(),
required: true,
where: {
}
]
}
- if (transaction) options.transaction = transaction
+ if (t) options.transaction = t
return AccountVideoRateModel.findOne(options)
}
]
}
- return AccountVideoRateModel.findAndCountAll(query)
+ return AccountVideoRateModel.findAndCountAll<MAccountVideoRateAccountUrl>(query)
}
static cleanOldRatesOf (videoId: number, type: VideoRateType, beforeUpdatedAt: Date) {
BeforeDestroy,
BelongsTo,
Column,
- CreatedAt, DataType,
+ CreatedAt,
+ DataType,
Default,
DefaultScope,
ForeignKey,
import { AccountBlocklistModel } from './account-blocklist'
import { ServerBlocklistModel } from '../server/server-blocklist'
import { ActorFollowModel } from '../activitypub/actor-follow'
+import { MAccountActor, MAccountDefault } from '../../typings/models'
+import * as Bluebird from 'bluebird'
export enum ScopeNames {
SUMMARY = 'SUMMARY'
return undefined
}
- static load (id: number, transaction?: Transaction) {
+ static load (id: number, transaction?: Transaction): Bluebird<MAccountDefault> {
return AccountModel.findByPk(id, { transaction })
}
- static loadByNameWithHost (nameWithHost: string) {
+ static loadByNameWithHost (nameWithHost: string): Bluebird<MAccountDefault> {
const [ accountName, host ] = nameWithHost.split('@')
if (!host || host === WEBSERVER.HOST) return AccountModel.loadLocalByName(accountName)
return AccountModel.loadByNameAndHost(accountName, host)
}
- static loadLocalByName (name: string) {
+ static loadLocalByName (name: string): Bluebird<MAccountDefault> {
const query = {
where: {
[ Op.or ]: [
return AccountModel.findOne(query)
}
- static loadByNameAndHost (name: string, host: string) {
+ static loadByNameAndHost (name: string, host: string): Bluebird<MAccountDefault> {
const query = {
include: [
{
return AccountModel.findOne(query)
}
- static loadByUrl (url: string, transaction?: Transaction) {
+ static loadByUrl (url: string, transaction?: Transaction): Bluebird<MAccountDefault> {
const query = {
include: [
{
})
}
- static listLocalsForSitemap (sort: string) {
+ static listLocalsForSitemap (sort: string): Bluebird<MAccountActor[]> {
const query = {
attributes: [ ],
offset: 0,
import { ActorFollowModel } from '../activitypub/actor-follow'
import { AvatarModel } from '../avatar/avatar'
import { ServerModel } from '../server/server'
+import { UserNotificationIncludes, UserNotificationModelForApi } from '@server/typings/models/user'
enum ScopeNames {
WITH_ALL = 'WITH_ALL'
return UserNotificationModel.update({ read: true }, query)
}
- toFormattedJSON (): UserNotification {
+ toFormattedJSON (this: UserNotificationModelForApi): UserNotification {
const video = this.Video
? Object.assign(this.formatVideo(this.Video),{ channel: this.formatActor(this.Video.VideoChannel) })
: undefined
}
}
- private formatVideo (video: VideoModel) {
+ formatVideo (this: UserNotificationModelForApi, video: UserNotificationIncludes.VideoInclude) {
return {
id: video.id,
uuid: video.uuid,
}
}
- private formatActor (accountOrChannel: AccountModel | VideoChannelModel) {
+ formatActor (
+ this: UserNotificationModelForApi,
+ accountOrChannel: UserNotificationIncludes.AccountIncludeActor | UserNotificationIncludes.VideoChannelIncludeActor
+ ) {
const avatar = accountOrChannel.Actor.Avatar
? { path: accountOrChannel.Actor.Avatar.getStaticPath() }
: undefined
import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, IsInt, Model, Table, UpdatedAt } from 'sequelize-typescript'
import { VideoModel } from '../video/video'
import { UserModel } from './user'
-import { Transaction, Op, DestroyOptions } from 'sequelize'
+import { DestroyOptions, Op, Transaction } from 'sequelize'
+import { MUserAccountId, MUserId } from '@server/typings/models'
@Table({
tableName: 'userVideoHistory',
})
User: UserModel
- static listForApi (user: UserModel, start: number, count: number) {
+ static listForApi (user: MUserAccountId, start: number, count: number) {
return VideoModel.listForApi({
start,
count,
})
}
- static removeUserHistoryBefore (user: UserModel, beforeDate: string, t: Transaction) {
+ static removeUserHistoryBefore (user: MUserId, beforeDate: string, t: Transaction) {
const query: DestroyOptions = {
where: {
userId: user.id
import { UserAdminFlag } from '../../../shared/models/users/user-flag.model'
import { isThemeNameValid } from '../../helpers/custom-validators/plugins'
import { getThemeOrDefault } from '../../lib/plugins/theme-utils'
+import * as Bluebird from 'bluebird'
+import { MUserChannel, MUserDefault, MUserId, MUserWithNotificationSetting } from '@server/typings/models'
enum ScopeNames {
WITH_VIDEO_CHANNEL = 'WITH_VIDEO_CHANNEL'
})
}
- static listWithRight (right: UserRight) {
+ static listWithRight (right: UserRight): Bluebird<MUserDefault[]> {
const roles = Object.keys(USER_ROLE_LABELS)
.map(k => parseInt(k, 10) as UserRole)
.filter(role => hasUserRight(role, right))
return UserModel.findAll(query)
}
- static listUserSubscribersOf (actorId: number) {
+ static listUserSubscribersOf (actorId: number): Bluebird<MUserWithNotificationSetting[]> {
const query = {
include: [
{
return UserModel.unscoped().findAll(query)
}
- static listByUsernames (usernames: string[]) {
+ static listByUsernames (usernames: string[]): Bluebird<MUserDefault[]> {
const query = {
where: {
username: usernames
return UserModel.findAll(query)
}
- static loadById (id: number) {
+ static loadById (id: number): Bluebird<MUserDefault> {
return UserModel.findByPk(id)
}
- static loadByUsername (username: string) {
+ static loadByUsername (username: string): Bluebird<MUserDefault> {
const query = {
where: {
username: { [ Op.iLike ]: username }
return UserModel.findOne(query)
}
- static loadByUsernameAndPopulateChannels (username: string) {
+ static loadByUsernameAndPopulateChannels (username: string): Bluebird<MUserChannel> {
const query = {
where: {
username: { [ Op.iLike ]: username }
return UserModel.scope(ScopeNames.WITH_VIDEO_CHANNEL).findOne(query)
}
- static loadByEmail (email: string) {
+ static loadByEmail (email: string): Bluebird<MUserDefault> {
const query = {
where: {
email
return UserModel.findOne(query)
}
- static loadByUsernameOrEmail (username: string, email?: string) {
+ static loadByUsernameOrEmail (username: string, email?: string): Bluebird<MUserDefault> {
if (!email) email = username
const query = {
return UserModel.findOne(query)
}
- static loadByVideoId (videoId: number) {
+ static loadByVideoId (videoId: number): Bluebird<MUserDefault> {
const query = {
include: [
{
return UserModel.findOne(query)
}
- static loadByVideoImportId (videoImportId: number) {
+ static loadByVideoImportId (videoImportId: number): Bluebird<MUserDefault> {
const query = {
include: [
{
return UserModel.findOne(query)
}
- static loadByChannelActorId (videoChannelActorId: number) {
+ static loadByChannelActorId (videoChannelActorId: number): Bluebird<MUserDefault> {
const query = {
include: [
{
return UserModel.findOne(query)
}
- static loadByAccountActorId (accountActorId: number) {
+ static loadByAccountActorId (accountActorId: number): Bluebird<MUserDefault> {
const query = {
include: [
{
return UserModel.findOne(query)
}
- static getOriginalVideoFileTotalFromUser (user: UserModel) {
+ static getOriginalVideoFileTotalFromUser (user: MUserId) {
// Don't use sequelize because we need to use a sub query
const query = UserModel.generateUserQuotaBaseSQL()
}
// Returns cumulative size of all video files uploaded in the last 24 hours.
- static getOriginalVideoFileTotalDailyFromUser (user: UserModel) {
+ static getOriginalVideoFileTotalDailyFromUser (user: MUserId) {
// Don't use sequelize because we need to use a sub query
const query = UserModel.generateUserQuotaBaseSQL('"video"."createdAt" > now() - interval \'24 hours\'')
import { ActorModel, unusedActorAttributesForAPI } from './actor'
import { VideoChannelModel } from '../video/video-channel'
import { AccountModel } from '../account/account'
-import { IncludeOptions, Op, Transaction, QueryTypes } from 'sequelize'
+import { IncludeOptions, Op, QueryTypes, Transaction } from 'sequelize'
+import {
+ MActorFollowActorsDefault,
+ MActorFollowActorsDefaultSubscription,
+ MActorFollowFollowingHost,
+ MActorFollowSubscriptions
+} from '@server/typings/models'
@Table({
tableName: 'actorFollow',
if (numberOfActorFollowsRemoved) logger.info('Removed bad %d actor follows.', numberOfActorFollowsRemoved)
}
- static loadByActorAndTarget (actorId: number, targetActorId: number, t?: Transaction) {
+ static loadByActorAndTarget (actorId: number, targetActorId: number, t?: Transaction): Bluebird<MActorFollowActorsDefault> {
const query = {
where: {
actorId,
return ActorFollowModel.findOne(query)
}
- static loadByActorAndTargetNameAndHostForAPI (actorId: number, targetName: string, targetHost: string, t?: Transaction) {
+ static loadByActorAndTargetNameAndHostForAPI (
+ actorId: number,
+ targetName: string,
+ targetHost: string,
+ t?: Transaction
+ ): Bluebird<MActorFollowActorsDefaultSubscription> {
const actorFollowingPartInclude: IncludeOptions = {
model: ActorModel,
required: true,
})
}
- static listSubscribedIn (actorId: number, targets: { name: string, host?: string }[]) {
+ static listSubscribedIn (actorId: number, targets: { name: string, host?: string }[]): Bluebird<MActorFollowFollowingHost[]> {
const whereTab = targets
.map(t => {
if (t.host) {
]
}
- return ActorFollowModel.findAndCountAll(query)
+ return ActorFollowModel.findAndCountAll<MActorFollowActorsDefault>(query)
.then(({ rows, count }) => {
return {
data: rows,
]
}
- return ActorFollowModel.findAndCountAll(query)
+ return ActorFollowModel.findAndCountAll<MActorFollowActorsDefault>(query)
.then(({ rows, count }) => {
return {
data: rows,
]
}
- return ActorFollowModel.findAndCountAll(query)
+ return ActorFollowModel.findAndCountAll<MActorFollowSubscriptions>(query)
.then(({ rows, count }) => {
return {
data: rows.map(r => r.ActorFollowing.VideoChannel),
import { VideoChannelModel } from '../video/video-channel'
import { ActorFollowModel } from './actor-follow'
import { VideoModel } from '../video/video'
+import { MActor, MActorAccountChannelId, MActorFull } from '../../typings/models'
+import * as Bluebird from 'bluebird'
enum ScopeNames {
FULL = 'FULL'
})
VideoChannel: VideoChannelModel
- static load (id: number) {
+ static load (id: number): Bluebird<MActor> {
return ActorModel.unscoped().findByPk(id)
}
- static loadAccountActorByVideoId (videoId: number, transaction: Sequelize.Transaction) {
+ static loadFull (id: number): Bluebird<MActorFull> {
+ return ActorModel.scope(ScopeNames.FULL).findByPk(id)
+ }
+
+ static loadFromAccountByVideoId (videoId: number, transaction: Sequelize.Transaction): Bluebird<MActor> {
const query = {
include: [
{
.then(a => !!a)
}
- static listByFollowersUrls (followersUrls: string[], transaction?: Sequelize.Transaction) {
+ static listByFollowersUrls (followersUrls: string[], transaction?: Sequelize.Transaction): Bluebird<MActorFull[]> {
const query = {
where: {
followersUrl: {
return ActorModel.scope(ScopeNames.FULL).findAll(query)
}
- static loadLocalByName (preferredUsername: string, transaction?: Sequelize.Transaction) {
+ static loadLocalByName (preferredUsername: string, transaction?: Sequelize.Transaction): Bluebird<MActorFull> {
const query = {
where: {
preferredUsername,
return ActorModel.scope(ScopeNames.FULL).findOne(query)
}
- static loadByNameAndHost (preferredUsername: string, host: string) {
+ static loadByNameAndHost (preferredUsername: string, host: string): Bluebird<MActorFull> {
const query = {
where: {
preferredUsername
return ActorModel.scope(ScopeNames.FULL).findOne(query)
}
- static loadByUrl (url: string, transaction?: Sequelize.Transaction) {
+ static loadByUrl (url: string, transaction?: Sequelize.Transaction): Bluebird<MActorAccountChannelId> {
const query = {
where: {
url
return ActorModel.unscoped().findOne(query)
}
- static loadByUrlAndPopulateAccountAndChannel (url: string, transaction?: Sequelize.Transaction) {
+ static loadByUrlAndPopulateAccountAndChannel (url: string, transaction?: Sequelize.Transaction): Bluebird<MActorFull> {
const query = {
where: {
url
import { AccountModel } from '../account/account'
import { ActorModel } from '../activitypub/actor'
import { clearCacheByToken } from '../../lib/oauth-model'
+import * as Bluebird from 'bluebird'
+import { MOAuthTokenUser } from '@server/typings/models/oauth/oauth-token'
export type OAuthTokenInfo = {
refreshToken: string
})
}
- static getByTokenAndPopulateUser (bearerToken: string) {
+ static getByTokenAndPopulateUser (bearerToken: string): Bluebird<MOAuthTokenUser> {
const query = {
where: {
accessToken: bearerToken
return OAuthTokenModel.scope(ScopeNames.WITH_USER)
.findOne(query)
.then(token => {
- if (token) token[ 'user' ] = token.User
+ if (!token) return null
- return token
+ return Object.assign(token, { user: token.User })
})
}
- static getByRefreshTokenAndPopulateUser (refreshToken: string) {
+ static getByRefreshTokenAndPopulateUser (refreshToken: string): Bluebird<MOAuthTokenUser> {
const query = {
where: {
refreshToken: refreshToken
return OAuthTokenModel.scope(ScopeNames.WITH_USER)
.findOne(query)
.then(token => {
- if (token) {
- token['user'] = token.User
- return token
- } else {
- return new OAuthTokenModel()
- }
+ if (!token) return new OAuthTokenModel()
+
+ return Object.assign(token, { user: token.User })
})
}
import { col, FindOptions, fn, literal, Op, Transaction } from 'sequelize'
import { VideoStreamingPlaylistModel } from '../video/video-streaming-playlist'
import { CONFIG } from '../../initializers/config'
+import { MVideoRedundancy, MVideoRedundancyVideo } from '@server/typings/models'
export enum ScopeNames {
WITH_VIDEO = 'WITH_VIDEO'
return undefined
}
- static async loadLocalByFileId (videoFileId: number) {
+ static async loadLocalByFileId (videoFileId: number): Promise<MVideoRedundancyVideo> {
const actor = await getServerActor()
const query = {
return VideoRedundancyModel.scope(ScopeNames.WITH_VIDEO).findOne(query)
}
- static async loadLocalByStreamingPlaylistId (videoStreamingPlaylistId: number) {
+ static async loadLocalByStreamingPlaylistId (videoStreamingPlaylistId: number): Promise<MVideoRedundancyVideo> {
const actor = await getServerActor()
const query = {
return VideoRedundancyModel.scope(ScopeNames.WITH_VIDEO).findOne(query)
}
- static loadByUrl (url: string, transaction?: Transaction) {
+ static loadByUrl (url: string, transaction?: Transaction): Bluebird<MVideoRedundancy> {
const query = {
where: {
url
return VideoRedundancyModel.getVideoSample(VideoModel.unscoped().findAll(query))
}
- static async loadOldestLocalThatAlreadyExpired (strategy: VideoRedundancyStrategy, expiresAfterMs: number) {
+ static async loadOldestLocalExpired (strategy: VideoRedundancyStrategy, expiresAfterMs: number): Promise<MVideoRedundancyVideo> {
const expiredDate = new Date()
expiredDate.setMilliseconds(expiredDate.getMilliseconds() - expiresAfterMs)
import { PeerTubePlugin } from '../../../shared/models/plugins/peertube-plugin.model'
import { FindAndCountOptions, json } from 'sequelize'
import { RegisterServerSettingOptions } from '../../../shared/models/plugins/register-server-setting.model'
+import * as Bluebird from 'bluebird'
+import { MPlugin } from '@server/typings/models'
@DefaultScope(() => ({
attributes: {
@UpdatedAt
updatedAt: Date
- static listEnabledPluginsAndThemes () {
+ static listEnabledPluginsAndThemes (): Bluebird<MPlugin[]> {
const query = {
where: {
enabled: true,
return PluginModel.findAll(query)
}
- static loadByNpmName (npmName: string) {
+ static loadByNpmName (npmName: string): Bluebird<MPlugin> {
const name = this.normalizePluginName(npmName)
const type = this.getTypeFromNpmName(npmName)
if (options.pluginType) query.where['type'] = options.pluginType
return PluginModel
- .findAndCountAll(query)
+ .findAndCountAll<MPlugin>(query)
.then(({ rows, count }) => {
return { total: count, data: rows }
})
}
- static listInstalled () {
+ static listInstalled (): Bluebird<MPlugin[]> {
const query = {
where: {
uninstalled: false
import { ServerModel } from './server'
import { ServerBlock } from '../../../shared/models/blocklist'
import { getSort } from '../utils'
+import * as Bluebird from 'bluebird'
+import { MServerBlocklist, MServerBlocklistAccountServer } from '@server/typings/models'
enum ScopeNames {
WITH_ACCOUNT = 'WITH_ACCOUNT',
})
BlockedServer: ServerModel
- static loadByAccountAndHost (accountId: number, host: string) {
+ static loadByAccountAndHost (accountId: number, host: string): Bluebird<MServerBlocklist> {
const query = {
where: {
accountId
return ServerBlocklistModel
.scope([ ScopeNames.WITH_ACCOUNT, ScopeNames.WITH_SERVER ])
- .findAndCountAll(query)
+ .findAndCountAll<MServerBlocklistAccountServer>(query)
.then(({ rows, count }) => {
return { total: count, data: rows }
})
import { isHostValid } from '../../helpers/custom-validators/servers'
import { ActorModel } from '../activitypub/actor'
import { throwIfNotValid } from '../utils'
-import { AccountBlocklistModel } from '../account/account-blocklist'
import { ServerBlocklistModel } from './server-blocklist'
+import * as Bluebird from 'bluebird'
+import { MServer } from '@server/typings/models/server'
@Table({
tableName: 'server',
})
BlockedByAccounts: ServerBlocklistModel[]
- static loadByHost (host: string) {
+ static loadByHost (host: string): Bluebird<MServer> {
const query = {
where: {
host
import { VideoModel } from './video'
import { VideoAbuseState } from '../../../shared'
import { CONSTRAINTS_FIELDS, VIDEO_ABUSE_STATES } from '../../initializers/constants'
+import { MVideoAbuse, MVideoAbuseAccountVideo, MVideoAbuseVideo } from '../../typings/models'
+import * as Bluebird from 'bluebird'
@Table({
tableName: 'videoAbuse',
})
Video: VideoModel
- static loadByIdAndVideoId (id: number, videoId: number) {
+ static loadByIdAndVideoId (id: number, videoId: number): Bluebird<MVideoAbuse> {
const query = {
where: {
id,
})
}
- toFormattedJSON (): VideoAbuse {
+ toFormattedJSON (this: MVideoAbuseAccountVideo): VideoAbuse {
return {
id: this.id,
reason: this.reason,
}
}
- toActivityPubObject (): VideoAbuseObject {
+ toActivityPubObject (this: MVideoAbuseVideo): VideoAbuseObject {
return {
type: 'Flag' as 'Flag',
content: this.reason,
import { AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
import { getSortOnModel, SortType, throwIfNotValid } from '../utils'
-import { ScopeNames as VideoModelScopeNames, VideoModel } from './video'
+import { VideoModel } from './video'
import { ScopeNames as VideoChannelScopeNames, SummaryOptions, VideoChannelModel } from './video-channel'
import { isVideoBlacklistReasonValid, isVideoBlacklistTypeValid } from '../../helpers/custom-validators/video-blacklist'
import { VideoBlacklist, VideoBlacklistType } from '../../../shared/models/videos'
import { CONSTRAINTS_FIELDS } from '../../initializers/constants'
import { FindOptions } from 'sequelize'
import { ThumbnailModel } from './thumbnail'
+import * as Bluebird from 'bluebird'
+import { MVideoBlacklist } from '@server/typings/models'
@Table({
tableName: 'videoBlacklist',
})
}
- static loadByVideoId (id: number) {
+ static loadByVideoId (id: number): Bluebird<MVideoBlacklist> {
const query = {
where: {
videoId: id
import { logger } from '../../helpers/logger'
import { remove } from 'fs-extra'
import { CONFIG } from '../../initializers/config'
+import * as Bluebird from 'bluebird'
+import { MVideoCaptionVideo } from '@server/typings/models'
export enum ScopeNames {
WITH_VIDEO_UUID_AND_REMOTE = 'WITH_VIDEO_UUID_AND_REMOTE'
[ScopeNames.WITH_VIDEO_UUID_AND_REMOTE]: {
include: [
{
- attributes: [ 'uuid', 'remote' ],
+ attributes: [ 'id', 'uuid', 'remote' ],
model: VideoModel.unscoped(),
required: true
}
return undefined
}
- static loadByVideoIdAndLanguage (videoId: string | number, language: string) {
+ static loadByVideoIdAndLanguage (videoId: string | number, language: string): Bluebird<MVideoCaptionVideo> {
const videoInclude = {
model: VideoModel.unscoped(),
attributes: [ 'id', 'remote', 'uuid' ],
.then(([ caption ]) => caption)
}
- static listVideoCaptions (videoId: number) {
+ static listVideoCaptions (videoId: number): Bluebird<MVideoCaptionVideo[]> {
const query = {
order: [ [ 'language', 'ASC' ] ] as OrderItem[],
where: {
import { ScopeNames as VideoScopeNames, VideoModel } from './video'
import { VideoChangeOwnership, VideoChangeOwnershipStatus } from '../../../shared/models/videos'
import { getSort } from '../utils'
+import { MVideoChangeOwnershipFull } from '@server/typings/models/video/video-change-ownership'
+import * as Bluebird from 'bluebird'
enum ScopeNames {
WITH_ACCOUNTS = 'WITH_ACCOUNTS',
return Promise.all([
VideoChangeOwnershipModel.scope(ScopeNames.WITH_ACCOUNTS).count(query),
- VideoChangeOwnershipModel.scope([ ScopeNames.WITH_ACCOUNTS, ScopeNames.WITH_VIDEO ]).findAll(query)
+ VideoChangeOwnershipModel.scope([ ScopeNames.WITH_ACCOUNTS, ScopeNames.WITH_VIDEO ]).findAll<MVideoChangeOwnershipFull>(query)
]).then(([ count, rows ]) => ({ total: count, data: rows }))
}
- static load (id: number) {
+ static load (id: number): Bluebird<MVideoChangeOwnershipFull> {
return VideoChangeOwnershipModel.scope([ ScopeNames.WITH_ACCOUNTS, ScopeNames.WITH_VIDEO ])
.findByPk(id)
}
import { FindOptions, ModelIndexesOptions, Op } from 'sequelize'
import { AvatarModel } from '../avatar/avatar'
import { VideoPlaylistModel } from './video-playlist'
+import * as Bluebird from 'bluebird'
+import {
+ MChannelAccountDefault,
+ MChannelActor,
+ MChannelActorAccountDefault,
+ MChannelActorAccountDefaultVideos
+} from '../../typings/models/video'
// FIXME: Define indexes here because there is an issue with TS and Sequelize.literal when called directly in the annotation
const indexes: ModelIndexesOptions[] = [
]
export enum ScopeNames {
- AVAILABLE_FOR_LIST = 'AVAILABLE_FOR_LIST',
+ FOR_API = 'FOR_API',
WITH_ACCOUNT = 'WITH_ACCOUNT',
WITH_ACTOR = 'WITH_ACTOR',
WITH_VIDEOS = 'WITH_VIDEOS',
@Scopes(() => ({
[ScopeNames.SUMMARY]: (options: SummaryOptions = {}) => {
const base: FindOptions = {
- attributes: [ 'name', 'description', 'id', 'actorId' ],
+ attributes: [ 'id', 'name', 'description', 'actorId' ],
include: [
{
- attributes: [ 'preferredUsername', 'url', 'serverId', 'avatarId' ],
+ attributes: [ 'id', 'preferredUsername', 'url', 'serverId', 'avatarId' ],
model: ActorModel.unscoped(),
required: true,
include: [
return base
},
- [ScopeNames.AVAILABLE_FOR_LIST]: (options: AvailableForListOptions) => {
+ [ScopeNames.FOR_API]: (options: AvailableForListOptions) => {
// Only list local channels OR channels that are on an instance followed by actorId
const inQueryInstanceFollow = buildServerIdsFollowedBy(options.actorId)
}
const scopes = {
- method: [ ScopeNames.AVAILABLE_FOR_LIST, { actorId } as AvailableForListOptions ]
+ method: [ ScopeNames.FOR_API, { actorId } as AvailableForListOptions ]
}
return VideoChannelModel
.scope(scopes)
})
}
- static listLocalsForSitemap (sort: string) {
+ static listLocalsForSitemap (sort: string): Bluebird<MChannelActor[]> {
const query = {
attributes: [ ],
offset: 0,
}
const scopes = {
- method: [ ScopeNames.AVAILABLE_FOR_LIST, { actorId: options.actorId } as AvailableForListOptions ]
+ method: [ ScopeNames.FOR_API, { actorId: options.actorId } as AvailableForListOptions ]
}
return VideoChannelModel
.scope(scopes)
})
}
- static loadByIdAndPopulateAccount (id: number) {
+ static loadByIdAndPopulateAccount (id: number): Bluebird<MChannelActorAccountDefault> {
return VideoChannelModel.unscoped()
.scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ])
.findByPk(id)
}
- static loadByIdAndAccount (id: number, accountId: number) {
+ static loadByIdAndAccount (id: number, accountId: number): Bluebird<MChannelActorAccountDefault> {
const query = {
where: {
id,
.findOne(query)
}
- static loadAndPopulateAccount (id: number) {
+ static loadAndPopulateAccount (id: number): Bluebird<MChannelActorAccountDefault> {
return VideoChannelModel.unscoped()
.scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ])
.findByPk(id)
}
- static loadByUrlAndPopulateAccount (url: string) {
+ static loadByUrlAndPopulateAccount (url: string): Bluebird<MChannelAccountDefault> {
const query = {
include: [
{
return VideoChannelModel.loadByNameAndHostAndPopulateAccount(name, host)
}
- static loadLocalByNameAndPopulateAccount (name: string) {
+ static loadLocalByNameAndPopulateAccount (name: string): Bluebird<MChannelActorAccountDefault> {
const query = {
include: [
{
.findOne(query)
}
- static loadByNameAndHostAndPopulateAccount (name: string, host: string) {
+ static loadByNameAndHostAndPopulateAccount (name: string, host: string): Bluebird<MChannelActorAccountDefault> {
const query = {
include: [
{
.findOne(query)
}
- static loadAndPopulateAccountAndVideos (id: number) {
+ static loadAndPopulateAccountAndVideos (id: number): Bluebird<MChannelActorAccountDefaultVideos> {
const options = {
include: [
VideoModel
-import {
- AllowNull,
- BeforeDestroy,
- BelongsTo,
- Column,
- CreatedAt,
- DataType,
- ForeignKey,
- Is,
- Model,
- Scopes,
- Table,
- UpdatedAt
-} from 'sequelize-typescript'
+import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript'
import { ActivityTagObject } from '../../../shared/models/activitypub/objects/common-objects'
import { VideoCommentObject } from '../../../shared/models/activitypub/objects/video-comment-object'
import { VideoComment } from '../../../shared/models/videos/video-comment.model'
import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
import { CONSTRAINTS_FIELDS, WEBSERVER } from '../../initializers/constants'
-import { sendDeleteVideoComment } from '../../lib/activitypub/send'
import { AccountModel } from '../account/account'
import { ActorModel } from '../activitypub/actor'
-import { AvatarModel } from '../avatar/avatar'
-import { ServerModel } from '../server/server'
import { buildBlockedAccountSQL, buildLocalAccountIdsIn, getSort, throwIfNotValid } from '../utils'
import { VideoModel } from './video'
import { VideoChannelModel } from './video-channel'
import { getServerActor } from '../../helpers/utils'
-import { UserModel } from '../account/user'
import { actorNameAlphabet } from '../../helpers/custom-validators/activitypub/actor'
import { regexpCapture } from '../../helpers/regexp'
import { uniq } from 'lodash'
-import { FindOptions, literal, Op, Order, ScopeOptions, Sequelize, Transaction } from 'sequelize'
+import { FindOptions, Op, Order, ScopeOptions, Sequelize, Transaction } from 'sequelize'
+import * as Bluebird from 'bluebird'
+import {
+ MComment,
+ MCommentId,
+ MCommentOwner,
+ MCommentOwnerReplyVideoLight,
+ MCommentOwnerVideo,
+ MCommentOwnerVideoFeed,
+ MCommentOwnerVideoReply
+} from '../../typings/models/video'
+import { MUserAccountId } from '@server/typings/models'
enum ScopeNames {
WITH_ACCOUNT = 'WITH_ACCOUNT',
[ScopeNames.WITH_ACCOUNT]: {
include: [
{
- model: AccountModel,
- include: [
- {
- model: ActorModel,
- include: [
- {
- model: ServerModel,
- required: false
- },
- {
- model: AvatarModel,
- required: false
- }
- ]
- }
- ]
+ model: AccountModel
}
]
},
required: true,
include: [
{
- model: VideoChannelModel.unscoped(),
+ model: VideoChannelModel,
required: true,
include: [
- {
- model: ActorModel,
- required: true
- },
{
model: AccountModel,
- required: true,
- include: [
- {
- model: ActorModel,
- required: true
- }
- ]
+ required: true
}
]
}
})
Account: AccountModel
- static loadById (id: number, t?: Transaction) {
+ static loadById (id: number, t?: Transaction): Bluebird<MComment> {
const query: FindOptions = {
where: {
id
return VideoCommentModel.findOne(query)
}
- static loadByIdAndPopulateVideoAndAccountAndReply (id: number, t?: Transaction) {
+ static loadByIdAndPopulateVideoAndAccountAndReply (id: number, t?: Transaction): Bluebird<MCommentOwnerVideoReply> {
const query: FindOptions = {
where: {
id
.findOne(query)
}
- static loadByUrlAndPopulateAccountAndVideo (url: string, t?: Transaction) {
+ static loadByUrlAndPopulateAccountAndVideo (url: string, t?: Transaction): Bluebird<MCommentOwnerVideo> {
const query: FindOptions = {
where: {
url
return VideoCommentModel.scope([ ScopeNames.WITH_ACCOUNT, ScopeNames.WITH_VIDEO ]).findOne(query)
}
- static loadByUrlAndPopulateReplyAndVideoUrlAndAccount (url: string, t?: Transaction) {
+ static loadByUrlAndPopulateReplyAndVideoUrlAndAccount (url: string, t?: Transaction): Bluebird<MCommentOwnerReplyVideoLight> {
const query: FindOptions = {
where: {
url
start: number,
count: number,
sort: string,
- user?: UserModel
+ user?: MUserAccountId
}) {
const { videoId, start, count, sort, user } = parameters
static async listThreadCommentsForApi (parameters: {
videoId: number,
threadId: number,
- user?: UserModel
+ user?: MUserAccountId
}) {
const { videoId, threadId, user } = parameters
})
}
- static listThreadParentComments (comment: VideoCommentModel, t: Transaction, order: 'ASC' | 'DESC' = 'ASC') {
+ static listThreadParentComments (comment: MCommentId, t: Transaction, order: 'ASC' | 'DESC' = 'ASC'): Bluebird<MCommentOwner[]> {
const query = {
order: [ [ 'createdAt', order ] ] as Order,
where: {
transaction: t
}
- return VideoCommentModel.findAndCountAll(query)
+ return VideoCommentModel.findAndCountAll<MComment>(query)
}
- static listForFeed (start: number, count: number, videoId?: number) {
+ static listForFeed (start: number, count: number, videoId?: number): Bluebird<MCommentOwnerVideoFeed[]> {
const query = {
order: [ [ 'createdAt', 'DESC' ] ] as Order,
offset: start,
} as VideoComment
}
- toActivityPubObject (threadParentComments: VideoCommentModel[]): VideoCommentObject {
+ toActivityPubObject (threadParentComments: MCommentOwner[]): VideoCommentObject {
let inReplyTo: string
// New thread, so in AS we reply to the video
if (this.inReplyToCommentId === null) {
import { VideoStreamingPlaylistModel } from './video-streaming-playlist'
import { FindOptions, QueryTypes, Transaction } from 'sequelize'
import { MIMETYPES } from '../../initializers/constants'
+import { MVideoFile } from '@server/typings/models'
@Table({
tableName: 'videoFile',
return !!MIMETYPES.AUDIO.EXT_MIMETYPE[this.extname]
}
- hasSameUniqueKeysThan (other: VideoFileModel) {
+ hasSameUniqueKeysThan (other: MVideoFile) {
return this.fps === other.fps &&
this.resolution === other.resolution &&
this.videoId === other.videoId
import { Video, VideoDetails, VideoFile } from '../../../shared/models/videos'
import { VideoModel } from './video'
-import { VideoFileModel } from './video-file'
import {
ActivityPlaylistInfohashesObject,
ActivityPlaylistSegmentHashesObject,
} from '../../lib/activitypub'
import { isArray } from '../../helpers/custom-validators/misc'
import { VideoStreamingPlaylist } from '../../../shared/models/videos/video-streaming-playlist.model'
-import { VideoStreamingPlaylistModel } from './video-streaming-playlist'
+import { MVideo, MVideoAP, MVideoDetails } from '../../typings/models'
+import { MStreamingPlaylistRedundancies } from '../../typings/models/video/video-streaming-playlist'
+import { MVideoFileRedundanciesOpt } from '../../typings/models/video/video-file'
export type VideoFormattingJSONOptions = {
completeDescription?: boolean
return videoObject
}
-function videoModelToFormattedDetailsJSON (video: VideoModel): VideoDetails {
+function videoModelToFormattedDetailsJSON (video: MVideoDetails): VideoDetails {
const formattedJson = video.toFormattedJSON({
additionalAttributes: {
scheduledUpdate: true,
const tags = video.Tags ? video.Tags.map(t => t.name) : []
- const streamingPlaylists = streamingPlaylistsModelToFormattedJSON(video, video.VideoStreamingPlaylists)
+ const streamingPlaylists = streamingPlaylistsModelToFormattedJSON(video.VideoStreamingPlaylists)
const detailsJson = {
support: video.support,
return Object.assign(formattedJson, detailsJson)
}
-function streamingPlaylistsModelToFormattedJSON (video: VideoModel, playlists: VideoStreamingPlaylistModel[]): VideoStreamingPlaylist[] {
+function streamingPlaylistsModelToFormattedJSON (playlists: MStreamingPlaylistRedundancies[]): VideoStreamingPlaylist[] {
if (isArray(playlists) === false) return []
return playlists
})
}
-function videoFilesModelToFormattedJSON (video: VideoModel, videoFiles: VideoFileModel[]): VideoFile[] {
+function videoFilesModelToFormattedJSON (video: MVideo, videoFiles: MVideoFileRedundanciesOpt[]): VideoFile[] {
const { baseUrlHttp, baseUrlWs } = video.getBaseUrls()
return videoFiles
})
}
-function videoModelToActivityPubObject (video: VideoModel): VideoTorrentObject {
+function videoModelToActivityPubObject (video: MVideoAP): VideoTorrentObject {
const { baseUrlHttp, baseUrlWs } = video.getBaseUrls()
if (!video.Tags) video.Tags = []
import { VideoImport, VideoImportState } from '../../../shared'
import { isVideoMagnetUriValid } from '../../helpers/custom-validators/videos'
import { UserModel } from '../account/user'
+import * as Bluebird from 'bluebird'
+import { MVideoImportDefault } from '@server/typings/models/video/video-import'
@DefaultScope(() => ({
include: [
required: true
},
{
- model: VideoModel.scope([ VideoModelScopeNames.WITH_ACCOUNT_DETAILS, VideoModelScopeNames.WITH_TAGS]),
+ model: VideoModel.scope([
+ VideoModelScopeNames.WITH_ACCOUNT_DETAILS,
+ VideoModelScopeNames.WITH_TAGS,
+ VideoModelScopeNames.WITH_THUMBNAILS
+ ]),
required: false
}
]
return undefined
}
- static loadAndPopulateVideo (id: number) {
+ static loadAndPopulateVideo (id: number): Bluebird<MVideoImportDefault> {
return VideoImportModel.findByPk(id)
}
}
}
- return VideoImportModel.findAndCountAll(query)
+ return VideoImportModel.findAndCountAll<MVideoImportDefault>(query)
.then(({ rows, count }) => {
return {
data: rows,
import { VideoPlaylistElement, VideoPlaylistElementType } from '../../../shared/models/videos/playlist/video-playlist-element.model'
import { AccountModel } from '../account/account'
import { VideoPrivacy } from '../../../shared/models/videos'
+import * as Bluebird from 'bluebird'
+import { MVideoPlaylistAP, MVideoPlaylistElement, MVideoPlaylistVideoThumbnail } from '@server/typings/models/video/video-playlist-element'
+import { MUserAccountId } from '@server/typings/models'
@Table({
tableName: 'videoPlaylistElement',
count: number,
videoPlaylistId: number,
serverAccount: AccountModel,
- user?: UserModel
+ user?: MUserAccountId
}) {
const accountIds = [ options.serverAccount.id ]
const videoScope: (ScopeOptions | string)[] = [
]).then(([ total, data ]) => ({ total, data }))
}
- static loadByPlaylistAndVideo (videoPlaylistId: number, videoId: number) {
+ static loadByPlaylistAndVideo (videoPlaylistId: number, videoId: number): Bluebird<MVideoPlaylistElement> {
const query = {
where: {
videoPlaylistId,
return VideoPlaylistElementModel.findOne(query)
}
- static loadById (playlistElementId: number) {
+ static loadById (playlistElementId: number): Bluebird<MVideoPlaylistElement> {
return VideoPlaylistElementModel.findByPk(playlistElementId)
}
- static loadByPlaylistAndVideoForAP (playlistId: number | string, videoId: number | string) {
+ static loadByPlaylistAndVideoForAP (playlistId: number | string, videoId: number | string): Bluebird<MVideoPlaylistAP> {
const playlistWhere = validator.isUUID('' + playlistId) ? { uuid: playlistId } : { id: playlistId }
const videoWhere = validator.isUUID('' + videoId) ? { uuid: videoId } : { id: videoId }
})
}
- static loadFirstElementWithVideoThumbnail (videoPlaylistId: number) {
+ static loadFirstElementWithVideoThumbnail (videoPlaylistId: number): Bluebird<MVideoPlaylistVideoThumbnail> {
const query = {
order: getSort('position'),
where: {
import { ThumbnailModel } from './thumbnail'
import { ActivityIconObject } from '../../../shared/models/activitypub/objects'
import { FindOptions, literal, Op, ScopeOptions, Transaction, WhereOptions } from 'sequelize'
+import * as Bluebird from 'bluebird'
+import {
+ MVideoPlaylistAccountThumbnail,
+ MVideoPlaylistFull,
+ MVideoPlaylistFullSummary,
+ MVideoPlaylistIdWithElements
+} from '../../typings/models/video/video-playlist'
+import { MThumbnail } from '../../typings/models/video/thumbnail'
enum ScopeNames {
AVAILABLE_FOR_LIST = 'AVAILABLE_FOR_LIST',
})
}
- static listPlaylistIdsOf (accountId: number, videoIds: number[]) {
+ static listPlaylistIdsOf (accountId: number, videoIds: number[]): Bluebird<MVideoPlaylistIdWithElements[]> {
const query = {
attributes: [ 'id' ],
where: {
.then(e => !!e)
}
- static loadWithAccountAndChannelSummary (id: number | string, transaction: Transaction) {
+ static loadWithAccountAndChannelSummary (id: number | string, transaction: Transaction): Bluebird<MVideoPlaylistFullSummary> {
const where = buildWhereIdOrUUID(id)
const query = {
.findOne(query)
}
- static loadWithAccountAndChannel (id: number | string, transaction: Transaction) {
+ static loadWithAccountAndChannel (id: number | string, transaction: Transaction): Bluebird<MVideoPlaylistFull> {
const where = buildWhereIdOrUUID(id)
const query = {
.findOne(query)
}
- static loadByUrlAndPopulateAccount (url: string) {
+ static loadByUrlAndPopulateAccount (url: string): Bluebird<MVideoPlaylistAccountThumbnail> {
const query = {
where: {
url
return VideoPlaylistModel.update({ privacy: VideoPlaylistPrivacy.PRIVATE, videoChannelId: null }, query)
}
- async setAndSaveThumbnail (thumbnail: ThumbnailModel, t: Transaction) {
+ async setAndSaveThumbnail (thumbnail: MThumbnail, t: Transaction) {
thumbnail.videoPlaylistId = this.id
this.Thumbnail = await thumbnail.save({ transaction: t })
import { VideoModel } from './video'
import { VideoChannelModel } from './video-channel'
import { Op, Transaction } from 'sequelize'
+import { MVideoShareActor, MVideoShareFull } from '../../typings/models/video'
+import { MActorDefault } from '../../typings/models'
enum ScopeNames {
FULL = 'FULL',
})
Video: VideoModel
- static load (actorId: number, videoId: number, t?: Transaction) {
+ static load (actorId: number, videoId: number, t?: Transaction): Bluebird<MVideoShareActor> {
return VideoShareModel.scope(ScopeNames.WITH_ACTOR).findOne({
where: {
actorId,
})
}
- static loadByUrl (url: string, t: Transaction) {
+ static loadByUrl (url: string, t: Transaction): Bluebird<MVideoShareFull> {
return VideoShareModel.scope(ScopeNames.FULL).findOne({
where: {
url
})
}
- static loadActorsByShare (videoId: number, t: Transaction) {
+ static loadActorsByShare (videoId: number, t: Transaction): Bluebird<MActorDefault[]> {
const query = {
where: {
videoId
}
return VideoShareModel.scope(ScopeNames.FULL).findAll(query)
- .then(res => res.map(r => r.Actor))
+ .then((res: MVideoShareFull[]) => res.map(r => r.Actor))
}
- static loadActorsWhoSharedVideosOf (actorOwnerId: number, t: Transaction): Bluebird<ActorModel[]> {
+ static loadActorsWhoSharedVideosOf (actorOwnerId: number, t: Transaction): Bluebird<MActorDefault[]> {
const query = {
attributes: [],
include: [
.then(res => res.map(r => r.Actor))
}
- static loadActorsByVideoChannel (videoChannelId: number, t: Transaction): Bluebird<ActorModel[]> {
+ static loadActorsByVideoChannel (videoChannelId: number, t: Transaction): Bluebird<MActorDefault[]> {
const query = {
attributes: [],
include: [
-import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, HasMany, Is, Model, Table, UpdatedAt, DataType } from 'sequelize-typescript'
+import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, HasMany, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
import { isVideoFileInfoHashValid } from '../../helpers/custom-validators/videos'
import { throwIfNotValid } from '../utils'
import { VideoModel } from './video'
import { VideoRedundancyModel } from '../redundancy/video-redundancy'
import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type'
import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
-import { CONSTRAINTS_FIELDS, STATIC_PATHS, P2P_MEDIA_LOADER_PEER_VERSION } from '../../initializers/constants'
-import { VideoFileModel } from './video-file'
+import { CONSTRAINTS_FIELDS, P2P_MEDIA_LOADER_PEER_VERSION, STATIC_PATHS } from '../../initializers/constants'
import { join } from 'path'
import { sha1 } from '../../helpers/core-utils'
import { isArrayOf } from '../../helpers/custom-validators/misc'
-import { QueryTypes, Op } from 'sequelize'
+import { Op, QueryTypes } from 'sequelize'
+import { MStreamingPlaylist, MVideoFile } from '@server/typings/models'
@Table({
tableName: 'videoStreamingPlaylist',
.then(results => results.length === 1)
}
- static buildP2PMediaLoaderInfoHashes (playlistUrl: string, videoFiles: VideoFileModel[]) {
+ static buildP2PMediaLoaderInfoHashes (playlistUrl: string, videoFiles: MVideoFile[]) {
const hashes: string[] = []
// https://github.com/Novage/p2p-media-loader/blob/master/p2p-media-loader-core/lib/p2p-media-manager.ts#L115
return baseUrlHttp + STATIC_PATHS.REDUNDANCY + this.getStringType() + '/' + this.Video.uuid
}
- hasSameUniqueKeysThan (other: VideoStreamingPlaylistModel) {
+ hasSameUniqueKeysThan (other: MStreamingPlaylist) {
return this.type === other.type &&
this.videoId === other.videoId
}
Table,
UpdatedAt
} from 'sequelize-typescript'
-import { UserRight, VideoPrivacy, VideoResolution, VideoState } from '../../../shared'
+import { UserRight, VideoPrivacy, VideoState } from '../../../shared'
import { VideoTorrentObject } from '../../../shared/models/activitypub/objects'
import { Video, VideoDetails, VideoFile } from '../../../shared/models/videos'
import { VideoFilter } from '../../../shared/models/videos/video-query.type'
videoModelToFormattedJSON
} from './video-format-utils'
import { UserVideoHistoryModel } from '../account/user-video-history'
-import { UserModel } from '../account/user'
import { VideoImportModel } from './video-import'
import { VideoStreamingPlaylistModel } from './video-streaming-playlist'
import { VideoPlaylistElementModel } from './video-playlist-element'
import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type'
import { createTorrentPromise } from '../../helpers/webtorrent'
import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type'
+import {
+ MChannel,
+ MChannelActorAccountDefault,
+ MChannelId,
+ MUserAccountId,
+ MUserId,
+ MVideoAccountAllFiles,
+ MVideoAccountLight,
+ MVideoDetails,
+ MVideoFullLight,
+ MVideoIdThumbnail,
+ MVideoThumbnail,
+ MVideoWithAllFiles,
+ MVideoWithBlacklistThumbnailScheduled,
+ MVideoWithRights
+} from '../../typings/models'
+import { MVideoFile, MVideoFileRedundanciesOpt } from '../../typings/models/video/video-file'
+import { MThumbnail } from '../../typings/models/video/thumbnail'
// FIXME: Define indexes here because there is an issue with TS and Sequelize.literal when called directly in the annotation
const indexes: (ModelIndexesOptions & { where?: WhereOptions })[] = [
videoPlaylistId?: number
trendingDays?: number
- user?: UserModel,
- historyOfUser?: UserModel
+ user?: MUserAccountId
+ historyOfUser?: MUserId
baseWhere?: WhereOptions[]
}
[ ScopeNames.WITH_BLACKLISTED ]: {
include: [
{
- attributes: [ 'id', 'reason' ],
+ attributes: [ 'id', 'reason', 'unfederated' ],
model: VideoBlacklistModel,
required: false
}
VideoCaptions: VideoCaptionModel[]
@BeforeDestroy
- static async sendDelete (instance: VideoModel, options) {
+ static async sendDelete (instance: MVideoAccountLight, options) {
if (instance.isOwned()) {
if (!instance.VideoChannel) {
instance.VideoChannel = await instance.$get('VideoChannel', {
include: [
- {
- model: AccountModel,
- include: [ ActorModel ]
- }
+ ActorModel,
+ AccountModel
],
transaction: options.transaction
- }) as VideoChannelModel
+ }) as MChannelActorAccountDefault
}
return sendDeleteVideo(instance, options.transaction)
return undefined
}
- static listLocal () {
+ static listLocal (): Bluebird<MVideoWithAllFiles[]> {
const query = {
where: {
remote: false
})
}
- static listUserVideosForApi (accountId: number, start: number, count: number, sort: string, withFiles = false) {
+ static listUserVideosForApi (accountId: number, start: number, count: number, sort: string) {
function buildBaseQuery (): FindOptions {
return {
offset: start,
ScopeNames.WITH_THUMBNAILS
]
- if (withFiles === true) {
- findQuery.include.push({
- model: VideoFileModel.unscoped(),
- required: true
- })
- }
-
return Promise.all([
VideoModel.count(countQuery),
VideoModel.scope(findScopes).findAll(findQuery)
]).then(([ count, rows ]) => {
return {
- data: rows,
+ data: rows as MVideoWithBlacklistThumbnailScheduled[],
total: count
}
})
followerActorId?: number
videoPlaylistId?: number,
trendingDays?: number,
- user?: UserModel,
- historyOfUser?: UserModel
+ user?: MUserAccountId,
+ historyOfUser?: MUserId
}, countVideos = true) {
if (options.filter && options.filter === 'all-local' && !options.user.hasRight(UserRight.SEE_ALL_VIDEOS)) {
throw new Error('Try to filter all-local but no user has not the see all videos right')
tagsAllOf?: string[]
durationMin?: number // seconds
durationMax?: number // seconds
- user?: UserModel,
+ user?: MUserAccountId,
filter?: VideoFilter
}) {
const whereAnd = []
return VideoModel.getAvailableForApi(query, queryOptions)
}
- static load (id: number | string, t?: Transaction) {
+ static load (id: number | string, t?: Transaction): Bluebird<MVideoThumbnail> {
const where = buildWhereIdOrUUID(id)
const options = {
where,
return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options)
}
- static loadWithRights (id: number | string, t?: Transaction) {
+ static loadWithRights (id: number | string, t?: Transaction): Bluebird<MVideoWithRights> {
const where = buildWhereIdOrUUID(id)
const options = {
where,
]).findOne(options)
}
- static loadOnlyId (id: number | string, t?: Transaction) {
+ static loadOnlyId (id: number | string, t?: Transaction): Bluebird<MVideoIdThumbnail> {
const where = buildWhereIdOrUUID(id)
const options = {
return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options)
}
- static loadWithFiles (id: number | string, t?: Transaction, logging?: boolean) {
+ static loadWithFiles (id: number | string, t?: Transaction, logging?: boolean): Bluebird<MVideoWithAllFiles> {
const where = buildWhereIdOrUUID(id)
const query = {
]).findOne(query)
}
- static loadByUUID (uuid: string) {
+ static loadByUUID (uuid: string): Bluebird<MVideoThumbnail> {
const options = {
where: {
uuid
return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options)
}
- static loadByUrl (url: string, transaction?: Transaction) {
+ static loadByUrl (url: string, transaction?: Transaction): Bluebird<MVideoThumbnail> {
const query: FindOptions = {
where: {
url
return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(query)
}
- static loadByUrlAndPopulateAccount (url: string, transaction?: Transaction) {
+ static loadByUrlAndPopulateAccount (url: string, transaction?: Transaction): Bluebird<MVideoAccountAllFiles> {
const query: FindOptions = {
where: {
url
ScopeNames.WITH_ACCOUNT_DETAILS,
ScopeNames.WITH_FILES,
ScopeNames.WITH_STREAMING_PLAYLISTS,
- ScopeNames.WITH_THUMBNAILS
+ ScopeNames.WITH_THUMBNAILS,
+ ScopeNames.WITH_BLACKLISTED
]).findOne(query)
}
- static loadAndPopulateAccountAndServerAndTags (id: number | string, t?: Transaction, userId?: number) {
+ static loadAndPopulateAccountAndServerAndTags (id: number | string, t?: Transaction, userId?: number): Bluebird<MVideoFullLight> {
const where = buildWhereIdOrUUID(id)
const options = {
id: number | string,
t?: Transaction,
userId?: number
- }) {
+ }): Bluebird<MVideoDetails> {
const { id, t, userId } = parameters
const where = buildWhereIdOrUUID(id)
.then(results => results.length === 1)
}
- static bulkUpdateSupportField (videoChannel: VideoChannelModel, t: Transaction) {
+ static bulkUpdateSupportField (videoChannel: MChannel, t: Transaction) {
const options = {
where: {
channelId: videoChannel.id
return VideoModel.update({ support: videoChannel.support }, options)
}
- static getAllIdsFromChannel (videoChannel: VideoChannelModel) {
+ static getAllIdsFromChannel (videoChannel: MChannelId): Bluebird<number[]> {
const query = {
attributes: [ 'id' ],
where: {
return this.VideoFiles.find(f => f.resolution === resolution)
}
- async addAndSaveThumbnail (thumbnail: ThumbnailModel, transaction: Transaction) {
+ async addAndSaveThumbnail (thumbnail: MThumbnail, transaction: Transaction) {
thumbnail.videoId = this.id
const savedThumbnail = await thumbnail.save({ transaction })
this.Thumbnails.push(savedThumbnail)
}
- getVideoFilename (videoFile: VideoFileModel) {
+ getVideoFilename (videoFile: MVideoFile) {
return this.uuid + '-' + videoFile.resolution + videoFile.extname
}
return this.Thumbnails.find(t => t.type === ThumbnailType.PREVIEW)
}
- getTorrentFileName (videoFile: VideoFileModel) {
+ getTorrentFileName (videoFile: MVideoFile) {
const extension = '.torrent'
return this.uuid + '-' + videoFile.resolution + extension
}
return this.remote === false
}
- getTorrentFilePath (videoFile: VideoFileModel) {
+ getTorrentFilePath (videoFile: MVideoFile) {
return join(CONFIG.STORAGE.TORRENTS_DIR, this.getTorrentFileName(videoFile))
}
- getVideoFilePath (videoFile: VideoFileModel) {
+ getVideoFilePath (videoFile: MVideoFile) {
return join(CONFIG.STORAGE.VIDEOS_DIR, this.getVideoFilename(videoFile))
}
- async createTorrentAndSetInfoHash (videoFile: VideoFileModel) {
+ async createTorrentAndSetInfoHash (videoFile: MVideoFile) {
const options = {
// Keep the extname, it's used by the client to stream the file inside a web browser
name: `${this.name} ${videoFile.resolution}p${videoFile.extname}`,
return this.VideoStreamingPlaylists.find(p => p.type === VideoStreamingPlaylistType.HLS)
}
- removeFile (videoFile: VideoFileModel, isRedundancy = false) {
+ removeFile (videoFile: MVideoFile, isRedundancy = false) {
const baseDir = isRedundancy ? CONFIG.STORAGE.REDUNDANCY_DIR : CONFIG.STORAGE.VIDEOS_DIR
const filePath = join(baseDir, this.getVideoFilename(videoFile))
.catch(err => logger.warn('Cannot delete file %s.', filePath, { err }))
}
- removeTorrent (videoFile: VideoFileModel) {
+ removeTorrent (videoFile: MVideoFile) {
const torrentPath = join(CONFIG.STORAGE.TORRENTS_DIR, this.getTorrentFileName(videoFile))
return remove(torrentPath)
.catch(err => logger.warn('Cannot delete torrent %s.', torrentPath, { err }))
return { baseUrlHttp, baseUrlWs }
}
- generateMagnetUri (videoFile: VideoFileModel, baseUrlHttp: string, baseUrlWs: string) {
+ generateMagnetUri (videoFile: MVideoFileRedundanciesOpt, baseUrlHttp: string, baseUrlWs: string) {
const xs = this.getTorrentUrl(videoFile, baseUrlHttp)
const announce = this.getTrackerUrls(baseUrlHttp, baseUrlWs)
let urlList = [ this.getVideoFileUrl(videoFile, baseUrlHttp) ]
return [ baseUrlWs + '/tracker/socket', baseUrlHttp + '/tracker/announce' ]
}
- getTorrentUrl (videoFile: VideoFileModel, baseUrlHttp: string) {
+ getTorrentUrl (videoFile: MVideoFile, baseUrlHttp: string) {
return baseUrlHttp + STATIC_PATHS.TORRENTS + this.getTorrentFileName(videoFile)
}
- getTorrentDownloadUrl (videoFile: VideoFileModel, baseUrlHttp: string) {
+ getTorrentDownloadUrl (videoFile: MVideoFile, baseUrlHttp: string) {
return baseUrlHttp + STATIC_DOWNLOAD_PATHS.TORRENTS + this.getTorrentFileName(videoFile)
}
- getVideoFileUrl (videoFile: VideoFileModel, baseUrlHttp: string) {
+ getVideoFileUrl (videoFile: MVideoFile, baseUrlHttp: string) {
return baseUrlHttp + STATIC_PATHS.WEBSEED + this.getVideoFilename(videoFile)
}
- getVideoRedundancyUrl (videoFile: VideoFileModel, baseUrlHttp: string) {
+ getVideoRedundancyUrl (videoFile: MVideoFile, baseUrlHttp: string) {
return baseUrlHttp + STATIC_PATHS.REDUNDANCY + this.getVideoFilename(videoFile)
}
- getVideoFileDownloadUrl (videoFile: VideoFileModel, baseUrlHttp: string) {
+ getVideoFileDownloadUrl (videoFile: MVideoFile, baseUrlHttp: string) {
return baseUrlHttp + STATIC_DOWNLOAD_PATHS.VIDEOS + this.getVideoFilename(videoFile)
}
- getBandwidthBits (videoFile: VideoFileModel) {
+ getBandwidthBits (videoFile: MVideoFile) {
return Math.ceil((videoFile.size * 8) / this.duration)
}
}
import { Activity } from '../../shared/models/activitypub'
-import { ActorModel } from '../models/activitypub/actor'
-import { SignatureActorModel } from './models'
+import { MActorDefault, MActorSignature } from './models'
export type APProcessorOptions<T extends Activity> = {
activity: T
- byActor: SignatureActorModel
- inboxActor?: ActorModel
+ byActor: MActorSignature
+ inboxActor?: MActorDefault
fromFetch?: boolean
}
-import { VideoChannelModel } from '../models/video/video-channel'
-import { VideoPlaylistModel } from '../models/video/video-playlist'
-import { VideoPlaylistElementModel } from '../models/video/video-playlist-element'
-import { UserModel } from '../models/account/user'
-import { VideoModel } from '../models/video/video'
-import { AccountModel } from '../models/account/account'
-import { VideoChangeOwnershipModel } from '../models/video/video-change-ownership'
-import { ActorModel } from '../models/activitypub/actor'
-import { VideoCommentModel } from '../models/video/video-comment'
-import { VideoShareModel } from '../models/video/video-share'
-import { AccountVideoRateModel } from '../models/account/account-video-rate'
-import { ActorFollowModel } from '../models/activitypub/actor-follow'
-import { ServerModel } from '../models/server/server'
-import { VideoFileModel } from '../models/video/video-file'
-import { VideoRedundancyModel } from '../models/redundancy/video-redundancy'
-import { ServerBlocklistModel } from '../models/server/server-blocklist'
-import { AccountBlocklistModel } from '../models/account/account-blocklist'
-import { VideoImportModel } from '../models/video/video-import'
-import { VideoAbuseModel } from '../models/video/video-abuse'
-import { VideoBlacklistModel } from '../models/video/video-blacklist'
-import { VideoCaptionModel } from '../models/video/video-caption'
-import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist'
import { RegisteredPlugin } from '../lib/plugins/plugin-manager'
-import { PluginModel } from '../models/server/plugin'
-import { SignatureActorModel } from './models'
+import {
+ MAccountDefault,
+ MActorAccountChannelId,
+ MActorFollowActorsDefault,
+ MActorFollowActorsDefaultSubscription,
+ MActorFull,
+ MChannelActorAccountDefault,
+ MComment,
+ MCommentOwnerVideoReply,
+ MUserDefault,
+ MVideoAbuse,
+ MVideoBlacklist,
+ MVideoCaptionVideo,
+ MVideoFullLight,
+ MVideoIdThumbnail,
+ MVideoRedundancyVideo,
+ MVideoShareActor,
+ MVideoThumbnail,
+ MVideoWithRights
+} from './models'
+import { MVideoPlaylistFull, MVideoPlaylistFullSummary } from './models/video/video-playlist'
+import { MVideoImportDefault } from '@server/typings/models/video/video-import'
+import { MAccountBlocklist, MStreamingPlaylist, MVideoFile } from '@server/typings/models'
+import { MVideoPlaylistElement } from '@server/typings/models/video/video-playlist-element'
+import { MAccountVideoRateAccountVideo } from '@server/typings/models/video/video-rate'
+import { MVideoChangeOwnershipFull } from './models/video/video-change-ownership'
+import { MPlugin, MServer } from '@server/typings/models/server'
+import { MServerBlocklist } from './models/server/server-blocklist'
+import { MOAuthTokenUser } from '@server/typings/models/oauth/oauth-token'
declare module 'express' {
interface Response {
+
locals: {
- video?: VideoModel
- videoShare?: VideoShareModel
- videoFile?: VideoFileModel
+ videoAll?: MVideoFullLight
+ onlyVideo?: MVideoThumbnail
+ onlyVideoWithRights?: MVideoWithRights
+ videoId?: MVideoIdThumbnail
+
+ videoShare?: MVideoShareActor
+
+ videoFile?: MVideoFile
+
+ videoImport?: MVideoImportDefault
+
+ videoBlacklist?: MVideoBlacklist
+
+ videoCaption?: MVideoCaptionVideo
+
+ videoAbuse?: MVideoAbuse
- videoImport?: VideoImportModel
+ videoStreamingPlaylist?: MStreamingPlaylist
- videoBlacklist?: VideoBlacklistModel
+ videoChannel?: MChannelActorAccountDefault
- videoCaption?: VideoCaptionModel
+ videoPlaylistFull?: MVideoPlaylistFull
+ videoPlaylistSummary?: MVideoPlaylistFullSummary
- videoAbuse?: VideoAbuseModel
+ videoPlaylistElement?: MVideoPlaylistElement
- videoStreamingPlaylist?: VideoStreamingPlaylistModel
+ accountVideoRate?: MAccountVideoRateAccountVideo
- videoChannel?: VideoChannelModel
+ videoCommentFull?: MCommentOwnerVideoReply
+ videoCommentThread?: MComment
- videoPlaylist?: VideoPlaylistModel
- videoPlaylistElement?: VideoPlaylistElementModel
+ follow?: MActorFollowActorsDefault
+ subscription?: MActorFollowActorsDefaultSubscription
- accountVideoRate?: AccountVideoRateModel
+ nextOwner?: MAccountDefault
+ videoChangeOwnership?: MVideoChangeOwnershipFull
- videoComment?: VideoCommentModel
- videoCommentThread?: VideoCommentModel
+ account?: MAccountDefault
- follow?: ActorFollowModel
- subscription?: ActorFollowModel
+ actorFull?: MActorFull
- nextOwner?: AccountModel
- videoChangeOwnership?: VideoChangeOwnershipModel
- account?: AccountModel
- actor?: ActorModel
- user?: UserModel
+ user?: MUserDefault
- server?: ServerModel
+ server?: MServer
- videoRedundancy?: VideoRedundancyModel
+ videoRedundancy?: MVideoRedundancyVideo
- accountBlock?: AccountBlocklistModel
- serverBlock?: ServerBlocklistModel
+ accountBlock?: MAccountBlocklist
+ serverBlock?: MServerBlocklist
oauth?: {
- token: {
- User: UserModel
- user: UserModel
- }
+ token: MOAuthTokenUser
}
signature?: {
- actor: SignatureActorModel
+ actor: MActorAccountChannelId
}
authenticated?: boolean
registeredPlugin?: RegisteredPlugin
- plugin?: PluginModel
+ plugin?: MPlugin
}
}
}
--- /dev/null
+import { AccountBlocklistModel } from '../../../models/account/account-blocklist'
+import { PickWith } from '../../utils'
+import { MAccountDefault } from './account'
+
+export type MAccountBlocklist = Omit<AccountBlocklistModel, 'ByAccount' | 'BlockedAccount'>
+
+export type MAccountBlocklistId = Pick<AccountBlocklistModel, 'id'>
+
+export type MAccountBlocklistAccounts = MAccountBlocklist &
+ PickWith<AccountBlocklistModel, 'ByAccount', MAccountDefault> &
+ PickWith<AccountBlocklistModel, 'BlockedAccount', MAccountDefault>
--- /dev/null
+import { AccountModel } from '../../../models/account/account'
+import {
+ MActor,
+ MActorAccountChannelId,
+ MActorAPI,
+ MActorAudience,
+ MActorDefault,
+ MActorDefaultLight, MActorId,
+ MActorServer,
+ MActorSummary,
+ MActorUrl
+} from './actor'
+import { PickWith } from '../../utils'
+import { MAccountBlocklistId } from './account-blocklist'
+import { MChannelDefault } from '@server/typings/models'
+
+export type MAccountId = Pick<AccountModel, 'id'>
+export type MAccountIdActor = MAccountId &
+ PickWith<AccountModel, 'Actor', MActorAccountChannelId>
+export type MAccountIdActorId = MAccountId &
+ PickWith<AccountModel, 'Actor', MActorId>
+
+export type MAccount = Omit<AccountModel, 'Actor' | 'User' | 'Application' | 'VideoChannels' | 'VideoPlaylists' |
+ 'VideoComments' | 'BlockedAccounts'>
+
+// Default scope
+export type MAccountDefault = MAccount &
+ PickWith<AccountModel, 'Actor', MActorDefault>
+
+export type MAccountDefaultChannelDefault = MAccountDefault &
+ PickWith<AccountModel, 'VideoChannels', MChannelDefault[]>
+
+export type MAccountLight = MAccount &
+ PickWith<AccountModel, 'Actor', MActorDefaultLight>
+
+export type MAccountUserId = Pick<MAccount, 'userId'>
+
+export type MAccountActor = MAccount &
+ PickWith<AccountModel, 'Actor', MActor>
+export type MAccountServer = MAccountActor &
+ PickWith<AccountModel, 'Actor', MActorServer>
+
+export type MAccountActorDefault = MAccount &
+ PickWith<AccountModel, 'Actor', MActorDefault>
+
+export type MAccountSummary = Pick<MAccount, 'id' | 'name'> &
+ PickWith<AccountModel, 'Actor', MActorSummary>
+
+export type MAccountBlocks = MAccountSummary &
+ PickWith<AccountModel, 'BlockedAccounts', MAccountBlocklistId[]>
+
+export type MAccountAPI = MAccountDefault &
+ PickWith<AccountModel, 'Actor', MActorAPI>
+
+export type MAccountUrl = PickWith<AccountModel, 'Actor', MActorUrl>
+export type MAccountAudience = PickWith<AccountModel, 'Actor', MActorAudience>
--- /dev/null
+import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
+import { MActor, MActorAccountChannel, MActorChannel, MActorChannelAccount, MActorDefault, MActorHost, MActorUsername } from './actor'
+import { PickWith } from '../../utils'
+
+export type MActorFollow = Omit<ActorFollowModel, 'ActorFollower' | 'ActorFollowing'>
+
+export type MActorFollowActors = MActorFollow &
+ PickWith<ActorFollowModel, 'ActorFollower', MActor> &
+ PickWith<ActorFollowModel, 'ActorFollowing', MActor>
+
+export type MActorFollowActorsDefault = MActorFollow &
+ PickWith<ActorFollowModel, 'ActorFollower', MActorDefault> &
+ PickWith<ActorFollowModel, 'ActorFollowing', MActorDefault>
+
+export type MActorFollowActorsDefaultSubscription = MActorFollow &
+ PickWith<ActorFollowModel, 'ActorFollower', MActorDefault> &
+ PickWith<ActorFollowModel, 'ActorFollowing', MActorDefault & MActorChannel>
+
+export type MActorFollowFull = MActorFollow &
+ PickWith<ActorFollowModel, 'ActorFollower', MActorAccountChannel> &
+ PickWith<ActorFollowModel, 'ActorFollowing', MActorAccountChannel>
+
+export type MActorFollowFollowingHost = MActorFollow &
+ PickWith<ActorFollowModel, 'ActorFollowing', MActorUsername & MActorHost>
+
+export type MActorFollowSubscriptions = MActorFollow &
+ PickWith<ActorFollowModel, 'ActorFollowing', MActorChannelAccount>
--- /dev/null
+import { ActorModel } from '../../../models/activitypub/actor'
+import { PickWith } from '../../utils'
+import { MAccount, MAccountActorDefault, MAccountId, MAccountIdActor } from './account'
+import { MServerHost, MServerHostBlocks, MServer } from '../server'
+import { MAvatar } from './avatar'
+import { MChannel, MChannelAccountActor, MChannelActorAccountDefault, MChannelId, MChannelIdActor } from '../video'
+
+export type MActor = Omit<ActorModel, 'Account' | 'VideoChannel' | 'ActorFollowing' | 'Avatar' | 'ActorFollowers' | 'Server'>
+
+export type MActorUrl = Pick<MActor, 'url'>
+export type MActorId = Pick<MActor, 'id'>
+export type MActorUsername = Pick<MActor, 'preferredUsername'>
+export type MActorHost = PickWith<ActorModel, 'Server', MServerHost>
+
+export type MActorFollowersUrl = Pick<MActor, 'followersUrl'>
+export type MActorAudience = MActorUrl & MActorFollowersUrl
+
+export type MActorLight = Omit<MActor, 'privateKey' | 'privateKey'>
+
+export type MActorDefaultLight = MActorLight &
+ MActorHost &
+ PickWith<ActorModel, 'Avatar', MAvatar>
+
+export type MActorAccountId = MActor &
+ PickWith<ActorModel, 'Account', MAccountId>
+export type MActorAccountIdActor = MActor &
+ PickWith<ActorModel, 'Account', MAccountIdActor>
+
+export type MActorChannelId = MActor &
+ PickWith<ActorModel, 'VideoChannel', MChannelId>
+export type MActorChannelIdActor = MActor &
+ PickWith<ActorModel, 'VideoChannel', MChannelIdActor>
+
+export type MActorAccountChannelId = MActorAccountId & MActorChannelId
+export type MActorAccountChannelIdActor = MActorAccountIdActor & MActorChannelIdActor
+
+export type MActorAccount = MActor &
+ PickWith<ActorModel, 'Account', MAccount>
+
+export type MActorChannel = MActor &
+ PickWith<ActorModel, 'VideoChannel', MChannel>
+
+export type MActorAccountChannel = MActorAccount & MActorChannel
+
+export type MActorChannelAccount = MActor &
+ PickWith<ActorModel, 'VideoChannel', MChannelAccountActor>
+
+export type MActorServer = MActor &
+ PickWith<ActorModel, 'Server', MServer>
+
+export type MActorDefault = MActorServer &
+ PickWith<ActorModel, 'Avatar', MAvatar>
+
+export type MActorFull = MActorDefault &
+ PickWith<ActorModel, 'Account', MAccount> &
+ PickWith<ActorModel, 'VideoChannel', MChannelAccountActor>
+
+export type MActorFullActor = MActorDefault &
+ PickWith<ActorModel, 'Account', MAccountActorDefault> &
+ PickWith<ActorModel, 'VideoChannel', MChannelActorAccountDefault>
+
+export type MActorSummary = Pick<MActor, 'id' | 'preferredUsername' | 'url' | 'serverId' | 'avatarId'> &
+ MActorHost &
+ PickWith<ActorModel, 'Avatar', MAvatar>
+
+export type MActorSummaryBlocks = Omit<MActorSummary, 'Server'> &
+ PickWith<ActorModel, 'Server', MServerHostBlocks>
+
+export type MActorFollowerException = Pick<ActorModel, 'sharedInboxUrl' | 'inboxUrl'>
+
+export type MActorAPI = Omit<MActorDefault, 'publicKey' | 'privateKey' | 'inboxUrl' | 'outboxUrl' | 'sharedInboxUrl' |
+ 'followersUrl' | 'followingUrl' | 'url' | 'createdAt' | 'updatedAt'>
+
+export type MActorSignature = MActorAccountChannelId
--- /dev/null
+import { AvatarModel } from '../../../models/avatar/avatar'
+
+export type MAvatar = AvatarModel
--- /dev/null
+export * from './account'
+export * from './account-blocklist'
+export * from './actor'
+export * from './actor-follow'
+export * from './avatar'
+++ /dev/null
-import { ActorFollowModel } from '../../models/activitypub/actor-follow'
-import { ActorModelOnly } from './actor'
-
-export type ActorFollowModelOnly = Omit<ActorFollowModel, 'ActorFollower' | 'ActorFollowing'>
-export type ActorFollowModelLight = ActorFollowModelOnly & {
- ActorFollower: ActorModelOnly
- ActorFollowing: ActorModelOnly
-}
+++ /dev/null
-import { ActorModel } from '../../models/activitypub/actor'
-import { VideoChannelModel } from '../../models/video/video-channel'
-import { AccountModel } from '../../models/account/account'
-import { FunctionProperties } from '../utils'
-
-export type VideoChannelModelId = FunctionProperties<VideoChannelModel>
-export type AccountModelId = FunctionProperties<AccountModel> | Pick<AccountModel, 'id'>
-
-export type VideoChannelModelIdActor = VideoChannelModelId & Pick<VideoChannelModel, 'Actor'>
-export type AccountModelIdActor = AccountModelId & Pick<AccountModel, 'Actor'>
-
-export type ActorModelUrl = Pick<ActorModel, 'url'>
-export type ActorModelOnly = Omit<ActorModel, 'Account' | 'VideoChannel' | 'ActorFollowing' | 'Avatar' | 'ActorFollowers' | 'Server'>
-export type ActorModelId = Pick<ActorModelOnly, 'id'>
-
-export type SignatureActorModel = ActorModelOnly & {
- VideoChannel: VideoChannelModelIdActor
-
- Account: AccountModelIdActor
-}
-
-export type ActorFollowerException = Pick<ActorModel, 'sharedInboxUrl' | 'inboxUrl'>
-export * from './actor'
+export * from './account'
+export * from './server'
+export * from './user'
+export * from './video'
--- /dev/null
+import { OAuthClientModel } from '@server/models/oauth/oauth-client'
+
+export type MOAuthClient = Omit<OAuthClientModel, 'OAuthTokens'>
--- /dev/null
+import { OAuthTokenModel } from '@server/models/oauth/oauth-token'
+import { PickWith } from '@server/typings/utils'
+import { MUserAccountUrl } from '@server/typings/models'
+
+export type MOAuthToken = Omit<OAuthTokenModel, 'User' | 'OAuthClients'>
+
+export type MOAuthTokenUser = MOAuthToken &
+ PickWith<OAuthTokenModel, 'User', MUserAccountUrl> &
+ { user?: MUserAccountUrl }
--- /dev/null
+export * from './plugin'
+export * from './server'
+export * from './server-blocklist'
--- /dev/null
+import { PluginModel } from '@server/models/server/plugin'
+
+export type MPlugin = PluginModel
--- /dev/null
+import { ServerBlocklistModel } from '@server/models/server/server-blocklist'
+import { PickWith } from '@server/typings/utils'
+import { MAccountDefault, MServer } from '@server/typings/models'
+
+export type MServerBlocklist = Omit<ServerBlocklistModel, 'ByAccount' | 'BlockedServer'>
+
+export type MServerBlocklistAccountServer = MServerBlocklist &
+ PickWith<ServerBlocklistModel, 'ByAccount', MAccountDefault> &
+ PickWith<ServerBlocklistModel, 'BlockedServer', MServer>
--- /dev/null
+import { ServerModel } from '../../../models/server/server'
+import { PickWith } from '../../utils'
+import { MAccountBlocklistId } from '../account'
+
+export type MServer = Omit<ServerModel, 'Actors' | 'BlockedByAccounts'>
+
+export type MServerHost = Pick<MServer, 'host'>
+
+export type MServerHostBlocks = MServerHost &
+ PickWith<ServerModel, 'BlockedByAccounts', MAccountBlocklistId[]>
--- /dev/null
+export * from './user'
+export * from './user-notification'
+export * from './user-video-history'
--- /dev/null
+import { UserNotificationSettingModel } from '@server/models/account/user-notification-setting'
+
+export type MNotificationSetting = Omit<UserNotificationSettingModel, 'User'>
--- /dev/null
+import { UserNotificationModel } from '../../../models/account/user-notification'
+import { PickWith } from '../../utils'
+import { VideoModel } from '../../../models/video/video'
+import { ActorModel } from '../../../models/activitypub/actor'
+import { ServerModel } from '../../../models/server/server'
+import { AvatarModel } from '../../../models/avatar/avatar'
+import { VideoChannelModel } from '../../../models/video/video-channel'
+import { AccountModel } from '../../../models/account/account'
+import { VideoCommentModel } from '../../../models/video/video-comment'
+import { VideoAbuseModel } from '../../../models/video/video-abuse'
+import { VideoBlacklistModel } from '../../../models/video/video-blacklist'
+import { VideoImportModel } from '../../../models/video/video-import'
+import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
+
+export namespace UserNotificationIncludes {
+ export type VideoInclude = Pick<VideoModel, 'id' | 'uuid' | 'name'>
+ export type VideoIncludeChannel = VideoInclude &
+ PickWith<VideoModel, 'VideoChannel', VideoChannelIncludeActor>
+
+ export type ActorInclude = Pick<ActorModel, 'preferredUsername' | 'getHost'> &
+ PickWith<ActorModel, 'Avatar', Pick<AvatarModel, 'filename' | 'getStaticPath'>> &
+ PickWith<ActorModel, 'Server', Pick<ServerModel, 'host'>>
+
+ export type VideoChannelInclude = Pick<VideoChannelModel, 'id' | 'name' | 'getDisplayName'>
+ export type VideoChannelIncludeActor = VideoChannelInclude &
+ PickWith<VideoChannelModel, 'Actor', ActorInclude>
+
+ export type AccountInclude = Pick<AccountModel, 'id' | 'name' | 'getDisplayName'>
+ export type AccountIncludeActor = AccountInclude &
+ PickWith<AccountModel, 'Actor', ActorInclude>
+
+ export type VideoCommentInclude = Pick<VideoCommentModel, 'id' | 'originCommentId' | 'getThreadId'> &
+ PickWith<VideoCommentModel, 'Account', AccountIncludeActor> &
+ PickWith<VideoCommentModel, 'Video', VideoInclude>
+
+ export type VideoAbuseInclude = Pick<VideoAbuseModel, 'id'> &
+ PickWith<VideoAbuseModel, 'Video', VideoInclude>
+
+ export type VideoBlacklistInclude = Pick<VideoBlacklistModel, 'id'> &
+ PickWith<VideoAbuseModel, 'Video', VideoInclude>
+
+ export type VideoImportInclude = Pick<VideoImportModel, 'id' | 'magnetUri' | 'targetUrl' | 'torrentName'> &
+ PickWith<VideoImportModel, 'Video', VideoInclude>
+
+ export type ActorFollower = Pick<ActorModel, 'preferredUsername' | 'getHost'> &
+ PickWith<ActorModel, 'Account', AccountInclude> &
+ PickWith<ActorModel, 'Avatar', Pick<AvatarModel, 'filename' | 'getStaticPath'>> &
+ PickWith<ActorModel, 'Server', Pick<ServerModel, 'host'>>
+
+ export type ActorFollowing = Pick<ActorModel, 'preferredUsername'> &
+ PickWith<ActorModel, 'VideoChannel', VideoChannelInclude> &
+ PickWith<ActorModel, 'Account', AccountInclude>
+
+ export type ActorFollowInclude = Pick<ActorFollowModel, 'id' | 'state'> &
+ PickWith<ActorFollowModel, 'ActorFollower', ActorFollower> &
+ PickWith<ActorFollowModel, 'ActorFollowing', ActorFollowing>
+}
+
+export type UserNotificationModelOnly = Omit<UserNotificationModel, 'User' | 'Video' | 'Comment' | 'VideoAbuse' | 'VideoBlacklist' |
+ 'VideoImport' | 'Account' | 'ActorFollow'>
+
+export type UserNotificationModelForApi = UserNotificationModelOnly &
+ PickWith<UserNotificationModel, 'Video', UserNotificationIncludes.VideoIncludeChannel> &
+ PickWith<UserNotificationModel, 'Comment', UserNotificationIncludes.VideoCommentInclude> &
+ PickWith<UserNotificationModel, 'VideoAbuse', UserNotificationIncludes.VideoAbuseInclude> &
+ PickWith<UserNotificationModel, 'VideoBlacklist', UserNotificationIncludes.VideoBlacklistInclude> &
+ PickWith<UserNotificationModel, 'VideoImport', UserNotificationIncludes.VideoImportInclude> &
+ PickWith<UserNotificationModel, 'ActorFollow', UserNotificationIncludes.ActorFollowInclude> &
+ PickWith<UserNotificationModel, 'Account', UserNotificationIncludes.AccountIncludeActor>
--- /dev/null
+import { UserVideoHistoryModel } from '../../../models/account/user-video-history'
+
+export type MUserVideoHistory = Omit<UserVideoHistoryModel, 'Video' | 'User'>
+
+export type MUserVideoHistoryTime = Pick<MUserVideoHistory, 'currentTime'>
--- /dev/null
+import { UserModel } from '../../../models/account/user'
+import { PickWith } from '../../utils'
+import { MAccount, MAccountDefault, MAccountDefaultChannelDefault, MAccountId, MAccountIdActorId, MAccountUrl } from '../account'
+import { MNotificationSetting } from './user-notification-setting'
+
+export type MUser = Omit<UserModel, 'Account' | 'NotificationSetting' | 'VideoImports' | 'OAuthTokens'>
+
+export type MUserId = Pick<UserModel, 'id'>
+
+export type MUserWithNotificationSetting = MUser &
+ PickWith<UserModel, 'NotificationSetting', MNotificationSetting>
+
+export type MUserAccountDefault = MUser &
+ PickWith<UserModel, 'Account', MAccountDefault>
+
+export type MUserAccount = MUser &
+ PickWith<UserModel, 'Account', MAccount>
+
+export type MUserAccountId = MUser &
+ PickWith<UserModel, 'Account', MAccountId>
+
+export type MUserNotifSettingAccount = MUserWithNotificationSetting & MUserAccount
+
+export type MUserDefault = MUser &
+ MUserWithNotificationSetting &
+ MUserAccountDefault
+
+export type MUserChannel = MUserWithNotificationSetting &
+ PickWith<UserModel, 'Account', MAccountDefaultChannelDefault>
+
+export type MUserAccountUrl = MUser &
+ PickWith<UserModel, 'Account', MAccountUrl & MAccountIdActorId>
+++ /dev/null
-import { VideoShareModel } from '../../models/video/video-share'
-
-export type VideoShareModelOnly = Omit<VideoShareModel, 'Actor' | 'Video'>
--- /dev/null
+export * from './schedule-video-update'
+export * from './tag'
+export * from './thumbnail'
+export * from './video'
+export * from './video-abuse'
+export * from './video-blacklist'
+export * from './video-caption'
+export * from './video-channels'
+export * from './video-comment'
+export * from './video-file'
+export * from './video-playlist'
+export * from './video-redundancy'
+export * from './video-share'
+export * from './video-streaming-playlist'
--- /dev/null
+import { ScheduleVideoUpdateModel } from '../../../models/video/schedule-video-update'
+
+export type MScheduleVideoUpdate = Omit<ScheduleVideoUpdateModel, 'Video'>
--- /dev/null
+import { TagModel } from '../../../models/video/tag'
+
+export type MTag = Omit<TagModel, 'Videos'>
--- /dev/null
+import { ThumbnailModel } from '../../../models/video/thumbnail'
+
+export type MThumbnail = Omit<ThumbnailModel, 'Video' | 'VideoPlaylist'>
--- /dev/null
+import { VideoAbuseModel } from '../../../models/video/video-abuse'
+import { PickWith } from '../../utils'
+import { MVideo } from './video'
+import { MAccountDefault } from '../account'
+
+export type MVideoAbuse = Omit<VideoAbuseModel, 'Account' | 'Video' | 'toActivityPubObject'>
+
+export type MVideoAbuseId = Pick<VideoAbuseModel, 'id'>
+
+export type MVideoAbuseVideo = MVideoAbuse &
+ Pick<VideoAbuseModel, 'toActivityPubObject'> &
+ PickWith<VideoAbuseModel, 'Video', MVideo>
+
+export type MVideoAbuseAccountVideo = MVideoAbuseVideo &
+ PickWith<VideoAbuseModel, 'Account', MAccountDefault>
--- /dev/null
+import { VideoBlacklistModel } from '../../../models/video/video-blacklist'
+import { PickWith } from '@server/typings/utils'
+import { MVideo } from '@server/typings/models'
+
+export type MVideoBlacklist = Omit<VideoBlacklistModel, 'Video'>
+
+export type MVideoBlacklistLight = Pick<MVideoBlacklist, 'id' | 'reason' | 'unfederated'>
+export type MVideoBlacklistUnfederated = Pick<MVideoBlacklist, 'unfederated'>
+
+export type MVideoBlacklistVideo = MVideoBlacklist &
+ PickWith<VideoBlacklistModel, 'Video', MVideo>
--- /dev/null
+import { VideoCaptionModel } from '../../../models/video/video-caption'
+import { PickWith } from '@server/typings/utils'
+import { VideoModel } from '@server/models/video/video'
+
+export type MVideoCaption = Omit<VideoCaptionModel, 'Video'>
+
+export type MVideoCaptionLanguage = Pick<MVideoCaption, 'language'>
+
+export type MVideoCaptionVideo = MVideoCaption &
+ PickWith<VideoCaptionModel, 'Video', Pick<VideoModel, 'id' | 'remote' | 'uuid'>>
--- /dev/null
+import { VideoChangeOwnershipModel } from '@server/models/video/video-change-ownership'
+import { PickWith } from '@server/typings/utils'
+import { MAccountDefault, MVideoWithFileThumbnail } from '@server/typings/models'
+
+export type MVideoChangeOwnership = Omit<VideoChangeOwnershipModel, 'Initiator' | 'NextOwner' | 'Video'>
+
+export type MVideoChangeOwnershipFull = MVideoChangeOwnership &
+ PickWith<VideoChangeOwnershipModel, 'Initiator', MAccountDefault> &
+ PickWith<VideoChangeOwnershipModel, 'NextOwner', MAccountDefault> &
+ PickWith<VideoChangeOwnershipModel, 'Video', MVideoWithFileThumbnail>
--- /dev/null
+import { FunctionProperties, PickWith } from '../../utils'
+import { VideoChannelModel } from '../../../models/video/video-channel'
+import {
+ MAccountActor,
+ MAccountAPI,
+ MAccountBlocks,
+ MAccountDefault,
+ MAccountLight,
+ MAccountUserId,
+ MActor,
+ MActorAccountChannelId,
+ MActorAPI,
+ MActorDefault,
+ MActorDefaultLight, MActorLight,
+ MActorSummary
+} from '../account'
+import { MVideo } from './video'
+
+export type MChannelId = FunctionProperties<VideoChannelModel>
+export type MChannelIdActor = MChannelId &
+ PickWith<VideoChannelModel, 'Actor', MActorAccountChannelId>
+
+export type MChannel = Omit<VideoChannelModel, 'Actor' | 'Account' | 'Videos' | 'VideoPlaylists'>
+
+export type MChannelUserId = Pick<MChannel, 'accountId'> &
+ PickWith<VideoChannelModel, 'Account', MAccountUserId>
+
+// Default scope
+export type MChannelDefault = MChannel &
+ PickWith<VideoChannelModel, 'Actor', MActorDefault>
+
+export type MChannelLight = MChannel &
+ PickWith<VideoChannelModel, 'Actor', MActorDefaultLight>
+
+export type MChannelAccountLight = MChannel &
+ PickWith<VideoChannelModel, 'Actor', MActorDefaultLight> &
+ PickWith<VideoChannelModel, 'Account', MAccountLight>
+
+export type MChannelSummary = Pick<MChannel, 'id' | 'name' | 'description' | 'actorId'> &
+ PickWith<VideoChannelModel, 'Actor', MActorSummary>
+
+export type MChannelSummaryAccount = MChannelSummary &
+ PickWith<VideoChannelModel, 'Account', MAccountBlocks>
+
+export type MChannelAPI = MChannel &
+ PickWith<VideoChannelModel, 'Actor', MActorAPI> &
+ PickWith<VideoChannelModel, 'Account', MAccountAPI>
+
+export type MChannelAccountActor = MChannel &
+ PickWith<VideoChannelModel, 'Account', MAccountActor>
+export type MChannelAccountDefault = MChannelActor &
+ PickWith<VideoChannelModel, 'Account', MAccountDefault>
+
+export type MChannelVideos = MChannel &
+ PickWith<VideoChannelModel, 'Videos', MVideo[]>
+
+export type MChannelActor = MChannel &
+ PickWith<VideoChannelModel, 'Actor', MActor>
+export type MChannelActorLight = MChannel &
+ PickWith<VideoChannelModel, 'Actor', MActorLight>
+export type MChannelActorDefault = MChannel &
+ PickWith<VideoChannelModel, 'Actor', MActorDefault>
+
+export type MChannelActorAccountActor = MChannelAccountActor & MChannelActor
+
+export type MChannelActorAccountDefault = MChannel &
+ PickWith<VideoChannelModel, 'Actor', MActorDefault> &
+ PickWith<VideoChannelModel, 'Account', MAccountDefault>
+
+export type MChannelActorAccountDefaultVideos = MChannelActorAccountDefault & MChannelVideos
--- /dev/null
+import { VideoCommentModel } from '../../../models/video/video-comment'
+import { PickWith } from '../../utils'
+import { MAccountDefault } from '../account'
+import { MVideoAccountDefault, MVideoAccountLight, MVideoFeed, MVideoIdUrl } from './video'
+
+export type MComment = Omit<VideoCommentModel, 'OriginVideoComment' | 'InReplyToVideoComment' | 'Video' | 'Account'>
+export type MCommentId = Pick<MComment, 'id'>
+
+export type MCommentAPI = MComment & { totalReplies: number }
+
+export type MCommentOwner = MComment &
+ PickWith<VideoCommentModel, 'Account', MAccountDefault>
+
+export type MCommentVideo = MComment &
+ PickWith<VideoCommentModel, 'Video', MVideoAccountLight>
+
+export type MCommentReply = MComment &
+ PickWith<VideoCommentModel, 'InReplyToVideoComment', MComment>
+
+export type MCommentOwnerReply = MCommentOwner & MCommentReply
+export type MCommentOwnerVideo = MCommentOwner & MCommentVideo
+export type MCommentReplyVideo = MCommentReply & MCommentVideo
+export type MCommentOwnerVideoReply = MCommentOwnerVideo & MCommentReply
+
+export type MCommentOwnerReplyVideoLight = MCommentOwnerReply &
+ PickWith<VideoCommentModel, 'Video', MVideoIdUrl>
+
+export type MCommentOwnerVideoFeed = MCommentOwner &
+ PickWith<VideoCommentModel, 'Video', MVideoFeed>
--- /dev/null
+import { VideoFileModel } from '../../../models/video/video-file'
+import { PickWith, PickWithOpt } from '../../utils'
+import { MVideo, MVideoUUID } from './video'
+import { MVideoRedundancyFileUrl } from './video-redundancy'
+
+export type MVideoFile = Omit<VideoFileModel, 'Video' | 'RedundancyVideos'>
+
+export type MVideoFileVideo = MVideoFile &
+ PickWith<VideoFileModel, 'Video', MVideo>
+
+export type MVideoFileVideoUUID = MVideoFile &
+ PickWith<VideoFileModel, 'Video', MVideoUUID>
+
+export type MVideoFileRedundanciesOpt = MVideoFile &
+ PickWithOpt<VideoFileModel, 'RedundancyVideos', MVideoRedundancyFileUrl[]>
--- /dev/null
+import { VideoImportModel } from '@server/models/video/video-import'
+import { PickWith } from '@server/typings/utils'
+import { MUser, MVideo, MVideoAccountLight, MVideoTag, MVideoThumbnail, MVideoWithFile } from '@server/typings/models'
+
+export type MVideoImport = Omit<VideoImportModel, 'User' | 'Video'>
+
+export type MVideoImportDefault = MVideoImport &
+ PickWith<VideoImportModel, 'User', MUser> &
+ PickWith<VideoImportModel, 'Video', MVideoTag & MVideoAccountLight & MVideoThumbnail>
+
+export type MVideoImportDefaultFiles = MVideoImportDefault &
+ PickWith<VideoImportModel, 'Video', MVideoTag & MVideoAccountLight & MVideoThumbnail & MVideoWithFile>
+
+export type MVideoImportVideo = MVideoImport &
+ PickWith<VideoImportModel, 'Video', MVideo>
--- /dev/null
+import { VideoPlaylistElementModel } from '@server/models/video/video-playlist-element'
+import { PickWith } from '@server/typings/utils'
+import { MVideoPlaylistPrivacy, MVideoThumbnail, MVideoUrl } from '@server/typings/models'
+
+export type MVideoPlaylistElement = Omit<VideoPlaylistElementModel, 'VideoPlaylist' | 'Video'>
+export type MVideoPlaylistElementId = Pick<MVideoPlaylistElement, 'id'>
+
+export type MVideoPlaylistElementLight = Pick<MVideoPlaylistElement, 'id' | 'videoId' | 'startTimestamp' | 'stopTimestamp'>
+
+export type MVideoPlaylistVideoThumbnail = MVideoPlaylistElement &
+ PickWith<VideoPlaylistElementModel, 'Video', MVideoThumbnail>
+
+export type MVideoPlaylistAP = MVideoPlaylistElement &
+ PickWith<VideoPlaylistElementModel, 'Video', MVideoUrl> &
+ PickWith<VideoPlaylistElementModel, 'VideoPlaylist', MVideoPlaylistPrivacy>
--- /dev/null
+import { VideoPlaylistModel } from '../../../models/video/video-playlist'
+import { PickWith } from '../../utils'
+import { MAccount, MAccountDefault, MAccountSummary } from '../account'
+import { MThumbnail } from './thumbnail'
+import { MChannelDefault, MChannelSummary } from './video-channels'
+import { MVideoPlaylistElementLight } from '@server/typings/models/video/video-playlist-element'
+
+export type MVideoPlaylist = Omit<VideoPlaylistModel, 'OwnerAccount' | 'VideoChannel' | 'VideoPlaylistElements' | 'Thumbnail'>
+export type MVideoPlaylistId = Pick<MVideoPlaylist, 'id'>
+export type MVideoPlaylistPrivacy = Pick<MVideoPlaylist, 'privacy'>
+
+export type MVideoPlaylistWithElements = MVideoPlaylist &
+ PickWith<VideoPlaylistModel, 'VideoPlaylistElements', MVideoPlaylistElementLight[]>
+export type MVideoPlaylistIdWithElements = MVideoPlaylistId & MVideoPlaylistWithElements
+
+export type MVideoPlaylistUUID = Pick<MVideoPlaylist, 'uuid'>
+
+export type MVideoPlaylistOwner = MVideoPlaylist &
+ PickWith<VideoPlaylistModel, 'OwnerAccount', MAccount>
+
+export type MVideoPlaylistOwnerDefault = MVideoPlaylist &
+ PickWith<VideoPlaylistModel, 'OwnerAccount', MAccountDefault>
+
+export type MVideoPlaylistThumbnail = MVideoPlaylist &
+ PickWith<VideoPlaylistModel, 'Thumbnail', MThumbnail>
+
+export type MVideoPlaylistAccountThumbnail = MVideoPlaylistOwnerDefault &
+ PickWith<VideoPlaylistModel, 'Thumbnail', MThumbnail>
+
+export type MVideoPlaylistAccountChannelSummary = MVideoPlaylist &
+ PickWith<VideoPlaylistModel, 'OwnerAccount', MAccountSummary> &
+ PickWith<VideoPlaylistModel, 'VideoChannel', MChannelSummary>
+
+export type MVideoPlaylistAccountChannelDefault = MVideoPlaylist &
+ PickWith<VideoPlaylistModel, 'OwnerAccount', MAccountDefault> &
+ PickWith<VideoPlaylistModel, 'VideoChannel', MChannelDefault>
+
+export type MVideoPlaylistVideosLength = MVideoPlaylist & { videosLength: number }
+
+export type MVideoPlaylistFullSummary = MVideoPlaylistAccountChannelSummary & MVideoPlaylistThumbnail
+
+export type MVideoPlaylistFull = MVideoPlaylist & MVideoPlaylistThumbnail & MVideoPlaylistAccountChannelDefault
--- /dev/null
+import { AccountVideoRateModel } from '@server/models/account/account-video-rate'
+import { PickWith } from '@server/typings/utils'
+import { MAccountAudience, MAccountUrl, MVideo } from '..'
+
+export type MAccountVideoRate = Omit<AccountVideoRateModel, 'Video' | 'Account'>
+
+export type MAccountVideoRateAccountUrl = MAccountVideoRate &
+ PickWith<AccountVideoRateModel, 'Account', MAccountUrl>
+
+export type MAccountVideoRateAccountVideo = MAccountVideoRate &
+ PickWith<AccountVideoRateModel, 'Account', MAccountAudience> &
+ PickWith<AccountVideoRateModel, 'Video', MVideo>
--- /dev/null
+import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy'
+import { PickWith } from '@server/typings/utils'
+import { MStreamingPlaylistVideo, MVideoFile, MVideoFileVideo } from '@server/typings/models'
+
+export type MVideoRedundancy = Omit<VideoRedundancyModel, 'VideoFile' | 'VideoStreamingPlaylist' | 'Actor'>
+
+export type MVideoRedundancyFileUrl = Pick<MVideoRedundancy, 'fileUrl'>
+
+export type MVideoRedundancyFile = MVideoRedundancy &
+ PickWith<VideoRedundancyModel, 'VideoFile', MVideoFile>
+
+export type MVideoRedundancyFileVideo = MVideoRedundancy &
+ PickWith<VideoRedundancyModel, 'VideoFile', MVideoFileVideo>
+
+export type MVideoRedundancyStreamingPlaylistVideo = MVideoRedundancy &
+ PickWith<VideoRedundancyModel, 'VideoStreamingPlaylist', MStreamingPlaylistVideo>
+
+export type MVideoRedundancyVideo = MVideoRedundancyFileVideo | MVideoRedundancyStreamingPlaylistVideo
--- /dev/null
+import { VideoShareModel } from '../../../models/video/video-share'
+import { PickWith } from '../../utils'
+import { MActorDefault } from '../account'
+import { MVideo } from './video'
+
+export type MVideoShare = Omit<VideoShareModel, 'Actor' | 'Video'>
+
+export type MVideoShareActor = MVideoShare &
+ PickWith<VideoShareModel, 'Actor', MActorDefault>
+
+export type MVideoShareFull = MVideoShareActor &
+ PickWith<VideoShareModel, 'Video', MVideo>
--- /dev/null
+import { VideoStreamingPlaylistModel } from '../../../models/video/video-streaming-playlist'
+import { PickWith } from '../../utils'
+import { MVideoRedundancyFileUrl } from './video-redundancy'
+import { MVideo } from '@server/typings/models'
+
+export type MStreamingPlaylist = Omit<VideoStreamingPlaylistModel, 'Video' | 'RedundancyVideos'>
+
+export type MStreamingPlaylistVideo = MStreamingPlaylist &
+ PickWith<VideoStreamingPlaylistModel, 'Video', MVideo>
+
+export type MStreamingPlaylistRedundancies = MStreamingPlaylist &
+ PickWith<VideoStreamingPlaylistModel, 'RedundancyVideos', MVideoRedundancyFileUrl[]>
--- /dev/null
+import { VideoModel } from '../../../models/video/video'
+import { PickWith, PickWithOpt } from '../../utils'
+import { MChannelAccountLight, MChannelActor, MChannelActorAccountDefault, MChannelUserId } from './video-channels'
+import { MTag } from './tag'
+import { MVideoCaptionLanguage } from './video-caption'
+import { MStreamingPlaylist, MStreamingPlaylistRedundancies } from './video-streaming-playlist'
+import { MVideoFile, MVideoFileRedundanciesOpt } from './video-file'
+import { MThumbnail } from './thumbnail'
+import { MVideoBlacklistLight, MVideoBlacklistUnfederated } from './video-blacklist'
+import { MScheduleVideoUpdate } from './schedule-video-update'
+import { MUserVideoHistoryTime } from '../user/user-video-history'
+
+export type MVideo = Omit<VideoModel, 'VideoChannel' | 'Tags' | 'Thumbnails' | 'VideoPlaylistElements' | 'VideoAbuses' |
+ 'VideoFiles' | 'VideoStreamingPlaylists' | 'VideoShares' | 'AccountVideoRates' | 'VideoComments' | 'VideoViews' | 'UserVideoHistories' |
+ 'ScheduleVideoUpdate' | 'VideoBlacklist' | 'VideoImport' | 'VideoCaptions'>
+
+export type MVideoId = Pick<MVideo, 'id'>
+export type MVideoUrl = Pick<MVideo, 'url'>
+export type MVideoUUID = Pick<MVideo, 'uuid'>
+
+export type MVideoIdUrl = MVideoId & MVideoUrl
+export type MVideoFeed = Pick<MVideo, 'name' | 'uuid'>
+
+export type MVideoWithFile = MVideo &
+ PickWith<VideoModel, 'VideoFiles', MVideoFile[]>
+
+export type MVideoThumbnail = MVideo &
+ PickWith<VideoModel, 'Thumbnails', MThumbnail[]>
+export type MVideoIdThumbnail = MVideoThumbnail & MVideoId
+
+export type MVideoTag = MVideo &
+ PickWith<VideoModel, 'Tags', MTag[]>
+
+export type MVideoWithSchedule = MVideo &
+ PickWithOpt<VideoModel, 'ScheduleVideoUpdate', MScheduleVideoUpdate>
+
+export type MVideoWithFileThumbnail = MVideoWithFile & MVideoThumbnail
+
+export type MVideoUser = MVideo &
+ PickWith<VideoModel, 'VideoChannel', MChannelUserId>
+
+export type MVideoWithCaptions = MVideo &
+ PickWith<VideoModel, 'VideoCaptions', MVideoCaptionLanguage[]>
+
+export type MVideoWithBlacklistLight = MVideo &
+ PickWith<VideoModel, 'VideoBlacklist', MVideoBlacklistLight>
+
+export type MVideoAccountLight = MVideo &
+ PickWith<VideoModel, 'VideoChannel', MChannelAccountLight>
+
+export type MVideoWithRights = MVideoWithBlacklistLight & MVideoThumbnail & MVideoUser
+
+export type MVideoWithStreamingPlaylist = MVideo &
+ PickWith<VideoModel, 'VideoStreamingPlaylists', MStreamingPlaylist[]>
+
+export type MVideoWithAllFiles = MVideoWithFileThumbnail & MVideoWithStreamingPlaylist
+
+export type MVideoAccountAllFiles = MVideoWithAllFiles & MVideoAccountLight & MVideoWithBlacklistLight
+export type MVideoAccountAllFilesCaptions = MVideoAccountAllFiles & MVideoWithCaptions
+
+export type MVideoUserHistory = MVideo &
+ PickWith<VideoModel, 'UserVideoHistories', MUserVideoHistoryTime[]>
+
+export type MVideoWithBlacklistThumbnailScheduled = MVideoWithSchedule & MVideoWithBlacklistLight & MVideoWithFileThumbnail
+
+export type MVideoAccountDefault = MVideo &
+ PickWith<VideoModel, 'VideoChannel', MChannelActorAccountDefault>
+
+export type MVideoThumbnailAccountDefault = MVideoThumbnail &
+ PickWith<VideoModel, 'VideoChannel', MChannelActorAccountDefault>
+
+export type MVideoWithChannelActor = MVideo &
+ PickWith<VideoModel, 'VideoChannel', MChannelActor>
+
+export type MVideoFullLight = MVideoThumbnail &
+ MVideoWithBlacklistLight &
+ MVideoTag &
+ MVideoAccountLight &
+ MVideoUserHistory &
+ MVideoWithFile &
+ MVideoWithSchedule &
+ MVideoWithStreamingPlaylist &
+ MVideoUserHistory
+
+export type MVideoAP = MVideo &
+ MVideoTag &
+ MVideoAccountLight &
+ MVideoWithStreamingPlaylist &
+ MVideoWithCaptions &
+ PickWith<VideoModel, 'VideoBlacklist', MVideoBlacklistUnfederated> &
+ PickWith<VideoModel, 'VideoFiles', MVideoFileRedundanciesOpt[]>
+
+export type MVideoAPWithoutCaption = Omit<MVideoAP, 'VideoCaptions'>
+
+export type MVideoDetails = MVideo &
+ MVideoWithBlacklistLight &
+ MVideoTag &
+ MVideoAccountLight &
+ MVideoWithSchedule &
+ MVideoThumbnail &
+ MVideoUserHistory &
+ PickWith<VideoModel, 'VideoStreamingPlaylists', MStreamingPlaylistRedundancies[]> &
+ PickWith<VideoModel, 'VideoFiles', MVideoFileRedundanciesOpt[]>
-export type FunctionPropertyNames<T> = { [K in keyof T]: T[K] extends Function ? K : never }[keyof T]
+export type FunctionPropertyNames<T> = {
+ [K in keyof T]: T[K] extends Function ? K : never
+}[keyof T]
export type FunctionProperties<T> = Pick<T, FunctionPropertyNames<T>>
+
+export type ValueOf <T, KT extends keyof T> = T[KT]
+
+export type PickWith<T, KT extends keyof T, V> = {
+ [P in KT]: T[P] extends V ? V : never
+}
+
+export type PickWithOpt<T, KT extends keyof T, V> = {
+ [P in KT]?: T[P] extends V ? V : never
+}
"es2016",
"es2017"
],
- "typeRoots": [ "node_modules/@types", "server/typings" ]
+ "typeRoots": [ "node_modules/@types", "server/typings" ],
+ "baseUrl": "./",
+ "paths": {
+ "@server/typings/*": [ "server/typings/*" ],
+ "@server/models/*": [ "server/models/*" ]
+ }
},
"exclude": [
"server/tools/",
- "client/node_modules",
"node_modules",
"dist",
"storage",