Remove references to author

pull/128/head
Chocobozzz 2017-11-10 14:48:08 +01:00
parent 0d0e8dd090
commit 38fa206583
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
34 changed files with 111 additions and 1367 deletions

View File

@ -32,8 +32,8 @@
// RemoteVideoChannelCreateData,
// RemoteVideoChannelUpdateData,
// RemoteVideoChannelRemoveData,
// RemoteVideoAuthorRemoveData,
// RemoteVideoAuthorCreateData
// RemoteVideoAccountRemoveData,
// RemoteVideoAccountCreateData
// } from '../../../../shared'
// import { VideoInstance } from '../../../models/video/video-interface'
//
@ -49,8 +49,8 @@
// functionsHash[ENDPOINT_ACTIONS.UPDATE_CHANNEL] = updateRemoteVideoChannelRetryWrapper
// functionsHash[ENDPOINT_ACTIONS.REMOVE_CHANNEL] = removeRemoteVideoChannelRetryWrapper
// functionsHash[ENDPOINT_ACTIONS.REPORT_ABUSE] = reportAbuseRemoteVideoRetryWrapper
// functionsHash[ENDPOINT_ACTIONS.ADD_AUTHOR] = addRemoteVideoAuthorRetryWrapper
// functionsHash[ENDPOINT_ACTIONS.REMOVE_AUTHOR] = removeRemoteVideoAuthorRetryWrapper
// functionsHash[ENDPOINT_ACTIONS.ADD_ACCOUNT] = addRemoteVideoAccountRetryWrapper
// functionsHash[ENDPOINT_ACTIONS.REMOVE_ACCOUNT] = removeRemoteVideoAccountRetryWrapper
//
// const remoteVideosRouter = express.Router()
//
@ -245,24 +245,24 @@
// logger.info('Remote video with uuid %s removed.', videoToRemoveData.uuid)
// }
//
// async function removeRemoteVideoAuthorRetryWrapper (authorAttributesToRemove: RemoteVideoAuthorRemoveData, fromPod: PodInstance) {
// async function removeRemoteVideoAccountRetryWrapper (accountAttributesToRemove: RemoteVideoAccountRemoveData, fromPod: PodInstance) {
// const options = {
// arguments: [ authorAttributesToRemove, fromPod ],
// errorMessage: 'Cannot remove the remote video author with many retries.'
// arguments: [ accountAttributesToRemove, fromPod ],
// errorMessage: 'Cannot remove the remote video account with many retries.'
// }
//
// await retryTransactionWrapper(removeRemoteVideoAuthor, options)
// await retryTransactionWrapper(removeRemoteVideoAccount, options)
// }
//
// async function removeRemoteVideoAuthor (authorAttributesToRemove: RemoteVideoAuthorRemoveData, fromPod: PodInstance) {
// logger.debug('Removing remote video author "%s".', authorAttributesToRemove.uuid)
// async function removeRemoteVideoAccount (accountAttributesToRemove: RemoteVideoAccountRemoveData, fromPod: PodInstance) {
// logger.debug('Removing remote video account "%s".', accountAttributesToRemove.uuid)
//
// await db.sequelize.transaction(async t => {
// const videoAuthor = await db.Author.loadAuthorByPodAndUUID(authorAttributesToRemove.uuid, fromPod.id, t)
// await videoAuthor.destroy({ transaction: t })
// const videoAccount = await db.Account.loadAccountByPodAndUUID(accountAttributesToRemove.uuid, fromPod.id, t)
// await videoAccount.destroy({ transaction: t })
// })
//
// logger.info('Remote video author with uuid %s removed.', authorAttributesToRemove.uuid)
// logger.info('Remote video account with uuid %s removed.', accountAttributesToRemove.uuid)
// }
//
// async function removeRemoteVideoChannelRetryWrapper (videoChannelAttributesToRemove: RemoteVideoChannelRemoveData, fromPod: PodInstance) {

View File

@ -28,7 +28,7 @@ import {
UserRole,
UserRight
} from '../../../shared'
import { createUserAuthorAndChannel } from '../../lib'
import { createUserAccountAndChannel } from '../../lib'
import { UserInstance } from '../../models'
import { videosSortValidator } from '../../middlewares/validators/sort'
import { setVideosSort } from '../../middlewares/sort'
@ -142,9 +142,9 @@ async function createUser (req: express.Request, res: express.Response, next: ex
videoQuota: body.videoQuota
})
await createUserAuthorAndChannel(user)
await createUserAccountAndChannel(user)
logger.info('User %s with its channel and author created.', body.username)
logger.info('User %s with its channel and account created.', body.username)
}
async function registerUser (req: express.Request, res: express.Response, next: express.NextFunction) {
@ -159,7 +159,7 @@ async function registerUser (req: express.Request, res: express.Response, next:
videoQuota: CONFIG.USER.VIDEO_QUOTA
})
await createUserAuthorAndChannel(user)
await createUserAccountAndChannel(user)
return res.type('json').status(204).end()
}

View File

@ -17,14 +17,14 @@ import {
videoChannelsRemoveValidator,
videoChannelGetValidator,
videoChannelsUpdateValidator,
listVideoAuthorChannelsValidator,
listVideoAccountChannelsValidator,
asyncMiddleware
} from '../../../middlewares'
import {
createVideoChannel,
updateVideoChannelToFriends
} from '../../../lib'
import { VideoChannelInstance, AuthorInstance } from '../../../models'
import { VideoChannelInstance, AccountInstance } from '../../../models'
import { VideoChannelCreate, VideoChannelUpdate } from '../../../../shared'
const videoChannelRouter = express.Router()
@ -37,9 +37,9 @@ videoChannelRouter.get('/channels',
asyncMiddleware(listVideoChannels)
)
videoChannelRouter.get('/authors/:authorId/channels',
listVideoAuthorChannelsValidator,
asyncMiddleware(listVideoAuthorChannels)
videoChannelRouter.get('/accounts/:accountId/channels',
listVideoAccountChannelsValidator,
asyncMiddleware(listVideoAccountChannels)
)
videoChannelRouter.post('/channels',
@ -79,8 +79,8 @@ async function listVideoChannels (req: express.Request, res: express.Response, n
return res.json(getFormattedObjects(resultList.data, resultList.total))
}
async function listVideoAuthorChannels (req: express.Request, res: express.Response, next: express.NextFunction) {
const resultList = await db.VideoChannel.listByAuthor(res.locals.author.id)
async function listVideoAccountChannels (req: express.Request, res: express.Response, next: express.NextFunction) {
const resultList = await db.VideoChannel.listByAccount(res.locals.account.id)
return res.json(getFormattedObjects(resultList.data, resultList.total))
}
@ -101,11 +101,11 @@ async function addVideoChannelRetryWrapper (req: express.Request, res: express.R
async function addVideoChannel (req: express.Request, res: express.Response) {
const videoChannelInfo: VideoChannelCreate = req.body
const author: AuthorInstance = res.locals.oauth.token.User.Author
const account: AccountInstance = res.locals.oauth.token.User.Account
let videoChannelCreated: VideoChannelInstance
await db.sequelize.transaction(async t => {
videoChannelCreated = await createVideoChannel(videoChannelInfo, author, t)
videoChannelCreated = await createVideoChannel(videoChannelInfo, account, t)
})
logger.info('Video channel with uuid %s created.', videoChannelCreated.uuid)
@ -179,7 +179,7 @@ async function removeVideoChannel (req: express.Request, res: express.Response)
}
async function getVideoChannel (req: express.Request, res: express.Response, next: express.NextFunction) {
const videoChannelWithVideos = await db.VideoChannel.loadAndPopulateAuthorAndVideos(res.locals.videoChannel.id)
const videoChannelWithVideos = await db.VideoChannel.loadAndPopulateAccountAndVideos(res.locals.videoChannel.id)
return res.json(videoChannelWithVideos.toFormattedJSON())
}

View File

@ -395,7 +395,7 @@ async function removeVideo (req: express.Request, res: express.Response) {
}
async function searchVideos (req: express.Request, res: express.Response, next: express.NextFunction) {
const resultList = await db.Video.searchAndPopulateAuthorAndPodAndTags(
const resultList = await db.Video.searchAndPopulateAccountAndPodAndTags(
req.params.value,
req.query.field,
req.query.start,

View File

@ -110,9 +110,9 @@ async function generateWatchHtmlPage (req: express.Request, res: express.Respons
// Let Angular application handle errors
if (validator.isUUID(videoId, 4)) {
videoPromise = db.Video.loadByUUIDAndPopulateAuthorAndPodAndTags(videoId)
videoPromise = db.Video.loadByUUIDAndPopulateAccountAndPodAndTags(videoId)
} else if (validator.isInt(videoId)) {
videoPromise = db.Video.loadAndPopulateAuthorAndPodAndTags(+videoId)
videoPromise = db.Video.loadAndPopulateAccountAndPodAndTags(+videoId)
} else {
return res.sendFile(indexPath)
}

View File

@ -47,7 +47,7 @@ function generateOEmbed (req: express.Request, res: express.Response, next: expr
width: embedWidth,
height: embedHeight,
title: video.name,
author_name: video.VideoChannel.Author.name,
author_name: video.VideoChannel.Account.name,
provider_name: 'PeerTube',
provider_url: webserverUrl
}

View File

@ -3,5 +3,6 @@ export * from './misc'
export * from './pods'
export * from './pods'
export * from './users'
export * from './video-accounts'
export * from './video-channels'
export * from './videos'

View File

@ -0,0 +1,45 @@
import * as Promise from 'bluebird'
import * as validator from 'validator'
import * as express from 'express'
import 'express-validator'
import { database as db } from '../../initializers'
import { AccountInstance } from '../../models'
import { logger } from '../logger'
import { isUserUsernameValid } from './users'
function isVideoAccountNameValid (value: string) {
return isUserUsernameValid(value)
}
function checkVideoAccountExists (id: string, res: express.Response, callback: () => void) {
let promise: Promise<AccountInstance>
if (validator.isInt(id)) {
promise = db.Account.load(+id)
} else { // UUID
promise = db.Account.loadByUUID(id)
}
promise.then(account => {
if (!account) {
return res.status(404)
.json({ error: 'Video account not found' })
.end()
}
res.locals.account = account
callback()
})
.catch(err => {
logger.error('Error in video account request validator.', err)
return res.sendStatus(500)
})
}
// ---------------------------------------------------------------------------
export {
checkVideoAccountExists,
isVideoAccountNameValid
}

View File

@ -26,9 +26,9 @@ function isVideoChannelUUIDValid (value: string) {
function checkVideoChannelExists (id: string, res: express.Response, callback: () => void) {
let promise: Promise<VideoChannelInstance>
if (validator.isInt(id)) {
promise = db.VideoChannel.loadAndPopulateAuthor(+id)
promise = db.VideoChannel.loadAndPopulateAccount(+id)
} else { // UUID
promise = db.VideoChannel.loadByUUIDAndPopulateAuthor(id)
promise = db.VideoChannel.loadByUUIDAndPopulateAccount(id)
}
promise.then(videoChannel => {

View File

@ -166,9 +166,9 @@ function isVideoFileInfoHashValid (value: string) {
function checkVideoExists (id: string, res: express.Response, callback: () => void) {
let promise: Promise<VideoInstance>
if (validator.isInt(id)) {
promise = db.Video.loadAndPopulateAuthorAndPodAndTags(+id)
promise = db.Video.loadAndPopulateAccountAndPodAndTags(+id)
} else { // UUID
promise = db.Video.loadByUUIDAndPopulateAuthorAndPodAndTags(id)
promise = db.Video.loadByUUIDAndPopulateAccountAndPodAndTags(id)
}
promise.then(video => {

View File

@ -29,7 +29,7 @@ const PAGINATION_COUNT_DEFAULT = 15
// Sortable columns per schema
const SEARCHABLE_COLUMNS = {
VIDEOS: [ 'name', 'magnetUri', 'host', 'author', 'tags' ]
VIDEOS: [ 'name', 'magnetUri', 'host', 'account', 'tags' ]
}
// Sortable columns per schema

View File

@ -44,10 +44,6 @@ const database: {
OAuthClient?: OAuthClientModel,
OAuthToken?: OAuthTokenModel,
Pod?: PodModel,
RequestToPod?: RequestToPodModel,
RequestVideoEvent?: RequestVideoEventModel,
RequestVideoQadu?: RequestVideoQaduModel,
Request?: RequestModel,
Tag?: TagModel,
AccountVideoRate?: AccountVideoRateModel,
AccountFollow?: AccountFollowModel,

View File

@ -5,7 +5,7 @@ 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 { createUserAuthorAndChannel } from '../lib'
import { createUserAccountAndChannel } from '../lib'
import { UserRole } from '../../shared'
async function installApplication () {
@ -117,7 +117,7 @@ async function createOAuthAdminIfNotExist () {
}
const user = db.User.build(userData)
await createUserAuthorAndChannel(user, validatePassword)
await createUserAccountAndChannel(user, validatePassword)
logger.info('Username: ' + username)
logger.info('User password: ' + password)

View File

@ -43,7 +43,7 @@ class VideosPreviewCache {
}
private async loadPreviews (key: string) {
const video = await db.Video.loadByUUIDAndPopulateAuthorAndPodAndTags(key)
const video = await db.Video.loadByUUIDAndPopulateAccountAndPodAndTags(key)
if (!video) return undefined
if (video.isOwned()) return join(CONFIG.STORAGE.PREVIEWS_DIR, video.getPreviewName())

View File

@ -1,567 +0,0 @@
import * as request from 'request'
import * as Sequelize from 'sequelize'
import * as Bluebird from 'bluebird'
import { join } from 'path'
import { database as db } from '../initializers/database'
import {
API_VERSION,
CONFIG,
REQUESTS_IN_PARALLEL,
REQUEST_ENDPOINTS,
REQUEST_ENDPOINT_ACTIONS,
REMOTE_SCHEME,
STATIC_PATHS
} from '../initializers'
import {
logger,
getMyPublicCert,
makeSecureRequest,
makeRetryRequest
} from '../helpers'
import {
RequestScheduler,
RequestSchedulerOptions,
RequestVideoQaduScheduler,
RequestVideoQaduSchedulerOptions,
RequestVideoEventScheduler,
RequestVideoEventSchedulerOptions
} from './request'
import {
PodInstance,
VideoInstance
} from '../models'
import {
RequestEndpoint,
RequestVideoEventType,
RequestVideoQaduType,
RemoteVideoCreateData,
RemoteVideoUpdateData,
RemoteVideoRemoveData,
RemoteVideoReportAbuseData,
ResultList,
RemoteVideoRequestType,
Pod as FormattedPod,
RemoteVideoChannelCreateData,
RemoteVideoChannelUpdateData,
RemoteVideoChannelRemoveData,
RemoteVideoAuthorCreateData,
RemoteVideoAuthorRemoveData
} from '../../shared'
type QaduParam = { videoId: number, type: RequestVideoQaduType }
type EventParam = { videoId: number, type: RequestVideoEventType }
const ENDPOINT_ACTIONS = REQUEST_ENDPOINT_ACTIONS[REQUEST_ENDPOINTS.VIDEOS]
const requestScheduler = new RequestScheduler()
const requestVideoQaduScheduler = new RequestVideoQaduScheduler()
const requestVideoEventScheduler = new RequestVideoEventScheduler()
function activateSchedulers () {
requestScheduler.activate()
requestVideoQaduScheduler.activate()
requestVideoEventScheduler.activate()
}
function addVideoToFriends (videoData: RemoteVideoCreateData, transaction: Sequelize.Transaction) {
const options = {
type: ENDPOINT_ACTIONS.ADD_VIDEO,
endpoint: REQUEST_ENDPOINTS.VIDEOS,
data: videoData,
transaction
}
return createRequest(options)
}
function updateVideoToFriends (videoData: RemoteVideoUpdateData, transaction: Sequelize.Transaction) {
const options = {
type: ENDPOINT_ACTIONS.UPDATE_VIDEO,
endpoint: REQUEST_ENDPOINTS.VIDEOS,
data: videoData,
transaction
}
return createRequest(options)
}
function removeVideoToFriends (videoParams: RemoteVideoRemoveData, transaction?: Sequelize.Transaction) {
const options = {
type: ENDPOINT_ACTIONS.REMOVE_VIDEO,
endpoint: REQUEST_ENDPOINTS.VIDEOS,
data: videoParams,
transaction
}
return createRequest(options)
}
function addVideoAuthorToFriends (authorData: RemoteVideoAuthorCreateData, transaction: Sequelize.Transaction) {
const options = {
type: ENDPOINT_ACTIONS.ADD_AUTHOR,
endpoint: REQUEST_ENDPOINTS.VIDEOS,
data: authorData,
transaction
}
return createRequest(options)
}
function removeVideoAuthorToFriends (authorData: RemoteVideoAuthorRemoveData, transaction?: Sequelize.Transaction) {
const options = {
type: ENDPOINT_ACTIONS.REMOVE_AUTHOR,
endpoint: REQUEST_ENDPOINTS.VIDEOS,
data: authorData,
transaction
}
return createRequest(options)
}
function addVideoChannelToFriends (videoChannelData: RemoteVideoChannelCreateData, transaction: Sequelize.Transaction) {
const options = {
type: ENDPOINT_ACTIONS.ADD_CHANNEL,
endpoint: REQUEST_ENDPOINTS.VIDEOS,
data: videoChannelData,
transaction
}
return createRequest(options)
}
function updateVideoChannelToFriends (videoChannelData: RemoteVideoChannelUpdateData, transaction: Sequelize.Transaction) {
const options = {
type: ENDPOINT_ACTIONS.UPDATE_CHANNEL,
endpoint: REQUEST_ENDPOINTS.VIDEOS,
data: videoChannelData,
transaction
}
return createRequest(options)
}
function removeVideoChannelToFriends (videoChannelParams: RemoteVideoChannelRemoveData, transaction?: Sequelize.Transaction) {
const options = {
type: ENDPOINT_ACTIONS.REMOVE_CHANNEL,
endpoint: REQUEST_ENDPOINTS.VIDEOS,
data: videoChannelParams,
transaction
}
return createRequest(options)
}
function reportAbuseVideoToFriend (reportData: RemoteVideoReportAbuseData, video: VideoInstance, transaction: Sequelize.Transaction) {
const options = {
type: ENDPOINT_ACTIONS.REPORT_ABUSE,
endpoint: REQUEST_ENDPOINTS.VIDEOS,
data: reportData,
toIds: [ video.VideoChannel.Author.podId ],
transaction
}
return createRequest(options)
}
function quickAndDirtyUpdateVideoToFriends (qaduParam: QaduParam, transaction?: Sequelize.Transaction) {
const options = {
videoId: qaduParam.videoId,
type: qaduParam.type,
transaction
}
return createVideoQaduRequest(options)
}
function quickAndDirtyUpdatesVideoToFriends (qadusParams: QaduParam[], transaction: Sequelize.Transaction) {
const tasks = []
qadusParams.forEach(qaduParams => {
tasks.push(quickAndDirtyUpdateVideoToFriends(qaduParams, transaction))
})
return Promise.all(tasks)
}
function addEventToRemoteVideo (eventParam: EventParam, transaction?: Sequelize.Transaction) {
const options = {
videoId: eventParam.videoId,
type: eventParam.type,
transaction
}
return createVideoEventRequest(options)
}
function addEventsToRemoteVideo (eventsParams: EventParam[], transaction: Sequelize.Transaction) {
const tasks = []
for (const eventParams of eventsParams) {
tasks.push(addEventToRemoteVideo(eventParams, transaction))
}
return Promise.all(tasks)
}
async function hasFriends () {
const count = await db.Pod.countAll()
return count !== 0
}
async function makeFriends (hosts: string[]) {
const podsScore = {}
logger.info('Make friends!')
const cert = await getMyPublicCert()
for (const host of hosts) {
await computeForeignPodsList(host, podsScore)
}
logger.debug('Pods scores computed.', { podsScore: podsScore })
const podsList = computeWinningPods(hosts, podsScore)
logger.debug('Pods that we keep.', { podsToKeep: podsList })
return makeRequestsToWinningPods(cert, podsList)
}
async function quitFriends () {
// Stop pool requests
requestScheduler.deactivate()
try {
await requestScheduler.flush()
await requestVideoQaduScheduler.flush()
const pods = await db.Pod.list()
const requestParams = {
method: 'POST' as 'POST',
path: '/api/' + API_VERSION + '/remote/pods/remove',
toPod: null
}
// Announce we quit them
// We don't care if the request fails
// The other pod will exclude us automatically after a while
try {
await Bluebird.map(pods, pod => {
requestParams.toPod = pod
return makeSecureRequest(requestParams)
}, { concurrency: REQUESTS_IN_PARALLEL })
} catch (err) { // Don't stop the process
logger.error('Some errors while quitting friends.', err)
}
const tasks = []
for (const pod of pods) {
tasks.push(pod.destroy())
}
await Promise.all(pods)
logger.info('Removed all remote videos.')
requestScheduler.activate()
} catch (err) {
// Don't forget to re activate the scheduler, even if there was an error
requestScheduler.activate()
throw err
}
}
async function sendOwnedDataToPod (podId: number) {
// First send authors
await sendOwnedAuthorsToPod(podId)
await sendOwnedChannelsToPod(podId)
await sendOwnedVideosToPod(podId)
}
async function sendOwnedChannelsToPod (podId: number) {
const videoChannels = await db.VideoChannel.listOwned()
const tasks: Promise<any>[] = []
for (const videoChannel of videoChannels) {
const remoteVideoChannel = videoChannel.toAddRemoteJSON()
const options = {
type: 'add-channel' as 'add-channel',
endpoint: REQUEST_ENDPOINTS.VIDEOS,
data: remoteVideoChannel,
toIds: [ podId ],
transaction: null
}
const p = createRequest(options)
tasks.push(p)
}
await Promise.all(tasks)
}
async function sendOwnedAuthorsToPod (podId: number) {
const authors = await db.Author.listOwned()
const tasks: Promise<any>[] = []
for (const author of authors) {
const remoteAuthor = author.toAddRemoteJSON()
const options = {
type: 'add-author' as 'add-author',
endpoint: REQUEST_ENDPOINTS.VIDEOS,
data: remoteAuthor,
toIds: [ podId ],
transaction: null
}
const p = createRequest(options)
tasks.push(p)
}
await Promise.all(tasks)
}
async function sendOwnedVideosToPod (podId: number) {
const videosList = await db.Video.listOwnedAndPopulateAuthorAndTags()
const tasks: Bluebird<any>[] = []
for (const video of videosList) {
const promise = video.toAddRemoteJSON()
.then(remoteVideo => {
const options = {
type: 'add-video' as 'add-video',
endpoint: REQUEST_ENDPOINTS.VIDEOS,
data: remoteVideo,
toIds: [ podId ],
transaction: null
}
return createRequest(options)
})
.catch(err => {
logger.error('Cannot convert video to remote.', err)
// Don't break the process
return undefined
})
tasks.push(promise)
}
await Promise.all(tasks)
}
function fetchRemotePreview (video: VideoInstance) {
const host = video.VideoChannel.Author.Pod.host
const path = join(STATIC_PATHS.PREVIEWS, video.getPreviewName())
return request.get(REMOTE_SCHEME.HTTP + '://' + host + path)
}
function fetchRemoteDescription (video: VideoInstance) {
const host = video.VideoChannel.Author.Pod.host
const path = video.getDescriptionPath()
const requestOptions = {
url: REMOTE_SCHEME.HTTP + '://' + host + path,
json: true
}
return new Promise<string>((res, rej) => {
request.get(requestOptions, (err, response, body) => {
if (err) return rej(err)
return res(body.description ? body.description : '')
})
})
}
async function removeFriend (pod: PodInstance) {
const requestParams = {
method: 'POST' as 'POST',
path: '/api/' + API_VERSION + '/remote/pods/remove',
toPod: pod
}
try {
await makeSecureRequest(requestParams)
} catch (err) {
logger.warn('Cannot notify friends %s we are quitting him.', pod.host, err)
}
try {
await pod.destroy()
logger.info('Removed friend %s.', pod.host)
} catch (err) {
logger.error('Cannot destroy friend %s.', pod.host, err)
}
}
function getRequestScheduler () {
return requestScheduler
}
function getRequestVideoQaduScheduler () {
return requestVideoQaduScheduler
}
function getRequestVideoEventScheduler () {
return requestVideoEventScheduler
}
// ---------------------------------------------------------------------------
export {
activateSchedulers,
addVideoToFriends,
removeVideoAuthorToFriends,
updateVideoToFriends,
addVideoAuthorToFriends,
reportAbuseVideoToFriend,
quickAndDirtyUpdateVideoToFriends,
quickAndDirtyUpdatesVideoToFriends,
addEventToRemoteVideo,
addEventsToRemoteVideo,
hasFriends,
makeFriends,
quitFriends,
removeFriend,
removeVideoToFriends,
sendOwnedDataToPod,
getRequestScheduler,
getRequestVideoQaduScheduler,
getRequestVideoEventScheduler,
fetchRemotePreview,
addVideoChannelToFriends,
fetchRemoteDescription,
updateVideoChannelToFriends,
removeVideoChannelToFriends
}
// ---------------------------------------------------------------------------
async function computeForeignPodsList (host: string, podsScore: { [ host: string ]: number }) {
const result = await getForeignPodsList(host)
const foreignPodsList: { host: string }[] = result.data
// Let's give 1 point to the pod we ask the friends list
foreignPodsList.push({ host })
for (const foreignPod of foreignPodsList) {
const foreignPodHost = foreignPod.host
if (podsScore[foreignPodHost]) podsScore[foreignPodHost]++
else podsScore[foreignPodHost] = 1
}
return undefined
}
function computeWinningPods (hosts: string[], podsScore: { [ host: string ]: number }) {
// Build the list of pods to add
// Only add a pod if it exists in more than a half base pods
const podsList = []
const baseScore = hosts.length / 2
for (const podHost of Object.keys(podsScore)) {
// If the pod is not me and with a good score we add it
if (isMe(podHost) === false && podsScore[podHost] > baseScore) {
podsList.push({ host: podHost })
}
}
return podsList
}
function getForeignPodsList (host: string) {
return new Promise< ResultList<FormattedPod> >((res, rej) => {
const path = '/api/' + API_VERSION + '/remote/pods/list'
request.post(REMOTE_SCHEME.HTTP + '://' + host + path, (err, response, body) => {
if (err) return rej(err)
try {
const json: ResultList<FormattedPod> = JSON.parse(body)
return res(json)
} catch (err) {
return rej(err)
}
})
})
}
async function makeRequestsToWinningPods (cert: string, podsList: PodInstance[]) {
// Stop pool requests
requestScheduler.deactivate()
// Flush pool requests
requestScheduler.forceSend()
try {
await Bluebird.map(podsList, async pod => {
const params = {
url: REMOTE_SCHEME.HTTP + '://' + pod.host + '/api/' + API_VERSION + '/remote/pods/add',
method: 'POST' as 'POST',
json: {
host: CONFIG.WEBSERVER.HOST,
email: CONFIG.ADMIN.EMAIL,
publicKey: cert
}
}
const { response, body } = await makeRetryRequest(params)
const typedBody = body as { cert: string, email: string }
if (response.statusCode === 200) {
const podObj = db.Pod.build({ host: pod.host, publicKey: typedBody.cert, email: typedBody.email })
let podCreated: PodInstance
try {
podCreated = await podObj.save()
} catch (err) {
logger.error('Cannot add friend %s pod.', pod.host, err)
}
// Add our videos to the request scheduler
sendOwnedDataToPod(podCreated.id)
.catch(err => logger.warn('Cannot send owned data to pod %d.', podCreated.id, err))
} else {
logger.error('Status not 200 for %s pod.', pod.host)
}
}, { concurrency: REQUESTS_IN_PARALLEL })
logger.debug('makeRequestsToWinningPods finished.')
requestScheduler.activate()
} catch (err) {
// Final callback, we've ended all the requests
// Now we made new friends, we can re activate the pool of requests
requestScheduler.activate()
}
}
// Wrapper that populate "toIds" argument with all our friends if it is not specified
type CreateRequestOptions = {
type: RemoteVideoRequestType
endpoint: RequestEndpoint
data: Object
toIds?: number[]
transaction: Sequelize.Transaction
}
async function createRequest (options: CreateRequestOptions) {
if (options.toIds !== undefined) {
await requestScheduler.createRequest(options as RequestSchedulerOptions)
return undefined
}
// If the "toIds" pods is not specified, we send the request to all our friends
const podIds = await db.Pod.listAllIds(options.transaction)
const newOptions = Object.assign(options, { toIds: podIds })
await requestScheduler.createRequest(newOptions)
return undefined
}
function createVideoQaduRequest (options: RequestVideoQaduSchedulerOptions) {
return requestVideoQaduScheduler.createRequest(options)
}
function createVideoEventRequest (options: RequestVideoEventSchedulerOptions) {
return requestVideoEventScheduler.createRequest(options)
}
function isMe (host: string) {
return host === CONFIG.WEBSERVER.HOST
}

View File

@ -7,7 +7,7 @@ import { addVideoToFriends } from '../../friends'
import { JobScheduler } from '../job-scheduler'
async function process (data: { videoUUID: string }, jobId: number) {
const video = await db.Video.loadByUUIDAndPopulateAuthorAndPodAndTags(data.videoUUID)
const video = await db.Video.loadByUUIDAndPopulateAccountAndPodAndTags(data.videoUUID)
// No video, maybe deleted?
if (!video) {
logger.info('Do not process job %d, video does not exist.', jobId, { videoUUID: video.uuid })
@ -30,7 +30,7 @@ async function onSuccess (jobId: number, video: VideoInstance) {
logger.info('Job %d is a success.', jobId)
// Maybe the video changed in database, refresh it
const videoDatabase = await db.Video.loadByUUIDAndPopulateAuthorAndPodAndTags(video.uuid)
const videoDatabase = await db.Video.loadByUUIDAndPopulateAccountAndPodAndTags(video.uuid)
// Video does not exist anymore
if (!videoDatabase) return undefined

View File

@ -5,7 +5,7 @@ import { VideoInstance } from '../../../models'
import { VideoResolution } from '../../../../shared'
async function process (data: { videoUUID: string, resolution: VideoResolution }, jobId: number) {
const video = await db.Video.loadByUUIDAndPopulateAuthorAndPodAndTags(data.videoUUID)
const video = await db.Video.loadByUUIDAndPopulateAccountAndPodAndTags(data.videoUUID)
// No video, maybe deleted?
if (!video) {
logger.info('Do not process job %d, video does not exist.', jobId, { videoUUID: video.uuid })
@ -28,7 +28,7 @@ async function onSuccess (jobId: number, video: VideoInstance) {
logger.info('Job %d is a success.', jobId)
// Maybe the video changed in database, refresh it
const videoDatabase = await db.Video.loadByUUIDAndPopulateAuthorAndPodAndTags(video.uuid)
const videoDatabase = await db.Video.loadByUUIDAndPopulateAccountAndPodAndTags(video.uuid)
// Video does not exist anymore
if (!videoDatabase) return undefined

View File

@ -11,7 +11,7 @@ async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account
name: videoChannelInfo.name,
description: videoChannelInfo.description,
remote: false,
authorId: account.id
accountId: account.id
}
const videoChannel = db.VideoChannel.build(videoChannelData)

View File

@ -9,19 +9,19 @@ import {
isVideoChannelDescriptionValid,
isVideoChannelNameValid,
checkVideoChannelExists,
checkVideoAuthorExists
checkVideoAccountExists
} from '../../helpers'
import { UserInstance } from '../../models'
import { UserRight } from '../../../shared'
const listVideoAuthorChannelsValidator = [
param('authorId').custom(isIdOrUUIDValid).withMessage('Should have a valid author id'),
const listVideoAccountChannelsValidator = [
param('accountId').custom(isIdOrUUIDValid).withMessage('Should have a valid account id'),
(req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking listVideoAuthorChannelsValidator parameters', { parameters: req.body })
logger.debug('Checking listVideoAccountChannelsValidator parameters', { parameters: req.body })
checkErrors(req, res, () => {
checkVideoAuthorExists(req.params.authorId, res, next)
checkVideoAccountExists(req.params.accountId, res, next)
})
}
]
@ -54,7 +54,7 @@ const videoChannelsUpdateValidator = [
.end()
}
if (res.locals.videoChannel.Author.userId !== res.locals.oauth.token.User.id) {
if (res.locals.videoChannel.Account.userId !== res.locals.oauth.token.User.id) {
return res.status(403)
.json({ error: 'Cannot update video channel of another user' })
.end()
@ -98,7 +98,7 @@ const videoChannelGetValidator = [
// ---------------------------------------------------------------------------
export {
listVideoAuthorChannelsValidator,
listVideoAccountChannelsValidator,
videoChannelsAddValidator,
videoChannelsUpdateValidator,
videoChannelsRemoveValidator,
@ -119,8 +119,8 @@ function checkUserCanDeleteVideoChannel (res: express.Response, callback: () =>
// Check if the user can delete the video channel
// The user can delete it if s/he is an admin
// Or if s/he is the video channel's author
if (user.hasRight(UserRight.REMOVE_ANY_VIDEO_CHANNEL) === false && res.locals.videoChannel.Author.userId !== user.id) {
// Or if s/he is the video channel's account
if (user.hasRight(UserRight.REMOVE_ANY_VIDEO_CHANNEL) === false && res.locals.videoChannel.Account.userId !== user.id) {
return res.status(403)
.json({ error: 'Cannot remove video channel of another user' })
.end()
@ -131,7 +131,7 @@ function checkUserCanDeleteVideoChannel (res: express.Response, callback: () =>
}
function checkVideoChannelIsNotTheLastOne (res: express.Response, callback: () => void) {
db.VideoChannel.countByAuthor(res.locals.oauth.token.User.Author.id)
db.VideoChannel.countByAccount(res.locals.oauth.token.User.Account.id)
.then(count => {
if (count <= 1) {
return res.status(409)

View File

@ -48,11 +48,11 @@ const videosAddValidator = [
const videoFile: Express.Multer.File = req.files['videofile'][0]
const user = res.locals.oauth.token.User
return db.VideoChannel.loadByIdAndAuthor(req.body.channelId, user.Author.id)
return db.VideoChannel.loadByIdAndAccount(req.body.channelId, user.Account.id)
.then(videoChannel => {
if (!videoChannel) {
res.status(400)
.json({ error: 'Unknown video video channel for this author.' })
.json({ error: 'Unknown video video channel for this account.' })
.end()
return undefined
@ -131,7 +131,7 @@ const videosUpdateValidator = [
.end()
}
if (video.VideoChannel.Author.userId !== res.locals.oauth.token.User.id) {
if (video.VideoChannel.Account.userId !== res.locals.oauth.token.User.id) {
return res.status(403)
.json({ error: 'Cannot update video of another user' })
.end()
@ -163,7 +163,7 @@ const videosGetValidator = [
if (video.privacy !== VideoPrivacy.PRIVATE) return next()
authenticate(req, res, () => {
if (video.VideoChannel.Author.userId !== res.locals.oauth.token.User.id) {
if (video.VideoChannel.Account.userId !== res.locals.oauth.token.User.id) {
return res.status(403)
.json({ error: 'Cannot get this private video of another user' })
.end()
@ -256,10 +256,10 @@ function checkUserCanDeleteVideo (userId: number, res: express.Response, callbac
// Check if the user can delete the video
// The user can delete it if s/he is an admin
// Or if s/he is the video's author
const author = res.locals.video.VideoChannel.Author
// Or if s/he is the video's account
const account = res.locals.video.VideoChannel.Account
const user = res.locals.oauth.token.User
if (user.hasRight(UserRight.REMOVE_ANY_VIDEO) === false && author.userId !== user.id) {
if (user.hasRight(UserRight.REMOVE_ANY_VIDEO) === false && account.userId !== user.id) {
return res.status(403)
.json({ error: 'Cannot remove video of another user' })
.end()

View File

@ -164,7 +164,7 @@ toFormattedJSON = function (this: UserInstance) {
roleLabel: USER_ROLE_LABELS[this.role],
videoQuota: this.videoQuota,
createdAt: this.createdAt,
author: {
account: {
id: this.Account.id,
uuid: this.Account.uuid
}
@ -295,7 +295,7 @@ function getOriginalVideoFileTotalFromUser (user: UserInstance) {
'(SELECT MAX("VideoFiles"."size") AS "size" FROM "VideoFiles" ' +
'INNER JOIN "Videos" ON "VideoFiles"."videoId" = "Videos"."id" ' +
'INNER JOIN "VideoChannels" ON "VideoChannels"."id" = "Videos"."channelId" ' +
'INNER JOIN "Accounts" ON "VideoChannels"."authorId" = "Accounts"."id" ' +
'INNER JOIN "Accounts" ON "VideoChannels"."accountId" = "Accounts"."id" ' +
'INNER JOIN "Users" ON "Accounts"."userId" = "Users"."id" ' +
'WHERE "Users"."id" = $userId GROUP BY "Videos"."id") t'

View File

@ -2,6 +2,5 @@ export * from './application'
export * from './job'
export * from './oauth'
export * from './pod'
export * from './request'
export * from './account'
export * from './video'

View File

@ -131,7 +131,7 @@ getByTokenAndPopulateUser = function (bearerToken: string) {
model: OAuthToken['sequelize'].models.User,
include: [
{
model: OAuthToken['sequelize'].models.Author,
model: OAuthToken['sequelize'].models.Account,
required: true
}
]
@ -156,7 +156,7 @@ getByRefreshTokenAndPopulateUser = function (refreshToken: string) {
model: OAuthToken['sequelize'].models.User,
include: [
{
model: OAuthToken['sequelize'].models.Author,
model: OAuthToken['sequelize'].models.Account,
required: true
}
]

View File

@ -1,12 +0,0 @@
import * as Promise from 'bluebird'
export interface AbstractRequestClass <T> {
countTotalRequests: () => Promise<number>
listWithLimitAndRandom: (limitPods: number, limitRequestsPerPod: number) => Promise<T>
removeWithEmptyTo: () => Promise<number>
removeAll: () => Promise<void>
}
export interface AbstractRequestToPodClass {
removeByRequestIdsAndPod: (ids: number[], podId: number) => Promise<number>
}

View File

@ -1,5 +0,0 @@
export * from './abstract-request-interface'
export * from './request-interface'
export * from './request-to-pod-interface'
export * from './request-video-event-interface'
export * from './request-video-qadu-interface'

View File

@ -1,46 +0,0 @@
import * as Sequelize from 'sequelize'
import * as Promise from 'bluebird'
import { AbstractRequestClass } from './abstract-request-interface'
import { PodInstance, PodAttributes } from '../pod/pod-interface'
import { RequestEndpoint } from '../../../shared/models/request-scheduler.model'
export type RequestsGrouped = {
[ podId: number ]: {
request: RequestInstance,
pod: PodInstance
}[]
}
export namespace RequestMethods {
export type CountTotalRequests = () => Promise<number>
export type ListWithLimitAndRandom = (limitPods: number, limitRequestsPerPod: number) => Promise<RequestsGrouped>
export type RemoveWithEmptyTo = () => Promise<number>
export type RemoveAll = () => Promise<void>
}
export interface RequestClass extends AbstractRequestClass<RequestsGrouped> {
countTotalRequests: RequestMethods.CountTotalRequests
listWithLimitAndRandom: RequestMethods.ListWithLimitAndRandom
removeWithEmptyTo: RequestMethods.RemoveWithEmptyTo
removeAll: RequestMethods.RemoveAll
}
export interface RequestAttributes {
request: object
endpoint: RequestEndpoint
}
export interface RequestInstance extends RequestClass, RequestAttributes, Sequelize.Instance<RequestAttributes> {
id: number
createdAt: Date
updatedAt: Date
setPods: Sequelize.HasManySetAssociationsMixin<PodAttributes, number>
Pods: PodInstance[]
}
export interface RequestModel extends RequestClass, Sequelize.Model<RequestInstance, RequestAttributes> {}

View File

@ -1,23 +0,0 @@
import * as Sequelize from 'sequelize'
import * as Promise from 'bluebird'
import { AbstractRequestToPodClass } from './abstract-request-interface'
export namespace RequestToPodMethods {
export type RemoveByRequestIdsAndPod = (requestsIds: number[], podId: number) => Promise<number>
}
export interface RequestToPodClass extends AbstractRequestToPodClass {
removeByRequestIdsAndPod: RequestToPodMethods.RemoveByRequestIdsAndPod
}
export interface RequestToPodAttributes {
}
export interface RequestToPodInstance extends RequestToPodClass, RequestToPodAttributes, Sequelize.Instance<RequestToPodAttributes> {
id: number
createdAt: Date
updatedAt: Date
}
export interface RequestToPodModel extends RequestToPodClass, Sequelize.Model<RequestToPodInstance, RequestToPodAttributes> {}

View File

@ -1,51 +0,0 @@
import * as Sequelize from 'sequelize'
import { addMethodsToModel } from '../utils'
import {
RequestToPodInstance,
RequestToPodAttributes,
RequestToPodMethods
} from './request-to-pod-interface'
let RequestToPod: Sequelize.Model<RequestToPodInstance, RequestToPodAttributes>
let removeByRequestIdsAndPod: RequestToPodMethods.RemoveByRequestIdsAndPod
export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
RequestToPod = sequelize.define<RequestToPodInstance, RequestToPodAttributes>('RequestToPod', {}, {
indexes: [
{
fields: [ 'requestId' ]
},
{
fields: [ 'podId' ]
},
{
fields: [ 'requestId', 'podId' ],
unique: true
}
]
})
const classMethods = [
removeByRequestIdsAndPod
]
addMethodsToModel(RequestToPod, classMethods)
return RequestToPod
}
// ---------------------------------------------------------------------------
removeByRequestIdsAndPod = function (requestsIds: number[], podId: number) {
const query = {
where: {
requestId: {
[Sequelize.Op.in]: requestsIds
},
podId: podId
}
}
return RequestToPod.destroy(query)
}

View File

@ -1,50 +0,0 @@
import * as Sequelize from 'sequelize'
import * as Promise from 'bluebird'
import { AbstractRequestClass, AbstractRequestToPodClass } from './abstract-request-interface'
import { VideoInstance } from '../video/video-interface'
import { PodInstance } from '../pod/pod-interface'
import { RequestVideoEventType } from '../../../shared/models/request-scheduler.model'
export type RequestsVideoEventGrouped = {
[ podId: number ]: {
id: number
type: RequestVideoEventType
count: number
video: VideoInstance
pod: PodInstance
}[]
}
export namespace RequestVideoEventMethods {
export type CountTotalRequests = () => Promise<number>
export type ListWithLimitAndRandom = (limitPods: number, limitRequestsPerPod: number) => Promise<RequestsVideoEventGrouped>
export type RemoveByRequestIdsAndPod = (ids: number[], podId: number) => Promise<number>
export type RemoveAll = () => Promise<void>
}
export interface RequestVideoEventClass extends AbstractRequestClass<RequestsVideoEventGrouped>, AbstractRequestToPodClass {
countTotalRequests: RequestVideoEventMethods.CountTotalRequests
listWithLimitAndRandom: RequestVideoEventMethods.ListWithLimitAndRandom
removeByRequestIdsAndPod: RequestVideoEventMethods.RemoveByRequestIdsAndPod
removeAll: RequestVideoEventMethods.RemoveAll
}
export interface RequestVideoEventAttributes {
type: RequestVideoEventType
count: number
}
export interface RequestVideoEventInstance
extends RequestVideoEventClass, RequestVideoEventAttributes, Sequelize.Instance<RequestVideoEventAttributes> {
id: number
Video: VideoInstance
}
export interface RequestVideoEventModel
extends RequestVideoEventClass, Sequelize.Model<RequestVideoEventInstance, RequestVideoEventAttributes> {}

View File

@ -1,191 +0,0 @@
/*
Request Video events (likes, dislikes, views...)
*/
import { values } from 'lodash'
import * as Sequelize from 'sequelize'
import { database as db } from '../../initializers/database'
import { REQUEST_VIDEO_EVENT_TYPES } from '../../initializers'
import { isVideoEventCountValid } from '../../helpers'
import { addMethodsToModel } from '../utils'
import {
RequestVideoEventInstance,
RequestVideoEventAttributes,
RequestVideoEventMethods,
RequestsVideoEventGrouped
} from './request-video-event-interface'
let RequestVideoEvent: Sequelize.Model<RequestVideoEventInstance, RequestVideoEventAttributes>
let countTotalRequests: RequestVideoEventMethods.CountTotalRequests
let listWithLimitAndRandom: RequestVideoEventMethods.ListWithLimitAndRandom
let removeByRequestIdsAndPod: RequestVideoEventMethods.RemoveByRequestIdsAndPod
let removeAll: RequestVideoEventMethods.RemoveAll
export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
RequestVideoEvent = sequelize.define<RequestVideoEventInstance, RequestVideoEventAttributes>('RequestVideoEvent',
{
type: {
type: DataTypes.ENUM(values(REQUEST_VIDEO_EVENT_TYPES)),
allowNull: false
},
count: {
type: DataTypes.INTEGER,
allowNull: false,
validate: {
countValid: function (value) {
const res = isVideoEventCountValid(value)
if (res === false) throw new Error('Video event count is not valid.')
}
}
}
},
{
updatedAt: false,
indexes: [
{
fields: [ 'videoId' ]
}
]
}
)
const classMethods = [
associate,
listWithLimitAndRandom,
countTotalRequests,
removeAll,
removeByRequestIdsAndPod
]
addMethodsToModel(RequestVideoEvent, classMethods)
return RequestVideoEvent
}
// ------------------------------ STATICS ------------------------------
function associate (models) {
RequestVideoEvent.belongsTo(models.Video, {
foreignKey: {
name: 'videoId',
allowNull: false
},
onDelete: 'CASCADE'
})
}
countTotalRequests = function () {
const query = {}
return RequestVideoEvent.count(query)
}
listWithLimitAndRandom = function (limitPods: number, limitRequestsPerPod: number) {
const Pod = db.Pod
// We make a join between videos and authors to find the podId of our video event requests
const podJoins = 'INNER JOIN "VideoChannels" ON "VideoChannels"."authorId" = "Authors"."id" ' +
'INNER JOIN "Videos" ON "Videos"."channelId" = "VideoChannels"."id" ' +
'INNER JOIN "RequestVideoEvents" ON "RequestVideoEvents"."videoId" = "Videos"."id"'
return Pod.listRandomPodIdsWithRequest(limitPods, 'Authors', podJoins).then(podIds => {
// We don't have friends that have requests
if (podIds.length === 0) return []
const query = {
order: [
[ 'id', 'ASC' ]
],
include: [
{
model: RequestVideoEvent['sequelize'].models.Video,
include: [
{
model: RequestVideoEvent['sequelize'].models.VideoChannel,
include: [
{
model: RequestVideoEvent['sequelize'].models.Author,
include: [
{
model: RequestVideoEvent['sequelize'].models.Pod,
where: {
id: {
[Sequelize.Op.in]: podIds
}
}
}
]
}
]
}
]
}
]
}
return RequestVideoEvent.findAll(query).then(requests => {
const requestsGrouped = groupAndTruncateRequests(requests, limitRequestsPerPod)
return requestsGrouped
})
})
}
removeByRequestIdsAndPod = function (ids: number[], podId: number) {
const query = {
where: {
id: {
[Sequelize.Op.in]: ids
}
},
include: [
{
model: RequestVideoEvent['sequelize'].models.Video,
include: [
{
model: RequestVideoEvent['sequelize'].models.VideoChannel,
include: [
{
model: RequestVideoEvent['sequelize'].models.Author,
where: {
podId
}
}
]
}
]
}
]
}
return RequestVideoEvent.destroy(query)
}
removeAll = function () {
// Delete all requests
return RequestVideoEvent.truncate({ cascade: true })
}
// ---------------------------------------------------------------------------
function groupAndTruncateRequests (events: RequestVideoEventInstance[], limitRequestsPerPod: number) {
const eventsGrouped: RequestsVideoEventGrouped = {}
events.forEach(event => {
const pod = event.Video.VideoChannel.Author.Pod
if (!eventsGrouped[pod.id]) eventsGrouped[pod.id] = []
if (eventsGrouped[pod.id].length < limitRequestsPerPod) {
eventsGrouped[pod.id].push({
id: event.id,
type: event.type,
count: event.count,
video: event.Video,
pod
})
}
})
return eventsGrouped
}

View File

@ -1,48 +0,0 @@
import * as Sequelize from 'sequelize'
import * as Promise from 'bluebird'
import { AbstractRequestClass, AbstractRequestToPodClass } from './abstract-request-interface'
import { VideoInstance } from '../video/video-interface'
import { PodInstance } from '../pod/pod-interface'
import { RequestVideoQaduType } from '../../../shared/models/request-scheduler.model'
export type RequestsVideoQaduGrouped = {
[ podId: number ]: {
request: RequestVideoQaduInstance
video: VideoInstance
pod: PodInstance
}
}
export namespace RequestVideoQaduMethods {
export type CountTotalRequests = () => Promise<number>
export type ListWithLimitAndRandom = (limitPods: number, limitRequestsPerPod: number) => Promise<RequestsVideoQaduGrouped>
export type RemoveByRequestIdsAndPod = (ids: number[], podId: number) => Promise<number>
export type RemoveAll = () => Promise<void>
}
export interface RequestVideoQaduClass extends AbstractRequestClass<RequestsVideoQaduGrouped>, AbstractRequestToPodClass {
countTotalRequests: RequestVideoQaduMethods.CountTotalRequests
listWithLimitAndRandom: RequestVideoQaduMethods.ListWithLimitAndRandom
removeByRequestIdsAndPod: RequestVideoQaduMethods.RemoveByRequestIdsAndPod
removeAll: RequestVideoQaduMethods.RemoveAll
}
export interface RequestVideoQaduAttributes {
type: RequestVideoQaduType
}
export interface RequestVideoQaduInstance
extends RequestVideoQaduClass, RequestVideoQaduAttributes, Sequelize.Instance<RequestVideoQaduAttributes> {
id: number
Pod: PodInstance
Video: VideoInstance
}
export interface RequestVideoQaduModel
extends RequestVideoQaduClass, Sequelize.Model<RequestVideoQaduInstance, RequestVideoQaduAttributes> {}

View File

@ -1,159 +0,0 @@
/*
Request Video for Quick And Dirty Updates like:
- views
- likes
- dislikes
We can't put it in the same system than basic requests for efficiency.
Moreover we don't want to slow down the basic requests with a lot of views/likes/dislikes requests.
So we put it an independant request scheduler.
*/
import { values } from 'lodash'
import * as Sequelize from 'sequelize'
import { database as db } from '../../initializers/database'
import { REQUEST_VIDEO_QADU_TYPES } from '../../initializers'
import { addMethodsToModel } from '../utils'
import {
RequestVideoQaduInstance,
RequestVideoQaduAttributes,
RequestVideoQaduMethods
} from './request-video-qadu-interface'
let RequestVideoQadu: Sequelize.Model<RequestVideoQaduInstance, RequestVideoQaduAttributes>
let countTotalRequests: RequestVideoQaduMethods.CountTotalRequests
let listWithLimitAndRandom: RequestVideoQaduMethods.ListWithLimitAndRandom
let removeByRequestIdsAndPod: RequestVideoQaduMethods.RemoveByRequestIdsAndPod
let removeAll: RequestVideoQaduMethods.RemoveAll
export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
RequestVideoQadu = sequelize.define<RequestVideoQaduInstance, RequestVideoQaduAttributes>('RequestVideoQadu',
{
type: {
type: DataTypes.ENUM(values(REQUEST_VIDEO_QADU_TYPES)),
allowNull: false
}
},
{
timestamps: false,
indexes: [
{
fields: [ 'podId' ]
},
{
fields: [ 'videoId' ]
}
]
}
)
const classMethods = [
associate,
listWithLimitAndRandom,
countTotalRequests,
removeAll,
removeByRequestIdsAndPod
]
addMethodsToModel(RequestVideoQadu, classMethods)
return RequestVideoQadu
}
// ------------------------------ STATICS ------------------------------
function associate (models) {
RequestVideoQadu.belongsTo(models.Pod, {
foreignKey: {
name: 'podId',
allowNull: false
},
onDelete: 'CASCADE'
})
RequestVideoQadu.belongsTo(models.Video, {
foreignKey: {
name: 'videoId',
allowNull: false
},
onDelete: 'CASCADE'
})
}
countTotalRequests = function () {
const query = {}
return RequestVideoQadu.count(query)
}
listWithLimitAndRandom = function (limitPods: number, limitRequestsPerPod: number) {
const Pod = db.Pod
const tableJoin = ''
return Pod.listRandomPodIdsWithRequest(limitPods, 'RequestVideoQadus', tableJoin).then(podIds => {
// We don't have friends that have requests
if (podIds.length === 0) return []
const query = {
include: [
{
model: RequestVideoQadu['sequelize'].models.Pod,
where: {
id: {
[Sequelize.Op.in]: podIds
}
}
},
{
model: RequestVideoQadu['sequelize'].models.Video
}
]
}
return RequestVideoQadu.findAll(query).then(requests => {
const requestsGrouped = groupAndTruncateRequests(requests, limitRequestsPerPod)
return requestsGrouped
})
})
}
removeByRequestIdsAndPod = function (ids: number[], podId: number) {
const query = {
where: {
id: {
[Sequelize.Op.in]: ids
},
podId
}
}
return RequestVideoQadu.destroy(query)
}
removeAll = function () {
// Delete all requests
return RequestVideoQadu.truncate({ cascade: true })
}
// ---------------------------------------------------------------------------
function groupAndTruncateRequests (requests: RequestVideoQaduInstance[], limitRequestsPerPod: number) {
const requestsGrouped = {}
requests.forEach(request => {
const pod = request.Pod
if (!requestsGrouped[pod.id]) requestsGrouped[pod.id] = []
if (requestsGrouped[pod.id].length < limitRequestsPerPod) {
requestsGrouped[pod.id].push({
request: request,
video: request.Video,
pod
})
}
})
return requestsGrouped
}

View File

@ -1,144 +0,0 @@
import { values } from 'lodash'
import * as Sequelize from 'sequelize'
import { database as db } from '../../initializers/database'
import { REQUEST_ENDPOINTS } from '../../initializers'
import { addMethodsToModel } from '../utils'
import {
RequestInstance,
RequestAttributes,
RequestMethods,
RequestsGrouped
} from './request-interface'
let Request: Sequelize.Model<RequestInstance, RequestAttributes>
let countTotalRequests: RequestMethods.CountTotalRequests
let listWithLimitAndRandom: RequestMethods.ListWithLimitAndRandom
let removeWithEmptyTo: RequestMethods.RemoveWithEmptyTo
let removeAll: RequestMethods.RemoveAll
export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
Request = sequelize.define<RequestInstance, RequestAttributes>('Request',
{
request: {
type: DataTypes.JSON,
allowNull: false
},
endpoint: {
type: DataTypes.ENUM(values(REQUEST_ENDPOINTS)),
allowNull: false
}
}
)
const classMethods = [
associate,
listWithLimitAndRandom,
countTotalRequests,
removeAll,
removeWithEmptyTo
]
addMethodsToModel(Request, classMethods)
return Request
}
// ------------------------------ STATICS ------------------------------
function associate (models) {
Request.belongsToMany(models.Pod, {
foreignKey: {
name: 'requestId',
allowNull: false
},
through: models.RequestToPod,
onDelete: 'CASCADE'
})
}
countTotalRequests = function () {
// We need to include Pod because there are no cascade delete when a pod is removed
// So we could count requests that do not have existing pod anymore
const query = {
include: [ Request['sequelize'].models.Pod ]
}
return Request.count(query)
}
listWithLimitAndRandom = function (limitPods: number, limitRequestsPerPod: number) {
const Pod = db.Pod
const tableJoin = ''
return Pod.listRandomPodIdsWithRequest(limitPods, 'RequestToPods', tableJoin).then(podIds => {
// We don't have friends that have requests
if (podIds.length === 0) return []
// The first x requests of these pods
// It is very important to sort by id ASC to keep the requests order!
const query = {
order: [
[ 'id', 'ASC' ]
],
include: [
{
model: Request['sequelize'].models.Pod,
where: {
id: {
[Sequelize.Op.in]: podIds
}
}
}
]
}
return Request.findAll(query).then(requests => {
const requestsGrouped = groupAndTruncateRequests(requests, limitRequestsPerPod)
return requestsGrouped
})
})
}
removeAll = function () {
// Delete all requests
return Request.truncate({ cascade: true })
}
removeWithEmptyTo = function () {
const query = {
where: {
id: {
[Sequelize.Op.notIn]: [
Sequelize.literal('SELECT "requestId" FROM "RequestToPods"')
]
}
}
}
return Request.destroy(query)
}
// ---------------------------------------------------------------------------
function groupAndTruncateRequests (requests: RequestInstance[], limitRequestsPerPod: number) {
const requestsGrouped: RequestsGrouped = {}
requests.forEach(request => {
request.Pods.forEach(pod => {
if (!requestsGrouped[pod.id]) requestsGrouped[pod.id] = []
if (requestsGrouped[pod.id].length < limitRequestsPerPod) {
requestsGrouped[pod.id].push({
request,
pod
})
}
})
})
return requestsGrouped
}

View File

@ -1,4 +1,3 @@
export * from './author-interface'
export * from './tag-interface'
export * from './video-abuse-interface'
export * from './video-blacklist-interface'