-import { Component, OnInit } from '@angular/core'
+import { Component } from '@angular/core'
import { NotificationsService } from 'angular2-notifications'
import { SortMeta } from 'primeng/primeng'
-
-import { ConfirmService } from '../../../core'
-import { RestTable, RestPagination } from '../../../shared'
-import { Pod } from '../../../../../../shared'
+import { AccountFollow } from '../../../../../../shared/models/accounts/follow.model'
+import { RestPagination, RestTable } from '../../../shared'
import { FollowService } from '../shared'
@Component({
styleUrls: [ './followers-list.component.scss' ]
})
export class FollowersListComponent extends RestTable {
- followers: Pod[] = []
+ followers: AccountFollow[] = []
totalRecords = 0
rowsPerPage = 10
sort: SortMeta = { field: 'createdAt', order: 1 }
</div>
<div *ngIf="canMakeFriends() === false" class="alert alert-warning">
- It seems that you are not on a HTTPS pod. Your webserver need to have TLS activated in order to follow servers.
+ It seems that you are not on a HTTPS server. Your webserver need to have TLS activated in order to follow servers.
</div>
<input type="submit" value="Add following" class="btn btn-default" [disabled]="!isFormValid()">
this.followService.follow(notEmptyHosts).subscribe(
status => {
this.notificationsService.success('Success', 'Follow request(s) sent!')
- // Wait requests between pods
- setTimeout(() => this.router.navigate([ '/admin/friends/list' ]), 1000)
},
err => this.notificationsService.error('Error', err.message)
-import { Component, OnInit } from '@angular/core'
-
+import { Component } from '@angular/core'
import { NotificationsService } from 'angular2-notifications'
import { SortMeta } from 'primeng/primeng'
-
-import { ConfirmService } from '../../../core'
-import { RestTable, RestPagination } from '../../../shared'
-import { Pod } from '../../../../../../shared'
+import { AccountFollow } from '../../../../../../shared/models/accounts/follow.model'
+import { RestPagination, RestTable } from '../../../shared'
import { FollowService } from '../shared'
@Component({
templateUrl: './following-list.component.html'
})
export class FollowingListComponent extends RestTable {
- following: Pod[] = []
+ following: AccountFollow[] = []
totalRecords = 0
rowsPerPage = 10
sort: SortMeta = { field: 'createdAt', order: 1 }
import { SortMeta } from 'primeng/primeng'
import { RestExtractor, RestPagination, RestService } from '../../../shared'
-import { Pod, ResultList } from '../../../../../../shared'
+import { AccountFollow, ResultList } from '../../../../../../shared'
@Injectable()
export class FollowService {
private restExtractor: RestExtractor
) {}
- getFollowing (pagination: RestPagination, sort: SortMeta): Observable<ResultList<Pod>> {
+ getFollowing (pagination: RestPagination, sort: SortMeta): Observable<ResultList<AccountFollow>> {
let params = new HttpParams()
params = this.restService.addRestGetParams(params, pagination, sort)
.catch(res => this.restExtractor.handleError(res))
}
- getFollowers (pagination: RestPagination, sort: SortMeta): Observable<ResultList<Pod>> {
+ getFollowers (pagination: RestPagination, sort: SortMeta): Observable<ResultList<AccountFollow>> {
let params = new HttpParams()
params = this.restService.addRestGetParams(params, pagination, sort)
>
<p-column field="id" header="ID" [sortable]="true"></p-column>
<p-column field="reason" header="Reason"></p-column>
- <p-column field="reporterPodHost" header="Reporter pod host"></p-column>
+ <p-column field="reporterServerHost" header="Reporter server host"></p-column>
<p-column field="reporterUsername" header="Reporter username"></p-column>
<p-column header="Video" styleClass="action-cell">
<ng-template pTemplate="body" let-videoAbuse="rowData">
<div class="alert alert-danger">
The video load seems to be abnormally long.
<ul>
- <li>Maybe the server {{ video.podHost }} is down :(</li>
+ <li>Maybe the server {{ video.serverHost }} is down :(</li>
<li>
If not, you can report an issue on
<a href="https://github.com/Chocobozzz/PeerTube/issues" title="Report an issue">
uuid: string
isLocal: boolean
name: string
- podHost: string
+ serverHost: string
tags: string[]
thumbnailPath: string
thumbnailUrl: string
uuid: string
isLocal: boolean
name: string
- podHost: string
+ serverHost: string
tags: string[]
thumbnailPath: string
thumbnailUrl: string
dislikes: number
nsfw: boolean
- private static createByString (account: string, podHost: string) {
- return account + '@' + podHost
+ private static createByString (account: string, serverHost: string) {
+ return account + '@' + serverHost
}
private static createDurationString (duration: number) {
this.uuid = hash.uuid
this.isLocal = hash.isLocal
this.name = hash.name
- this.podHost = hash.podHost
+ this.serverHost = hash.serverHost
this.tags = hash.tags
this.thumbnailPath = hash.thumbnailPath
this.thumbnailUrl = absoluteAPIUrl + hash.thumbnailPath
this.dislikes = hash.dislikes
this.nsfw = hash.nsfw
- this.by = Video.createByString(hash.account, hash.podHost)
+ this.by = Video.createByString(hash.account, hash.serverHost)
}
isVideoNSFWForUser (user: User) {
+++ /dev/null
-// import * as express from 'express'
-//
-// import { database as db } from '../../../initializers/database'
-// import {
-// checkSignature,
-// signatureValidator,
-// setBodyHostPort,
-// remotePodsAddValidator,
-// asyncMiddleware
-// } from '../../../middlewares'
-// import { sendOwnedDataToPod } from '../../../lib'
-// import { getMyPublicCert, getFormattedObjects } from '../../../helpers'
-// import { CONFIG } from '../../../initializers'
-// import { PodInstance } from '../../../models'
-// import { PodSignature, Pod as FormattedPod } from '../../../../shared'
-//
-// const remotePodsRouter = express.Router()
-//
-// remotePodsRouter.post('/remove',
-// signatureValidator,
-// checkSignature,
-// asyncMiddleware(removePods)
-// )
-//
-// remotePodsRouter.post('/list',
-// asyncMiddleware(remotePodsList)
-// )
-//
-// remotePodsRouter.post('/add',
-// setBodyHostPort, // We need to modify the host before running the validator!
-// remotePodsAddValidator,
-// asyncMiddleware(addPods)
-// )
-//
-// // ---------------------------------------------------------------------------
-//
-// export {
-// remotePodsRouter
-// }
-//
-// // ---------------------------------------------------------------------------
-//
-// async function addPods (req: express.Request, res: express.Response, next: express.NextFunction) {
-// const information = req.body
-//
-// const pod = db.Pod.build(information)
-// const podCreated = await pod.save()
-//
-// await sendOwnedDataToPod(podCreated.id)
-//
-// const cert = await getMyPublicCert()
-// return res.json({ cert, email: CONFIG.ADMIN.EMAIL })
-// }
-//
-// async function remotePodsList (req: express.Request, res: express.Response, next: express.NextFunction) {
-// const pods = await db.Pod.list()
-//
-// return res.json(getFormattedObjects<FormattedPod, PodInstance>(pods, pods.length))
-// }
-//
-// async function removePods (req: express.Request, res: express.Response, next: express.NextFunction) {
-// const signature: PodSignature = req.body.signature
-// const host = signature.host
-//
-// const pod = await db.Pod.loadByHost(host)
-// await pod.destroy()
-//
-// return res.type('json').status(204).end()
-// }
+++ /dev/null
-// import * as express from 'express'
-// import * as Bluebird from 'bluebird'
-// import * as Sequelize from 'sequelize'
-//
-// import { database as db } from '../../../initializers/database'
-// import {
-// REQUEST_ENDPOINT_ACTIONS,
-// REQUEST_ENDPOINTS,
-// REQUEST_VIDEO_EVENT_TYPES,
-// REQUEST_VIDEO_QADU_TYPES
-// } from '../../../initializers'
-// import {
-// checkSignature,
-// signatureValidator,
-// remoteVideosValidator,
-// remoteQaduVideosValidator,
-// remoteEventsVideosValidator
-// } from '../../../middlewares'
-// import { logger, retryTransactionWrapper, resetSequelizeInstance } from '../../../helpers'
-// import { quickAndDirtyUpdatesVideoToFriends, fetchVideoChannelByHostAndUUID } from '../../../lib'
-// import { PodInstance, VideoFileInstance } from '../../../models'
-// import {
-// RemoteVideoRequest,
-// RemoteVideoCreateData,
-// RemoteVideoUpdateData,
-// RemoteVideoRemoveData,
-// RemoteVideoReportAbuseData,
-// RemoteQaduVideoRequest,
-// RemoteQaduVideoData,
-// RemoteVideoEventRequest,
-// RemoteVideoEventData,
-// RemoteVideoChannelCreateData,
-// RemoteVideoChannelUpdateData,
-// RemoteVideoChannelRemoveData,
-// RemoteVideoAccountRemoveData,
-// RemoteVideoAccountCreateData
-// } from '../../../../shared'
-// import { VideoInstance } from '../../../models/video/video-interface'
-//
-// const ENDPOINT_ACTIONS = REQUEST_ENDPOINT_ACTIONS[REQUEST_ENDPOINTS.VIDEOS]
-//
-// // Functions to call when processing a remote request
-// // FIXME: use RemoteVideoRequestType as id type
-// const functionsHash: { [ id: string ]: (...args) => Promise<any> } = {}
-// functionsHash[ENDPOINT_ACTIONS.ADD_VIDEO] = addRemoteVideoRetryWrapper
-// functionsHash[ENDPOINT_ACTIONS.UPDATE_VIDEO] = updateRemoteVideoRetryWrapper
-// functionsHash[ENDPOINT_ACTIONS.REMOVE_VIDEO] = removeRemoteVideoRetryWrapper
-// functionsHash[ENDPOINT_ACTIONS.ADD_CHANNEL] = addRemoteVideoChannelRetryWrapper
-// functionsHash[ENDPOINT_ACTIONS.UPDATE_CHANNEL] = updateRemoteVideoChannelRetryWrapper
-// functionsHash[ENDPOINT_ACTIONS.REMOVE_CHANNEL] = removeRemoteVideoChannelRetryWrapper
-// functionsHash[ENDPOINT_ACTIONS.REPORT_ABUSE] = reportAbuseRemoteVideoRetryWrapper
-// functionsHash[ENDPOINT_ACTIONS.ADD_ACCOUNT] = addRemoteVideoAccountRetryWrapper
-// functionsHash[ENDPOINT_ACTIONS.REMOVE_ACCOUNT] = removeRemoteVideoAccountRetryWrapper
-//
-// const remoteVideosRouter = express.Router()
-//
-// remoteVideosRouter.post('/',
-// signatureValidator,
-// checkSignature,
-// remoteVideosValidator,
-// remoteVideos
-// )
-//
-// remoteVideosRouter.post('/qadu',
-// signatureValidator,
-// checkSignature,
-// remoteQaduVideosValidator,
-// remoteVideosQadu
-// )
-//
-// remoteVideosRouter.post('/events',
-// signatureValidator,
-// checkSignature,
-// remoteEventsVideosValidator,
-// remoteVideosEvents
-// )
-//
-// // ---------------------------------------------------------------------------
-//
-// export {
-// remoteVideosRouter
-// }
-//
-// // ---------------------------------------------------------------------------
-//
-// function remoteVideos (req: express.Request, res: express.Response, next: express.NextFunction) {
-// const requests: RemoteVideoRequest[] = req.body.data
-// const fromPod = res.locals.secure.pod
-//
-// // We need to process in the same order to keep consistency
-// Bluebird.each(requests, request => {
-// const data = request.data
-//
-// // Get the function we need to call in order to process the request
-// const fun = functionsHash[request.type]
-// if (fun === undefined) {
-// logger.error('Unknown remote request type %s.', request.type)
-// return
-// }
-//
-// return fun.call(this, data, fromPod)
-// })
-// .catch(err => logger.error('Error managing remote videos.', err))
-//
-// // Don't block the other pod
-// return res.type('json').status(204).end()
-// }
-//
-// function remoteVideosQadu (req: express.Request, res: express.Response, next: express.NextFunction) {
-// const requests: RemoteQaduVideoRequest[] = req.body.data
-// const fromPod = res.locals.secure.pod
-//
-// Bluebird.each(requests, request => {
-// const videoData = request.data
-//
-// return quickAndDirtyUpdateVideoRetryWrapper(videoData, fromPod)
-// })
-// .catch(err => logger.error('Error managing remote videos.', err))
-//
-// return res.type('json').status(204).end()
-// }
-//
-// function remoteVideosEvents (req: express.Request, res: express.Response, next: express.NextFunction) {
-// const requests: RemoteVideoEventRequest[] = req.body.data
-// const fromPod = res.locals.secure.pod
-//
-// Bluebird.each(requests, request => {
-// const eventData = request.data
-//
-// return processVideosEventsRetryWrapper(eventData, fromPod)
-// })
-// .catch(err => logger.error('Error managing remote videos.', err))
-//
-// return res.type('json').status(204).end()
-// }
-//
-// async function processVideosEventsRetryWrapper (eventData: RemoteVideoEventData, fromPod: PodInstance) {
-// const options = {
-// arguments: [ eventData, fromPod ],
-// errorMessage: 'Cannot process videos events with many retries.'
-// }
-//
-// await retryTransactionWrapper(processVideosEvents, options)
-// }
-//
-// async function processVideosEvents (eventData: RemoteVideoEventData, fromPod: PodInstance) {
-// await db.sequelize.transaction(async t => {
-// const sequelizeOptions = { transaction: t }
-// const videoInstance = await fetchLocalVideoByUUID(eventData.uuid, t)
-//
-// let columnToUpdate
-// let qaduType
-//
-// switch (eventData.eventType) {
-// case REQUEST_VIDEO_EVENT_TYPES.VIEWS:
-// columnToUpdate = 'views'
-// qaduType = REQUEST_VIDEO_QADU_TYPES.VIEWS
-// break
-//
-// case REQUEST_VIDEO_EVENT_TYPES.LIKES:
-// columnToUpdate = 'likes'
-// qaduType = REQUEST_VIDEO_QADU_TYPES.LIKES
-// break
-//
-// case REQUEST_VIDEO_EVENT_TYPES.DISLIKES:
-// columnToUpdate = 'dislikes'
-// qaduType = REQUEST_VIDEO_QADU_TYPES.DISLIKES
-// break
-//
-// default:
-// throw new Error('Unknown video event type.')
-// }
-//
-// const query = {}
-// query[columnToUpdate] = eventData.count
-//
-// await videoInstance.increment(query, sequelizeOptions)
-//
-// const qadusParams = [
-// {
-// videoId: videoInstance.id,
-// type: qaduType
-// }
-// ]
-// await quickAndDirtyUpdatesVideoToFriends(qadusParams, t)
-// })
-//
-// logger.info('Remote video event processed for video with uuid %s.', eventData.uuid)
-// }
-//
-// async function quickAndDirtyUpdateVideoRetryWrapper (videoData: RemoteQaduVideoData, fromPod: PodInstance) {
-// const options = {
-// arguments: [ videoData, fromPod ],
-// errorMessage: 'Cannot update quick and dirty the remote video with many retries.'
-// }
-//
-// await retryTransactionWrapper(quickAndDirtyUpdateVideo, options)
-// }
-//
-// async function quickAndDirtyUpdateVideo (videoData: RemoteQaduVideoData, fromPod: PodInstance) {
-// let videoUUID = ''
-//
-// await db.sequelize.transaction(async t => {
-// const videoInstance = await fetchVideoByHostAndUUID(fromPod.host, videoData.uuid, t)
-// const sequelizeOptions = { transaction: t }
-//
-// videoUUID = videoInstance.uuid
-//
-// if (videoData.views) {
-// videoInstance.set('views', videoData.views)
-// }
-//
-// if (videoData.likes) {
-// videoInstance.set('likes', videoData.likes)
-// }
-//
-// if (videoData.dislikes) {
-// videoInstance.set('dislikes', videoData.dislikes)
-// }
-//
-// await videoInstance.save(sequelizeOptions)
-// })
-//
-// logger.info('Remote video with uuid %s quick and dirty updated', videoUUID)
-// }
-//
-// async function reportAbuseRemoteVideoRetryWrapper (reportData: RemoteVideoReportAbuseData, fromPod: PodInstance) {
-// const options = {
-// arguments: [ reportData, fromPod ],
-// errorMessage: 'Cannot create remote abuse video with many retries.'
-// }
-//
-// await retryTransactionWrapper(reportAbuseRemoteVideo, options)
-// }
-//
-// async function reportAbuseRemoteVideo (reportData: RemoteVideoReportAbuseData, fromPod: PodInstance) {
-// logger.debug('Reporting remote abuse for video %s.', reportData.videoUUID)
-//
-// await db.sequelize.transaction(async t => {
-// const videoInstance = await fetchLocalVideoByUUID(reportData.videoUUID, t)
-// const videoAbuseData = {
-// reporterUsername: reportData.reporterUsername,
-// reason: reportData.reportReason,
-// reporterPodId: fromPod.id,
-// videoId: videoInstance.id
-// }
-//
-// await db.VideoAbuse.create(videoAbuseData)
-//
-// })
-//
-// logger.info('Remote abuse for video uuid %s created', reportData.videoUUID)
-// }
-//
-// async function fetchLocalVideoByUUID (id: string, t: Sequelize.Transaction) {
-// try {
-// const video = await db.Video.loadLocalVideoByUUID(id, t)
-//
-// if (!video) throw new Error('Video ' + id + ' not found')
-//
-// return video
-// } catch (err) {
-// logger.error('Cannot load owned video from id.', { error: err.stack, id })
-// throw err
-// }
-// }
-//
-// async function fetchVideoByHostAndUUID (podHost: string, uuid: string, t: Sequelize.Transaction) {
-// try {
-// const video = await db.Video.loadByHostAndUUID(podHost, uuid, t)
-// if (!video) throw new Error('Video not found')
-//
-// return video
-// } catch (err) {
-// logger.error('Cannot load video from host and uuid.', { error: err.stack, podHost, uuid })
-// throw err
-// }
-// }
import { oauthClientsRouter } from './oauth-clients'
import { configRouter } from './config'
-import { applicationRouter } from './application'
+import { applicationRouter } from './server'
import { usersRouter } from './users'
import { videosRouter } from './videos'
import { sendFollow } from '../../../lib/activitypub/send-request'
import { asyncMiddleware, paginationValidator, setFollowersSort, setPagination } from '../../../middlewares'
import { authenticate } from '../../../middlewares/oauth'
-import { setBodyHostsPort } from '../../../middlewares/pods'
+import { setBodyHostsPort } from '../../../middlewares/servers'
import { setFollowingSort } from '../../../middlewares/sort'
import { ensureUserHasRight } from '../../../middlewares/user-right'
-import { followValidator } from '../../../middlewares/validators/pods'
+import { followValidator } from '../../../middlewares/validators/servers'
import { followersSortValidator, followingSortValidator } from '../../../middlewares/validators/sort'
const applicationFollowsRouter = express.Router()
reporterUsername,
reason: body.reason,
videoId: videoInstance.id,
- reporterPodId: null // This is our pod that reported this abuse
+ reporterServerId: null // This is our server that reported this abuse
}
await db.sequelize.transaction(async t => {
const abuse = await db.VideoAbuse.create(abuseToCreate, { transaction: t })
- // We send the information to the destination pod
+ // We send the information to the destination server
if (videoInstance.isOwned() === false) {
const reportData = {
reporterUsername,
}
// await friends.reportAbuseVideoToFriend(reportData, videoInstance, t)
- // TODO: send abuse to origin pod
+ // TODO: send abuse to origin server
}
})
// Let transcoding job send the video to friends because the video file extension might change
if (CONFIG.TRANSCODING.ENABLED === true) return undefined
- // Don't send video to remote pods, it is private
+ // Don't send video to remote servers, it is private
if (video.privacy === VideoPrivacy.PRIVATE) return undefined
await sendAddVideo(video, t)
await sendUpdateVideoChannel(videoInstance, t)
}
- // Video is not private anymore, send a create action to remote pods
+ // Video is not private anymore, send a create action to remote servers
if (wasPrivateVideo === true && videoInstance.privacy !== VideoPrivacy.PRIVATE) {
await sendAddVideo(videoInstance, t)
}
}
async function searchVideos (req: express.Request, res: express.Response, next: express.NextFunction) {
- const resultList = await db.Video.searchAndPopulateAccountAndPodAndTags(
+ const resultList = await db.Video.searchAndPopulateAccountAndServerAndTags(
req.params.value,
req.query.field,
req.query.start,
await videoInstance.increment(incrementQuery, sequelizeOptions)
if (videoInstance.isOwned() === false) {
- // TODO: Send a event to original pod
+ // TODO: Send a event to original server
} else {
// TODO: Send update to followers
}
// Let Angular application handle errors
if (validator.isUUID(videoId, 4)) {
- videoPromise = db.Video.loadByUUIDAndPopulateAccountAndPodAndTags(videoId)
+ videoPromise = db.Video.loadByUUIDAndPopulateAccountAndServerAndTags(videoId)
} else if (validator.isInt(videoId)) {
- videoPromise = db.Video.loadAndPopulateAccountAndPodAndTags(+videoId)
+ videoPromise = db.Video.loadAndPopulateAccountAndServerAndTags(+videoId)
} else {
return res.sendFile(indexPath)
}
const staticRouter = express.Router()
/*
- Cors is very important to let other pods access torrent and video files
+ Cors is very important to let other servers access torrent and video files
*/
const torrentsPhysicalPath = CONFIG.STORAGE.TORRENTS_DIR
// We don't have this account in our database, fetch it on remote
if (!account) {
- const res = await fetchRemoteAccountAndCreatePod(accountUrl)
+ const res = await fetchRemoteAccountAndCreateServer(accountUrl)
if (res === undefined) throw new Error('Cannot fetch remote account.')
// Save our new account in database
return account
}
-async function fetchRemoteAccountAndCreatePod (accountUrl: string) {
+async function fetchRemoteAccountAndCreateServer (accountUrl: string) {
const options = {
uri: accountUrl,
method: 'GET',
})
const accountHost = url.parse(account.url).host
- const podOptions = {
+ const serverOptions = {
where: {
host: accountHost
},
host: accountHost
}
}
- const [ pod ] = await db.Pod.findOrCreate(podOptions)
- account.set('podId', pod.id)
+ const [ server ] = await db.Server.findOrCreate(serverOptions)
+ account.set('serverId', server.id)
- return { account, pod }
+ return { account, server }
}
function fetchRemoteVideoPreview (video: VideoInstance) {
// FIXME: use url
- const host = video.VideoChannel.Account.Pod.host
+ const host = video.VideoChannel.Account.Server.host
const path = join(STATIC_PATHS.PREVIEWS, video.getPreviewName())
return request.get(REMOTE_SCHEME.HTTP + '://' + host + path)
// ---------------------------------------------------------------------------
export {
- fetchRemoteAccountAndCreatePod,
+ fetchRemoteAccountAndCreateServer,
activityPubContextify,
activityPubCollectionPagination,
getActivityPubUrl,
import { logger } from '../logger'
import { isUserUsernameValid } from './users'
-import { isHostValid } from './pods'
+import { isHostValid } from './servers'
function isAccountNameValid (value: string) {
return isUserUsernameValid(value)
export * from './activitypub'
export * from './misc'
-export * from './pods'
-export * from './pods'
+export * from './servers'
+export * from './servers'
export * from './users'
export * from './accounts'
export * from './video-channels'
function checkVideoExists (id: string, res: express.Response, callback: () => void) {
let promise: Promise<VideoInstance>
if (validator.isInt(id)) {
- promise = db.Video.loadAndPopulateAccountAndPodAndTags(+id)
+ promise = db.Video.loadAndPopulateAccountAndServerAndTags(+id)
} else { // UUID
- promise = db.Video.loadByUUIDAndPopulateAccountAndPodAndTags(id)
+ promise = db.Video.loadByUUIDAndPopulateAccountAndServerAndTags(id)
}
promise.then(video => {
import { isTestInstance } from './core-utils'
import { isActivityPubUrlValid } from './custom-validators'
import { WebFingerData } from '../../shared'
-import { fetchRemoteAccountAndCreatePod } from './activitypub'
+import { fetchRemoteAccountAndCreateServer } from './activitypub'
const webfinger = new WebFinger({
webfist_fallback: false,
throw new Error('Cannot find self link or href is not a valid URL.')
}
- const res = await fetchRemoteAccountAndCreatePod(selfLink.href)
- if (res === undefined) throw new Error('Cannot fetch and create pod of remote account ' + selfLink.href)
+ const res = await fetchRemoteAccountAndCreateServer(selfLink.href)
+ if (res === undefined) throw new Error('Cannot fetch and create server of remote account ' + selfLink.href)
return res.account
}
import {
VideoRateType,
- RequestEndpoint,
- RequestVideoEventType,
- RequestVideoQaduType,
- RemoteVideoRequestType,
JobState,
JobCategory
} from '../../shared/models'
// ---------------------------------------------------------------------------
-// Score a pod has when we create it as a friend
+// Score a server has when we create it as a friend
const FRIEND_SCORE = {
BASE: 100,
MAX: 1000
// ---------------------------------------------------------------------------
// Number of points we add/remove from a friend after a successful/bad request
-const PODS_SCORE = {
+const SERVERS_SCORE = {
PENALTY: -10,
BONUS: 10
}
OAUTH_LIFETIME,
OPENGRAPH_AND_OEMBED_COMMENT,
PAGINATION_COUNT_DEFAULT,
- PODS_SCORE,
+ SERVERS_SCORE,
PREVIEWS_SIZE,
REMOTE_SCHEME,
ACTIVITY_PUB_ACCEPT_HEADER,
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 { PodModel } from './../models/pod/pod-interface'
+import { ServerModel } from '../models/server/server-interface'
import { OAuthTokenModel } from './../models/oauth/oauth-token-interface'
import { OAuthClientModel } from './../models/oauth/oauth-client-interface'
import { JobModel } from './../models/job/job-interface'
Job?: JobModel,
OAuthClient?: OAuthClientModel,
OAuthToken?: OAuthTokenModel,
- Pod?: PodModel,
+ Server?: ServerModel,
Tag?: TagModel,
AccountVideoRate?: AccountVideoRateModel,
AccountFollow?: AccountFollowModel,
}
private async loadPreviews (key: string) {
- const video = await db.Video.loadByUUIDAndPopulateAccountAndPodAndTags(key)
+ const video = await db.Video.loadByUUIDAndPopulateAccountAndServerAndTags(key)
if (!video) return undefined
if (video.isOwned()) return join(CONFIG.STORAGE.PREVIEWS_DIR, video.getPreviewName())
import { TranscodingJobPayload } from './transcoding-job-scheduler'
async function process (data: TranscodingJobPayload, jobId: number) {
- const video = await db.Video.loadByUUIDAndPopulateAccountAndPodAndTags(data.videoUUID)
+ const video = await db.Video.loadByUUIDAndPopulateAccountAndServerAndTags(data.videoUUID)
// No video, maybe deleted?
if (!video) {
logger.info('Do not process job %d, video does not exist.', jobId, { videoUUID: video.uuid })
logger.info('Job %d is a success.', jobId)
// Maybe the video changed in database, refresh it
- const videoDatabase = await db.Video.loadByUUIDAndPopulateAccountAndPodAndTags(video.uuid)
+ const videoDatabase = await db.Video.loadByUUIDAndPopulateAccountAndServerAndTags(video.uuid)
// Video does not exist anymore
if (!videoDatabase) return undefined
import { sendUpdateVideo } from '../../activitypub/send-request'
async function process (data: { videoUUID: string, resolution: VideoResolution }, jobId: number) {
- const video = await db.Video.loadByUUIDAndPopulateAccountAndPodAndTags(data.videoUUID)
+ const video = await db.Video.loadByUUIDAndPopulateAccountAndServerAndTags(data.videoUUID)
// No video, maybe deleted?
if (!video) {
logger.info('Do not process job %d, video does not exist.', jobId, { videoUUID: video.uuid })
logger.info('Job %d is a success.', jobId)
// Maybe the video changed in database, refresh it
- const videoDatabase = await db.Video.loadByUUIDAndPopulateAccountAndPodAndTags(video.uuid)
+ const videoDatabase = await db.Video.loadByUUIDAndPopulateAccountAndServerAndTags(video.uuid)
// Video does not exist anymore
if (!videoDatabase) return undefined
followingUrl: url + '/following',
userId,
applicationId,
- podId: null // It is our pod
+ serverId: null // It is our server
})
return accountInstance.save({ transaction: t })
return videoChannelCreated
}
-async function fetchVideoChannelByHostAndUUID (podHost: string, uuid: string, t: Sequelize.Transaction) {
+async function fetchVideoChannelByHostAndUUID (serverHost: string, uuid: string, t: Sequelize.Transaction) {
try {
- const videoChannel = await db.VideoChannel.loadByHostAndUUID(podHost, uuid, t)
+ const videoChannel = await db.VideoChannel.loadByHostAndUUID(serverHost, uuid, t)
if (!videoChannel) throw new Error('Video channel not found')
return videoChannel
} catch (err) {
- logger.error('Cannot load video channel from host and uuid.', { error: err.stack, podHost, uuid })
+ logger.error('Cannot load video channel from host and uuid.', { error: err.stack, serverHost, uuid })
throw err
}
}
import { NextFunction, Request, Response, RequestHandler } from 'express'
import { ActivityPubSignature } from '../../shared'
import { isSignatureVerified, logger } from '../helpers'
-import { fetchRemoteAccountAndCreatePod } from '../helpers/activitypub'
+import { fetchRemoteAccountAndCreateServer } from '../helpers/activitypub'
import { database as db, ACTIVITY_PUB_ACCEPT_HEADER } from '../initializers'
import { each, eachSeries, waterfall } from 'async'
// We don't have this account in our database, fetch it on remote
if (!account) {
- const accountResult = await fetchRemoteAccountAndCreatePod(signatureObject.creator)
+ const accountResult = await fetchRemoteAccountAndCreateServer(signatureObject.creator)
if (!accountResult) {
return res.sendStatus(403)
export * from './async'
export * from './oauth'
export * from './pagination'
-export * from './pods'
+export * from './servers'
export * from './search'
export * from './sort'
export * from './user-right'
import { SortType } from '../helpers'
import { database } from '../initializers'
-function setPodsSort (req: express.Request, res: express.Response, next: express.NextFunction) {
- if (!req.query.sort) req.query.sort = '-createdAt'
-
- return next()
-}
-
function setUsersSort (req: express.Request, res: express.Response, next: express.NextFunction) {
if (!req.query.sort) req.query.sort = '-createdAt'
// ---------------------------------------------------------------------------
export {
- setPodsSort,
setUsersSort,
setVideoAbusesSort,
setVideoChannelsSort,
export * from './oembed'
export * from './activitypub'
export * from './pagination'
-export * from './pods'
+export * from './servers'
export * from './sort'
export * from './users'
export * from './videos'
import * as express from 'express'
import { body } from 'express-validator/check'
-import { isEachUniqueHostValid } from '../../helpers/custom-validators/pods'
+import { isEachUniqueHostValid } from '../../helpers/custom-validators/servers'
import { isTestInstance } from '../../helpers/core-utils'
import { CONFIG } from '../../initializers/constants'
import { logger } from '../../helpers/logger'
// We need to make additional checks
if (res.locals.videoChannel.isOwned() === false) {
return res.status(403)
- .json({ error: 'Cannot update video channel of another pod' })
+ .json({ error: 'Cannot update video channel of another server' })
.end()
}
// Retrieve the user who did the request
if (res.locals.videoChannel.isOwned() === false) {
return res.status(403)
- .json({ error: 'Cannot remove video channel of another pod.' })
+ .json({ error: 'Cannot remove video channel of another server.' })
.end()
}
// We need to make additional checks
if (video.isOwned() === false) {
return res.status(403)
- .json({ error: 'Cannot update video of another pod' })
+ .json({ error: 'Cannot update video of another server' })
.end()
}
// Retrieve the user who did the request
if (res.locals.video.isOwned() === false) {
return res.status(403)
- .json({ error: 'Cannot remove video of another pod, blacklist it' })
+ .json({ error: 'Cannot remove video of another server, blacklist it' })
.end()
}
model: AccountFollow['sequelize'].models.Account,
as: 'AccountFollowing',
required: true,
- include: [ AccountFollow['sequelize'].models.Pod ]
+ include: [ AccountFollow['sequelize'].models.Server ]
}
]
}
model: AccountFollow[ 'sequelize' ].models.Account,
required: true,
as: 'AccountFollower',
- include: [ AccountFollow['sequelize'].models.Pod ]
+ include: [ AccountFollow['sequelize'].models.Server ]
},
{
model: AccountFollow['sequelize'].models.Account,
import * as Bluebird from 'bluebird'
import * as Sequelize from 'sequelize'
import { Account as FormattedAccount, ActivityPubActor } from '../../../shared'
-import { ResultList } from '../../../shared/models/result-list.model'
-import { PodInstance } from '../pod/pod-interface'
+import { ServerInstance } from '../server/server-interface'
import { VideoChannelInstance } from '../video/video-channel-interface'
export namespace AccountMethods {
export type Load = (id: number) => Bluebird<AccountInstance>
export type LoadByUUID = (uuid: string) => Bluebird<AccountInstance>
export type LoadByUrl = (url: string, transaction?: Sequelize.Transaction) => Bluebird<AccountInstance>
- export type LoadAccountByPodAndUUID = (uuid: string, podId: number, transaction: Sequelize.Transaction) => Bluebird<AccountInstance>
+ export type LoadAccountByServerAndUUID = (uuid: string, serverId: number, transaction: Sequelize.Transaction) => Bluebird<AccountInstance>
export type LoadLocalByName = (name: string) => Bluebird<AccountInstance>
export type LoadByNameAndHost = (name: string, host: string) => Bluebird<AccountInstance>
export type ListOwned = () => Bluebird<AccountInstance[]>
export interface AccountClass {
loadApplication: AccountMethods.LoadApplication
- loadAccountByPodAndUUID: AccountMethods.LoadAccountByPodAndUUID
+ loadAccountByServerAndUUID: AccountMethods.LoadAccountByServerAndUUID
load: AccountMethods.Load
loadByUUID: AccountMethods.LoadByUUID
loadByUrl: AccountMethods.LoadByUrl
uuid?: string
- podId?: number
+ serverId?: number
userId?: number
applicationId?: number
}
createdAt: Date
updatedAt: Date
- Pod: PodInstance
+ Server: ServerInstance
VideoChannels: VideoChannelInstance[]
}
import { CONFIG, CONSTRAINTS_FIELDS } from '../../initializers/constants'
let Account: Sequelize.Model<AccountInstance, AccountAttributes>
-let loadAccountByPodAndUUID: AccountMethods.LoadAccountByPodAndUUID
+let loadAccountByServerAndUUID: AccountMethods.LoadAccountByServerAndUUID
let load: AccountMethods.Load
let loadApplication: AccountMethods.LoadApplication
let loadByUUID: AccountMethods.LoadByUUID
fields: [ 'name' ]
},
{
- fields: [ 'podId' ]
+ fields: [ 'serverId' ]
},
{
fields: [ 'userId' ],
unique: true
},
{
- fields: [ 'name', 'podId', 'applicationId' ],
+ fields: [ 'name', 'serverId', 'applicationId' ],
unique: true
}
],
const classMethods = [
associate,
- loadAccountByPodAndUUID,
+ loadAccountByServerAndUUID,
loadApplication,
load,
loadByUUID,
// ---------------------------------------------------------------------------
function associate (models) {
- Account.belongsTo(models.Pod, {
+ Account.belongsTo(models.Server, {
foreignKey: {
- name: 'podId',
+ name: 'serverId',
allowNull: true
},
onDelete: 'cascade'
}
toFormattedJSON = function (this: AccountInstance) {
- let host = this.Pod ? this.Pod.host : CONFIG.WEBSERVER.HOST
+ let host = CONFIG.WEBSERVER.HOST
+ let score: number
+
+ if (this.Server) {
+ host = this.Server.host
+ score = this.Server.score as number
+ }
const json = {
id: this.id,
host,
- name: this.name
+ score,
+ name: this.name,
+ createdAt: this.createdAt,
+ updatedAt: this.updatedAt
}
return json
}
toActivityPubObject = function (this: AccountInstance) {
- const type = this.podId ? 'Application' as 'Application' : 'Person' as 'Person'
+ const type = this.serverId ? 'Application' as 'Application' : 'Person' as 'Person'
const json = {
type,
}
isOwned = function (this: AccountInstance) {
- return this.podId === null
+ return this.serverId === null
}
getFollowerSharedInboxUrls = function (this: AccountInstance) {
listOwned = function () {
const query: Sequelize.FindOptions<AccountAttributes> = {
where: {
- podId: null
+ serverId: null
}
}
},
include: [
{
- model: Account['sequelize'].models.Pod,
+ model: Account['sequelize'].models.Server,
required: true,
where: {
host
return Account.findOne(query)
}
-loadAccountByPodAndUUID = function (uuid: string, podId: number, transaction: Sequelize.Transaction) {
+loadAccountByServerAndUUID = function (uuid: string, serverId: number, transaction: Sequelize.Transaction) {
const query: Sequelize.FindOptions<AccountAttributes> = {
where: {
- podId,
+ serverId,
uuid
},
transaction
export * from './application'
export * from './job'
export * from './oauth'
-export * from './pod'
+export * from './server'
export * from './account'
export * from './video'
+++ /dev/null
-export * from './pod-interface'
+++ /dev/null
-import * as Sequelize from 'sequelize'
-import * as Promise from 'bluebird'
-
-// Don't use barrel, import just what we need
-import { Pod as FormattedPod } from '../../../shared/models/pods/pod.model'
-import { ResultList } from '../../../shared/models/result-list.model'
-
-export namespace PodMethods {
- export type ToFormattedJSON = (this: PodInstance) => FormattedPod
-
- export type CountAll = () => Promise<number>
-
- export type IncrementScores = (ids: number[], value: number) => Promise<[ number, PodInstance[] ]>
-
- export type List = () => Promise<PodInstance[]>
-
- export type ListForApi = (start: number, count: number, sort: string) => Promise< ResultList<PodInstance> >
-
- export type ListAllIds = (transaction: Sequelize.Transaction) => Promise<number[]>
-
- export type ListRandomPodIdsWithRequest = (limit: number, tableWithPods: string, tableWithPodsJoins: string) => Promise<number[]>
-
- export type ListBadPods = () => Promise<PodInstance[]>
-
- export type Load = (id: number) => Promise<PodInstance>
-
- export type LoadByHost = (host: string) => Promise<PodInstance>
-
- export type RemoveAll = () => Promise<number>
-
- export type UpdatePodsScore = (goodPods: number[], badPods: number[]) => void
-}
-
-export interface PodClass {
- countAll: PodMethods.CountAll
- incrementScores: PodMethods.IncrementScores
- list: PodMethods.List
- listForApi: PodMethods.ListForApi
- listAllIds: PodMethods.ListAllIds
- listRandomPodIdsWithRequest: PodMethods.ListRandomPodIdsWithRequest
- listBadPods: PodMethods.ListBadPods
- load: PodMethods.Load
- loadByHost: PodMethods.LoadByHost
- removeAll: PodMethods.RemoveAll
- updatePodsScore: PodMethods.UpdatePodsScore
-}
-
-export interface PodAttributes {
- id?: number
- host?: string
- score?: number | Sequelize.literal // Sequelize literal for 'score +' + value
-}
-
-export interface PodInstance extends PodClass, PodAttributes, Sequelize.Instance<PodAttributes> {
- createdAt: Date
- updatedAt: Date
-
- toFormattedJSON: PodMethods.ToFormattedJSON,
-}
-
-export interface PodModel extends PodClass, Sequelize.Model<PodInstance, PodAttributes> {}
+++ /dev/null
-import { map } from 'lodash'
-import * as Sequelize from 'sequelize'
-
-import { FRIEND_SCORE, PODS_SCORE } from '../../initializers'
-import { logger, isHostValid } from '../../helpers'
-
-import { addMethodsToModel, getSort } from '../utils'
-import {
- PodInstance,
- PodAttributes,
-
- PodMethods
-} from './pod-interface'
-
-let Pod: Sequelize.Model<PodInstance, PodAttributes>
-let toFormattedJSON: PodMethods.ToFormattedJSON
-let countAll: PodMethods.CountAll
-let incrementScores: PodMethods.IncrementScores
-let list: PodMethods.List
-let listForApi: PodMethods.ListForApi
-let listAllIds: PodMethods.ListAllIds
-let listRandomPodIdsWithRequest: PodMethods.ListRandomPodIdsWithRequest
-let listBadPods: PodMethods.ListBadPods
-let load: PodMethods.Load
-let loadByHost: PodMethods.LoadByHost
-let removeAll: PodMethods.RemoveAll
-let updatePodsScore: PodMethods.UpdatePodsScore
-
-export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
- Pod = sequelize.define<PodInstance, PodAttributes>('Pod',
- {
- host: {
- type: DataTypes.STRING,
- allowNull: false,
- validate: {
- isHost: value => {
- const res = isHostValid(value)
- if (res === false) throw new Error('Host not valid.')
- }
- }
- },
- score: {
- type: DataTypes.INTEGER,
- defaultValue: FRIEND_SCORE.BASE,
- allowNull: false,
- validate: {
- isInt: true,
- max: FRIEND_SCORE.MAX
- }
- }
- },
- {
- indexes: [
- {
- fields: [ 'host' ],
- unique: true
- },
- {
- fields: [ 'score' ]
- }
- ]
- }
- )
-
- const classMethods = [
- countAll,
- incrementScores,
- list,
- listForApi,
- listAllIds,
- listRandomPodIdsWithRequest,
- listBadPods,
- load,
- loadByHost,
- updatePodsScore,
- removeAll
- ]
- const instanceMethods = [ toFormattedJSON ]
- addMethodsToModel(Pod, classMethods, instanceMethods)
-
- return Pod
-}
-
-// ------------------------------ METHODS ------------------------------
-
-toFormattedJSON = function (this: PodInstance) {
- const json = {
- id: this.id,
- host: this.host,
- score: this.score as number,
- createdAt: this.createdAt
- }
-
- return json
-}
-
-// ------------------------------ Statics ------------------------------
-
-countAll = function () {
- return Pod.count()
-}
-
-incrementScores = function (ids: number[], value: number) {
- const update = {
- score: Sequelize.literal('score +' + value)
- }
-
- const options = {
- where: {
- id: {
- [Sequelize.Op.in]: ids
- }
- },
- // In this case score is a literal and not an integer so we do not validate it
- validate: false
- }
-
- return Pod.update(update, options)
-}
-
-list = function () {
- return Pod.findAll()
-}
-
-listForApi = function (start: number, count: number, sort: string) {
- const query = {
- offset: start,
- limit: count,
- order: [ getSort(sort) ]
- }
-
- return Pod.findAndCountAll(query).then(({ rows, count }) => {
- return {
- data: rows,
- total: count
- }
- })
-}
-
-listAllIds = function (transaction: Sequelize.Transaction) {
- const query = {
- attributes: [ 'id' ],
- transaction
- }
-
- return Pod.findAll(query).then(pods => {
- return map(pods, 'id')
- })
-}
-
-listRandomPodIdsWithRequest = function (limit: number, tableWithPods: string, tableWithPodsJoins: string) {
- return Pod.count().then(count => {
- // Optimization...
- if (count === 0) return []
-
- let start = Math.floor(Math.random() * count) - limit
- if (start < 0) start = 0
-
- const subQuery = `(SELECT DISTINCT "${tableWithPods}"."podId" FROM "${tableWithPods}" ${tableWithPodsJoins})`
- const query = {
- attributes: [ 'id' ],
- order: [
- [ 'id', 'ASC' ]
- ],
- offset: start,
- limit: limit,
- where: {
- id: {
- [Sequelize.Op.in]: Sequelize.literal(subQuery)
- }
- }
- }
-
- return Pod.findAll(query).then(pods => {
- return map(pods, 'id')
- })
- })
-}
-
-listBadPods = function () {
- const query = {
- where: {
- score: {
- [Sequelize.Op.lte]: 0
- }
- }
- }
-
- return Pod.findAll(query)
-}
-
-load = function (id: number) {
- return Pod.findById(id)
-}
-
-loadByHost = function (host: string) {
- const query = {
- where: {
- host: host
- }
- }
-
- return Pod.findOne(query)
-}
-
-removeAll = function () {
- return Pod.destroy()
-}
-
-updatePodsScore = function (goodPods: number[], badPods: number[]) {
- logger.info('Updating %d good pods and %d bad pods scores.', goodPods.length, badPods.length)
-
- if (goodPods.length !== 0) {
- incrementScores(goodPods, PODS_SCORE.BONUS).catch(err => {
- logger.error('Cannot increment scores of good pods.', err)
- })
- }
-
- if (badPods.length !== 0) {
- incrementScores(badPods, PODS_SCORE.PENALTY)
- .then(() => removeBadPods())
- .catch(err => {
- if (err) logger.error('Cannot decrement scores of bad pods.', err)
- })
- }
-}
-
-// ---------------------------------------------------------------------------
-
-// Remove pods with a score of 0 (too many requests where they were unreachable)
-async function removeBadPods () {
- try {
- const pods = await listBadPods()
-
- const podsRemovePromises = pods.map(pod => pod.destroy())
- await Promise.all(podsRemovePromises)
-
- const numberOfPodsRemoved = pods.length
-
- if (numberOfPodsRemoved) {
- logger.info('Removed %d pods.', numberOfPodsRemoved)
- } else {
- logger.info('No need to remove bad pods.')
- }
- } catch (err) {
- logger.error('Cannot remove bad pods.', err)
- }
-}
--- /dev/null
+export * from './server-interface'
--- /dev/null
+import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
+
+// Don't use barrel, import just what we need
+import { ResultList } from '../../../shared/models/result-list.model'
+
+export namespace ServerMethods {
+ export type CountAll = () => Promise<number>
+
+ export type IncrementScores = (ids: number[], value: number) => Promise<[ number, ServerInstance[] ]>
+
+ export type List = () => Promise<ServerInstance[]>
+
+ export type ListForApi = (start: number, count: number, sort: string) => Promise< ResultList<ServerInstance> >
+
+ export type ListAllIds = (transaction: Sequelize.Transaction) => Promise<number[]>
+
+ export type ListRandomServerIdsWithRequest = (limit: number, tableWithServers: string, tableWithServersJoins: string) => Promise<number[]>
+
+ export type ListBadServers = () => Promise<ServerInstance[]>
+
+ export type Load = (id: number) => Promise<ServerInstance>
+
+ export type LoadByHost = (host: string) => Promise<ServerInstance>
+
+ export type RemoveAll = () => Promise<number>
+
+ export type UpdateServersScore = (goodServers: number[], badServers: number[]) => void
+}
+
+export interface ServerClass {
+ countAll: ServerMethods.CountAll
+ incrementScores: ServerMethods.IncrementScores
+ list: ServerMethods.List
+ listForApi: ServerMethods.ListForApi
+ listAllIds: ServerMethods.ListAllIds
+ listRandomServerIdsWithRequest: ServerMethods.ListRandomServerIdsWithRequest
+ listBadServers: ServerMethods.ListBadServers
+ load: ServerMethods.Load
+ loadByHost: ServerMethods.LoadByHost
+ removeAll: ServerMethods.RemoveAll
+ updateServersScore: ServerMethods.UpdateServersScore
+}
+
+export interface ServerAttributes {
+ id?: number
+ host?: string
+ score?: number | Sequelize.literal // Sequelize literal for 'score +' + value
+}
+
+export interface ServerInstance extends ServerClass, ServerAttributes, Sequelize.Instance<ServerAttributes> {
+ createdAt: Date
+ updatedAt: Date
+}
+
+export interface ServerModel extends ServerClass, Sequelize.Model<ServerInstance, ServerAttributes> {}
--- /dev/null
+import { map } from 'lodash'
+import * as Sequelize from 'sequelize'
+
+import { FRIEND_SCORE, SERVERS_SCORE } from '../../initializers'
+import { logger, isHostValid } from '../../helpers'
+
+import { addMethodsToModel, getSort } from '../utils'
+import {
+ ServerInstance,
+ ServerAttributes,
+
+ ServerMethods
+} from './server-interface'
+
+let Server: Sequelize.Model<ServerInstance, ServerAttributes>
+let countAll: ServerMethods.CountAll
+let incrementScores: ServerMethods.IncrementScores
+let list: ServerMethods.List
+let listForApi: ServerMethods.ListForApi
+let listAllIds: ServerMethods.ListAllIds
+let listRandomServerIdsWithRequest: ServerMethods.ListRandomServerIdsWithRequest
+let listBadServers: ServerMethods.ListBadServers
+let load: ServerMethods.Load
+let loadByHost: ServerMethods.LoadByHost
+let removeAll: ServerMethods.RemoveAll
+let updateServersScore: ServerMethods.UpdateServersScore
+
+export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
+ Server = sequelize.define<ServerInstance, ServerAttributes>('Server',
+ {
+ host: {
+ type: DataTypes.STRING,
+ allowNull: false,
+ validate: {
+ isHost: value => {
+ const res = isHostValid(value)
+ if (res === false) throw new Error('Host not valid.')
+ }
+ }
+ },
+ score: {
+ type: DataTypes.INTEGER,
+ defaultValue: FRIEND_SCORE.BASE,
+ allowNull: false,
+ validate: {
+ isInt: true,
+ max: FRIEND_SCORE.MAX
+ }
+ }
+ },
+ {
+ indexes: [
+ {
+ fields: [ 'host' ],
+ unique: true
+ },
+ {
+ fields: [ 'score' ]
+ }
+ ]
+ }
+ )
+
+ const classMethods = [
+ countAll,
+ incrementScores,
+ list,
+ listForApi,
+ listAllIds,
+ listRandomServerIdsWithRequest,
+ listBadServers,
+ load,
+ loadByHost,
+ updateServersScore,
+ removeAll
+ ]
+ addMethodsToModel(Server, classMethods)
+
+ return Server
+}
+
+// ------------------------------ Statics ------------------------------
+
+countAll = function () {
+ return Server.count()
+}
+
+incrementScores = function (ids: number[], value: number) {
+ const update = {
+ score: Sequelize.literal('score +' + value)
+ }
+
+ const options = {
+ where: {
+ id: {
+ [Sequelize.Op.in]: ids
+ }
+ },
+ // In this case score is a literal and not an integer so we do not validate it
+ validate: false
+ }
+
+ return Server.update(update, options)
+}
+
+list = function () {
+ return Server.findAll()
+}
+
+listForApi = function (start: number, count: number, sort: string) {
+ const query = {
+ offset: start,
+ limit: count,
+ order: [ getSort(sort) ]
+ }
+
+ return Server.findAndCountAll(query).then(({ rows, count }) => {
+ return {
+ data: rows,
+ total: count
+ }
+ })
+}
+
+listAllIds = function (transaction: Sequelize.Transaction) {
+ const query = {
+ attributes: [ 'id' ],
+ transaction
+ }
+
+ return Server.findAll(query).then(servers => {
+ return map(servers, 'id')
+ })
+}
+
+listRandomServerIdsWithRequest = function (limit: number, tableWithServers: string, tableWithServersJoins: string) {
+ return Server.count().then(count => {
+ // Optimization...
+ if (count === 0) return []
+
+ let start = Math.floor(Math.random() * count) - limit
+ if (start < 0) start = 0
+
+ const subQuery = `(SELECT DISTINCT "${tableWithServers}"."serverId" FROM "${tableWithServers}" ${tableWithServersJoins})`
+ const query = {
+ attributes: [ 'id' ],
+ order: [
+ [ 'id', 'ASC' ]
+ ],
+ offset: start,
+ limit: limit,
+ where: {
+ id: {
+ [Sequelize.Op.in]: Sequelize.literal(subQuery)
+ }
+ }
+ }
+
+ return Server.findAll(query).then(servers => {
+ return map(servers, 'id')
+ })
+ })
+}
+
+listBadServers = function () {
+ const query = {
+ where: {
+ score: {
+ [Sequelize.Op.lte]: 0
+ }
+ }
+ }
+
+ return Server.findAll(query)
+}
+
+load = function (id: number) {
+ return Server.findById(id)
+}
+
+loadByHost = function (host: string) {
+ const query = {
+ where: {
+ host: host
+ }
+ }
+
+ return Server.findOne(query)
+}
+
+removeAll = function () {
+ return Server.destroy()
+}
+
+updateServersScore = function (goodServers: number[], badServers: number[]) {
+ logger.info('Updating %d good servers and %d bad servers scores.', goodServers.length, badServers.length)
+
+ if (goodServers.length !== 0) {
+ incrementScores(goodServers, SERVERS_SCORE.BONUS).catch(err => {
+ logger.error('Cannot increment scores of good servers.', err)
+ })
+ }
+
+ if (badServers.length !== 0) {
+ incrementScores(badServers, SERVERS_SCORE.PENALTY)
+ .then(() => removeBadServers())
+ .catch(err => {
+ if (err) logger.error('Cannot decrement scores of bad servers.', err)
+ })
+ }
+}
+
+// ---------------------------------------------------------------------------
+
+// Remove servers with a score of 0 (too many requests where they were unreachable)
+async function removeBadServers () {
+ try {
+ const servers = await listBadServers()
+
+ const serversRemovePromises = servers.map(server => server.destroy())
+ await Promise.all(serversRemovePromises)
+
+ const numberOfServersRemoved = servers.length
+
+ if (numberOfServersRemoved) {
+ logger.info('Removed %d servers.', numberOfServersRemoved)
+ } else {
+ logger.info('No need to remove bad servers.')
+ }
+ } catch (err) {
+ logger.error('Cannot remove bad servers.', err)
+ }
+}
import * as Sequelize from 'sequelize'
import * as Promise from 'bluebird'
-import { PodInstance } from '../pod/pod-interface'
+import { ServerInstance } from '../server/server-interface'
import { ResultList } from '../../../shared'
// Don't use barrel, import just what we need
createdAt: Date
updatedAt: Date
- Pod: PodInstance
+ Server: ServerInstance
toFormattedJSON: VideoAbuseMethods.ToFormattedJSON
}
fields: [ 'videoId' ]
},
{
- fields: [ 'reporterPodId' ]
+ fields: [ 'reporterServerId' ]
}
]
}
// ------------------------------ METHODS ------------------------------
toFormattedJSON = function (this: VideoAbuseInstance) {
- let reporterPodHost
+ let reporterServerHost
- if (this.Pod) {
- reporterPodHost = this.Pod.host
+ if (this.Server) {
+ reporterServerHost = this.Server.host
} else {
// It means it's our video
- reporterPodHost = CONFIG.WEBSERVER.HOST
+ reporterServerHost = CONFIG.WEBSERVER.HOST
}
const json = {
id: this.id,
- reporterPodHost,
+ reporterServerHost,
reason: this.reason,
reporterUsername: this.reporterUsername,
videoId: this.videoId,
// ------------------------------ STATICS ------------------------------
function associate (models) {
- VideoAbuse.belongsTo(models.Pod, {
+ VideoAbuse.belongsTo(models.Server, {
foreignKey: {
- name: 'reporterPodId',
+ name: 'reporterServerId',
allowNull: true
},
onDelete: 'CASCADE'
order: [ getSort(sort) ],
include: [
{
- model: VideoAbuse['sequelize'].models.Pod,
+ model: VideoAbuse['sequelize'].models.Server,
required: false
}
]
export type LoadAndPopulateAccount = (id: number) => Promise<VideoChannelInstance>
export type LoadByUUIDAndPopulateAccount = (uuid: string) => Promise<VideoChannelInstance>
export type LoadByUUID = (uuid: string, t?: Sequelize.Transaction) => Promise<VideoChannelInstance>
- export type LoadByHostAndUUID = (uuid: string, podHost: string, t?: Sequelize.Transaction) => Promise<VideoChannelInstance>
+ export type LoadByHostAndUUID = (uuid: string, serverHost: string, t?: Sequelize.Transaction) => Promise<VideoChannelInstance>
export type LoadAndPopulateAccountAndVideos = (id: number) => Promise<VideoChannelInstance>
export type LoadByUrl = (uuid: string, t?: Sequelize.Transaction) => Promise<VideoChannelInstance>
export type LoadByUUIDOrUrl = (uuid: string, url: string, t?: Sequelize.Transaction) => Promise<VideoChannelInstance>
{
model: VideoChannel['sequelize'].models.Account,
required: true,
- include: [ { model: VideoChannel['sequelize'].models.Pod, required: false } ]
+ include: [ { model: VideoChannel['sequelize'].models.Server, required: false } ]
}
]
}
id: accountId
},
required: true,
- include: [ { model: VideoChannel['sequelize'].models.Pod, required: false } ]
+ include: [ { model: VideoChannel['sequelize'].models.Server, required: false } ]
}
]
}
model: VideoChannel['sequelize'].models.Account,
include: [
{
- model: VideoChannel['sequelize'].models.Pod,
+ model: VideoChannel['sequelize'].models.Server,
required: true,
where: {
host: fromHost
include: [
{
model: VideoChannel['sequelize'].models.Account,
- include: [ { model: VideoChannel['sequelize'].models.Pod, required: false } ]
+ include: [ { model: VideoChannel['sequelize'].models.Server, required: false } ]
}
]
}
include: [
{
model: VideoChannel['sequelize'].models.Account,
- include: [ { model: VideoChannel['sequelize'].models.Pod, required: false } ]
+ include: [ { model: VideoChannel['sequelize'].models.Server, required: false } ]
}
]
}
include: [
{
model: VideoChannel['sequelize'].models.Account,
- include: [ { model: VideoChannel['sequelize'].models.Pod, required: false } ]
+ include: [ { model: VideoChannel['sequelize'].models.Server, required: false } ]
}
]
}
include: [
{
model: VideoChannel['sequelize'].models.Account,
- include: [ { model: VideoChannel['sequelize'].models.Pod, required: false } ]
+ include: [ { model: VideoChannel['sequelize'].models.Server, required: false } ]
},
VideoChannel['sequelize'].models.Video
]
export type ListForApi = (start: number, count: number, sort: string) => Bluebird< ResultList<VideoInstance> >
export type ListUserVideosForApi = (userId: number, start: number, count: number, sort: string) => Bluebird< ResultList<VideoInstance> >
- export type SearchAndPopulateAccountAndPodAndTags = (
+ export type SearchAndPopulateAccountAndServerAndTags = (
value: string,
field: string,
start: number,
export type LoadLocalVideoByUUID = (uuid: string, t?: Sequelize.Transaction) => Bluebird<VideoInstance>
export type LoadByHostAndUUID = (fromHost: string, uuid: string, t?: Sequelize.Transaction) => Bluebird<VideoInstance>
export type LoadAndPopulateAccount = (id: number) => Bluebird<VideoInstance>
- export type LoadAndPopulateAccountAndPodAndTags = (id: number) => Bluebird<VideoInstance>
- export type LoadByUUIDAndPopulateAccountAndPodAndTags = (uuid: string) => Bluebird<VideoInstance>
+ export type LoadAndPopulateAccountAndServerAndTags = (id: number) => Bluebird<VideoInstance>
+ export type LoadByUUIDAndPopulateAccountAndServerAndTags = (uuid: string) => Bluebird<VideoInstance>
export type LoadByUUIDOrURL = (uuid: string, url: string, t?: Sequelize.Transaction) => Bluebird<VideoInstance>
export type RemoveThumbnail = (this: VideoInstance) => Promise<void>
listOwnedByAccount: VideoMethods.ListOwnedByAccount
load: VideoMethods.Load
loadAndPopulateAccount: VideoMethods.LoadAndPopulateAccount
- loadAndPopulateAccountAndPodAndTags: VideoMethods.LoadAndPopulateAccountAndPodAndTags
+ loadAndPopulateAccountAndServerAndTags: VideoMethods.LoadAndPopulateAccountAndServerAndTags
loadByHostAndUUID: VideoMethods.LoadByHostAndUUID
loadByUUID: VideoMethods.LoadByUUID
loadByUrl: VideoMethods.LoadByUrl
loadByUUIDOrURL: VideoMethods.LoadByUUIDOrURL
loadLocalVideoByUUID: VideoMethods.LoadLocalVideoByUUID
- loadByUUIDAndPopulateAccountAndPodAndTags: VideoMethods.LoadByUUIDAndPopulateAccountAndPodAndTags
- searchAndPopulateAccountAndPodAndTags: VideoMethods.SearchAndPopulateAccountAndPodAndTags
+ loadByUUIDAndPopulateAccountAndServerAndTags: VideoMethods.LoadByUUIDAndPopulateAccountAndServerAndTags
+ searchAndPopulateAccountAndServerAndTags: VideoMethods.SearchAndPopulateAccountAndServerAndTags
}
export interface VideoAttributes {
let loadByUUIDOrURL: VideoMethods.LoadByUUIDOrURL
let loadLocalVideoByUUID: VideoMethods.LoadLocalVideoByUUID
let loadAndPopulateAccount: VideoMethods.LoadAndPopulateAccount
-let loadAndPopulateAccountAndPodAndTags: VideoMethods.LoadAndPopulateAccountAndPodAndTags
-let loadByUUIDAndPopulateAccountAndPodAndTags: VideoMethods.LoadByUUIDAndPopulateAccountAndPodAndTags
-let searchAndPopulateAccountAndPodAndTags: VideoMethods.SearchAndPopulateAccountAndPodAndTags
+let loadAndPopulateAccountAndServerAndTags: VideoMethods.LoadAndPopulateAccountAndServerAndTags
+let loadByUUIDAndPopulateAccountAndServerAndTags: VideoMethods.LoadByUUIDAndPopulateAccountAndServerAndTags
+let searchAndPopulateAccountAndServerAndTags: VideoMethods.SearchAndPopulateAccountAndServerAndTags
let removeThumbnail: VideoMethods.RemoveThumbnail
let removePreview: VideoMethods.RemovePreview
let removeFile: VideoMethods.RemoveFile
listOwnedByAccount,
load,
loadAndPopulateAccount,
- loadAndPopulateAccountAndPodAndTags,
+ loadAndPopulateAccountAndServerAndTags,
loadByHostAndUUID,
loadByUUIDOrURL,
loadByUUID,
loadLocalVideoByUUID,
- loadByUUIDAndPopulateAccountAndPodAndTags,
- searchAndPopulateAccountAndPodAndTags
+ loadByUUIDAndPopulateAccountAndServerAndTags,
+ searchAndPopulateAccountAndServerAndTags
]
const instanceMethods = [
createPreview,
}
toFormattedJSON = function (this: VideoInstance) {
- let podHost
+ let serverHost
- if (this.VideoChannel.Account.Pod) {
- podHost = this.VideoChannel.Account.Pod.host
+ if (this.VideoChannel.Account.Server) {
+ serverHost = this.VideoChannel.Account.Server.host
} else {
// It means it's our video
- podHost = CONFIG.WEBSERVER.HOST
+ serverHost = CONFIG.WEBSERVER.HOST
}
const json = {
languageLabel: this.getLanguageLabel(),
nsfw: this.nsfw,
description: this.getTruncatedDescription(),
- podHost,
+ serverHost,
isLocal: this.isOwned(),
account: this.VideoChannel.Account.name,
duration: this.duration,
toFormattedDetailsJSON = function (this: VideoInstance) {
const formattedJson = this.toFormattedJSON()
- // Maybe our pod is not up to date and there are new privacy settings since our version
+ // Maybe our server is not up to date and there are new privacy settings since our version
let privacyLabel = VIDEO_PRIVACIES[this.privacy]
if (!privacyLabel) privacyLabel = 'Unknown'
getCategoryLabel = function (this: VideoInstance) {
let categoryLabel = VIDEO_CATEGORIES[this.category]
- // Maybe our pod is not up to date and there are new categories since our version
+ // Maybe our server is not up to date and there are new categories since our version
if (!categoryLabel) categoryLabel = 'Misc'
return categoryLabel
getLicenceLabel = function (this: VideoInstance) {
let licenceLabel = VIDEO_LICENCES[this.licence]
- // Maybe our pod is not up to date and there are new licences since our version
+ // Maybe our server is not up to date and there are new licences since our version
if (!licenceLabel) licenceLabel = 'Unknown'
return licenceLabel
model: Video['sequelize'].models.Account,
include: [
{
- model: Video['sequelize'].models.Pod,
+ model: Video['sequelize'].models.Server,
required: false
}
]
model: Video['sequelize'].models.Account,
include: [
{
- model: Video['sequelize'].models.Pod,
+ model: Video['sequelize'].models.Server,
required: true,
where: {
host: fromHost
return Video.findById(id, options)
}
-loadAndPopulateAccountAndPodAndTags = function (id: number) {
+loadAndPopulateAccountAndServerAndTags = function (id: number) {
const options = {
include: [
{
include: [
{
model: Video['sequelize'].models.Account,
- include: [ { model: Video['sequelize'].models.Pod, required: false } ]
+ include: [ { model: Video['sequelize'].models.Server, required: false } ]
}
]
},
return Video.findById(id, options)
}
-loadByUUIDAndPopulateAccountAndPodAndTags = function (uuid: string) {
+loadByUUIDAndPopulateAccountAndServerAndTags = function (uuid: string) {
const options = {
where: {
uuid
include: [
{
model: Video['sequelize'].models.Account,
- include: [ { model: Video['sequelize'].models.Pod, required: false } ]
+ include: [ { model: Video['sequelize'].models.Server, required: false } ]
}
]
},
return Video.findOne(options)
}
-searchAndPopulateAccountAndPodAndTags = function (value: string, field: string, start: number, count: number, sort: string) {
- const podInclude: Sequelize.IncludeOptions = {
- model: Video['sequelize'].models.Pod,
+searchAndPopulateAccountAndServerAndTags = function (value: string, field: string, start: number, count: number, sort: string) {
+ const serverInclude: Sequelize.IncludeOptions = {
+ model: Video['sequelize'].models.Server,
required: false
}
const accountInclude: Sequelize.IncludeOptions = {
model: Video['sequelize'].models.Account,
- include: [ podInclude ]
+ include: [ serverInclude ]
}
const videoChannelInclude: Sequelize.IncludeOptions = {
)`
)
} else if (field === 'host') {
- // FIXME: Include our pod? (not stored in the database)
- podInclude.where = {
+ // FIXME: Include our server? (not stored in the database)
+ serverInclude.where = {
host: {
[Sequelize.Op.iLike]: '%' + value + '%'
}
}
- podInclude.required = true
+ serverInclude.required = true
} else if (field === 'account') {
accountInclude.where = {
name: {
baseUrlHttp = CONFIG.WEBSERVER.URL
baseUrlWs = CONFIG.WEBSERVER.WS + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT
} else {
- baseUrlHttp = REMOTE_SCHEME.HTTP + '://' + video.VideoChannel.Account.Pod.host
- baseUrlWs = REMOTE_SCHEME.WS + '://' + video.VideoChannel.Account.Pod.host
+ baseUrlHttp = REMOTE_SCHEME.HTTP + '://' + video.VideoChannel.Account.Server.host
+ baseUrlWs = REMOTE_SCHEME.WS + '://' + video.VideoChannel.Account.Server.host
}
return { baseUrlHttp, baseUrlWs }
export type FollowState = 'pending' | 'accepted'
+
+export interface AccountFollow {
+ id: number
+ name: string
+ score?: number // Used for followers
+ host: string
+}
export * from './accounts'
export * from './activitypub'
-export * from './pods'
export * from './users'
export * from './videos'
export * from './job.model'
export * from './oauth-client-local.model'
export * from './result-list.model'
-export * from './request-scheduler.model'
export * from './server-config.model'
+++ /dev/null
-export * from './pod-signature.model'
-export * from './pod.model'
-export * from './remote-video'
+++ /dev/null
-export interface PodSignature {
- host: string
- signature: string
-}
+++ /dev/null
-export interface Pod {
- id: number,
- host: string,
- score: number,
- createdAt: Date
-}
+++ /dev/null
-export * from './remote-qadu-video-request.model'
-export * from './remote-video-author-create-request.model'
-export * from './remote-video-author-remove-request.model'
-export * from './remote-video-event-request.model'
-export * from './remote-video-request.model'
-export * from './remote-video-create-request.model'
-export * from './remote-video-update-request.model'
-export * from './remote-video-remove-request.model'
-export * from './remote-video-channel-create-request.model'
-export * from './remote-video-channel-update-request.model'
-export * from './remote-video-channel-remove-request.model'
-export * from './remote-video-report-abuse-request.model'
+++ /dev/null
-export interface RemoteQaduVideoData {
- uuid: string
- views?: number
- likes?: number
- dislikes?: number
-}
-
-export interface RemoteQaduVideoRequest {
- data: RemoteQaduVideoData
-}
+++ /dev/null
-import { RemoteVideoRequest } from './remote-video-request.model'
-
-export interface RemoteVideoAuthorCreateData {
- uuid: string
- name: string
-}
-
-export interface RemoteVideoAuthorCreateRequest extends RemoteVideoRequest {
- type: 'add-author'
- data: RemoteVideoAuthorCreateData
-}
+++ /dev/null
-import { RemoteVideoRequest } from './remote-video-request.model'
-
-export interface RemoteVideoAuthorRemoveData {
- uuid: string
-}
-
-export interface RemoteVideoAuthorRemoveRequest extends RemoteVideoRequest {
- type: 'remove-author'
- data: RemoteVideoAuthorRemoveData
-}
+++ /dev/null
-import { RemoteVideoRequest } from './remote-video-request.model'
-
-export interface RemoteVideoChannelCreateData {
- uuid: string
- name: string
- description: string
- createdAt: Date
- updatedAt: Date
- ownerUUID: string
-}
-
-export interface RemoteVideoChannelCreateRequest extends RemoteVideoRequest {
- type: 'add-channel'
- data: RemoteVideoChannelCreateData
-}
+++ /dev/null
-import { RemoteVideoRequest } from './remote-video-request.model'
-
-export interface RemoteVideoChannelRemoveData {
- uuid: string
-}
-
-export interface RemoteVideoChannelRemoveRequest extends RemoteVideoRequest {
- type: 'remove-channel'
- data: RemoteVideoChannelRemoveData
-}
+++ /dev/null
-import { RemoteVideoRequest } from './remote-video-request.model'
-
-export interface RemoteVideoChannelUpdateData {
- uuid: string
- name: string
- description: string
- createdAt: Date
- updatedAt: Date
- ownerUUID: string
-}
-
-export interface RemoteVideoChannelUpdateRequest extends RemoteVideoRequest {
- type: 'update-channel'
- data: RemoteVideoChannelUpdateData
-}
+++ /dev/null
-import { RemoteVideoRequest } from './remote-video-request.model'
-
-export interface RemoteVideoCreateData {
- uuid: string
- channelUUID: string
- tags: string[]
- name: string
- category: number
- licence: number
- language: number
- nsfw: boolean
- truncatedDescription: string
- duration: number
- createdAt: Date
- updatedAt: Date
- views: number
- likes: number
- dislikes: number
- privacy: number
- thumbnailData: string
- files: {
- infoHash: string
- extname: string
- resolution: number
- size: number
- }[]
-}
-
-export interface RemoteVideoCreateRequest extends RemoteVideoRequest {
- type: 'add-video'
- data: RemoteVideoCreateData
-}
+++ /dev/null
-export type RemoteVideoEventType = 'views' | 'likes' | 'dislikes'
-
-export interface RemoteVideoEventData {
- uuid: string
- eventType: RemoteVideoEventType
- count: number
-}
-
-export interface RemoteVideoEventRequest {
- data: RemoteVideoEventData
-}
+++ /dev/null
-import { RemoteVideoRequest } from './remote-video-request.model'
-
-export interface RemoteVideoRemoveData {
- uuid: string
-}
-
-export interface RemoteVideoRemoveRequest extends RemoteVideoRequest {
- type: 'remove-video'
- data: RemoteVideoRemoveData
-}
+++ /dev/null
-import { RemoteVideoRequest } from './remote-video-request.model'
-
-export interface RemoteVideoReportAbuseData {
- videoUUID: string
- reporterUsername: string
- reportReason: string
-}
-
-export interface RemoteVideoReportAbuseRequest extends RemoteVideoRequest {
- type: 'report-abuse'
- data: RemoteVideoReportAbuseData
-}
+++ /dev/null
-export interface RemoteVideoRequest {
- type: RemoteVideoRequestType
- data: any
-}
-
-export type RemoteVideoRequestType = 'add-video' | 'update-video' | 'remove-video' |
- 'add-channel' | 'update-channel' | 'remove-channel' |
- 'report-abuse' |
- 'add-author' | 'remove-author'
+++ /dev/null
-import { RemoteVideoRequest } from './remote-video-request.model'
-
-export interface RemoteVideoUpdateData {
- uuid: string
- tags: string[]
- name: string
- category: number
- licence: number
- language: number
- nsfw: boolean
- truncatedDescription: string
- duration: number
- createdAt: Date
- updatedAt: Date
- views: number
- likes: number
- dislikes: number
- privacy: number
- files: {
- infoHash: string
- extname: string
- resolution: number
- size: number
- }[]
-}
-
-export interface RemoteVideoUpdateRequest extends RemoteVideoRequest {
- type: 'update-video'
- data: RemoteVideoUpdateData
-}
+++ /dev/null
-export type RequestEndpoint = 'videos'
-
-export type RequestVideoQaduType = 'likes' | 'dislikes' | 'views'
-
-export type RequestVideoEventType = 'likes' | 'dislikes' | 'views'
-
-export type RequestSchedulerStatsAttributes = {
- totalRequests: number
- requestsLimitPods: number
- requestsLimitPerPod: number
- remainingMilliSeconds: number
- milliSecondsInterval: number
-}
-
-export interface RequestSchedulerStats {
- requestScheduler: RequestSchedulerStatsAttributes
- requestVideoQaduScheduler: RequestSchedulerStatsAttributes
- requestVideoEventScheduler: RequestSchedulerStatsAttributes
-}
export interface VideoAbuse {
id: number
- reporterPodHost: string
+ reporterServerHost: string
reason: string
reporterUsername: string
videoId: number
duration: number
isLocal: boolean
name: string
- podHost: string
+ serverHost: string
tags: string[]
thumbnailPath: string
previewPath: string