1
0
Fork 0

Stronger model typings

This commit is contained in:
Chocobozzz 2019-08-15 11:53:26 +02:00
parent 13176a07a9
commit 453e83ea5d
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
176 changed files with 2118 additions and 1133 deletions

View File

@ -16,7 +16,6 @@ import {
} 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'
@ -38,6 +37,7 @@ import { buildDislikeActivity } from '../../lib/activitypub/send/send-dislike'
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()
@ -148,7 +148,7 @@ activityPubClientRouter.get('/redundancy/streaming-playlists/:streamingPlaylistT
activityPubClientRouter.get('/video-playlists/:playlistId',
executeIfActivityPub,
asyncMiddleware(videoPlaylistsGetValidator),
asyncMiddleware(videoPlaylistsGetValidator('all')),
asyncMiddleware(videoPlaylistController)
)
activityPubClientRouter.get('/video-playlists/:playlistId/:videoId',
@ -208,18 +208,19 @@ function getAccountVideoRate (rateType: VideoRateType) {
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)
}
@ -231,13 +232,13 @@ async function videoAnnounceController (req: express.Request, res: express.Respo
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)
@ -252,21 +253,21 @@ async function videoAnnouncesController (req: express.Request, res: express.Resp
}
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)
@ -301,7 +302,7 @@ async function videoChannelFollowingController (req: express.Request, res: expre
}
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)
@ -337,7 +338,7 @@ async function videoRedundancyController (req: express.Request, res: express.Res
}
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)
@ -358,7 +359,7 @@ async function videoPlaylistElementController (req: express.Request, res: expres
// ---------------------------------------------------------------------------
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)
}
@ -366,7 +367,7 @@ async function actorFollowing (req: express.Request, actor: ActorModel) {
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)
}
@ -374,7 +375,7 @@ async function actorFollowers (req: express.Request, actor: ActorModel) {
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)
}
@ -382,7 +383,7 @@ async function actorPlaylists (req: express.Request, account: AccountModel) {
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 {

View File

@ -7,7 +7,7 @@ import { asyncMiddleware, checkSignature, localAccountValidator, localVideoChann
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()
@ -41,7 +41,8 @@ export {
// ---------------------------------------------------------------------------
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)

View File

@ -6,11 +6,9 @@ import { logger } from '../../helpers/logger'
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()
@ -45,14 +43,10 @@ async function outboxController (req: express.Request, res: express.Response) {
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)

View File

@ -19,6 +19,7 @@ import { getOrCreateActorAndServerAndModel, getOrCreateVideoAndAccountAndChannel
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()
@ -84,7 +85,7 @@ async function searchVideoChannelsDB (query: VideoChannelsSearchQuery, res: expr
}
async function searchVideoChannelURI (search: string, isWebfingerSearch: boolean, res: express.Response) {
let videoChannel: VideoChannelModel
let videoChannel: MChannelAccountDefault
let uri = search
if (isWebfingerSearch) {
@ -137,7 +138,7 @@ async function searchVideosDB (query: VideosSearchQuery, res: express.Response)
}
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)) {

View File

@ -48,6 +48,7 @@ import { CONFIG } from '../../../initializers/config'
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')
@ -359,7 +360,7 @@ function success (req: express.Request, res: express.Response) {
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

View File

@ -147,7 +147,7 @@ async function getUserVideoQuotaUsed (req: express.Request, res: express.Respons
}
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)

View File

@ -7,7 +7,6 @@ import {
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'

View File

@ -136,7 +136,7 @@ async function updateVideoChannelAvatar (req: express.Request, res: express.Resp
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)

View File

@ -40,7 +40,7 @@ import { JobQueue } from '../../lib/job-queue'
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 })
@ -58,7 +58,7 @@ videoPlaylistRouter.get('/',
)
videoPlaylistRouter.get('/:playlistId',
asyncMiddleware(videoPlaylistsGetValidator),
asyncMiddleware(videoPlaylistsGetValidator('summary')),
getVideoPlaylist
)
@ -83,7 +83,7 @@ videoPlaylistRouter.delete('/:playlistId',
)
videoPlaylistRouter.get('/:playlistId/videos',
asyncMiddleware(videoPlaylistsGetValidator),
asyncMiddleware(videoPlaylistsGetValidator('summary')),
paginationValidator,
setDefaultPagination,
optionalAuthenticate,
@ -140,7 +140,7 @@ async function listVideoPlaylists (req: express.Request, res: express.Response)
}
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 } })
@ -159,7 +159,7 @@ async function addVideoPlaylist (req: express.Request, res: express.Response) {
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
@ -175,8 +175,8 @@ async function addVideoPlaylist (req: express.Request, res: express.Response) {
? 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
@ -201,7 +201,7 @@ async function addVideoPlaylist (req: express.Request, res: express.Response) {
}
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
@ -275,7 +275,7 @@ async function updateVideoPlaylist (req: express.Request, res: express.Response)
}
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 })
@ -290,10 +290,10 @@ async function removeVideoPlaylist (req: express.Request, res: express.Response)
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({
@ -330,7 +330,7 @@ async function addVideoInPlaylist (req: express.Request, res: express.Response)
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 => {
@ -354,7 +354,7 @@ async function updateVideoPlaylistElement (req: express.Request, res: express.Re
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 => {
@ -381,7 +381,7 @@ async function removeVideoFromPlaylist (req: express.Request, res: express.Respo
}
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
@ -434,7 +434,7 @@ async function reorderVideosPlaylist (req: express.Request, res: express.Respons
}
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()
@ -453,7 +453,7 @@ async function getVideoPlaylistVideos (req: express.Request, res: express.Respon
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
@ -461,7 +461,7 @@ async function regeneratePlaylistThumbnail (videoPlaylist: VideoPlaylistModel) {
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)

View File

@ -21,6 +21,7 @@ import { VideoAbuseModel } from '../../../models/video/video-abuse'
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()
@ -94,10 +95,10 @@ async function deleteVideoAbuse (req: express.Request, res: express.Response) {
}
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 = {
@ -107,7 +108,7 @@ async function reportVideoAbuse (req: express.Request, res: express.Response) {
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

View File

@ -1,5 +1,5 @@
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 {
@ -11,15 +11,16 @@ 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()
@ -64,7 +65,7 @@ export {
// ---------------------------------------------------------------------------
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 = {
@ -74,7 +75,7 @@ async function addVideoToBlacklist (req: express.Request, res: express.Response)
type: VideoBlacklistType.MANUAL
}
const blacklist = await VideoBlacklistModel.create(toCreate)
const blacklist: MVideoBlacklistVideo = await VideoBlacklistModel.create(toCreate)
blacklist.Video = videoInstance
if (body.unfederate === true) {
@ -83,7 +84,7 @@ async function addVideoToBlacklist (req: express.Request, res: express.Response)
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()
}
@ -108,7 +109,7 @@ async function listBlacklist (req: express.Request, res: express.Response) {
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
@ -135,7 +136,7 @@ async function removeVideoFromBlacklistController (req: express.Request, res: ex
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()
}

View File

@ -10,6 +10,7 @@ import { federateVideoIfNeeded } from '../../../lib/activitypub'
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' ],
@ -46,19 +47,19 @@ export {
// ---------------------------------------------------------------------------
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
@ -75,7 +76,7 @@ async function addVideoCaption (req: express.Request, res: express.Response) {
}
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 => {

View File

@ -27,9 +27,6 @@ import { auditLoggerFactory, CommentAuditView, getAuditIdFromRes } from '../../.
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')
@ -75,7 +72,7 @@ export {
// ---------------------------------------------------------------------------
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>
@ -86,7 +83,7 @@ async function listVideoThreads (req: express.Request, res: express.Response) {
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(
@ -105,7 +102,7 @@ async function listVideoThreads (req: express.Request, res: express.Response) {
}
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>
@ -141,7 +138,7 @@ async function addVideoCommentThread (req: express.Request, res: express.Respons
return createVideoComment({
text: videoCommentInfo.text,
inReplyToComment: null,
video: res.locals.video,
video: res.locals.videoAll,
account
}, t)
})
@ -164,8 +161,8 @@ async function addVideoCommentReply (req: express.Request, res: express.Response
return createVideoComment({
text: videoCommentInfo.text,
inReplyToComment: res.locals.videoComment,
video: res.locals.video,
inReplyToComment: res.locals.videoCommentFull,
video: res.locals.videoAll,
account
}, t)
})
@ -179,7 +176,7 @@ async function addVideoCommentReply (req: express.Request, res: express.Response
}
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 })

View File

@ -15,7 +15,6 @@ import { VideoImportModel } from '../../../models/video/video-import'
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'
@ -25,8 +24,14 @@ import { CONFIG } from '../../../initializers/config'
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()
@ -225,28 +230,28 @@ async function processPreview (req: express.Request, video: VideoModel) {
}
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,
@ -259,16 +264,13 @@ function insertIntoDB (parameters: {
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

View File

@ -63,6 +63,7 @@ import { createVideoMiniatureFromExisting, generateVideoMiniature } from '../../
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()
@ -238,7 +239,7 @@ async function addVideo (req: express.Request, res: express.Response) {
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)
@ -318,7 +319,7 @@ async function addVideo (req: express.Request, res: express.Response) {
}
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
@ -371,7 +372,7 @@ async function updateVideo (req: express.Request, res: express.Response) {
}
}
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)
@ -447,7 +448,7 @@ async function getVideo (req: express.Request, res: express.Response) {
const video = await Hooks.wrapPromiseFun(
VideoModel.loadForGetAPI,
{ id: res.locals.video.id, userId },
{ id: res.locals.onlyVideoWithRights.id, userId },
'filter:api.video.get.result'
)
@ -460,7 +461,7 @@ async function getVideo (req: express.Request, res: express.Response) {
}
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)
@ -483,7 +484,7 @@ async function viewVideo (req: express.Request, res: express.Response) {
}
async function getVideoDescription (req: express.Request, res: express.Response) {
const videoInstance = res.locals.video
const videoInstance = res.locals.videoAll
let description = ''
if (videoInstance.isOwned()) {
@ -522,7 +523,7 @@ async function listVideos (req: express.Request, res: express.Response) {
}
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 })

View File

@ -18,6 +18,7 @@ import { getFormattedObjects } from '../../../helpers/utils'
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()
@ -56,7 +57,7 @@ export {
// ---------------------------------------------------------------------------
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
@ -107,7 +108,7 @@ async function acceptOwnership (req: express.Request, res: express.Response) {
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) {

View File

@ -27,7 +27,7 @@ export {
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 => {

View File

@ -23,7 +23,7 @@ async function userWatchVideo (req: express.Request, res: express.Response) {
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,

View File

@ -43,7 +43,7 @@ export {
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)

View File

@ -23,7 +23,7 @@ export {
// ---------------------------------------------------------------------------
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)

View File

@ -226,14 +226,14 @@ async function generateNodeinfo (req: express.Request, res: express.Response) {
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()
@ -242,7 +242,7 @@ async function downloadVideoFile (req: express.Request, res: express.Response, n
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)

View File

@ -18,7 +18,7 @@ export {
// ---------------------------------------------------------------------------
function webfingerController (req: express.Request, res: express.Response) {
const actor = res.locals.actor
const actor = res.locals.actorFull
const json = {
subject: req.query.resource,

View File

@ -7,6 +7,7 @@ import { ActorModel } from '../models/activitypub/actor'
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, {
@ -143,7 +144,7 @@ async function activityPubCollectionPagination (baseUrl: string, handler: Activi
}
function buildSignedActivity (byActor: ActorModel, data: Object) {
function buildSignedActivity (byActor: MActor, data: Object) {
const activity = activityPubContextify(data)
return signJsonLDObject(byActor, activity) as Promise<Activity>

View File

@ -1,10 +1,13 @@
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 {

View File

@ -1,10 +1,10 @@
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())

View File

@ -1,10 +1,10 @@
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)
@ -18,19 +18,7 @@ export async function doesChangeVideoOwnershipExist (id: string, res: Response):
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
}

View File

@ -1,6 +1,7 @@
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)
@ -15,10 +16,12 @@ function doesLocalAccountNameExist (name: string, res: Response, sendNotFound =
}
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) {

View File

@ -1,41 +1,23 @@
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
}

View File

@ -1,8 +1,8 @@
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) {

View File

@ -1,23 +1,42 @@
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
}

View File

@ -1,11 +1,31 @@
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' })
@ -14,12 +34,5 @@ async function doesVideoPlaylistExist (id: number | string, res: express.Respons
return false
}
res.locals.videoPlaylist = videoPlaylist
return true
}
// ---------------------------------------------------------------------------
export {
doesVideoPlaylistExist
}

View File

@ -1,9 +1,8 @@
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
@ -18,11 +17,28 @@ async function doesVideoExist (id: number | string, res: Response, fetchType: Vi
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) {
@ -50,7 +66,7 @@ async function doesVideoChannelOfAccountExist (channelId: number, user: UserMode
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)

View File

@ -8,6 +8,7 @@ import { cloneDeep } from 'lodash'
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)
@ -46,7 +47,7 @@ function isHTTPSignatureDigestValid (rawBody: Buffer, req: Request): boolean {
return true
}
function isHTTPSignatureVerified (httpSignatureParsed: any, actor: ActorModel): boolean {
function isHTTPSignatureVerified (httpSignatureParsed: any, actor: MActor): boolean {
return httpSignature.verifySignature(httpSignatureParsed, actor.publicKey) === true
}
@ -56,7 +57,7 @@ function parseHTTPSignature (req: Request, clockSkew?: number) {
// 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)
@ -93,7 +94,7 @@ async function isJsonLDSignatureVerified (fromActor: ActorModel, 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, {
@ -130,7 +131,7 @@ async function isJsonLDRSA2017Verified (fromActor: ActorModel, signedDocument: a
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,

View File

@ -1,8 +1,24 @@
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)
@ -13,15 +29,24 @@ function fetchVideo (id: number | string, fetchType: VideoFetchType, userId?: nu
}
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
}

View File

@ -4,6 +4,7 @@ import { ActorModel } from '../models/activitypub/actor'
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,
@ -17,7 +18,7 @@ async function loadActorUrlOrGetFromWebfinger (uriArg: string) {
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)

View File

@ -22,13 +22,25 @@ import { JobQueue } from '../job-queue'
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 => {
@ -37,12 +49,26 @@ function setAsyncActorKeys (actor: ActorModel) {
})
}
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
@ -61,7 +87,7 @@ async function getOrCreateActorAndServerAndModel (
// 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)
@ -85,8 +111,8 @@ async function getOrCreateActorAndServerAndModel (
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.')
@ -140,7 +166,8 @@ async function updateActorInstance (actorInstance: ActorModel, attributes: Activ
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 {
@ -212,14 +239,16 @@ async function addFetchOutboxJob (actor: Pick<ActorModel, 'id' | 'outboxUrl'>) {
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
@ -297,9 +326,9 @@ export {
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)
@ -336,7 +365,7 @@ function saveActorAndServerAndModelIfNotExist (
// 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
@ -345,10 +374,10 @@ function saveActorAndServerAndModelIfNotExist (
})
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
}
@ -360,7 +389,7 @@ function saveActorAndServerAndModelIfNotExist (
}
type FetchRemoteActorResult = {
actor: ActorModel
actor: MActor
name: string
summary: string
support?: string
@ -429,7 +458,7 @@ async function fetchRemoteActor (actorUrl: string): Promise<{ statusCode?: numbe
}
}
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,
@ -442,10 +471,10 @@ async function saveAccount (actor: ActorModel, result: FetchRemoteActorResult, t
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,
@ -460,5 +489,5 @@ async function saveVideoChannel (actor: ActorModel, result: FetchRemoteActorResu
transaction: t
})
return videoChannelCreated
return videoChannelCreated as MChannel
}

View File

@ -3,11 +3,10 @@ import { ActivityAudience } from '../../../shared/models/activitypub'
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)
@ -15,9 +14,9 @@ function getRemoteVideoAudience (video: VideoModel, actorsInvolvedInVideo: Actor
}
function getVideoCommentAudience (
videoComment: VideoCommentModel,
threadParentComments: VideoCommentModel[],
actorsInvolvedInVideo: ActorModel[],
videoComment: MCommentOwnerVideo,
threadParentComments: MCommentOwner[],
actorsInvolvedInVideo: MActorFollowersUrl[],
isOrigin = false
): ActivityAudience {
const to = [ ACTIVITY_PUB.PUBLIC ]
@ -42,26 +41,28 @@ function getVideoCommentAudience (
}
}
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)
}

View File

@ -1,10 +1,10 @@
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
@ -39,7 +39,7 @@ function cacheFileActivityObjectToDBAttributes (cacheFileObject: CacheFileObject
}
}
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) {
@ -49,7 +49,7 @@ async function createOrUpdateCacheFile (cacheFileObject: CacheFileObject, video:
}
}
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 })
@ -57,9 +57,9 @@ function createCacheFile (cacheFileObject: CacheFileObject, video: VideoModel, b
function updateCacheFile (
cacheFileObject: CacheFileObject,
redundancyModel: VideoRedundancyModel,
video: VideoModel,
byActor: { id?: number },
redundancyModel: MVideoRedundancy,
video: MVideoWithAllFiles,
byActor: MActorId,
t: Transaction
) {
if (redundancyModel.actorId !== byActor.id) {

View File

@ -1,7 +1,6 @@
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'
@ -13,14 +12,14 @@ import { PlaylistElementObject } from '../../../shared/models/activitypub/object
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 {
@ -36,7 +35,7 @@ function playlistObjectToDBAttributes (playlistObject: PlaylistObject, byAccount
}
}
function playlistElementObjectToDBAttributes (elementObject: PlaylistElementObject, videoPlaylist: VideoPlaylistModel, video: VideoModel) {
function playlistElementObjectToDBAttributes (elementObject: PlaylistElementObject, videoPlaylist: MVideoPlaylistId, video: MVideoId) {
return {
position: elementObject.position,
url: elementObject.id,
@ -47,7 +46,7 @@ function playlistElementObjectToDBAttributes (elementObject: PlaylistElementObje
}
}
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)
@ -75,7 +74,7 @@ async function createAccountPlaylists (playlistUrls: string[], account: AccountM
}, { 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) {
@ -88,7 +87,7 @@ async function createOrUpdateVideoPlaylist (playlistObject: PlaylistObject, byAc
}
}
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 => {
@ -114,7 +113,7 @@ async function createOrUpdateVideoPlaylist (playlistObject: PlaylistObject, byAc
return resetVideoPlaylistElements(accItems, refreshedPlaylist)
}
async function refreshVideoPlaylistIfNeeded (videoPlaylist: VideoPlaylistModel): Promise<VideoPlaylistModel> {
async function refreshVideoPlaylistIfNeeded (videoPlaylist: MVideoPlaylistOwner): Promise<MVideoPlaylistOwner> {
if (!videoPlaylist.isOutdated()) return videoPlaylist
try {
@ -157,7 +156,7 @@ export {
// ---------------------------------------------------------------------------
async function resetVideoPlaylistElements (elementUrls: string[], playlist: VideoPlaylistModel) {
async function resetVideoPlaylistElements (elementUrls: string[], playlist: MVideoPlaylist) {
const elementsToCreate: FilteredModelAttributes<VideoPlaylistElementModel>[] = []
await Bluebird.map(elementUrls, async elementUrl => {

View File

@ -1,9 +1,8 @@
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
@ -20,7 +19,7 @@ export {
// ---------------------------------------------------------------------------
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.')

View File

@ -5,10 +5,9 @@ import { VideoShareModel } from '../../../models/video/video-share'
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
@ -26,10 +25,10 @@ export {
// ---------------------------------------------------------------------------
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 {

View File

@ -10,10 +10,8 @@ import { createOrUpdateCacheFile } from '../cache-file'
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
@ -61,7 +59,7 @@ async function processCreateVideo (activity: ActivityCreate, notify: boolean) {
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 })
@ -77,15 +75,15 @@ async function processCreateCacheFile (activity: ActivityCreate, byActor: Signat
}
}
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
@ -110,7 +108,7 @@ async function processCreateVideoComment (activity: ActivityCreate, byActor: Sig
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

View File

@ -2,15 +2,13 @@ import { ActivityDelete } from '../../../../shared/models/activitypub'
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
@ -24,13 +22,17 @@ async function processDeleteActivity (options: APProcessorOptions<ActivityDelete
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)
}
}
@ -70,7 +72,7 @@ export {
// ---------------------------------------------------------------------------
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 => {
@ -84,7 +86,7 @@ async function processDeleteVideo (actor: ActorModel, videoToDelete: VideoModel)
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 => {
@ -98,7 +100,7 @@ async function processDeleteVideoPlaylist (actor: ActorModel, playlistToDelete:
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 => {
@ -108,7 +110,7 @@ async function processDeleteAccount (accountToRemove: AccountModel) {
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 => {
@ -118,7 +120,7 @@ async function processDeleteVideoChannel (videoChannelToRemove: VideoChannelMode
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 => {

View File

@ -7,7 +7,7 @@ import { getOrCreateVideoAndAccountAndChannel } from '../videos'
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
@ -22,7 +22,7 @@ export {
// ---------------------------------------------------------------------------
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

View File

@ -8,7 +8,7 @@ import { getOrCreateVideoAndAccountAndChannel } from '../videos'
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
@ -23,7 +23,7 @@ export {
// ---------------------------------------------------------------------------
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))
@ -41,7 +41,7 @@ async function processCreateVideoAbuse (activity: ActivityCreate | ActivityFlag,
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)

View File

@ -10,8 +10,7 @@ import { getAPId } from '../../../helpers/activitypub'
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
@ -28,7 +27,7 @@ export {
// ---------------------------------------------------------------------------
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)
@ -43,10 +42,10 @@ async function processFollow (byActor: SignatureActorModel, targetActorURL: stri
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
@ -57,7 +56,7 @@ async function processFollow (byActor: SignatureActorModel, targetActorURL: stri
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'
@ -77,8 +76,14 @@ async function processFollow (byActor: SignatureActorModel, targetActorURL: stri
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)

View File

@ -7,7 +7,7 @@ import { getOrCreateVideoAndAccountAndChannel } from '../videos'
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
@ -22,7 +22,7 @@ export {
// ---------------------------------------------------------------------------
async function processLikeVideo (byActor: SignatureActorModel, activity: ActivityLike) {
async function processLikeVideo (byActor: MActorSignature, activity: ActivityLike) {
const videoUrl = getAPId(activity.object)
const byAccount = byActor.Account

View File

@ -2,7 +2,7 @@ import { ActivityReject } from '../../../../shared/models/activitypub/activity'
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
@ -19,7 +19,7 @@ export {
// ---------------------------------------------------------------------------
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)

View File

@ -11,7 +11,7 @@ import { getOrCreateVideoAndAccountAndChannel } from '../videos'
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
@ -54,7 +54,7 @@ export {
// ---------------------------------------------------------------------------
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 })
@ -77,7 +77,7 @@ async function processUndoLike (byActor: SignatureActorModel, activity: Activity
})
}
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
@ -102,7 +102,7 @@ async function processUndoDislike (byActor: SignatureActorModel, activity: Activ
})
}
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 })
@ -127,7 +127,7 @@ async function processUndoCacheFile (byActor: SignatureActorModel, activity: Act
})
}
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)
@ -140,7 +140,7 @@ function processUndoFollow (follower: SignatureActorModel, followActivity: Activ
})
}
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}.`)

View File

@ -15,7 +15,7 @@ import { forwardVideoRelatedActivity } from '../send/utils'
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
@ -53,7 +53,7 @@ export {
// ---------------------------------------------------------------------------
async function processUpdateVideo (actor: SignatureActorModel, activity: ActivityUpdate) {
async function processUpdateVideo (actor: MActorSignature, activity: ActivityUpdate) {
const videoObject = activity.object as VideoTorrentObject
if (sanitizeAndCheckVideoTorrentObject(videoObject) === false) {
@ -61,20 +61,20 @@ async function processUpdateVideo (actor: SignatureActorModel, activity: Activit
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)) {
@ -150,7 +150,7 @@ async function processUpdateActor (actor: ActorModel, activity: ActivityUpdate)
}
}
async function processUpdatePlaylist (byActor: SignatureActorModel, activity: ActivityUpdate) {
async function processUpdatePlaylist (byActor: MActorSignature, activity: ActivityUpdate) {
const playlistObject = activity.object as PlaylistObject
const byAccount = byActor.Account

View File

@ -3,7 +3,7 @@ import { forwardVideoRelatedActivity } from '../send/utils'
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
@ -18,11 +18,11 @@ export {
// ---------------------------------------------------------------------------
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)

View File

@ -1,7 +1,6 @@
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'
@ -16,7 +15,7 @@ import { processDislikeActivity } from './process-dislike'
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,
@ -36,15 +35,15 @@ const processActivity: { [ P in ActivityType ]: (options: APProcessorOptions<Act
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) {
@ -75,7 +74,7 @@ async function processActivities (
}
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 })
}

View File

@ -3,10 +3,9 @@ import { getActorFollowAcceptActivityPubUrl, getActorFollowActivityPubUrl } from
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
@ -34,7 +33,7 @@ export {
// ---------------------------------------------------------------------------
function buildAcceptActivity (url: string, byActor: ActorModelOnly, followActivityData: ActivityFollow): ActivityAccept {
function buildAcceptActivity (url: string, byActor: MActor, followActivityData: ActivityFollow): ActivityAccept {
return {
type: 'Accept',
id: url,

View File

@ -1,16 +1,15 @@
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
@ -23,7 +22,7 @@ async function buildAnnounceWithVideoAudience (
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)
@ -32,7 +31,7 @@ async function sendVideoAnnounce (byActor: ActorModelOnly, videoShare: VideoShar
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({

View File

@ -1,19 +1,23 @@
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'
import {
MActorLight,
MCommentOwnerVideo,
MVideoAccountLight,
MVideoAP,
MVideoPlaylistFull,
MVideoRedundancyFileVideo,
MVideoRedundancyStreamingPlaylistVideo
} from '../../../typings/models'
async function sendCreateVideo (video: VideoModel, t: Transaction) {
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)
@ -27,7 +31,11 @@ async function sendCreateVideo (video: VideoModel, t: Transaction) {
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({
@ -38,7 +46,7 @@ async function sendCreateCacheFile (byActor: ActorModel, video: VideoModel, file
})
}
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)
@ -57,7 +65,7 @@ async function sendCreateVideoPlaylist (playlist: VideoPlaylistModel, t: Transac
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()
@ -95,7 +103,7 @@ async function sendCreateVideoComment (comment: VideoCommentModel, t: Transactio
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(
@ -122,8 +130,8 @@ export {
// ---------------------------------------------------------------------------
async function sendVideoRelatedCreateActivity (options: {
byActor: ActorModel,
video: VideoModel,
byActor: MActorLight,
video: MVideoAccountLight,
url: string,
object: any,
transaction?: Transaction

View File

@ -1,17 +1,17 @@
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
@ -42,7 +42,7 @@ async function sendDeleteActor (byActor: ActorModel, t: Transaction) {
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()
@ -74,7 +74,7 @@ async function sendDeleteVideoComment (videoComment: VideoCommentModel, t: Trans
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
@ -101,7 +101,7 @@ export {
// ---------------------------------------------------------------------------
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,

View File

@ -1,13 +1,12 @@
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) => {
@ -19,7 +18,7 @@ async function sendDislike (byActor: ActorModel, video: VideoModel, t: Transacti
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(

View File

@ -1,14 +1,13 @@
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)
@ -22,7 +21,7 @@ async function sendVideoAbuse (byActor: ActorModel, videoAbuse: VideoAbuseModel,
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(

View File

@ -4,9 +4,9 @@ import { getActorFollowActivityPubUrl } from '../url'
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
@ -21,7 +21,7 @@ function sendFollow (actorFollow: ActorFollowModel, t: Transaction) {
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,

View File

@ -1,13 +1,12 @@
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) => {
@ -19,7 +18,7 @@ async function sendLike (byActor: ActorModel, video: VideoModel, t: Transaction)
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(

View File

@ -1,12 +1,11 @@
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
@ -31,7 +30,7 @@ export {
// ---------------------------------------------------------------------------
function buildRejectActivity (url: string, byActor: ActorModel, followActivityData: ActivityFollow): ActivityReject {
function buildRejectActivity (url: string, byActor: MActor, followActivityData: ActivityFollow): ActivityReject {
return {
type: 'Reject',
id: url,

View File

@ -2,13 +2,12 @@ import { Transaction } from 'sequelize'
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'
@ -16,13 +15,20 @@ import { audiencify, getAudience } from '../audience'
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'
import {
MActor, MActorAudience,
MActorFollowActors,
MActorLight,
MVideo,
MVideoAccountLight,
MVideoRedundancyVideo,
MVideoShare
} from '../../../typings/models'
async function sendUndoFollow (actorFollow: ActorFollowModel, t: Transaction) {
async function sendUndoFollow (actorFollow: MActorFollowActors, t: Transaction) {
const me = actorFollow.ActorFollower
const following = actorFollow.ActorFollowing
@ -40,7 +46,7 @@ async function sendUndoFollow (actorFollow: ActorFollowModel, t: Transaction) {
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)
@ -52,7 +58,7 @@ async function sendUndoAnnounce (byActor: ActorModel, videoShare: VideoShareMode
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)
@ -61,7 +67,7 @@ async function sendUndoLike (byActor: ActorModel, video: VideoModel, t: Transact
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)
@ -70,7 +76,7 @@ async function sendUndoDislike (byActor: ActorModel, video: VideoModel, t: Trans
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
@ -94,7 +100,7 @@ export {
function undoActivityData (
url: string,
byActor: ActorModel,
byActor: MActorAudience,
object: ActivityFollow | ActivityLike | ActivityDislike | ActivityCreate | ActivityAnnounce,
audience?: ActivityAudience
): ActivityUndo {
@ -112,8 +118,8 @@ function undoActivityData (
}
async function sendUndoVideoRelatedActivity (options: {
byActor: ActorModel,
video: VideoModel,
byActor: MActor,
video: MVideoAccountLight,
url: string,
activity: ActivityFollow | ActivityLike | ActivityDislike | ActivityCreate | ActivityAnnounce,
transaction: Transaction

View File

@ -2,21 +2,29 @@ import { Transaction } from 'sequelize'
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)
@ -41,7 +49,7 @@ async function sendUpdateVideo (video: VideoModel, t: Transaction, overrodeByAct
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)
@ -51,7 +59,7 @@ async function sendUpdateActor (accountOrChannel: AccountModel | VideoChannelMod
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)
@ -65,7 +73,7 @@ async function sendUpdateActor (accountOrChannel: AccountModel | VideoChannelMod
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)
@ -80,7 +88,7 @@ async function sendUpdateCacheFile (byActor: ActorModel, redundancyModel: VideoR
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
@ -113,7 +121,7 @@ export {
// ---------------------------------------------------------------------------
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(
@ -121,8 +129,7 @@ function buildUpdateActivity (url: string, byActor: ActorModel, object: any, aud
type: 'Update' as 'Update',
id: url,
actor: byActor.url,
object: audiencify(object, audience
)
object: audiencify(object, audience)
},
audience
)

View File

@ -1,13 +1,13 @@
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) => {
@ -19,7 +19,7 @@ async function sendView (byActor: ActorModel, video: VideoModel, t: Transaction)
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(

View File

@ -4,15 +4,14 @@ import { logger } from '../../../helpers/logger'
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
@ -41,8 +40,8 @@ async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAud
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)
@ -54,7 +53,7 @@ async function forwardVideoRelatedActivity (
async function forwardActivity (
activity: Activity,
t: Transaction,
followersException: ActorFollowerException[] = [],
followersException: MActorFollowerException[] = [],
additionalFollowerUrls: string[] = []
) {
logger.info('Forwarding activity %s.', activity.id)
@ -88,10 +87,10 @@ async function forwardActivity (
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)
@ -100,16 +99,16 @@ async function broadcastToFollowers (
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 })
@ -123,7 +122,7 @@ function broadcastTo (uris: string[], data: any, byActor: ActorModelId) {
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 = {
@ -148,7 +147,7 @@ export {
// ---------------------------------------------------------------------------
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)
@ -157,7 +156,7 @@ async function computeFollowerUris (toFollowersOf: ActorModelId[], actorsExcepti
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
@ -170,7 +169,7 @@ async function computeUris (toActors: ActorModelOnly[], actorsException: ActorFo
.filter(sharedInbox => sharedInboxesException.indexOf(sharedInbox) === -1)
}
async function buildSharedInboxesException (actorsException: ActorFollowerException[]) {
async function buildSharedInboxesException (actorsException: MActorFollowerException[]) {
const serverActor = await getServerActor()
return actorsException

View File

@ -1,19 +1,18 @@
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([
@ -22,7 +21,11 @@ async function shareVideoByServerAndChannel (video: VideoModel, t: Transaction)
])
}
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)
@ -30,7 +33,7 @@ async function changeVideoChannelShare (video: VideoModel, oldVideoChannel: Vide
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
@ -50,7 +53,7 @@ async function addVideoShares (shareUrls: string[], instance: VideoModel) {
const entry = {
actorId: actor.id,
videoId: instance.id,
videoId: video.id,
url: shareUrl
}
@ -69,7 +72,7 @@ export {
// ---------------------------------------------------------------------------
async function shareByServer (video: VideoModel, t: Transaction) {
async function shareByServer (video: MVideo, t: Transaction) {
const serverActor = await getServerActor()
const serverShareUrl = getVideoAnnounceActivityPubUrl(serverActor, video)
@ -88,7 +91,7 @@ async function shareByServer (video: VideoModel, t: Transaction) {
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: {
@ -105,7 +108,7 @@ async function shareByVideoChannel (video: VideoModel, t: Transaction) {
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)

View File

@ -1,36 +1,42 @@
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'
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: VideoModel) {
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
}
@ -42,54 +48,54 @@ function getAccountActivityPubUrl (accountName: string) {
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
}

View File

@ -2,20 +2,20 @@ import { sanitizeAndCheckVideoCommentObject } from '../../helpers/custom-validat
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 => {
@ -85,9 +85,9 @@ async function tryResolveThreadFromVideo (params: ResolveThreadParams) {
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
@ -97,7 +97,7 @@ async function tryResolveThreadFromVideo (params: ResolveThreadParams) {
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
@ -107,7 +107,7 @@ async function tryResolveThreadFromVideo (params: ResolveThreadParams) {
comments[i] = await comment.save()
}
resultComment = comments[0]
resultComment = comments[0] as MCommentOwnerVideo
}
return { video, comment: resultComment, commentCreated }
@ -151,7 +151,7 @@ async function resolveParentComment (params: ResolveThreadParams) {
originCommentId: null,
createdAt: new Date(body.published),
updatedAt: new Date(body.updated)
})
}) as MCommentOwner
comment.Account = actor.Account
return resolveThread({

View File

@ -1,6 +1,4 @@
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'
@ -10,11 +8,11 @@ import { logger } from '../../helpers/logger'
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 => {
@ -64,11 +62,13 @@ async function createRates (ratesUrl: string[], video: VideoModel, rate: VideoRa
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
@ -84,8 +84,10 @@ async function sendVideoRateChange (account: AccountModel,
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 {

View File

@ -24,7 +24,6 @@ import {
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'
@ -38,7 +37,6 @@ import { JobQueue } from '../job-queue'
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'
@ -49,15 +47,33 @@ import { VideoShareModel } from '../../models/video/video-share'
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)) &&
@ -102,7 +118,7 @@ async function fetchRemoteVideo (videoUrl: string): Promise<{ response: request.
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 = {
@ -114,14 +130,14 @@ async function fetchRemoteVideoDescription (video: VideoModel) {
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
@ -146,7 +162,7 @@ type SyncParam = {
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[] = []
@ -194,12 +210,24 @@ async function syncVideoExternalAttributes (video: VideoModel, fetchedVideo: Vid
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'
@ -227,8 +255,9 @@ async function getOrCreateVideoAndAccountAndChannel (options: {
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)
@ -236,22 +265,22 @@ async function getOrCreateVideoAndAccountAndChannel (options: {
}
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)
@ -259,7 +288,7 @@ async function updateVideoFromAP (options: {
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()
@ -293,21 +322,21 @@ async function updateVideoFromAP (options: {
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)
@ -318,15 +347,15 @@ async function updateVideoFromAP (options: {
.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)
@ -337,38 +366,42 @@ async function updateVideoFromAP (options: {
.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)
@ -381,15 +414,15 @@ async function updateVideoFromAP (options: {
}
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 {
@ -410,12 +443,11 @@ async function refreshVideoIfNeeded (options: {
}
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)
@ -467,15 +499,15 @@ function isAPPlaylistSegmentHashesUrlObject (tag: any): tag is ActivityPlaylistS
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
}
@ -483,8 +515,8 @@ async function createVideo (videoObject: VideoTorrentObject, channelActor: Actor
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)
@ -517,15 +549,14 @@ async function createVideo (videoObject: VideoTorrentObject, channelActor: Actor
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,
@ -548,11 +579,7 @@ async function createVideo (videoObject: VideoTorrentObject, channelActor: Actor
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]+/, '')
@ -603,7 +630,7 @@ async function videoActivityObjectToDBAttributes (
}
}
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) {
@ -641,7 +668,7 @@ function videoFileActivityUrlToDBAttributes (video: VideoModel, videoObject: Vid
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 []

View File

@ -3,8 +3,6 @@ import { sendUpdateActor } from './activitypub/send'
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'
@ -13,8 +11,12 @@ import { sequelizeTypescript } from '../initializers/database'
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)

View File

@ -1,6 +1,7 @@
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 => {
@ -20,13 +21,13 @@ function addServerInBlocklist (byAccountId: number, targetServerId: number) {
})
}
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 })
})

View File

@ -13,6 +13,7 @@ import { VideoChannelModel } from '../models/video/video-channel'
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 {
@ -65,7 +66,7 @@ export class ClientHtml {
}
private static async getAccountOrChannelHTMLPage (
loader: () => Bluebird<AccountModel | VideoChannelModel>,
loader: () => Bluebird<MAccountActor | MChannelActor>,
req: express.Request,
res: express.Response
) {
@ -157,7 +158,7 @@ export class ClientHtml {
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()
@ -236,7 +237,7 @@ export class ClientHtml {
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}" />`

View File

@ -3,16 +3,14 @@ import { isTestInstance } from '../helpers/core-utils'
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[]
@ -90,7 +88,7 @@ class Emailer {
}
}
addNewVideoFromSubscriberNotification (to: string[], video: VideoModel) {
addNewVideoFromSubscriberNotification (to: string[], video: MVideoAccountLight) {
const channelName = video.VideoChannel.getDisplayName()
const videoUrl = WEBSERVER.URL + video.getWatchStaticPath()
@ -111,7 +109,7 @@ class Emailer {
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()
@ -130,7 +128,7 @@ class Emailer {
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` +
@ -148,7 +146,7 @@ class Emailer {
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` +
@ -168,7 +166,7 @@ class Emailer {
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` +
@ -188,7 +186,7 @@ class Emailer {
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` +
@ -208,7 +206,7 @@ class Emailer {
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()
@ -230,7 +228,7 @@ class Emailer {
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()
@ -252,7 +250,7 @@ class Emailer {
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` +
@ -269,7 +267,7 @@ class Emailer {
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()
@ -292,7 +290,7 @@ class Emailer {
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` +
@ -307,7 +305,7 @@ class Emailer {
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()
@ -329,7 +327,7 @@ class Emailer {
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' +
@ -381,7 +379,7 @@ class Emailer {
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}.`

View File

@ -1,4 +1,3 @@
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'
@ -12,6 +11,7 @@ import { flatten, uniq } from 'lodash'
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()
@ -28,7 +28,7 @@ async function updateStreamingPlaylistsInfohashesIfNeeded () {
}
}
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())
@ -55,7 +55,7 @@ async function updateMasterHLSPlaylist (video: VideoModel) {
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)

View File

@ -10,6 +10,7 @@ import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
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
@ -23,13 +24,13 @@ async function processActivityPubFollow (job: Bull.Job) {
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)
@ -44,7 +45,7 @@ export {
// ---------------------------------------------------------------------------
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.')
}
@ -53,7 +54,7 @@ async function follow (fromActor: ActorModel, targetActor: ActorModel) {
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

View File

@ -11,6 +11,7 @@ import { AccountModel } from '../../../models/account/account'
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'
@ -26,10 +27,10 @@ async function processActivityPubHttpFetcher (job: Bull.Job) {
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> } = {

View File

@ -3,6 +3,7 @@ import { getServerActor } from '../../../../helpers/utils'
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 }
@ -19,7 +20,8 @@ async function computeBody (payload: Payload) {
}
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.')

View File

@ -6,6 +6,7 @@ import { getVideoFileFPS, getVideoFileResolution } from '../../../helpers/ffmpeg
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,
@ -37,7 +38,7 @@ export {
// ---------------------------------------------------------------------------
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)
@ -48,7 +49,7 @@ async function updateVideoFile (video: VideoModel, inputFilePath: string) {
size,
fps,
videoId: video.id
})
}) as MVideoFile
const currentVideoFile = video.VideoFiles.find(videoFile => videoFile.resolution === updatedVideoFile.resolution)
@ -60,9 +61,9 @@ async function updateVideoFile (video: VideoModel, inputFilePath: string) {
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
}

View File

@ -17,9 +17,10 @@ import { move, remove, stat } from 'fs-extra'
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'
@ -110,11 +111,13 @@ type ProcessFileOptions = {
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()
@ -148,7 +151,7 @@ async function processFile (downloader: () => Promise<string>, videoImport: Vide
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) {
@ -156,7 +159,7 @@ async function processFile (downloader: () => Promise<string>, videoImport: Vide
}
// 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) {
@ -166,14 +169,15 @@ async function processFile (downloader: () => Promise<string>, videoImport: Vide
// 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
@ -188,25 +192,25 @@ async function processFile (downloader: () => Promise<string>, videoImport: Vide
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',

View File

@ -11,6 +11,7 @@ import { computeResolutionsToTranscode } from '../../../helpers/ffmpeg-utils'
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
@ -73,7 +74,7 @@ async function processVideoTranscoding (job: Bull.Job) {
return video
}
async function onHlsPlaylistGenerationSuccess (video: VideoModel) {
async function onHlsPlaylistGenerationSuccess (video: MVideoUUID) {
if (video === undefined) return undefined
await sequelizeTypescript.transaction(async t => {
@ -87,7 +88,7 @@ async function onHlsPlaylistGenerationSuccess (video: VideoModel) {
})
}
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)
@ -119,7 +120,7 @@ async function publishNewResolutionIfNeeded (video: VideoModel, payload?: NewRes
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)

View File

@ -8,13 +8,23 @@ import { UserModel } from '../models/account/user'
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 {
@ -22,7 +32,7 @@ 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
@ -30,7 +40,7 @@ class Notifier {
.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
@ -38,7 +48,7 @@ class Notifier {
.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
@ -46,7 +56,7 @@ class Notifier {
.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
@ -54,7 +64,7 @@ class Notifier {
.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 }))
@ -62,37 +72,37 @@ class Notifier {
.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(
@ -104,14 +114,14 @@ class Notifier {
})
}
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)
@ -127,7 +137,7 @@ class Notifier {
userId: user.id,
videoId: video.id
})
notification.Video = video
notification.Video = video as VideoModel
return notification
}
@ -139,7 +149,7 @@ class Notifier {
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)
@ -162,7 +172,7 @@ class Notifier {
userId: user.id,
commentId: comment.id
})
notification.Comment = comment
notification.Comment = comment as VideoCommentModel
return notification
}
@ -174,7 +184,7 @@ class Notifier {
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,
@ -209,7 +219,7 @@ class Notifier {
userId: user.id,
commentId: comment.id
})
notification.Comment = comment
notification.Comment = comment as VideoCommentModel
return notification
}
@ -221,7 +231,7 @@ class Notifier {
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?
@ -236,9 +246,6 @@ class Notifier {
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)
@ -256,7 +263,7 @@ class Notifier {
userId: user.id,
actorFollowId: actorFollow.id
})
notification.ActorFollow = actorFollow
notification.ActorFollow = actorFollow as ActorFollowModel
return notification
}
@ -268,7 +275,7 @@ class Notifier {
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)
@ -283,7 +290,7 @@ class Notifier {
userId: user.id,
actorFollowId: actorFollow.id
})
notification.ActorFollow = actorFollow
notification.ActorFollow = actorFollow as ActorFollowModel
return notification
}
@ -295,7 +302,7 @@ class Notifier {
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
@ -306,7 +313,7 @@ class Notifier {
}
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
@ -323,7 +330,7 @@ class Notifier {
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
@ -339,7 +346,7 @@ class Notifier {
userId: user.id,
videoId: video.id
})
notification.Video = video
notification.Video = video as VideoModel
return notification
}
@ -351,7 +358,7 @@ class Notifier {
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
@ -367,7 +374,7 @@ class Notifier {
userId: user.id,
videoBlacklistId: videoBlacklist.id
})
notification.VideoBlacklist = videoBlacklist
notification.VideoBlacklist = videoBlacklist as VideoBlacklistModel
return notification
}
@ -379,7 +386,7 @@ class Notifier {
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
@ -395,7 +402,7 @@ class Notifier {
userId: user.id,
videoId: video.id
})
notification.Video = video
notification.Video = video as VideoModel
return notification
}
@ -407,7 +414,7 @@ class Notifier {
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
@ -423,7 +430,7 @@ class Notifier {
userId: user.id,
videoId: video.id
})
notification.Video = video
notification.Video = video as VideoModel
return notification
}
@ -435,7 +442,7 @@ class Notifier {
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
@ -451,7 +458,7 @@ class Notifier {
userId: user.id,
videoImportId: videoImport.id
})
notification.VideoImport = videoImport
notification.VideoImport = videoImport as VideoImportModel
return notification
}
@ -465,13 +472,13 @@ class Notifier {
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) {
@ -484,7 +491,7 @@ class Notifier {
userId: user.id,
accountId: registeredUser.Account.id
})
notification.Account = registeredUser.Account
notification.Account = registeredUser.Account as AccountModel
return notification
}
@ -497,10 +504,10 @@ class Notifier {
}
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[] = []
@ -521,7 +528,7 @@ class Notifier {
}
}
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

View File

@ -8,10 +8,11 @@ import { LRU_CACHE } from '../initializers/constants'
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 })
// ---------------------------------------------------------------------------

View File

@ -1,8 +1,8 @@
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 {
@ -32,7 +32,7 @@ class PeerTubeSocket {
})
}
sendNotification (userId: number, notification: UserNotificationModel) {
sendNotification (userId: number, notification: UserNotificationModelForApi) {
const socket = this.userNotificationSockets[userId]
if (!socket) return

View File

@ -2,8 +2,9 @@ import { VideoRedundancyModel } from '../models/redundancy/video-redundancy'
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

View File

@ -3,7 +3,6 @@ import { HLS_REDUNDANCY_DIRECTORY, REDUNDANCY, VIDEO_IMPORT_TIMEOUT, WEBSERVER }
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'
@ -12,16 +11,29 @@ import { sendCreateCacheFile, sendUpdateCacheFile } from '../activitypub/send'
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 {
@ -102,7 +114,7 @@ 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) {
@ -172,7 +184,8 @@ export class VideosRedundancyScheduler extends AbstractScheduler {
}
}
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()
@ -187,7 +200,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler {
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),
@ -203,7 +216,12 @@ export class VideosRedundancyScheduler extends AbstractScheduler {
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()
@ -213,7 +231,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler {
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),
@ -229,7 +247,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler {
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()
@ -243,7 +261,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler {
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)
@ -263,14 +281,14 @@ export class VideosRedundancyScheduler extends AbstractScheduler {
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

View File

@ -1,20 +1,20 @@
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
@ -26,7 +26,7 @@ function createPlaylistMiniatureFromExisting (
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
@ -34,7 +34,7 @@ function createPlaylistMiniatureFromUrl (fileUrl: string, playlist: VideoPlaylis
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 })
@ -43,7 +43,7 @@ function createVideoMiniatureFromUrl (fileUrl: string, video: VideoModel, type:
function createVideoMiniatureFromExisting (
inputPath: string,
video: VideoModel,
video: MVideoThumbnail,
type: ThumbnailType,
automaticallyGenerated: boolean,
size?: ImageSize
@ -54,7 +54,7 @@ function createVideoMiniatureFromExisting (
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)
@ -65,7 +65,7 @@ function generateVideoMiniature (video: VideoModel, videoFile: VideoFileModel, t
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()
@ -90,7 +90,7 @@ export {
createPlaylistMiniatureFromExisting
}
function buildMetadataFromPlaylist (playlist: VideoPlaylistModel, size: ImageSize) {
function buildMetadataFromPlaylist (playlist: MVideoPlaylistThumbnail, size: ImageSize) {
const filename = playlist.generateThumbnailName()
const basePath = CONFIG.STORAGE.THUMBNAILS_DIR
@ -104,7 +104,7 @@ function buildMetadataFromPlaylist (playlist: VideoPlaylistModel, size: ImageSiz
}
}
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
@ -148,7 +148,7 @@ async function createThumbnailFromFunction (parameters: {
type: ThumbnailType,
automaticallyGenerated?: boolean,
fileUrl?: string,
existingThumbnail?: ThumbnailModel
existingThumbnail?: MThumbnail
}) {
const { thumbnailCreator, filename, width, height, type, existingThumbnail, automaticallyGenerated = null, fileUrl = null } = parameters

View File

@ -5,7 +5,6 @@ import { AccountModel } from '../models/account/account'
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'
@ -14,14 +13,17 @@ import { sequelizeTypescript } from '../initializers/database'
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 => {
@ -30,7 +32,7 @@ async function createUserAccountAndChannelAndPlaylist (parameters: {
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({
@ -50,15 +52,15 @@ async function createUserAccountAndChannelAndPlaylist (parameters: {
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: {
@ -73,7 +75,7 @@ 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,
@ -82,7 +84,7 @@ async function createLocalAccountWithoutKeys (parameters: {
actorId: actorInstanceCreated.id
})
const accountInstanceCreated = await accountInstance.save({ transaction: t })
const accountInstanceCreated: MAccountActor = await accountInstance.save({ transaction: t })
accountInstanceCreated.Actor = actorInstanceCreated
return accountInstanceCreated
@ -102,7 +104,7 @@ async function createApplicationActor (applicationId: number) {
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
@ -124,7 +126,7 @@ export {
// ---------------------------------------------------------------------------
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,
@ -143,7 +145,7 @@ function createDefaultUserNotificationSettings (user: UserModel, t: Transaction
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'

View File

@ -2,16 +2,15 @@ import { Transaction } from 'sequelize'
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,
@ -32,7 +31,7 @@ async function autoBlacklistVideoIfNeeded (parameters: {
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
},
@ -49,10 +48,10 @@ async function autoBlacklistVideoIfNeeded (parameters: {
}
async function autoBlacklistNeeded (parameters: {
video: VideoModel,
video: MVideoWithBlacklistLight,
isRemote: boolean,
isNew: boolean,
user?: UserModel
user?: MUser
}) {
const { user, video, isRemote, isNew } = parameters

View File

@ -1,12 +1,19 @@
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)
@ -21,10 +28,10 @@ async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account
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
@ -34,7 +41,7 @@ async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account
return videoChannelCreated
}
async function federateAllVideosOfChannel (videoChannel: VideoChannelModel) {
async function federateAllVideosOfChannel (videoChannel: MChannelId) {
const videoIds = await VideoModel.getAllIdsFromChannel(videoChannel)
for (const videoId of videoIds) {

View File

@ -1,17 +1,16 @@
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
@ -32,7 +31,7 @@ async function createVideoComment (obj: {
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

View File

@ -1,12 +1,13 @@
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,

View File

@ -5,16 +5,16 @@ import { ensureDir, move, remove, stat } from 'fs-extra'
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'
@ -57,7 +57,7 @@ async function optimizeVideofile (video: VideoModel, inputVideoFileArg?: VideoFi
/**
* 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'
@ -87,7 +87,7 @@ async function transcodeOriginalVideofile (video: VideoModel, resolution: VideoR
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'
@ -117,7 +117,7 @@ async function mergeAudioVideofile (video: VideoModel, resolution: VideoResoluti
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))
@ -165,14 +165,14 @@ export {
// ---------------------------------------------------------------------------
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)

View File

@ -10,6 +10,7 @@ import { areValidationErrors } from './utils'
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'),
@ -65,7 +66,7 @@ const getFollowerValidator = [
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)

View File

@ -24,7 +24,7 @@ const videoFileRedundancyGetValidator = [
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)
})
@ -50,7 +50,7 @@ const videoPlaylistRedundancyGetValidator = [
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.' })

View File

@ -2,7 +2,7 @@ import * as Bluebird from 'bluebird'
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,
@ -31,6 +31,7 @@ import { isThemeNameValid } from '../../helpers/custom-validators/plugins'
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)'),
@ -457,7 +458,7 @@ async function checkUserNameOrEmailDoesNotAlreadyExist (username: string, email:
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) {

View File

@ -33,7 +33,7 @@ const videoAbuseGetValidator = [
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()
}
@ -54,7 +54,7 @@ const videoAbuseUpdateValidator = [
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()
}

View File

@ -14,7 +14,7 @@ const videosBlacklistRemoveValidator = [
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()
}
@ -36,7 +36,7 @@ const videosBlacklistAddValidator = [
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)
@ -59,7 +59,7 @@ const videosBlacklistUpdateValidator = [
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()
}

View File

@ -26,7 +26,7 @@ const addVideoCaptionValidator = [
// 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()
}
@ -41,11 +41,11 @@ const deleteVideoCaptionValidator = [
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()
}

View File

@ -7,13 +7,14 @@ import {
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'),
@ -131,7 +132,7 @@ export {
// ---------------------------------------------------------------------------
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.' })

Some files were not shown because too many files have changed in this diff Show More