Make it compile at least
This commit is contained in:
parent
38fa206583
commit
571389d43b
|
@ -77,7 +77,6 @@
|
||||||
"pg": "^6.4.2",
|
"pg": "^6.4.2",
|
||||||
"pg-hstore": "^2.3.2",
|
"pg-hstore": "^2.3.2",
|
||||||
"request": "^2.81.0",
|
"request": "^2.81.0",
|
||||||
"request-replay": "^1.0.2",
|
|
||||||
"rimraf": "^2.5.4",
|
"rimraf": "^2.5.4",
|
||||||
"safe-buffer": "^5.0.1",
|
"safe-buffer": "^5.0.1",
|
||||||
"scripty": "^1.5.0",
|
"scripty": "^1.5.0",
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import * as Promise from 'bluebird'
|
|
||||||
|
|
||||||
import { database as db } from '../server/initializers/database'
|
import { database as db } from '../server/initializers/database'
|
||||||
import { hasFriends } from '../server/lib/friends'
|
// import { hasFriends } from '../server/lib/friends'
|
||||||
|
|
||||||
db.init(true)
|
db.init(true)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return hasFriends()
|
// FIXME: check if has followers
|
||||||
|
// return hasFriends()
|
||||||
|
return true
|
||||||
})
|
})
|
||||||
.then(itHasFriends => {
|
.then(itHasFriends => {
|
||||||
if (itHasFriends === true) {
|
if (itHasFriends === true) {
|
||||||
|
|
16
server.ts
16
server.ts
|
@ -46,7 +46,7 @@ db.init(false).then(() => onDatabaseInitDone())
|
||||||
|
|
||||||
// ----------- PeerTube modules -----------
|
// ----------- PeerTube modules -----------
|
||||||
import { migrate, installApplication } from './server/initializers'
|
import { migrate, installApplication } from './server/initializers'
|
||||||
import { JobScheduler, activateSchedulers, VideosPreviewCache } from './server/lib'
|
import { httpRequestJobScheduler, transcodingJobScheduler, VideosPreviewCache } from './server/lib'
|
||||||
import { apiRouter, clientsRouter, staticRouter, servicesRouter } from './server/controllers'
|
import { apiRouter, clientsRouter, staticRouter, servicesRouter } from './server/controllers'
|
||||||
|
|
||||||
// ----------- Command line -----------
|
// ----------- Command line -----------
|
||||||
|
@ -146,19 +146,13 @@ function onDatabaseInitDone () {
|
||||||
const port = CONFIG.LISTEN.PORT
|
const port = CONFIG.LISTEN.PORT
|
||||||
// Run the migration scripts if needed
|
// Run the migration scripts if needed
|
||||||
migrate()
|
migrate()
|
||||||
.then(() => {
|
.then(() => installApplication())
|
||||||
return installApplication()
|
|
||||||
})
|
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// ----------- Make the server listening -----------
|
// ----------- Make the server listening -----------
|
||||||
server.listen(port, function () {
|
server.listen(port, () => {
|
||||||
// Activate the communication with friends
|
|
||||||
activateSchedulers()
|
|
||||||
|
|
||||||
// Activate job scheduler
|
|
||||||
JobScheduler.Instance.activate()
|
|
||||||
|
|
||||||
VideosPreviewCache.Instance.init(CONFIG.CACHE.PREVIEWS.SIZE)
|
VideosPreviewCache.Instance.init(CONFIG.CACHE.PREVIEWS.SIZE)
|
||||||
|
httpRequestJobScheduler.activate()
|
||||||
|
transcodingJobScheduler.activate()
|
||||||
|
|
||||||
logger.info('Server listening on port %d', port)
|
logger.info('Server listening on port %d', port)
|
||||||
logger.info('Web server: %s', CONFIG.WEBSERVER.URL)
|
logger.info('Web server: %s', CONFIG.WEBSERVER.URL)
|
||||||
|
|
|
@ -2,10 +2,12 @@ import * as express from 'express'
|
||||||
|
|
||||||
import { badRequest } from '../../helpers'
|
import { badRequest } from '../../helpers'
|
||||||
import { inboxRouter } from './inbox'
|
import { inboxRouter } from './inbox'
|
||||||
|
import { activityPubClientRouter } from './client'
|
||||||
|
|
||||||
const remoteRouter = express.Router()
|
const remoteRouter = express.Router()
|
||||||
|
|
||||||
remoteRouter.use('/inbox', inboxRouter)
|
remoteRouter.use('/inbox', inboxRouter)
|
||||||
|
remoteRouter.use('/', activityPubClientRouter)
|
||||||
remoteRouter.use('/*', badRequest)
|
remoteRouter.use('/*', badRequest)
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
@ -5,8 +5,6 @@ import { badRequest } from '../../helpers'
|
||||||
import { oauthClientsRouter } from './oauth-clients'
|
import { oauthClientsRouter } from './oauth-clients'
|
||||||
import { configRouter } from './config'
|
import { configRouter } from './config'
|
||||||
import { podsRouter } from './pods'
|
import { podsRouter } from './pods'
|
||||||
import { remoteRouter } from './remote'
|
|
||||||
import { requestSchedulerRouter } from './request-schedulers'
|
|
||||||
import { usersRouter } from './users'
|
import { usersRouter } from './users'
|
||||||
import { videosRouter } from './videos'
|
import { videosRouter } from './videos'
|
||||||
|
|
||||||
|
@ -15,8 +13,6 @@ const apiRouter = express.Router()
|
||||||
apiRouter.use('/oauth-clients', oauthClientsRouter)
|
apiRouter.use('/oauth-clients', oauthClientsRouter)
|
||||||
apiRouter.use('/config', configRouter)
|
apiRouter.use('/config', configRouter)
|
||||||
apiRouter.use('/pods', podsRouter)
|
apiRouter.use('/pods', podsRouter)
|
||||||
apiRouter.use('/remote', remoteRouter)
|
|
||||||
apiRouter.use('/request-schedulers', requestSchedulerRouter)
|
|
||||||
apiRouter.use('/users', usersRouter)
|
apiRouter.use('/users', usersRouter)
|
||||||
apiRouter.use('/videos', videosRouter)
|
apiRouter.use('/videos', videosRouter)
|
||||||
apiRouter.use('/ping', pong)
|
apiRouter.use('/ping', pong)
|
||||||
|
|
|
@ -1,26 +1,7 @@
|
||||||
import * as express from 'express'
|
import * as express from 'express'
|
||||||
|
import { getFormattedObjects } from '../../helpers'
|
||||||
import { database as db } from '../../initializers/database'
|
import { database as db } from '../../initializers/database'
|
||||||
import { logger, getFormattedObjects } from '../../helpers'
|
import { asyncMiddleware, paginationValidator, podsSortValidator, setPagination, setPodsSort } from '../../middlewares'
|
||||||
import {
|
|
||||||
makeFriends,
|
|
||||||
quitFriends,
|
|
||||||
removeFriend
|
|
||||||
} from '../../lib'
|
|
||||||
import {
|
|
||||||
authenticate,
|
|
||||||
ensureUserHasRight,
|
|
||||||
makeFriendsValidator,
|
|
||||||
setBodyHostsPort,
|
|
||||||
podRemoveValidator,
|
|
||||||
paginationValidator,
|
|
||||||
setPagination,
|
|
||||||
setPodsSort,
|
|
||||||
podsSortValidator,
|
|
||||||
asyncMiddleware
|
|
||||||
} from '../../middlewares'
|
|
||||||
import { PodInstance } from '../../models'
|
|
||||||
import { UserRight } from '../../../shared'
|
|
||||||
|
|
||||||
const podsRouter = express.Router()
|
const podsRouter = express.Router()
|
||||||
|
|
||||||
|
@ -31,24 +12,6 @@ podsRouter.get('/',
|
||||||
setPagination,
|
setPagination,
|
||||||
asyncMiddleware(listPods)
|
asyncMiddleware(listPods)
|
||||||
)
|
)
|
||||||
podsRouter.post('/make-friends',
|
|
||||||
authenticate,
|
|
||||||
ensureUserHasRight(UserRight.MANAGE_PODS),
|
|
||||||
makeFriendsValidator,
|
|
||||||
setBodyHostsPort,
|
|
||||||
asyncMiddleware(makeFriendsController)
|
|
||||||
)
|
|
||||||
podsRouter.get('/quit-friends',
|
|
||||||
authenticate,
|
|
||||||
ensureUserHasRight(UserRight.MANAGE_PODS),
|
|
||||||
asyncMiddleware(quitFriendsController)
|
|
||||||
)
|
|
||||||
podsRouter.delete('/:id',
|
|
||||||
authenticate,
|
|
||||||
ensureUserHasRight(UserRight.MANAGE_PODS),
|
|
||||||
podRemoveValidator,
|
|
||||||
asyncMiddleware(removeFriendController)
|
|
||||||
)
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -63,28 +26,3 @@ async function listPods (req: express.Request, res: express.Response, next: expr
|
||||||
|
|
||||||
return res.json(getFormattedObjects(resultList.data, resultList.total))
|
return res.json(getFormattedObjects(resultList.data, resultList.total))
|
||||||
}
|
}
|
||||||
|
|
||||||
async function makeFriendsController (req: express.Request, res: express.Response, next: express.NextFunction) {
|
|
||||||
const hosts = req.body.hosts as string[]
|
|
||||||
|
|
||||||
// Don't wait the process that could be long
|
|
||||||
makeFriends(hosts)
|
|
||||||
.then(() => logger.info('Made friends!'))
|
|
||||||
.catch(err => logger.error('Could not make friends.', err))
|
|
||||||
|
|
||||||
return res.type('json').status(204).end()
|
|
||||||
}
|
|
||||||
|
|
||||||
async function quitFriendsController (req: express.Request, res: express.Response, next: express.NextFunction) {
|
|
||||||
await quitFriends()
|
|
||||||
|
|
||||||
return res.type('json').status(204).end()
|
|
||||||
}
|
|
||||||
|
|
||||||
async function removeFriendController (req: express.Request, res: express.Response, next: express.NextFunction) {
|
|
||||||
const pod = res.locals.pod as PodInstance
|
|
||||||
|
|
||||||
await removeFriend(pod)
|
|
||||||
|
|
||||||
return res.type('json').status(204).end()
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,53 +0,0 @@
|
||||||
import * as express from 'express'
|
|
||||||
import * as Bluebird from 'bluebird'
|
|
||||||
|
|
||||||
import {
|
|
||||||
AbstractRequestScheduler,
|
|
||||||
getRequestScheduler,
|
|
||||||
getRequestVideoQaduScheduler,
|
|
||||||
getRequestVideoEventScheduler
|
|
||||||
} from '../../lib'
|
|
||||||
import { authenticate, ensureUserHasRight, asyncMiddleware } from '../../middlewares'
|
|
||||||
import { RequestSchedulerStatsAttributes, UserRight } from '../../../shared'
|
|
||||||
|
|
||||||
const requestSchedulerRouter = express.Router()
|
|
||||||
|
|
||||||
requestSchedulerRouter.get('/stats',
|
|
||||||
authenticate,
|
|
||||||
ensureUserHasRight(UserRight.MANAGE_REQUEST_SCHEDULERS),
|
|
||||||
asyncMiddleware(getRequestSchedulersStats)
|
|
||||||
)
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
export {
|
|
||||||
requestSchedulerRouter
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
async function getRequestSchedulersStats (req: express.Request, res: express.Response, next: express.NextFunction) {
|
|
||||||
const result = await Bluebird.props({
|
|
||||||
requestScheduler: buildRequestSchedulerStats(getRequestScheduler()),
|
|
||||||
requestVideoQaduScheduler: buildRequestSchedulerStats(getRequestVideoQaduScheduler()),
|
|
||||||
requestVideoEventScheduler: buildRequestSchedulerStats(getRequestVideoEventScheduler())
|
|
||||||
})
|
|
||||||
|
|
||||||
return res.json(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
async function buildRequestSchedulerStats (requestScheduler: AbstractRequestScheduler<any>) {
|
|
||||||
const count = await requestScheduler.remainingRequestsCount()
|
|
||||||
|
|
||||||
const result: RequestSchedulerStatsAttributes = {
|
|
||||||
totalRequests: count,
|
|
||||||
requestsLimitPods: requestScheduler.limitPods,
|
|
||||||
requestsLimitPerPod: requestScheduler.limitPerPod,
|
|
||||||
remainingMilliSeconds: requestScheduler.remainingMilliSeconds(),
|
|
||||||
milliSecondsInterval: requestScheduler.requestInterval
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
|
@ -1,37 +1,29 @@
|
||||||
import * as express from 'express'
|
import * as express from 'express'
|
||||||
|
import { UserCreate, UserRight, UserRole, UserUpdate, UserUpdateMe, UserVideoRate as FormattedUserVideoRate } from '../../../shared'
|
||||||
import { database as db, CONFIG } from '../../initializers'
|
import { getFormattedObjects, logger, retryTransactionWrapper } from '../../helpers'
|
||||||
import { logger, getFormattedObjects, retryTransactionWrapper } from '../../helpers'
|
import { CONFIG, database as db } from '../../initializers'
|
||||||
|
import { createUserAccountAndChannel } from '../../lib'
|
||||||
import {
|
import {
|
||||||
|
asyncMiddleware,
|
||||||
authenticate,
|
authenticate,
|
||||||
ensureUserHasRight,
|
ensureUserHasRight,
|
||||||
ensureUserRegistrationAllowed,
|
ensureUserRegistrationAllowed,
|
||||||
usersAddValidator,
|
|
||||||
usersRegisterValidator,
|
|
||||||
usersUpdateValidator,
|
|
||||||
usersUpdateMeValidator,
|
|
||||||
usersRemoveValidator,
|
|
||||||
usersVideoRatingValidator,
|
|
||||||
usersGetValidator,
|
|
||||||
paginationValidator,
|
paginationValidator,
|
||||||
setPagination,
|
setPagination,
|
||||||
usersSortValidator,
|
|
||||||
setUsersSort,
|
setUsersSort,
|
||||||
token,
|
token,
|
||||||
asyncMiddleware
|
usersAddValidator,
|
||||||
|
usersGetValidator,
|
||||||
|
usersRegisterValidator,
|
||||||
|
usersRemoveValidator,
|
||||||
|
usersSortValidator,
|
||||||
|
usersUpdateMeValidator,
|
||||||
|
usersUpdateValidator,
|
||||||
|
usersVideoRatingValidator
|
||||||
} from '../../middlewares'
|
} from '../../middlewares'
|
||||||
import {
|
|
||||||
UserVideoRate as FormattedUserVideoRate,
|
|
||||||
UserCreate,
|
|
||||||
UserUpdate,
|
|
||||||
UserUpdateMe,
|
|
||||||
UserRole,
|
|
||||||
UserRight
|
|
||||||
} from '../../../shared'
|
|
||||||
import { createUserAccountAndChannel } from '../../lib'
|
|
||||||
import { UserInstance } from '../../models'
|
|
||||||
import { videosSortValidator } from '../../middlewares/validators/sort'
|
|
||||||
import { setVideosSort } from '../../middlewares/sort'
|
import { setVideosSort } from '../../middlewares/sort'
|
||||||
|
import { videosSortValidator } from '../../middlewares/validators/sort'
|
||||||
|
import { UserInstance } from '../../models'
|
||||||
|
|
||||||
const usersRouter = express.Router()
|
const usersRouter = express.Router()
|
||||||
|
|
||||||
|
@ -176,9 +168,9 @@ function getUser (req: express.Request, res: express.Response, next: express.Nex
|
||||||
|
|
||||||
async function getUserVideoRating (req: express.Request, res: express.Response, next: express.NextFunction) {
|
async function getUserVideoRating (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||||
const videoId = +req.params.videoId
|
const videoId = +req.params.videoId
|
||||||
const userId = +res.locals.oauth.token.User.id
|
const accountId = +res.locals.oauth.token.User.Account.id
|
||||||
|
|
||||||
const ratingObj = await db.UserVideoRate.load(userId, videoId, null)
|
const ratingObj = await db.AccountVideoRate.load(accountId, videoId, null)
|
||||||
const rating = ratingObj ? ratingObj.type : 'none'
|
const rating = ratingObj ? ratingObj.type : 'none'
|
||||||
|
|
||||||
const json: FormattedUserVideoRate = {
|
const json: FormattedUserVideoRate = {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import * as express from 'express'
|
import * as express from 'express'
|
||||||
|
|
||||||
import { database as db } from '../../../initializers/database'
|
import { database as db } from '../../../initializers/database'
|
||||||
import * as friends from '../../../lib/friends'
|
|
||||||
import {
|
import {
|
||||||
logger,
|
logger,
|
||||||
getFormattedObjects,
|
getFormattedObjects,
|
||||||
|
@ -84,7 +83,8 @@ async function reportVideoAbuse (req: express.Request, res: express.Response) {
|
||||||
videoUUID: videoInstance.uuid
|
videoUUID: videoInstance.uuid
|
||||||
}
|
}
|
||||||
|
|
||||||
await friends.reportAbuseVideoToFriend(reportData, videoInstance, t)
|
// await friends.reportAbuseVideoToFriend(reportData, videoInstance, t)
|
||||||
|
// TODO: send abuse to origin pod
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -1,31 +1,23 @@
|
||||||
import * as express from 'express'
|
import * as express from 'express'
|
||||||
|
|
||||||
import { database as db } from '../../../initializers'
|
|
||||||
import {
|
|
||||||
logger,
|
|
||||||
getFormattedObjects,
|
|
||||||
retryTransactionWrapper,
|
|
||||||
resetSequelizeInstance
|
|
||||||
} from '../../../helpers'
|
|
||||||
import {
|
|
||||||
authenticate,
|
|
||||||
paginationValidator,
|
|
||||||
videoChannelsSortValidator,
|
|
||||||
videoChannelsAddValidator,
|
|
||||||
setVideoChannelsSort,
|
|
||||||
setPagination,
|
|
||||||
videoChannelsRemoveValidator,
|
|
||||||
videoChannelGetValidator,
|
|
||||||
videoChannelsUpdateValidator,
|
|
||||||
listVideoAccountChannelsValidator,
|
|
||||||
asyncMiddleware
|
|
||||||
} from '../../../middlewares'
|
|
||||||
import {
|
|
||||||
createVideoChannel,
|
|
||||||
updateVideoChannelToFriends
|
|
||||||
} from '../../../lib'
|
|
||||||
import { VideoChannelInstance, AccountInstance } from '../../../models'
|
|
||||||
import { VideoChannelCreate, VideoChannelUpdate } from '../../../../shared'
|
import { VideoChannelCreate, VideoChannelUpdate } from '../../../../shared'
|
||||||
|
import { getFormattedObjects, logger, resetSequelizeInstance, retryTransactionWrapper } from '../../../helpers'
|
||||||
|
import { database as db } from '../../../initializers'
|
||||||
|
import { createVideoChannel } from '../../../lib'
|
||||||
|
import {
|
||||||
|
asyncMiddleware,
|
||||||
|
authenticate,
|
||||||
|
listVideoAccountChannelsValidator,
|
||||||
|
paginationValidator,
|
||||||
|
setPagination,
|
||||||
|
setVideoChannelsSort,
|
||||||
|
videoChannelGetValidator,
|
||||||
|
videoChannelsAddValidator,
|
||||||
|
videoChannelsRemoveValidator,
|
||||||
|
videoChannelsSortValidator,
|
||||||
|
videoChannelsUpdateValidator
|
||||||
|
} from '../../../middlewares'
|
||||||
|
import { AccountInstance, VideoChannelInstance } from '../../../models'
|
||||||
|
import { sendUpdateVideoChannel } from '../../../lib/activitypub/send-request'
|
||||||
|
|
||||||
const videoChannelRouter = express.Router()
|
const videoChannelRouter = express.Router()
|
||||||
|
|
||||||
|
@ -137,11 +129,8 @@ async function updateVideoChannel (req: express.Request, res: express.Response)
|
||||||
if (videoChannelInfoToUpdate.description !== undefined) videoChannelInstance.set('description', videoChannelInfoToUpdate.description)
|
if (videoChannelInfoToUpdate.description !== undefined) videoChannelInstance.set('description', videoChannelInfoToUpdate.description)
|
||||||
|
|
||||||
await videoChannelInstance.save(sequelizeOptions)
|
await videoChannelInstance.save(sequelizeOptions)
|
||||||
const json = videoChannelInstance.toUpdateRemoteJSON()
|
|
||||||
|
|
||||||
// Now we'll update the video channel's meta data to our friends
|
|
||||||
return updateVideoChannelToFriends(json, t)
|
|
||||||
|
|
||||||
|
await sendUpdateVideoChannel(videoChannelInstance, t)
|
||||||
})
|
})
|
||||||
|
|
||||||
logger.info('Video channel with name %s and uuid %s updated.', videoChannelInstance.name, videoChannelInstance.uuid)
|
logger.info('Video channel with name %s and uuid %s updated.', videoChannelInstance.name, videoChannelInstance.uuid)
|
||||||
|
|
|
@ -1,57 +1,41 @@
|
||||||
import * as express from 'express'
|
import * as express from 'express'
|
||||||
import * as multer from 'multer'
|
import * as multer from 'multer'
|
||||||
import { extname, join } from 'path'
|
import { extname, join } from 'path'
|
||||||
|
import { VideoCreate, VideoPrivacy, VideoUpdate } from '../../../../shared'
|
||||||
|
import {
|
||||||
|
fetchRemoteVideoDescription,
|
||||||
|
generateRandomString,
|
||||||
|
getFormattedObjects,
|
||||||
|
getVideoFileHeight,
|
||||||
|
logger,
|
||||||
|
renamePromise,
|
||||||
|
resetSequelizeInstance,
|
||||||
|
retryTransactionWrapper
|
||||||
|
} from '../../../helpers'
|
||||||
|
import { getActivityPubUrl } from '../../../helpers/activitypub'
|
||||||
|
import { CONFIG, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_MIMETYPE_EXT, VIDEO_PRIVACIES } from '../../../initializers'
|
||||||
import { database as db } from '../../../initializers/database'
|
import { database as db } from '../../../initializers/database'
|
||||||
|
import { sendAddVideo, sendUpdateVideoChannel } from '../../../lib/activitypub/send-request'
|
||||||
|
import { transcodingJobScheduler } from '../../../lib/jobs/transcoding-job-scheduler/transcoding-job-scheduler'
|
||||||
import {
|
import {
|
||||||
CONFIG,
|
asyncMiddleware,
|
||||||
REQUEST_VIDEO_QADU_TYPES,
|
|
||||||
REQUEST_VIDEO_EVENT_TYPES,
|
|
||||||
VIDEO_CATEGORIES,
|
|
||||||
VIDEO_LICENCES,
|
|
||||||
VIDEO_LANGUAGES,
|
|
||||||
VIDEO_PRIVACIES,
|
|
||||||
VIDEO_MIMETYPE_EXT
|
|
||||||
} from '../../../initializers'
|
|
||||||
import {
|
|
||||||
addEventToRemoteVideo,
|
|
||||||
quickAndDirtyUpdateVideoToFriends,
|
|
||||||
addVideoToFriends,
|
|
||||||
updateVideoToFriends,
|
|
||||||
JobScheduler,
|
|
||||||
fetchRemoteDescription
|
|
||||||
} from '../../../lib'
|
|
||||||
import {
|
|
||||||
authenticate,
|
authenticate,
|
||||||
paginationValidator,
|
paginationValidator,
|
||||||
videosSortValidator,
|
|
||||||
setVideosSort,
|
|
||||||
setPagination,
|
setPagination,
|
||||||
setVideosSearch,
|
setVideosSearch,
|
||||||
videosUpdateValidator,
|
setVideosSort,
|
||||||
videosSearchValidator,
|
|
||||||
videosAddValidator,
|
videosAddValidator,
|
||||||
videosGetValidator,
|
videosGetValidator,
|
||||||
videosRemoveValidator,
|
videosRemoveValidator,
|
||||||
asyncMiddleware
|
videosSearchValidator,
|
||||||
|
videosSortValidator,
|
||||||
|
videosUpdateValidator
|
||||||
} from '../../../middlewares'
|
} from '../../../middlewares'
|
||||||
import {
|
|
||||||
logger,
|
|
||||||
retryTransactionWrapper,
|
|
||||||
generateRandomString,
|
|
||||||
getFormattedObjects,
|
|
||||||
renamePromise,
|
|
||||||
getVideoFileHeight,
|
|
||||||
resetSequelizeInstance
|
|
||||||
} from '../../../helpers'
|
|
||||||
import { VideoInstance } from '../../../models'
|
import { VideoInstance } from '../../../models'
|
||||||
import { VideoCreate, VideoUpdate, VideoPrivacy } from '../../../../shared'
|
|
||||||
|
|
||||||
import { abuseVideoRouter } from './abuse'
|
import { abuseVideoRouter } from './abuse'
|
||||||
import { blacklistRouter } from './blacklist'
|
import { blacklistRouter } from './blacklist'
|
||||||
import { rateVideoRouter } from './rate'
|
|
||||||
import { videoChannelRouter } from './channel'
|
import { videoChannelRouter } from './channel'
|
||||||
import { getActivityPubUrl } from '../../../helpers/activitypub'
|
import { rateVideoRouter } from './rate'
|
||||||
|
|
||||||
const videosRouter = express.Router()
|
const videosRouter = express.Router()
|
||||||
|
|
||||||
|
@ -225,7 +209,7 @@ async function addVideo (req: express.Request, res: express.Response, videoPhysi
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.push(
|
tasks.push(
|
||||||
JobScheduler.Instance.createJob(t, 'videoFileOptimizer', dataInput)
|
transcodingJobScheduler.createJob(t, 'videoFileOptimizer', dataInput)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
await Promise.all(tasks)
|
await Promise.all(tasks)
|
||||||
|
@ -252,9 +236,7 @@ async function addVideo (req: express.Request, res: express.Response, videoPhysi
|
||||||
// Don't send video to remote pods, it is private
|
// Don't send video to remote pods, it is private
|
||||||
if (video.privacy === VideoPrivacy.PRIVATE) return undefined
|
if (video.privacy === VideoPrivacy.PRIVATE) return undefined
|
||||||
|
|
||||||
const remoteVideo = await video.toAddRemoteJSON()
|
await sendAddVideo(video, t)
|
||||||
// Now we'll add the video's meta data to our friends
|
|
||||||
return addVideoToFriends(remoteVideo, t)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
logger.info('Video with name %s and uuid %s created.', videoInfo.name, videoUUID)
|
logger.info('Video with name %s and uuid %s created.', videoInfo.name, videoUUID)
|
||||||
|
@ -302,14 +284,12 @@ async function updateVideo (req: express.Request, res: express.Response) {
|
||||||
|
|
||||||
// Now we'll update the video's meta data to our friends
|
// Now we'll update the video's meta data to our friends
|
||||||
if (wasPrivateVideo === false) {
|
if (wasPrivateVideo === false) {
|
||||||
const json = videoInstance.toUpdateRemoteJSON()
|
await sendUpdateVideoChannel(videoInstance, t)
|
||||||
return updateVideoToFriends(json, t)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Video is not private anymore, send a create action to remote pods
|
// Video is not private anymore, send a create action to remote pods
|
||||||
if (wasPrivateVideo === true && videoInstance.privacy !== VideoPrivacy.PRIVATE) {
|
if (wasPrivateVideo === true && videoInstance.privacy !== VideoPrivacy.PRIVATE) {
|
||||||
const remoteVideo = await videoInstance.toAddRemoteJSON()
|
await sendAddVideo(videoInstance, t)
|
||||||
return addVideoToFriends(remoteVideo, t)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -324,7 +304,7 @@ async function updateVideo (req: express.Request, res: express.Response) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getVideo (req: express.Request, res: express.Response) {
|
async function getVideo (req: express.Request, res: express.Response) {
|
||||||
const videoInstance = res.locals.video
|
const videoInstance = res.locals.video
|
||||||
|
|
||||||
if (videoInstance.isOwned()) {
|
if (videoInstance.isOwned()) {
|
||||||
|
@ -333,21 +313,11 @@ function getVideo (req: express.Request, res: express.Response) {
|
||||||
// For example, only add a view when a user watch a video during 30s etc
|
// For example, only add a view when a user watch a video during 30s etc
|
||||||
videoInstance.increment('views')
|
videoInstance.increment('views')
|
||||||
.then(() => {
|
.then(() => {
|
||||||
const qaduParams = {
|
// TODO: send to followers a notification
|
||||||
videoId: videoInstance.id,
|
|
||||||
type: REQUEST_VIDEO_QADU_TYPES.VIEWS
|
|
||||||
}
|
|
||||||
return quickAndDirtyUpdateVideoToFriends(qaduParams)
|
|
||||||
})
|
})
|
||||||
.catch(err => logger.error('Cannot add view to video %s.', videoInstance.uuid, err))
|
.catch(err => logger.error('Cannot add view to video %s.', videoInstance.uuid, err))
|
||||||
} else {
|
} else {
|
||||||
// Just send the event to our friends
|
// TODO: send view event to followers
|
||||||
const eventParams = {
|
|
||||||
videoId: videoInstance.id,
|
|
||||||
type: REQUEST_VIDEO_EVENT_TYPES.VIEWS
|
|
||||||
}
|
|
||||||
addEventToRemoteVideo(eventParams)
|
|
||||||
.catch(err => logger.error('Cannot add event to remote video %s.', videoInstance.uuid, err))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not wait the view system
|
// Do not wait the view system
|
||||||
|
@ -361,7 +331,7 @@ async function getVideoDescription (req: express.Request, res: express.Response)
|
||||||
if (videoInstance.isOwned()) {
|
if (videoInstance.isOwned()) {
|
||||||
description = videoInstance.description
|
description = videoInstance.description
|
||||||
} else {
|
} else {
|
||||||
description = await fetchRemoteDescription(videoInstance)
|
description = await fetchRemoteVideoDescription(videoInstance)
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.json({ description })
|
return res.json({ description })
|
||||||
|
|
|
@ -1,25 +1,11 @@
|
||||||
import * as express from 'express'
|
import * as express from 'express'
|
||||||
|
|
||||||
import { database as db } from '../../../initializers/database'
|
|
||||||
import {
|
|
||||||
logger,
|
|
||||||
retryTransactionWrapper
|
|
||||||
} from '../../../helpers'
|
|
||||||
import {
|
|
||||||
VIDEO_RATE_TYPES,
|
|
||||||
REQUEST_VIDEO_EVENT_TYPES,
|
|
||||||
REQUEST_VIDEO_QADU_TYPES
|
|
||||||
} from '../../../initializers'
|
|
||||||
import {
|
|
||||||
addEventsToRemoteVideo,
|
|
||||||
quickAndDirtyUpdatesVideoToFriends
|
|
||||||
} from '../../../lib'
|
|
||||||
import {
|
|
||||||
authenticate,
|
|
||||||
videoRateValidator,
|
|
||||||
asyncMiddleware
|
|
||||||
} from '../../../middlewares'
|
|
||||||
import { UserVideoRateUpdate } from '../../../../shared'
|
import { UserVideoRateUpdate } from '../../../../shared'
|
||||||
|
import { logger, retryTransactionWrapper } from '../../../helpers'
|
||||||
|
import { VIDEO_RATE_TYPES } from '../../../initializers'
|
||||||
|
import { database as db } from '../../../initializers/database'
|
||||||
|
import { asyncMiddleware, authenticate, videoRateValidator } from '../../../middlewares'
|
||||||
|
import { AccountInstance } from '../../../models/account/account-interface'
|
||||||
|
import { VideoInstance } from '../../../models/video/video-interface'
|
||||||
|
|
||||||
const rateVideoRouter = express.Router()
|
const rateVideoRouter = express.Router()
|
||||||
|
|
||||||
|
@ -51,12 +37,12 @@ async function rateVideoRetryWrapper (req: express.Request, res: express.Respons
|
||||||
async function rateVideo (req: express.Request, res: express.Response) {
|
async function rateVideo (req: express.Request, res: express.Response) {
|
||||||
const body: UserVideoRateUpdate = req.body
|
const body: UserVideoRateUpdate = req.body
|
||||||
const rateType = body.rating
|
const rateType = body.rating
|
||||||
const videoInstance = res.locals.video
|
const videoInstance: VideoInstance = res.locals.video
|
||||||
const userInstance = res.locals.oauth.token.User
|
const accountInstance: AccountInstance = res.locals.oauth.token.User.Account
|
||||||
|
|
||||||
await db.sequelize.transaction(async t => {
|
await db.sequelize.transaction(async t => {
|
||||||
const sequelizeOptions = { transaction: t }
|
const sequelizeOptions = { transaction: t }
|
||||||
const previousRate = await db.UserVideoRate.load(userInstance.id, videoInstance.id, t)
|
const previousRate = await db.AccountVideoRate.load(accountInstance.id, videoInstance.id, t)
|
||||||
|
|
||||||
let likesToIncrement = 0
|
let likesToIncrement = 0
|
||||||
let dislikesToIncrement = 0
|
let dislikesToIncrement = 0
|
||||||
|
@ -79,12 +65,12 @@ async function rateVideo (req: express.Request, res: express.Response) {
|
||||||
}
|
}
|
||||||
} else if (rateType !== 'none') { // There was not a previous rate, insert a new one if there is a rate
|
} else if (rateType !== 'none') { // There was not a previous rate, insert a new one if there is a rate
|
||||||
const query = {
|
const query = {
|
||||||
userId: userInstance.id,
|
accountId: accountInstance.id,
|
||||||
videoId: videoInstance.id,
|
videoId: videoInstance.id,
|
||||||
type: rateType
|
type: rateType
|
||||||
}
|
}
|
||||||
|
|
||||||
await db.UserVideoRate.create(query, sequelizeOptions)
|
await db.AccountVideoRate.create(query, sequelizeOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
const incrementQuery = {
|
const incrementQuery = {
|
||||||
|
@ -96,48 +82,12 @@ async function rateVideo (req: express.Request, res: express.Response) {
|
||||||
// It is useful for the user to have a feedback
|
// It is useful for the user to have a feedback
|
||||||
await videoInstance.increment(incrementQuery, sequelizeOptions)
|
await videoInstance.increment(incrementQuery, sequelizeOptions)
|
||||||
|
|
||||||
// Send a event to original pod
|
|
||||||
if (videoInstance.isOwned() === false) {
|
if (videoInstance.isOwned() === false) {
|
||||||
|
// TODO: Send a event to original pod
|
||||||
const eventsParams = []
|
} else {
|
||||||
|
// TODO: Send update to followers
|
||||||
if (likesToIncrement !== 0) {
|
|
||||||
eventsParams.push({
|
|
||||||
videoId: videoInstance.id,
|
|
||||||
type: REQUEST_VIDEO_EVENT_TYPES.LIKES,
|
|
||||||
count: likesToIncrement
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dislikesToIncrement !== 0) {
|
|
||||||
eventsParams.push({
|
|
||||||
videoId: videoInstance.id,
|
|
||||||
type: REQUEST_VIDEO_EVENT_TYPES.DISLIKES,
|
|
||||||
count: dislikesToIncrement
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
await addEventsToRemoteVideo(eventsParams, t)
|
|
||||||
} else { // We own the video, we need to send a quick and dirty update to friends to notify the counts changed
|
|
||||||
const qadusParams = []
|
|
||||||
|
|
||||||
if (likesToIncrement !== 0) {
|
|
||||||
qadusParams.push({
|
|
||||||
videoId: videoInstance.id,
|
|
||||||
type: REQUEST_VIDEO_QADU_TYPES.LIKES
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dislikesToIncrement !== 0) {
|
|
||||||
qadusParams.push({
|
|
||||||
videoId: videoInstance.id,
|
|
||||||
type: REQUEST_VIDEO_QADU_TYPES.DISLIKES
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
await quickAndDirtyUpdatesVideoToFriends(qadusParams, t)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
logger.info('User video rate for video %s of user %s updated.', videoInstance.name, userInstance.username)
|
logger.info('Account video rate for video %s of account %s updated.', videoInstance.name, accountInstance.name)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
|
import { join } from 'path'
|
||||||
|
import * as request from 'request'
|
||||||
import * as url from 'url'
|
import * as url from 'url'
|
||||||
|
import { ActivityIconObject } from '../../shared/index'
|
||||||
import { database as db } from '../initializers'
|
|
||||||
import { logger } from './logger'
|
|
||||||
import { doRequest, doRequestAndSaveToFile } from './requests'
|
|
||||||
import { isRemoteAccountValid } from './custom-validators'
|
|
||||||
import { ActivityPubActor } from '../../shared/models/activitypub/activitypub-actor'
|
import { ActivityPubActor } from '../../shared/models/activitypub/activitypub-actor'
|
||||||
import { ResultList } from '../../shared/models/result-list.model'
|
import { ResultList } from '../../shared/models/result-list.model'
|
||||||
import { CONFIG } from '../initializers/constants'
|
import { database as db, REMOTE_SCHEME } from '../initializers'
|
||||||
|
import { CONFIG, STATIC_PATHS } from '../initializers/constants'
|
||||||
import { VideoInstance } from '../models/video/video-interface'
|
import { VideoInstance } from '../models/video/video-interface'
|
||||||
import { ActivityIconObject } from '../../shared/index'
|
import { isRemoteAccountValid } from './custom-validators'
|
||||||
import { join } from 'path'
|
import { logger } from './logger'
|
||||||
|
import { doRequest, doRequestAndSaveToFile } from './requests'
|
||||||
|
|
||||||
function generateThumbnailFromUrl (video: VideoInstance, icon: ActivityIconObject) {
|
function generateThumbnailFromUrl (video: VideoInstance, icon: ActivityIconObject) {
|
||||||
const thumbnailName = video.getThumbnailName()
|
const thumbnailName = video.getThumbnailName()
|
||||||
|
@ -22,9 +22,10 @@ function generateThumbnailFromUrl (video: VideoInstance, icon: ActivityIconObjec
|
||||||
return doRequestAndSaveToFile(options, thumbnailPath)
|
return doRequestAndSaveToFile(options, thumbnailPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
function getActivityPubUrl (type: 'video' | 'videoChannel', uuid: string) {
|
function getActivityPubUrl (type: 'video' | 'videoChannel' | 'account', id: string) {
|
||||||
if (type === 'video') return CONFIG.WEBSERVER.URL + '/videos/watch/' + uuid
|
if (type === 'video') return CONFIG.WEBSERVER.URL + '/videos/watch/' + id
|
||||||
else if (type === 'videoChannel') return CONFIG.WEBSERVER.URL + '/video-channels/' + uuid
|
else if (type === 'videoChannel') return CONFIG.WEBSERVER.URL + '/video-channels/' + id
|
||||||
|
else if (type === 'account') return CONFIG.WEBSERVER.URL + '/account/' + id
|
||||||
|
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
@ -94,7 +95,24 @@ async function fetchRemoteAccountAndCreatePod (accountUrl: string) {
|
||||||
return { account, pod }
|
return { account, pod }
|
||||||
}
|
}
|
||||||
|
|
||||||
function activityPubContextify (data: object) {
|
function fetchRemoteVideoPreview (video: VideoInstance) {
|
||||||
|
// FIXME: use url
|
||||||
|
const host = video.VideoChannel.Account.Pod.host
|
||||||
|
const path = join(STATIC_PATHS.PREVIEWS, video.getPreviewName())
|
||||||
|
|
||||||
|
return request.get(REMOTE_SCHEME.HTTP + '://' + host + path)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchRemoteVideoDescription (video: VideoInstance) {
|
||||||
|
const options = {
|
||||||
|
uri: video.url
|
||||||
|
}
|
||||||
|
|
||||||
|
const { body } = await doRequest(options)
|
||||||
|
return body.description ? body.description : ''
|
||||||
|
}
|
||||||
|
|
||||||
|
function activityPubContextify <T> (data: T) {
|
||||||
return Object.assign(data,{
|
return Object.assign(data,{
|
||||||
'@context': [
|
'@context': [
|
||||||
'https://www.w3.org/ns/activitystreams',
|
'https://www.w3.org/ns/activitystreams',
|
||||||
|
@ -141,7 +159,9 @@ export {
|
||||||
activityPubCollectionPagination,
|
activityPubCollectionPagination,
|
||||||
getActivityPubUrl,
|
getActivityPubUrl,
|
||||||
generateThumbnailFromUrl,
|
generateThumbnailFromUrl,
|
||||||
getOrCreateAccount
|
getOrCreateAccount,
|
||||||
|
fetchRemoteVideoPreview,
|
||||||
|
fetchRemoteVideoDescription
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
@ -1,16 +1,6 @@
|
||||||
import * as replay from 'request-replay'
|
|
||||||
import * as request from 'request'
|
|
||||||
import * as Promise from 'bluebird'
|
import * as Promise from 'bluebird'
|
||||||
|
|
||||||
import {
|
|
||||||
RETRY_REQUESTS,
|
|
||||||
REMOTE_SCHEME,
|
|
||||||
CONFIG
|
|
||||||
} from '../initializers'
|
|
||||||
import { PodInstance } from '../models'
|
|
||||||
import { PodSignature } from '../../shared'
|
|
||||||
import { signObject } from './peertube-crypto'
|
|
||||||
import { createWriteStream } from 'fs'
|
import { createWriteStream } from 'fs'
|
||||||
|
import * as request from 'request'
|
||||||
|
|
||||||
function doRequest (requestOptions: request.CoreOptions & request.UriOptions) {
|
function doRequest (requestOptions: request.CoreOptions & request.UriOptions) {
|
||||||
return new Promise<{ response: request.RequestResponse, body: any }>((res, rej) => {
|
return new Promise<{ response: request.RequestResponse, body: any }>((res, rej) => {
|
||||||
|
@ -27,78 +17,9 @@ function doRequestAndSaveToFile (requestOptions: request.CoreOptions & request.U
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type MakeRetryRequestParams = {
|
|
||||||
url: string,
|
|
||||||
method: 'GET' | 'POST',
|
|
||||||
json: Object
|
|
||||||
}
|
|
||||||
function makeRetryRequest (params: MakeRetryRequestParams) {
|
|
||||||
return new Promise<{ response: request.RequestResponse, body: any }>((res, rej) => {
|
|
||||||
replay(
|
|
||||||
request(params, (err, response, body) => err ? rej(err) : res({ response, body })),
|
|
||||||
{
|
|
||||||
retries: RETRY_REQUESTS,
|
|
||||||
factor: 3,
|
|
||||||
maxTimeout: Infinity,
|
|
||||||
errorCodes: [ 'EADDRINFO', 'ETIMEDOUT', 'ECONNRESET', 'ESOCKETTIMEDOUT', 'ENOTFOUND', 'ECONNREFUSED' ]
|
|
||||||
}
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
type MakeSecureRequestParams = {
|
|
||||||
toPod: PodInstance
|
|
||||||
path: string
|
|
||||||
data?: Object
|
|
||||||
}
|
|
||||||
function makeSecureRequest (params: MakeSecureRequestParams) {
|
|
||||||
const requestParams: {
|
|
||||||
method: 'POST',
|
|
||||||
uri: string,
|
|
||||||
json: {
|
|
||||||
signature: PodSignature,
|
|
||||||
data: any
|
|
||||||
}
|
|
||||||
} = {
|
|
||||||
method: 'POST',
|
|
||||||
uri: REMOTE_SCHEME.HTTP + '://' + params.toPod.host + params.path,
|
|
||||||
json: {
|
|
||||||
signature: null,
|
|
||||||
data: null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const host = CONFIG.WEBSERVER.HOST
|
|
||||||
|
|
||||||
let dataToSign
|
|
||||||
if (params.data) {
|
|
||||||
dataToSign = params.data
|
|
||||||
} else {
|
|
||||||
// We do not have data to sign so we just take our host
|
|
||||||
// It is not ideal but the connection should be in HTTPS
|
|
||||||
dataToSign = host
|
|
||||||
}
|
|
||||||
|
|
||||||
sign(dataToSign).then(signature => {
|
|
||||||
requestParams.json.signature = {
|
|
||||||
host, // Which host we pretend to be
|
|
||||||
signature
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there are data information
|
|
||||||
if (params.data) {
|
|
||||||
requestParams.json.data = params.data
|
|
||||||
}
|
|
||||||
|
|
||||||
return doRequest(requestParams)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
export {
|
export {
|
||||||
doRequest,
|
doRequest,
|
||||||
doRequestAndSaveToFile,
|
doRequestAndSaveToFile
|
||||||
makeRetryRequest,
|
|
||||||
makeSecureRequest
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ export {
|
||||||
|
|
||||||
function webfingerLookup (url: string) {
|
function webfingerLookup (url: string) {
|
||||||
return new Promise<WebFingerData>((res, rej) => {
|
return new Promise<WebFingerData>((res, rej) => {
|
||||||
webfinger.lookup('nick@silverbucket.net', (err, p) => {
|
webfinger.lookup(url, (err, p) => {
|
||||||
if (err) return rej(err)
|
if (err) return rej(err)
|
||||||
|
|
||||||
return p
|
return p
|
||||||
|
|
|
@ -361,12 +361,6 @@ export {
|
||||||
PODS_SCORE,
|
PODS_SCORE,
|
||||||
PREVIEWS_SIZE,
|
PREVIEWS_SIZE,
|
||||||
REMOTE_SCHEME,
|
REMOTE_SCHEME,
|
||||||
REQUEST_ENDPOINT_ACTIONS,
|
|
||||||
REQUEST_ENDPOINTS,
|
|
||||||
REQUEST_VIDEO_EVENT_ENDPOINT,
|
|
||||||
REQUEST_VIDEO_EVENT_TYPES,
|
|
||||||
REQUEST_VIDEO_QADU_ENDPOINT,
|
|
||||||
REQUEST_VIDEO_QADU_TYPES,
|
|
||||||
REQUESTS_IN_PARALLEL,
|
REQUESTS_IN_PARALLEL,
|
||||||
REQUESTS_INTERVAL,
|
REQUESTS_INTERVAL,
|
||||||
REQUESTS_LIMIT_PER_POD,
|
REQUESTS_LIMIT_PER_POD,
|
||||||
|
|
|
@ -2,7 +2,6 @@ import { join } from 'path'
|
||||||
import { flattenDepth } from 'lodash'
|
import { flattenDepth } from 'lodash'
|
||||||
require('pg').defaults.parseInt8 = true // Avoid BIGINT to be converted to string
|
require('pg').defaults.parseInt8 = true // Avoid BIGINT to be converted to string
|
||||||
import * as Sequelize from 'sequelize'
|
import * as Sequelize from 'sequelize'
|
||||||
import * as Bluebird from 'bluebird'
|
|
||||||
|
|
||||||
import { CONFIG } from './constants'
|
import { CONFIG } from './constants'
|
||||||
// Do not use barrel, we need to load database first
|
// Do not use barrel, we need to load database first
|
||||||
|
@ -19,10 +18,6 @@ import { UserModel } from '../models/account/user-interface'
|
||||||
import { AccountVideoRateModel } from '../models/account/account-video-rate-interface'
|
import { AccountVideoRateModel } from '../models/account/account-video-rate-interface'
|
||||||
import { AccountFollowModel } from '../models/account/account-follow-interface'
|
import { AccountFollowModel } from '../models/account/account-follow-interface'
|
||||||
import { TagModel } from './../models/video/tag-interface'
|
import { TagModel } from './../models/video/tag-interface'
|
||||||
import { RequestModel } from './../models/request/request-interface'
|
|
||||||
import { RequestVideoQaduModel } from './../models/request/request-video-qadu-interface'
|
|
||||||
import { RequestVideoEventModel } from './../models/request/request-video-event-interface'
|
|
||||||
import { RequestToPodModel } from './../models/request/request-to-pod-interface'
|
|
||||||
import { PodModel } from './../models/pod/pod-interface'
|
import { PodModel } from './../models/pod/pod-interface'
|
||||||
import { OAuthTokenModel } from './../models/oauth/oauth-token-interface'
|
import { OAuthTokenModel } from './../models/oauth/oauth-token-interface'
|
||||||
import { OAuthClientModel } from './../models/oauth/oauth-client-interface'
|
import { OAuthClientModel } from './../models/oauth/oauth-client-interface'
|
||||||
|
|
|
@ -1,20 +1,21 @@
|
||||||
import * as passwordGenerator from 'password-generator'
|
import * as passwordGenerator from 'password-generator'
|
||||||
import * as Bluebird from 'bluebird'
|
import { UserRole } from '../../shared'
|
||||||
|
import { logger, mkdirpPromise, rimrafPromise } from '../helpers'
|
||||||
|
import { createPrivateAndPublicKeys } from '../helpers/peertube-crypto'
|
||||||
|
import { createUserAccountAndChannel } from '../lib'
|
||||||
|
import { clientsExist, usersExist } from './checker'
|
||||||
|
import { CACHE, CONFIG, LAST_MIGRATION_VERSION } from './constants'
|
||||||
|
|
||||||
import { database as db } from './database'
|
import { database as db } from './database'
|
||||||
import { CONFIG, LAST_MIGRATION_VERSION, CACHE } from './constants'
|
import { createLocalAccount } from '../lib/user'
|
||||||
import { clientsExist, usersExist } from './checker'
|
|
||||||
import { logger, createCertsIfNotExist, mkdirpPromise, rimrafPromise } from '../helpers'
|
|
||||||
import { createUserAccountAndChannel } from '../lib'
|
|
||||||
import { UserRole } from '../../shared'
|
|
||||||
|
|
||||||
async function installApplication () {
|
async function installApplication () {
|
||||||
await db.sequelize.sync()
|
await db.sequelize.sync()
|
||||||
await removeCacheDirectories()
|
await removeCacheDirectories()
|
||||||
await createDirectoriesIfNotExist()
|
await createDirectoriesIfNotExist()
|
||||||
await createCertsIfNotExist()
|
|
||||||
await createOAuthClientIfNotExist()
|
await createOAuthClientIfNotExist()
|
||||||
await createOAuthAdminIfNotExist()
|
await createOAuthAdminIfNotExist()
|
||||||
|
await createApplicationIfNotExist()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
@ -28,7 +29,7 @@ export {
|
||||||
function removeCacheDirectories () {
|
function removeCacheDirectories () {
|
||||||
const cacheDirectories = CACHE.DIRECTORIES
|
const cacheDirectories = CACHE.DIRECTORIES
|
||||||
|
|
||||||
const tasks: Bluebird<any>[] = []
|
const tasks: Promise<any>[] = []
|
||||||
|
|
||||||
// Cache directories
|
// Cache directories
|
||||||
for (const key of Object.keys(cacheDirectories)) {
|
for (const key of Object.keys(cacheDirectories)) {
|
||||||
|
@ -120,7 +121,12 @@ async function createOAuthAdminIfNotExist () {
|
||||||
await createUserAccountAndChannel(user, validatePassword)
|
await createUserAccountAndChannel(user, validatePassword)
|
||||||
logger.info('Username: ' + username)
|
logger.info('Username: ' + username)
|
||||||
logger.info('User password: ' + password)
|
logger.info('User password: ' + password)
|
||||||
|
}
|
||||||
logger.info('Creating Application table.')
|
|
||||||
await db.Application.create({ migrationVersion: LAST_MIGRATION_VERSION })
|
async function createApplicationIfNotExist () {
|
||||||
|
logger.info('Creating Application table.')
|
||||||
|
const applicationInstance = await db.Application.create({ migrationVersion: LAST_MIGRATION_VERSION })
|
||||||
|
|
||||||
|
logger.info('Creating application account.')
|
||||||
|
return createLocalAccount('peertube', null, applicationInstance.id, undefined)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import * as Sequelize from 'sequelize'
|
import * as Sequelize from 'sequelize'
|
||||||
import * as Promise from 'bluebird'
|
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
|
|
||||||
import { readdirPromise, renamePromise } from '../../helpers/core-utils'
|
import { readdirPromise, renamePromise } from '../../helpers/core-utils'
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
export * from './process-create'
|
export * from './process-create'
|
||||||
export * from './process-flag'
|
export * from './process-flag'
|
||||||
export * from './process-update'
|
export * from './process-update'
|
||||||
|
export * from './send-request'
|
||||||
|
|
|
@ -8,7 +8,11 @@ import { VideoChannelInstance } from '../../models/video/video-channel-interface
|
||||||
import { VideoFileAttributes } from '../../models/video/video-file-interface'
|
import { VideoFileAttributes } from '../../models/video/video-file-interface'
|
||||||
import { VideoAttributes, VideoInstance } from '../../models/video/video-interface'
|
import { VideoAttributes, VideoInstance } from '../../models/video/video-interface'
|
||||||
|
|
||||||
async function videoActivityObjectToDBAttributes (videoChannel: VideoChannelInstance, videoObject: VideoTorrentObject, t: Sequelize.Transaction) {
|
async function videoActivityObjectToDBAttributes (
|
||||||
|
videoChannel: VideoChannelInstance,
|
||||||
|
videoObject: VideoTorrentObject,
|
||||||
|
t: Sequelize.Transaction
|
||||||
|
) {
|
||||||
const videoFromDatabase = await db.Video.loadByUUIDOrURL(videoObject.uuid, videoObject.id, t)
|
const videoFromDatabase = await db.Video.loadByUUIDOrURL(videoObject.uuid, videoObject.id, t)
|
||||||
if (videoFromDatabase) throw new Error('Video with this UUID/Url already exists.')
|
if (videoFromDatabase) throw new Error('Video with this UUID/Url already exists.')
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import {
|
||||||
} from '../../../shared'
|
} from '../../../shared'
|
||||||
|
|
||||||
function processFlagActivity (activity: ActivityCreate) {
|
function processFlagActivity (activity: ActivityCreate) {
|
||||||
// empty
|
return Promise.resolve(undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import * as Sequelize from 'sequelize'
|
import * as Sequelize from 'sequelize'
|
||||||
|
|
||||||
|
import { database as db } from '../../initializers'
|
||||||
import {
|
import {
|
||||||
AccountInstance,
|
AccountInstance,
|
||||||
VideoInstance,
|
VideoInstance,
|
||||||
|
@ -13,54 +14,66 @@ function sendCreateVideoChannel (videoChannel: VideoChannelInstance, t: Sequeliz
|
||||||
const videoChannelObject = videoChannel.toActivityPubObject()
|
const videoChannelObject = videoChannel.toActivityPubObject()
|
||||||
const data = createActivityData(videoChannel.url, videoChannel.Account, videoChannelObject)
|
const data = createActivityData(videoChannel.url, videoChannel.Account, videoChannelObject)
|
||||||
|
|
||||||
return broadcastToFollowers(data, t)
|
return broadcastToFollowers(data, videoChannel.Account, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendUpdateVideoChannel (videoChannel: VideoChannelInstance, t: Sequelize.Transaction) {
|
function sendUpdateVideoChannel (videoChannel: VideoChannelInstance, t: Sequelize.Transaction) {
|
||||||
const videoChannelObject = videoChannel.toActivityPubObject()
|
const videoChannelObject = videoChannel.toActivityPubObject()
|
||||||
const data = updateActivityData(videoChannel.url, videoChannel.Account, videoChannelObject)
|
const data = updateActivityData(videoChannel.url, videoChannel.Account, videoChannelObject)
|
||||||
|
|
||||||
return broadcastToFollowers(data, t)
|
return broadcastToFollowers(data, videoChannel.Account, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendDeleteVideoChannel (videoChannel: VideoChannelInstance, t: Sequelize.Transaction) {
|
function sendDeleteVideoChannel (videoChannel: VideoChannelInstance, t: Sequelize.Transaction) {
|
||||||
const videoChannelObject = videoChannel.toActivityPubObject()
|
const videoChannelObject = videoChannel.toActivityPubObject()
|
||||||
const data = deleteActivityData(videoChannel.url, videoChannel.Account, videoChannelObject)
|
const data = deleteActivityData(videoChannel.url, videoChannel.Account, videoChannelObject)
|
||||||
|
|
||||||
return broadcastToFollowers(data, t)
|
return broadcastToFollowers(data, videoChannel.Account, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendAddVideo (video: VideoInstance, t: Sequelize.Transaction) {
|
function sendAddVideo (video: VideoInstance, t: Sequelize.Transaction) {
|
||||||
const videoObject = video.toActivityPubObject()
|
const videoObject = video.toActivityPubObject()
|
||||||
const data = addActivityData(video.url, video.VideoChannel.Account, video.VideoChannel.url, videoObject)
|
const data = addActivityData(video.url, video.VideoChannel.Account, video.VideoChannel.url, videoObject)
|
||||||
|
|
||||||
return broadcastToFollowers(data, t)
|
return broadcastToFollowers(data, video.VideoChannel.Account, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendUpdateVideo (video: VideoInstance, t: Sequelize.Transaction) {
|
function sendUpdateVideo (video: VideoInstance, t: Sequelize.Transaction) {
|
||||||
const videoObject = video.toActivityPubObject()
|
const videoObject = video.toActivityPubObject()
|
||||||
const data = updateActivityData(video.url, video.VideoChannel.Account, videoObject)
|
const data = updateActivityData(video.url, video.VideoChannel.Account, videoObject)
|
||||||
|
|
||||||
return broadcastToFollowers(data, t)
|
return broadcastToFollowers(data, video.VideoChannel.Account, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendDeleteVideo (video: VideoInstance, t: Sequelize.Transaction) {
|
function sendDeleteVideo (video: VideoInstance, t: Sequelize.Transaction) {
|
||||||
const videoObject = video.toActivityPubObject()
|
const videoObject = video.toActivityPubObject()
|
||||||
const data = deleteActivityData(video.url, video.VideoChannel.Account, videoObject)
|
const data = deleteActivityData(video.url, video.VideoChannel.Account, videoObject)
|
||||||
|
|
||||||
return broadcastToFollowers(data, t)
|
return broadcastToFollowers(data, video.VideoChannel.Account, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
sendCreateVideoChannel,
|
||||||
|
sendUpdateVideoChannel,
|
||||||
|
sendDeleteVideoChannel,
|
||||||
|
sendAddVideo,
|
||||||
|
sendUpdateVideo,
|
||||||
|
sendDeleteVideo
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
function broadcastToFollowers (data: any, t: Sequelize.Transaction) {
|
async function broadcastToFollowers (data: any, fromAccount: AccountInstance, t: Sequelize.Transaction) {
|
||||||
return httpRequestJobScheduler.createJob(t, 'http-request', 'httpRequestBroadcastHandler', data)
|
const result = await db.Account.listFollowerUrlsForApi(fromAccount.name, 0)
|
||||||
|
|
||||||
|
const jobPayload = {
|
||||||
|
uris: result.data,
|
||||||
|
body: data
|
||||||
|
}
|
||||||
|
|
||||||
|
return httpRequestJobScheduler.createJob(t, 'httpRequestBroadcastHandler', jobPayload)
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildSignedActivity (byAccount: AccountInstance, data: Object) {
|
function buildSignedActivity (byAccount: AccountInstance, data: Object) {
|
||||||
|
|
|
@ -3,9 +3,8 @@ import { join } from 'path'
|
||||||
import { createWriteStream } from 'fs'
|
import { createWriteStream } from 'fs'
|
||||||
|
|
||||||
import { database as db, CONFIG, CACHE } from '../../initializers'
|
import { database as db, CONFIG, CACHE } from '../../initializers'
|
||||||
import { logger, unlinkPromise } from '../../helpers'
|
import { logger, unlinkPromise, fetchRemoteVideoPreview } from '../../helpers'
|
||||||
import { VideoInstance } from '../../models'
|
import { VideoInstance } from '../../models'
|
||||||
import { fetchRemotePreview } from '../../lib'
|
|
||||||
|
|
||||||
class VideosPreviewCache {
|
class VideosPreviewCache {
|
||||||
|
|
||||||
|
@ -54,7 +53,7 @@ class VideosPreviewCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
private saveRemotePreviewAndReturnPath (video: VideoInstance) {
|
private saveRemotePreviewAndReturnPath (video: VideoInstance) {
|
||||||
const req = fetchRemotePreview(video)
|
const req = fetchRemoteVideoPreview(video)
|
||||||
|
|
||||||
return new Promise<string>((res, rej) => {
|
return new Promise<string>((res, rej) => {
|
||||||
const path = join(CACHE.DIRECTORIES.PREVIEWS, video.getPreviewName())
|
const path = join(CACHE.DIRECTORIES.PREVIEWS, video.getPreviewName())
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
export * from './activitypub'
|
export * from './activitypub'
|
||||||
export * from './cache'
|
export * from './cache'
|
||||||
export * from './jobs'
|
export * from './jobs'
|
||||||
export * from './request'
|
|
||||||
export * from './friends'
|
|
||||||
export * from './oauth-model'
|
export * from './oauth-model'
|
||||||
export * from './user'
|
export * from './user'
|
||||||
export * from './video-channel'
|
export * from './video-channel'
|
||||||
|
|
|
@ -1,19 +1,28 @@
|
||||||
import * as Bluebird from 'bluebird'
|
|
||||||
|
|
||||||
import { database as db } from '../../../initializers/database'
|
|
||||||
import { logger } from '../../../helpers'
|
import { logger } from '../../../helpers'
|
||||||
|
import { doRequest } from '../../../helpers/requests'
|
||||||
|
import { HTTPRequestPayload } from './http-request-job-scheduler'
|
||||||
|
|
||||||
async function process (data: { videoUUID: string }, jobId: number) {
|
async function process (payload: HTTPRequestPayload, jobId: number) {
|
||||||
|
logger.info('Processing broadcast in job %d.', jobId)
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
uri: '',
|
||||||
|
json: payload.body
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const uri of payload.uris) {
|
||||||
|
options.uri = uri
|
||||||
|
await doRequest(options)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onError (err: Error, jobId: number) {
|
function onError (err: Error, jobId: number) {
|
||||||
logger.error('Error when optimized video file in job %d.', jobId, err)
|
logger.error('Error when broadcasting request in job %d.', jobId, err)
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onSuccess (jobId: number) {
|
async function onSuccess (jobId: number) {
|
||||||
|
logger.info('Job %d is a success.', jobId)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
@ -4,7 +4,11 @@ import * as httpRequestBroadcastHandler from './http-request-broadcast-handler'
|
||||||
import * as httpRequestUnicastHandler from './http-request-unicast-handler'
|
import * as httpRequestUnicastHandler from './http-request-unicast-handler'
|
||||||
import { JobCategory } from '../../../../shared'
|
import { JobCategory } from '../../../../shared'
|
||||||
|
|
||||||
const jobHandlers: { [ handlerName: string ]: JobHandler<any> } = {
|
type HTTPRequestPayload = {
|
||||||
|
uris: string[]
|
||||||
|
body: any
|
||||||
|
}
|
||||||
|
const jobHandlers: { [ handlerName: string ]: JobHandler<HTTPRequestPayload, void> } = {
|
||||||
httpRequestBroadcastHandler,
|
httpRequestBroadcastHandler,
|
||||||
httpRequestUnicastHandler
|
httpRequestUnicastHandler
|
||||||
}
|
}
|
||||||
|
@ -13,5 +17,6 @@ const jobCategory: JobCategory = 'http-request'
|
||||||
const httpRequestJobScheduler = new JobScheduler(jobCategory, jobHandlers)
|
const httpRequestJobScheduler = new JobScheduler(jobCategory, jobHandlers)
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
HTTPRequestPayload,
|
||||||
httpRequestJobScheduler
|
httpRequestJobScheduler
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,26 @@
|
||||||
import * as Bluebird from 'bluebird'
|
|
||||||
|
|
||||||
import { database as db } from '../../../initializers/database'
|
|
||||||
import { logger } from '../../../helpers'
|
import { logger } from '../../../helpers'
|
||||||
|
import { doRequest } from '../../../helpers/requests'
|
||||||
|
import { HTTPRequestPayload } from './http-request-job-scheduler'
|
||||||
|
|
||||||
async function process (data: { videoUUID: string }, jobId: number) {
|
async function process (payload: HTTPRequestPayload, jobId: number) {
|
||||||
|
logger.info('Processing unicast in job %d.', jobId)
|
||||||
|
|
||||||
|
const uri = payload.uris[0]
|
||||||
|
const options = {
|
||||||
|
uri,
|
||||||
|
json: payload.body
|
||||||
|
}
|
||||||
|
|
||||||
|
await doRequest(options)
|
||||||
}
|
}
|
||||||
|
|
||||||
function onError (err: Error, jobId: number) {
|
function onError (err: Error, jobId: number) {
|
||||||
logger.error('Error when optimized video file in job %d.', jobId, err)
|
logger.error('Error when sending request in job %d.', jobId, err)
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onSuccess (jobId: number) {
|
async function onSuccess (jobId: number) {
|
||||||
|
logger.info('Job %d is a success.', jobId)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
@ -1,28 +1,22 @@
|
||||||
import { AsyncQueue, forever, queue } from 'async'
|
import { AsyncQueue, forever, queue } from 'async'
|
||||||
import * as Sequelize from 'sequelize'
|
import * as Sequelize from 'sequelize'
|
||||||
|
|
||||||
import {
|
|
||||||
database as db,
|
|
||||||
JOBS_FETCHING_INTERVAL,
|
|
||||||
JOBS_FETCH_LIMIT_PER_CYCLE,
|
|
||||||
JOB_STATES
|
|
||||||
} from '../../initializers'
|
|
||||||
import { logger } from '../../helpers'
|
|
||||||
import { JobInstance } from '../../models'
|
|
||||||
import { JobCategory } from '../../../shared'
|
import { JobCategory } from '../../../shared'
|
||||||
|
import { logger } from '../../helpers'
|
||||||
|
import { database as db, JOB_STATES, JOBS_FETCH_LIMIT_PER_CYCLE, JOBS_FETCHING_INTERVAL } from '../../initializers'
|
||||||
|
import { JobInstance } from '../../models'
|
||||||
|
|
||||||
export interface JobHandler<T> {
|
export interface JobHandler<P, T> {
|
||||||
process (data: object, jobId: number): T
|
process (data: object, jobId: number): Promise<T>
|
||||||
onError (err: Error, jobId: number)
|
onError (err: Error, jobId: number)
|
||||||
onSuccess (jobId: number, jobResult: T)
|
onSuccess (jobId: number, jobResult: T, jobScheduler: JobScheduler<P, T>)
|
||||||
}
|
}
|
||||||
type JobQueueCallback = (err: Error) => void
|
type JobQueueCallback = (err: Error) => void
|
||||||
|
|
||||||
class JobScheduler<T> {
|
class JobScheduler<P, T> {
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
private jobCategory: JobCategory,
|
private jobCategory: JobCategory,
|
||||||
private jobHandlers: { [ id: string ]: JobHandler<T> }
|
private jobHandlers: { [ id: string ]: JobHandler<P, T> }
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async activate () {
|
async activate () {
|
||||||
|
@ -66,13 +60,14 @@ class JobScheduler<T> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
createJob (transaction: Sequelize.Transaction, category: JobCategory, handlerName: string, handlerInputData: object) {
|
createJob (transaction: Sequelize.Transaction, handlerName: string, handlerInputData: P) {
|
||||||
const createQuery = {
|
const createQuery = {
|
||||||
state: JOB_STATES.PENDING,
|
state: JOB_STATES.PENDING,
|
||||||
category,
|
category: this.jobCategory,
|
||||||
handlerName,
|
handlerName,
|
||||||
handlerInputData
|
handlerInputData
|
||||||
}
|
}
|
||||||
|
|
||||||
const options = { transaction }
|
const options = { transaction }
|
||||||
|
|
||||||
return db.Job.create(createQuery, options)
|
return db.Job.create(createQuery, options)
|
||||||
|
@ -95,7 +90,7 @@ class JobScheduler<T> {
|
||||||
await job.save()
|
await job.save()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await jobHandler.process(job.handlerInputData, job.id)
|
const result: T = await jobHandler.process(job.handlerInputData, job.id)
|
||||||
await this.onJobSuccess(jobHandler, job, result)
|
await this.onJobSuccess(jobHandler, job, result)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error('Error in job handler %s.', job.handlerName, err)
|
logger.error('Error in job handler %s.', job.handlerName, err)
|
||||||
|
@ -111,7 +106,7 @@ class JobScheduler<T> {
|
||||||
callback(null)
|
callback(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
private async onJobError (jobHandler: JobHandler<any>, job: JobInstance, err: Error) {
|
private async onJobError (jobHandler: JobHandler<P, T>, job: JobInstance, err: Error) {
|
||||||
job.state = JOB_STATES.ERROR
|
job.state = JOB_STATES.ERROR
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -122,12 +117,12 @@ class JobScheduler<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async onJobSuccess (jobHandler: JobHandler<any>, job: JobInstance, jobResult: any) {
|
private async onJobSuccess (jobHandler: JobHandler<P, T>, job: JobInstance, jobResult: T) {
|
||||||
job.state = JOB_STATES.SUCCESS
|
job.state = JOB_STATES.SUCCESS
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await job.save()
|
await job.save()
|
||||||
jobHandler.onSuccess(job.id, jobResult)
|
jobHandler.onSuccess(job.id, jobResult, this)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.cannotSaveJobError(err)
|
this.cannotSaveJobError(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
import { JobScheduler, JobHandler } from '../job-scheduler'
|
import { JobCategory } from '../../../../shared'
|
||||||
|
import { JobHandler, JobScheduler } from '../job-scheduler'
|
||||||
import * as videoFileOptimizer from './video-file-optimizer-handler'
|
import * as videoFileOptimizer from './video-file-optimizer-handler'
|
||||||
import * as videoFileTranscoder from './video-file-transcoder-handler'
|
import * as videoFileTranscoder from './video-file-transcoder-handler'
|
||||||
import { JobCategory } from '../../../../shared'
|
import { VideoInstance } from '../../../models/video/video-interface'
|
||||||
|
|
||||||
const jobHandlers: { [ handlerName: string ]: JobHandler<any> } = {
|
type TranscodingJobPayload = {
|
||||||
|
videoUUID: string
|
||||||
|
resolution?: number
|
||||||
|
}
|
||||||
|
const jobHandlers: { [ handlerName: string ]: JobHandler<TranscodingJobPayload, VideoInstance> } = {
|
||||||
videoFileOptimizer,
|
videoFileOptimizer,
|
||||||
videoFileTranscoder
|
videoFileTranscoder
|
||||||
}
|
}
|
||||||
|
@ -13,5 +17,6 @@ const jobCategory: JobCategory = 'transcoding'
|
||||||
const transcodingJobScheduler = new JobScheduler(jobCategory, jobHandlers)
|
const transcodingJobScheduler = new JobScheduler(jobCategory, jobHandlers)
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
TranscodingJobPayload,
|
||||||
transcodingJobScheduler
|
transcodingJobScheduler
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
import * as Bluebird from 'bluebird'
|
import * as Bluebird from 'bluebird'
|
||||||
|
import { computeResolutionsToTranscode, logger } from '../../../helpers'
|
||||||
|
|
||||||
import { database as db } from '../../../initializers/database'
|
import { database as db } from '../../../initializers/database'
|
||||||
import { logger, computeResolutionsToTranscode } from '../../../helpers'
|
|
||||||
import { VideoInstance } from '../../../models'
|
import { VideoInstance } from '../../../models'
|
||||||
import { addVideoToFriends } from '../../friends'
|
import { sendAddVideo } from '../../activitypub/send-request'
|
||||||
import { JobScheduler } from '../job-scheduler'
|
import { JobScheduler } from '../job-scheduler'
|
||||||
|
import { TranscodingJobPayload } from './transcoding-job-scheduler'
|
||||||
|
|
||||||
async function process (data: { videoUUID: string }, jobId: number) {
|
async function process (data: TranscodingJobPayload, jobId: number) {
|
||||||
const video = await db.Video.loadByUUIDAndPopulateAccountAndPodAndTags(data.videoUUID)
|
const video = await db.Video.loadByUUIDAndPopulateAccountAndPodAndTags(data.videoUUID)
|
||||||
// No video, maybe deleted?
|
// No video, maybe deleted?
|
||||||
if (!video) {
|
if (!video) {
|
||||||
|
@ -24,7 +25,7 @@ function onError (err: Error, jobId: number) {
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onSuccess (jobId: number, video: VideoInstance) {
|
async function onSuccess (jobId: number, video: VideoInstance, jobScheduler: JobScheduler<TranscodingJobPayload, VideoInstance>) {
|
||||||
if (video === undefined) return undefined
|
if (video === undefined) return undefined
|
||||||
|
|
||||||
logger.info('Job %d is a success.', jobId)
|
logger.info('Job %d is a success.', jobId)
|
||||||
|
@ -34,10 +35,8 @@ async function onSuccess (jobId: number, video: VideoInstance) {
|
||||||
// Video does not exist anymore
|
// Video does not exist anymore
|
||||||
if (!videoDatabase) return undefined
|
if (!videoDatabase) return undefined
|
||||||
|
|
||||||
const remoteVideo = await videoDatabase.toAddRemoteJSON()
|
// Now we'll add the video's meta data to our followers
|
||||||
|
await sendAddVideo(video, undefined)
|
||||||
// Now we'll add the video's meta data to our friends
|
|
||||||
await addVideoToFriends(remoteVideo, null)
|
|
||||||
|
|
||||||
const originalFileHeight = await videoDatabase.getOriginalFileHeight()
|
const originalFileHeight = await videoDatabase.getOriginalFileHeight()
|
||||||
// Create transcoding jobs if there are enabled resolutions
|
// Create transcoding jobs if there are enabled resolutions
|
||||||
|
@ -59,7 +58,7 @@ async function onSuccess (jobId: number, video: VideoInstance) {
|
||||||
resolution
|
resolution
|
||||||
}
|
}
|
||||||
|
|
||||||
const p = JobScheduler.Instance.createJob(t, 'videoFileTranscoder', dataInput)
|
const p = jobScheduler.createJob(t, 'videoFileTranscoder', dataInput)
|
||||||
tasks.push(p)
|
tasks.push(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { database as db } from '../../../initializers/database'
|
|
||||||
import { updateVideoToFriends } from '../../friends'
|
|
||||||
import { logger } from '../../../helpers'
|
|
||||||
import { VideoInstance } from '../../../models'
|
|
||||||
import { VideoResolution } from '../../../../shared'
|
import { VideoResolution } from '../../../../shared'
|
||||||
|
import { logger } from '../../../helpers'
|
||||||
|
import { database as db } from '../../../initializers/database'
|
||||||
|
import { VideoInstance } from '../../../models'
|
||||||
|
import { sendUpdateVideo } from '../../activitypub/send-request'
|
||||||
|
|
||||||
async function process (data: { videoUUID: string, resolution: VideoResolution }, jobId: number) {
|
async function process (data: { videoUUID: string, resolution: VideoResolution }, jobId: number) {
|
||||||
const video = await db.Video.loadByUUIDAndPopulateAccountAndPodAndTags(data.videoUUID)
|
const video = await db.Video.loadByUUIDAndPopulateAccountAndPodAndTags(data.videoUUID)
|
||||||
|
@ -32,10 +32,7 @@ async function onSuccess (jobId: number, video: VideoInstance) {
|
||||||
// Video does not exist anymore
|
// Video does not exist anymore
|
||||||
if (!videoDatabase) return undefined
|
if (!videoDatabase) return undefined
|
||||||
|
|
||||||
const remoteVideo = videoDatabase.toUpdateRemoteJSON()
|
await sendUpdateVideo(video, undefined)
|
||||||
|
|
||||||
// Now we'll add the video's meta data to our friends
|
|
||||||
await updateVideoToFriends(remoteVideo, null)
|
|
||||||
|
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,168 +0,0 @@
|
||||||
import { isEmpty } from 'lodash'
|
|
||||||
import * as Bluebird from 'bluebird'
|
|
||||||
|
|
||||||
import { database as db } from '../../initializers/database'
|
|
||||||
import { logger, makeSecureRequest } from '../../helpers'
|
|
||||||
import { AbstractRequestClass, AbstractRequestToPodClass, PodInstance } from '../../models'
|
|
||||||
import {
|
|
||||||
API_VERSION,
|
|
||||||
REQUESTS_IN_PARALLEL,
|
|
||||||
REQUESTS_INTERVAL
|
|
||||||
} from '../../initializers'
|
|
||||||
|
|
||||||
interface RequestsObjects<U> {
|
|
||||||
[ id: string ]: {
|
|
||||||
toPod: PodInstance
|
|
||||||
endpoint: string
|
|
||||||
ids: number[] // ids
|
|
||||||
datas: U[]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class AbstractRequestScheduler <T> {
|
|
||||||
requestInterval: number
|
|
||||||
limitPods: number
|
|
||||||
limitPerPod: number
|
|
||||||
|
|
||||||
protected lastRequestTimestamp: number
|
|
||||||
protected timer: NodeJS.Timer
|
|
||||||
protected description: string
|
|
||||||
|
|
||||||
constructor () {
|
|
||||||
this.lastRequestTimestamp = 0
|
|
||||||
this.timer = null
|
|
||||||
this.requestInterval = REQUESTS_INTERVAL
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract getRequestModel (): AbstractRequestClass<T>
|
|
||||||
abstract getRequestToPodModel (): AbstractRequestToPodClass
|
|
||||||
abstract buildRequestsObjects (requestsGrouped: T): RequestsObjects<any>
|
|
||||||
|
|
||||||
activate () {
|
|
||||||
logger.info('Requests scheduler activated.')
|
|
||||||
this.lastRequestTimestamp = Date.now()
|
|
||||||
|
|
||||||
this.timer = setInterval(() => {
|
|
||||||
this.lastRequestTimestamp = Date.now()
|
|
||||||
this.makeRequests()
|
|
||||||
}, this.requestInterval)
|
|
||||||
}
|
|
||||||
|
|
||||||
deactivate () {
|
|
||||||
logger.info('Requests scheduler deactivated.')
|
|
||||||
clearInterval(this.timer)
|
|
||||||
this.timer = null
|
|
||||||
}
|
|
||||||
|
|
||||||
forceSend () {
|
|
||||||
logger.info('Force requests scheduler sending.')
|
|
||||||
this.makeRequests()
|
|
||||||
}
|
|
||||||
|
|
||||||
remainingMilliSeconds () {
|
|
||||||
if (this.timer === null) return -1
|
|
||||||
|
|
||||||
return REQUESTS_INTERVAL - (Date.now() - this.lastRequestTimestamp)
|
|
||||||
}
|
|
||||||
|
|
||||||
remainingRequestsCount () {
|
|
||||||
return this.getRequestModel().countTotalRequests()
|
|
||||||
}
|
|
||||||
|
|
||||||
flush () {
|
|
||||||
return this.getRequestModel().removeAll()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// Make a requests to friends of a certain type
|
|
||||||
protected async makeRequest (toPod: PodInstance, requestEndpoint: string, requestsToMake: any) {
|
|
||||||
const params = {
|
|
||||||
toPod: toPod,
|
|
||||||
method: 'POST' as 'POST',
|
|
||||||
path: '/api/' + API_VERSION + '/remote/' + requestEndpoint,
|
|
||||||
data: requestsToMake // Requests we need to make
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make multiple retry requests to all of pods
|
|
||||||
// The function fire some useful callbacks
|
|
||||||
try {
|
|
||||||
const { response } = await makeSecureRequest(params)
|
|
||||||
|
|
||||||
// 400 because if the other pod is not up to date, it may not understand our request
|
|
||||||
if ([ 200, 201, 204, 400 ].indexOf(response.statusCode) === -1) {
|
|
||||||
throw new Error('Status code not 20x or 400 : ' + response.statusCode)
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
logger.error('Error sending secure request to %s pod.', toPod.host, err)
|
|
||||||
|
|
||||||
throw err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make all the requests of the scheduler
|
|
||||||
protected async makeRequests () {
|
|
||||||
let requestsGrouped: T
|
|
||||||
|
|
||||||
try {
|
|
||||||
requestsGrouped = await this.getRequestModel().listWithLimitAndRandom(this.limitPods, this.limitPerPod)
|
|
||||||
} catch (err) {
|
|
||||||
logger.error('Cannot get the list of "%s".', this.description, { error: err.stack })
|
|
||||||
throw err
|
|
||||||
}
|
|
||||||
|
|
||||||
// We want to group requests by destinations pod and endpoint
|
|
||||||
const requestsToMake = this.buildRequestsObjects(requestsGrouped)
|
|
||||||
|
|
||||||
// If there are no requests, abort
|
|
||||||
if (isEmpty(requestsToMake) === true) {
|
|
||||||
logger.info('No "%s" to make.', this.description)
|
|
||||||
return { goodPods: [], badPods: [] }
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info('Making "%s" to friends.', this.description)
|
|
||||||
|
|
||||||
const goodPods: number[] = []
|
|
||||||
const badPods: number[] = []
|
|
||||||
|
|
||||||
await Bluebird.map(Object.keys(requestsToMake), async hashKey => {
|
|
||||||
const requestToMake = requestsToMake[hashKey]
|
|
||||||
const toPod: PodInstance = requestToMake.toPod
|
|
||||||
|
|
||||||
try {
|
|
||||||
await this.makeRequest(toPod, requestToMake.endpoint, requestToMake.datas)
|
|
||||||
logger.debug('Removing requests for pod %s.', requestToMake.toPod.id, { requestsIds: requestToMake.ids })
|
|
||||||
goodPods.push(requestToMake.toPod.id)
|
|
||||||
|
|
||||||
this.afterRequestHook()
|
|
||||||
|
|
||||||
// Remove the pod id of these request ids
|
|
||||||
await this.getRequestToPodModel()
|
|
||||||
.removeByRequestIdsAndPod(requestToMake.ids, requestToMake.toPod.id)
|
|
||||||
} catch (err) {
|
|
||||||
badPods.push(requestToMake.toPod.id)
|
|
||||||
logger.info('Cannot make request to %s.', toPod.host, err)
|
|
||||||
}
|
|
||||||
}, { concurrency: REQUESTS_IN_PARALLEL })
|
|
||||||
|
|
||||||
this.afterRequestsHook()
|
|
||||||
|
|
||||||
// All the requests were made, we update the pods score
|
|
||||||
db.Pod.updatePodsScore(goodPods, badPods)
|
|
||||||
}
|
|
||||||
|
|
||||||
protected afterRequestHook () {
|
|
||||||
// Nothing to do, let children re-implement it
|
|
||||||
}
|
|
||||||
|
|
||||||
protected afterRequestsHook () {
|
|
||||||
// Nothing to do, let children re-implement it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
export {
|
|
||||||
AbstractRequestScheduler,
|
|
||||||
RequestsObjects
|
|
||||||
}
|
|
|
@ -1,4 +0,0 @@
|
||||||
export * from './abstract-request-scheduler'
|
|
||||||
export * from './request-scheduler'
|
|
||||||
export * from './request-video-event-scheduler'
|
|
||||||
export * from './request-video-qadu-scheduler'
|
|
|
@ -1,96 +0,0 @@
|
||||||
import * as Sequelize from 'sequelize'
|
|
||||||
|
|
||||||
import { database as db } from '../../initializers/database'
|
|
||||||
import { AbstractRequestScheduler, RequestsObjects } from './abstract-request-scheduler'
|
|
||||||
import { logger } from '../../helpers'
|
|
||||||
import { REQUESTS_LIMIT_PODS, REQUESTS_LIMIT_PER_POD } from '../../initializers'
|
|
||||||
import { RequestsGrouped } from '../../models'
|
|
||||||
import { RequestEndpoint, RemoteVideoRequest } from '../../../shared'
|
|
||||||
|
|
||||||
export type RequestSchedulerOptions = {
|
|
||||||
type: string
|
|
||||||
endpoint: RequestEndpoint
|
|
||||||
data: Object
|
|
||||||
toIds: number[]
|
|
||||||
transaction: Sequelize.Transaction
|
|
||||||
}
|
|
||||||
|
|
||||||
class RequestScheduler extends AbstractRequestScheduler<RequestsGrouped> {
|
|
||||||
constructor () {
|
|
||||||
super()
|
|
||||||
|
|
||||||
// We limit the size of the requests
|
|
||||||
this.limitPods = REQUESTS_LIMIT_PODS
|
|
||||||
this.limitPerPod = REQUESTS_LIMIT_PER_POD
|
|
||||||
|
|
||||||
this.description = 'requests'
|
|
||||||
}
|
|
||||||
|
|
||||||
getRequestModel () {
|
|
||||||
return db.Request
|
|
||||||
}
|
|
||||||
|
|
||||||
getRequestToPodModel () {
|
|
||||||
return db.RequestToPod
|
|
||||||
}
|
|
||||||
|
|
||||||
buildRequestsObjects (requestsGrouped: RequestsGrouped) {
|
|
||||||
const requestsToMakeGrouped: RequestsObjects<RemoteVideoRequest> = {}
|
|
||||||
|
|
||||||
for (const toPodId of Object.keys(requestsGrouped)) {
|
|
||||||
for (const data of requestsGrouped[toPodId]) {
|
|
||||||
const request = data.request
|
|
||||||
const pod = data.pod
|
|
||||||
const hashKey = toPodId + request.endpoint
|
|
||||||
|
|
||||||
if (!requestsToMakeGrouped[hashKey]) {
|
|
||||||
requestsToMakeGrouped[hashKey] = {
|
|
||||||
toPod: pod,
|
|
||||||
endpoint: request.endpoint,
|
|
||||||
ids: [], // request ids, to delete them from the DB in the future
|
|
||||||
datas: [] // requests data,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
requestsToMakeGrouped[hashKey].ids.push(request.id)
|
|
||||||
requestsToMakeGrouped[hashKey].datas.push(request.request)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return requestsToMakeGrouped
|
|
||||||
}
|
|
||||||
|
|
||||||
async createRequest ({ type, endpoint, data, toIds, transaction }: RequestSchedulerOptions) {
|
|
||||||
// If there are no destination pods abort
|
|
||||||
if (toIds.length === 0) return undefined
|
|
||||||
|
|
||||||
const createQuery = {
|
|
||||||
endpoint,
|
|
||||||
request: {
|
|
||||||
type: type,
|
|
||||||
data: data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const dbRequestOptions: Sequelize.CreateOptions = {
|
|
||||||
transaction
|
|
||||||
}
|
|
||||||
|
|
||||||
const request = await db.Request.create(createQuery, dbRequestOptions)
|
|
||||||
await request.setPods(toIds, dbRequestOptions)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
afterRequestsHook () {
|
|
||||||
// Flush requests with no pod
|
|
||||||
this.getRequestModel().removeWithEmptyTo()
|
|
||||||
.catch(err => logger.error('Error when removing requests with no pods.', err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
export {
|
|
||||||
RequestScheduler
|
|
||||||
}
|
|
|
@ -1,129 +0,0 @@
|
||||||
import * as Sequelize from 'sequelize'
|
|
||||||
|
|
||||||
import { database as db } from '../../initializers/database'
|
|
||||||
import { AbstractRequestScheduler, RequestsObjects } from './abstract-request-scheduler'
|
|
||||||
import {
|
|
||||||
REQUESTS_VIDEO_EVENT_LIMIT_PODS,
|
|
||||||
REQUESTS_VIDEO_EVENT_LIMIT_PER_POD,
|
|
||||||
REQUEST_VIDEO_EVENT_ENDPOINT
|
|
||||||
} from '../../initializers'
|
|
||||||
import { RequestsVideoEventGrouped } from '../../models'
|
|
||||||
import { RequestVideoEventType, RemoteVideoEventRequest, RemoteVideoEventType } from '../../../shared'
|
|
||||||
|
|
||||||
export type RequestVideoEventSchedulerOptions = {
|
|
||||||
type: RequestVideoEventType
|
|
||||||
videoId: number
|
|
||||||
count?: number
|
|
||||||
transaction?: Sequelize.Transaction
|
|
||||||
}
|
|
||||||
|
|
||||||
class RequestVideoEventScheduler extends AbstractRequestScheduler<RequestsVideoEventGrouped> {
|
|
||||||
constructor () {
|
|
||||||
super()
|
|
||||||
|
|
||||||
// We limit the size of the requests
|
|
||||||
this.limitPods = REQUESTS_VIDEO_EVENT_LIMIT_PODS
|
|
||||||
this.limitPerPod = REQUESTS_VIDEO_EVENT_LIMIT_PER_POD
|
|
||||||
|
|
||||||
this.description = 'video event requests'
|
|
||||||
}
|
|
||||||
|
|
||||||
getRequestModel () {
|
|
||||||
return db.RequestVideoEvent
|
|
||||||
}
|
|
||||||
|
|
||||||
getRequestToPodModel () {
|
|
||||||
return db.RequestVideoEvent
|
|
||||||
}
|
|
||||||
|
|
||||||
buildRequestsObjects (eventRequests: RequestsVideoEventGrouped) {
|
|
||||||
const requestsToMakeGrouped: RequestsObjects<RemoteVideoEventRequest> = {}
|
|
||||||
|
|
||||||
/* Example:
|
|
||||||
{
|
|
||||||
pod1: {
|
|
||||||
video1: { views: 4, likes: 5 },
|
|
||||||
video2: { likes: 5 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
const eventsPerVideoPerPod: {
|
|
||||||
[ podId: string ]: {
|
|
||||||
[ videoUUID: string ]: {
|
|
||||||
views?: number
|
|
||||||
likes?: number
|
|
||||||
dislikes?: number
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} = {}
|
|
||||||
|
|
||||||
// We group video events per video and per pod
|
|
||||||
// We add the counts of the same event types
|
|
||||||
for (const toPodId of Object.keys(eventRequests)) {
|
|
||||||
for (const eventToProcess of eventRequests[toPodId]) {
|
|
||||||
if (!eventsPerVideoPerPod[toPodId]) eventsPerVideoPerPod[toPodId] = {}
|
|
||||||
|
|
||||||
if (!requestsToMakeGrouped[toPodId]) {
|
|
||||||
requestsToMakeGrouped[toPodId] = {
|
|
||||||
toPod: eventToProcess.pod,
|
|
||||||
endpoint: REQUEST_VIDEO_EVENT_ENDPOINT,
|
|
||||||
ids: [], // request ids, to delete them from the DB in the future
|
|
||||||
datas: [] // requests data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
requestsToMakeGrouped[toPodId].ids.push(eventToProcess.id)
|
|
||||||
|
|
||||||
const eventsPerVideo = eventsPerVideoPerPod[toPodId]
|
|
||||||
const uuid = eventToProcess.video.uuid
|
|
||||||
if (!eventsPerVideo[uuid]) eventsPerVideo[uuid] = {}
|
|
||||||
|
|
||||||
const events = eventsPerVideo[uuid]
|
|
||||||
if (!events[eventToProcess.type]) events[eventToProcess.type] = 0
|
|
||||||
|
|
||||||
events[eventToProcess.type] += eventToProcess.count
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now we build our requests array per pod
|
|
||||||
for (const toPodId of Object.keys(eventsPerVideoPerPod)) {
|
|
||||||
const eventsForPod = eventsPerVideoPerPod[toPodId]
|
|
||||||
|
|
||||||
for (const uuid of Object.keys(eventsForPod)) {
|
|
||||||
const eventsForVideo = eventsForPod[uuid]
|
|
||||||
|
|
||||||
for (const eventType of Object.keys(eventsForVideo)) {
|
|
||||||
requestsToMakeGrouped[toPodId].datas.push({
|
|
||||||
data: {
|
|
||||||
uuid,
|
|
||||||
eventType: eventType as RemoteVideoEventType,
|
|
||||||
count: +eventsForVideo[eventType]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return requestsToMakeGrouped
|
|
||||||
}
|
|
||||||
|
|
||||||
createRequest ({ type, videoId, count, transaction }: RequestVideoEventSchedulerOptions) {
|
|
||||||
if (count === undefined) count = 1
|
|
||||||
|
|
||||||
const dbRequestOptions: Sequelize.CreateOptions = {}
|
|
||||||
if (transaction) dbRequestOptions.transaction = transaction
|
|
||||||
|
|
||||||
const createQuery = {
|
|
||||||
type,
|
|
||||||
count,
|
|
||||||
videoId
|
|
||||||
}
|
|
||||||
|
|
||||||
return db.RequestVideoEvent.create(createQuery, dbRequestOptions)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
export {
|
|
||||||
RequestVideoEventScheduler
|
|
||||||
}
|
|
|
@ -1,148 +0,0 @@
|
||||||
import * as Sequelize from 'sequelize'
|
|
||||||
|
|
||||||
import { database as db } from '../../initializers/database'
|
|
||||||
import { AbstractRequestScheduler, RequestsObjects } from './abstract-request-scheduler'
|
|
||||||
import { logger } from '../../helpers'
|
|
||||||
import {
|
|
||||||
REQUESTS_VIDEO_QADU_LIMIT_PODS,
|
|
||||||
REQUESTS_VIDEO_QADU_LIMIT_PER_POD,
|
|
||||||
REQUEST_VIDEO_QADU_ENDPOINT,
|
|
||||||
REQUEST_VIDEO_QADU_TYPES
|
|
||||||
} from '../../initializers'
|
|
||||||
import { RequestsVideoQaduGrouped, PodInstance } from '../../models'
|
|
||||||
import { RemoteQaduVideoRequest, RequestVideoQaduType } from '../../../shared'
|
|
||||||
|
|
||||||
// We create a custom interface because we need "videos" attribute for our computations
|
|
||||||
interface RequestsObjectsCustom<U> extends RequestsObjects<U> {
|
|
||||||
[ id: string ]: {
|
|
||||||
toPod: PodInstance
|
|
||||||
endpoint: string
|
|
||||||
ids: number[] // ids
|
|
||||||
datas: U[]
|
|
||||||
|
|
||||||
videos: {
|
|
||||||
[ uuid: string ]: {
|
|
||||||
uuid: string
|
|
||||||
likes?: number
|
|
||||||
dislikes?: number
|
|
||||||
views?: number
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export type RequestVideoQaduSchedulerOptions = {
|
|
||||||
type: RequestVideoQaduType
|
|
||||||
videoId: number
|
|
||||||
transaction?: Sequelize.Transaction
|
|
||||||
}
|
|
||||||
|
|
||||||
class RequestVideoQaduScheduler extends AbstractRequestScheduler<RequestsVideoQaduGrouped> {
|
|
||||||
constructor () {
|
|
||||||
super()
|
|
||||||
|
|
||||||
// We limit the size of the requests
|
|
||||||
this.limitPods = REQUESTS_VIDEO_QADU_LIMIT_PODS
|
|
||||||
this.limitPerPod = REQUESTS_VIDEO_QADU_LIMIT_PER_POD
|
|
||||||
|
|
||||||
this.description = 'video QADU requests'
|
|
||||||
}
|
|
||||||
|
|
||||||
getRequestModel () {
|
|
||||||
return db.RequestVideoQadu
|
|
||||||
}
|
|
||||||
|
|
||||||
getRequestToPodModel () {
|
|
||||||
return db.RequestVideoQadu
|
|
||||||
}
|
|
||||||
|
|
||||||
buildRequestsObjects (requests: RequestsVideoQaduGrouped) {
|
|
||||||
const requestsToMakeGrouped: RequestsObjectsCustom<RemoteQaduVideoRequest> = {}
|
|
||||||
|
|
||||||
for (const toPodId of Object.keys(requests)) {
|
|
||||||
for (const data of requests[toPodId]) {
|
|
||||||
const request = data.request
|
|
||||||
const video = data.video
|
|
||||||
const pod = data.pod
|
|
||||||
const hashKey = toPodId
|
|
||||||
|
|
||||||
if (!requestsToMakeGrouped[hashKey]) {
|
|
||||||
requestsToMakeGrouped[hashKey] = {
|
|
||||||
toPod: pod,
|
|
||||||
endpoint: REQUEST_VIDEO_QADU_ENDPOINT,
|
|
||||||
ids: [], // request ids, to delete them from the DB in the future
|
|
||||||
datas: [], // requests data
|
|
||||||
videos: {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Maybe another attribute was filled for this video
|
|
||||||
let videoData = requestsToMakeGrouped[hashKey].videos[video.id]
|
|
||||||
if (!videoData) videoData = { uuid: null }
|
|
||||||
|
|
||||||
switch (request.type) {
|
|
||||||
case REQUEST_VIDEO_QADU_TYPES.LIKES:
|
|
||||||
videoData.likes = video.likes
|
|
||||||
break
|
|
||||||
|
|
||||||
case REQUEST_VIDEO_QADU_TYPES.DISLIKES:
|
|
||||||
videoData.dislikes = video.dislikes
|
|
||||||
break
|
|
||||||
|
|
||||||
case REQUEST_VIDEO_QADU_TYPES.VIEWS:
|
|
||||||
videoData.views = video.views
|
|
||||||
break
|
|
||||||
|
|
||||||
default:
|
|
||||||
logger.error('Unknown request video QADU type %s.', request.type)
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do not forget the uuid so the remote pod can identify the video
|
|
||||||
videoData.uuid = video.uuid
|
|
||||||
requestsToMakeGrouped[hashKey].ids.push(request.id)
|
|
||||||
|
|
||||||
// Maybe there are multiple quick and dirty update for the same video
|
|
||||||
// We use this hash map to dedupe them
|
|
||||||
requestsToMakeGrouped[hashKey].videos[video.id] = videoData
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now we deduped similar quick and dirty updates, we can build our requests data
|
|
||||||
for (const hashKey of Object.keys(requestsToMakeGrouped)) {
|
|
||||||
for (const videoUUID of Object.keys(requestsToMakeGrouped[hashKey].videos)) {
|
|
||||||
const videoData = requestsToMakeGrouped[hashKey].videos[videoUUID]
|
|
||||||
|
|
||||||
requestsToMakeGrouped[hashKey].datas.push({
|
|
||||||
data: videoData
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// We don't need it anymore, it was just to build our data array
|
|
||||||
delete requestsToMakeGrouped[hashKey].videos
|
|
||||||
}
|
|
||||||
|
|
||||||
return requestsToMakeGrouped
|
|
||||||
}
|
|
||||||
|
|
||||||
async createRequest ({ type, videoId, transaction }: RequestVideoQaduSchedulerOptions) {
|
|
||||||
const dbRequestOptions: Sequelize.BulkCreateOptions = {}
|
|
||||||
if (transaction) dbRequestOptions.transaction = transaction
|
|
||||||
|
|
||||||
// Send the update to all our friends
|
|
||||||
const podIds = await db.Pod.listAllIds(transaction)
|
|
||||||
const queries = []
|
|
||||||
for (const podId of podIds) {
|
|
||||||
queries.push({ type, videoId, podId })
|
|
||||||
}
|
|
||||||
|
|
||||||
await db.RequestVideoQadu.bulkCreate(queries, dbRequestOptions)
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
export {
|
|
||||||
RequestVideoQaduScheduler
|
|
||||||
}
|
|
|
@ -1,6 +1,9 @@
|
||||||
|
import * as Sequelize from 'sequelize'
|
||||||
|
import { getActivityPubUrl } from '../helpers/activitypub'
|
||||||
|
import { createPrivateAndPublicKeys } from '../helpers/peertube-crypto'
|
||||||
import { database as db } from '../initializers'
|
import { database as db } from '../initializers'
|
||||||
|
import { CONFIG } from '../initializers/constants'
|
||||||
import { UserInstance } from '../models'
|
import { UserInstance } from '../models'
|
||||||
import { addVideoAccountToFriends } from './friends'
|
|
||||||
import { createVideoChannel } from './video-channel'
|
import { createVideoChannel } from './video-channel'
|
||||||
|
|
||||||
async function createUserAccountAndChannel (user: UserInstance, validateUser = true) {
|
async function createUserAccountAndChannel (user: UserInstance, validateUser = true) {
|
||||||
|
@ -11,32 +14,46 @@ async function createUserAccountAndChannel (user: UserInstance, validateUser = t
|
||||||
}
|
}
|
||||||
|
|
||||||
const userCreated = await user.save(userOptions)
|
const userCreated = await user.save(userOptions)
|
||||||
const accountInstance = db.Account.build({
|
const accountCreated = await createLocalAccount(user.username, user.id, null, t)
|
||||||
name: userCreated.username,
|
|
||||||
podId: null, // It is our pod
|
|
||||||
userId: userCreated.id
|
|
||||||
})
|
|
||||||
|
|
||||||
const accountCreated = await accountInstance.save({ transaction: t })
|
|
||||||
|
|
||||||
const remoteVideoAccount = accountCreated.toAddRemoteJSON()
|
|
||||||
|
|
||||||
// Now we'll add the video channel's meta data to our friends
|
|
||||||
const account = await addVideoAccountToFriends(remoteVideoAccount, t)
|
|
||||||
|
|
||||||
const videoChannelInfo = {
|
const videoChannelInfo = {
|
||||||
name: `Default ${userCreated.username} channel`
|
name: `Default ${userCreated.username} channel`
|
||||||
}
|
}
|
||||||
const videoChannel = await createVideoChannel(videoChannelInfo, accountCreated, t)
|
const videoChannel = await createVideoChannel(videoChannelInfo, accountCreated, t)
|
||||||
|
|
||||||
return { account, videoChannel }
|
return { account: accountCreated, videoChannel }
|
||||||
})
|
})
|
||||||
|
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function createLocalAccount (name: string, userId: number, applicationId: number, t: Sequelize.Transaction) {
|
||||||
|
const { publicKey, privateKey } = await createPrivateAndPublicKeys()
|
||||||
|
const url = getActivityPubUrl('account', name)
|
||||||
|
|
||||||
|
const accountInstance = db.Account.build({
|
||||||
|
name,
|
||||||
|
url,
|
||||||
|
publicKey,
|
||||||
|
privateKey,
|
||||||
|
followersCount: 0,
|
||||||
|
followingCount: 0,
|
||||||
|
inboxUrl: url + '/inbox',
|
||||||
|
outboxUrl: url + '/outbox',
|
||||||
|
sharedInboxUrl: CONFIG.WEBSERVER.URL + '/inbox',
|
||||||
|
followersUrl: url + '/followers',
|
||||||
|
followingUrl: url + '/following',
|
||||||
|
userId,
|
||||||
|
applicationId,
|
||||||
|
podId: null // It is our pod
|
||||||
|
})
|
||||||
|
|
||||||
|
return accountInstance.save({ transaction: t })
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
export {
|
export {
|
||||||
createUserAccountAndChannel
|
createUserAccountAndChannel,
|
||||||
|
createLocalAccount
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import * as Sequelize from 'sequelize'
|
import * as Sequelize from 'sequelize'
|
||||||
|
|
||||||
import { addVideoChannelToFriends } from './friends'
|
|
||||||
import { database as db } from '../initializers'
|
import { database as db } from '../initializers'
|
||||||
import { logger } from '../helpers'
|
import { logger } from '../helpers'
|
||||||
import { AccountInstance } from '../models'
|
import { AccountInstance } from '../models'
|
||||||
import { VideoChannelCreate } from '../../shared/models'
|
import { VideoChannelCreate } from '../../shared/models'
|
||||||
|
import { sendCreateVideoChannel } from './activitypub/send-request'
|
||||||
|
|
||||||
async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account: AccountInstance, t: Sequelize.Transaction) {
|
async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account: AccountInstance, t: Sequelize.Transaction) {
|
||||||
const videoChannelData = {
|
const videoChannelData = {
|
||||||
|
@ -22,10 +22,7 @@ async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account
|
||||||
// Do not forget to add Account information to the created video channel
|
// Do not forget to add Account information to the created video channel
|
||||||
videoChannelCreated.Account = account
|
videoChannelCreated.Account = account
|
||||||
|
|
||||||
const remoteVideoChannel = videoChannelCreated.toAddRemoteJSON()
|
sendCreateVideoChannel(videoChannelCreated, t)
|
||||||
|
|
||||||
// Now we'll add the video channel's meta data to our friends
|
|
||||||
await addVideoChannelToFriends(remoteVideoChannel, t)
|
|
||||||
|
|
||||||
return videoChannelCreated
|
return videoChannelCreated
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,2 @@
|
||||||
export * from './pods'
|
export * from './activity'
|
||||||
export * from './signature'
|
export * from './signature'
|
||||||
export * from './videos'
|
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
import { body } from 'express-validator/check'
|
|
||||||
import * as express from 'express'
|
|
||||||
|
|
||||||
import { database as db } from '../../../initializers'
|
|
||||||
import { isHostValid, logger } from '../../../helpers'
|
|
||||||
import { checkErrors } from '../utils'
|
|
||||||
|
|
||||||
const remotePodsAddValidator = [
|
|
||||||
body('host').custom(isHostValid).withMessage('Should have a host'),
|
|
||||||
body('email').isEmail().withMessage('Should have an email'),
|
|
||||||
body('publicKey').not().isEmpty().withMessage('Should have a public key'),
|
|
||||||
|
|
||||||
(req: express.Request, res: express.Response, next: express.NextFunction) => {
|
|
||||||
logger.debug('Checking podsAdd parameters', { parameters: req.body })
|
|
||||||
|
|
||||||
checkErrors(req, res, () => {
|
|
||||||
db.Pod.loadByHost(req.body.host)
|
|
||||||
.then(pod => {
|
|
||||||
// Pod with this host already exists
|
|
||||||
if (pod) {
|
|
||||||
return res.sendStatus(409)
|
|
||||||
}
|
|
||||||
|
|
||||||
return next()
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
logger.error('Cannot load pod by host.', err)
|
|
||||||
res.sendStatus(500)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
export {
|
|
||||||
remotePodsAddValidator
|
|
||||||
}
|
|
|
@ -2,7 +2,6 @@ export * from './account'
|
||||||
export * from './oembed'
|
export * from './oembed'
|
||||||
export * from './activitypub'
|
export * from './activitypub'
|
||||||
export * from './pagination'
|
export * from './pagination'
|
||||||
export * from './pods'
|
|
||||||
export * from './sort'
|
export * from './sort'
|
||||||
export * from './users'
|
export * from './users'
|
||||||
export * from './videos'
|
export * from './videos'
|
||||||
|
|
|
@ -1,73 +0,0 @@
|
||||||
import { body, param } from 'express-validator/check'
|
|
||||||
import * as express from 'express'
|
|
||||||
|
|
||||||
import { database as db } from '../../initializers/database'
|
|
||||||
import { checkErrors } from './utils'
|
|
||||||
import { logger, isEachUniqueHostValid, isTestInstance } from '../../helpers'
|
|
||||||
import { CONFIG } from '../../initializers'
|
|
||||||
import { hasFriends } from '../../lib'
|
|
||||||
|
|
||||||
const makeFriendsValidator = [
|
|
||||||
body('hosts').custom(isEachUniqueHostValid).withMessage('Should have an array of unique hosts'),
|
|
||||||
|
|
||||||
(req: express.Request, res: express.Response, next: express.NextFunction) => {
|
|
||||||
// Force https if the administrator wants to make friends
|
|
||||||
if (isTestInstance() === false && CONFIG.WEBSERVER.SCHEME === 'http') {
|
|
||||||
return res.status(400)
|
|
||||||
.json({
|
|
||||||
error: 'Cannot make friends with a non HTTPS web server.'
|
|
||||||
})
|
|
||||||
.end()
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.debug('Checking makeFriends parameters', { parameters: req.body })
|
|
||||||
|
|
||||||
checkErrors(req, res, () => {
|
|
||||||
hasFriends()
|
|
||||||
.then(heHasFriends => {
|
|
||||||
if (heHasFriends === true) {
|
|
||||||
// We need to quit our friends before make new ones
|
|
||||||
return res.sendStatus(409)
|
|
||||||
}
|
|
||||||
|
|
||||||
return next()
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
logger.error('Cannot know if we have friends.', err)
|
|
||||||
res.sendStatus(500)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
const podRemoveValidator = [
|
|
||||||
param('id').isNumeric().not().isEmpty().withMessage('Should have a valid id'),
|
|
||||||
|
|
||||||
(req: express.Request, res: express.Response, next: express.NextFunction) => {
|
|
||||||
logger.debug('Checking podRemoveValidator parameters', { parameters: req.params })
|
|
||||||
|
|
||||||
checkErrors(req, res, () => {
|
|
||||||
db.Pod.load(req.params.id)
|
|
||||||
.then(pod => {
|
|
||||||
if (!pod) {
|
|
||||||
logger.error('Cannot find pod %d.', req.params.id)
|
|
||||||
return res.sendStatus(404)
|
|
||||||
}
|
|
||||||
|
|
||||||
res.locals.pod = pod
|
|
||||||
return next()
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
logger.error('Cannot load pod %d.', req.params.id, err)
|
|
||||||
res.sendStatus(500)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
export {
|
|
||||||
makeFriendsValidator,
|
|
||||||
podRemoveValidator
|
|
||||||
}
|
|
|
@ -13,8 +13,8 @@ export namespace AccountMethods {
|
||||||
export type LoadAccountByPodAndUUID = (uuid: string, podId: number, transaction: Sequelize.Transaction) => Bluebird<AccountInstance>
|
export type LoadAccountByPodAndUUID = (uuid: string, podId: number, transaction: Sequelize.Transaction) => Bluebird<AccountInstance>
|
||||||
export type LoadLocalAccountByName = (name: string) => Bluebird<AccountInstance>
|
export type LoadLocalAccountByName = (name: string) => Bluebird<AccountInstance>
|
||||||
export type ListOwned = () => Bluebird<AccountInstance[]>
|
export type ListOwned = () => Bluebird<AccountInstance[]>
|
||||||
export type ListFollowerUrlsForApi = (name: string, start: number, count: number) => Promise< ResultList<string> >
|
export type ListFollowerUrlsForApi = (name: string, start: number, count?: number) => Promise< ResultList<string> >
|
||||||
export type ListFollowingUrlsForApi = (name: string, start: number, count: number) => Promise< ResultList<string> >
|
export type ListFollowingUrlsForApi = (name: string, start: number, count?: number) => Promise< ResultList<string> >
|
||||||
|
|
||||||
export type ToActivityPubObject = (this: AccountInstance) => ActivityPubActor
|
export type ToActivityPubObject = (this: AccountInstance) => ActivityPubActor
|
||||||
export type IsOwned = (this: AccountInstance) => boolean
|
export type IsOwned = (this: AccountInstance) => boolean
|
||||||
|
|
|
@ -268,14 +268,15 @@ function afterDestroy (account: AccountInstance) {
|
||||||
uuid: account.uuid
|
uuid: account.uuid
|
||||||
}
|
}
|
||||||
|
|
||||||
return removeVideoAccountToFriends(removeVideoAccountToFriendsParams)
|
// FIXME: remove account in followers
|
||||||
|
// return removeVideoAccountToFriends(removeVideoAccountToFriendsParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
toActivityPubObject = function (this: AccountInstance) {
|
toActivityPubObject = function (this: AccountInstance) {
|
||||||
const type = this.podId ? 'Application' : 'Person'
|
const type = this.podId ? 'Application' as 'Application' : 'Person' as 'Person'
|
||||||
|
|
||||||
const json = {
|
const json = {
|
||||||
type,
|
type,
|
||||||
|
@ -346,11 +347,11 @@ listOwned = function () {
|
||||||
return Account.findAll(query)
|
return Account.findAll(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
listFollowerUrlsForApi = function (name: string, start: number, count: number) {
|
listFollowerUrlsForApi = function (name: string, start: number, count?: number) {
|
||||||
return createListFollowForApiQuery('followers', name, start, count)
|
return createListFollowForApiQuery('followers', name, start, count)
|
||||||
}
|
}
|
||||||
|
|
||||||
listFollowingUrlsForApi = function (name: string, start: number, count: number) {
|
listFollowingUrlsForApi = function (name: string, start: number, count?: number) {
|
||||||
return createListFollowForApiQuery('following', name, start, count)
|
return createListFollowForApiQuery('following', name, start, count)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -405,7 +406,7 @@ loadAccountByPodAndUUID = function (uuid: string, podId: number, transaction: Se
|
||||||
|
|
||||||
// ------------------------------ UTILS ------------------------------
|
// ------------------------------ UTILS ------------------------------
|
||||||
|
|
||||||
async function createListFollowForApiQuery (type: 'followers' | 'following', name: string, start: number, count: number) {
|
async function createListFollowForApiQuery (type: 'followers' | 'following', name: string, start: number, count?: number) {
|
||||||
let firstJoin: string
|
let firstJoin: string
|
||||||
let secondJoin: string
|
let secondJoin: string
|
||||||
|
|
||||||
|
@ -421,11 +422,13 @@ async function createListFollowForApiQuery (type: 'followers' | 'following', nam
|
||||||
const tasks: Promise<any>[] = []
|
const tasks: Promise<any>[] = []
|
||||||
|
|
||||||
for (const selection of selections) {
|
for (const selection of selections) {
|
||||||
const query = 'SELECT ' + selection + ' FROM "Account" ' +
|
let query = 'SELECT ' + selection + ' FROM "Account" ' +
|
||||||
'INNER JOIN "AccountFollower" ON "AccountFollower"."' + firstJoin + '" = "Account"."id" ' +
|
'INNER JOIN "AccountFollower" ON "AccountFollower"."' + firstJoin + '" = "Account"."id" ' +
|
||||||
'INNER JOIN "Account" AS "Followers" ON "Followers"."id" = "AccountFollower"."' + secondJoin + '" ' +
|
'INNER JOIN "Account" AS "Followers" ON "Followers"."id" = "AccountFollower"."' + secondJoin + '" ' +
|
||||||
'WHERE "Account"."name" = \'$name\' ' +
|
'WHERE "Account"."name" = \'$name\' ' +
|
||||||
'LIMIT ' + start + ', ' + count
|
'LIMIT ' + start
|
||||||
|
|
||||||
|
if (count !== undefined) query += ', ' + count
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
bind: { name },
|
bind: { name },
|
||||||
|
|
|
@ -14,7 +14,7 @@ export interface JobClass {
|
||||||
export interface JobAttributes {
|
export interface JobAttributes {
|
||||||
state: JobState
|
state: JobState
|
||||||
handlerName: string
|
handlerName: string
|
||||||
handlerInputData: object
|
handlerInputData: any
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface JobInstance extends JobClass, JobAttributes, Sequelize.Instance<JobAttributes> {
|
export interface JobInstance extends JobClass, JobAttributes, Sequelize.Instance<JobAttributes> {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import * as Sequelize from 'sequelize'
|
import * as Sequelize from 'sequelize'
|
||||||
|
|
||||||
import { isVideoChannelNameValid, isVideoChannelDescriptionValid } from '../../helpers'
|
import { isVideoChannelNameValid, isVideoChannelDescriptionValid } from '../../helpers'
|
||||||
import { removeVideoChannelToFriends } from '../../lib'
|
|
||||||
|
|
||||||
import { addMethodsToModel, getSort } from '../utils'
|
import { addMethodsToModel, getSort } from '../utils'
|
||||||
import {
|
import {
|
||||||
|
@ -143,12 +142,13 @@ toFormattedJSON = function (this: VideoChannelInstance) {
|
||||||
|
|
||||||
toActivityPubObject = function (this: VideoChannelInstance) {
|
toActivityPubObject = function (this: VideoChannelInstance) {
|
||||||
const json = {
|
const json = {
|
||||||
|
type: 'VideoChannel' as 'VideoChannel',
|
||||||
|
id: this.url,
|
||||||
uuid: this.uuid,
|
uuid: this.uuid,
|
||||||
|
content: this.description,
|
||||||
name: this.name,
|
name: this.name,
|
||||||
description: this.description,
|
published: this.createdAt,
|
||||||
createdAt: this.createdAt,
|
updated: this.updatedAt
|
||||||
updatedAt: this.updatedAt,
|
|
||||||
ownerUUID: this.Account.uuid
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return json
|
return json
|
||||||
|
@ -180,7 +180,7 @@ function afterDestroy (videoChannel: VideoChannelInstance) {
|
||||||
uuid: videoChannel.uuid
|
uuid: videoChannel.uuid
|
||||||
}
|
}
|
||||||
|
|
||||||
return removeVideoChannelToFriends(removeVideoChannelToFriendsParams)
|
// FIXME: send remove event to followers
|
||||||
}
|
}
|
||||||
|
|
||||||
return undefined
|
return undefined
|
||||||
|
@ -277,7 +277,7 @@ loadByUUIDOrUrl = function (uuid: string, url: string, t?: Sequelize.Transaction
|
||||||
{ uuid },
|
{ uuid },
|
||||||
{ url }
|
{ url }
|
||||||
]
|
]
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (t !== undefined) query.transaction = t
|
if (t !== undefined) query.transaction = t
|
||||||
|
|
|
@ -1,58 +1,52 @@
|
||||||
import * as safeBuffer from 'safe-buffer'
|
|
||||||
const Buffer = safeBuffer.Buffer
|
|
||||||
import * as magnetUtil from 'magnet-uri'
|
|
||||||
import { map, maxBy, truncate } from 'lodash'
|
import { map, maxBy, truncate } from 'lodash'
|
||||||
|
import * as magnetUtil from 'magnet-uri'
|
||||||
import * as parseTorrent from 'parse-torrent'
|
import * as parseTorrent from 'parse-torrent'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
|
import * as safeBuffer from 'safe-buffer'
|
||||||
import * as Sequelize from 'sequelize'
|
import * as Sequelize from 'sequelize'
|
||||||
|
import { VideoPrivacy, VideoResolution } from '../../../shared'
|
||||||
import { TagInstance } from './tag-interface'
|
import { VideoTorrentObject } from '../../../shared/models/activitypub/objects/video-torrent-object'
|
||||||
import {
|
import {
|
||||||
logger,
|
createTorrentPromise,
|
||||||
isVideoNameValid,
|
generateImageFromVideoFile,
|
||||||
|
getActivityPubUrl,
|
||||||
|
getVideoFileHeight,
|
||||||
isVideoCategoryValid,
|
isVideoCategoryValid,
|
||||||
isVideoLicenceValid,
|
|
||||||
isVideoLanguageValid,
|
|
||||||
isVideoNSFWValid,
|
|
||||||
isVideoDescriptionValid,
|
isVideoDescriptionValid,
|
||||||
isVideoDurationValid,
|
isVideoDurationValid,
|
||||||
|
isVideoLanguageValid,
|
||||||
|
isVideoLicenceValid,
|
||||||
|
isVideoNameValid,
|
||||||
|
isVideoNSFWValid,
|
||||||
isVideoPrivacyValid,
|
isVideoPrivacyValid,
|
||||||
readFileBufferPromise,
|
logger,
|
||||||
unlinkPromise,
|
|
||||||
renamePromise,
|
renamePromise,
|
||||||
writeFilePromise,
|
|
||||||
createTorrentPromise,
|
|
||||||
statPromise,
|
statPromise,
|
||||||
generateImageFromVideoFile,
|
|
||||||
transcode,
|
transcode,
|
||||||
getVideoFileHeight,
|
unlinkPromise,
|
||||||
getActivityPubUrl
|
writeFilePromise
|
||||||
} from '../../helpers'
|
} from '../../helpers'
|
||||||
import {
|
import {
|
||||||
|
API_VERSION,
|
||||||
CONFIG,
|
CONFIG,
|
||||||
|
CONSTRAINTS_FIELDS,
|
||||||
|
PREVIEWS_SIZE,
|
||||||
REMOTE_SCHEME,
|
REMOTE_SCHEME,
|
||||||
STATIC_PATHS,
|
STATIC_PATHS,
|
||||||
VIDEO_CATEGORIES,
|
|
||||||
VIDEO_LICENCES,
|
|
||||||
VIDEO_LANGUAGES,
|
|
||||||
THUMBNAILS_SIZE,
|
THUMBNAILS_SIZE,
|
||||||
PREVIEWS_SIZE,
|
VIDEO_CATEGORIES,
|
||||||
CONSTRAINTS_FIELDS,
|
VIDEO_LANGUAGES,
|
||||||
API_VERSION,
|
VIDEO_LICENCES,
|
||||||
VIDEO_PRIVACIES
|
VIDEO_PRIVACIES
|
||||||
} from '../../initializers'
|
} from '../../initializers'
|
||||||
import { removeVideoToFriends } from '../../lib'
|
|
||||||
import { VideoResolution, VideoPrivacy } from '../../../shared'
|
|
||||||
import { VideoFileInstance, VideoFileModel } from './video-file-interface'
|
|
||||||
|
|
||||||
import { addMethodsToModel, getSort } from '../utils'
|
import { addMethodsToModel, getSort } from '../utils'
|
||||||
import {
|
|
||||||
VideoInstance,
|
|
||||||
VideoAttributes,
|
|
||||||
|
|
||||||
VideoMethods
|
import { TagInstance } from './tag-interface'
|
||||||
} from './video-interface'
|
import { VideoFileInstance, VideoFileModel } from './video-file-interface'
|
||||||
import { VideoTorrentObject } from '../../../shared/models/activitypub/objects/video-torrent-object'
|
import { VideoAttributes, VideoInstance, VideoMethods } from './video-interface'
|
||||||
|
|
||||||
|
const Buffer = safeBuffer.Buffer
|
||||||
|
|
||||||
let Video: Sequelize.Model<VideoInstance, VideoAttributes>
|
let Video: Sequelize.Model<VideoInstance, VideoAttributes>
|
||||||
let getOriginalFile: VideoMethods.GetOriginalFile
|
let getOriginalFile: VideoMethods.GetOriginalFile
|
||||||
|
@ -374,8 +368,8 @@ function afterDestroy (video: VideoInstance) {
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.push(
|
tasks.push(
|
||||||
video.removePreview(),
|
video.removePreview()
|
||||||
removeVideoToFriends(removeVideoToFriendsParams)
|
// FIXME: remove video for followers
|
||||||
)
|
)
|
||||||
|
|
||||||
// Remove physical files and torrents
|
// Remove physical files and torrents
|
||||||
|
@ -566,7 +560,7 @@ toActivityPubObject = function (this: VideoInstance) {
|
||||||
const { baseUrlHttp, baseUrlWs } = getBaseUrls(this)
|
const { baseUrlHttp, baseUrlWs } = getBaseUrls(this)
|
||||||
|
|
||||||
const tag = this.Tags.map(t => ({
|
const tag = this.Tags.map(t => ({
|
||||||
type: 'Hashtag',
|
type: 'Hashtag' as 'Hashtag',
|
||||||
name: t.name
|
name: t.name
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
@ -596,7 +590,7 @@ toActivityPubObject = function (this: VideoInstance) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const videoObject: VideoTorrentObject = {
|
const videoObject: VideoTorrentObject = {
|
||||||
type: 'Video',
|
type: 'Video' as 'Video',
|
||||||
id: getActivityPubUrl('video', this.uuid),
|
id: getActivityPubUrl('video', this.uuid),
|
||||||
name: this.name,
|
name: this.name,
|
||||||
// https://www.w3.org/TR/activitystreams-vocabulary/#dfn-duration
|
// https://www.w3.org/TR/activitystreams-vocabulary/#dfn-duration
|
||||||
|
@ -604,15 +598,15 @@ toActivityPubObject = function (this: VideoInstance) {
|
||||||
uuid: this.uuid,
|
uuid: this.uuid,
|
||||||
tag,
|
tag,
|
||||||
category: {
|
category: {
|
||||||
id: this.category,
|
identifier: this.category + '',
|
||||||
label: this.getCategoryLabel()
|
name: this.getCategoryLabel()
|
||||||
},
|
},
|
||||||
licence: {
|
licence: {
|
||||||
id: this.licence,
|
identifier: this.licence + '',
|
||||||
name: this.getLicenceLabel()
|
name: this.getLicenceLabel()
|
||||||
},
|
},
|
||||||
language: {
|
language: {
|
||||||
id: this.language,
|
identifier: this.language + '',
|
||||||
name: this.getLanguageLabel()
|
name: this.getLanguageLabel()
|
||||||
},
|
},
|
||||||
views: this.views,
|
views: this.views,
|
||||||
|
|
|
@ -65,7 +65,7 @@ describe('Test a video channels', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should have two video channels when getting author channels', async () => {
|
it('Should have two video channels when getting author channels', async () => {
|
||||||
const res = await getAuthorVideoChannelsList(server.url, userInfo.author.uuid)
|
const res = await getAuthorVideoChannelsList(server.url, userInfo.account.uuid)
|
||||||
|
|
||||||
expect(res.body.total).to.equal(2)
|
expect(res.body.total).to.equal(2)
|
||||||
expect(res.body.data).to.be.an('array')
|
expect(res.body.data).to.be.an('array')
|
||||||
|
|
|
@ -4,7 +4,7 @@ import {
|
||||||
} from './objects'
|
} from './objects'
|
||||||
import { ActivityPubSignature } from './activitypub-signature'
|
import { ActivityPubSignature } from './activitypub-signature'
|
||||||
|
|
||||||
export type Activity = ActivityCreate | ActivityUpdate | ActivityFlag
|
export type Activity = ActivityCreate | ActivityAdd | ActivityUpdate | ActivityFlag
|
||||||
|
|
||||||
// Flag -> report abuse
|
// Flag -> report abuse
|
||||||
export type ActivityType = 'Create' | 'Add' | 'Update' | 'Flag'
|
export type ActivityType = 'Create' | 'Add' | 'Update' | 'Flag'
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
export interface Pod {
|
export interface Pod {
|
||||||
id: number,
|
id: number,
|
||||||
host: string,
|
host: string,
|
||||||
email: string,
|
|
||||||
score: number,
|
score: number,
|
||||||
createdAt: Date
|
createdAt: Date
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ export interface User {
|
||||||
role: UserRole
|
role: UserRole
|
||||||
videoQuota: number
|
videoQuota: number
|
||||||
createdAt: Date,
|
createdAt: Date,
|
||||||
author: {
|
account: {
|
||||||
id: number
|
id: number
|
||||||
uuid: string
|
uuid: string
|
||||||
}
|
}
|
||||||
|
|
10
yarn.lock
10
yarn.lock
|
@ -3473,12 +3473,6 @@ repeat-string@^1.5.2:
|
||||||
version "1.6.1"
|
version "1.6.1"
|
||||||
resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
|
resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
|
||||||
|
|
||||||
request-replay@^1.0.2:
|
|
||||||
version "1.0.4"
|
|
||||||
resolved "https://registry.yarnpkg.com/request-replay/-/request-replay-1.0.4.tgz#b6e5953a7eb39fc8a48e8111c277d35355adfe06"
|
|
||||||
dependencies:
|
|
||||||
retry "^0.10.0"
|
|
||||||
|
|
||||||
request@2.81.0:
|
request@2.81.0:
|
||||||
version "2.81.0"
|
version "2.81.0"
|
||||||
resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0"
|
resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0"
|
||||||
|
@ -3564,10 +3558,6 @@ retry-as-promised@^2.3.1:
|
||||||
bluebird "^3.4.6"
|
bluebird "^3.4.6"
|
||||||
debug "^2.6.9"
|
debug "^2.6.9"
|
||||||
|
|
||||||
retry@^0.10.0:
|
|
||||||
version "0.10.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/retry/-/retry-0.10.1.tgz#e76388d217992c252750241d3d3956fed98d8ff4"
|
|
||||||
|
|
||||||
rimraf@2, rimraf@^2.2.8, rimraf@^2.4.2, rimraf@^2.5.1, rimraf@^2.5.4, rimraf@^2.6.1:
|
rimraf@2, rimraf@^2.2.8, rimraf@^2.4.2, rimraf@^2.5.1, rimraf@^2.5.4, rimraf@^2.6.1:
|
||||||
version "2.6.2"
|
version "2.6.2"
|
||||||
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36"
|
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36"
|
||||||
|
|
Loading…
Reference in New Issue