Make it compile at least

This commit is contained in:
Chocobozzz 2017-11-10 17:27:49 +01:00
parent 38fa206583
commit 571389d43b
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
53 changed files with 342 additions and 1256 deletions

View File

@ -77,7 +77,6 @@
"pg": "^6.4.2",
"pg-hstore": "^2.3.2",
"request": "^2.81.0",
"request-replay": "^1.0.2",
"rimraf": "^2.5.4",
"safe-buffer": "^5.0.1",
"scripty": "^1.5.0",

View File

@ -1,11 +1,11 @@
import * as Promise from 'bluebird'
import { database as db } from '../server/initializers/database'
import { hasFriends } from '../server/lib/friends'
// import { hasFriends } from '../server/lib/friends'
db.init(true)
.then(() => {
return hasFriends()
// FIXME: check if has followers
// return hasFriends()
return true
})
.then(itHasFriends => {
if (itHasFriends === true) {

View File

@ -46,7 +46,7 @@ db.init(false).then(() => onDatabaseInitDone())
// ----------- PeerTube modules -----------
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'
// ----------- Command line -----------
@ -146,19 +146,13 @@ function onDatabaseInitDone () {
const port = CONFIG.LISTEN.PORT
// Run the migration scripts if needed
migrate()
.then(() => {
return installApplication()
})
.then(() => installApplication())
.then(() => {
// ----------- Make the server listening -----------
server.listen(port, function () {
// Activate the communication with friends
activateSchedulers()
// Activate job scheduler
JobScheduler.Instance.activate()
server.listen(port, () => {
VideosPreviewCache.Instance.init(CONFIG.CACHE.PREVIEWS.SIZE)
httpRequestJobScheduler.activate()
transcodingJobScheduler.activate()
logger.info('Server listening on port %d', port)
logger.info('Web server: %s', CONFIG.WEBSERVER.URL)

View File

@ -2,10 +2,12 @@ import * as express from 'express'
import { badRequest } from '../../helpers'
import { inboxRouter } from './inbox'
import { activityPubClientRouter } from './client'
const remoteRouter = express.Router()
remoteRouter.use('/inbox', inboxRouter)
remoteRouter.use('/', activityPubClientRouter)
remoteRouter.use('/*', badRequest)
// ---------------------------------------------------------------------------

View File

@ -5,8 +5,6 @@ import { badRequest } from '../../helpers'
import { oauthClientsRouter } from './oauth-clients'
import { configRouter } from './config'
import { podsRouter } from './pods'
import { remoteRouter } from './remote'
import { requestSchedulerRouter } from './request-schedulers'
import { usersRouter } from './users'
import { videosRouter } from './videos'
@ -15,8 +13,6 @@ const apiRouter = express.Router()
apiRouter.use('/oauth-clients', oauthClientsRouter)
apiRouter.use('/config', configRouter)
apiRouter.use('/pods', podsRouter)
apiRouter.use('/remote', remoteRouter)
apiRouter.use('/request-schedulers', requestSchedulerRouter)
apiRouter.use('/users', usersRouter)
apiRouter.use('/videos', videosRouter)
apiRouter.use('/ping', pong)

View File

@ -1,26 +1,7 @@
import * as express from 'express'
import { getFormattedObjects } from '../../helpers'
import { database as db } from '../../initializers/database'
import { logger, getFormattedObjects } from '../../helpers'
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'
import { asyncMiddleware, paginationValidator, podsSortValidator, setPagination, setPodsSort } from '../../middlewares'
const podsRouter = express.Router()
@ -31,24 +12,6 @@ podsRouter.get('/',
setPagination,
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))
}
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()
}

View File

@ -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
}

View File

@ -1,37 +1,29 @@
import * as express from 'express'
import { database as db, CONFIG } from '../../initializers'
import { logger, getFormattedObjects, retryTransactionWrapper } from '../../helpers'
import { UserCreate, UserRight, UserRole, UserUpdate, UserUpdateMe, UserVideoRate as FormattedUserVideoRate } from '../../../shared'
import { getFormattedObjects, logger, retryTransactionWrapper } from '../../helpers'
import { CONFIG, database as db } from '../../initializers'
import { createUserAccountAndChannel } from '../../lib'
import {
asyncMiddleware,
authenticate,
ensureUserHasRight,
ensureUserRegistrationAllowed,
usersAddValidator,
usersRegisterValidator,
usersUpdateValidator,
usersUpdateMeValidator,
usersRemoveValidator,
usersVideoRatingValidator,
usersGetValidator,
paginationValidator,
setPagination,
usersSortValidator,
setUsersSort,
token,
asyncMiddleware
usersAddValidator,
usersGetValidator,
usersRegisterValidator,
usersRemoveValidator,
usersSortValidator,
usersUpdateMeValidator,
usersUpdateValidator,
usersVideoRatingValidator
} 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 { videosSortValidator } from '../../middlewares/validators/sort'
import { UserInstance } from '../../models'
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) {
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 json: FormattedUserVideoRate = {

View File

@ -1,7 +1,6 @@
import * as express from 'express'
import { database as db } from '../../../initializers/database'
import * as friends from '../../../lib/friends'
import {
logger,
getFormattedObjects,
@ -84,7 +83,8 @@ async function reportVideoAbuse (req: express.Request, res: express.Response) {
videoUUID: videoInstance.uuid
}
await friends.reportAbuseVideoToFriend(reportData, videoInstance, t)
// await friends.reportAbuseVideoToFriend(reportData, videoInstance, t)
// TODO: send abuse to origin pod
}
})

View File

@ -1,31 +1,23 @@
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 { 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()
@ -137,11 +129,8 @@ async function updateVideoChannel (req: express.Request, res: express.Response)
if (videoChannelInfoToUpdate.description !== undefined) videoChannelInstance.set('description', videoChannelInfoToUpdate.description)
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)

View File

@ -1,57 +1,41 @@
import * as express from 'express'
import * as multer from 'multer'
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 { sendAddVideo, sendUpdateVideoChannel } from '../../../lib/activitypub/send-request'
import { transcodingJobScheduler } from '../../../lib/jobs/transcoding-job-scheduler/transcoding-job-scheduler'
import {
CONFIG,
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 {
asyncMiddleware,
authenticate,
paginationValidator,
videosSortValidator,
setVideosSort,
setPagination,
setVideosSearch,
videosUpdateValidator,
videosSearchValidator,
setVideosSort,
videosAddValidator,
videosGetValidator,
videosRemoveValidator,
asyncMiddleware
videosSearchValidator,
videosSortValidator,
videosUpdateValidator
} from '../../../middlewares'
import {
logger,
retryTransactionWrapper,
generateRandomString,
getFormattedObjects,
renamePromise,
getVideoFileHeight,
resetSequelizeInstance
} from '../../../helpers'
import { VideoInstance } from '../../../models'
import { VideoCreate, VideoUpdate, VideoPrivacy } from '../../../../shared'
import { abuseVideoRouter } from './abuse'
import { blacklistRouter } from './blacklist'
import { rateVideoRouter } from './rate'
import { videoChannelRouter } from './channel'
import { getActivityPubUrl } from '../../../helpers/activitypub'
import { rateVideoRouter } from './rate'
const videosRouter = express.Router()
@ -225,7 +209,7 @@ async function addVideo (req: express.Request, res: express.Response, videoPhysi
}
tasks.push(
JobScheduler.Instance.createJob(t, 'videoFileOptimizer', dataInput)
transcodingJobScheduler.createJob(t, 'videoFileOptimizer', dataInput)
)
}
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
if (video.privacy === VideoPrivacy.PRIVATE) return undefined
const remoteVideo = await video.toAddRemoteJSON()
// Now we'll add the video's meta data to our friends
return addVideoToFriends(remoteVideo, t)
await sendAddVideo(video, t)
})
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
if (wasPrivateVideo === false) {
const json = videoInstance.toUpdateRemoteJSON()
return updateVideoToFriends(json, t)
await sendUpdateVideoChannel(videoInstance, t)
}
// Video is not private anymore, send a create action to remote pods
if (wasPrivateVideo === true && videoInstance.privacy !== VideoPrivacy.PRIVATE) {
const remoteVideo = await videoInstance.toAddRemoteJSON()
return addVideoToFriends(remoteVideo, t)
await sendAddVideo(videoInstance, 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
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
videoInstance.increment('views')
.then(() => {
const qaduParams = {
videoId: videoInstance.id,
type: REQUEST_VIDEO_QADU_TYPES.VIEWS
}
return quickAndDirtyUpdateVideoToFriends(qaduParams)
// TODO: send to followers a notification
})
.catch(err => logger.error('Cannot add view to video %s.', videoInstance.uuid, err))
} else {
// Just send the event to our friends
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))
// TODO: send view event to followers
}
// Do not wait the view system
@ -361,7 +331,7 @@ async function getVideoDescription (req: express.Request, res: express.Response)
if (videoInstance.isOwned()) {
description = videoInstance.description
} else {
description = await fetchRemoteDescription(videoInstance)
description = await fetchRemoteVideoDescription(videoInstance)
}
return res.json({ description })

View File

@ -1,25 +1,11 @@
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 { 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()
@ -51,12 +37,12 @@ async function rateVideoRetryWrapper (req: express.Request, res: express.Respons
async function rateVideo (req: express.Request, res: express.Response) {
const body: UserVideoRateUpdate = req.body
const rateType = body.rating
const videoInstance = res.locals.video
const userInstance = res.locals.oauth.token.User
const videoInstance: VideoInstance = res.locals.video
const accountInstance: AccountInstance = res.locals.oauth.token.User.Account
await db.sequelize.transaction(async 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 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
const query = {
userId: userInstance.id,
accountId: accountInstance.id,
videoId: videoInstance.id,
type: rateType
}
await db.UserVideoRate.create(query, sequelizeOptions)
await db.AccountVideoRate.create(query, sequelizeOptions)
}
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
await videoInstance.increment(incrementQuery, sequelizeOptions)
// Send a event to original pod
if (videoInstance.isOwned() === false) {
const eventsParams = []
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)
// TODO: Send a event to original pod
} else {
// TODO: Send update to followers
}
})
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)
}

View File

@ -1,15 +1,15 @@
import { join } from 'path'
import * as request from 'request'
import * as url from 'url'
import { database as db } from '../initializers'
import { logger } from './logger'
import { doRequest, doRequestAndSaveToFile } from './requests'
import { isRemoteAccountValid } from './custom-validators'
import { ActivityIconObject } from '../../shared/index'
import { ActivityPubActor } from '../../shared/models/activitypub/activitypub-actor'
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 { ActivityIconObject } from '../../shared/index'
import { join } from 'path'
import { isRemoteAccountValid } from './custom-validators'
import { logger } from './logger'
import { doRequest, doRequestAndSaveToFile } from './requests'
function generateThumbnailFromUrl (video: VideoInstance, icon: ActivityIconObject) {
const thumbnailName = video.getThumbnailName()
@ -22,9 +22,10 @@ function generateThumbnailFromUrl (video: VideoInstance, icon: ActivityIconObjec
return doRequestAndSaveToFile(options, thumbnailPath)
}
function getActivityPubUrl (type: 'video' | 'videoChannel', uuid: string) {
if (type === 'video') return CONFIG.WEBSERVER.URL + '/videos/watch/' + uuid
else if (type === 'videoChannel') return CONFIG.WEBSERVER.URL + '/video-channels/' + uuid
function getActivityPubUrl (type: 'video' | 'videoChannel' | 'account', id: string) {
if (type === 'video') return CONFIG.WEBSERVER.URL + '/videos/watch/' + id
else if (type === 'videoChannel') return CONFIG.WEBSERVER.URL + '/video-channels/' + id
else if (type === 'account') return CONFIG.WEBSERVER.URL + '/account/' + id
return ''
}
@ -94,7 +95,24 @@ async function fetchRemoteAccountAndCreatePod (accountUrl: string) {
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,{
'@context': [
'https://www.w3.org/ns/activitystreams',
@ -141,7 +159,9 @@ export {
activityPubCollectionPagination,
getActivityPubUrl,
generateThumbnailFromUrl,
getOrCreateAccount
getOrCreateAccount,
fetchRemoteVideoPreview,
fetchRemoteVideoDescription
}
// ---------------------------------------------------------------------------

View File

@ -1,16 +1,6 @@
import * as replay from 'request-replay'
import * as request from 'request'
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 * as request from 'request'
function doRequest (requestOptions: request.CoreOptions & request.UriOptions) {
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 {
doRequest,
doRequestAndSaveToFile,
makeRetryRequest,
makeSecureRequest
doRequestAndSaveToFile
}

View File

@ -35,7 +35,7 @@ export {
function webfingerLookup (url: string) {
return new Promise<WebFingerData>((res, rej) => {
webfinger.lookup('nick@silverbucket.net', (err, p) => {
webfinger.lookup(url, (err, p) => {
if (err) return rej(err)
return p

View File

@ -361,12 +361,6 @@ export {
PODS_SCORE,
PREVIEWS_SIZE,
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_INTERVAL,
REQUESTS_LIMIT_PER_POD,

View File

@ -2,7 +2,6 @@ import { join } from 'path'
import { flattenDepth } from 'lodash'
require('pg').defaults.parseInt8 = true // Avoid BIGINT to be converted to string
import * as Sequelize from 'sequelize'
import * as Bluebird from 'bluebird'
import { CONFIG } from './constants'
// 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 { AccountFollowModel } from '../models/account/account-follow-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 { OAuthTokenModel } from './../models/oauth/oauth-token-interface'
import { OAuthClientModel } from './../models/oauth/oauth-client-interface'

View File

@ -1,20 +1,21 @@
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 { CONFIG, LAST_MIGRATION_VERSION, CACHE } from './constants'
import { clientsExist, usersExist } from './checker'
import { logger, createCertsIfNotExist, mkdirpPromise, rimrafPromise } from '../helpers'
import { createUserAccountAndChannel } from '../lib'
import { UserRole } from '../../shared'
import { createLocalAccount } from '../lib/user'
async function installApplication () {
await db.sequelize.sync()
await removeCacheDirectories()
await createDirectoriesIfNotExist()
await createCertsIfNotExist()
await createOAuthClientIfNotExist()
await createOAuthAdminIfNotExist()
await createApplicationIfNotExist()
}
// ---------------------------------------------------------------------------
@ -28,7 +29,7 @@ export {
function removeCacheDirectories () {
const cacheDirectories = CACHE.DIRECTORIES
const tasks: Bluebird<any>[] = []
const tasks: Promise<any>[] = []
// Cache directories
for (const key of Object.keys(cacheDirectories)) {
@ -120,7 +121,12 @@ async function createOAuthAdminIfNotExist () {
await createUserAccountAndChannel(user, validatePassword)
logger.info('Username: ' + username)
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)
}

View File

@ -1,5 +1,4 @@
import * as Sequelize from 'sequelize'
import * as Promise from 'bluebird'
import { join } from 'path'
import { readdirPromise, renamePromise } from '../../helpers/core-utils'

View File

@ -1,3 +1,4 @@
export * from './process-create'
export * from './process-flag'
export * from './process-update'
export * from './send-request'

View File

@ -8,7 +8,11 @@ import { VideoChannelInstance } from '../../models/video/video-channel-interface
import { VideoFileAttributes } from '../../models/video/video-file-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)
if (videoFromDatabase) throw new Error('Video with this UUID/Url already exists.')

View File

@ -5,7 +5,7 @@ import {
} from '../../../shared'
function processFlagActivity (activity: ActivityCreate) {
// empty
return Promise.resolve(undefined)
}
// ---------------------------------------------------------------------------

View File

@ -1,5 +1,6 @@
import * as Sequelize from 'sequelize'
import { database as db } from '../../initializers'
import {
AccountInstance,
VideoInstance,
@ -13,54 +14,66 @@ function sendCreateVideoChannel (videoChannel: VideoChannelInstance, t: Sequeliz
const videoChannelObject = videoChannel.toActivityPubObject()
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) {
const videoChannelObject = videoChannel.toActivityPubObject()
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) {
const videoChannelObject = videoChannel.toActivityPubObject()
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) {
const videoObject = video.toActivityPubObject()
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) {
const videoObject = video.toActivityPubObject()
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) {
const videoObject = video.toActivityPubObject()
const data = deleteActivityData(video.url, video.VideoChannel.Account, videoObject)
return broadcastToFollowers(data, t)
return broadcastToFollowers(data, video.VideoChannel.Account, t)
}
// ---------------------------------------------------------------------------
export {
sendCreateVideoChannel,
sendUpdateVideoChannel,
sendDeleteVideoChannel,
sendAddVideo,
sendUpdateVideo,
sendDeleteVideo
}
// ---------------------------------------------------------------------------
function broadcastToFollowers (data: any, t: Sequelize.Transaction) {
return httpRequestJobScheduler.createJob(t, 'http-request', 'httpRequestBroadcastHandler', data)
async function broadcastToFollowers (data: any, fromAccount: AccountInstance, t: Sequelize.Transaction) {
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) {

View File

@ -3,9 +3,8 @@ import { join } from 'path'
import { createWriteStream } from 'fs'
import { database as db, CONFIG, CACHE } from '../../initializers'
import { logger, unlinkPromise } from '../../helpers'
import { logger, unlinkPromise, fetchRemoteVideoPreview } from '../../helpers'
import { VideoInstance } from '../../models'
import { fetchRemotePreview } from '../../lib'
class VideosPreviewCache {
@ -54,7 +53,7 @@ class VideosPreviewCache {
}
private saveRemotePreviewAndReturnPath (video: VideoInstance) {
const req = fetchRemotePreview(video)
const req = fetchRemoteVideoPreview(video)
return new Promise<string>((res, rej) => {
const path = join(CACHE.DIRECTORIES.PREVIEWS, video.getPreviewName())

View File

@ -1,8 +1,6 @@
export * from './activitypub'
export * from './cache'
export * from './jobs'
export * from './request'
export * from './friends'
export * from './oauth-model'
export * from './user'
export * from './video-channel'

View File

@ -1,19 +1,28 @@
import * as Bluebird from 'bluebird'
import { database as db } from '../../../initializers/database'
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) {
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()
}
async function onSuccess (jobId: number) {
logger.info('Job %d is a success.', jobId)
}
// ---------------------------------------------------------------------------

View File

@ -4,7 +4,11 @@ import * as httpRequestBroadcastHandler from './http-request-broadcast-handler'
import * as httpRequestUnicastHandler from './http-request-unicast-handler'
import { JobCategory } from '../../../../shared'
const jobHandlers: { [ handlerName: string ]: JobHandler<any> } = {
type HTTPRequestPayload = {
uris: string[]
body: any
}
const jobHandlers: { [ handlerName: string ]: JobHandler<HTTPRequestPayload, void> } = {
httpRequestBroadcastHandler,
httpRequestUnicastHandler
}
@ -13,5 +17,6 @@ const jobCategory: JobCategory = 'http-request'
const httpRequestJobScheduler = new JobScheduler(jobCategory, jobHandlers)
export {
HTTPRequestPayload,
httpRequestJobScheduler
}

View File

@ -1,19 +1,26 @@
import * as Bluebird from 'bluebird'
import { database as db } from '../../../initializers/database'
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) {
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()
}
async function onSuccess (jobId: number) {
logger.info('Job %d is a success.', jobId)
}
// ---------------------------------------------------------------------------

View File

@ -1,28 +1,22 @@
import { AsyncQueue, forever, queue } from 'async'
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 { 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> {
process (data: object, jobId: number): T
export interface JobHandler<P, T> {
process (data: object, jobId: number): Promise<T>
onError (err: Error, jobId: number)
onSuccess (jobId: number, jobResult: T)
onSuccess (jobId: number, jobResult: T, jobScheduler: JobScheduler<P, T>)
}
type JobQueueCallback = (err: Error) => void
class JobScheduler<T> {
class JobScheduler<P, T> {
constructor (
private jobCategory: JobCategory,
private jobHandlers: { [ id: string ]: JobHandler<T> }
private jobHandlers: { [ id: string ]: JobHandler<P, T> }
) {}
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 = {
state: JOB_STATES.PENDING,
category,
category: this.jobCategory,
handlerName,
handlerInputData
}
const options = { transaction }
return db.Job.create(createQuery, options)
@ -95,7 +90,7 @@ class JobScheduler<T> {
await job.save()
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)
} catch (err) {
logger.error('Error in job handler %s.', job.handlerName, err)
@ -111,7 +106,7 @@ class JobScheduler<T> {
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
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
try {
await job.save()
jobHandler.onSuccess(job.id, jobResult)
jobHandler.onSuccess(job.id, jobResult, this)
} catch (err) {
this.cannotSaveJobError(err)
}

View File

@ -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 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,
videoFileTranscoder
}
@ -13,5 +17,6 @@ const jobCategory: JobCategory = 'transcoding'
const transcodingJobScheduler = new JobScheduler(jobCategory, jobHandlers)
export {
TranscodingJobPayload,
transcodingJobScheduler
}

View File

@ -1,12 +1,13 @@
import * as Bluebird from 'bluebird'
import { computeResolutionsToTranscode, logger } from '../../../helpers'
import { database as db } from '../../../initializers/database'
import { logger, computeResolutionsToTranscode } from '../../../helpers'
import { VideoInstance } from '../../../models'
import { addVideoToFriends } from '../../friends'
import { sendAddVideo } from '../../activitypub/send-request'
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)
// No video, maybe deleted?
if (!video) {
@ -24,7 +25,7 @@ function onError (err: Error, jobId: number) {
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
logger.info('Job %d is a success.', jobId)
@ -34,10 +35,8 @@ async function onSuccess (jobId: number, video: VideoInstance) {
// Video does not exist anymore
if (!videoDatabase) return undefined
const remoteVideo = await videoDatabase.toAddRemoteJSON()
// Now we'll add the video's meta data to our friends
await addVideoToFriends(remoteVideo, null)
// Now we'll add the video's meta data to our followers
await sendAddVideo(video, undefined)
const originalFileHeight = await videoDatabase.getOriginalFileHeight()
// Create transcoding jobs if there are enabled resolutions
@ -59,7 +58,7 @@ async function onSuccess (jobId: number, video: VideoInstance) {
resolution
}
const p = JobScheduler.Instance.createJob(t, 'videoFileTranscoder', dataInput)
const p = jobScheduler.createJob(t, 'videoFileTranscoder', dataInput)
tasks.push(p)
}

View File

@ -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 { 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) {
const video = await db.Video.loadByUUIDAndPopulateAccountAndPodAndTags(data.videoUUID)
@ -32,10 +32,7 @@ async function onSuccess (jobId: number, video: VideoInstance) {
// Video does not exist anymore
if (!videoDatabase) return undefined
const remoteVideo = videoDatabase.toUpdateRemoteJSON()
// Now we'll add the video's meta data to our friends
await updateVideoToFriends(remoteVideo, null)
await sendUpdateVideo(video, undefined)
return undefined
}

View File

@ -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
}

View File

@ -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'

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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 { CONFIG } from '../initializers/constants'
import { UserInstance } from '../models'
import { addVideoAccountToFriends } from './friends'
import { createVideoChannel } from './video-channel'
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 accountInstance = db.Account.build({
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 accountCreated = await createLocalAccount(user.username, user.id, null, t)
const videoChannelInfo = {
name: `Default ${userCreated.username} channel`
}
const videoChannel = await createVideoChannel(videoChannelInfo, accountCreated, t)
return { account, videoChannel }
return { account: accountCreated, videoChannel }
})
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 {
createUserAccountAndChannel
createUserAccountAndChannel,
createLocalAccount
}

View File

@ -1,10 +1,10 @@
import * as Sequelize from 'sequelize'
import { addVideoChannelToFriends } from './friends'
import { database as db } from '../initializers'
import { logger } from '../helpers'
import { AccountInstance } from '../models'
import { VideoChannelCreate } from '../../shared/models'
import { sendCreateVideoChannel } from './activitypub/send-request'
async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account: AccountInstance, t: Sequelize.Transaction) {
const videoChannelData = {
@ -22,10 +22,7 @@ async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account
// Do not forget to add Account information to the created video channel
videoChannelCreated.Account = account
const remoteVideoChannel = videoChannelCreated.toAddRemoteJSON()
// Now we'll add the video channel's meta data to our friends
await addVideoChannelToFriends(remoteVideoChannel, t)
sendCreateVideoChannel(videoChannelCreated, t)
return videoChannelCreated
}

View File

@ -1,3 +1,2 @@
export * from './pods'
export * from './activity'
export * from './signature'
export * from './videos'

View File

@ -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
}

View File

@ -2,7 +2,6 @@ export * from './account'
export * from './oembed'
export * from './activitypub'
export * from './pagination'
export * from './pods'
export * from './sort'
export * from './users'
export * from './videos'

View File

@ -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
}

View File

@ -13,8 +13,8 @@ export namespace AccountMethods {
export type LoadAccountByPodAndUUID = (uuid: string, podId: number, transaction: Sequelize.Transaction) => Bluebird<AccountInstance>
export type LoadLocalAccountByName = (name: string) => Bluebird<AccountInstance>
export type ListOwned = () => Bluebird<AccountInstance[]>
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 ListFollowerUrlsForApi = (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 IsOwned = (this: AccountInstance) => boolean

View File

@ -268,14 +268,15 @@ function afterDestroy (account: AccountInstance) {
uuid: account.uuid
}
return removeVideoAccountToFriends(removeVideoAccountToFriendsParams)
// FIXME: remove account in followers
// return removeVideoAccountToFriends(removeVideoAccountToFriendsParams)
}
return undefined
}
toActivityPubObject = function (this: AccountInstance) {
const type = this.podId ? 'Application' : 'Person'
const type = this.podId ? 'Application' as 'Application' : 'Person' as 'Person'
const json = {
type,
@ -346,11 +347,11 @@ listOwned = function () {
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)
}
listFollowingUrlsForApi = function (name: string, start: number, count: number) {
listFollowingUrlsForApi = function (name: string, start: number, count?: number) {
return createListFollowForApiQuery('following', name, start, count)
}
@ -405,7 +406,7 @@ loadAccountByPodAndUUID = function (uuid: string, podId: number, transaction: Se
// ------------------------------ 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 secondJoin: string
@ -421,11 +422,13 @@ async function createListFollowForApiQuery (type: 'followers' | 'following', nam
const tasks: Promise<any>[] = []
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 "Account" AS "Followers" ON "Followers"."id" = "AccountFollower"."' + secondJoin + '" ' +
'WHERE "Account"."name" = \'$name\' ' +
'LIMIT ' + start + ', ' + count
'LIMIT ' + start
if (count !== undefined) query += ', ' + count
const options = {
bind: { name },

View File

@ -14,7 +14,7 @@ export interface JobClass {
export interface JobAttributes {
state: JobState
handlerName: string
handlerInputData: object
handlerInputData: any
}
export interface JobInstance extends JobClass, JobAttributes, Sequelize.Instance<JobAttributes> {

View File

@ -1,7 +1,6 @@
import * as Sequelize from 'sequelize'
import { isVideoChannelNameValid, isVideoChannelDescriptionValid } from '../../helpers'
import { removeVideoChannelToFriends } from '../../lib'
import { addMethodsToModel, getSort } from '../utils'
import {
@ -143,12 +142,13 @@ toFormattedJSON = function (this: VideoChannelInstance) {
toActivityPubObject = function (this: VideoChannelInstance) {
const json = {
type: 'VideoChannel' as 'VideoChannel',
id: this.url,
uuid: this.uuid,
content: this.description,
name: this.name,
description: this.description,
createdAt: this.createdAt,
updatedAt: this.updatedAt,
ownerUUID: this.Account.uuid
published: this.createdAt,
updated: this.updatedAt
}
return json
@ -180,7 +180,7 @@ function afterDestroy (videoChannel: VideoChannelInstance) {
uuid: videoChannel.uuid
}
return removeVideoChannelToFriends(removeVideoChannelToFriendsParams)
// FIXME: send remove event to followers
}
return undefined
@ -277,7 +277,7 @@ loadByUUIDOrUrl = function (uuid: string, url: string, t?: Sequelize.Transaction
{ uuid },
{ url }
]
},
}
}
if (t !== undefined) query.transaction = t

View File

@ -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 * as magnetUtil from 'magnet-uri'
import * as parseTorrent from 'parse-torrent'
import { join } from 'path'
import * as safeBuffer from 'safe-buffer'
import * as Sequelize from 'sequelize'
import { TagInstance } from './tag-interface'
import { VideoPrivacy, VideoResolution } from '../../../shared'
import { VideoTorrentObject } from '../../../shared/models/activitypub/objects/video-torrent-object'
import {
logger,
isVideoNameValid,
createTorrentPromise,
generateImageFromVideoFile,
getActivityPubUrl,
getVideoFileHeight,
isVideoCategoryValid,
isVideoLicenceValid,
isVideoLanguageValid,
isVideoNSFWValid,
isVideoDescriptionValid,
isVideoDurationValid,
isVideoLanguageValid,
isVideoLicenceValid,
isVideoNameValid,
isVideoNSFWValid,
isVideoPrivacyValid,
readFileBufferPromise,
unlinkPromise,
logger,
renamePromise,
writeFilePromise,
createTorrentPromise,
statPromise,
generateImageFromVideoFile,
transcode,
getVideoFileHeight,
getActivityPubUrl
unlinkPromise,
writeFilePromise
} from '../../helpers'
import {
API_VERSION,
CONFIG,
CONSTRAINTS_FIELDS,
PREVIEWS_SIZE,
REMOTE_SCHEME,
STATIC_PATHS,
VIDEO_CATEGORIES,
VIDEO_LICENCES,
VIDEO_LANGUAGES,
THUMBNAILS_SIZE,
PREVIEWS_SIZE,
CONSTRAINTS_FIELDS,
API_VERSION,
VIDEO_CATEGORIES,
VIDEO_LANGUAGES,
VIDEO_LICENCES,
VIDEO_PRIVACIES
} from '../../initializers'
import { removeVideoToFriends } from '../../lib'
import { VideoResolution, VideoPrivacy } from '../../../shared'
import { VideoFileInstance, VideoFileModel } from './video-file-interface'
import { addMethodsToModel, getSort } from '../utils'
import {
VideoInstance,
VideoAttributes,
VideoMethods
} from './video-interface'
import { VideoTorrentObject } from '../../../shared/models/activitypub/objects/video-torrent-object'
import { TagInstance } from './tag-interface'
import { VideoFileInstance, VideoFileModel } from './video-file-interface'
import { VideoAttributes, VideoInstance, VideoMethods } from './video-interface'
const Buffer = safeBuffer.Buffer
let Video: Sequelize.Model<VideoInstance, VideoAttributes>
let getOriginalFile: VideoMethods.GetOriginalFile
@ -374,8 +368,8 @@ function afterDestroy (video: VideoInstance) {
}
tasks.push(
video.removePreview(),
removeVideoToFriends(removeVideoToFriendsParams)
video.removePreview()
// FIXME: remove video for followers
)
// Remove physical files and torrents
@ -566,7 +560,7 @@ toActivityPubObject = function (this: VideoInstance) {
const { baseUrlHttp, baseUrlWs } = getBaseUrls(this)
const tag = this.Tags.map(t => ({
type: 'Hashtag',
type: 'Hashtag' as 'Hashtag',
name: t.name
}))
@ -596,7 +590,7 @@ toActivityPubObject = function (this: VideoInstance) {
}
const videoObject: VideoTorrentObject = {
type: 'Video',
type: 'Video' as 'Video',
id: getActivityPubUrl('video', this.uuid),
name: this.name,
// https://www.w3.org/TR/activitystreams-vocabulary/#dfn-duration
@ -604,15 +598,15 @@ toActivityPubObject = function (this: VideoInstance) {
uuid: this.uuid,
tag,
category: {
id: this.category,
label: this.getCategoryLabel()
identifier: this.category + '',
name: this.getCategoryLabel()
},
licence: {
id: this.licence,
identifier: this.licence + '',
name: this.getLicenceLabel()
},
language: {
id: this.language,
identifier: this.language + '',
name: this.getLanguageLabel()
},
views: this.views,

View File

@ -65,7 +65,7 @@ describe('Test a video channels', function () {
})
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.data).to.be.an('array')

View File

@ -4,7 +4,7 @@ import {
} from './objects'
import { ActivityPubSignature } from './activitypub-signature'
export type Activity = ActivityCreate | ActivityUpdate | ActivityFlag
export type Activity = ActivityCreate | ActivityAdd | ActivityUpdate | ActivityFlag
// Flag -> report abuse
export type ActivityType = 'Create' | 'Add' | 'Update' | 'Flag'

View File

@ -1,7 +1,6 @@
export interface Pod {
id: number,
host: string,
email: string,
score: number,
createdAt: Date
}

View File

@ -9,7 +9,7 @@ export interface User {
role: UserRole
videoQuota: number
createdAt: Date,
author: {
account: {
id: number
uuid: string
}

View File

@ -3473,12 +3473,6 @@ repeat-string@^1.5.2:
version "1.6.1"
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:
version "2.81.0"
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"
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:
version "2.6.2"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36"