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