Stronger model typings
This commit is contained in:
parent
13176a07a9
commit
453e83ea5d
|
@ -16,7 +16,6 @@ import {
|
||||||
} from '../../middlewares'
|
} from '../../middlewares'
|
||||||
import { getAccountVideoRateValidator, videoCommentGetValidator } from '../../middlewares/validators'
|
import { getAccountVideoRateValidator, videoCommentGetValidator } from '../../middlewares/validators'
|
||||||
import { AccountModel } from '../../models/account/account'
|
import { AccountModel } from '../../models/account/account'
|
||||||
import { ActorModel } from '../../models/activitypub/actor'
|
|
||||||
import { ActorFollowModel } from '../../models/activitypub/actor-follow'
|
import { ActorFollowModel } from '../../models/activitypub/actor-follow'
|
||||||
import { VideoModel } from '../../models/video/video'
|
import { VideoModel } from '../../models/video/video'
|
||||||
import { VideoCommentModel } from '../../models/video/video-comment'
|
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 { videoPlaylistElementAPGetValidator, videoPlaylistsGetValidator } from '../../middlewares/validators/videos/video-playlists'
|
||||||
import { VideoPlaylistModel } from '../../models/video/video-playlist'
|
import { VideoPlaylistModel } from '../../models/video/video-playlist'
|
||||||
import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model'
|
import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model'
|
||||||
|
import { MAccountId, MActorId, MVideo, MVideoAPWithoutCaption } from '@server/typings/models'
|
||||||
|
|
||||||
const activityPubClientRouter = express.Router()
|
const activityPubClientRouter = express.Router()
|
||||||
|
|
||||||
|
@ -148,7 +148,7 @@ activityPubClientRouter.get('/redundancy/streaming-playlists/:streamingPlaylistT
|
||||||
|
|
||||||
activityPubClientRouter.get('/video-playlists/:playlistId',
|
activityPubClientRouter.get('/video-playlists/:playlistId',
|
||||||
executeIfActivityPub,
|
executeIfActivityPub,
|
||||||
asyncMiddleware(videoPlaylistsGetValidator),
|
asyncMiddleware(videoPlaylistsGetValidator('all')),
|
||||||
asyncMiddleware(videoPlaylistController)
|
asyncMiddleware(videoPlaylistController)
|
||||||
)
|
)
|
||||||
activityPubClientRouter.get('/video-playlists/:playlistId/:videoId',
|
activityPubClientRouter.get('/video-playlists/:playlistId/:videoId',
|
||||||
|
@ -208,18 +208,19 @@ function getAccountVideoRate (rateType: VideoRateType) {
|
||||||
|
|
||||||
async function videoController (req: express.Request, res: express.Response) {
|
async function videoController (req: express.Request, res: express.Response) {
|
||||||
// We need more attributes
|
// 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)
|
if (video.url.startsWith(WEBSERVER.URL) === false) return res.redirect(video.url)
|
||||||
|
|
||||||
// We need captions to render AP object
|
// 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 audience = getAudience(videoWithCaptions.VideoChannel.Account.Actor, videoWithCaptions.privacy === VideoPrivacy.PUBLIC)
|
||||||
const videoObject = audiencify(video.toActivityPubObject(), audience)
|
const videoObject = audiencify(videoWithCaptions.toActivityPubObject(), audience)
|
||||||
|
|
||||||
if (req.path.endsWith('/activity')) {
|
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)
|
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)
|
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)
|
return activityPubResponse(activityPubContextify(activity), res)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function videoAnnouncesController (req: express.Request, res: express.Response) {
|
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 handler = async (start: number, count: number) => {
|
||||||
const result = await VideoShareModel.listAndCountByVideoId(video.id, start, count)
|
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) {
|
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))
|
const json = await videoRates(req, 'like', video, getVideoLikesActivityPubUrl(video))
|
||||||
|
|
||||||
return activityPubResponse(activityPubContextify(json), res)
|
return activityPubResponse(activityPubContextify(json), res)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function videoDislikesController (req: express.Request, res: express.Response) {
|
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))
|
const json = await videoRates(req, 'dislike', video, getVideoDislikesActivityPubUrl(video))
|
||||||
|
|
||||||
return activityPubResponse(activityPubContextify(json), res)
|
return activityPubResponse(activityPubContextify(json), res)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function videoCommentsController (req: express.Request, res: express.Response) {
|
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 handler = async (start: number, count: number) => {
|
||||||
const result = await VideoCommentModel.listAndCountByVideoId(video.id, start, count)
|
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) {
|
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)
|
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) {
|
async function videoPlaylistController (req: express.Request, res: express.Response) {
|
||||||
const playlist = res.locals.videoPlaylist
|
const playlist = res.locals.videoPlaylistFull
|
||||||
|
|
||||||
// We need more attributes
|
// We need more attributes
|
||||||
playlist.OwnerAccount = await AccountModel.load(playlist.ownerAccountId)
|
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) => {
|
const handler = (start: number, count: number) => {
|
||||||
return ActorFollowModel.listAcceptedFollowingUrlsForApi([ actor.id ], undefined, start, count)
|
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)
|
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) => {
|
const handler = (start: number, count: number) => {
|
||||||
return ActorFollowModel.listAcceptedFollowerUrlsForAP([ actor.id ], undefined, start, count)
|
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)
|
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) => {
|
const handler = (start: number, count: number) => {
|
||||||
return VideoPlaylistModel.listPublicUrlsOfForAP(account.id, start, count)
|
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)
|
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 handler = async (start: number, count: number) => {
|
||||||
const result = await AccountVideoRateModel.listAndCountAccountUrlsByVideoId(rateType, video.id, start, count)
|
const result = await AccountVideoRateModel.listAndCountAccountUrlsByVideoId(rateType, video.id, start, count)
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { asyncMiddleware, checkSignature, localAccountValidator, localVideoChann
|
||||||
import { activityPubValidator } from '../../middlewares/validators/activitypub/activity'
|
import { activityPubValidator } from '../../middlewares/validators/activitypub/activity'
|
||||||
import { queue } from 'async'
|
import { queue } from 'async'
|
||||||
import { ActorModel } from '../../models/activitypub/actor'
|
import { ActorModel } from '../../models/activitypub/actor'
|
||||||
import { SignatureActorModel } from '../../typings/models'
|
import { MActorDefault, MActorSignature } from '../../typings/models'
|
||||||
|
|
||||||
const inboxRouter = express.Router()
|
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 }
|
const options = { signatureActor: task.signatureActor, inboxActor: task.inboxActor }
|
||||||
|
|
||||||
processActivities(task.activities, options)
|
processActivities(task.activities, options)
|
||||||
|
|
|
@ -6,11 +6,9 @@ import { logger } from '../../helpers/logger'
|
||||||
import { buildAnnounceActivity, buildCreateActivity } from '../../lib/activitypub/send'
|
import { buildAnnounceActivity, buildCreateActivity } from '../../lib/activitypub/send'
|
||||||
import { buildAudience } from '../../lib/activitypub/audience'
|
import { buildAudience } from '../../lib/activitypub/audience'
|
||||||
import { asyncMiddleware, localAccountValidator, localVideoChannelValidator } from '../../middlewares'
|
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 { VideoModel } from '../../models/video/video'
|
||||||
import { activityPubResponse } from './utils'
|
import { activityPubResponse } from './utils'
|
||||||
import { VideoChannelModel } from '../../models/video/video-channel'
|
import { MActorLight } from '@server/typings/models'
|
||||||
|
|
||||||
const outboxRouter = express.Router()
|
const outboxRouter = express.Router()
|
||||||
|
|
||||||
|
@ -45,14 +43,10 @@ async function outboxController (req: express.Request, res: express.Response) {
|
||||||
return activityPubResponse(activityPubContextify(json), res)
|
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 data = await VideoModel.listAllAndSharedByActorForOutbox(actor.id, start, count)
|
||||||
const activities: Activity[] = []
|
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) {
|
for (const video of data.data) {
|
||||||
const byActor = video.VideoChannel.Account.Actor
|
const byActor = video.VideoChannel.Account.Actor
|
||||||
const createActivityAudience = buildAudience([ byActor.followersUrl ], video.privacy === VideoPrivacy.PUBLIC)
|
const createActivityAudience = buildAudience([ byActor.followersUrl ], video.privacy === VideoPrivacy.PUBLIC)
|
||||||
|
|
|
@ -19,6 +19,7 @@ import { getOrCreateActorAndServerAndModel, getOrCreateVideoAndAccountAndChannel
|
||||||
import { logger } from '../../helpers/logger'
|
import { logger } from '../../helpers/logger'
|
||||||
import { VideoChannelModel } from '../../models/video/video-channel'
|
import { VideoChannelModel } from '../../models/video/video-channel'
|
||||||
import { loadActorUrlOrGetFromWebfinger } from '../../helpers/webfinger'
|
import { loadActorUrlOrGetFromWebfinger } from '../../helpers/webfinger'
|
||||||
|
import { MChannelAccountDefault, MVideoAccountAllFiles } from '../../typings/models'
|
||||||
|
|
||||||
const searchRouter = express.Router()
|
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) {
|
async function searchVideoChannelURI (search: string, isWebfingerSearch: boolean, res: express.Response) {
|
||||||
let videoChannel: VideoChannelModel
|
let videoChannel: MChannelAccountDefault
|
||||||
let uri = search
|
let uri = search
|
||||||
|
|
||||||
if (isWebfingerSearch) {
|
if (isWebfingerSearch) {
|
||||||
|
@ -137,7 +138,7 @@ async function searchVideosDB (query: VideosSearchQuery, res: express.Response)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function searchVideoURI (url: string, 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
|
// Check if we can fetch a remote video with the URL
|
||||||
if (isUserAbleToSearchRemoteURI(res)) {
|
if (isUserAbleToSearchRemoteURI(res)) {
|
||||||
|
|
|
@ -48,6 +48,7 @@ import { CONFIG } from '../../../initializers/config'
|
||||||
import { sequelizeTypescript } from '../../../initializers/database'
|
import { sequelizeTypescript } from '../../../initializers/database'
|
||||||
import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model'
|
import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model'
|
||||||
import { UserRegister } from '../../../../shared/models/users/user-register.model'
|
import { UserRegister } from '../../../../shared/models/users/user-register.model'
|
||||||
|
import { MUser, MUserAccountDefault } from '@server/typings/models'
|
||||||
|
|
||||||
const auditLogger = auditLoggerFactory('users')
|
const auditLogger = auditLoggerFactory('users')
|
||||||
|
|
||||||
|
@ -359,7 +360,7 @@ function success (req: express.Request, res: express.Response) {
|
||||||
res.end()
|
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())
|
const oldUserAuditView = new UserAuditView(user.toFormattedJSON())
|
||||||
|
|
||||||
user.blocked = block
|
user.blocked = block
|
||||||
|
|
|
@ -147,7 +147,7 @@ async function getUserVideoQuotaUsed (req: express.Request, res: express.Respons
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getUserVideoRating (req: express.Request, res: express.Response) {
|
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 accountId = +res.locals.oauth.token.User.Account.id
|
||||||
|
|
||||||
const ratingObj = await AccountVideoRateModel.load(accountId, videoId, null)
|
const ratingObj = await AccountVideoRateModel.load(accountId, videoId, null)
|
||||||
|
|
|
@ -7,7 +7,6 @@ import {
|
||||||
setDefaultPagination,
|
setDefaultPagination,
|
||||||
userHistoryRemoveValidator
|
userHistoryRemoveValidator
|
||||||
} from '../../../middlewares'
|
} from '../../../middlewares'
|
||||||
import { UserModel } from '../../../models/account/user'
|
|
||||||
import { getFormattedObjects } from '../../../helpers/utils'
|
import { getFormattedObjects } from '../../../helpers/utils'
|
||||||
import { UserVideoHistoryModel } from '../../../models/account/user-video-history'
|
import { UserVideoHistoryModel } from '../../../models/account/user-video-history'
|
||||||
import { sequelizeTypescript } from '../../../initializers'
|
import { sequelizeTypescript } from '../../../initializers'
|
||||||
|
|
|
@ -136,7 +136,7 @@ async function updateVideoChannelAvatar (req: express.Request, res: express.Resp
|
||||||
async function addVideoChannel (req: express.Request, res: express.Response) {
|
async function addVideoChannel (req: express.Request, res: express.Response) {
|
||||||
const videoChannelInfo: VideoChannelCreate = req.body
|
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)
|
const account = await AccountModel.load(res.locals.oauth.token.User.Account.id, t)
|
||||||
|
|
||||||
return createVideoChannel(videoChannelInfo, account, t)
|
return createVideoChannel(videoChannelInfo, account, t)
|
||||||
|
|
|
@ -40,7 +40,7 @@ import { JobQueue } from '../../lib/job-queue'
|
||||||
import { CONFIG } from '../../initializers/config'
|
import { CONFIG } from '../../initializers/config'
|
||||||
import { sequelizeTypescript } from '../../initializers/database'
|
import { sequelizeTypescript } from '../../initializers/database'
|
||||||
import { createPlaylistMiniatureFromExisting } from '../../lib/thumbnail'
|
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 })
|
const reqThumbnailFile = createReqFiles([ 'thumbnailfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT, { thumbnailfile: CONFIG.STORAGE.TMP_DIR })
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ videoPlaylistRouter.get('/',
|
||||||
)
|
)
|
||||||
|
|
||||||
videoPlaylistRouter.get('/:playlistId',
|
videoPlaylistRouter.get('/:playlistId',
|
||||||
asyncMiddleware(videoPlaylistsGetValidator),
|
asyncMiddleware(videoPlaylistsGetValidator('summary')),
|
||||||
getVideoPlaylist
|
getVideoPlaylist
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ videoPlaylistRouter.delete('/:playlistId',
|
||||||
)
|
)
|
||||||
|
|
||||||
videoPlaylistRouter.get('/:playlistId/videos',
|
videoPlaylistRouter.get('/:playlistId/videos',
|
||||||
asyncMiddleware(videoPlaylistsGetValidator),
|
asyncMiddleware(videoPlaylistsGetValidator('summary')),
|
||||||
paginationValidator,
|
paginationValidator,
|
||||||
setDefaultPagination,
|
setDefaultPagination,
|
||||||
optionalAuthenticate,
|
optionalAuthenticate,
|
||||||
|
@ -140,7 +140,7 @@ async function listVideoPlaylists (req: express.Request, res: express.Response)
|
||||||
}
|
}
|
||||||
|
|
||||||
function getVideoPlaylist (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()) {
|
if (videoPlaylist.isOutdated()) {
|
||||||
JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'video-playlist', url: videoPlaylist.url } })
|
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,
|
description: videoPlaylistInfo.description,
|
||||||
privacy: videoPlaylistInfo.privacy || VideoPlaylistPrivacy.PRIVATE,
|
privacy: videoPlaylistInfo.privacy || VideoPlaylistPrivacy.PRIVATE,
|
||||||
ownerAccountId: user.Account.id
|
ownerAccountId: user.Account.id
|
||||||
})
|
}) as MVideoPlaylistFull
|
||||||
|
|
||||||
videoPlaylist.url = getVideoPlaylistActivityPubUrl(videoPlaylist) // We use the UUID, so set the URL after building the object
|
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)
|
? await createPlaylistMiniatureFromExisting(thumbnailField[0].path, videoPlaylist, false)
|
||||||
: undefined
|
: undefined
|
||||||
|
|
||||||
const videoPlaylistCreated: VideoPlaylistModel = await sequelizeTypescript.transaction(async t => {
|
const videoPlaylistCreated = await sequelizeTypescript.transaction(async t => {
|
||||||
const videoPlaylistCreated = await videoPlaylist.save({ transaction: t })
|
const videoPlaylistCreated = await videoPlaylist.save({ transaction: t }) as MVideoPlaylistFull
|
||||||
|
|
||||||
if (thumbnailModel) {
|
if (thumbnailModel) {
|
||||||
thumbnailModel.automaticallyGenerated = false
|
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) {
|
async function updateVideoPlaylist (req: express.Request, res: express.Response) {
|
||||||
const videoPlaylistInstance = res.locals.videoPlaylist
|
const videoPlaylistInstance = res.locals.videoPlaylistFull
|
||||||
const videoPlaylistFieldsSave = videoPlaylistInstance.toJSON()
|
const videoPlaylistFieldsSave = videoPlaylistInstance.toJSON()
|
||||||
const videoPlaylistInfoToUpdate = req.body as VideoPlaylistUpdate
|
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) {
|
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 sequelizeTypescript.transaction(async t => {
|
||||||
await videoPlaylistInstance.destroy({ transaction: 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) {
|
async function addVideoInPlaylist (req: express.Request, res: express.Response) {
|
||||||
const body: VideoPlaylistElementCreate = req.body
|
const body: VideoPlaylistElementCreate = req.body
|
||||||
const videoPlaylist = res.locals.videoPlaylist
|
const videoPlaylist = res.locals.videoPlaylistFull
|
||||||
const video = res.locals.video
|
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 position = await VideoPlaylistElementModel.getNextPositionOf(videoPlaylist.id, t)
|
||||||
|
|
||||||
const playlistElement = await VideoPlaylistElementModel.create({
|
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) {
|
async function updateVideoPlaylistElement (req: express.Request, res: express.Response) {
|
||||||
const body: VideoPlaylistElementUpdate = req.body
|
const body: VideoPlaylistElementUpdate = req.body
|
||||||
const videoPlaylist = res.locals.videoPlaylist
|
const videoPlaylist = res.locals.videoPlaylistFull
|
||||||
const videoPlaylistElement = res.locals.videoPlaylistElement
|
const videoPlaylistElement = res.locals.videoPlaylistElement
|
||||||
|
|
||||||
const playlistElement: VideoPlaylistElementModel = await sequelizeTypescript.transaction(async t => {
|
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) {
|
async function removeVideoFromPlaylist (req: express.Request, res: express.Response) {
|
||||||
const videoPlaylistElement = res.locals.videoPlaylistElement
|
const videoPlaylistElement = res.locals.videoPlaylistElement
|
||||||
const videoPlaylist = res.locals.videoPlaylist
|
const videoPlaylist = res.locals.videoPlaylistFull
|
||||||
const positionToDelete = videoPlaylistElement.position
|
const positionToDelete = videoPlaylistElement.position
|
||||||
|
|
||||||
await sequelizeTypescript.transaction(async t => {
|
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) {
|
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 body: VideoPlaylistReorder = req.body
|
||||||
|
|
||||||
const start: number = body.startPosition
|
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) {
|
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 user = res.locals.oauth ? res.locals.oauth.token.User : undefined
|
||||||
const server = await getServerActor()
|
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))
|
return res.json(getFormattedObjects(resultList.data, resultList.total, options))
|
||||||
}
|
}
|
||||||
|
|
||||||
async function regeneratePlaylistThumbnail (videoPlaylist: VideoPlaylistModel) {
|
async function regeneratePlaylistThumbnail (videoPlaylist: MVideoPlaylistThumbnail) {
|
||||||
await videoPlaylist.Thumbnail.destroy()
|
await videoPlaylist.Thumbnail.destroy()
|
||||||
videoPlaylist.Thumbnail = null
|
videoPlaylist.Thumbnail = null
|
||||||
|
|
||||||
|
@ -461,7 +461,7 @@ async function regeneratePlaylistThumbnail (videoPlaylist: VideoPlaylistModel) {
|
||||||
if (firstElement) await generateThumbnailForPlaylist(videoPlaylist, firstElement.Video)
|
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)
|
logger.info('Generating default thumbnail to playlist %s.', videoPlaylist.url)
|
||||||
|
|
||||||
const inputPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, video.getMiniature().filename)
|
const inputPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, video.getMiniature().filename)
|
||||||
|
|
|
@ -21,6 +21,7 @@ import { VideoAbuseModel } from '../../../models/video/video-abuse'
|
||||||
import { auditLoggerFactory, VideoAbuseAuditView } from '../../../helpers/audit-logger'
|
import { auditLoggerFactory, VideoAbuseAuditView } from '../../../helpers/audit-logger'
|
||||||
import { Notifier } from '../../../lib/notifier'
|
import { Notifier } from '../../../lib/notifier'
|
||||||
import { sendVideoAbuse } from '../../../lib/activitypub/send/send-flag'
|
import { sendVideoAbuse } from '../../../lib/activitypub/send/send-flag'
|
||||||
|
import { MVideoAbuseAccountVideo } from '../../../typings/models/video'
|
||||||
|
|
||||||
const auditLogger = auditLoggerFactory('abuse')
|
const auditLogger = auditLoggerFactory('abuse')
|
||||||
const abuseVideoRouter = express.Router()
|
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) {
|
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 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 reporterAccount = await AccountModel.load(res.locals.oauth.token.User.Account.id, t)
|
||||||
|
|
||||||
const abuseToCreate = {
|
const abuseToCreate = {
|
||||||
|
@ -107,7 +108,7 @@ async function reportVideoAbuse (req: express.Request, res: express.Response) {
|
||||||
state: VideoAbuseState.PENDING
|
state: VideoAbuseState.PENDING
|
||||||
}
|
}
|
||||||
|
|
||||||
const videoAbuseInstance = await VideoAbuseModel.create(abuseToCreate, { transaction: t })
|
const videoAbuseInstance: MVideoAbuseAccountVideo = await VideoAbuseModel.create(abuseToCreate, { transaction: t })
|
||||||
videoAbuseInstance.Video = videoInstance
|
videoAbuseInstance.Video = videoInstance
|
||||||
videoAbuseInstance.Account = reporterAccount
|
videoAbuseInstance.Account = reporterAccount
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import * as express from 'express'
|
import * as express from 'express'
|
||||||
import { VideoBlacklist, UserRight, VideoBlacklistCreate, VideoBlacklistType } from '../../../../shared'
|
import { UserRight, VideoBlacklistCreate, VideoBlacklistType } from '../../../../shared'
|
||||||
import { logger } from '../../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
import { getFormattedObjects } from '../../../helpers/utils'
|
import { getFormattedObjects } from '../../../helpers/utils'
|
||||||
import {
|
import {
|
||||||
|
@ -11,15 +11,16 @@ import {
|
||||||
setBlacklistSort,
|
setBlacklistSort,
|
||||||
setDefaultPagination,
|
setDefaultPagination,
|
||||||
videosBlacklistAddValidator,
|
videosBlacklistAddValidator,
|
||||||
|
videosBlacklistFiltersValidator,
|
||||||
videosBlacklistRemoveValidator,
|
videosBlacklistRemoveValidator,
|
||||||
videosBlacklistUpdateValidator,
|
videosBlacklistUpdateValidator
|
||||||
videosBlacklistFiltersValidator
|
|
||||||
} from '../../../middlewares'
|
} from '../../../middlewares'
|
||||||
import { VideoBlacklistModel } from '../../../models/video/video-blacklist'
|
import { VideoBlacklistModel } from '../../../models/video/video-blacklist'
|
||||||
import { sequelizeTypescript } from '../../../initializers'
|
import { sequelizeTypescript } from '../../../initializers'
|
||||||
import { Notifier } from '../../../lib/notifier'
|
import { Notifier } from '../../../lib/notifier'
|
||||||
import { sendDeleteVideo } from '../../../lib/activitypub/send'
|
import { sendDeleteVideo } from '../../../lib/activitypub/send'
|
||||||
import { federateVideoIfNeeded } from '../../../lib/activitypub'
|
import { federateVideoIfNeeded } from '../../../lib/activitypub'
|
||||||
|
import { MVideoBlacklistVideo } from '@server/typings/models'
|
||||||
|
|
||||||
const blacklistRouter = express.Router()
|
const blacklistRouter = express.Router()
|
||||||
|
|
||||||
|
@ -64,7 +65,7 @@ export {
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async function addVideoToBlacklist (req: express.Request, res: express.Response) {
|
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 body: VideoBlacklistCreate = req.body
|
||||||
|
|
||||||
const toCreate = {
|
const toCreate = {
|
||||||
|
@ -74,7 +75,7 @@ async function addVideoToBlacklist (req: express.Request, res: express.Response)
|
||||||
type: VideoBlacklistType.MANUAL
|
type: VideoBlacklistType.MANUAL
|
||||||
}
|
}
|
||||||
|
|
||||||
const blacklist = await VideoBlacklistModel.create(toCreate)
|
const blacklist: MVideoBlacklistVideo = await VideoBlacklistModel.create(toCreate)
|
||||||
blacklist.Video = videoInstance
|
blacklist.Video = videoInstance
|
||||||
|
|
||||||
if (body.unfederate === true) {
|
if (body.unfederate === true) {
|
||||||
|
@ -83,7 +84,7 @@ async function addVideoToBlacklist (req: express.Request, res: express.Response)
|
||||||
|
|
||||||
Notifier.Instance.notifyOnVideoBlacklist(blacklist)
|
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()
|
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) {
|
async function removeVideoFromBlacklistController (req: express.Request, res: express.Response) {
|
||||||
const videoBlacklist = res.locals.videoBlacklist
|
const videoBlacklist = res.locals.videoBlacklist
|
||||||
const video = res.locals.video
|
const video = res.locals.videoAll
|
||||||
|
|
||||||
const videoBlacklistType = await sequelizeTypescript.transaction(async t => {
|
const videoBlacklistType = await sequelizeTypescript.transaction(async t => {
|
||||||
const unfederated = videoBlacklist.unfederated
|
const unfederated = videoBlacklist.unfederated
|
||||||
|
@ -135,7 +136,7 @@ async function removeVideoFromBlacklistController (req: express.Request, res: ex
|
||||||
Notifier.Instance.notifyOnNewVideoIfNeeded(video)
|
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()
|
return res.type('json').status(204).end()
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { federateVideoIfNeeded } from '../../../lib/activitypub'
|
||||||
import { moveAndProcessCaptionFile } from '../../../helpers/captions-utils'
|
import { moveAndProcessCaptionFile } from '../../../helpers/captions-utils'
|
||||||
import { CONFIG } from '../../../initializers/config'
|
import { CONFIG } from '../../../initializers/config'
|
||||||
import { sequelizeTypescript } from '../../../initializers/database'
|
import { sequelizeTypescript } from '../../../initializers/database'
|
||||||
|
import { MVideoCaptionVideo } from '@server/typings/models'
|
||||||
|
|
||||||
const reqVideoCaptionAdd = createReqFiles(
|
const reqVideoCaptionAdd = createReqFiles(
|
||||||
[ 'captionfile' ],
|
[ 'captionfile' ],
|
||||||
|
@ -46,19 +47,19 @@ export {
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async function listVideoCaptions (req: express.Request, res: express.Response) {
|
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))
|
return res.json(getFormattedObjects(data, data.length))
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addVideoCaption (req: express.Request, res: express.Response) {
|
async function addVideoCaption (req: express.Request, res: express.Response) {
|
||||||
const videoCaptionPhysicalFile = req.files['captionfile'][0]
|
const videoCaptionPhysicalFile = req.files['captionfile'][0]
|
||||||
const video = res.locals.video
|
const video = res.locals.videoAll
|
||||||
|
|
||||||
const videoCaption = new VideoCaptionModel({
|
const videoCaption = new VideoCaptionModel({
|
||||||
videoId: video.id,
|
videoId: video.id,
|
||||||
language: req.params.captionLanguage
|
language: req.params.captionLanguage
|
||||||
})
|
}) as MVideoCaptionVideo
|
||||||
videoCaption.Video = video
|
videoCaption.Video = video
|
||||||
|
|
||||||
// Move physical file
|
// 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) {
|
async function deleteVideoCaption (req: express.Request, res: express.Response) {
|
||||||
const video = res.locals.video
|
const video = res.locals.videoAll
|
||||||
const videoCaption = res.locals.videoCaption
|
const videoCaption = res.locals.videoCaption
|
||||||
|
|
||||||
await sequelizeTypescript.transaction(async t => {
|
await sequelizeTypescript.transaction(async t => {
|
||||||
|
|
|
@ -27,9 +27,6 @@ import { auditLoggerFactory, CommentAuditView, getAuditIdFromRes } from '../../.
|
||||||
import { AccountModel } from '../../../models/account/account'
|
import { AccountModel } from '../../../models/account/account'
|
||||||
import { Notifier } from '../../../lib/notifier'
|
import { Notifier } from '../../../lib/notifier'
|
||||||
import { Hooks } from '../../../lib/plugins/hooks'
|
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'
|
import { sendDeleteVideoComment } from '../../../lib/activitypub/send'
|
||||||
|
|
||||||
const auditLogger = auditLoggerFactory('comments')
|
const auditLogger = auditLoggerFactory('comments')
|
||||||
|
@ -75,7 +72,7 @@ export {
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async function listVideoThreads (req: express.Request, res: express.Response) {
|
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
|
const user = res.locals.oauth ? res.locals.oauth.token.User : undefined
|
||||||
|
|
||||||
let resultList: ResultList<VideoCommentModel>
|
let resultList: ResultList<VideoCommentModel>
|
||||||
|
@ -86,7 +83,7 @@ async function listVideoThreads (req: express.Request, res: express.Response) {
|
||||||
start: req.query.start,
|
start: req.query.start,
|
||||||
count: req.query.count,
|
count: req.query.count,
|
||||||
sort: req.query.sort,
|
sort: req.query.sort,
|
||||||
user: user
|
user
|
||||||
}, 'filter:api.video-threads.list.params')
|
}, 'filter:api.video-threads.list.params')
|
||||||
|
|
||||||
resultList = await Hooks.wrapPromiseFun(
|
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) {
|
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
|
const user = res.locals.oauth ? res.locals.oauth.token.User : undefined
|
||||||
|
|
||||||
let resultList: ResultList<VideoCommentModel>
|
let resultList: ResultList<VideoCommentModel>
|
||||||
|
@ -141,7 +138,7 @@ async function addVideoCommentThread (req: express.Request, res: express.Respons
|
||||||
return createVideoComment({
|
return createVideoComment({
|
||||||
text: videoCommentInfo.text,
|
text: videoCommentInfo.text,
|
||||||
inReplyToComment: null,
|
inReplyToComment: null,
|
||||||
video: res.locals.video,
|
video: res.locals.videoAll,
|
||||||
account
|
account
|
||||||
}, t)
|
}, t)
|
||||||
})
|
})
|
||||||
|
@ -164,8 +161,8 @@ async function addVideoCommentReply (req: express.Request, res: express.Response
|
||||||
|
|
||||||
return createVideoComment({
|
return createVideoComment({
|
||||||
text: videoCommentInfo.text,
|
text: videoCommentInfo.text,
|
||||||
inReplyToComment: res.locals.videoComment,
|
inReplyToComment: res.locals.videoCommentFull,
|
||||||
video: res.locals.video,
|
video: res.locals.videoAll,
|
||||||
account
|
account
|
||||||
}, t)
|
}, t)
|
||||||
})
|
})
|
||||||
|
@ -179,7 +176,7 @@ async function addVideoCommentReply (req: express.Request, res: express.Response
|
||||||
}
|
}
|
||||||
|
|
||||||
async function removeVideoComment (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 sequelizeTypescript.transaction(async t => {
|
||||||
await videoCommentInstance.destroy({ transaction: t })
|
await videoCommentInstance.destroy({ transaction: t })
|
||||||
|
|
|
@ -15,7 +15,6 @@ import { VideoImportModel } from '../../../models/video/video-import'
|
||||||
import { JobQueue } from '../../../lib/job-queue/job-queue'
|
import { JobQueue } from '../../../lib/job-queue/job-queue'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import { isArray } from '../../../helpers/custom-validators/misc'
|
import { isArray } from '../../../helpers/custom-validators/misc'
|
||||||
import { VideoChannelModel } from '../../../models/video/video-channel'
|
|
||||||
import * as Bluebird from 'bluebird'
|
import * as Bluebird from 'bluebird'
|
||||||
import * as parseTorrent from 'parse-torrent'
|
import * as parseTorrent from 'parse-torrent'
|
||||||
import { getSecureTorrentName } from '../../../helpers/utils'
|
import { getSecureTorrentName } from '../../../helpers/utils'
|
||||||
|
@ -25,8 +24,14 @@ import { CONFIG } from '../../../initializers/config'
|
||||||
import { sequelizeTypescript } from '../../../initializers/database'
|
import { sequelizeTypescript } from '../../../initializers/database'
|
||||||
import { createVideoMiniatureFromExisting } from '../../../lib/thumbnail'
|
import { createVideoMiniatureFromExisting } from '../../../lib/thumbnail'
|
||||||
import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type'
|
import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type'
|
||||||
import { ThumbnailModel } from '../../../models/video/thumbnail'
|
import {
|
||||||
import { UserModel } from '../../../models/account/user'
|
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 auditLogger = auditLoggerFactory('video-imports')
|
||||||
const videoImportsRouter = express.Router()
|
const videoImportsRouter = express.Router()
|
||||||
|
@ -225,28 +230,28 @@ async function processPreview (req: express.Request, video: VideoModel) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function insertIntoDB (parameters: {
|
function insertIntoDB (parameters: {
|
||||||
video: VideoModel,
|
video: MVideoThumbnailAccountDefault,
|
||||||
thumbnailModel: ThumbnailModel,
|
thumbnailModel: MThumbnail,
|
||||||
previewModel: ThumbnailModel,
|
previewModel: MThumbnail,
|
||||||
videoChannel: VideoChannelModel,
|
videoChannel: MChannelActorAccountDefault,
|
||||||
tags: string[],
|
tags: string[],
|
||||||
videoImportAttributes: Partial<VideoImportModel>,
|
videoImportAttributes: Partial<MVideoImport>,
|
||||||
user: UserModel
|
user: MUser
|
||||||
}): Bluebird<VideoImportModel> {
|
}): Bluebird<MVideoImportVideo> {
|
||||||
const { video, thumbnailModel, previewModel, videoChannel, tags, videoImportAttributes, user } = parameters
|
const { video, thumbnailModel, previewModel, videoChannel, tags, videoImportAttributes, user } = parameters
|
||||||
|
|
||||||
return sequelizeTypescript.transaction(async t => {
|
return sequelizeTypescript.transaction(async t => {
|
||||||
const sequelizeOptions = { transaction: t }
|
const sequelizeOptions = { transaction: t }
|
||||||
|
|
||||||
// Save video object in database
|
// Save video object in database
|
||||||
const videoCreated = await video.save(sequelizeOptions)
|
const videoCreated = await video.save(sequelizeOptions) as (MVideoThumbnailAccountDefault & MVideoWithBlacklistLight)
|
||||||
videoCreated.VideoChannel = videoChannel
|
videoCreated.VideoChannel = videoChannel
|
||||||
|
|
||||||
if (thumbnailModel) await videoCreated.addAndSaveThumbnail(thumbnailModel, t)
|
if (thumbnailModel) await videoCreated.addAndSaveThumbnail(thumbnailModel, t)
|
||||||
if (previewModel) await videoCreated.addAndSaveThumbnail(previewModel, t)
|
if (previewModel) await videoCreated.addAndSaveThumbnail(previewModel, t)
|
||||||
|
|
||||||
await autoBlacklistVideoIfNeeded({
|
await autoBlacklistVideoIfNeeded({
|
||||||
video,
|
video: videoCreated,
|
||||||
user,
|
user,
|
||||||
notify: false,
|
notify: false,
|
||||||
isRemote: false,
|
isRemote: false,
|
||||||
|
@ -259,16 +264,13 @@ function insertIntoDB (parameters: {
|
||||||
const tagInstances = await TagModel.findOrCreateTags(tags, t)
|
const tagInstances = await TagModel.findOrCreateTags(tags, t)
|
||||||
|
|
||||||
await videoCreated.$set('Tags', tagInstances, sequelizeOptions)
|
await videoCreated.$set('Tags', tagInstances, sequelizeOptions)
|
||||||
videoCreated.Tags = tagInstances
|
|
||||||
} else {
|
|
||||||
videoCreated.Tags = []
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create video import object in database
|
// Create video import object in database
|
||||||
const videoImport = await VideoImportModel.create(
|
const videoImport = await VideoImportModel.create(
|
||||||
Object.assign({ videoId: videoCreated.id }, videoImportAttributes),
|
Object.assign({ videoId: videoCreated.id }, videoImportAttributes),
|
||||||
sequelizeOptions
|
sequelizeOptions
|
||||||
)
|
) as MVideoImportVideo
|
||||||
videoImport.Video = videoCreated
|
videoImport.Video = videoCreated
|
||||||
|
|
||||||
return videoImport
|
return videoImport
|
||||||
|
|
|
@ -63,6 +63,7 @@ import { createVideoMiniatureFromExisting, generateVideoMiniature } from '../../
|
||||||
import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type'
|
import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type'
|
||||||
import { VideoTranscodingPayload } from '../../../lib/job-queue/handlers/video-transcoding'
|
import { VideoTranscodingPayload } from '../../../lib/job-queue/handlers/video-transcoding'
|
||||||
import { Hooks } from '../../../lib/plugins/hooks'
|
import { Hooks } from '../../../lib/plugins/hooks'
|
||||||
|
import { MVideoFullLight } from '@server/typings/models'
|
||||||
|
|
||||||
const auditLogger = auditLoggerFactory('videos')
|
const auditLogger = auditLoggerFactory('videos')
|
||||||
const videosRouter = express.Router()
|
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 { videoCreated } = await sequelizeTypescript.transaction(async t => {
|
||||||
const sequelizeOptions = { transaction: 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(thumbnailModel, t)
|
||||||
await videoCreated.addAndSaveThumbnail(previewModel, 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) {
|
async function updateVideo (req: express.Request, res: express.Response) {
|
||||||
const videoInstance = res.locals.video
|
const videoInstance = res.locals.videoAll
|
||||||
const videoFieldsSave = videoInstance.toJSON()
|
const videoFieldsSave = videoInstance.toJSON()
|
||||||
const oldVideoAuditView = new VideoAuditView(videoInstance.toFormattedDetailsJSON())
|
const oldVideoAuditView = new VideoAuditView(videoInstance.toFormattedDetailsJSON())
|
||||||
const videoInfoToUpdate: VideoUpdate = req.body
|
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 (thumbnailModel) await videoInstanceUpdated.addAndSaveThumbnail(thumbnailModel, t)
|
||||||
if (previewModel) await videoInstanceUpdated.addAndSaveThumbnail(previewModel, 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(
|
const video = await Hooks.wrapPromiseFun(
|
||||||
VideoModel.loadForGetAPI,
|
VideoModel.loadForGetAPI,
|
||||||
{ id: res.locals.video.id, userId },
|
{ id: res.locals.onlyVideoWithRights.id, userId },
|
||||||
'filter:api.video.get.result'
|
'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) {
|
async function viewVideo (req: express.Request, res: express.Response) {
|
||||||
const videoInstance = res.locals.video
|
const videoInstance = res.locals.videoAll
|
||||||
|
|
||||||
const ip = req.ip
|
const ip = req.ip
|
||||||
const exists = await Redis.Instance.doesVideoIPViewExist(ip, videoInstance.uuid)
|
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) {
|
async function getVideoDescription (req: express.Request, res: express.Response) {
|
||||||
const videoInstance = res.locals.video
|
const videoInstance = res.locals.videoAll
|
||||||
let description = ''
|
let description = ''
|
||||||
|
|
||||||
if (videoInstance.isOwned()) {
|
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) {
|
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 sequelizeTypescript.transaction(async t => {
|
||||||
await videoInstance.destroy({ transaction: t })
|
await videoInstance.destroy({ transaction: t })
|
||||||
|
|
|
@ -18,6 +18,7 @@ import { getFormattedObjects } from '../../../helpers/utils'
|
||||||
import { changeVideoChannelShare } from '../../../lib/activitypub'
|
import { changeVideoChannelShare } from '../../../lib/activitypub'
|
||||||
import { sendUpdateVideo } from '../../../lib/activitypub/send'
|
import { sendUpdateVideo } from '../../../lib/activitypub/send'
|
||||||
import { VideoModel } from '../../../models/video/video'
|
import { VideoModel } from '../../../models/video/video'
|
||||||
|
import { MVideoFullLight } from '@server/typings/models'
|
||||||
|
|
||||||
const ownershipVideoRouter = express.Router()
|
const ownershipVideoRouter = express.Router()
|
||||||
|
|
||||||
|
@ -56,7 +57,7 @@ export {
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async function giveVideoOwnership (req: express.Request, res: express.Response) {
|
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 initiatorAccountId = res.locals.oauth.token.User.Account.id
|
||||||
const nextOwner = res.locals.nextOwner
|
const nextOwner = res.locals.nextOwner
|
||||||
|
|
||||||
|
@ -107,7 +108,7 @@ async function acceptOwnership (req: express.Request, res: express.Response) {
|
||||||
|
|
||||||
targetVideo.channelId = channel.id
|
targetVideo.channelId = channel.id
|
||||||
|
|
||||||
const targetVideoUpdated = await targetVideo.save({ transaction: t })
|
const targetVideoUpdated = await targetVideo.save({ transaction: t }) as MVideoFullLight
|
||||||
targetVideoUpdated.VideoChannel = channel
|
targetVideoUpdated.VideoChannel = channel
|
||||||
|
|
||||||
if (targetVideoUpdated.privacy !== VideoPrivacy.PRIVATE && targetVideoUpdated.state === VideoState.PUBLISHED) {
|
if (targetVideoUpdated.privacy !== VideoPrivacy.PRIVATE && targetVideoUpdated.state === VideoState.PUBLISHED) {
|
||||||
|
|
|
@ -27,7 +27,7 @@ export {
|
||||||
async function rateVideo (req: express.Request, res: express.Response) {
|
async function rateVideo (req: express.Request, res: express.Response) {
|
||||||
const body: UserVideoRateUpdate = req.body
|
const body: UserVideoRateUpdate = req.body
|
||||||
const rateType = body.rating
|
const rateType = body.rating
|
||||||
const videoInstance = res.locals.video
|
const videoInstance = res.locals.videoAll
|
||||||
const userAccount = res.locals.oauth.token.User.Account
|
const userAccount = res.locals.oauth.token.User.Account
|
||||||
|
|
||||||
await sequelizeTypescript.transaction(async t => {
|
await sequelizeTypescript.transaction(async t => {
|
||||||
|
|
|
@ -23,7 +23,7 @@ async function userWatchVideo (req: express.Request, res: express.Response) {
|
||||||
const user = res.locals.oauth.token.User
|
const user = res.locals.oauth.token.User
|
||||||
|
|
||||||
const body: UserWatchingVideo = req.body
|
const body: UserWatchingVideo = req.body
|
||||||
const { id: videoId } = res.locals.video as { id: number }
|
const { id: videoId } = res.locals.videoId
|
||||||
|
|
||||||
await UserVideoHistoryModel.upsert({
|
await UserVideoHistoryModel.upsert({
|
||||||
videoId,
|
videoId,
|
||||||
|
|
|
@ -43,7 +43,7 @@ export {
|
||||||
async function generateVideoCommentsFeed (req: express.Request, res: express.Response) {
|
async function generateVideoCommentsFeed (req: express.Request, res: express.Response) {
|
||||||
const start = 0
|
const start = 0
|
||||||
|
|
||||||
const video = res.locals.video
|
const video = res.locals.videoAll
|
||||||
const videoId: number = video ? video.id : undefined
|
const videoId: number = video ? video.id : undefined
|
||||||
|
|
||||||
const comments = await VideoCommentModel.listForFeed(start, FEEDS.COUNT, videoId)
|
const comments = await VideoCommentModel.listForFeed(start, FEEDS.COUNT, videoId)
|
||||||
|
|
|
@ -23,7 +23,7 @@ export {
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
function generateOEmbed (req: express.Request, res: express.Response) {
|
function generateOEmbed (req: express.Request, res: express.Response) {
|
||||||
const video = res.locals.video
|
const video = res.locals.videoAll
|
||||||
const webserverUrl = WEBSERVER.URL
|
const webserverUrl = WEBSERVER.URL
|
||||||
const maxHeight = parseInt(req.query.maxheight, 10)
|
const maxHeight = parseInt(req.query.maxheight, 10)
|
||||||
const maxWidth = parseInt(req.query.maxwidth, 10)
|
const maxWidth = parseInt(req.query.maxwidth, 10)
|
||||||
|
|
|
@ -226,14 +226,14 @@ async function generateNodeinfo (req: express.Request, res: express.Response) {
|
||||||
return res.send(json).end()
|
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)
|
const { video, videoFile } = getVideoAndFile(req, res)
|
||||||
if (!videoFile) return res.status(404).end()
|
if (!videoFile) return res.status(404).end()
|
||||||
|
|
||||||
return res.download(video.getTorrentFilePath(videoFile), `${video.name}-${videoFile.resolution}p.torrent`)
|
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)
|
const { video, videoFile } = getVideoAndFile(req, res)
|
||||||
if (!videoFile) return res.status(404).end()
|
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) {
|
function getVideoAndFile (req: express.Request, res: express.Response) {
|
||||||
const resolution = parseInt(req.params.resolution, 10)
|
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)
|
const videoFile = video.VideoFiles.find(f => f.resolution === resolution)
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ export {
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
function webfingerController (req: express.Request, res: express.Response) {
|
function webfingerController (req: express.Request, res: express.Response) {
|
||||||
const actor = res.locals.actor
|
const actor = res.locals.actorFull
|
||||||
|
|
||||||
const json = {
|
const json = {
|
||||||
subject: req.query.resource,
|
subject: req.query.resource,
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { ActorModel } from '../models/activitypub/actor'
|
||||||
import { signJsonLDObject } from './peertube-crypto'
|
import { signJsonLDObject } from './peertube-crypto'
|
||||||
import { pageToStartAndCount } from './core-utils'
|
import { pageToStartAndCount } from './core-utils'
|
||||||
import { parse } from 'url'
|
import { parse } from 'url'
|
||||||
|
import { MActor } from '../typings/models'
|
||||||
|
|
||||||
function activityPubContextify <T> (data: T) {
|
function activityPubContextify <T> (data: T) {
|
||||||
return Object.assign(data, {
|
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)
|
const activity = activityPubContextify(data)
|
||||||
|
|
||||||
return signJsonLDObject(byActor, activity) as Promise<Activity>
|
return signJsonLDObject(byActor, activity) as Promise<Activity>
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
import { ActorModel } from '../models/activitypub/actor'
|
import { ActorModel } from '../models/activitypub/actor'
|
||||||
|
import * as Bluebird from 'bluebird'
|
||||||
|
import { MActorFull, MActorAccountChannelId } from '../typings/models'
|
||||||
|
|
||||||
type ActorFetchByUrlType = 'all' | 'actor-and-association-ids'
|
type ActorFetchByUrlType = 'all' | 'association-ids'
|
||||||
function fetchActorByUrl (url: string, fetchType: ActorFetchByUrlType) {
|
|
||||||
|
function fetchActorByUrl (url: string, fetchType: ActorFetchByUrlType): Bluebird<MActorFull | MActorAccountChannelId> {
|
||||||
if (fetchType === 'all') return ActorModel.loadByUrlAndPopulateAccountAndChannel(url)
|
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 {
|
export {
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import { CONFIG } from '../initializers/config'
|
import { CONFIG } from '../initializers/config'
|
||||||
import { VideoCaptionModel } from '../models/video/video-caption'
|
|
||||||
import * as srt2vtt from 'srt-to-vtt'
|
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 videoCaptionsDir = CONFIG.STORAGE.CAPTIONS_DIR
|
||||||
const destination = join(videoCaptionsDir, videoCaption.getCaptionName())
|
const destination = join(videoCaptionsDir, videoCaption.getCaptionName())
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { Response } from 'express'
|
import { Response } from 'express'
|
||||||
import * as validator from 'validator'
|
|
||||||
import { VideoChangeOwnershipModel } from '../../models/video/video-change-ownership'
|
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> {
|
export async function doesChangeVideoOwnershipExist (id: number, res: Response) {
|
||||||
const videoChangeOwnership = await loadVideoChangeOwnership(id)
|
const videoChangeOwnership = await VideoChangeOwnershipModel.load(id)
|
||||||
|
|
||||||
if (!videoChangeOwnership) {
|
if (!videoChangeOwnership) {
|
||||||
res.status(404)
|
res.status(404)
|
||||||
|
@ -18,19 +18,7 @@ export async function doesChangeVideoOwnershipExist (id: string, res: Response):
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadVideoChangeOwnership (id: string): Promise<VideoChangeOwnershipModel | undefined> {
|
export function checkUserCanTerminateOwnershipChange (user: MUserId, videoChangeOwnership: MVideoChangeOwnershipFull, res: Response) {
|
||||||
if (validator.isInt(id)) {
|
|
||||||
return VideoChangeOwnershipModel.load(parseInt(id, 10))
|
|
||||||
}
|
|
||||||
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
export function checkUserCanTerminateOwnershipChange (
|
|
||||||
user: UserModel,
|
|
||||||
videoChangeOwnership: VideoChangeOwnershipModel,
|
|
||||||
res: Response
|
|
||||||
): boolean {
|
|
||||||
if (videoChangeOwnership.NextOwner.userId === user.id) {
|
if (videoChangeOwnership.NextOwner.userId === user.id) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Response } from 'express'
|
import { Response } from 'express'
|
||||||
import { AccountModel } from '../../models/account/account'
|
import { AccountModel } from '../../models/account/account'
|
||||||
import * as Bluebird from 'bluebird'
|
import * as Bluebird from 'bluebird'
|
||||||
|
import { MAccountDefault } from '../../typings/models'
|
||||||
|
|
||||||
function doesAccountIdExist (id: number, res: Response, sendNotFound = true) {
|
function doesAccountIdExist (id: number, res: Response, sendNotFound = true) {
|
||||||
const promise = AccountModel.load(id)
|
const promise = AccountModel.load(id)
|
||||||
|
@ -15,10 +16,12 @@ function doesLocalAccountNameExist (name: string, res: Response, sendNotFound =
|
||||||
}
|
}
|
||||||
|
|
||||||
function doesAccountNameWithHostExist (nameWithDomain: string, res: Response, sendNotFound = true) {
|
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
|
const account = await p
|
||||||
|
|
||||||
if (!account) {
|
if (!account) {
|
||||||
|
|
|
@ -1,41 +1,23 @@
|
||||||
import * as express from 'express'
|
import { Response } from 'express'
|
||||||
import { VideoChannelModel } from '../../models/video/video-channel'
|
import { VideoAbuseModel } from '../../models/video/video-abuse'
|
||||||
|
|
||||||
async function doesLocalVideoChannelNameExist (name: string, res: express.Response) {
|
async function doesVideoAbuseExist (abuseId: number, videoId: number, res: Response) {
|
||||||
const videoChannel = await VideoChannelModel.loadLocalByNameAndPopulateAccount(name)
|
const videoAbuse = await VideoAbuseModel.loadByIdAndVideoId(abuseId, videoId)
|
||||||
|
|
||||||
return processVideoChannelExist(videoChannel, res)
|
if (videoAbuse === null) {
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
res.status(404)
|
res.status(404)
|
||||||
.json({ error: 'Video channel not found' })
|
.json({ error: 'Video abuse not found' })
|
||||||
.end()
|
.end()
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
res.locals.videoChannel = videoChannel
|
res.locals.videoAbuse = videoAbuse
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export {
|
||||||
|
doesVideoAbuseExist
|
||||||
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { VideoModel } from '../../models/video/video'
|
|
||||||
import { Response } from 'express'
|
import { Response } from 'express'
|
||||||
import { VideoCaptionModel } from '../../models/video/video-caption'
|
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)
|
const videoCaption = await VideoCaptionModel.loadByVideoIdAndLanguage(video.id, language)
|
||||||
|
|
||||||
if (!videoCaption) {
|
if (!videoCaption) {
|
||||||
|
|
|
@ -1,23 +1,42 @@
|
||||||
import { Response } from 'express'
|
import * as express from 'express'
|
||||||
import { VideoAbuseModel } from '../../models/video/video-abuse'
|
import { VideoChannelModel } from '../../models/video/video-channel'
|
||||||
|
import { MChannelActorAccountDefault } from '../../typings/models'
|
||||||
|
|
||||||
async function doesVideoAbuseExist (abuseId: number, videoId: number, res: Response) {
|
async function doesLocalVideoChannelNameExist (name: string, res: express.Response) {
|
||||||
const videoAbuse = await VideoAbuseModel.loadByIdAndVideoId(abuseId, videoId)
|
const videoChannel = await VideoChannelModel.loadLocalByNameAndPopulateAccount(name)
|
||||||
|
|
||||||
if (videoAbuse === null) {
|
return processVideoChannelExist(videoChannel, res)
|
||||||
res.status(404)
|
}
|
||||||
.json({ error: 'Video abuse not found' })
|
|
||||||
.end()
|
|
||||||
|
|
||||||
return false
|
async function doesVideoChannelIdExist (id: number, res: express.Response) {
|
||||||
}
|
const videoChannel = await VideoChannelModel.loadAndPopulateAccount(+id)
|
||||||
|
|
||||||
res.locals.videoAbuse = videoAbuse
|
return processVideoChannelExist(videoChannel, res)
|
||||||
return true
|
}
|
||||||
|
|
||||||
|
async function doesVideoChannelNameWithHostExist (nameWithDomain: string, res: express.Response) {
|
||||||
|
const videoChannel = await VideoChannelModel.loadByNameWithHostAndPopulateAccount(nameWithDomain)
|
||||||
|
|
||||||
|
return processVideoChannelExist(videoChannel, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
export {
|
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
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,31 @@
|
||||||
import * as express from 'express'
|
import * as express from 'express'
|
||||||
import { VideoPlaylistModel } from '../../models/video/video-playlist'
|
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') {
|
export type VideoPlaylistFetchType = 'summary' | 'all'
|
||||||
const videoPlaylist = fetchType === 'summary'
|
async function doesVideoPlaylistExist (id: number | string, res: express.Response, fetchType: VideoPlaylistFetchType = 'summary') {
|
||||||
? await VideoPlaylistModel.loadWithAccountAndChannelSummary(id, undefined)
|
if (fetchType === 'summary') {
|
||||||
: await VideoPlaylistModel.loadWithAccountAndChannel(id, undefined)
|
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) {
|
if (!videoPlaylist) {
|
||||||
res.status(404)
|
res.status(404)
|
||||||
.json({ error: 'Video playlist not found' })
|
.json({ error: 'Video playlist not found' })
|
||||||
|
@ -14,12 +34,5 @@ async function doesVideoPlaylistExist (id: number | string, res: express.Respons
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
res.locals.videoPlaylist = videoPlaylist
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
export {
|
|
||||||
doesVideoPlaylistExist
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
import { Response } from 'express'
|
import { Response } from 'express'
|
||||||
import { fetchVideo, VideoFetchType } from '../video'
|
import { fetchVideo, VideoFetchType } from '../video'
|
||||||
import { UserModel } from '../../models/account/user'
|
|
||||||
import { UserRight } from '../../../shared/models/users'
|
import { UserRight } from '../../../shared/models/users'
|
||||||
import { VideoChannelModel } from '../../models/video/video-channel'
|
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') {
|
async function doesVideoExist (id: number | string, res: Response, fetchType: VideoFetchType = 'all') {
|
||||||
const userId = res.locals.oauth ? res.locals.oauth.token.User.id : undefined
|
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
|
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
|
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) {
|
if (user.hasRight(UserRight.UPDATE_ANY_VIDEO) === true) {
|
||||||
const videoChannel = await VideoChannelModel.loadAndPopulateAccount(channelId)
|
const videoChannel = await VideoChannelModel.loadAndPopulateAccount(channelId)
|
||||||
if (videoChannel === null) {
|
if (videoChannel === null) {
|
||||||
|
@ -50,7 +66,7 @@ async function doesVideoChannelOfAccountExist (channelId: number, user: UserMode
|
||||||
return true
|
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
|
// Retrieve the user who did the request
|
||||||
if (video.isOwned() === false) {
|
if (video.isOwned() === false) {
|
||||||
res.status(403)
|
res.status(403)
|
||||||
|
|
|
@ -8,6 +8,7 @@ import { cloneDeep } from 'lodash'
|
||||||
import { createVerify } from 'crypto'
|
import { createVerify } from 'crypto'
|
||||||
import { buildDigest } from '../lib/job-queue/handlers/utils/activitypub-http-utils'
|
import { buildDigest } from '../lib/job-queue/handlers/utils/activitypub-http-utils'
|
||||||
import * as bcrypt from 'bcrypt'
|
import * as bcrypt from 'bcrypt'
|
||||||
|
import { MActor } from '../typings/models'
|
||||||
|
|
||||||
const bcryptComparePromise = promisify2<any, string, boolean>(bcrypt.compare)
|
const bcryptComparePromise = promisify2<any, string, boolean>(bcrypt.compare)
|
||||||
const bcryptGenSaltPromise = promisify1<number, string>(bcrypt.genSalt)
|
const bcryptGenSaltPromise = promisify1<number, string>(bcrypt.genSalt)
|
||||||
|
@ -46,7 +47,7 @@ function isHTTPSignatureDigestValid (rawBody: Buffer, req: Request): boolean {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
function isHTTPSignatureVerified (httpSignatureParsed: any, actor: ActorModel): boolean {
|
function isHTTPSignatureVerified (httpSignatureParsed: any, actor: MActor): boolean {
|
||||||
return httpSignature.verifySignature(httpSignatureParsed, actor.publicKey) === true
|
return httpSignature.verifySignature(httpSignatureParsed, actor.publicKey) === true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,7 +57,7 @@ function parseHTTPSignature (req: Request, clockSkew?: number) {
|
||||||
|
|
||||||
// JSONLD
|
// JSONLD
|
||||||
|
|
||||||
async function isJsonLDSignatureVerified (fromActor: ActorModel, signedDocument: any): Promise<boolean> {
|
async function isJsonLDSignatureVerified (fromActor: MActor, signedDocument: any): Promise<boolean> {
|
||||||
if (signedDocument.signature.type === 'RsaSignature2017') {
|
if (signedDocument.signature.type === 'RsaSignature2017') {
|
||||||
// Mastodon algorithm
|
// Mastodon algorithm
|
||||||
const res = await isJsonLDRSA2017Verified(fromActor, signedDocument)
|
const res = await isJsonLDRSA2017Verified(fromActor, signedDocument)
|
||||||
|
@ -93,7 +94,7 @@ async function isJsonLDSignatureVerified (fromActor: ActorModel, signedDocument:
|
||||||
}
|
}
|
||||||
|
|
||||||
// Backward compatibility with "other" implementations
|
// 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> {
|
function hash (obj: any): Promise<any> {
|
||||||
return jsonld.promises
|
return jsonld.promises
|
||||||
.normalize(obj, {
|
.normalize(obj, {
|
||||||
|
@ -130,7 +131,7 @@ async function isJsonLDRSA2017Verified (fromActor: ActorModel, signedDocument: a
|
||||||
return verify.verify(fromActor.publicKey, signedDocument.signature.signatureValue, 'base64')
|
return verify.verify(fromActor.publicKey, signedDocument.signature.signatureValue, 'base64')
|
||||||
}
|
}
|
||||||
|
|
||||||
function signJsonLDObject (byActor: ActorModel, data: any) {
|
function signJsonLDObject (byActor: MActor, data: any) {
|
||||||
const options = {
|
const options = {
|
||||||
privateKeyPem: byActor.privateKey,
|
privateKeyPem: byActor.privateKey,
|
||||||
creator: byActor.url,
|
creator: byActor.url,
|
||||||
|
|
|
@ -1,8 +1,24 @@
|
||||||
import { VideoModel } from '../models/video/video'
|
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'
|
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 === 'all') return VideoModel.loadAndPopulateAccountAndServerAndTags(id, undefined, userId)
|
||||||
|
|
||||||
if (fetchType === 'only-video-with-rights') return VideoModel.loadWithRights(id)
|
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'
|
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 === 'all') return VideoModel.loadByUrlAndPopulateAccount(url)
|
||||||
|
|
||||||
if (fetchType === 'only-video') return VideoModel.loadByUrl(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 {
|
export {
|
||||||
VideoFetchType,
|
VideoFetchType,
|
||||||
VideoFetchByUrlType,
|
VideoFetchByUrlType,
|
||||||
fetchVideo,
|
fetchVideo,
|
||||||
|
getVideo,
|
||||||
fetchVideoByUrl
|
fetchVideoByUrl
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { ActorModel } from '../models/activitypub/actor'
|
||||||
import { isTestInstance } from './core-utils'
|
import { isTestInstance } from './core-utils'
|
||||||
import { isActivityPubUrlValid } from './custom-validators/activitypub/misc'
|
import { isActivityPubUrlValid } from './custom-validators/activitypub/misc'
|
||||||
import { WEBSERVER } from '../initializers/constants'
|
import { WEBSERVER } from '../initializers/constants'
|
||||||
|
import { MActorFull } from '../typings/models'
|
||||||
|
|
||||||
const webfinger = new WebFinger({
|
const webfinger = new WebFinger({
|
||||||
webfist_fallback: false,
|
webfist_fallback: false,
|
||||||
|
@ -17,7 +18,7 @@ async function loadActorUrlOrGetFromWebfinger (uriArg: string) {
|
||||||
const uri = uriArg.startsWith('@') ? uriArg.slice(1) : uriArg
|
const uri = uriArg.startsWith('@') ? uriArg.slice(1) : uriArg
|
||||||
|
|
||||||
const [ name, host ] = uri.split('@')
|
const [ name, host ] = uri.split('@')
|
||||||
let actor: ActorModel
|
let actor: MActorFull
|
||||||
|
|
||||||
if (!host || host === WEBSERVER.HOST) {
|
if (!host || host === WEBSERVER.HOST) {
|
||||||
actor = await ActorModel.loadLocalByName(name)
|
actor = await ActorModel.loadLocalByName(name)
|
||||||
|
|
|
@ -22,13 +22,25 @@ import { JobQueue } from '../job-queue'
|
||||||
import { getServerActor } from '../../helpers/utils'
|
import { getServerActor } from '../../helpers/utils'
|
||||||
import { ActorFetchByUrlType, fetchActorByUrl } from '../../helpers/actor'
|
import { ActorFetchByUrlType, fetchActorByUrl } from '../../helpers/actor'
|
||||||
import { sequelizeTypescript } from '../../initializers/database'
|
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
|
// 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()
|
return createPrivateAndPublicKeys()
|
||||||
.then(({ publicKey, privateKey }) => {
|
.then(({ publicKey, privateKey }) => {
|
||||||
actor.set('publicKey', publicKey)
|
actor.publicKey = publicKey
|
||||||
actor.set('privateKey', privateKey)
|
actor.privateKey = privateKey
|
||||||
return actor.save()
|
return actor.save()
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.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 (
|
async function getOrCreateActorAndServerAndModel (
|
||||||
activityActor: string | ActivityPubActor,
|
activityActor: string | ActivityPubActor,
|
||||||
fetchType: ActorFetchByUrlType = 'actor-and-association-ids',
|
fetchType: ActorFetchByUrlType = 'association-ids',
|
||||||
recurseIfNeeded = true,
|
recurseIfNeeded = true,
|
||||||
updateCollections = false
|
updateCollections = false
|
||||||
) {
|
): Promise<MActorFullActor | MActorAccountChannelId> {
|
||||||
const actorUrl = getAPId(activityActor)
|
const actorUrl = getAPId(activityActor)
|
||||||
let created = false
|
let created = false
|
||||||
let accountPlaylistsUrl: string
|
let accountPlaylistsUrl: string
|
||||||
|
@ -61,7 +87,7 @@ async function getOrCreateActorAndServerAndModel (
|
||||||
|
|
||||||
// Create the attributed to actor
|
// Create the attributed to actor
|
||||||
// In PeerTube a video channel is owned by an account
|
// In PeerTube a video channel is owned by an account
|
||||||
let ownerActor: ActorModel = undefined
|
let ownerActor: MActorFullActor
|
||||||
if (recurseIfNeeded === true && result.actor.type === 'Group') {
|
if (recurseIfNeeded === true && result.actor.type === 'Group') {
|
||||||
const accountAttributedTo = result.attributedTo.find(a => a.type === 'Person')
|
const accountAttributedTo = result.attributedTo.find(a => a.type === 'Person')
|
||||||
if (!accountAttributedTo) throw new Error('Cannot find account attributed to video channel ' + actor.url)
|
if (!accountAttributedTo) throw new Error('Cannot find account attributed to video channel ' + actor.url)
|
||||||
|
@ -85,8 +111,8 @@ async function getOrCreateActorAndServerAndModel (
|
||||||
accountPlaylistsUrl = result.playlists
|
accountPlaylistsUrl = result.playlists
|
||||||
}
|
}
|
||||||
|
|
||||||
if (actor.Account) actor.Account.Actor = actor
|
if (actor.Account) (actor as MActorAccountChannelIdActor).Account.Actor = actor
|
||||||
if (actor.VideoChannel) actor.VideoChannel.Actor = actor
|
if (actor.VideoChannel) (actor as MActorAccountChannelIdActor).VideoChannel.Actor = actor
|
||||||
|
|
||||||
const { actor: actorRefreshed, refreshed } = await retryTransactionWrapper(refreshActorIfNeeded, actor, fetchType)
|
const { actor: actorRefreshed, refreshed } = await retryTransactionWrapper(refreshActorIfNeeded, actor, fetchType)
|
||||||
if (!actorRefreshed) throw new Error('Actor ' + actorRefreshed.url + ' does not exist anymore.')
|
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
|
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 (info.name !== undefined) {
|
||||||
if (actor.avatarId) {
|
if (actor.avatarId) {
|
||||||
try {
|
try {
|
||||||
|
@ -212,14 +239,16 @@ async function addFetchOutboxJob (actor: Pick<ActorModel, 'id' | 'outboxUrl'>) {
|
||||||
return JobQueue.Instance.createJob({ type: 'activitypub-http-fetcher', payload })
|
return JobQueue.Instance.createJob({ type: 'activitypub-http-fetcher', payload })
|
||||||
}
|
}
|
||||||
|
|
||||||
async function refreshActorIfNeeded (
|
async function refreshActorIfNeeded <T extends MActorFull | MActorAccountChannelId> (
|
||||||
actorArg: ActorModel,
|
actorArg: T,
|
||||||
fetchedType: ActorFetchByUrlType
|
fetchedType: ActorFetchByUrlType
|
||||||
): Promise<{ actor: ActorModel, refreshed: boolean }> {
|
): Promise<{ actor: T | MActorFull, refreshed: boolean }> {
|
||||||
if (!actorArg.isOutdated()) return { actor: actorArg, refreshed: false }
|
if (!actorArg.isOutdated()) return { actor: actorArg, refreshed: false }
|
||||||
|
|
||||||
// We need more attributes
|
// 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 {
|
try {
|
||||||
let actorUrl: string
|
let actorUrl: string
|
||||||
|
@ -297,9 +326,9 @@ export {
|
||||||
|
|
||||||
function saveActorAndServerAndModelIfNotExist (
|
function saveActorAndServerAndModelIfNotExist (
|
||||||
result: FetchRemoteActorResult,
|
result: FetchRemoteActorResult,
|
||||||
ownerActor?: ActorModel,
|
ownerActor?: MActorFullActor,
|
||||||
t?: Transaction
|
t?: Transaction
|
||||||
): Bluebird<ActorModel> | Promise<ActorModel> {
|
): Bluebird<MActorFullActor> | Promise<MActorFullActor> {
|
||||||
let actor = result.actor
|
let actor = result.actor
|
||||||
|
|
||||||
if (t !== undefined) return save(t)
|
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
|
// Force the actor creation, sometimes Sequelize skips the save() when it thinks the instance already exists
|
||||||
// (which could be false in a retried query)
|
// (which could be false in a retried query)
|
||||||
const [ actorCreated ] = await ActorModel.findOrCreate({
|
const [ actorCreated ] = await ActorModel.findOrCreate<MActorFullActor>({
|
||||||
defaults: actor.toJSON(),
|
defaults: actor.toJSON(),
|
||||||
where: {
|
where: {
|
||||||
url: actor.url
|
url: actor.url
|
||||||
|
@ -345,10 +374,10 @@ function saveActorAndServerAndModelIfNotExist (
|
||||||
})
|
})
|
||||||
|
|
||||||
if (actorCreated.type === 'Person' || actorCreated.type === 'Application') {
|
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
|
actorCreated.Account.Actor = actorCreated
|
||||||
} else if (actorCreated.type === 'Group') { // Video channel
|
} 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.Actor = actorCreated
|
||||||
actorCreated.VideoChannel.Account = ownerActor.Account
|
actorCreated.VideoChannel.Account = ownerActor.Account
|
||||||
}
|
}
|
||||||
|
@ -360,7 +389,7 @@ function saveActorAndServerAndModelIfNotExist (
|
||||||
}
|
}
|
||||||
|
|
||||||
type FetchRemoteActorResult = {
|
type FetchRemoteActorResult = {
|
||||||
actor: ActorModel
|
actor: MActor
|
||||||
name: string
|
name: string
|
||||||
summary: string
|
summary: string
|
||||||
support?: 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({
|
const [ accountCreated ] = await AccountModel.findOrCreate({
|
||||||
defaults: {
|
defaults: {
|
||||||
name: result.name,
|
name: result.name,
|
||||||
|
@ -442,10 +471,10 @@ async function saveAccount (actor: ActorModel, result: FetchRemoteActorResult, t
|
||||||
transaction: 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({
|
const [ videoChannelCreated ] = await VideoChannelModel.findOrCreate({
|
||||||
defaults: {
|
defaults: {
|
||||||
name: result.name,
|
name: result.name,
|
||||||
|
@ -460,5 +489,5 @@ async function saveVideoChannel (actor: ActorModel, result: FetchRemoteActorResu
|
||||||
transaction: t
|
transaction: t
|
||||||
})
|
})
|
||||||
|
|
||||||
return videoChannelCreated
|
return videoChannelCreated as MChannel
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,10 @@ import { ActivityAudience } from '../../../shared/models/activitypub'
|
||||||
import { ACTIVITY_PUB } from '../../initializers/constants'
|
import { ACTIVITY_PUB } from '../../initializers/constants'
|
||||||
import { ActorModel } from '../../models/activitypub/actor'
|
import { ActorModel } from '../../models/activitypub/actor'
|
||||||
import { VideoModel } from '../../models/video/video'
|
import { VideoModel } from '../../models/video/video'
|
||||||
import { VideoCommentModel } from '../../models/video/video-comment'
|
|
||||||
import { VideoShareModel } from '../../models/video/video-share'
|
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 {
|
return {
|
||||||
to: [ video.VideoChannel.Account.Actor.url ],
|
to: [ video.VideoChannel.Account.Actor.url ],
|
||||||
cc: actorsInvolvedInVideo.map(a => a.followersUrl)
|
cc: actorsInvolvedInVideo.map(a => a.followersUrl)
|
||||||
|
@ -15,9 +14,9 @@ function getRemoteVideoAudience (video: VideoModel, actorsInvolvedInVideo: Actor
|
||||||
}
|
}
|
||||||
|
|
||||||
function getVideoCommentAudience (
|
function getVideoCommentAudience (
|
||||||
videoComment: VideoCommentModel,
|
videoComment: MCommentOwnerVideo,
|
||||||
threadParentComments: VideoCommentModel[],
|
threadParentComments: MCommentOwner[],
|
||||||
actorsInvolvedInVideo: ActorModel[],
|
actorsInvolvedInVideo: MActorFollowersUrl[],
|
||||||
isOrigin = false
|
isOrigin = false
|
||||||
): ActivityAudience {
|
): ActivityAudience {
|
||||||
const to = [ ACTIVITY_PUB.PUBLIC ]
|
const to = [ ACTIVITY_PUB.PUBLIC ]
|
||||||
|
@ -42,26 +41,28 @@ function getVideoCommentAudience (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAudienceFromFollowersOf (actorsInvolvedInObject: ActorModel[]): ActivityAudience {
|
function getAudienceFromFollowersOf (actorsInvolvedInObject: MActorFollowersUrl[]): ActivityAudience {
|
||||||
return {
|
return {
|
||||||
to: [ ACTIVITY_PUB.PUBLIC ].concat(actorsInvolvedInObject.map(a => a.followersUrl)),
|
to: [ ACTIVITY_PUB.PUBLIC ].concat(actorsInvolvedInObject.map(a => a.followersUrl)),
|
||||||
cc: []
|
cc: []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getActorsInvolvedInVideo (video: VideoModel, t: Transaction) {
|
async function getActorsInvolvedInVideo (video: MVideo, t: Transaction) {
|
||||||
const actors = await VideoShareModel.loadActorsByShare(video.id, t)
|
const actors: MActorLight[] = await VideoShareModel.loadActorsByShare(video.id, t)
|
||||||
|
|
||||||
const videoActor = video.VideoChannel && video.VideoChannel.Account
|
const videoAll = video as VideoModel
|
||||||
? video.VideoChannel.Account.Actor
|
|
||||||
: await ActorModel.loadAccountActorByVideoId(video.id, t)
|
const videoActor = videoAll.VideoChannel && videoAll.VideoChannel.Account
|
||||||
|
? videoAll.VideoChannel.Account.Actor
|
||||||
|
: await ActorModel.loadFromAccountByVideoId(video.id, t)
|
||||||
|
|
||||||
actors.push(videoActor)
|
actors.push(videoActor)
|
||||||
|
|
||||||
return actors
|
return actors
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAudience (actorSender: ActorModelOnly, isPublic = true) {
|
function getAudience (actorSender: MActorFollowersUrl, isPublic = true) {
|
||||||
return buildAudience([ actorSender.followersUrl ], isPublic)
|
return buildAudience([ actorSender.followersUrl ], isPublic)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { CacheFileObject } from '../../../shared/index'
|
import { CacheFileObject } from '../../../shared/index'
|
||||||
import { VideoModel } from '../../models/video/video'
|
|
||||||
import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy'
|
import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy'
|
||||||
import { Transaction } from 'sequelize'
|
import { Transaction } from 'sequelize'
|
||||||
import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type'
|
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') {
|
if (cacheFileObject.url.mediaType === 'application/x-mpegURL') {
|
||||||
const url = cacheFileObject.url
|
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)
|
const redundancyModel = await VideoRedundancyModel.loadByUrl(cacheFileObject.id, t)
|
||||||
|
|
||||||
if (!redundancyModel) {
|
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)
|
const attributes = cacheFileActivityObjectToDBAttributes(cacheFileObject, video, byActor)
|
||||||
|
|
||||||
return VideoRedundancyModel.create(attributes, { transaction: t })
|
return VideoRedundancyModel.create(attributes, { transaction: t })
|
||||||
|
@ -57,9 +57,9 @@ function createCacheFile (cacheFileObject: CacheFileObject, video: VideoModel, b
|
||||||
|
|
||||||
function updateCacheFile (
|
function updateCacheFile (
|
||||||
cacheFileObject: CacheFileObject,
|
cacheFileObject: CacheFileObject,
|
||||||
redundancyModel: VideoRedundancyModel,
|
redundancyModel: MVideoRedundancy,
|
||||||
video: VideoModel,
|
video: MVideoWithAllFiles,
|
||||||
byActor: { id?: number },
|
byActor: MActorId,
|
||||||
t: Transaction
|
t: Transaction
|
||||||
) {
|
) {
|
||||||
if (redundancyModel.actorId !== byActor.id) {
|
if (redundancyModel.actorId !== byActor.id) {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { PlaylistObject } from '../../../shared/models/activitypub/objects/playlist-object'
|
import { PlaylistObject } from '../../../shared/models/activitypub/objects/playlist-object'
|
||||||
import { crawlCollectionPage } from './crawl'
|
import { crawlCollectionPage } from './crawl'
|
||||||
import { ACTIVITY_PUB, CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants'
|
import { ACTIVITY_PUB, CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants'
|
||||||
import { AccountModel } from '../../models/account/account'
|
|
||||||
import { isArray } from '../../helpers/custom-validators/misc'
|
import { isArray } from '../../helpers/custom-validators/misc'
|
||||||
import { getOrCreateActorAndServerAndModel } from './actor'
|
import { getOrCreateActorAndServerAndModel } from './actor'
|
||||||
import { logger } from '../../helpers/logger'
|
import { logger } from '../../helpers/logger'
|
||||||
|
@ -13,14 +12,14 @@ import { PlaylistElementObject } from '../../../shared/models/activitypub/object
|
||||||
import { getOrCreateVideoAndAccountAndChannel } from './videos'
|
import { getOrCreateVideoAndAccountAndChannel } from './videos'
|
||||||
import { isPlaylistElementObjectValid, isPlaylistObjectValid } from '../../helpers/custom-validators/activitypub/playlist'
|
import { isPlaylistElementObjectValid, isPlaylistObjectValid } from '../../helpers/custom-validators/activitypub/playlist'
|
||||||
import { VideoPlaylistElementModel } from '../../models/video/video-playlist-element'
|
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 { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model'
|
||||||
import { sequelizeTypescript } from '../../initializers/database'
|
import { sequelizeTypescript } from '../../initializers/database'
|
||||||
import { createPlaylistMiniatureFromUrl } from '../thumbnail'
|
import { createPlaylistMiniatureFromUrl } from '../thumbnail'
|
||||||
import { FilteredModelAttributes } from '../../typings/sequelize'
|
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
|
const privacy = to.indexOf(ACTIVITY_PUB.PUBLIC) !== -1 ? VideoPlaylistPrivacy.PUBLIC : VideoPlaylistPrivacy.UNLISTED
|
||||||
|
|
||||||
return {
|
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 {
|
return {
|
||||||
position: elementObject.position,
|
position: elementObject.position,
|
||||||
url: elementObject.id,
|
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 => {
|
await Bluebird.map(playlistUrls, async playlistUrl => {
|
||||||
try {
|
try {
|
||||||
const exists = await VideoPlaylistModel.doesPlaylistExist(playlistUrl)
|
const exists = await VideoPlaylistModel.doesPlaylistExist(playlistUrl)
|
||||||
|
@ -75,7 +74,7 @@ async function createAccountPlaylists (playlistUrls: string[], account: AccountM
|
||||||
}, { concurrency: CRAWL_REQUEST_CONCURRENCY })
|
}, { 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)
|
const playlistAttributes = playlistObjectToDBAttributes(playlistObject, byAccount, to)
|
||||||
|
|
||||||
if (isArray(playlistObject.attributedTo) && playlistObject.attributedTo.length === 1) {
|
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[] = []
|
let accItems: string[] = []
|
||||||
await crawlCollectionPage<string>(playlistObject.id, items => {
|
await crawlCollectionPage<string>(playlistObject.id, items => {
|
||||||
|
@ -114,7 +113,7 @@ async function createOrUpdateVideoPlaylist (playlistObject: PlaylistObject, byAc
|
||||||
return resetVideoPlaylistElements(accItems, refreshedPlaylist)
|
return resetVideoPlaylistElements(accItems, refreshedPlaylist)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function refreshVideoPlaylistIfNeeded (videoPlaylist: VideoPlaylistModel): Promise<VideoPlaylistModel> {
|
async function refreshVideoPlaylistIfNeeded (videoPlaylist: MVideoPlaylistOwner): Promise<MVideoPlaylistOwner> {
|
||||||
if (!videoPlaylist.isOutdated()) return videoPlaylist
|
if (!videoPlaylist.isOutdated()) return videoPlaylist
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -157,7 +156,7 @@ export {
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async function resetVideoPlaylistElements (elementUrls: string[], playlist: VideoPlaylistModel) {
|
async function resetVideoPlaylistElements (elementUrls: string[], playlist: MVideoPlaylist) {
|
||||||
const elementsToCreate: FilteredModelAttributes<VideoPlaylistElementModel>[] = []
|
const elementsToCreate: FilteredModelAttributes<VideoPlaylistElementModel>[] = []
|
||||||
|
|
||||||
await Bluebird.map(elementUrls, async elementUrl => {
|
await Bluebird.map(elementUrls, async elementUrl => {
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
import { ActivityAccept } from '../../../../shared/models/activitypub'
|
import { ActivityAccept } from '../../../../shared/models/activitypub'
|
||||||
import { ActorModel } from '../../../models/activitypub/actor'
|
|
||||||
import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
|
import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
|
||||||
import { addFetchOutboxJob } from '../actor'
|
import { addFetchOutboxJob } from '../actor'
|
||||||
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
||||||
import { SignatureActorModel } from '../../../typings/models'
|
import { MActorDefault, MActorSignature } from '../../../typings/models'
|
||||||
|
|
||||||
async function processAcceptActivity (options: APProcessorOptions<ActivityAccept>) {
|
async function processAcceptActivity (options: APProcessorOptions<ActivityAccept>) {
|
||||||
const { byActor: targetActor, inboxActor } = options
|
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)
|
const follow = await ActorFollowModel.loadByActorAndTarget(actor.id, targetActor.id)
|
||||||
if (!follow) throw new Error('Cannot find associated follow.')
|
if (!follow) throw new Error('Cannot find associated follow.')
|
||||||
|
|
||||||
|
|
|
@ -5,10 +5,9 @@ import { VideoShareModel } from '../../../models/video/video-share'
|
||||||
import { forwardVideoRelatedActivity } from '../send/utils'
|
import { forwardVideoRelatedActivity } from '../send/utils'
|
||||||
import { getOrCreateVideoAndAccountAndChannel } from '../videos'
|
import { getOrCreateVideoAndAccountAndChannel } from '../videos'
|
||||||
import { Notifier } from '../../notifier'
|
import { Notifier } from '../../notifier'
|
||||||
import { VideoModel } from '../../../models/video/video'
|
|
||||||
import { logger } from '../../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
||||||
import { SignatureActorModel } from '../../../typings/models'
|
import { MActorSignature, MVideoAccountAllFiles } from '../../../typings/models'
|
||||||
|
|
||||||
async function processAnnounceActivity (options: APProcessorOptions<ActivityAnnounce>) {
|
async function processAnnounceActivity (options: APProcessorOptions<ActivityAnnounce>) {
|
||||||
const { activity, byActor: actorAnnouncer } = options
|
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
|
const objectUri = typeof activity.object === 'string' ? activity.object : activity.object.id
|
||||||
|
|
||||||
let video: VideoModel
|
let video: MVideoAccountAllFiles
|
||||||
let videoCreated: boolean
|
let videoCreated: boolean
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -10,10 +10,8 @@ import { createOrUpdateCacheFile } from '../cache-file'
|
||||||
import { Notifier } from '../../notifier'
|
import { Notifier } from '../../notifier'
|
||||||
import { PlaylistObject } from '../../../../shared/models/activitypub/objects/playlist-object'
|
import { PlaylistObject } from '../../../../shared/models/activitypub/objects/playlist-object'
|
||||||
import { createOrUpdateVideoPlaylist } from '../playlist'
|
import { createOrUpdateVideoPlaylist } from '../playlist'
|
||||||
import { VideoModel } from '../../../models/video/video'
|
|
||||||
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
||||||
import { VideoCommentModel } from '../../../models/video/video-comment'
|
import { MActorSignature, MCommentOwnerVideo, MVideoAccountAllFiles } from '../../../typings/models'
|
||||||
import { SignatureActorModel } from '../../../typings/models'
|
|
||||||
|
|
||||||
async function processCreateActivity (options: APProcessorOptions<ActivityCreate>) {
|
async function processCreateActivity (options: APProcessorOptions<ActivityCreate>) {
|
||||||
const { activity, byActor } = options
|
const { activity, byActor } = options
|
||||||
|
@ -61,7 +59,7 @@ async function processCreateVideo (activity: ActivityCreate, notify: boolean) {
|
||||||
return video
|
return video
|
||||||
}
|
}
|
||||||
|
|
||||||
async function processCreateCacheFile (activity: ActivityCreate, byActor: SignatureActorModel) {
|
async function processCreateCacheFile (activity: ActivityCreate, byActor: MActorSignature) {
|
||||||
const cacheFile = activity.object as CacheFileObject
|
const cacheFile = activity.object as CacheFileObject
|
||||||
|
|
||||||
const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: cacheFile.object })
|
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 commentObject = activity.object as VideoCommentObject
|
||||||
const byAccount = byActor.Account
|
const byAccount = byActor.Account
|
||||||
|
|
||||||
if (!byAccount) throw new Error('Cannot create video comment with the non account actor ' + byActor.url)
|
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 created: boolean
|
||||||
let comment: VideoCommentModel
|
let comment: MCommentOwnerVideo
|
||||||
try {
|
try {
|
||||||
const resolveThreadResult = await resolveThread({ url: commentObject.id, isVideo: false })
|
const resolveThreadResult = await resolveThread({ url: commentObject.id, isVideo: false })
|
||||||
video = resolveThreadResult.video
|
video = resolveThreadResult.video
|
||||||
|
@ -110,7 +108,7 @@ async function processCreateVideoComment (activity: ActivityCreate, byActor: Sig
|
||||||
if (created && notify) Notifier.Instance.notifyOnNewComment(comment)
|
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 playlistObject = activity.object as PlaylistObject
|
||||||
const byAccount = byActor.Account
|
const byAccount = byActor.Account
|
||||||
|
|
||||||
|
|
|
@ -2,15 +2,13 @@ import { ActivityDelete } from '../../../../shared/models/activitypub'
|
||||||
import { retryTransactionWrapper } from '../../../helpers/database-utils'
|
import { retryTransactionWrapper } from '../../../helpers/database-utils'
|
||||||
import { logger } from '../../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
import { sequelizeTypescript } from '../../../initializers'
|
import { sequelizeTypescript } from '../../../initializers'
|
||||||
import { AccountModel } from '../../../models/account/account'
|
|
||||||
import { ActorModel } from '../../../models/activitypub/actor'
|
import { ActorModel } from '../../../models/activitypub/actor'
|
||||||
import { VideoModel } from '../../../models/video/video'
|
import { VideoModel } from '../../../models/video/video'
|
||||||
import { VideoChannelModel } from '../../../models/video/video-channel'
|
|
||||||
import { VideoCommentModel } from '../../../models/video/video-comment'
|
import { VideoCommentModel } from '../../../models/video/video-comment'
|
||||||
import { forwardVideoRelatedActivity } from '../send/utils'
|
import { forwardVideoRelatedActivity } from '../send/utils'
|
||||||
import { VideoPlaylistModel } from '../../../models/video/video-playlist'
|
import { VideoPlaylistModel } from '../../../models/video/video-playlist'
|
||||||
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
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>) {
|
async function processDeleteActivity (options: APProcessorOptions<ActivityDelete>) {
|
||||||
const { activity, byActor } = options
|
const { activity, byActor } = options
|
||||||
|
@ -24,13 +22,17 @@ async function processDeleteActivity (options: APProcessorOptions<ActivityDelete
|
||||||
if (byActorFull.type === 'Person') {
|
if (byActorFull.type === 'Person') {
|
||||||
if (!byActorFull.Account) throw new Error('Actor ' + byActorFull.url + ' is a person but we cannot find it in database.')
|
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
|
const accountToDelete = byActorFull.Account as MAccountActor
|
||||||
return retryTransactionWrapper(processDeleteAccount, byActorFull.Account)
|
accountToDelete.Actor = byActorFull
|
||||||
|
|
||||||
|
return retryTransactionWrapper(processDeleteAccount, accountToDelete)
|
||||||
} else if (byActorFull.type === 'Group') {
|
} else if (byActorFull.type === 'Group') {
|
||||||
if (!byActorFull.VideoChannel) throw new Error('Actor ' + byActorFull.url + ' is a group but we cannot find it in database.')
|
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
|
const channelToDelete = byActorFull.VideoChannel as MChannelActorAccountActor
|
||||||
return retryTransactionWrapper(processDeleteVideoChannel, byActorFull.VideoChannel)
|
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)
|
logger.debug('Removing remote video "%s".', videoToDelete.uuid)
|
||||||
|
|
||||||
await sequelizeTypescript.transaction(async t => {
|
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)
|
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)
|
logger.debug('Removing remote video playlist "%s".', playlistToDelete.uuid)
|
||||||
|
|
||||||
await sequelizeTypescript.transaction(async t => {
|
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)
|
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)
|
logger.debug('Removing remote account "%s".', accountToRemove.Actor.url)
|
||||||
|
|
||||||
await sequelizeTypescript.transaction(async t => {
|
await sequelizeTypescript.transaction(async t => {
|
||||||
|
@ -108,7 +110,7 @@ async function processDeleteAccount (accountToRemove: AccountModel) {
|
||||||
logger.info('Remote account %s removed.', accountToRemove.Actor.url)
|
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)
|
logger.debug('Removing remote video channel "%s".', videoChannelToRemove.Actor.url)
|
||||||
|
|
||||||
await sequelizeTypescript.transaction(async t => {
|
await sequelizeTypescript.transaction(async t => {
|
||||||
|
@ -118,7 +120,7 @@ async function processDeleteVideoChannel (videoChannelToRemove: VideoChannelMode
|
||||||
logger.info('Remote video channel %s removed.', videoChannelToRemove.Actor.url)
|
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)
|
logger.debug('Removing remote video comment "%s".', videoComment.url)
|
||||||
|
|
||||||
return sequelizeTypescript.transaction(async t => {
|
return sequelizeTypescript.transaction(async t => {
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { getOrCreateVideoAndAccountAndChannel } from '../videos'
|
||||||
import { forwardVideoRelatedActivity } from '../send/utils'
|
import { forwardVideoRelatedActivity } from '../send/utils'
|
||||||
import { getVideoDislikeActivityPubUrl } from '../url'
|
import { getVideoDislikeActivityPubUrl } from '../url'
|
||||||
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
||||||
import { SignatureActorModel } from '../../../typings/models'
|
import { MActorSignature } from '../../../typings/models'
|
||||||
|
|
||||||
async function processDislikeActivity (options: APProcessorOptions<ActivityCreate | ActivityDislike>) {
|
async function processDislikeActivity (options: APProcessorOptions<ActivityCreate | ActivityDislike>) {
|
||||||
const { activity, byActor } = options
|
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 dislikeObject = activity.type === 'Dislike' ? activity.object : (activity.object as DislikeObject).object
|
||||||
const byAccount = byActor.Account
|
const byAccount = byActor.Account
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { getOrCreateVideoAndAccountAndChannel } from '../videos'
|
||||||
import { Notifier } from '../../notifier'
|
import { Notifier } from '../../notifier'
|
||||||
import { getAPId } from '../../../helpers/activitypub'
|
import { getAPId } from '../../../helpers/activitypub'
|
||||||
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
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>) {
|
async function processFlagActivity (options: APProcessorOptions<ActivityCreate | ActivityFlag>) {
|
||||||
const { activity, byActor } = options
|
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)
|
const flag = activity.type === 'Flag' ? activity : (activity.object as VideoAbuseObject)
|
||||||
|
|
||||||
logger.debug('Reporting remote abuse for video %s.', getAPId(flag.object))
|
logger.debug('Reporting remote abuse for video %s.', getAPId(flag.object))
|
||||||
|
@ -41,7 +41,7 @@ async function processCreateVideoAbuse (activity: ActivityCreate | ActivityFlag,
|
||||||
state: VideoAbuseState.PENDING
|
state: VideoAbuseState.PENDING
|
||||||
}
|
}
|
||||||
|
|
||||||
const videoAbuseInstance = await VideoAbuseModel.create(videoAbuseData, { transaction: t })
|
const videoAbuseInstance = await VideoAbuseModel.create(videoAbuseData, { transaction: t }) as MVideoAbuseVideo
|
||||||
videoAbuseInstance.Video = video
|
videoAbuseInstance.Video = video
|
||||||
|
|
||||||
logger.info('Remote abuse for video uuid %s created', flag.object)
|
logger.info('Remote abuse for video uuid %s created', flag.object)
|
||||||
|
|
|
@ -10,8 +10,7 @@ import { getAPId } from '../../../helpers/activitypub'
|
||||||
import { getServerActor } from '../../../helpers/utils'
|
import { getServerActor } from '../../../helpers/utils'
|
||||||
import { CONFIG } from '../../../initializers/config'
|
import { CONFIG } from '../../../initializers/config'
|
||||||
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
||||||
import { SignatureActorModel } from '../../../typings/models'
|
import { MAccount, MActorFollowActors, MActorFollowFull, MActorSignature } from '../../../typings/models'
|
||||||
import { ActorFollowModelLight } from '../../../typings/models/actor-follow'
|
|
||||||
|
|
||||||
async function processFollowActivity (options: APProcessorOptions<ActivityFollow>) {
|
async function processFollowActivity (options: APProcessorOptions<ActivityFollow>) {
|
||||||
const { activity, byActor } = options
|
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 { actorFollow, created, isFollowingInstance } = await sequelizeTypescript.transaction(async t => {
|
||||||
const targetActor = await ActorModel.loadByUrlAndPopulateAccountAndChannel(targetActorURL, t)
|
const targetActor = await ActorModel.loadByUrlAndPopulateAccountAndChannel(targetActorURL, t)
|
||||||
|
|
||||||
|
@ -43,10 +42,10 @@ async function processFollow (byActor: SignatureActorModel, targetActorURL: stri
|
||||||
|
|
||||||
await sendReject(byActor, targetActor)
|
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: {
|
where: {
|
||||||
actorId: byActor.id,
|
actorId: byActor.id,
|
||||||
targetActorId: targetActor.id
|
targetActorId: targetActor.id
|
||||||
|
@ -57,7 +56,7 @@ async function processFollow (byActor: SignatureActorModel, targetActorURL: stri
|
||||||
state: CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL ? 'pending' : 'accepted'
|
state: CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL ? 'pending' : 'accepted'
|
||||||
},
|
},
|
||||||
transaction: t
|
transaction: t
|
||||||
}) as [ ActorFollowModelLight, boolean ]
|
})
|
||||||
|
|
||||||
if (actorFollow.state !== 'accepted' && CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL === false) {
|
if (actorFollow.state !== 'accepted' && CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL === false) {
|
||||||
actorFollow.state = 'accepted'
|
actorFollow.state = 'accepted'
|
||||||
|
@ -77,8 +76,14 @@ async function processFollow (byActor: SignatureActorModel, targetActorURL: stri
|
||||||
if (!actorFollow) return
|
if (!actorFollow) return
|
||||||
|
|
||||||
if (created) {
|
if (created) {
|
||||||
if (isFollowingInstance) Notifier.Instance.notifyOfNewInstanceFollow(actorFollow)
|
if (isFollowingInstance) {
|
||||||
else Notifier.Instance.notifyOfNewUserFollow(actorFollow)
|
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)
|
logger.info('Actor %s is followed by actor %s.', targetActorURL, byActor.url)
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { getOrCreateVideoAndAccountAndChannel } from '../videos'
|
||||||
import { getVideoLikeActivityPubUrl } from '../url'
|
import { getVideoLikeActivityPubUrl } from '../url'
|
||||||
import { getAPId } from '../../../helpers/activitypub'
|
import { getAPId } from '../../../helpers/activitypub'
|
||||||
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
||||||
import { SignatureActorModel } from '../../../typings/models'
|
import { MActorSignature } from '../../../typings/models'
|
||||||
|
|
||||||
async function processLikeActivity (options: APProcessorOptions<ActivityLike>) {
|
async function processLikeActivity (options: APProcessorOptions<ActivityLike>) {
|
||||||
const { activity, byActor } = options
|
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 videoUrl = getAPId(activity.object)
|
||||||
|
|
||||||
const byAccount = byActor.Account
|
const byAccount = byActor.Account
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { ActivityReject } from '../../../../shared/models/activitypub/activity'
|
||||||
import { sequelizeTypescript } from '../../../initializers'
|
import { sequelizeTypescript } from '../../../initializers'
|
||||||
import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
|
import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
|
||||||
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
||||||
import { ActorModelOnly } from '../../../typings/models'
|
import { MActor } from '../../../typings/models'
|
||||||
|
|
||||||
async function processRejectActivity (options: APProcessorOptions<ActivityReject>) {
|
async function processRejectActivity (options: APProcessorOptions<ActivityReject>) {
|
||||||
const { byActor: targetActor, inboxActor } = options
|
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 => {
|
return sequelizeTypescript.transaction(async t => {
|
||||||
const actorFollow = await ActorFollowModel.loadByActorAndTarget(follower.id, targetActor.id, t)
|
const actorFollow = await ActorFollowModel.loadByActorAndTarget(follower.id, targetActor.id, t)
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ import { getOrCreateVideoAndAccountAndChannel } from '../videos'
|
||||||
import { VideoShareModel } from '../../../models/video/video-share'
|
import { VideoShareModel } from '../../../models/video/video-share'
|
||||||
import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy'
|
import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy'
|
||||||
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
||||||
import { SignatureActorModel } from '../../../typings/models'
|
import { MActorSignature } from '../../../typings/models'
|
||||||
|
|
||||||
async function processUndoActivity (options: APProcessorOptions<ActivityUndo>) {
|
async function processUndoActivity (options: APProcessorOptions<ActivityUndo>) {
|
||||||
const { activity, byActor } = options
|
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 likeActivity = activity.object as ActivityLike
|
||||||
|
|
||||||
const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: likeActivity.object })
|
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'
|
const dislike = activity.object.type === 'Dislike'
|
||||||
? activity.object
|
? activity.object
|
||||||
: activity.object.object as DislikeObject
|
: 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 cacheFileObject = activity.object.object as CacheFileObject
|
||||||
|
|
||||||
const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: cacheFileObject.object })
|
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 => {
|
return sequelizeTypescript.transaction(async t => {
|
||||||
const following = await ActorModel.loadByUrlAndPopulateAccountAndChannel(followActivity.object, t)
|
const following = await ActorModel.loadByUrlAndPopulateAccountAndChannel(followActivity.object, t)
|
||||||
const actorFollow = await ActorFollowModel.loadByActorAndTarget(follower.id, following.id, 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 => {
|
return sequelizeTypescript.transaction(async t => {
|
||||||
const share = await VideoShareModel.loadByUrl(announceActivity.id, t)
|
const share = await VideoShareModel.loadByUrl(announceActivity.id, t)
|
||||||
if (!share) throw new Error(`Unknown video share ${announceActivity.id}.`)
|
if (!share) throw new Error(`Unknown video share ${announceActivity.id}.`)
|
||||||
|
|
|
@ -15,7 +15,7 @@ import { forwardVideoRelatedActivity } from '../send/utils'
|
||||||
import { PlaylistObject } from '../../../../shared/models/activitypub/objects/playlist-object'
|
import { PlaylistObject } from '../../../../shared/models/activitypub/objects/playlist-object'
|
||||||
import { createOrUpdateVideoPlaylist } from '../playlist'
|
import { createOrUpdateVideoPlaylist } from '../playlist'
|
||||||
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
||||||
import { SignatureActorModel } from '../../../typings/models'
|
import { MActorSignature } from '../../../typings/models'
|
||||||
|
|
||||||
async function processUpdateActivity (options: APProcessorOptions<ActivityUpdate>) {
|
async function processUpdateActivity (options: APProcessorOptions<ActivityUpdate>) {
|
||||||
const { activity, byActor } = options
|
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
|
const videoObject = activity.object as VideoTorrentObject
|
||||||
|
|
||||||
if (sanitizeAndCheckVideoTorrentObject(videoObject) === false) {
|
if (sanitizeAndCheckVideoTorrentObject(videoObject) === false) {
|
||||||
|
@ -61,20 +61,20 @@ async function processUpdateVideo (actor: SignatureActorModel, activity: Activit
|
||||||
return undefined
|
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 channelActor = await getOrCreateVideoChannelFromVideoObject(videoObject)
|
||||||
|
|
||||||
const updateOptions = {
|
const updateOptions = {
|
||||||
video,
|
video,
|
||||||
videoObject,
|
videoObject,
|
||||||
account: actor.Account,
|
account: channelActor.VideoChannel.Account,
|
||||||
channel: channelActor.VideoChannel,
|
channel: channelActor.VideoChannel,
|
||||||
overrideTo: activity.to
|
overrideTo: activity.to
|
||||||
}
|
}
|
||||||
return updateVideoFromAP(updateOptions)
|
return updateVideoFromAP(updateOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function processUpdateCacheFile (byActor: SignatureActorModel, activity: ActivityUpdate) {
|
async function processUpdateCacheFile (byActor: MActorSignature, activity: ActivityUpdate) {
|
||||||
const cacheFileObject = activity.object as CacheFileObject
|
const cacheFileObject = activity.object as CacheFileObject
|
||||||
|
|
||||||
if (!isCacheFileObjectValid(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 playlistObject = activity.object as PlaylistObject
|
||||||
const byAccount = byActor.Account
|
const byAccount = byActor.Account
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { forwardVideoRelatedActivity } from '../send/utils'
|
||||||
import { Redis } from '../../redis'
|
import { Redis } from '../../redis'
|
||||||
import { ActivityCreate, ActivityView, ViewObject } from '../../../../shared/models/activitypub'
|
import { ActivityCreate, ActivityView, ViewObject } from '../../../../shared/models/activitypub'
|
||||||
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
||||||
import { SignatureActorModel } from '../../../typings/models'
|
import { MActorSignature } from '../../../typings/models'
|
||||||
|
|
||||||
async function processViewActivity (options: APProcessorOptions<ActivityCreate | ActivityView>) {
|
async function processViewActivity (options: APProcessorOptions<ActivityCreate | ActivityView>) {
|
||||||
const { activity, byActor } = options
|
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 videoObject = activity.type === 'View' ? activity.object : (activity.object as ViewObject).object
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
videoObject: videoObject,
|
videoObject,
|
||||||
fetchType: 'only-video' as 'only-video'
|
fetchType: 'only-video' as 'only-video'
|
||||||
}
|
}
|
||||||
const { video } = await getOrCreateVideoAndAccountAndChannel(options)
|
const { video } = await getOrCreateVideoAndAccountAndChannel(options)
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { Activity, ActivityType } from '../../../../shared/models/activitypub'
|
import { Activity, ActivityType } from '../../../../shared/models/activitypub'
|
||||||
import { checkUrlsSameHost, getAPId } from '../../../helpers/activitypub'
|
import { checkUrlsSameHost, getAPId } from '../../../helpers/activitypub'
|
||||||
import { logger } from '../../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
import { ActorModel } from '../../../models/activitypub/actor'
|
|
||||||
import { processAcceptActivity } from './process-accept'
|
import { processAcceptActivity } from './process-accept'
|
||||||
import { processAnnounceActivity } from './process-announce'
|
import { processAnnounceActivity } from './process-announce'
|
||||||
import { processCreateActivity } from './process-create'
|
import { processCreateActivity } from './process-create'
|
||||||
|
@ -16,7 +15,7 @@ import { processDislikeActivity } from './process-dislike'
|
||||||
import { processFlagActivity } from './process-flag'
|
import { processFlagActivity } from './process-flag'
|
||||||
import { processViewActivity } from './process-view'
|
import { processViewActivity } from './process-view'
|
||||||
import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
|
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> } = {
|
const processActivity: { [ P in ActivityType ]: (options: APProcessorOptions<Activity>) => Promise<any> } = {
|
||||||
Create: processCreateActivity,
|
Create: processCreateActivity,
|
||||||
|
@ -36,15 +35,15 @@ const processActivity: { [ P in ActivityType ]: (options: APProcessorOptions<Act
|
||||||
async function processActivities (
|
async function processActivities (
|
||||||
activities: Activity[],
|
activities: Activity[],
|
||||||
options: {
|
options: {
|
||||||
signatureActor?: SignatureActorModel
|
signatureActor?: MActorSignature
|
||||||
inboxActor?: ActorModel
|
inboxActor?: MActorDefault
|
||||||
outboxUrl?: string
|
outboxUrl?: string
|
||||||
fromFetch?: boolean
|
fromFetch?: boolean
|
||||||
} = {}
|
} = {}
|
||||||
) {
|
) {
|
||||||
const { outboxUrl, signatureActor, inboxActor, fromFetch = false } = options
|
const { outboxUrl, signatureActor, inboxActor, fromFetch = false } = options
|
||||||
|
|
||||||
const actorsCache: { [ url: string ]: SignatureActorModel } = {}
|
const actorsCache: { [ url: string ]: MActorSignature } = {}
|
||||||
|
|
||||||
for (const activity of activities) {
|
for (const activity of activities) {
|
||||||
if (!signatureActor && [ 'Create', 'Announce', 'Like' ].includes(activity.type) === false) {
|
if (!signatureActor && [ 'Create', 'Announce', 'Like' ].includes(activity.type) === false) {
|
||||||
|
@ -75,7 +74,7 @@ async function processActivities (
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await activityProcessor({ activity, byActor, inboxActor: inboxActor, fromFetch })
|
await activityProcessor({ activity, byActor, inboxActor, fromFetch })
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.warn('Cannot process activity %s.', activity.type, { err })
|
logger.warn('Cannot process activity %s.', activity.type, { err })
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,10 +3,9 @@ import { getActorFollowAcceptActivityPubUrl, getActorFollowActivityPubUrl } from
|
||||||
import { unicastTo } from './utils'
|
import { unicastTo } from './utils'
|
||||||
import { buildFollowActivity } from './send-follow'
|
import { buildFollowActivity } from './send-follow'
|
||||||
import { logger } from '../../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
import { ActorFollowModelLight } from '../../../typings/models/actor-follow'
|
import { MActor, MActorFollowActors } from '../../../typings/models'
|
||||||
import { ActorModelOnly } from '../../../typings/models'
|
|
||||||
|
|
||||||
async function sendAccept (actorFollow: ActorFollowModelLight) {
|
async function sendAccept (actorFollow: MActorFollowActors) {
|
||||||
const follower = actorFollow.ActorFollower
|
const follower = actorFollow.ActorFollower
|
||||||
const me = actorFollow.ActorFollowing
|
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 {
|
return {
|
||||||
type: 'Accept',
|
type: 'Accept',
|
||||||
id: url,
|
id: url,
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
import { Transaction } from 'sequelize'
|
import { Transaction } from 'sequelize'
|
||||||
import { ActivityAnnounce, ActivityAudience } from '../../../../shared/models/activitypub'
|
import { ActivityAnnounce, ActivityAudience } from '../../../../shared/models/activitypub'
|
||||||
import { VideoModel } from '../../../models/video/video'
|
|
||||||
import { broadcastToFollowers } from './utils'
|
import { broadcastToFollowers } from './utils'
|
||||||
import { audiencify, getActorsInvolvedInVideo, getAudience, getAudienceFromFollowersOf } from '../audience'
|
import { audiencify, getActorsInvolvedInVideo, getAudience, getAudienceFromFollowersOf } from '../audience'
|
||||||
import { logger } from '../../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
import { ActorModelOnly } from '../../../typings/models'
|
import { MActorLight, MVideo } from '../../../typings/models'
|
||||||
import { VideoShareModelOnly } from '../../../typings/models/video-share'
|
import { MVideoShare } from '../../../typings/models/video'
|
||||||
|
|
||||||
async function buildAnnounceWithVideoAudience (
|
async function buildAnnounceWithVideoAudience (
|
||||||
byActor: ActorModelOnly,
|
byActor: MActorLight,
|
||||||
videoShare: VideoShareModelOnly,
|
videoShare: MVideoShare,
|
||||||
video: VideoModel,
|
video: MVideo,
|
||||||
t: Transaction
|
t: Transaction
|
||||||
) {
|
) {
|
||||||
const announcedObject = video.url
|
const announcedObject = video.url
|
||||||
|
@ -23,7 +22,7 @@ async function buildAnnounceWithVideoAudience (
|
||||||
return { activity, actorsInvolvedInVideo }
|
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)
|
const { activity, actorsInvolvedInVideo } = await buildAnnounceWithVideoAudience(byActor, videoShare, video, t)
|
||||||
|
|
||||||
logger.info('Creating job to send announce %s.', videoShare.url)
|
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)
|
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)
|
if (!audience) audience = getAudience(byActor)
|
||||||
|
|
||||||
return audiencify({
|
return audiencify({
|
||||||
|
|
|
@ -1,19 +1,23 @@
|
||||||
import { Transaction } from 'sequelize'
|
import { Transaction } from 'sequelize'
|
||||||
import { ActivityAudience, ActivityCreate } from '../../../../shared/models/activitypub'
|
import { ActivityAudience, ActivityCreate } from '../../../../shared/models/activitypub'
|
||||||
import { VideoPrivacy } from '../../../../shared/models/videos'
|
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 { VideoCommentModel } from '../../../models/video/video-comment'
|
||||||
import { broadcastToActors, broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils'
|
import { broadcastToActors, broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils'
|
||||||
import { audiencify, getActorsInvolvedInVideo, getAudience, getAudienceFromFollowersOf, getVideoCommentAudience } from '../audience'
|
import { audiencify, getActorsInvolvedInVideo, getAudience, getAudienceFromFollowersOf, getVideoCommentAudience } from '../audience'
|
||||||
import { logger } from '../../../helpers/logger'
|
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 { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model'
|
||||||
import { getServerActor } from '../../../helpers/utils'
|
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
|
if (video.privacy === VideoPrivacy.PRIVATE) return undefined
|
||||||
|
|
||||||
logger.info('Creating job to send video creation of %s.', video.url)
|
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)
|
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)
|
logger.info('Creating job to send file cache of %s.', fileRedundancy.url)
|
||||||
|
|
||||||
return sendVideoRelatedCreateActivity({
|
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
|
if (playlist.privacy === VideoPlaylistPrivacy.PRIVATE) return undefined
|
||||||
|
|
||||||
logger.info('Creating job to send create video playlist of %s.', playlist.url)
|
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)
|
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)
|
logger.info('Creating job to send comment %s.', comment.url)
|
||||||
|
|
||||||
const isOrigin = comment.Video.isOwned()
|
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))
|
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)
|
if (!audience) audience = getAudience(byActor)
|
||||||
|
|
||||||
return audiencify(
|
return audiencify(
|
||||||
|
@ -122,8 +130,8 @@ export {
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async function sendVideoRelatedCreateActivity (options: {
|
async function sendVideoRelatedCreateActivity (options: {
|
||||||
byActor: ActorModel,
|
byActor: MActorLight,
|
||||||
video: VideoModel,
|
video: MVideoAccountLight,
|
||||||
url: string,
|
url: string,
|
||||||
object: any,
|
object: any,
|
||||||
transaction?: Transaction
|
transaction?: Transaction
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
import { Transaction } from 'sequelize'
|
import { Transaction } from 'sequelize'
|
||||||
import { ActivityAudience, ActivityDelete } from '../../../../shared/models/activitypub'
|
import { ActivityAudience, ActivityDelete } from '../../../../shared/models/activitypub'
|
||||||
import { ActorModel } from '../../../models/activitypub/actor'
|
import { ActorModel } from '../../../models/activitypub/actor'
|
||||||
import { VideoModel } from '../../../models/video/video'
|
|
||||||
import { VideoCommentModel } from '../../../models/video/video-comment'
|
import { VideoCommentModel } from '../../../models/video/video-comment'
|
||||||
import { VideoShareModel } from '../../../models/video/video-share'
|
import { VideoShareModel } from '../../../models/video/video-share'
|
||||||
import { getDeleteActivityPubUrl } from '../url'
|
import { getDeleteActivityPubUrl } from '../url'
|
||||||
import { broadcastToActors, broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils'
|
import { broadcastToActors, broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils'
|
||||||
import { audiencify, getActorsInvolvedInVideo, getVideoCommentAudience } from '../audience'
|
import { audiencify, getActorsInvolvedInVideo, getVideoCommentAudience } from '../audience'
|
||||||
import { logger } from '../../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
import { VideoPlaylistModel } from '../../../models/video/video-playlist'
|
|
||||||
import { getServerActor } from '../../../helpers/utils'
|
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)
|
logger.info('Creating job to broadcast delete of video %s.', video.url)
|
||||||
|
|
||||||
const byActor = video.VideoChannel.Account.Actor
|
const byActor = video.VideoChannel.Account.Actor
|
||||||
|
@ -42,7 +42,7 @@ async function sendDeleteActor (byActor: ActorModel, t: Transaction) {
|
||||||
return broadcastToFollowers(activity, byActor, actorsInvolved, t)
|
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)
|
logger.info('Creating job to send delete of comment %s.', videoComment.url)
|
||||||
|
|
||||||
const isVideoOrigin = videoComment.Video.isOwned()
|
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))
|
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)
|
logger.info('Creating job to send delete of playlist %s.', videoPlaylist.url)
|
||||||
|
|
||||||
const byActor = videoPlaylist.OwnerAccount.Actor
|
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 = {
|
const activity = {
|
||||||
type: 'Delete' as 'Delete',
|
type: 'Delete' as 'Delete',
|
||||||
id: url,
|
id: url,
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
import { Transaction } from 'sequelize'
|
import { Transaction } from 'sequelize'
|
||||||
import { ActorModel } from '../../../models/activitypub/actor'
|
|
||||||
import { VideoModel } from '../../../models/video/video'
|
|
||||||
import { getVideoDislikeActivityPubUrl } from '../url'
|
import { getVideoDislikeActivityPubUrl } from '../url'
|
||||||
import { logger } from '../../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
import { ActivityAudience, ActivityDislike } from '../../../../shared/models/activitypub'
|
import { ActivityAudience, ActivityDislike } from '../../../../shared/models/activitypub'
|
||||||
import { sendVideoRelatedActivity } from './utils'
|
import { sendVideoRelatedActivity } from './utils'
|
||||||
import { audiencify, getAudience } from '../audience'
|
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)
|
logger.info('Creating job to dislike %s.', video.url)
|
||||||
|
|
||||||
const activityBuilder = (audience: ActivityAudience) => {
|
const activityBuilder = (audience: ActivityAudience) => {
|
||||||
|
@ -19,7 +18,7 @@ async function sendDislike (byActor: ActorModel, video: VideoModel, t: Transacti
|
||||||
return sendVideoRelatedActivity(activityBuilder, { byActor, video, transaction: t })
|
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)
|
if (!audience) audience = getAudience(byActor)
|
||||||
|
|
||||||
return audiencify(
|
return audiencify(
|
||||||
|
|
|
@ -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 { getVideoAbuseActivityPubUrl } from '../url'
|
||||||
import { unicastTo } from './utils'
|
import { unicastTo } from './utils'
|
||||||
import { logger } from '../../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
import { ActivityAudience, ActivityFlag } from '../../../../shared/models/activitypub'
|
import { ActivityAudience, ActivityFlag } from '../../../../shared/models/activitypub'
|
||||||
import { audiencify, getAudience } from '../audience'
|
import { audiencify, getAudience } from '../audience'
|
||||||
import { Transaction } from 'sequelize'
|
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
|
if (!video.VideoChannel.Account.Actor.serverId) return // Local user
|
||||||
|
|
||||||
const url = getVideoAbuseActivityPubUrl(videoAbuse)
|
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))
|
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)
|
if (!audience) audience = getAudience(byActor)
|
||||||
|
|
||||||
const activity = Object.assign(
|
const activity = Object.assign(
|
||||||
|
|
|
@ -4,9 +4,9 @@ import { getActorFollowActivityPubUrl } from '../url'
|
||||||
import { unicastTo } from './utils'
|
import { unicastTo } from './utils'
|
||||||
import { logger } from '../../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
import { Transaction } from 'sequelize'
|
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 me = actorFollow.ActorFollower
|
||||||
const following = actorFollow.ActorFollowing
|
const following = actorFollow.ActorFollowing
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ function sendFollow (actorFollow: ActorFollowModel, t: Transaction) {
|
||||||
t.afterCommit(() => unicastTo(data, me, following.inboxUrl))
|
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 {
|
return {
|
||||||
type: 'Follow',
|
type: 'Follow',
|
||||||
id: url,
|
id: url,
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
import { Transaction } from 'sequelize'
|
import { Transaction } from 'sequelize'
|
||||||
import { ActivityAudience, ActivityLike } from '../../../../shared/models/activitypub'
|
import { ActivityAudience, ActivityLike } from '../../../../shared/models/activitypub'
|
||||||
import { ActorModel } from '../../../models/activitypub/actor'
|
|
||||||
import { VideoModel } from '../../../models/video/video'
|
|
||||||
import { getVideoLikeActivityPubUrl } from '../url'
|
import { getVideoLikeActivityPubUrl } from '../url'
|
||||||
import { sendVideoRelatedActivity } from './utils'
|
import { sendVideoRelatedActivity } from './utils'
|
||||||
import { audiencify, getAudience } from '../audience'
|
import { audiencify, getAudience } from '../audience'
|
||||||
import { logger } from '../../../helpers/logger'
|
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)
|
logger.info('Creating job to like %s.', video.url)
|
||||||
|
|
||||||
const activityBuilder = (audience: ActivityAudience) => {
|
const activityBuilder = (audience: ActivityAudience) => {
|
||||||
|
@ -19,7 +18,7 @@ async function sendLike (byActor: ActorModel, video: VideoModel, t: Transaction)
|
||||||
return sendVideoRelatedActivity(activityBuilder, { byActor, video, transaction: t })
|
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)
|
if (!audience) audience = getAudience(byActor)
|
||||||
|
|
||||||
return audiencify(
|
return audiencify(
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
import { ActivityFollow, ActivityReject } from '../../../../shared/models/activitypub'
|
import { ActivityFollow, ActivityReject } from '../../../../shared/models/activitypub'
|
||||||
import { ActorModel } from '../../../models/activitypub/actor'
|
|
||||||
import { getActorFollowActivityPubUrl, getActorFollowRejectActivityPubUrl } from '../url'
|
import { getActorFollowActivityPubUrl, getActorFollowRejectActivityPubUrl } from '../url'
|
||||||
import { unicastTo } from './utils'
|
import { unicastTo } from './utils'
|
||||||
import { buildFollowActivity } from './send-follow'
|
import { buildFollowActivity } from './send-follow'
|
||||||
import { logger } from '../../../helpers/logger'
|
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
|
if (!follower.serverId) { // This should never happen
|
||||||
logger.warn('Do not sending reject to local follower.')
|
logger.warn('Do not sending reject to local follower.')
|
||||||
return
|
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 {
|
return {
|
||||||
type: 'Reject',
|
type: 'Reject',
|
||||||
id: url,
|
id: url,
|
||||||
|
|
|
@ -2,13 +2,12 @@ import { Transaction } from 'sequelize'
|
||||||
import {
|
import {
|
||||||
ActivityAnnounce,
|
ActivityAnnounce,
|
||||||
ActivityAudience,
|
ActivityAudience,
|
||||||
ActivityCreate, ActivityDislike,
|
ActivityCreate,
|
||||||
|
ActivityDislike,
|
||||||
ActivityFollow,
|
ActivityFollow,
|
||||||
ActivityLike,
|
ActivityLike,
|
||||||
ActivityUndo
|
ActivityUndo
|
||||||
} from '../../../../shared/models/activitypub'
|
} from '../../../../shared/models/activitypub'
|
||||||
import { ActorModel } from '../../../models/activitypub/actor'
|
|
||||||
import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
|
|
||||||
import { VideoModel } from '../../../models/video/video'
|
import { VideoModel } from '../../../models/video/video'
|
||||||
import { getActorFollowActivityPubUrl, getUndoActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from '../url'
|
import { getActorFollowActivityPubUrl, getUndoActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from '../url'
|
||||||
import { broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils'
|
import { broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils'
|
||||||
|
@ -16,13 +15,20 @@ import { audiencify, getAudience } from '../audience'
|
||||||
import { buildCreateActivity } from './send-create'
|
import { buildCreateActivity } from './send-create'
|
||||||
import { buildFollowActivity } from './send-follow'
|
import { buildFollowActivity } from './send-follow'
|
||||||
import { buildLikeActivity } from './send-like'
|
import { buildLikeActivity } from './send-like'
|
||||||
import { VideoShareModel } from '../../../models/video/video-share'
|
|
||||||
import { buildAnnounceWithVideoAudience } from './send-announce'
|
import { buildAnnounceWithVideoAudience } from './send-announce'
|
||||||
import { logger } from '../../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy'
|
|
||||||
import { buildDislikeActivity } from './send-dislike'
|
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 me = actorFollow.ActorFollower
|
||||||
const following = actorFollow.ActorFollowing
|
const following = actorFollow.ActorFollowing
|
||||||
|
|
||||||
|
@ -40,7 +46,7 @@ async function sendUndoFollow (actorFollow: ActorFollowModel, t: Transaction) {
|
||||||
t.afterCommit(() => unicastTo(undoActivity, me, following.inboxUrl))
|
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)
|
logger.info('Creating job to undo announce %s.', videoShare.url)
|
||||||
|
|
||||||
const undoUrl = getUndoActivityPubUrl(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)
|
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)
|
logger.info('Creating job to undo a like of video %s.', video.url)
|
||||||
|
|
||||||
const likeUrl = getVideoLikeActivityPubUrl(byActor, video)
|
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 })
|
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)
|
logger.info('Creating job to undo a dislike of video %s.', video.url)
|
||||||
|
|
||||||
const dislikeUrl = getVideoDislikeActivityPubUrl(byActor, video)
|
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 })
|
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)
|
logger.info('Creating job to undo cache file %s.', redundancyModel.url)
|
||||||
|
|
||||||
const videoId = redundancyModel.getVideo().id
|
const videoId = redundancyModel.getVideo().id
|
||||||
|
@ -94,7 +100,7 @@ export {
|
||||||
|
|
||||||
function undoActivityData (
|
function undoActivityData (
|
||||||
url: string,
|
url: string,
|
||||||
byActor: ActorModel,
|
byActor: MActorAudience,
|
||||||
object: ActivityFollow | ActivityLike | ActivityDislike | ActivityCreate | ActivityAnnounce,
|
object: ActivityFollow | ActivityLike | ActivityDislike | ActivityCreate | ActivityAnnounce,
|
||||||
audience?: ActivityAudience
|
audience?: ActivityAudience
|
||||||
): ActivityUndo {
|
): ActivityUndo {
|
||||||
|
@ -112,8 +118,8 @@ function undoActivityData (
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sendUndoVideoRelatedActivity (options: {
|
async function sendUndoVideoRelatedActivity (options: {
|
||||||
byActor: ActorModel,
|
byActor: MActor,
|
||||||
video: VideoModel,
|
video: MVideoAccountLight,
|
||||||
url: string,
|
url: string,
|
||||||
activity: ActivityFollow | ActivityLike | ActivityDislike | ActivityCreate | ActivityAnnounce,
|
activity: ActivityFollow | ActivityLike | ActivityDislike | ActivityCreate | ActivityAnnounce,
|
||||||
transaction: Transaction
|
transaction: Transaction
|
||||||
|
|
|
@ -2,21 +2,29 @@ import { Transaction } from 'sequelize'
|
||||||
import { ActivityAudience, ActivityUpdate } from '../../../../shared/models/activitypub'
|
import { ActivityAudience, ActivityUpdate } from '../../../../shared/models/activitypub'
|
||||||
import { VideoPrivacy } from '../../../../shared/models/videos'
|
import { VideoPrivacy } from '../../../../shared/models/videos'
|
||||||
import { AccountModel } from '../../../models/account/account'
|
import { AccountModel } from '../../../models/account/account'
|
||||||
import { ActorModel } from '../../../models/activitypub/actor'
|
|
||||||
import { VideoModel } from '../../../models/video/video'
|
import { VideoModel } from '../../../models/video/video'
|
||||||
import { VideoChannelModel } from '../../../models/video/video-channel'
|
|
||||||
import { VideoShareModel } from '../../../models/video/video-share'
|
import { VideoShareModel } from '../../../models/video/video-share'
|
||||||
import { getUpdateActivityPubUrl } from '../url'
|
import { getUpdateActivityPubUrl } from '../url'
|
||||||
import { broadcastToFollowers, sendVideoRelatedActivity } from './utils'
|
import { broadcastToFollowers, sendVideoRelatedActivity } from './utils'
|
||||||
import { audiencify, getActorsInvolvedInVideo, getAudience } from '../audience'
|
import { audiencify, getActorsInvolvedInVideo, getAudience } from '../audience'
|
||||||
import { logger } from '../../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
import { VideoCaptionModel } from '../../../models/video/video-caption'
|
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 { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model'
|
||||||
import { getServerActor } from '../../../helpers/utils'
|
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
|
if (video.privacy === VideoPrivacy.PRIVATE) return undefined
|
||||||
|
|
||||||
logger.info('Creating job to update video %s.', video.url)
|
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)
|
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
|
const byActor = accountOrChannel.Actor
|
||||||
|
|
||||||
logger.info('Creating job to update actor %s.', byActor.url)
|
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 audience = getAudience(byActor)
|
||||||
const updateActivity = buildUpdateActivity(url, byActor, accountOrChannelObject, audience)
|
const updateActivity = buildUpdateActivity(url, byActor, accountOrChannelObject, audience)
|
||||||
|
|
||||||
let actorsInvolved: ActorModel[]
|
let actorsInvolved: MActor[]
|
||||||
if (accountOrChannel instanceof AccountModel) {
|
if (accountOrChannel instanceof AccountModel) {
|
||||||
// Actors that shared my videos are involved too
|
// Actors that shared my videos are involved too
|
||||||
actorsInvolved = await VideoShareModel.loadActorsWhoSharedVideosOf(byActor.id, t)
|
actorsInvolved = await VideoShareModel.loadActorsWhoSharedVideosOf(byActor.id, t)
|
||||||
|
@ -65,7 +73,7 @@ async function sendUpdateActor (accountOrChannel: AccountModel | VideoChannelMod
|
||||||
return broadcastToFollowers(updateActivity, byActor, actorsInvolved, t)
|
return broadcastToFollowers(updateActivity, byActor, actorsInvolved, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sendUpdateCacheFile (byActor: ActorModel, redundancyModel: VideoRedundancyModel) {
|
async function sendUpdateCacheFile (byActor: MActorLight, redundancyModel: MVideoRedundancyVideo) {
|
||||||
logger.info('Creating job to update cache file %s.', redundancyModel.url)
|
logger.info('Creating job to update cache file %s.', redundancyModel.url)
|
||||||
|
|
||||||
const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(redundancyModel.getVideo().id)
|
const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(redundancyModel.getVideo().id)
|
||||||
|
@ -80,7 +88,7 @@ async function sendUpdateCacheFile (byActor: ActorModel, redundancyModel: VideoR
|
||||||
return sendVideoRelatedActivity(activityBuilder, { byActor, video })
|
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
|
if (videoPlaylist.privacy === VideoPlaylistPrivacy.PRIVATE) return undefined
|
||||||
|
|
||||||
const byActor = videoPlaylist.OwnerAccount.Actor
|
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)
|
if (!audience) audience = getAudience(byActor)
|
||||||
|
|
||||||
return audiencify(
|
return audiencify(
|
||||||
|
@ -121,8 +129,7 @@ function buildUpdateActivity (url: string, byActor: ActorModel, object: any, aud
|
||||||
type: 'Update' as 'Update',
|
type: 'Update' as 'Update',
|
||||||
id: url,
|
id: url,
|
||||||
actor: byActor.url,
|
actor: byActor.url,
|
||||||
object: audiencify(object, audience
|
object: audiencify(object, audience)
|
||||||
)
|
|
||||||
},
|
},
|
||||||
audience
|
audience
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import { Transaction } from 'sequelize'
|
import { Transaction } from 'sequelize'
|
||||||
import { ActivityAudience, ActivityView } from '../../../../shared/models/activitypub'
|
import { ActivityAudience, ActivityView } from '../../../../shared/models/activitypub'
|
||||||
import { ActorModel } from '../../../models/activitypub/actor'
|
import { ActorModel } from '../../../models/activitypub/actor'
|
||||||
import { VideoModel } from '../../../models/video/video'
|
|
||||||
import { getVideoLikeActivityPubUrl } from '../url'
|
import { getVideoLikeActivityPubUrl } from '../url'
|
||||||
import { sendVideoRelatedActivity } from './utils'
|
import { sendVideoRelatedActivity } from './utils'
|
||||||
import { audiencify, getAudience } from '../audience'
|
import { audiencify, getAudience } from '../audience'
|
||||||
import { logger } from '../../../helpers/logger'
|
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)
|
logger.info('Creating job to send view of %s.', video.url)
|
||||||
|
|
||||||
const activityBuilder = (audience: ActivityAudience) => {
|
const activityBuilder = (audience: ActivityAudience) => {
|
||||||
|
@ -19,7 +19,7 @@ async function sendView (byActor: ActorModel, video: VideoModel, t: Transaction)
|
||||||
return sendVideoRelatedActivity(activityBuilder, { byActor, video, transaction: t })
|
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)
|
if (!audience) audience = getAudience(byActor)
|
||||||
|
|
||||||
return audiencify(
|
return audiencify(
|
||||||
|
|
|
@ -4,15 +4,14 @@ import { logger } from '../../../helpers/logger'
|
||||||
import { ActorModel } from '../../../models/activitypub/actor'
|
import { ActorModel } from '../../../models/activitypub/actor'
|
||||||
import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
|
import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
|
||||||
import { JobQueue } from '../../job-queue'
|
import { JobQueue } from '../../job-queue'
|
||||||
import { VideoModel } from '../../../models/video/video'
|
|
||||||
import { getActorsInvolvedInVideo, getAudienceFromFollowersOf, getRemoteVideoAudience } from '../audience'
|
import { getActorsInvolvedInVideo, getAudienceFromFollowersOf, getRemoteVideoAudience } from '../audience'
|
||||||
import { getServerActor } from '../../../helpers/utils'
|
import { getServerActor } from '../../../helpers/utils'
|
||||||
import { afterCommitIfTransaction } from '../../../helpers/database-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: {
|
async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAudience) => Activity, options: {
|
||||||
byActor: ActorModelOnly,
|
byActor: MActorLight,
|
||||||
video: VideoModel,
|
video: MVideoAccountLight,
|
||||||
transaction?: Transaction
|
transaction?: Transaction
|
||||||
}) {
|
}) {
|
||||||
const { byActor, video, transaction } = options
|
const { byActor, video, transaction } = options
|
||||||
|
@ -41,8 +40,8 @@ async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAud
|
||||||
async function forwardVideoRelatedActivity (
|
async function forwardVideoRelatedActivity (
|
||||||
activity: Activity,
|
activity: Activity,
|
||||||
t: Transaction,
|
t: Transaction,
|
||||||
followersException: ActorFollowerException[] = [],
|
followersException: MActorFollowerException[] = [],
|
||||||
video: VideoModel
|
video: MVideo
|
||||||
) {
|
) {
|
||||||
// Mastodon does not add our announces in audience, so we forward to them manually
|
// Mastodon does not add our announces in audience, so we forward to them manually
|
||||||
const additionalActors = await getActorsInvolvedInVideo(video, t)
|
const additionalActors = await getActorsInvolvedInVideo(video, t)
|
||||||
|
@ -54,7 +53,7 @@ async function forwardVideoRelatedActivity (
|
||||||
async function forwardActivity (
|
async function forwardActivity (
|
||||||
activity: Activity,
|
activity: Activity,
|
||||||
t: Transaction,
|
t: Transaction,
|
||||||
followersException: ActorFollowerException[] = [],
|
followersException: MActorFollowerException[] = [],
|
||||||
additionalFollowerUrls: string[] = []
|
additionalFollowerUrls: string[] = []
|
||||||
) {
|
) {
|
||||||
logger.info('Forwarding activity %s.', activity.id)
|
logger.info('Forwarding activity %s.', activity.id)
|
||||||
|
@ -88,10 +87,10 @@ async function forwardActivity (
|
||||||
|
|
||||||
async function broadcastToFollowers (
|
async function broadcastToFollowers (
|
||||||
data: any,
|
data: any,
|
||||||
byActor: ActorModelId,
|
byActor: MActorId,
|
||||||
toFollowersOf: ActorModelId[],
|
toFollowersOf: MActorId[],
|
||||||
t: Transaction,
|
t: Transaction,
|
||||||
actorsException: ActorFollowerException[] = []
|
actorsException: MActorFollowerException[] = []
|
||||||
) {
|
) {
|
||||||
const uris = await computeFollowerUris(toFollowersOf, actorsException, t)
|
const uris = await computeFollowerUris(toFollowersOf, actorsException, t)
|
||||||
|
|
||||||
|
@ -100,16 +99,16 @@ async function broadcastToFollowers (
|
||||||
|
|
||||||
async function broadcastToActors (
|
async function broadcastToActors (
|
||||||
data: any,
|
data: any,
|
||||||
byActor: ActorModelId,
|
byActor: MActorId,
|
||||||
toActors: ActorModelOnly[],
|
toActors: MActor[],
|
||||||
t?: Transaction,
|
t?: Transaction,
|
||||||
actorsException: ActorFollowerException[] = []
|
actorsException: MActorFollowerException[] = []
|
||||||
) {
|
) {
|
||||||
const uris = await computeUris(toActors, actorsException)
|
const uris = await computeUris(toActors, actorsException)
|
||||||
return afterCommitIfTransaction(t, () => broadcastTo(uris, data, byActor))
|
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
|
if (uris.length === 0) return undefined
|
||||||
|
|
||||||
logger.debug('Creating broadcast job.', { uris })
|
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 })
|
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 })
|
logger.debug('Creating unicast job.', { uri: toActorUrl })
|
||||||
|
|
||||||
const payload = {
|
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 toActorFollowerIds = toFollowersOf.map(a => a.id)
|
||||||
|
|
||||||
const result = await ActorFollowModel.listAcceptedFollowerSharedInboxUrls(toActorFollowerIds, t)
|
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)
|
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 serverActor = await getServerActor()
|
||||||
const targetUrls = toActors
|
const targetUrls = toActors
|
||||||
.filter(a => a.id !== serverActor.id) // Don't send to ourselves
|
.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)
|
.filter(sharedInbox => sharedInboxesException.indexOf(sharedInbox) === -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildSharedInboxesException (actorsException: ActorFollowerException[]) {
|
async function buildSharedInboxesException (actorsException: MActorFollowerException[]) {
|
||||||
const serverActor = await getServerActor()
|
const serverActor = await getServerActor()
|
||||||
|
|
||||||
return actorsException
|
return actorsException
|
||||||
|
|
|
@ -1,19 +1,18 @@
|
||||||
import { Transaction } from 'sequelize'
|
import { Transaction } from 'sequelize'
|
||||||
import { VideoPrivacy } from '../../../shared/models/videos'
|
import { VideoPrivacy } from '../../../shared/models/videos'
|
||||||
import { getServerActor } from '../../helpers/utils'
|
import { getServerActor } from '../../helpers/utils'
|
||||||
import { VideoModel } from '../../models/video/video'
|
|
||||||
import { VideoShareModel } from '../../models/video/video-share'
|
import { VideoShareModel } from '../../models/video/video-share'
|
||||||
import { sendUndoAnnounce, sendVideoAnnounce } from './send'
|
import { sendUndoAnnounce, sendVideoAnnounce } from './send'
|
||||||
import { getVideoAnnounceActivityPubUrl } from './url'
|
import { getVideoAnnounceActivityPubUrl } from './url'
|
||||||
import { VideoChannelModel } from '../../models/video/video-channel'
|
|
||||||
import * as Bluebird from 'bluebird'
|
import * as Bluebird from 'bluebird'
|
||||||
import { doRequest } from '../../helpers/requests'
|
import { doRequest } from '../../helpers/requests'
|
||||||
import { getOrCreateActorAndServerAndModel } from './actor'
|
import { getOrCreateActorAndServerAndModel } from './actor'
|
||||||
import { logger } from '../../helpers/logger'
|
import { logger } from '../../helpers/logger'
|
||||||
import { CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants'
|
import { CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants'
|
||||||
import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub'
|
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
|
if (video.privacy === VideoPrivacy.PRIVATE) return undefined
|
||||||
|
|
||||||
return Promise.all([
|
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)
|
logger.info('Updating video channel of video %s: %s -> %s.', video.uuid, oldVideoChannel.name, video.VideoChannel.name)
|
||||||
|
|
||||||
await undoShareByVideoChannel(video, oldVideoChannel, t)
|
await undoShareByVideoChannel(video, oldVideoChannel, t)
|
||||||
|
@ -30,7 +33,7 @@ async function changeVideoChannelShare (video: VideoModel, oldVideoChannel: Vide
|
||||||
await shareByVideoChannel(video, t)
|
await shareByVideoChannel(video, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addVideoShares (shareUrls: string[], instance: VideoModel) {
|
async function addVideoShares (shareUrls: string[], video: MVideoId) {
|
||||||
await Bluebird.map(shareUrls, async shareUrl => {
|
await Bluebird.map(shareUrls, async shareUrl => {
|
||||||
try {
|
try {
|
||||||
// Fetch url
|
// Fetch url
|
||||||
|
@ -50,7 +53,7 @@ async function addVideoShares (shareUrls: string[], instance: VideoModel) {
|
||||||
|
|
||||||
const entry = {
|
const entry = {
|
||||||
actorId: actor.id,
|
actorId: actor.id,
|
||||||
videoId: instance.id,
|
videoId: video.id,
|
||||||
url: shareUrl
|
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 serverActor = await getServerActor()
|
||||||
|
|
||||||
const serverShareUrl = getVideoAnnounceActivityPubUrl(serverActor, video)
|
const serverShareUrl = getVideoAnnounceActivityPubUrl(serverActor, video)
|
||||||
|
@ -88,7 +91,7 @@ async function shareByServer (video: VideoModel, t: Transaction) {
|
||||||
return sendVideoAnnounce(serverActor, serverShare, video, t)
|
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 videoChannelShareUrl = getVideoAnnounceActivityPubUrl(video.VideoChannel.Actor, video)
|
||||||
const [ videoChannelShare ] = await VideoShareModel.findOrCreate({
|
const [ videoChannelShare ] = await VideoShareModel.findOrCreate({
|
||||||
defaults: {
|
defaults: {
|
||||||
|
@ -105,7 +108,7 @@ async function shareByVideoChannel (video: VideoModel, t: Transaction) {
|
||||||
return sendVideoAnnounce(video.VideoChannel.Actor, videoChannelShare, video, t)
|
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
|
// Load old share
|
||||||
const oldShare = await VideoShareModel.load(oldVideoChannel.actorId, video.id, t)
|
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)
|
if (!oldShare) return new Error('Cannot find old video channel share ' + oldVideoChannel.actorId + ' for video ' + video.id)
|
||||||
|
|
|
@ -1,36 +1,42 @@
|
||||||
import { WEBSERVER } from '../../initializers/constants'
|
import { WEBSERVER } from '../../initializers/constants'
|
||||||
import { VideoModel } from '../../models/video/video'
|
import {
|
||||||
import { VideoAbuseModel } from '../../models/video/video-abuse'
|
MActor,
|
||||||
import { VideoCommentModel } from '../../models/video/video-comment'
|
MActorFollowActors,
|
||||||
import { VideoFileModel } from '../../models/video/video-file'
|
MActorId,
|
||||||
import { VideoStreamingPlaylistModel } from '../../models/video/video-streaming-playlist'
|
MActorUrl,
|
||||||
import { VideoPlaylistModel } from '../../models/video/video-playlist'
|
MCommentId,
|
||||||
import { ActorModelOnly, ActorModelUrl } from '../../typings/models'
|
MVideoAbuseId,
|
||||||
import { ActorFollowModelLight } from '../../typings/models/actor-follow'
|
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
|
return WEBSERVER.URL + '/videos/watch/' + video.uuid
|
||||||
}
|
}
|
||||||
|
|
||||||
function getVideoPlaylistActivityPubUrl (videoPlaylist: VideoPlaylistModel) {
|
function getVideoPlaylistActivityPubUrl (videoPlaylist: MVideoPlaylist) {
|
||||||
return WEBSERVER.URL + '/video-playlists/' + videoPlaylist.uuid
|
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
|
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 : ''
|
const suffixFPS = videoFile.fps && videoFile.fps !== -1 ? '-' + videoFile.fps : ''
|
||||||
|
|
||||||
return `${WEBSERVER.URL}/redundancy/videos/${videoFile.Video.uuid}/${videoFile.resolution}${suffixFPS}`
|
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}`
|
return `${WEBSERVER.URL}/redundancy/streaming-playlists/${playlist.getStringType()}/${video.uuid}`
|
||||||
}
|
}
|
||||||
|
|
||||||
function getVideoCommentActivityPubUrl (video: VideoModel, videoComment: VideoCommentModel) {
|
function getVideoCommentActivityPubUrl (video: MVideoUUID, videoComment: MCommentId) {
|
||||||
return WEBSERVER.URL + '/videos/watch/' + video.uuid + '/comments/' + videoComment.id
|
return WEBSERVER.URL + '/videos/watch/' + video.uuid + '/comments/' + videoComment.id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,54 +48,54 @@ function getAccountActivityPubUrl (accountName: string) {
|
||||||
return WEBSERVER.URL + '/accounts/' + accountName
|
return WEBSERVER.URL + '/accounts/' + accountName
|
||||||
}
|
}
|
||||||
|
|
||||||
function getVideoAbuseActivityPubUrl (videoAbuse: VideoAbuseModel) {
|
function getVideoAbuseActivityPubUrl (videoAbuse: MVideoAbuseId) {
|
||||||
return WEBSERVER.URL + '/admin/video-abuses/' + videoAbuse.id
|
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()
|
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
|
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
|
return byActor.url + '/dislikes/' + video.id
|
||||||
}
|
}
|
||||||
|
|
||||||
function getVideoSharesActivityPubUrl (video: VideoModel) {
|
function getVideoSharesActivityPubUrl (video: MVideoUrl) {
|
||||||
return video.url + '/announces'
|
return video.url + '/announces'
|
||||||
}
|
}
|
||||||
|
|
||||||
function getVideoCommentsActivityPubUrl (video: VideoModel) {
|
function getVideoCommentsActivityPubUrl (video: MVideoUrl) {
|
||||||
return video.url + '/comments'
|
return video.url + '/comments'
|
||||||
}
|
}
|
||||||
|
|
||||||
function getVideoLikesActivityPubUrl (video: VideoModel) {
|
function getVideoLikesActivityPubUrl (video: MVideoUrl) {
|
||||||
return video.url + '/likes'
|
return video.url + '/likes'
|
||||||
}
|
}
|
||||||
|
|
||||||
function getVideoDislikesActivityPubUrl (video: VideoModel) {
|
function getVideoDislikesActivityPubUrl (video: MVideoUrl) {
|
||||||
return video.url + '/dislikes'
|
return video.url + '/dislikes'
|
||||||
}
|
}
|
||||||
|
|
||||||
function getActorFollowActivityPubUrl (follower: ActorModelOnly, following: ActorModelOnly) {
|
function getActorFollowActivityPubUrl (follower: MActor, following: MActorId) {
|
||||||
return follower.url + '/follows/' + following.id
|
return follower.url + '/follows/' + following.id
|
||||||
}
|
}
|
||||||
|
|
||||||
function getActorFollowAcceptActivityPubUrl (actorFollow: ActorFollowModelLight) {
|
function getActorFollowAcceptActivityPubUrl (actorFollow: MActorFollowActors) {
|
||||||
const follower = actorFollow.ActorFollower
|
const follower = actorFollow.ActorFollower
|
||||||
const me = actorFollow.ActorFollowing
|
const me = actorFollow.ActorFollowing
|
||||||
|
|
||||||
return follower.url + '/accepts/follows/' + me.id
|
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
|
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
|
return video.url + '/announces/' + byActor.id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,20 +2,20 @@ import { sanitizeAndCheckVideoCommentObject } from '../../helpers/custom-validat
|
||||||
import { logger } from '../../helpers/logger'
|
import { logger } from '../../helpers/logger'
|
||||||
import { doRequest } from '../../helpers/requests'
|
import { doRequest } from '../../helpers/requests'
|
||||||
import { ACTIVITY_PUB, CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants'
|
import { ACTIVITY_PUB, CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants'
|
||||||
import { VideoModel } from '../../models/video/video'
|
|
||||||
import { VideoCommentModel } from '../../models/video/video-comment'
|
import { VideoCommentModel } from '../../models/video/video-comment'
|
||||||
import { getOrCreateActorAndServerAndModel } from './actor'
|
import { getOrCreateActorAndServerAndModel } from './actor'
|
||||||
import { getOrCreateVideoAndAccountAndChannel } from './videos'
|
import { getOrCreateVideoAndAccountAndChannel } from './videos'
|
||||||
import * as Bluebird from 'bluebird'
|
import * as Bluebird from 'bluebird'
|
||||||
import { checkUrlsSameHost } from '../../helpers/activitypub'
|
import { checkUrlsSameHost } from '../../helpers/activitypub'
|
||||||
|
import { MCommentOwner, MCommentOwnerVideo, MVideoAccountAllFiles } from '../../typings/models/video'
|
||||||
|
|
||||||
type ResolveThreadParams = {
|
type ResolveThreadParams = {
|
||||||
url: string,
|
url: string,
|
||||||
comments?: VideoCommentModel[],
|
comments?: MCommentOwner[],
|
||||||
isVideo?: boolean,
|
isVideo?: boolean,
|
||||||
commentCreated?: 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[]) {
|
async function addVideoComments (commentUrls: string[]) {
|
||||||
return Bluebird.map(commentUrls, commentUrl => {
|
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 syncParam = { likes: true, dislikes: true, shares: true, comments: false, thumbnail: true, refreshVideo: false }
|
||||||
const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: url, syncParam })
|
const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: url, syncParam })
|
||||||
|
|
||||||
let resultComment: VideoCommentModel
|
let resultComment: MCommentOwnerVideo
|
||||||
if (comments.length !== 0) {
|
if (comments.length !== 0) {
|
||||||
const firstReply = comments[ comments.length - 1 ]
|
const firstReply = comments[ comments.length - 1 ] as MCommentOwnerVideo
|
||||||
firstReply.inReplyToCommentId = null
|
firstReply.inReplyToCommentId = null
|
||||||
firstReply.originCommentId = null
|
firstReply.originCommentId = null
|
||||||
firstReply.videoId = video.id
|
firstReply.videoId = video.id
|
||||||
|
@ -97,7 +97,7 @@ async function tryResolveThreadFromVideo (params: ResolveThreadParams) {
|
||||||
comments[comments.length - 1] = await firstReply.save()
|
comments[comments.length - 1] = await firstReply.save()
|
||||||
|
|
||||||
for (let i = comments.length - 2; i >= 0; i--) {
|
for (let i = comments.length - 2; i >= 0; i--) {
|
||||||
const comment = comments[ i ]
|
const comment = comments[ i ] as MCommentOwnerVideo
|
||||||
comment.originCommentId = firstReply.id
|
comment.originCommentId = firstReply.id
|
||||||
comment.inReplyToCommentId = comments[ i + 1 ].id
|
comment.inReplyToCommentId = comments[ i + 1 ].id
|
||||||
comment.videoId = video.id
|
comment.videoId = video.id
|
||||||
|
@ -107,7 +107,7 @@ async function tryResolveThreadFromVideo (params: ResolveThreadParams) {
|
||||||
comments[i] = await comment.save()
|
comments[i] = await comment.save()
|
||||||
}
|
}
|
||||||
|
|
||||||
resultComment = comments[0]
|
resultComment = comments[0] as MCommentOwnerVideo
|
||||||
}
|
}
|
||||||
|
|
||||||
return { video, comment: resultComment, commentCreated }
|
return { video, comment: resultComment, commentCreated }
|
||||||
|
@ -151,7 +151,7 @@ async function resolveParentComment (params: ResolveThreadParams) {
|
||||||
originCommentId: null,
|
originCommentId: null,
|
||||||
createdAt: new Date(body.published),
|
createdAt: new Date(body.published),
|
||||||
updatedAt: new Date(body.updated)
|
updatedAt: new Date(body.updated)
|
||||||
})
|
}) as MCommentOwner
|
||||||
comment.Account = actor.Account
|
comment.Account = actor.Account
|
||||||
|
|
||||||
return resolveThread({
|
return resolveThread({
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
import { Transaction } from 'sequelize'
|
import { Transaction } from 'sequelize'
|
||||||
import { AccountModel } from '../../models/account/account'
|
|
||||||
import { VideoModel } from '../../models/video/video'
|
|
||||||
import { sendLike, sendUndoDislike, sendUndoLike } from './send'
|
import { sendLike, sendUndoDislike, sendUndoLike } from './send'
|
||||||
import { VideoRateType } from '../../../shared/models/videos'
|
import { VideoRateType } from '../../../shared/models/videos'
|
||||||
import * as Bluebird from 'bluebird'
|
import * as Bluebird from 'bluebird'
|
||||||
|
@ -10,11 +8,11 @@ import { logger } from '../../helpers/logger'
|
||||||
import { CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants'
|
import { CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants'
|
||||||
import { doRequest } from '../../helpers/requests'
|
import { doRequest } from '../../helpers/requests'
|
||||||
import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub'
|
import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub'
|
||||||
import { ActorModel } from '../../models/activitypub/actor'
|
|
||||||
import { getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from './url'
|
import { getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from './url'
|
||||||
import { sendDislike } from './send/send-dislike'
|
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
|
let rateCounts = 0
|
||||||
|
|
||||||
await Bluebird.map(ratesUrl, async rateUrl => {
|
await Bluebird.map(ratesUrl, async rateUrl => {
|
||||||
|
@ -64,11 +62,13 @@ async function createRates (ratesUrl: string[], video: VideoModel, rate: VideoRa
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sendVideoRateChange (account: AccountModel,
|
async function sendVideoRateChange (
|
||||||
video: VideoModel,
|
account: MAccountActor,
|
||||||
likes: number,
|
video: MVideoAccountLight,
|
||||||
dislikes: number,
|
likes: number,
|
||||||
t: Transaction) {
|
dislikes: number,
|
||||||
|
t: Transaction
|
||||||
|
) {
|
||||||
const actor = account.Actor
|
const actor = account.Actor
|
||||||
|
|
||||||
// Keep the order: first we undo and then we create
|
// 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)
|
if (dislikes > 0) await sendDislike(actor, video, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRateUrl (rateType: VideoRateType, actor: ActorModel, video: VideoModel) {
|
function getRateUrl (rateType: VideoRateType, actor: MActorUrl, video: MVideoId) {
|
||||||
return rateType === 'like' ? getVideoLikeActivityPubUrl(actor, video) : getVideoDislikeActivityPubUrl(actor, video)
|
return rateType === 'like'
|
||||||
|
? getVideoLikeActivityPubUrl(actor, video)
|
||||||
|
: getVideoDislikeActivityPubUrl(actor, video)
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
|
|
@ -24,7 +24,6 @@ import {
|
||||||
REMOTE_SCHEME,
|
REMOTE_SCHEME,
|
||||||
STATIC_PATHS
|
STATIC_PATHS
|
||||||
} from '../../initializers/constants'
|
} from '../../initializers/constants'
|
||||||
import { ActorModel } from '../../models/activitypub/actor'
|
|
||||||
import { TagModel } from '../../models/video/tag'
|
import { TagModel } from '../../models/video/tag'
|
||||||
import { VideoModel } from '../../models/video/video'
|
import { VideoModel } from '../../models/video/video'
|
||||||
import { VideoFileModel } from '../../models/video/video-file'
|
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 { ActivitypubHttpFetcherPayload } from '../job-queue/handlers/activitypub-http-fetcher'
|
||||||
import { createRates } from './video-rates'
|
import { createRates } from './video-rates'
|
||||||
import { addVideoShares, shareVideoByServerAndChannel } from './share'
|
import { addVideoShares, shareVideoByServerAndChannel } from './share'
|
||||||
import { AccountModel } from '../../models/account/account'
|
|
||||||
import { fetchVideoByUrl, VideoFetchByUrlType } from '../../helpers/video'
|
import { fetchVideoByUrl, VideoFetchByUrlType } from '../../helpers/video'
|
||||||
import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub'
|
import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub'
|
||||||
import { Notifier } from '../notifier'
|
import { Notifier } from '../notifier'
|
||||||
|
@ -49,15 +47,33 @@ import { VideoShareModel } from '../../models/video/video-share'
|
||||||
import { VideoCommentModel } from '../../models/video/video-comment'
|
import { VideoCommentModel } from '../../models/video/video-comment'
|
||||||
import { sequelizeTypescript } from '../../initializers/database'
|
import { sequelizeTypescript } from '../../initializers/database'
|
||||||
import { createPlaceholderThumbnail, createVideoMiniatureFromUrl } from '../thumbnail'
|
import { createPlaceholderThumbnail, createVideoMiniatureFromUrl } from '../thumbnail'
|
||||||
import { ThumbnailModel } from '../../models/video/thumbnail'
|
|
||||||
import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type'
|
import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import { FilteredModelAttributes } from '../../typings/sequelize'
|
import { FilteredModelAttributes } from '../../typings/sequelize'
|
||||||
import { autoBlacklistVideoIfNeeded } from '../video-blacklist'
|
import { autoBlacklistVideoIfNeeded } from '../video-blacklist'
|
||||||
import { ActorFollowScoreCache } from '../files-cache'
|
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 (
|
if (
|
||||||
// Check this is not a blacklisted video, or unfederated blacklisted video
|
// Check this is not a blacklisted video, or unfederated blacklisted video
|
||||||
(video.isBlacklisted() === false || (isNewVideo === false && video.VideoBlacklist.unfederated === false)) &&
|
(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 }
|
return { response, videoObject: body }
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchRemoteVideoDescription (video: VideoModel) {
|
async function fetchRemoteVideoDescription (video: MVideoAccountLight) {
|
||||||
const host = video.VideoChannel.Account.Actor.Server.host
|
const host = video.VideoChannel.Account.Actor.Server.host
|
||||||
const path = video.getDescriptionAPIPath()
|
const path = video.getDescriptionAPIPath()
|
||||||
const options = {
|
const options = {
|
||||||
|
@ -114,14 +130,14 @@ async function fetchRemoteVideoDescription (video: VideoModel) {
|
||||||
return body.description ? body.description : ''
|
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)
|
const url = buildRemoteBaseUrl(video, path)
|
||||||
|
|
||||||
// We need to provide a callback, if no we could have an uncaught exception
|
// We need to provide a callback, if no we could have an uncaught exception
|
||||||
return doRequestAndSaveToFile({ uri: url }, destPath)
|
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
|
const host = video.VideoChannel.Account.Actor.Server.host
|
||||||
|
|
||||||
return REMOTE_SCHEME.HTTP + '://' + host + path
|
return REMOTE_SCHEME.HTTP + '://' + host + path
|
||||||
|
@ -146,7 +162,7 @@ type SyncParam = {
|
||||||
thumbnail: boolean
|
thumbnail: boolean
|
||||||
refreshVideo?: 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)
|
logger.info('Adding likes/dislikes/shares/comments of video %s.', video.uuid)
|
||||||
|
|
||||||
const jobPayloads: ActivitypubHttpFetcherPayload[] = []
|
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 }))
|
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: {
|
async function getOrCreateVideoAndAccountAndChannel (options: {
|
||||||
videoObject: { id: string } | string,
|
videoObject: { id: string } | string,
|
||||||
syncParam?: SyncParam,
|
syncParam?: SyncParam,
|
||||||
fetchType?: VideoFetchByUrlType,
|
fetchType?: VideoFetchByUrlType,
|
||||||
allowRefresh?: boolean // true by default
|
allowRefresh?: boolean // true by default
|
||||||
}) {
|
}): Promise<{ video: MVideoAccountAllFiles | MVideoThumbnail, created: boolean, autoBlacklisted?: boolean }> {
|
||||||
// Default params
|
// Default params
|
||||||
const syncParam = options.syncParam || { likes: true, dislikes: true, shares: true, comments: true, thumbnail: true, refreshVideo: false }
|
const syncParam = options.syncParam || { likes: true, dislikes: true, shares: true, comments: true, thumbnail: true, refreshVideo: false }
|
||||||
const fetchType = options.fetchType || 'all'
|
const fetchType = options.fetchType || 'all'
|
||||||
|
@ -227,8 +255,9 @@ async function getOrCreateVideoAndAccountAndChannel (options: {
|
||||||
const { videoObject: fetchedVideo } = await fetchRemoteVideo(videoUrl)
|
const { videoObject: fetchedVideo } = await fetchRemoteVideo(videoUrl)
|
||||||
if (!fetchedVideo) throw new Error('Cannot fetch remote video with url: ' + videoUrl)
|
if (!fetchedVideo) throw new Error('Cannot fetch remote video with url: ' + videoUrl)
|
||||||
|
|
||||||
const channelActor = await getOrCreateVideoChannelFromVideoObject(fetchedVideo)
|
const actor = await getOrCreateVideoChannelFromVideoObject(fetchedVideo)
|
||||||
const { autoBlacklisted, videoCreated } = await retryTransactionWrapper(createVideo, fetchedVideo, channelActor, syncParam.thumbnail)
|
const videoChannel = actor.VideoChannel
|
||||||
|
const { autoBlacklisted, videoCreated } = await retryTransactionWrapper(createVideo, fetchedVideo, videoChannel, syncParam.thumbnail)
|
||||||
|
|
||||||
await syncVideoExternalAttributes(videoCreated, fetchedVideo, syncParam)
|
await syncVideoExternalAttributes(videoCreated, fetchedVideo, syncParam)
|
||||||
|
|
||||||
|
@ -236,22 +265,22 @@ async function getOrCreateVideoAndAccountAndChannel (options: {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateVideoFromAP (options: {
|
async function updateVideoFromAP (options: {
|
||||||
video: VideoModel,
|
video: MVideoAccountAllFiles,
|
||||||
videoObject: VideoTorrentObject,
|
videoObject: VideoTorrentObject,
|
||||||
account: AccountModelIdActor,
|
account: MAccountActor,
|
||||||
channel: VideoChannelModelIdActor,
|
channel: MChannelDefault,
|
||||||
overrideTo?: string[]
|
overrideTo?: string[]
|
||||||
}) {
|
}) {
|
||||||
const { video, videoObject, account, channel, overrideTo } = options
|
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
|
let videoFieldsSave: any
|
||||||
const wasPrivateVideo = video.privacy === VideoPrivacy.PRIVATE
|
const wasPrivateVideo = video.privacy === VideoPrivacy.PRIVATE
|
||||||
const wasUnlistedVideo = video.privacy === VideoPrivacy.UNLISTED
|
const wasUnlistedVideo = video.privacy === VideoPrivacy.UNLISTED
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let thumbnailModel: ThumbnailModel
|
let thumbnailModel: MThumbnail
|
||||||
|
|
||||||
try {
|
try {
|
||||||
thumbnailModel = await createVideoMiniatureFromUrl(videoObject.icon.url, video, ThumbnailType.MINIATURE)
|
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 })
|
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 }
|
const sequelizeOptions = { transaction: t }
|
||||||
|
|
||||||
videoFieldsSave = video.toJSON()
|
videoFieldsSave = video.toJSON()
|
||||||
|
@ -293,21 +322,21 @@ async function updateVideoFromAP (options: {
|
||||||
video.channelId = videoData.channelId
|
video.channelId = videoData.channelId
|
||||||
video.views = videoData.views
|
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
|
// 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)
|
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))
|
const newVideoFiles = videoFileAttributes.map(a => new VideoFileModel(a))
|
||||||
|
|
||||||
// Remove video files that do not exist anymore
|
// Remove video files that do not exist anymore
|
||||||
const destroyTasks = video.VideoFiles
|
const destroyTasks = videoUpdated.VideoFiles
|
||||||
.filter(f => !newVideoFiles.find(newFile => newFile.hasSameUniqueKeysThan(f)))
|
.filter(f => !newVideoFiles.find(newFile => newFile.hasSameUniqueKeysThan(f)))
|
||||||
.map(f => f.destroy(sequelizeOptions))
|
.map(f => f.destroy(sequelizeOptions))
|
||||||
await Promise.all(destroyTasks)
|
await Promise.all(destroyTasks)
|
||||||
|
@ -318,15 +347,15 @@ async function updateVideoFromAP (options: {
|
||||||
.then(([ file ]) => file)
|
.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))
|
const newStreamingPlaylists = streamingPlaylistAttributes.map(a => new VideoStreamingPlaylistModel(a))
|
||||||
|
|
||||||
// Remove video files that do not exist anymore
|
// Remove video files that do not exist anymore
|
||||||
const destroyTasks = video.VideoStreamingPlaylists
|
const destroyTasks = videoUpdated.VideoStreamingPlaylists
|
||||||
.filter(f => !newStreamingPlaylists.find(newPlaylist => newPlaylist.hasSameUniqueKeysThan(f)))
|
.filter(f => !newStreamingPlaylists.find(newPlaylist => newPlaylist.hasSameUniqueKeysThan(f)))
|
||||||
.map(f => f.destroy(sequelizeOptions))
|
.map(f => f.destroy(sequelizeOptions))
|
||||||
await Promise.all(destroyTasks)
|
await Promise.all(destroyTasks)
|
||||||
|
@ -337,38 +366,42 @@ async function updateVideoFromAP (options: {
|
||||||
.then(([ streamingPlaylist ]) => streamingPlaylist)
|
.then(([ streamingPlaylist ]) => streamingPlaylist)
|
||||||
})
|
})
|
||||||
|
|
||||||
video.VideoStreamingPlaylists = await Promise.all(upsertTasks)
|
videoUpdated.VideoStreamingPlaylists = await Promise.all(upsertTasks)
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
// Update Tags
|
// Update Tags
|
||||||
const tags = videoObject.tag.map(tag => tag.name)
|
const tags = videoObject.tag.map(tag => tag.name)
|
||||||
const tagInstances = await TagModel.findOrCreateTags(tags, t)
|
const tagInstances = await TagModel.findOrCreateTags(tags, t)
|
||||||
await video.$set('Tags', tagInstances, sequelizeOptions)
|
await videoUpdated.$set('Tags', tagInstances, sequelizeOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
// Update captions
|
// Update captions
|
||||||
await VideoCaptionModel.deleteAllCaptionsOfRemoteVideo(video.id, t)
|
await VideoCaptionModel.deleteAllCaptionsOfRemoteVideo(videoUpdated.id, t)
|
||||||
|
|
||||||
const videoCaptionsPromises = videoObject.subtitleLanguage.map(c => {
|
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({
|
await autoBlacklistVideoIfNeeded({
|
||||||
video,
|
video: videoUpdated,
|
||||||
user: undefined,
|
user: undefined,
|
||||||
isRemote: true,
|
isRemote: true,
|
||||||
isNew: false,
|
isNew: false,
|
||||||
transaction: undefined
|
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)
|
logger.info('Remote video with uuid %s updated', videoObject.uuid)
|
||||||
|
|
||||||
|
return videoUpdated
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (video !== undefined && videoFieldsSave !== undefined) {
|
if (video !== undefined && videoFieldsSave !== undefined) {
|
||||||
resetSequelizeInstance(video, videoFieldsSave)
|
resetSequelizeInstance(video, videoFieldsSave)
|
||||||
|
@ -381,15 +414,15 @@ async function updateVideoFromAP (options: {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function refreshVideoIfNeeded (options: {
|
async function refreshVideoIfNeeded (options: {
|
||||||
video: VideoModel,
|
video: MVideoThumbnail,
|
||||||
fetchedType: VideoFetchByUrlType,
|
fetchedType: VideoFetchByUrlType,
|
||||||
syncParam: SyncParam
|
syncParam: SyncParam
|
||||||
}): Promise<VideoModel> {
|
}): Promise<MVideoThumbnail> {
|
||||||
if (!options.video.isOutdated()) return options.video
|
if (!options.video.isOutdated()) return options.video
|
||||||
|
|
||||||
// We need more attributes if the argument video was fetched with not enough joints
|
// We need more attributes if the argument video was fetched with not enough joints
|
||||||
const video = options.fetchedType === 'all'
|
const video = options.fetchedType === 'all'
|
||||||
? options.video
|
? options.video as MVideoAccountAllFiles
|
||||||
: await VideoModel.loadByUrlAndPopulateAccount(options.video.url)
|
: await VideoModel.loadByUrlAndPopulateAccount(options.video.url)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -410,12 +443,11 @@ async function refreshVideoIfNeeded (options: {
|
||||||
}
|
}
|
||||||
|
|
||||||
const channelActor = await getOrCreateVideoChannelFromVideoObject(videoObject)
|
const channelActor = await getOrCreateVideoChannelFromVideoObject(videoObject)
|
||||||
const account = await AccountModel.load(channelActor.VideoChannel.accountId)
|
|
||||||
|
|
||||||
const updateOptions = {
|
const updateOptions = {
|
||||||
video,
|
video,
|
||||||
videoObject,
|
videoObject,
|
||||||
account,
|
account: channelActor.VideoChannel.Account,
|
||||||
channel: channelActor.VideoChannel
|
channel: channelActor.VideoChannel
|
||||||
}
|
}
|
||||||
await retryTransactionWrapper(updateVideoFromAP, updateOptions)
|
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'
|
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)
|
logger.debug('Adding remote video %s.', videoObject.id)
|
||||||
|
|
||||||
const videoData = await videoActivityObjectToDBAttributes(channelActor.VideoChannel, videoObject, videoObject.to)
|
const videoData = await videoActivityObjectToDBAttributes(channel, videoObject, videoObject.to)
|
||||||
const video = VideoModel.build(videoData)
|
const video = VideoModel.build(videoData) as MVideoThumbnail
|
||||||
|
|
||||||
const promiseThumbnail = createVideoMiniatureFromUrl(videoObject.icon.url, video, ThumbnailType.MINIATURE)
|
const promiseThumbnail = createVideoMiniatureFromUrl(videoObject.icon.url, video, ThumbnailType.MINIATURE)
|
||||||
|
|
||||||
let thumbnailModel: ThumbnailModel
|
let thumbnailModel: MThumbnail
|
||||||
if (waitThumbnail === true) {
|
if (waitThumbnail === true) {
|
||||||
thumbnailModel = await promiseThumbnail
|
thumbnailModel = await promiseThumbnail
|
||||||
}
|
}
|
||||||
|
@ -483,8 +515,8 @@ async function createVideo (videoObject: VideoTorrentObject, channelActor: Actor
|
||||||
const { autoBlacklisted, videoCreated } = await sequelizeTypescript.transaction(async t => {
|
const { autoBlacklisted, videoCreated } = await sequelizeTypescript.transaction(async t => {
|
||||||
const sequelizeOptions = { transaction: t }
|
const sequelizeOptions = { transaction: t }
|
||||||
|
|
||||||
const videoCreated = await video.save(sequelizeOptions)
|
const videoCreated = await video.save(sequelizeOptions) as MVideoFullLight
|
||||||
videoCreated.VideoChannel = channelActor.VideoChannel
|
videoCreated.VideoChannel = channel
|
||||||
|
|
||||||
if (thumbnailModel) await videoCreated.addAndSaveThumbnail(thumbnailModel, t)
|
if (thumbnailModel) await videoCreated.addAndSaveThumbnail(thumbnailModel, t)
|
||||||
|
|
||||||
|
@ -517,15 +549,14 @@ async function createVideo (videoObject: VideoTorrentObject, channelActor: Actor
|
||||||
const videoCaptionsPromises = videoObject.subtitleLanguage.map(c => {
|
const videoCaptionsPromises = videoObject.subtitleLanguage.map(c => {
|
||||||
return VideoCaptionModel.insertOrReplaceLanguage(videoCreated.id, c.identifier, t)
|
return VideoCaptionModel.insertOrReplaceLanguage(videoCreated.id, c.identifier, t)
|
||||||
})
|
})
|
||||||
const captions = await Promise.all(videoCaptionsPromises)
|
await Promise.all(videoCaptionsPromises)
|
||||||
|
|
||||||
video.VideoFiles = videoFiles
|
videoCreated.VideoFiles = videoFiles
|
||||||
video.VideoStreamingPlaylists = streamingPlaylists
|
videoCreated.VideoStreamingPlaylists = streamingPlaylists
|
||||||
video.Tags = tagInstances
|
videoCreated.Tags = tagInstances
|
||||||
video.VideoCaptions = captions
|
|
||||||
|
|
||||||
const autoBlacklisted = await autoBlacklistVideoIfNeeded({
|
const autoBlacklisted = await autoBlacklistVideoIfNeeded({
|
||||||
video,
|
video: videoCreated,
|
||||||
user: undefined,
|
user: undefined,
|
||||||
isRemote: true,
|
isRemote: true,
|
||||||
isNew: true,
|
isNew: true,
|
||||||
|
@ -548,11 +579,7 @@ async function createVideo (videoObject: VideoTorrentObject, channelActor: Actor
|
||||||
return { autoBlacklisted, videoCreated }
|
return { autoBlacklisted, videoCreated }
|
||||||
}
|
}
|
||||||
|
|
||||||
async function videoActivityObjectToDBAttributes (
|
async function videoActivityObjectToDBAttributes (videoChannel: MChannelId, videoObject: VideoTorrentObject, to: string[] = []) {
|
||||||
videoChannel: VideoChannelModelId,
|
|
||||||
videoObject: VideoTorrentObject,
|
|
||||||
to: string[] = []
|
|
||||||
) {
|
|
||||||
const privacy = to.indexOf(ACTIVITY_PUB.PUBLIC) !== -1 ? VideoPrivacy.PUBLIC : VideoPrivacy.UNLISTED
|
const privacy = to.indexOf(ACTIVITY_PUB.PUBLIC) !== -1 ? VideoPrivacy.PUBLIC : VideoPrivacy.UNLISTED
|
||||||
const duration = videoObject.duration.replace(/[^\d]+/, '')
|
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[]
|
const fileUrls = videoObject.url.filter(u => isAPVideoUrlObject(u)) as ActivityVideoUrlObject[]
|
||||||
|
|
||||||
if (fileUrls.length === 0) {
|
if (fileUrls.length === 0) {
|
||||||
|
@ -641,7 +668,7 @@ function videoFileActivityUrlToDBAttributes (video: VideoModel, videoObject: Vid
|
||||||
return attributes
|
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[]
|
const playlistUrls = videoObject.url.filter(u => isAPStreamingPlaylistUrlObject(u)) as ActivityPlaylistUrlObject[]
|
||||||
if (playlistUrls.length === 0) return []
|
if (playlistUrls.length === 0) return []
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,6 @@ import { sendUpdateActor } from './activitypub/send'
|
||||||
import { AVATARS_SIZE, LRU_CACHE, QUEUE_CONCURRENCY } from '../initializers/constants'
|
import { AVATARS_SIZE, LRU_CACHE, QUEUE_CONCURRENCY } from '../initializers/constants'
|
||||||
import { updateActorAvatarInstance } from './activitypub'
|
import { updateActorAvatarInstance } from './activitypub'
|
||||||
import { processImage } from '../helpers/image-utils'
|
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 { extname, join } from 'path'
|
||||||
import { retryTransactionWrapper } from '../helpers/database-utils'
|
import { retryTransactionWrapper } from '../helpers/database-utils'
|
||||||
import * as uuidv4 from 'uuid/v4'
|
import * as uuidv4 from 'uuid/v4'
|
||||||
|
@ -13,8 +11,12 @@ import { sequelizeTypescript } from '../initializers/database'
|
||||||
import * as LRUCache from 'lru-cache'
|
import * as LRUCache from 'lru-cache'
|
||||||
import { queue } from 'async'
|
import { queue } from 'async'
|
||||||
import { downloadImage } from '../helpers/requests'
|
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 extension = extname(avatarPhysicalFile.filename)
|
||||||
const avatarName = uuidv4() + extension
|
const avatarName = uuidv4() + extension
|
||||||
const destination = join(CONFIG.STORAGE.AVATARS_DIR, avatarName)
|
const destination = join(CONFIG.STORAGE.AVATARS_DIR, avatarName)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { sequelizeTypescript } from '../initializers'
|
import { sequelizeTypescript } from '../initializers'
|
||||||
import { AccountBlocklistModel } from '../models/account/account-blocklist'
|
import { AccountBlocklistModel } from '../models/account/account-blocklist'
|
||||||
import { ServerBlocklistModel } from '../models/server/server-blocklist'
|
import { ServerBlocklistModel } from '../models/server/server-blocklist'
|
||||||
|
import { MAccountBlocklist, MServerBlocklist } from '@server/typings/models'
|
||||||
|
|
||||||
function addAccountInBlocklist (byAccountId: number, targetAccountId: number) {
|
function addAccountInBlocklist (byAccountId: number, targetAccountId: number) {
|
||||||
return sequelizeTypescript.transaction(async t => {
|
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 sequelizeTypescript.transaction(async t => {
|
||||||
return accountBlock.destroy({ transaction: t })
|
return accountBlock.destroy({ transaction: t })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeServerFromBlocklist (serverBlock: ServerBlocklistModel) {
|
function removeServerFromBlocklist (serverBlock: MServerBlocklist) {
|
||||||
return sequelizeTypescript.transaction(async t => {
|
return sequelizeTypescript.transaction(async t => {
|
||||||
return serverBlock.destroy({ transaction: t })
|
return serverBlock.destroy({ transaction: t })
|
||||||
})
|
})
|
||||||
|
|
|
@ -13,6 +13,7 @@ import { VideoChannelModel } from '../models/video/video-channel'
|
||||||
import * as Bluebird from 'bluebird'
|
import * as Bluebird from 'bluebird'
|
||||||
import { CONFIG } from '../initializers/config'
|
import { CONFIG } from '../initializers/config'
|
||||||
import { logger } from '../helpers/logger'
|
import { logger } from '../helpers/logger'
|
||||||
|
import { MAccountActor, MChannelActor, MVideo } from '../typings/models'
|
||||||
|
|
||||||
export class ClientHtml {
|
export class ClientHtml {
|
||||||
|
|
||||||
|
@ -65,7 +66,7 @@ export class ClientHtml {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async getAccountOrChannelHTMLPage (
|
private static async getAccountOrChannelHTMLPage (
|
||||||
loader: () => Bluebird<AccountModel | VideoChannelModel>,
|
loader: () => Bluebird<MAccountActor | MChannelActor>,
|
||||||
req: express.Request,
|
req: express.Request,
|
||||||
res: express.Response
|
res: express.Response
|
||||||
) {
|
) {
|
||||||
|
@ -157,7 +158,7 @@ export class ClientHtml {
|
||||||
return htmlStringPage.replace('</head>', linkTag + '</head>')
|
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 previewUrl = WEBSERVER.URL + video.getPreviewStaticPath()
|
||||||
const videoUrl = WEBSERVER.URL + video.getWatchStaticPath()
|
const videoUrl = WEBSERVER.URL + video.getWatchStaticPath()
|
||||||
|
|
||||||
|
@ -236,7 +237,7 @@ export class ClientHtml {
|
||||||
return this.addOpenGraphAndOEmbedTags(htmlStringPage, tagsString)
|
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
|
// SEO, use origin account or channel URL
|
||||||
const metaTags = `<link rel="canonical" href="${entity.Actor.url}" />`
|
const metaTags = `<link rel="canonical" href="${entity.Actor.url}" />`
|
||||||
|
|
||||||
|
|
|
@ -3,16 +3,14 @@ import { isTestInstance } from '../helpers/core-utils'
|
||||||
import { bunyanLogger, logger } from '../helpers/logger'
|
import { bunyanLogger, logger } from '../helpers/logger'
|
||||||
import { CONFIG } from '../initializers/config'
|
import { CONFIG } from '../initializers/config'
|
||||||
import { UserModel } from '../models/account/user'
|
import { UserModel } from '../models/account/user'
|
||||||
import { VideoModel } from '../models/video/video'
|
|
||||||
import { JobQueue } from './job-queue'
|
import { JobQueue } from './job-queue'
|
||||||
import { EmailPayload } from './job-queue/handlers/email'
|
import { EmailPayload } from './job-queue/handlers/email'
|
||||||
import { readFileSync } from 'fs-extra'
|
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 { 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 { 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 = {
|
type SendEmailOptions = {
|
||||||
to: string[]
|
to: string[]
|
||||||
|
@ -90,7 +88,7 @@ class Emailer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addNewVideoFromSubscriberNotification (to: string[], video: VideoModel) {
|
addNewVideoFromSubscriberNotification (to: string[], video: MVideoAccountLight) {
|
||||||
const channelName = video.VideoChannel.getDisplayName()
|
const channelName = video.VideoChannel.getDisplayName()
|
||||||
const videoUrl = WEBSERVER.URL + video.getWatchStaticPath()
|
const videoUrl = WEBSERVER.URL + video.getWatchStaticPath()
|
||||||
|
|
||||||
|
@ -111,7 +109,7 @@ class Emailer {
|
||||||
return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
|
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 followerName = actorFollow.ActorFollower.Account.getDisplayName()
|
||||||
const followingName = (actorFollow.ActorFollowing.VideoChannel || actorFollow.ActorFollowing.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 })
|
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 awaitingApproval = actorFollow.state === 'pending' ? ' awaiting manual approval.' : ''
|
||||||
|
|
||||||
const text = `Hi dear admin,\n\n` +
|
const text = `Hi dear admin,\n\n` +
|
||||||
|
@ -148,7 +146,7 @@ class Emailer {
|
||||||
return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
|
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 videoUrl = WEBSERVER.URL + video.getWatchStaticPath()
|
||||||
|
|
||||||
const text = `Hi dear user,\n\n` +
|
const text = `Hi dear user,\n\n` +
|
||||||
|
@ -168,7 +166,7 @@ class Emailer {
|
||||||
return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
|
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 videoUrl = WEBSERVER.URL + videoImport.Video.getWatchStaticPath()
|
||||||
|
|
||||||
const text = `Hi dear user,\n\n` +
|
const text = `Hi dear user,\n\n` +
|
||||||
|
@ -188,7 +186,7 @@ class Emailer {
|
||||||
return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
|
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 importUrl = WEBSERVER.URL + '/my-account/video-imports'
|
||||||
|
|
||||||
const text = `Hi dear user,\n\n` +
|
const text = `Hi dear user,\n\n` +
|
||||||
|
@ -208,7 +206,7 @@ class Emailer {
|
||||||
return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
|
return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
|
||||||
}
|
}
|
||||||
|
|
||||||
addNewCommentOnMyVideoNotification (to: string[], comment: VideoCommentModel) {
|
addNewCommentOnMyVideoNotification (to: string[], comment: MCommentOwnerVideo) {
|
||||||
const accountName = comment.Account.getDisplayName()
|
const accountName = comment.Account.getDisplayName()
|
||||||
const video = comment.Video
|
const video = comment.Video
|
||||||
const commentUrl = WEBSERVER.URL + comment.getCommentStaticPath()
|
const commentUrl = WEBSERVER.URL + comment.getCommentStaticPath()
|
||||||
|
@ -230,7 +228,7 @@ class Emailer {
|
||||||
return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
|
return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
|
||||||
}
|
}
|
||||||
|
|
||||||
addNewCommentMentionNotification (to: string[], comment: VideoCommentModel) {
|
addNewCommentMentionNotification (to: string[], comment: MCommentOwnerVideo) {
|
||||||
const accountName = comment.Account.getDisplayName()
|
const accountName = comment.Account.getDisplayName()
|
||||||
const video = comment.Video
|
const video = comment.Video
|
||||||
const commentUrl = WEBSERVER.URL + comment.getCommentStaticPath()
|
const commentUrl = WEBSERVER.URL + comment.getCommentStaticPath()
|
||||||
|
@ -252,7 +250,7 @@ class Emailer {
|
||||||
return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
|
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 videoUrl = WEBSERVER.URL + videoAbuse.Video.getWatchStaticPath()
|
||||||
|
|
||||||
const text = `Hi,\n\n` +
|
const text = `Hi,\n\n` +
|
||||||
|
@ -269,7 +267,7 @@ class Emailer {
|
||||||
return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
|
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 VIDEO_AUTO_BLACKLIST_URL = WEBSERVER.URL + '/admin/moderation/video-auto-blacklist/list'
|
||||||
const videoUrl = WEBSERVER.URL + video.getWatchStaticPath()
|
const videoUrl = WEBSERVER.URL + video.getWatchStaticPath()
|
||||||
|
|
||||||
|
@ -292,7 +290,7 @@ class Emailer {
|
||||||
return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
|
return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
|
||||||
}
|
}
|
||||||
|
|
||||||
addNewUserRegistrationNotification (to: string[], user: UserModel) {
|
addNewUserRegistrationNotification (to: string[], user: MUser) {
|
||||||
const text = `Hi,\n\n` +
|
const text = `Hi,\n\n` +
|
||||||
`User ${user.username} just registered on ${WEBSERVER.HOST} PeerTube instance.\n\n` +
|
`User ${user.username} just registered on ${WEBSERVER.HOST} PeerTube instance.\n\n` +
|
||||||
`Cheers,\n` +
|
`Cheers,\n` +
|
||||||
|
@ -307,7 +305,7 @@ class Emailer {
|
||||||
return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
|
return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
|
||||||
}
|
}
|
||||||
|
|
||||||
addVideoBlacklistNotification (to: string[], videoBlacklist: VideoBlacklistModel) {
|
addVideoBlacklistNotification (to: string[], videoBlacklist: MVideoBlacklistVideo) {
|
||||||
const videoName = videoBlacklist.Video.name
|
const videoName = videoBlacklist.Video.name
|
||||||
const videoUrl = WEBSERVER.URL + videoBlacklist.Video.getWatchStaticPath()
|
const videoUrl = WEBSERVER.URL + videoBlacklist.Video.getWatchStaticPath()
|
||||||
|
|
||||||
|
@ -329,7 +327,7 @@ class Emailer {
|
||||||
return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
|
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 videoUrl = WEBSERVER.URL + video.getWatchStaticPath()
|
||||||
|
|
||||||
const text = 'Hi,\n\n' +
|
const text = 'Hi,\n\n' +
|
||||||
|
@ -381,7 +379,7 @@ class Emailer {
|
||||||
return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
|
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 reasonString = reason ? ` for the following reason: ${reason}` : ''
|
||||||
const blockedWord = blocked ? 'blocked' : 'unblocked'
|
const blockedWord = blocked ? 'blocked' : 'unblocked'
|
||||||
const blockedString = `Your account ${user.username} on ${WEBSERVER.HOST} has been ${blockedWord}${reasonString}.`
|
const blockedString = `Your account ${user.username} on ${WEBSERVER.HOST} has been ${blockedWord}${reasonString}.`
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { VideoModel } from '../models/video/video'
|
|
||||||
import { basename, dirname, join } from 'path'
|
import { basename, dirname, join } from 'path'
|
||||||
import { HLS_STREAMING_PLAYLIST_DIRECTORY, P2P_MEDIA_LOADER_PEER_VERSION } from '../initializers/constants'
|
import { HLS_STREAMING_PLAYLIST_DIRECTORY, P2P_MEDIA_LOADER_PEER_VERSION } from '../initializers/constants'
|
||||||
import { close, ensureDir, move, open, outputJSON, pathExists, read, readFile, remove, writeFile } from 'fs-extra'
|
import { 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 { VideoFileModel } from '../models/video/video-file'
|
||||||
import { CONFIG } from '../initializers/config'
|
import { CONFIG } from '../initializers/config'
|
||||||
import { sequelizeTypescript } from '../initializers/database'
|
import { sequelizeTypescript } from '../initializers/database'
|
||||||
|
import { MVideoWithFile } from '@server/typings/models'
|
||||||
|
|
||||||
async function updateStreamingPlaylistsInfohashesIfNeeded () {
|
async function updateStreamingPlaylistsInfohashesIfNeeded () {
|
||||||
const playlistsToUpdate = await VideoStreamingPlaylistModel.listByIncorrectPeerVersion()
|
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 directory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid)
|
||||||
const masterPlaylists: string[] = [ '#EXTM3U', '#EXT-X-VERSION:3' ]
|
const masterPlaylists: string[] = [ '#EXTM3U', '#EXT-X-VERSION:3' ]
|
||||||
const masterPlaylistPath = join(directory, VideoStreamingPlaylistModel.getMasterHlsPlaylistFilename())
|
const masterPlaylistPath = join(directory, VideoStreamingPlaylistModel.getMasterHlsPlaylistFilename())
|
||||||
|
@ -55,7 +55,7 @@ async function updateMasterHLSPlaylist (video: VideoModel) {
|
||||||
await writeFile(masterPlaylistPath, masterPlaylists.join('\n') + '\n')
|
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 json: { [filename: string]: { [range: string]: string } } = {}
|
||||||
|
|
||||||
const playlistDirectory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid)
|
const playlistDirectory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid)
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
|
||||||
import { ActorModel } from '../../../models/activitypub/actor'
|
import { ActorModel } from '../../../models/activitypub/actor'
|
||||||
import { Notifier } from '../../notifier'
|
import { Notifier } from '../../notifier'
|
||||||
import { sequelizeTypescript } from '../../../initializers/database'
|
import { sequelizeTypescript } from '../../../initializers/database'
|
||||||
|
import { MActorFollowFull, MActorFull } from '../../../typings/models'
|
||||||
|
|
||||||
export type ActivitypubFollowPayload = {
|
export type ActivitypubFollowPayload = {
|
||||||
followerActorId: number
|
followerActorId: number
|
||||||
|
@ -23,13 +24,13 @@ async function processActivityPubFollow (job: Bull.Job) {
|
||||||
|
|
||||||
logger.info('Processing ActivityPub follow in job %d.', job.id)
|
logger.info('Processing ActivityPub follow in job %d.', job.id)
|
||||||
|
|
||||||
let targetActor: ActorModel
|
let targetActor: MActorFull
|
||||||
if (!host || host === WEBSERVER.HOST) {
|
if (!host || host === WEBSERVER.HOST) {
|
||||||
targetActor = await ActorModel.loadLocalByName(payload.name)
|
targetActor = await ActorModel.loadLocalByName(payload.name)
|
||||||
} else {
|
} else {
|
||||||
const sanitizedHost = sanitizeHost(host, REMOTE_SCHEME.HTTP)
|
const sanitizedHost = sanitizeHost(host, REMOTE_SCHEME.HTTP)
|
||||||
const actorUrl = await loadActorUrlOrGetFromWebfinger(payload.name + '@' + sanitizedHost)
|
const actorUrl = await loadActorUrlOrGetFromWebfinger(payload.name + '@' + sanitizedHost)
|
||||||
targetActor = await getOrCreateActorAndServerAndModel(actorUrl)
|
targetActor = await getOrCreateActorAndServerAndModel(actorUrl, 'all')
|
||||||
}
|
}
|
||||||
|
|
||||||
const fromActor = await ActorModel.load(payload.followerActorId)
|
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) {
|
if (fromActor.id === targetActor.id) {
|
||||||
throw new Error('Follower is the same than target actor.')
|
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 state = !fromActor.serverId && !targetActor.serverId ? 'accepted' : 'pending'
|
||||||
|
|
||||||
const actorFollow = await sequelizeTypescript.transaction(async t => {
|
const actorFollow = await sequelizeTypescript.transaction(async t => {
|
||||||
const [ actorFollow ] = await ActorFollowModel.findOrCreate({
|
const [ actorFollow ] = await ActorFollowModel.findOrCreate<MActorFollowFull>({
|
||||||
where: {
|
where: {
|
||||||
actorId: fromActor.id,
|
actorId: fromActor.id,
|
||||||
targetActorId: targetActor.id
|
targetActorId: targetActor.id
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { AccountModel } from '../../../models/account/account'
|
||||||
import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
|
import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
|
||||||
import { VideoShareModel } from '../../../models/video/video-share'
|
import { VideoShareModel } from '../../../models/video/video-share'
|
||||||
import { VideoCommentModel } from '../../../models/video/video-comment'
|
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'
|
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
|
const payload = job.data as ActivitypubHttpFetcherPayload
|
||||||
|
|
||||||
let video: VideoModel
|
let video: MVideoFullLight
|
||||||
if (payload.videoId) video = await VideoModel.loadAndPopulateAccountAndServerAndTags(payload.videoId)
|
if (payload.videoId) video = await VideoModel.loadAndPopulateAccountAndServerAndTags(payload.videoId)
|
||||||
|
|
||||||
let account: AccountModel
|
let account: MAccountDefault
|
||||||
if (payload.accountId) account = await AccountModel.load(payload.accountId)
|
if (payload.accountId) account = await AccountModel.load(payload.accountId)
|
||||||
|
|
||||||
const fetcherType: { [ id in FetchType ]: (items: any[]) => Promise<any> } = {
|
const fetcherType: { [ id in FetchType ]: (items: any[]) => Promise<any> } = {
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { getServerActor } from '../../../../helpers/utils'
|
||||||
import { ActorModel } from '../../../../models/activitypub/actor'
|
import { ActorModel } from '../../../../models/activitypub/actor'
|
||||||
import { sha256 } from '../../../../helpers/core-utils'
|
import { sha256 } from '../../../../helpers/core-utils'
|
||||||
import { HTTP_SIGNATURE } from '../../../../initializers/constants'
|
import { HTTP_SIGNATURE } from '../../../../initializers/constants'
|
||||||
|
import { MActor } from '../../../../typings/models'
|
||||||
|
|
||||||
type Payload = { body: any, signatureActorId?: number }
|
type Payload = { body: any, signatureActorId?: number }
|
||||||
|
|
||||||
|
@ -19,7 +20,8 @@ async function computeBody (payload: Payload) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildSignedRequestOptions (payload: Payload) {
|
async function buildSignedRequestOptions (payload: Payload) {
|
||||||
let actor: ActorModel | null
|
let actor: MActor | null
|
||||||
|
|
||||||
if (payload.signatureActorId) {
|
if (payload.signatureActorId) {
|
||||||
actor = await ActorModel.load(payload.signatureActorId)
|
actor = await ActorModel.load(payload.signatureActorId)
|
||||||
if (!actor) throw new Error('Unknown signature actor id.')
|
if (!actor) throw new Error('Unknown signature actor id.')
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { getVideoFileFPS, getVideoFileResolution } from '../../../helpers/ffmpeg
|
||||||
import { copy, stat } from 'fs-extra'
|
import { copy, stat } from 'fs-extra'
|
||||||
import { VideoFileModel } from '../../../models/video/video-file'
|
import { VideoFileModel } from '../../../models/video/video-file'
|
||||||
import { extname } from 'path'
|
import { extname } from 'path'
|
||||||
|
import { MVideoFile, MVideoWithFile } from '@server/typings/models'
|
||||||
|
|
||||||
export type VideoFileImportPayload = {
|
export type VideoFileImportPayload = {
|
||||||
videoUUID: string,
|
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 { videoFileResolution } = await getVideoFileResolution(inputFilePath)
|
||||||
const { size } = await stat(inputFilePath)
|
const { size } = await stat(inputFilePath)
|
||||||
const fps = await getVideoFileFPS(inputFilePath)
|
const fps = await getVideoFileFPS(inputFilePath)
|
||||||
|
@ -48,7 +49,7 @@ async function updateVideoFile (video: VideoModel, inputFilePath: string) {
|
||||||
size,
|
size,
|
||||||
fps,
|
fps,
|
||||||
videoId: video.id
|
videoId: video.id
|
||||||
})
|
}) as MVideoFile
|
||||||
|
|
||||||
const currentVideoFile = video.VideoFiles.find(videoFile => videoFile.resolution === updatedVideoFile.resolution)
|
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)
|
video.VideoFiles = video.VideoFiles.filter(f => f !== currentVideoFile)
|
||||||
|
|
||||||
// Update the database
|
// Update the database
|
||||||
currentVideoFile.set('extname', updatedVideoFile.extname)
|
currentVideoFile.extname = updatedVideoFile.extname
|
||||||
currentVideoFile.set('size', updatedVideoFile.size)
|
currentVideoFile.size = updatedVideoFile.size
|
||||||
currentVideoFile.set('fps', updatedVideoFile.fps)
|
currentVideoFile.fps = updatedVideoFile.fps
|
||||||
|
|
||||||
updatedVideoFile = currentVideoFile
|
updatedVideoFile = currentVideoFile
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,9 +17,10 @@ import { move, remove, stat } from 'fs-extra'
|
||||||
import { Notifier } from '../../notifier'
|
import { Notifier } from '../../notifier'
|
||||||
import { CONFIG } from '../../../initializers/config'
|
import { CONFIG } from '../../../initializers/config'
|
||||||
import { sequelizeTypescript } from '../../../initializers/database'
|
import { sequelizeTypescript } from '../../../initializers/database'
|
||||||
import { ThumbnailModel } from '../../../models/video/thumbnail'
|
|
||||||
import { createVideoMiniatureFromUrl, generateVideoMiniature } from '../../thumbnail'
|
import { createVideoMiniatureFromUrl, generateVideoMiniature } from '../../thumbnail'
|
||||||
import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type'
|
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 VideoImportYoutubeDLPayload = {
|
||||||
type: 'youtube-dl'
|
type: 'youtube-dl'
|
||||||
|
@ -110,11 +111,13 @@ type ProcessFileOptions = {
|
||||||
generateThumbnail: boolean
|
generateThumbnail: boolean
|
||||||
generatePreview: 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 tempVideoPath: string
|
||||||
let videoDestFile: string
|
let videoDestFile: string
|
||||||
let videoFile: VideoFileModel
|
let videoFile: VideoFileModel
|
||||||
|
|
||||||
|
const videoImport = videoImportArg as MVideoImportDefaultFiles
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Download video from youtubeDL
|
// Download video from youtubeDL
|
||||||
tempVideoPath = await downloader()
|
tempVideoPath = await downloader()
|
||||||
|
@ -148,7 +151,7 @@ async function processFile (downloader: () => Promise<string>, videoImport: Vide
|
||||||
tempVideoPath = null // This path is not used anymore
|
tempVideoPath = null // This path is not used anymore
|
||||||
|
|
||||||
// Process thumbnail
|
// Process thumbnail
|
||||||
let thumbnailModel: ThumbnailModel
|
let thumbnailModel: MThumbnail
|
||||||
if (options.downloadThumbnail && options.thumbnailUrl) {
|
if (options.downloadThumbnail && options.thumbnailUrl) {
|
||||||
thumbnailModel = await createVideoMiniatureFromUrl(options.thumbnailUrl, videoImport.Video, ThumbnailType.MINIATURE)
|
thumbnailModel = await createVideoMiniatureFromUrl(options.thumbnailUrl, videoImport.Video, ThumbnailType.MINIATURE)
|
||||||
} else if (options.generateThumbnail || options.downloadThumbnail) {
|
} else if (options.generateThumbnail || options.downloadThumbnail) {
|
||||||
|
@ -156,7 +159,7 @@ async function processFile (downloader: () => Promise<string>, videoImport: Vide
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process preview
|
// Process preview
|
||||||
let previewModel: ThumbnailModel
|
let previewModel: MThumbnail
|
||||||
if (options.downloadPreview && options.thumbnailUrl) {
|
if (options.downloadPreview && options.thumbnailUrl) {
|
||||||
previewModel = await createVideoMiniatureFromUrl(options.thumbnailUrl, videoImport.Video, ThumbnailType.PREVIEW)
|
previewModel = await createVideoMiniatureFromUrl(options.thumbnailUrl, videoImport.Video, ThumbnailType.PREVIEW)
|
||||||
} else if (options.generatePreview || options.downloadPreview) {
|
} else if (options.generatePreview || options.downloadPreview) {
|
||||||
|
@ -166,14 +169,15 @@ async function processFile (downloader: () => Promise<string>, videoImport: Vide
|
||||||
// Create torrent
|
// Create torrent
|
||||||
await videoImport.Video.createTorrentAndSetInfoHash(videoFile)
|
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
|
// Refresh video
|
||||||
const video = await VideoModel.load(videoImport.videoId, t)
|
const video = await VideoModel.load(videoImportToUpdate.videoId, t)
|
||||||
if (!video) throw new Error('Video linked to import ' + videoImport.videoId + ' does not exist anymore.')
|
if (!video) throw new Error('Video linked to import ' + videoImportToUpdate.videoId + ' does not exist anymore.')
|
||||||
videoImport.Video = video
|
|
||||||
|
|
||||||
const videoFileCreated = await videoFile.save({ transaction: t })
|
const videoFileCreated = await videoFile.save({ transaction: t })
|
||||||
video.VideoFiles = [ videoFileCreated ]
|
videoImportToUpdate.Video = Object.assign(video, { VideoFiles: [ videoFileCreated ] })
|
||||||
|
|
||||||
// Update video DB object
|
// Update video DB object
|
||||||
video.duration = duration
|
video.duration = duration
|
||||||
|
@ -188,25 +192,25 @@ async function processFile (downloader: () => Promise<string>, videoImport: Vide
|
||||||
await federateVideoIfNeeded(videoForFederation, true, t)
|
await federateVideoIfNeeded(videoForFederation, true, t)
|
||||||
|
|
||||||
// Update video import object
|
// Update video import object
|
||||||
videoImport.state = VideoImportState.SUCCESS
|
videoImportToUpdate.state = VideoImportState.SUCCESS
|
||||||
const videoImportUpdated = await videoImport.save({ transaction: t })
|
const videoImportUpdated = await videoImportToUpdate.save({ transaction: t }) as MVideoImportVideo
|
||||||
|
videoImportUpdated.Video = video
|
||||||
|
|
||||||
logger.info('Video %s imported.', video.uuid)
|
logger.info('Video %s imported.', video.uuid)
|
||||||
|
|
||||||
videoImportUpdated.Video = videoForFederation
|
return { videoImportUpdated, video: videoForFederation }
|
||||||
return videoImportUpdated
|
|
||||||
})
|
})
|
||||||
|
|
||||||
Notifier.Instance.notifyOnFinishedVideoImport(videoImportUpdated, true)
|
Notifier.Instance.notifyOnFinishedVideoImport(videoImportUpdated, true)
|
||||||
|
|
||||||
if (videoImportUpdated.Video.isBlacklisted()) {
|
if (video.isBlacklisted()) {
|
||||||
Notifier.Instance.notifyOnVideoAutoBlacklist(videoImportUpdated.Video)
|
Notifier.Instance.notifyOnVideoAutoBlacklist(video)
|
||||||
} else {
|
} else {
|
||||||
Notifier.Instance.notifyOnNewVideoIfNeeded(videoImportUpdated.Video)
|
Notifier.Instance.notifyOnNewVideoIfNeeded(video)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create transcoding jobs?
|
// 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
|
// Put uuid because we don't have id auto incremented for now
|
||||||
const dataInput = {
|
const dataInput = {
|
||||||
type: 'optimize' as 'optimize',
|
type: 'optimize' as 'optimize',
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { computeResolutionsToTranscode } from '../../../helpers/ffmpeg-utils'
|
||||||
import { generateHlsPlaylist, optimizeVideofile, transcodeOriginalVideofile, mergeAudioVideofile } from '../../video-transcoding'
|
import { generateHlsPlaylist, optimizeVideofile, transcodeOriginalVideofile, mergeAudioVideofile } from '../../video-transcoding'
|
||||||
import { Notifier } from '../../notifier'
|
import { Notifier } from '../../notifier'
|
||||||
import { CONFIG } from '../../../initializers/config'
|
import { CONFIG } from '../../../initializers/config'
|
||||||
|
import { MVideoUUID, MVideoWithFile } from '@server/typings/models'
|
||||||
|
|
||||||
interface BaseTranscodingPayload {
|
interface BaseTranscodingPayload {
|
||||||
videoUUID: string
|
videoUUID: string
|
||||||
|
@ -73,7 +74,7 @@ async function processVideoTranscoding (job: Bull.Job) {
|
||||||
return video
|
return video
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onHlsPlaylistGenerationSuccess (video: VideoModel) {
|
async function onHlsPlaylistGenerationSuccess (video: MVideoUUID) {
|
||||||
if (video === undefined) return undefined
|
if (video === undefined) return undefined
|
||||||
|
|
||||||
await sequelizeTypescript.transaction(async t => {
|
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 => {
|
const { videoDatabase, videoPublished } = await sequelizeTypescript.transaction(async t => {
|
||||||
// Maybe the video changed in database, refresh it
|
// Maybe the video changed in database, refresh it
|
||||||
let videoDatabase = await VideoModel.loadAndPopulateAccountAndServerAndTags(video.uuid, t)
|
let videoDatabase = await VideoModel.loadAndPopulateAccountAndServerAndTags(video.uuid, t)
|
||||||
|
@ -119,7 +120,7 @@ async function publishNewResolutionIfNeeded (video: VideoModel, payload?: NewRes
|
||||||
await createHlsJobIfEnabled(payload)
|
await createHlsJobIfEnabled(payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onVideoFileOptimizerSuccess (videoArg: VideoModel, payload: OptimizeTranscodingPayload) {
|
async function onVideoFileOptimizerSuccess (videoArg: MVideoWithFile, payload: OptimizeTranscodingPayload) {
|
||||||
if (videoArg === undefined) return undefined
|
if (videoArg === undefined) return undefined
|
||||||
|
|
||||||
// Outside the transaction (IO on disk)
|
// Outside the transaction (IO on disk)
|
||||||
|
|
|
@ -8,13 +8,23 @@ import { UserModel } from '../models/account/user'
|
||||||
import { PeerTubeSocket } from './peertube-socket'
|
import { PeerTubeSocket } from './peertube-socket'
|
||||||
import { CONFIG } from '../initializers/config'
|
import { CONFIG } from '../initializers/config'
|
||||||
import { VideoPrivacy, VideoState } from '../../shared/models/videos'
|
import { VideoPrivacy, VideoState } from '../../shared/models/videos'
|
||||||
import { VideoAbuseModel } from '../models/video/video-abuse'
|
|
||||||
import { VideoBlacklistModel } from '../models/video/video-blacklist'
|
import { VideoBlacklistModel } from '../models/video/video-blacklist'
|
||||||
import * as Bluebird from 'bluebird'
|
import * as Bluebird from 'bluebird'
|
||||||
import { VideoImportModel } from '../models/video/video-import'
|
import { VideoImportModel } from '../models/video/video-import'
|
||||||
import { AccountBlocklistModel } from '../models/account/account-blocklist'
|
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 { 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 {
|
class Notifier {
|
||||||
|
|
||||||
|
@ -22,7 +32,7 @@ class Notifier {
|
||||||
|
|
||||||
private constructor () {}
|
private constructor () {}
|
||||||
|
|
||||||
notifyOnNewVideoIfNeeded (video: VideoModel): void {
|
notifyOnNewVideoIfNeeded (video: MVideoAccountLight): void {
|
||||||
// Only notify on public and published videos which are not blacklisted
|
// Only notify on public and published videos which are not blacklisted
|
||||||
if (video.privacy !== VideoPrivacy.PUBLIC || video.state !== VideoState.PUBLISHED || video.isBlacklisted()) return
|
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 }))
|
.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
|
// 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
|
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 }))
|
.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
|
// don't notify if video is still blacklisted or waiting for transcoding
|
||||||
if (video.VideoBlacklist || (video.waitTranscoding && video.state !== VideoState.PUBLISHED)) return
|
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 }))
|
.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
|
// don't notify if video is still waiting for transcoding or scheduled update
|
||||||
if (video.ScheduleVideoUpdate || (video.waitTranscoding && video.state !== VideoState.PUBLISHED)) return
|
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
|
.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)
|
this.notifyVideoOwnerOfNewComment(comment)
|
||||||
.catch(err => logger.error('Cannot notify video owner of new comment %s.', comment.url, { err }))
|
.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 }))
|
.catch(err => logger.error('Cannot notify mentions of comment %s.', comment.url, { err }))
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyOnNewVideoAbuse (videoAbuse: VideoAbuseModel): void {
|
notifyOnNewVideoAbuse (videoAbuse: MVideoAbuseVideo): void {
|
||||||
this.notifyModeratorsOfNewVideoAbuse(videoAbuse)
|
this.notifyModeratorsOfNewVideoAbuse(videoAbuse)
|
||||||
.catch(err => logger.error('Cannot notify of new video abuse of video %s.', videoAbuse.Video.url, { err }))
|
.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)
|
this.notifyModeratorsOfVideoAutoBlacklist(video)
|
||||||
.catch(err => logger.error('Cannot notify of auto-blacklist of video %s.', video.url, { err }))
|
.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)
|
this.notifyVideoOwnerOfBlacklist(videoBlacklist)
|
||||||
.catch(err => logger.error('Cannot notify video owner of new video blacklist of %s.', videoBlacklist.Video.url, { err }))
|
.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)
|
this.notifyVideoOwnerOfUnblacklist(video)
|
||||||
.catch(err => logger.error('Cannot notify video owner of unblacklist of %s.', video.url, { err }))
|
.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)
|
this.notifyOwnerVideoImportIsFinished(videoImport, success)
|
||||||
.catch(err => logger.error('Cannot notify owner that its video import %s is finished.', videoImport.getTargetIdentifier(), { err }))
|
.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)
|
this.notifyModeratorsOfNewUserRegistration(user)
|
||||||
.catch(err => logger.error('Cannot notify moderators of new user registration (%s).', user.username, { err }))
|
.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)
|
this.notifyUserOfNewActorFollow(actorFollow)
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
logger.error(
|
logger.error(
|
||||||
|
@ -104,14 +114,14 @@ class Notifier {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyOfNewInstanceFollow (actorFollow: ActorFollowModel): void {
|
notifyOfNewInstanceFollow (actorFollow: MActorFollowActors): void {
|
||||||
this.notifyAdminsOfNewInstanceFollow(actorFollow)
|
this.notifyAdminsOfNewInstanceFollow(actorFollow)
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
logger.error('Cannot notify administrators of new follower %s.', actorFollow.ActorFollower.url, { 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
|
// List all followers that are users
|
||||||
const users = await UserModel.listUserSubscribersOf(video.VideoChannel.actorId)
|
const users = await UserModel.listUserSubscribersOf(video.VideoChannel.actorId)
|
||||||
|
|
||||||
|
@ -127,7 +137,7 @@ class Notifier {
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
videoId: video.id
|
videoId: video.id
|
||||||
})
|
})
|
||||||
notification.Video = video
|
notification.Video = video as VideoModel
|
||||||
|
|
||||||
return notification
|
return notification
|
||||||
}
|
}
|
||||||
|
@ -139,7 +149,7 @@ class Notifier {
|
||||||
return this.notify({ users, settingGetter, notificationCreator, emailSender })
|
return this.notify({ users, settingGetter, notificationCreator, emailSender })
|
||||||
}
|
}
|
||||||
|
|
||||||
private async notifyVideoOwnerOfNewComment (comment: VideoCommentModel) {
|
private async notifyVideoOwnerOfNewComment (comment: MCommentOwnerVideo) {
|
||||||
if (comment.Video.isOwned() === false) return
|
if (comment.Video.isOwned() === false) return
|
||||||
|
|
||||||
const user = await UserModel.loadByVideoId(comment.videoId)
|
const user = await UserModel.loadByVideoId(comment.videoId)
|
||||||
|
@ -162,7 +172,7 @@ class Notifier {
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
commentId: comment.id
|
commentId: comment.id
|
||||||
})
|
})
|
||||||
notification.Comment = comment
|
notification.Comment = comment as VideoCommentModel
|
||||||
|
|
||||||
return notification
|
return notification
|
||||||
}
|
}
|
||||||
|
@ -174,7 +184,7 @@ class Notifier {
|
||||||
return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender })
|
return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender })
|
||||||
}
|
}
|
||||||
|
|
||||||
private async notifyOfCommentMention (comment: VideoCommentModel) {
|
private async notifyOfCommentMention (comment: MCommentOwnerVideo) {
|
||||||
const extractedUsernames = comment.extractMentions()
|
const extractedUsernames = comment.extractMentions()
|
||||||
logger.debug(
|
logger.debug(
|
||||||
'Extracted %d username from comment %s.', extractedUsernames.length, comment.url,
|
'Extracted %d username from comment %s.', extractedUsernames.length, comment.url,
|
||||||
|
@ -209,7 +219,7 @@ class Notifier {
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
commentId: comment.id
|
commentId: comment.id
|
||||||
})
|
})
|
||||||
notification.Comment = comment
|
notification.Comment = comment as VideoCommentModel
|
||||||
|
|
||||||
return notification
|
return notification
|
||||||
}
|
}
|
||||||
|
@ -221,7 +231,7 @@ class Notifier {
|
||||||
return this.notify({ users, settingGetter, notificationCreator, emailSender })
|
return this.notify({ users, settingGetter, notificationCreator, emailSender })
|
||||||
}
|
}
|
||||||
|
|
||||||
private async notifyUserOfNewActorFollow (actorFollow: ActorFollowModel) {
|
private async notifyUserOfNewActorFollow (actorFollow: MActorFollowFull) {
|
||||||
if (actorFollow.ActorFollowing.isOwned() === false) return
|
if (actorFollow.ActorFollowing.isOwned() === false) return
|
||||||
|
|
||||||
// Account follows one of our account?
|
// Account follows one of our account?
|
||||||
|
@ -236,9 +246,6 @@ class Notifier {
|
||||||
|
|
||||||
if (!user) return
|
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 followerAccount = actorFollow.ActorFollower.Account
|
||||||
|
|
||||||
const accountMuted = await AccountBlocklistModel.isAccountMutedBy(user.Account.id, followerAccount.id)
|
const accountMuted = await AccountBlocklistModel.isAccountMutedBy(user.Account.id, followerAccount.id)
|
||||||
|
@ -256,7 +263,7 @@ class Notifier {
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
actorFollowId: actorFollow.id
|
actorFollowId: actorFollow.id
|
||||||
})
|
})
|
||||||
notification.ActorFollow = actorFollow
|
notification.ActorFollow = actorFollow as ActorFollowModel
|
||||||
|
|
||||||
return notification
|
return notification
|
||||||
}
|
}
|
||||||
|
@ -268,7 +275,7 @@ class Notifier {
|
||||||
return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender })
|
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)
|
const admins = await UserModel.listWithRight(UserRight.MANAGE_SERVER_FOLLOW)
|
||||||
|
|
||||||
logger.info('Notifying %d administrators of new instance follower: %s.', admins.length, actorFollow.ActorFollower.url)
|
logger.info('Notifying %d administrators of new instance follower: %s.', admins.length, actorFollow.ActorFollower.url)
|
||||||
|
@ -283,7 +290,7 @@ class Notifier {
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
actorFollowId: actorFollow.id
|
actorFollowId: actorFollow.id
|
||||||
})
|
})
|
||||||
notification.ActorFollow = actorFollow
|
notification.ActorFollow = actorFollow as ActorFollowModel
|
||||||
|
|
||||||
return notification
|
return notification
|
||||||
}
|
}
|
||||||
|
@ -295,7 +302,7 @@ class Notifier {
|
||||||
return this.notify({ users: admins, settingGetter, notificationCreator, emailSender })
|
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)
|
const moderators = await UserModel.listWithRight(UserRight.MANAGE_VIDEO_ABUSES)
|
||||||
if (moderators.length === 0) return
|
if (moderators.length === 0) return
|
||||||
|
|
||||||
|
@ -306,7 +313,7 @@ class Notifier {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function notificationCreator (user: UserModel) {
|
async function notificationCreator (user: UserModel) {
|
||||||
const notification = await UserNotificationModel.create({
|
const notification: UserNotificationModelForApi = await UserNotificationModel.create({
|
||||||
type: UserNotificationType.NEW_VIDEO_ABUSE_FOR_MODERATORS,
|
type: UserNotificationType.NEW_VIDEO_ABUSE_FOR_MODERATORS,
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
videoAbuseId: videoAbuse.id
|
videoAbuseId: videoAbuse.id
|
||||||
|
@ -323,7 +330,7 @@ class Notifier {
|
||||||
return this.notify({ users: moderators, settingGetter, notificationCreator, emailSender })
|
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)
|
const moderators = await UserModel.listWithRight(UserRight.MANAGE_VIDEO_BLACKLIST)
|
||||||
if (moderators.length === 0) return
|
if (moderators.length === 0) return
|
||||||
|
|
||||||
|
@ -339,7 +346,7 @@ class Notifier {
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
videoId: video.id
|
videoId: video.id
|
||||||
})
|
})
|
||||||
notification.Video = video
|
notification.Video = video as VideoModel
|
||||||
|
|
||||||
return notification
|
return notification
|
||||||
}
|
}
|
||||||
|
@ -351,7 +358,7 @@ class Notifier {
|
||||||
return this.notify({ users: moderators, settingGetter, notificationCreator, emailSender })
|
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)
|
const user = await UserModel.loadByVideoId(videoBlacklist.videoId)
|
||||||
if (!user) return
|
if (!user) return
|
||||||
|
|
||||||
|
@ -367,7 +374,7 @@ class Notifier {
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
videoBlacklistId: videoBlacklist.id
|
videoBlacklistId: videoBlacklist.id
|
||||||
})
|
})
|
||||||
notification.VideoBlacklist = videoBlacklist
|
notification.VideoBlacklist = videoBlacklist as VideoBlacklistModel
|
||||||
|
|
||||||
return notification
|
return notification
|
||||||
}
|
}
|
||||||
|
@ -379,7 +386,7 @@ class Notifier {
|
||||||
return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender })
|
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)
|
const user = await UserModel.loadByVideoId(video.id)
|
||||||
if (!user) return
|
if (!user) return
|
||||||
|
|
||||||
|
@ -395,7 +402,7 @@ class Notifier {
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
videoId: video.id
|
videoId: video.id
|
||||||
})
|
})
|
||||||
notification.Video = video
|
notification.Video = video as VideoModel
|
||||||
|
|
||||||
return notification
|
return notification
|
||||||
}
|
}
|
||||||
|
@ -407,7 +414,7 @@ class Notifier {
|
||||||
return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender })
|
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)
|
const user = await UserModel.loadByVideoId(video.id)
|
||||||
if (!user) return
|
if (!user) return
|
||||||
|
|
||||||
|
@ -423,7 +430,7 @@ class Notifier {
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
videoId: video.id
|
videoId: video.id
|
||||||
})
|
})
|
||||||
notification.Video = video
|
notification.Video = video as VideoModel
|
||||||
|
|
||||||
return notification
|
return notification
|
||||||
}
|
}
|
||||||
|
@ -435,7 +442,7 @@ class Notifier {
|
||||||
return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender })
|
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)
|
const user = await UserModel.loadByVideoImportId(videoImport.id)
|
||||||
if (!user) return
|
if (!user) return
|
||||||
|
|
||||||
|
@ -451,7 +458,7 @@ class Notifier {
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
videoImportId: videoImport.id
|
videoImportId: videoImport.id
|
||||||
})
|
})
|
||||||
notification.VideoImport = videoImport
|
notification.VideoImport = videoImport as VideoImportModel
|
||||||
|
|
||||||
return notification
|
return notification
|
||||||
}
|
}
|
||||||
|
@ -465,13 +472,13 @@ class Notifier {
|
||||||
return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender })
|
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)
|
const moderators = await UserModel.listWithRight(UserRight.MANAGE_USERS)
|
||||||
if (moderators.length === 0) return
|
if (moderators.length === 0) return
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
'Notifying %s moderators of new user registration of %s.',
|
'Notifying %s moderators of new user registration of %s.',
|
||||||
moderators.length, registeredUser.Account.Actor.preferredUsername
|
moderators.length, registeredUser.username
|
||||||
)
|
)
|
||||||
|
|
||||||
function settingGetter (user: UserModel) {
|
function settingGetter (user: UserModel) {
|
||||||
|
@ -484,7 +491,7 @@ class Notifier {
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
accountId: registeredUser.Account.id
|
accountId: registeredUser.Account.id
|
||||||
})
|
})
|
||||||
notification.Account = registeredUser.Account
|
notification.Account = registeredUser.Account as AccountModel
|
||||||
|
|
||||||
return notification
|
return notification
|
||||||
}
|
}
|
||||||
|
@ -497,10 +504,10 @@ class Notifier {
|
||||||
}
|
}
|
||||||
|
|
||||||
private async notify (options: {
|
private async notify (options: {
|
||||||
users: UserModel[],
|
users: MUserWithNotificationSetting[],
|
||||||
notificationCreator: (user: UserModel) => Promise<UserNotificationModel>,
|
notificationCreator: (user: MUserWithNotificationSetting) => Promise<UserNotificationModelForApi>,
|
||||||
emailSender: (emails: string[]) => Promise<any> | Bluebird<any>,
|
emailSender: (emails: string[]) => Promise<any> | Bluebird<any>,
|
||||||
settingGetter: (user: UserModel) => UserNotificationSettingValue
|
settingGetter: (user: MUserWithNotificationSetting) => UserNotificationSettingValue
|
||||||
}) {
|
}) {
|
||||||
const emails: string[] = []
|
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
|
if (CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION === true && user.emailVerified === false) return false
|
||||||
|
|
||||||
return value & UserNotificationSettingValue.EMAIL
|
return value & UserNotificationSettingValue.EMAIL
|
||||||
|
|
|
@ -8,10 +8,11 @@ import { LRU_CACHE } from '../initializers/constants'
|
||||||
import { Transaction } from 'sequelize'
|
import { Transaction } from 'sequelize'
|
||||||
import { CONFIG } from '../initializers/config'
|
import { CONFIG } from '../initializers/config'
|
||||||
import * as LRUCache from 'lru-cache'
|
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 }
|
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 })
|
const userHavingToken = new LRUCache<number, string>({ max: LRU_CACHE.USER_TOKENS.MAX_SIZE })
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import * as SocketIO from 'socket.io'
|
import * as SocketIO from 'socket.io'
|
||||||
import { authenticateSocket } from '../middlewares'
|
import { authenticateSocket } from '../middlewares'
|
||||||
import { UserNotificationModel } from '../models/account/user-notification'
|
|
||||||
import { logger } from '../helpers/logger'
|
import { logger } from '../helpers/logger'
|
||||||
import { Server } from 'http'
|
import { Server } from 'http'
|
||||||
|
import { UserNotificationModelForApi } from '@server/typings/models/user'
|
||||||
|
|
||||||
class PeerTubeSocket {
|
class PeerTubeSocket {
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ class PeerTubeSocket {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
sendNotification (userId: number, notification: UserNotificationModel) {
|
sendNotification (userId: number, notification: UserNotificationModelForApi) {
|
||||||
const socket = this.userNotificationSockets[userId]
|
const socket = this.userNotificationSockets[userId]
|
||||||
|
|
||||||
if (!socket) return
|
if (!socket) return
|
||||||
|
|
|
@ -2,8 +2,9 @@ import { VideoRedundancyModel } from '../models/redundancy/video-redundancy'
|
||||||
import { sendUndoCacheFile } from './activitypub/send'
|
import { sendUndoCacheFile } from './activitypub/send'
|
||||||
import { Transaction } from 'sequelize'
|
import { Transaction } from 'sequelize'
|
||||||
import { getServerActor } from '../helpers/utils'
|
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()
|
const serverActor = await getServerActor()
|
||||||
|
|
||||||
// Local cache, send undo to remote instances
|
// Local cache, send undo to remote instances
|
||||||
|
|
|
@ -3,7 +3,6 @@ import { HLS_REDUNDANCY_DIRECTORY, REDUNDANCY, VIDEO_IMPORT_TIMEOUT, WEBSERVER }
|
||||||
import { logger } from '../../helpers/logger'
|
import { logger } from '../../helpers/logger'
|
||||||
import { VideosRedundancy } from '../../../shared/models/redundancy'
|
import { VideosRedundancy } from '../../../shared/models/redundancy'
|
||||||
import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy'
|
import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy'
|
||||||
import { VideoFileModel } from '../../models/video/video-file'
|
|
||||||
import { downloadWebTorrentVideo } from '../../helpers/webtorrent'
|
import { downloadWebTorrentVideo } from '../../helpers/webtorrent'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import { move } from 'fs-extra'
|
import { move } from 'fs-extra'
|
||||||
|
@ -12,16 +11,29 @@ import { sendCreateCacheFile, sendUpdateCacheFile } from '../activitypub/send'
|
||||||
import { getVideoCacheFileActivityPubUrl, getVideoCacheStreamingPlaylistActivityPubUrl } from '../activitypub/url'
|
import { getVideoCacheFileActivityPubUrl, getVideoCacheStreamingPlaylistActivityPubUrl } from '../activitypub/url'
|
||||||
import { removeVideoRedundancy } from '../redundancy'
|
import { removeVideoRedundancy } from '../redundancy'
|
||||||
import { getOrCreateVideoAndAccountAndChannel } from '../activitypub'
|
import { getOrCreateVideoAndAccountAndChannel } from '../activitypub'
|
||||||
import { VideoStreamingPlaylistModel } from '../../models/video/video-streaming-playlist'
|
|
||||||
import { VideoModel } from '../../models/video/video'
|
|
||||||
import { downloadPlaylistSegments } from '../hls'
|
import { downloadPlaylistSegments } from '../hls'
|
||||||
import { CONFIG } from '../../initializers/config'
|
import { CONFIG } from '../../initializers/config'
|
||||||
|
import {
|
||||||
|
MStreamingPlaylist,
|
||||||
|
MStreamingPlaylistVideo,
|
||||||
|
MVideoAccountLight,
|
||||||
|
MVideoFile,
|
||||||
|
MVideoFileVideo,
|
||||||
|
MVideoRedundancyFileVideo,
|
||||||
|
MVideoRedundancyStreamingPlaylistVideo,
|
||||||
|
MVideoRedundancyVideo,
|
||||||
|
MVideoWithAllFiles
|
||||||
|
} from '@server/typings/models'
|
||||||
|
|
||||||
type CandidateToDuplicate = {
|
type CandidateToDuplicate = {
|
||||||
redundancy: VideosRedundancy,
|
redundancy: VideosRedundancy,
|
||||||
video: VideoModel,
|
video: MVideoWithAllFiles,
|
||||||
files: VideoFileModel[],
|
files: MVideoFile[],
|
||||||
streamingPlaylists: VideoStreamingPlaylistModel[]
|
streamingPlaylists: MStreamingPlaylist[]
|
||||||
|
}
|
||||||
|
|
||||||
|
function isMVideoRedundancyFileVideo (o: MVideoRedundancyVideo): o is MVideoRedundancyFileVideo {
|
||||||
|
return !!(o as MVideoRedundancyFileVideo).VideoFile
|
||||||
}
|
}
|
||||||
|
|
||||||
export class VideosRedundancyScheduler extends AbstractScheduler {
|
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)
|
const redundancy = CONFIG.REDUNDANCY.VIDEOS.STRATEGIES.find(s => s.strategy === redundancyModel.strategy)
|
||||||
// Redundancy strategy disabled, remove our redundancy instead of extending expiration
|
// Redundancy strategy disabled, remove our redundancy instead of extending expiration
|
||||||
if (!redundancy) {
|
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
|
file.Video = video
|
||||||
|
|
||||||
const serverActor = await getServerActor()
|
const serverActor = await getServerActor()
|
||||||
|
@ -187,7 +200,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler {
|
||||||
const destPath = join(CONFIG.STORAGE.REDUNDANCY_DIR, video.getVideoFilename(file))
|
const destPath = join(CONFIG.STORAGE.REDUNDANCY_DIR, video.getVideoFilename(file))
|
||||||
await move(tmpPath, destPath)
|
await move(tmpPath, destPath)
|
||||||
|
|
||||||
const createdModel = await VideoRedundancyModel.create({
|
const createdModel: MVideoRedundancyFileVideo = await VideoRedundancyModel.create({
|
||||||
expiresOn: this.buildNewExpiration(redundancy.minLifetime),
|
expiresOn: this.buildNewExpiration(redundancy.minLifetime),
|
||||||
url: getVideoCacheFileActivityPubUrl(file),
|
url: getVideoCacheFileActivityPubUrl(file),
|
||||||
fileUrl: video.getVideoRedundancyUrl(file, WEBSERVER.URL),
|
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)
|
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
|
playlist.Video = video
|
||||||
|
|
||||||
const serverActor = await getServerActor()
|
const serverActor = await getServerActor()
|
||||||
|
@ -213,7 +231,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler {
|
||||||
const destDirectory = join(HLS_REDUNDANCY_DIRECTORY, video.uuid)
|
const destDirectory = join(HLS_REDUNDANCY_DIRECTORY, video.uuid)
|
||||||
await downloadPlaylistSegments(playlist.playlistUrl, destDirectory, VIDEO_IMPORT_TIMEOUT)
|
await downloadPlaylistSegments(playlist.playlistUrl, destDirectory, VIDEO_IMPORT_TIMEOUT)
|
||||||
|
|
||||||
const createdModel = await VideoRedundancyModel.create({
|
const createdModel: MVideoRedundancyStreamingPlaylistVideo = await VideoRedundancyModel.create({
|
||||||
expiresOn: this.buildNewExpiration(redundancy.minLifetime),
|
expiresOn: this.buildNewExpiration(redundancy.minLifetime),
|
||||||
url: getVideoCacheStreamingPlaylistActivityPubUrl(video, playlist),
|
url: getVideoCacheStreamingPlaylistActivityPubUrl(video, playlist),
|
||||||
fileUrl: playlist.getVideoRedundancyUrl(WEBSERVER.URL),
|
fileUrl: playlist.getVideoRedundancyUrl(WEBSERVER.URL),
|
||||||
|
@ -229,7 +247,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler {
|
||||||
logger.info('Duplicated playlist %s -> %s.', playlist.playlistUrl, createdModel.url)
|
logger.info('Duplicated playlist %s -> %s.', playlist.playlistUrl, createdModel.url)
|
||||||
}
|
}
|
||||||
|
|
||||||
private async extendsExpirationOf (redundancy: VideoRedundancyModel, expiresAfterMs: number) {
|
private async extendsExpirationOf (redundancy: MVideoRedundancyVideo, expiresAfterMs: number) {
|
||||||
logger.info('Extending expiration of %s.', redundancy.url)
|
logger.info('Extending expiration of %s.', redundancy.url)
|
||||||
|
|
||||||
const serverActor = await getServerActor()
|
const serverActor = await getServerActor()
|
||||||
|
@ -243,7 +261,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler {
|
||||||
private async purgeCacheIfNeeded (candidateToDuplicate: CandidateToDuplicate) {
|
private async purgeCacheIfNeeded (candidateToDuplicate: CandidateToDuplicate) {
|
||||||
while (this.isTooHeavy(candidateToDuplicate)) {
|
while (this.isTooHeavy(candidateToDuplicate)) {
|
||||||
const redundancy = candidateToDuplicate.redundancy
|
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
|
if (!toDelete) return
|
||||||
|
|
||||||
await removeVideoRedundancy(toDelete)
|
await removeVideoRedundancy(toDelete)
|
||||||
|
@ -263,14 +281,14 @@ export class VideosRedundancyScheduler extends AbstractScheduler {
|
||||||
return new Date(Date.now() + expiresAfterMs)
|
return new Date(Date.now() + expiresAfterMs)
|
||||||
}
|
}
|
||||||
|
|
||||||
private buildEntryLogId (object: VideoRedundancyModel) {
|
private buildEntryLogId (object: MVideoRedundancyFileVideo | MVideoRedundancyStreamingPlaylistVideo) {
|
||||||
if (object.VideoFile) return `${object.VideoFile.Video.url}-${object.VideoFile.resolution}`
|
if (isMVideoRedundancyFileVideo(object)) return `${object.VideoFile.Video.url}-${object.VideoFile.resolution}`
|
||||||
|
|
||||||
return `${object.VideoStreamingPlaylist.playlistUrl}`
|
return `${object.VideoStreamingPlaylist.playlistUrl}`
|
||||||
}
|
}
|
||||||
|
|
||||||
private getTotalFileSizes (files: VideoFileModel[], playlists: VideoStreamingPlaylistModel[]) {
|
private getTotalFileSizes (files: MVideoFile[], playlists: MStreamingPlaylist[]) {
|
||||||
const fileReducer = (previous: number, current: VideoFileModel) => previous + current.size
|
const fileReducer = (previous: number, current: MVideoFile) => previous + current.size
|
||||||
|
|
||||||
const totalSize = files.reduce(fileReducer, 0)
|
const totalSize = files.reduce(fileReducer, 0)
|
||||||
if (playlists.length === 0) return totalSize
|
if (playlists.length === 0) return totalSize
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
import { VideoFileModel } from '../models/video/video-file'
|
|
||||||
import { generateImageFromVideoFile } from '../helpers/ffmpeg-utils'
|
import { generateImageFromVideoFile } from '../helpers/ffmpeg-utils'
|
||||||
import { CONFIG } from '../initializers/config'
|
import { CONFIG } from '../initializers/config'
|
||||||
import { PREVIEWS_SIZE, THUMBNAILS_SIZE, ASSETS_PATH } from '../initializers/constants'
|
import { ASSETS_PATH, PREVIEWS_SIZE, THUMBNAILS_SIZE } from '../initializers/constants'
|
||||||
import { VideoModel } from '../models/video/video'
|
|
||||||
import { ThumbnailModel } from '../models/video/thumbnail'
|
import { ThumbnailModel } from '../models/video/thumbnail'
|
||||||
import { ThumbnailType } from '../../shared/models/videos/thumbnail.type'
|
import { ThumbnailType } from '../../shared/models/videos/thumbnail.type'
|
||||||
import { processImage } from '../helpers/image-utils'
|
import { processImage } from '../helpers/image-utils'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import { downloadImage } from '../helpers/requests'
|
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 }
|
type ImageSize = { height: number, width: number }
|
||||||
|
|
||||||
function createPlaylistMiniatureFromExisting (
|
function createPlaylistMiniatureFromExisting (
|
||||||
inputPath: string,
|
inputPath: string,
|
||||||
playlist: VideoPlaylistModel,
|
playlist: MVideoPlaylistThumbnail,
|
||||||
automaticallyGenerated: boolean,
|
automaticallyGenerated: boolean,
|
||||||
keepOriginal = false,
|
keepOriginal = false,
|
||||||
size?: ImageSize
|
size?: ImageSize
|
||||||
|
@ -26,7 +26,7 @@ function createPlaylistMiniatureFromExisting (
|
||||||
return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, automaticallyGenerated, existingThumbnail })
|
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 { filename, basePath, height, width, existingThumbnail } = buildMetadataFromPlaylist(playlist, size)
|
||||||
const type = ThumbnailType.MINIATURE
|
const type = ThumbnailType.MINIATURE
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ function createPlaylistMiniatureFromUrl (fileUrl: string, playlist: VideoPlaylis
|
||||||
return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, existingThumbnail, fileUrl })
|
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 { filename, basePath, height, width, existingThumbnail } = buildMetadataFromVideo(video, type, size)
|
||||||
const thumbnailCreator = () => downloadImage(fileUrl, basePath, filename, { width, height })
|
const thumbnailCreator = () => downloadImage(fileUrl, basePath, filename, { width, height })
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ function createVideoMiniatureFromUrl (fileUrl: string, video: VideoModel, type:
|
||||||
|
|
||||||
function createVideoMiniatureFromExisting (
|
function createVideoMiniatureFromExisting (
|
||||||
inputPath: string,
|
inputPath: string,
|
||||||
video: VideoModel,
|
video: MVideoThumbnail,
|
||||||
type: ThumbnailType,
|
type: ThumbnailType,
|
||||||
automaticallyGenerated: boolean,
|
automaticallyGenerated: boolean,
|
||||||
size?: ImageSize
|
size?: ImageSize
|
||||||
|
@ -54,7 +54,7 @@ function createVideoMiniatureFromExisting (
|
||||||
return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, automaticallyGenerated, existingThumbnail })
|
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 input = video.getVideoFilePath(videoFile)
|
||||||
|
|
||||||
const { filename, basePath, height, width, existingThumbnail, outputPath } = buildMetadataFromVideo(video, type)
|
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 })
|
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 { filename, height, width, existingThumbnail } = buildMetadataFromVideo(video, type, size)
|
||||||
|
|
||||||
const thumbnail = existingThumbnail ? existingThumbnail : new ThumbnailModel()
|
const thumbnail = existingThumbnail ? existingThumbnail : new ThumbnailModel()
|
||||||
|
@ -90,7 +90,7 @@ export {
|
||||||
createPlaylistMiniatureFromExisting
|
createPlaylistMiniatureFromExisting
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildMetadataFromPlaylist (playlist: VideoPlaylistModel, size: ImageSize) {
|
function buildMetadataFromPlaylist (playlist: MVideoPlaylistThumbnail, size: ImageSize) {
|
||||||
const filename = playlist.generateThumbnailName()
|
const filename = playlist.generateThumbnailName()
|
||||||
const basePath = CONFIG.STORAGE.THUMBNAILS_DIR
|
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)
|
const existingThumbnail = Array.isArray(video.Thumbnails)
|
||||||
? video.Thumbnails.find(t => t.type === type)
|
? video.Thumbnails.find(t => t.type === type)
|
||||||
: undefined
|
: undefined
|
||||||
|
@ -148,7 +148,7 @@ async function createThumbnailFromFunction (parameters: {
|
||||||
type: ThumbnailType,
|
type: ThumbnailType,
|
||||||
automaticallyGenerated?: boolean,
|
automaticallyGenerated?: boolean,
|
||||||
fileUrl?: string,
|
fileUrl?: string,
|
||||||
existingThumbnail?: ThumbnailModel
|
existingThumbnail?: MThumbnail
|
||||||
}) {
|
}) {
|
||||||
const { thumbnailCreator, filename, width, height, type, existingThumbnail, automaticallyGenerated = null, fileUrl = null } = parameters
|
const { thumbnailCreator, filename, width, height, type, existingThumbnail, automaticallyGenerated = null, fileUrl = null } = parameters
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,6 @@ import { AccountModel } from '../models/account/account'
|
||||||
import { UserModel } from '../models/account/user'
|
import { UserModel } from '../models/account/user'
|
||||||
import { buildActorInstance, getAccountActivityPubUrl, setAsyncActorKeys } from './activitypub'
|
import { buildActorInstance, getAccountActivityPubUrl, setAsyncActorKeys } from './activitypub'
|
||||||
import { createVideoChannel } from './video-channel'
|
import { createVideoChannel } from './video-channel'
|
||||||
import { VideoChannelModel } from '../models/video/video-channel'
|
|
||||||
import { ActorModel } from '../models/activitypub/actor'
|
import { ActorModel } from '../models/activitypub/actor'
|
||||||
import { UserNotificationSettingModel } from '../models/account/user-notification-setting'
|
import { UserNotificationSettingModel } from '../models/account/user-notification-setting'
|
||||||
import { UserNotificationSetting, UserNotificationSettingValue } from '../../shared/models/users'
|
import { UserNotificationSetting, UserNotificationSettingValue } from '../../shared/models/users'
|
||||||
|
@ -14,14 +13,17 @@ import { sequelizeTypescript } from '../initializers/database'
|
||||||
import { Transaction } from 'sequelize/types'
|
import { Transaction } from 'sequelize/types'
|
||||||
import { Redis } from './redis'
|
import { Redis } from './redis'
|
||||||
import { Emailer } from './emailer'
|
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 }
|
type ChannelNames = { name: string, displayName: string }
|
||||||
|
|
||||||
async function createUserAccountAndChannelAndPlaylist (parameters: {
|
async function createUserAccountAndChannelAndPlaylist (parameters: {
|
||||||
userToCreate: UserModel,
|
userToCreate: UserModel,
|
||||||
userDisplayName?: string,
|
userDisplayName?: string,
|
||||||
channelNames?: ChannelNames,
|
channelNames?: ChannelNames,
|
||||||
validateUser?: boolean
|
validateUser?: boolean
|
||||||
}) {
|
}): Promise<{ user: MUserNotifSettingAccount, account: MAccountActor, videoChannel: MChannelActor }> {
|
||||||
const { userToCreate, userDisplayName, channelNames, validateUser = true } = parameters
|
const { userToCreate, userDisplayName, channelNames, validateUser = true } = parameters
|
||||||
|
|
||||||
const { user, account, videoChannel } = await sequelizeTypescript.transaction(async t => {
|
const { user, account, videoChannel } = await sequelizeTypescript.transaction(async t => {
|
||||||
|
@ -30,7 +32,7 @@ async function createUserAccountAndChannelAndPlaylist (parameters: {
|
||||||
validate: validateUser
|
validate: validateUser
|
||||||
}
|
}
|
||||||
|
|
||||||
const userCreated = await userToCreate.save(userOptions)
|
const userCreated: MUserNotifSettingAccount = await userToCreate.save(userOptions)
|
||||||
userCreated.NotificationSetting = await createDefaultUserNotificationSettings(userCreated, t)
|
userCreated.NotificationSetting = await createDefaultUserNotificationSettings(userCreated, t)
|
||||||
|
|
||||||
const accountCreated = await createLocalAccountWithoutKeys({
|
const accountCreated = await createLocalAccountWithoutKeys({
|
||||||
|
@ -50,15 +52,15 @@ async function createUserAccountAndChannelAndPlaylist (parameters: {
|
||||||
return { user: userCreated, account: accountCreated, videoChannel, videoPlaylist }
|
return { user: userCreated, account: accountCreated, videoChannel, videoPlaylist }
|
||||||
})
|
})
|
||||||
|
|
||||||
const [ accountKeys, channelKeys ] = await Promise.all([
|
const [ accountActorWithKeys, channelActorWithKeys ] = await Promise.all([
|
||||||
setAsyncActorKeys(account.Actor),
|
setAsyncActorKeys(account.Actor),
|
||||||
setAsyncActorKeys(videoChannel.Actor)
|
setAsyncActorKeys(videoChannel.Actor)
|
||||||
])
|
])
|
||||||
|
|
||||||
account.Actor = accountKeys
|
account.Actor = accountActorWithKeys
|
||||||
videoChannel.Actor = channelKeys
|
videoChannel.Actor = channelActorWithKeys
|
||||||
|
|
||||||
return { user, account, videoChannel } as { user: UserModel, account: AccountModel, videoChannel: VideoChannelModel }
|
return { user, account, videoChannel }
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createLocalAccountWithoutKeys (parameters: {
|
async function createLocalAccountWithoutKeys (parameters: {
|
||||||
|
@ -73,7 +75,7 @@ async function createLocalAccountWithoutKeys (parameters: {
|
||||||
const url = getAccountActivityPubUrl(name)
|
const url = getAccountActivityPubUrl(name)
|
||||||
|
|
||||||
const actorInstance = buildActorInstance(type, url, 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({
|
const accountInstance = new AccountModel({
|
||||||
name: displayName || name,
|
name: displayName || name,
|
||||||
|
@ -82,7 +84,7 @@ async function createLocalAccountWithoutKeys (parameters: {
|
||||||
actorId: actorInstanceCreated.id
|
actorId: actorInstanceCreated.id
|
||||||
})
|
})
|
||||||
|
|
||||||
const accountInstanceCreated = await accountInstance.save({ transaction: t })
|
const accountInstanceCreated: MAccountActor = await accountInstance.save({ transaction: t })
|
||||||
accountInstanceCreated.Actor = actorInstanceCreated
|
accountInstanceCreated.Actor = actorInstanceCreated
|
||||||
|
|
||||||
return accountInstanceCreated
|
return accountInstanceCreated
|
||||||
|
@ -102,7 +104,7 @@ async function createApplicationActor (applicationId: number) {
|
||||||
return accountCreated
|
return accountCreated
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sendVerifyUserEmail (user: UserModel, isPendingEmail = false) {
|
async function sendVerifyUserEmail (user: MUser, isPendingEmail = false) {
|
||||||
const verificationString = await Redis.Instance.setVerifyEmailVerificationString(user.id)
|
const verificationString = await Redis.Instance.setVerifyEmailVerificationString(user.id)
|
||||||
let url = WEBSERVER.URL + '/verify-account/email?userId=' + user.id + '&verificationString=' + verificationString
|
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 } = {
|
const values: UserNotificationSetting & { userId: number } = {
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
newVideoFromSubscription: UserNotificationSettingValue.WEB,
|
newVideoFromSubscription: UserNotificationSettingValue.WEB,
|
||||||
|
@ -143,7 +145,7 @@ function createDefaultUserNotificationSettings (user: UserModel, t: Transaction
|
||||||
return UserNotificationSettingModel.create(values, { transaction: t })
|
return UserNotificationSettingModel.create(values, { transaction: t })
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildChannelAttributes (user: UserModel, channelNames?: ChannelNames) {
|
async function buildChannelAttributes (user: MUser, channelNames?: ChannelNames) {
|
||||||
if (channelNames) return channelNames
|
if (channelNames) return channelNames
|
||||||
|
|
||||||
let channelName = user.username + '_channel'
|
let channelName = user.username + '_channel'
|
||||||
|
|
|
@ -2,16 +2,15 @@ import { Transaction } from 'sequelize'
|
||||||
import { CONFIG } from '../initializers/config'
|
import { CONFIG } from '../initializers/config'
|
||||||
import { UserRight, VideoBlacklistType } from '../../shared/models'
|
import { UserRight, VideoBlacklistType } from '../../shared/models'
|
||||||
import { VideoBlacklistModel } from '../models/video/video-blacklist'
|
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 { logger } from '../helpers/logger'
|
||||||
import { UserAdminFlag } from '../../shared/models/users/user-flag.model'
|
import { UserAdminFlag } from '../../shared/models/users/user-flag.model'
|
||||||
import { Hooks } from './plugins/hooks'
|
import { Hooks } from './plugins/hooks'
|
||||||
import { Notifier } from './notifier'
|
import { Notifier } from './notifier'
|
||||||
|
import { MUser, MVideoBlacklist, MVideoWithBlacklistLight } from '@server/typings/models'
|
||||||
|
|
||||||
async function autoBlacklistVideoIfNeeded (parameters: {
|
async function autoBlacklistVideoIfNeeded (parameters: {
|
||||||
video: VideoModel,
|
video: MVideoWithBlacklistLight,
|
||||||
user?: UserModel,
|
user?: MUser,
|
||||||
isRemote: boolean,
|
isRemote: boolean,
|
||||||
isNew: boolean,
|
isNew: boolean,
|
||||||
notify?: boolean,
|
notify?: boolean,
|
||||||
|
@ -32,7 +31,7 @@ async function autoBlacklistVideoIfNeeded (parameters: {
|
||||||
reason: 'Auto-blacklisted. Moderator review required.',
|
reason: 'Auto-blacklisted. Moderator review required.',
|
||||||
type: VideoBlacklistType.AUTO_BEFORE_PUBLISHED
|
type: VideoBlacklistType.AUTO_BEFORE_PUBLISHED
|
||||||
}
|
}
|
||||||
const [ videoBlacklist ] = await VideoBlacklistModel.findOrCreate({
|
const [ videoBlacklist ] = await VideoBlacklistModel.findOrCreate<MVideoBlacklist>({
|
||||||
where: {
|
where: {
|
||||||
videoId: video.id
|
videoId: video.id
|
||||||
},
|
},
|
||||||
|
@ -49,10 +48,10 @@ async function autoBlacklistVideoIfNeeded (parameters: {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function autoBlacklistNeeded (parameters: {
|
async function autoBlacklistNeeded (parameters: {
|
||||||
video: VideoModel,
|
video: MVideoWithBlacklistLight,
|
||||||
isRemote: boolean,
|
isRemote: boolean,
|
||||||
isNew: boolean,
|
isNew: boolean,
|
||||||
user?: UserModel
|
user?: MUser
|
||||||
}) {
|
}) {
|
||||||
const { user, video, isRemote, isNew } = parameters
|
const { user, video, isRemote, isNew } = parameters
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,19 @@
|
||||||
import * as Sequelize from 'sequelize'
|
import * as Sequelize from 'sequelize'
|
||||||
import * as uuidv4 from 'uuid/v4'
|
import * as uuidv4 from 'uuid/v4'
|
||||||
import { VideoChannelCreate } from '../../shared/models'
|
import { VideoChannelCreate } from '../../shared/models'
|
||||||
import { AccountModel } from '../models/account/account'
|
|
||||||
import { VideoChannelModel } from '../models/video/video-channel'
|
import { VideoChannelModel } from '../models/video/video-channel'
|
||||||
import { buildActorInstance, federateVideoIfNeeded, getVideoChannelActivityPubUrl } from './activitypub'
|
import { buildActorInstance, federateVideoIfNeeded, getVideoChannelActivityPubUrl } from './activitypub'
|
||||||
import { VideoModel } from '../models/video/video'
|
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 uuid = uuidv4()
|
||||||
const url = getVideoChannelActivityPubUrl(videoChannelInfo.name)
|
const url = getVideoChannelActivityPubUrl(videoChannelInfo.name)
|
||||||
const actorInstance = buildActorInstance('Group', url, videoChannelInfo.name, uuid)
|
const actorInstance = buildActorInstance('Group', url, videoChannelInfo.name, uuid)
|
||||||
|
@ -21,10 +28,10 @@ async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account
|
||||||
actorId: actorInstanceCreated.id
|
actorId: actorInstanceCreated.id
|
||||||
}
|
}
|
||||||
|
|
||||||
const videoChannel = VideoChannelModel.build(videoChannelData)
|
const videoChannel = new VideoChannelModel(videoChannelData)
|
||||||
|
|
||||||
const options = { transaction: t }
|
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
|
// Do not forget to add Account/Actor information to the created video channel
|
||||||
videoChannelCreated.Account = account
|
videoChannelCreated.Account = account
|
||||||
|
@ -34,7 +41,7 @@ async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account
|
||||||
return videoChannelCreated
|
return videoChannelCreated
|
||||||
}
|
}
|
||||||
|
|
||||||
async function federateAllVideosOfChannel (videoChannel: VideoChannelModel) {
|
async function federateAllVideosOfChannel (videoChannel: MChannelId) {
|
||||||
const videoIds = await VideoModel.getAllIdsFromChannel(videoChannel)
|
const videoIds = await VideoModel.getAllIdsFromChannel(videoChannel)
|
||||||
|
|
||||||
for (const videoId of videoIds) {
|
for (const videoId of videoIds) {
|
||||||
|
|
|
@ -1,17 +1,16 @@
|
||||||
import * as Sequelize from 'sequelize'
|
import * as Sequelize from 'sequelize'
|
||||||
import { ResultList } from '../../shared/models'
|
import { ResultList } from '../../shared/models'
|
||||||
import { VideoCommentThreadTree } from '../../shared/models/videos/video-comment.model'
|
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 { VideoCommentModel } from '../models/video/video-comment'
|
||||||
import { getVideoCommentActivityPubUrl } from './activitypub'
|
import { getVideoCommentActivityPubUrl } from './activitypub'
|
||||||
import { sendCreateVideoComment } from './activitypub/send'
|
import { sendCreateVideoComment } from './activitypub/send'
|
||||||
|
import { MAccountDefault, MComment, MCommentOwnerVideoReply, MVideoFullLight } from '../typings/models'
|
||||||
|
|
||||||
async function createVideoComment (obj: {
|
async function createVideoComment (obj: {
|
||||||
text: string,
|
text: string,
|
||||||
inReplyToComment: VideoCommentModel | null,
|
inReplyToComment: MComment | null,
|
||||||
video: VideoModel
|
video: MVideoFullLight,
|
||||||
account: AccountModel
|
account: MAccountDefault
|
||||||
}, t: Sequelize.Transaction) {
|
}, t: Sequelize.Transaction) {
|
||||||
let originCommentId: number | null = null
|
let originCommentId: number | null = null
|
||||||
let inReplyToCommentId: number | null = null
|
let inReplyToCommentId: number | null = null
|
||||||
|
@ -32,7 +31,7 @@ async function createVideoComment (obj: {
|
||||||
|
|
||||||
comment.url = getVideoCommentActivityPubUrl(obj.video, comment)
|
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.InReplyToVideoComment = obj.inReplyToComment
|
||||||
savedComment.Video = obj.video
|
savedComment.Video = obj.video
|
||||||
savedComment.Account = obj.account
|
savedComment.Account = obj.account
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
import * as Sequelize from 'sequelize'
|
import * as Sequelize from 'sequelize'
|
||||||
import { AccountModel } from '../models/account/account'
|
|
||||||
import { VideoPlaylistModel } from '../models/video/video-playlist'
|
import { VideoPlaylistModel } from '../models/video/video-playlist'
|
||||||
import { VideoPlaylistPrivacy } from '../../shared/models/videos/playlist/video-playlist-privacy.model'
|
import { VideoPlaylistPrivacy } from '../../shared/models/videos/playlist/video-playlist-privacy.model'
|
||||||
import { getVideoPlaylistActivityPubUrl } from './activitypub'
|
import { getVideoPlaylistActivityPubUrl } from './activitypub'
|
||||||
import { VideoPlaylistType } from '../../shared/models/videos/playlist/video-playlist-type.model'
|
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) {
|
async function createWatchLaterPlaylist (account: MAccount, t: Sequelize.Transaction) {
|
||||||
const videoPlaylist = new VideoPlaylistModel({
|
const videoPlaylist: MVideoPlaylistOwner = new VideoPlaylistModel({
|
||||||
name: 'Watch later',
|
name: 'Watch later',
|
||||||
privacy: VideoPlaylistPrivacy.PRIVATE,
|
privacy: VideoPlaylistPrivacy.PRIVATE,
|
||||||
type: VideoPlaylistType.WATCH_LATER,
|
type: VideoPlaylistType.WATCH_LATER,
|
||||||
|
|
|
@ -5,16 +5,16 @@ import { ensureDir, move, remove, stat } from 'fs-extra'
|
||||||
import { logger } from '../helpers/logger'
|
import { logger } from '../helpers/logger'
|
||||||
import { VideoResolution } from '../../shared/models/videos'
|
import { VideoResolution } from '../../shared/models/videos'
|
||||||
import { VideoFileModel } from '../models/video/video-file'
|
import { VideoFileModel } from '../models/video/video-file'
|
||||||
import { VideoModel } from '../models/video/video'
|
|
||||||
import { updateMasterHLSPlaylist, updateSha256Segments } from './hls'
|
import { updateMasterHLSPlaylist, updateSha256Segments } from './hls'
|
||||||
import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist'
|
import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist'
|
||||||
import { VideoStreamingPlaylistType } from '../../shared/models/videos/video-streaming-playlist.type'
|
import { VideoStreamingPlaylistType } from '../../shared/models/videos/video-streaming-playlist.type'
|
||||||
import { CONFIG } from '../initializers/config'
|
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.
|
* 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 videosDirectory = CONFIG.STORAGE.VIDEOS_DIR
|
||||||
const transcodeDirectory = CONFIG.STORAGE.TMP_DIR
|
const transcodeDirectory = CONFIG.STORAGE.TMP_DIR
|
||||||
const newExtname = '.mp4'
|
const newExtname = '.mp4'
|
||||||
|
@ -57,7 +57,7 @@ async function optimizeVideofile (video: VideoModel, inputVideoFileArg?: VideoFi
|
||||||
/**
|
/**
|
||||||
* Transcode the original video file to a lower resolution.
|
* 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 videosDirectory = CONFIG.STORAGE.VIDEOS_DIR
|
||||||
const transcodeDirectory = CONFIG.STORAGE.TMP_DIR
|
const transcodeDirectory = CONFIG.STORAGE.TMP_DIR
|
||||||
const extname = '.mp4'
|
const extname = '.mp4'
|
||||||
|
@ -87,7 +87,7 @@ async function transcodeOriginalVideofile (video: VideoModel, resolution: VideoR
|
||||||
return onVideoFileTranscoding(video, newVideoFile, videoTranscodedPath, videoOutputPath)
|
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 videosDirectory = CONFIG.STORAGE.VIDEOS_DIR
|
||||||
const transcodeDirectory = CONFIG.STORAGE.TMP_DIR
|
const transcodeDirectory = CONFIG.STORAGE.TMP_DIR
|
||||||
const newExtname = '.mp4'
|
const newExtname = '.mp4'
|
||||||
|
@ -117,7 +117,7 @@ async function mergeAudioVideofile (video: VideoModel, resolution: VideoResoluti
|
||||||
return onVideoFileTranscoding(video, inputVideoFile, videoTranscodedPath, videoOutputPath)
|
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)
|
const baseHlsDirectory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid)
|
||||||
await ensureDir(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 stats = await stat(transcodingPath)
|
||||||
const fps = await getVideoFileFPS(transcodingPath)
|
const fps = await getVideoFileFPS(transcodingPath)
|
||||||
|
|
||||||
await move(transcodingPath, outputPath)
|
await move(transcodingPath, outputPath)
|
||||||
|
|
||||||
videoFile.set('size', stats.size)
|
videoFile.size = stats.size
|
||||||
videoFile.set('fps', fps)
|
videoFile.fps = fps
|
||||||
|
|
||||||
await video.createTorrentAndSetInfoHash(videoFile)
|
await video.createTorrentAndSetInfoHash(videoFile)
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { areValidationErrors } from './utils'
|
||||||
import { ActorModel } from '../../models/activitypub/actor'
|
import { ActorModel } from '../../models/activitypub/actor'
|
||||||
import { loadActorUrlOrGetFromWebfinger } from '../../helpers/webfinger'
|
import { loadActorUrlOrGetFromWebfinger } from '../../helpers/webfinger'
|
||||||
import { isValidActorHandle } from '../../helpers/custom-validators/activitypub/actor'
|
import { isValidActorHandle } from '../../helpers/custom-validators/activitypub/actor'
|
||||||
|
import { MActorFollowActorsDefault } from '@server/typings/models'
|
||||||
|
|
||||||
const followValidator = [
|
const followValidator = [
|
||||||
body('hosts').custom(isEachUniqueHostValid).withMessage('Should have an array of unique hosts'),
|
body('hosts').custom(isEachUniqueHostValid).withMessage('Should have an array of unique hosts'),
|
||||||
|
@ -65,7 +66,7 @@ const getFollowerValidator = [
|
||||||
|
|
||||||
if (areValidationErrors(req, res)) return
|
if (areValidationErrors(req, res)) return
|
||||||
|
|
||||||
let follow: ActorFollowModel
|
let follow: MActorFollowActorsDefault
|
||||||
try {
|
try {
|
||||||
const actorUrl = await loadActorUrlOrGetFromWebfinger(req.params.nameWithHost)
|
const actorUrl = await loadActorUrlOrGetFromWebfinger(req.params.nameWithHost)
|
||||||
const actor = await ActorModel.loadByUrl(actorUrl)
|
const actor = await ActorModel.loadByUrl(actorUrl)
|
||||||
|
|
|
@ -24,7 +24,7 @@ const videoFileRedundancyGetValidator = [
|
||||||
if (areValidationErrors(req, res)) return
|
if (areValidationErrors(req, res)) return
|
||||||
if (!await doesVideoExist(req.params.videoId, 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 => {
|
const videoFile = video.VideoFiles.find(f => {
|
||||||
return f.resolution === req.params.resolution && (!req.params.fps || f.fps === req.params.fps)
|
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 (areValidationErrors(req, res)) return
|
||||||
if (!await doesVideoExist(req.params.videoId, 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)
|
const videoStreamingPlaylist = video.VideoStreamingPlaylists.find(p => p === req.params.streamingPlaylistType)
|
||||||
|
|
||||||
if (!videoStreamingPlaylist) return res.status(404).json({ error: 'Video playlist not found.' })
|
if (!videoStreamingPlaylist) return res.status(404).json({ error: 'Video playlist not found.' })
|
||||||
|
|
|
@ -2,7 +2,7 @@ import * as Bluebird from 'bluebird'
|
||||||
import * as express from 'express'
|
import * as express from 'express'
|
||||||
import { body, param } from 'express-validator'
|
import { body, param } from 'express-validator'
|
||||||
import { omit } from 'lodash'
|
import { omit } from 'lodash'
|
||||||
import { isIdOrUUIDValid, toBooleanOrNull, toIntOrNull } from '../../helpers/custom-validators/misc'
|
import { isIdOrUUIDValid, toBooleanOrNull } from '../../helpers/custom-validators/misc'
|
||||||
import {
|
import {
|
||||||
isUserAdminFlagsValid,
|
isUserAdminFlagsValid,
|
||||||
isUserAutoPlayVideoValid,
|
isUserAutoPlayVideoValid,
|
||||||
|
@ -31,6 +31,7 @@ import { isThemeNameValid } from '../../helpers/custom-validators/plugins'
|
||||||
import { isThemeRegistered } from '../../lib/plugins/theme-utils'
|
import { isThemeRegistered } from '../../lib/plugins/theme-utils'
|
||||||
import { doesVideoExist } from '../../helpers/middlewares'
|
import { doesVideoExist } from '../../helpers/middlewares'
|
||||||
import { UserRole } from '../../../shared/models/users'
|
import { UserRole } from '../../../shared/models/users'
|
||||||
|
import { MUserDefault } from '@server/typings/models'
|
||||||
|
|
||||||
const usersAddValidator = [
|
const usersAddValidator = [
|
||||||
body('username').custom(isUserUsernameValid).withMessage('Should have a valid username (lowercase alphanumeric characters)'),
|
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
|
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()
|
const user = await finder()
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
|
|
|
@ -33,7 +33,7 @@ const videoAbuseGetValidator = [
|
||||||
|
|
||||||
if (areValidationErrors(req, res)) return
|
if (areValidationErrors(req, res)) return
|
||||||
if (!await doesVideoExist(req.params.videoId, 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()
|
return next()
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ const videoAbuseUpdateValidator = [
|
||||||
|
|
||||||
if (areValidationErrors(req, res)) return
|
if (areValidationErrors(req, res)) return
|
||||||
if (!await doesVideoExist(req.params.videoId, 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()
|
return next()
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ const videosBlacklistRemoveValidator = [
|
||||||
|
|
||||||
if (areValidationErrors(req, res)) return
|
if (areValidationErrors(req, res)) return
|
||||||
if (!await doesVideoExist(req.params.videoId, 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()
|
return next()
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ const videosBlacklistAddValidator = [
|
||||||
if (areValidationErrors(req, res)) return
|
if (areValidationErrors(req, res)) return
|
||||||
if (!await doesVideoExist(req.params.videoId, 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) {
|
if (req.body.unfederate === true && video.remote === true) {
|
||||||
return res
|
return res
|
||||||
.status(409)
|
.status(409)
|
||||||
|
@ -59,7 +59,7 @@ const videosBlacklistUpdateValidator = [
|
||||||
|
|
||||||
if (areValidationErrors(req, res)) return
|
if (areValidationErrors(req, res)) return
|
||||||
if (!await doesVideoExist(req.params.videoId, 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()
|
return next()
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ const addVideoCaptionValidator = [
|
||||||
|
|
||||||
// Check if the user who did the request is able to update the video
|
// Check if the user who did the request is able to update the video
|
||||||
const user = res.locals.oauth.token.User
|
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()
|
return next()
|
||||||
}
|
}
|
||||||
|
@ -41,11 +41,11 @@ const deleteVideoCaptionValidator = [
|
||||||
|
|
||||||
if (areValidationErrors(req, res)) return
|
if (areValidationErrors(req, res)) return
|
||||||
if (!await doesVideoExist(req.params.videoId, 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
|
// Check if the user who did the request is able to update the video
|
||||||
const user = res.locals.oauth.token.User
|
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()
|
return next()
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,13 +7,14 @@ import {
|
||||||
isVideoChannelSupportValid
|
isVideoChannelSupportValid
|
||||||
} from '../../../helpers/custom-validators/video-channels'
|
} from '../../../helpers/custom-validators/video-channels'
|
||||||
import { logger } from '../../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
import { UserModel } from '../../../models/account/user'
|
|
||||||
import { VideoChannelModel } from '../../../models/video/video-channel'
|
import { VideoChannelModel } from '../../../models/video/video-channel'
|
||||||
import { areValidationErrors } from '../utils'
|
import { areValidationErrors } from '../utils'
|
||||||
import { isActorPreferredUsernameValid } from '../../../helpers/custom-validators/activitypub/actor'
|
import { isActorPreferredUsernameValid } from '../../../helpers/custom-validators/activitypub/actor'
|
||||||
import { ActorModel } from '../../../models/activitypub/actor'
|
import { ActorModel } from '../../../models/activitypub/actor'
|
||||||
import { isBooleanValid } from '../../../helpers/custom-validators/misc'
|
import { isBooleanValid } from '../../../helpers/custom-validators/misc'
|
||||||
import { doesLocalVideoChannelNameExist, doesVideoChannelNameWithHostExist } from '../../../helpers/middlewares'
|
import { doesLocalVideoChannelNameExist, doesVideoChannelNameWithHostExist } from '../../../helpers/middlewares'
|
||||||
|
import { MChannelActorAccountDefault } from '../../../typings/models/video'
|
||||||
|
import { MUser } from '@server/typings/models'
|
||||||
|
|
||||||
const videoChannelsAddValidator = [
|
const videoChannelsAddValidator = [
|
||||||
body('name').custom(isActorPreferredUsernameValid).withMessage('Should have a valid channel name'),
|
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) {
|
if (videoChannel.Actor.isOwned() === false) {
|
||||||
res.status(403)
|
res.status(403)
|
||||||
.json({ error: 'Cannot remove video channel of another server.' })
|
.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
Loading…
Reference in New Issue