mirror of
https://github.com/Chocobozzz/PeerTube.git
synced 2025-10-05 19:42:24 +02:00
First version with PostgreSQL
This commit is contained in:
parent
108626609e
commit
feb4bdfd9b
68 changed files with 1171 additions and 730 deletions
|
@ -1,31 +1,36 @@
|
|||
const mongoose = require('mongoose')
|
||||
module.exports = function (sequelize, DataTypes) {
|
||||
const Application = sequelize.define('Application',
|
||||
{
|
||||
sqlSchemaVersion: {
|
||||
type: DataTypes.INTEGER,
|
||||
defaultValue: 0
|
||||
}
|
||||
},
|
||||
{
|
||||
classMethods: {
|
||||
loadSqlSchemaVersion,
|
||||
updateSqlSchemaVersion
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const ApplicationSchema = mongoose.Schema({
|
||||
mongoSchemaVersion: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
})
|
||||
|
||||
ApplicationSchema.statics = {
|
||||
loadMongoSchemaVersion,
|
||||
updateMongoSchemaVersion
|
||||
return Application
|
||||
}
|
||||
|
||||
mongoose.model('Application', ApplicationSchema)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function loadMongoSchemaVersion (callback) {
|
||||
return this.findOne({}, { mongoSchemaVersion: 1 }, function (err, data) {
|
||||
const version = data ? data.mongoSchemaVersion : 0
|
||||
function loadSqlSchemaVersion (callback) {
|
||||
const query = {
|
||||
attributes: [ 'sqlSchemaVersion' ]
|
||||
}
|
||||
|
||||
return this.findOne(query).asCallback(function (err, data) {
|
||||
const version = data ? data.sqlSchemaVersion : 0
|
||||
|
||||
return callback(err, version)
|
||||
})
|
||||
}
|
||||
|
||||
function updateMongoSchemaVersion (newVersion, callback) {
|
||||
return this.update({}, { mongoSchemaVersion: newVersion }, callback)
|
||||
function updateSqlSchemaVersion (newVersion, callback) {
|
||||
return this.update({ sqlSchemaVersion: newVersion }).asCallback(callback)
|
||||
}
|
||||
|
|
28
server/models/author.js
Normal file
28
server/models/author.js
Normal file
|
@ -0,0 +1,28 @@
|
|||
module.exports = function (sequelize, DataTypes) {
|
||||
const Author = sequelize.define('Author',
|
||||
{
|
||||
name: {
|
||||
type: DataTypes.STRING
|
||||
}
|
||||
},
|
||||
{
|
||||
classMethods: {
|
||||
associate
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
return Author
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function associate (models) {
|
||||
this.belongsTo(models.Pod, {
|
||||
foreignKey: {
|
||||
name: 'podId',
|
||||
allowNull: true
|
||||
},
|
||||
onDelete: 'cascade'
|
||||
})
|
||||
}
|
|
@ -1,33 +1,63 @@
|
|||
const mongoose = require('mongoose')
|
||||
module.exports = function (sequelize, DataTypes) {
|
||||
const OAuthClient = sequelize.define('OAuthClient',
|
||||
{
|
||||
clientId: {
|
||||
type: DataTypes.STRING
|
||||
},
|
||||
clientSecret: {
|
||||
type: DataTypes.STRING
|
||||
},
|
||||
grants: {
|
||||
type: DataTypes.ARRAY(DataTypes.STRING)
|
||||
},
|
||||
redirectUris: {
|
||||
type: DataTypes.ARRAY(DataTypes.STRING)
|
||||
}
|
||||
},
|
||||
{
|
||||
classMethods: {
|
||||
associate,
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
getByIdAndSecret,
|
||||
list,
|
||||
loadFirstClient
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
const OAuthClientSchema = mongoose.Schema({
|
||||
clientSecret: String,
|
||||
grants: Array,
|
||||
redirectUris: Array
|
||||
})
|
||||
|
||||
OAuthClientSchema.path('clientSecret').required(true)
|
||||
|
||||
OAuthClientSchema.statics = {
|
||||
getByIdAndSecret,
|
||||
list,
|
||||
loadFirstClient
|
||||
return OAuthClient
|
||||
}
|
||||
|
||||
mongoose.model('OAuthClient', OAuthClientSchema)
|
||||
// TODO: validation
|
||||
// OAuthClientSchema.path('clientSecret').required(true)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function associate (models) {
|
||||
this.hasMany(models.OAuthToken, {
|
||||
foreignKey: {
|
||||
name: 'oAuthClientId',
|
||||
allowNull: false
|
||||
},
|
||||
onDelete: 'cascade'
|
||||
})
|
||||
}
|
||||
|
||||
function list (callback) {
|
||||
return this.find(callback)
|
||||
return this.findAll().asCallback(callback)
|
||||
}
|
||||
|
||||
function loadFirstClient (callback) {
|
||||
return this.findOne({}, callback)
|
||||
return this.findOne().asCallback(callback)
|
||||
}
|
||||
|
||||
function getByIdAndSecret (id, clientSecret) {
|
||||
return this.findOne({ _id: id, clientSecret: clientSecret }).exec()
|
||||
function getByIdAndSecret (clientId, clientSecret) {
|
||||
const query = {
|
||||
where: {
|
||||
clientId: clientId,
|
||||
clientSecret: clientSecret
|
||||
}
|
||||
}
|
||||
|
||||
return this.findOne(query)
|
||||
}
|
||||
|
|
|
@ -1,42 +1,71 @@
|
|||
const mongoose = require('mongoose')
|
||||
|
||||
const logger = require('../helpers/logger')
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const OAuthTokenSchema = mongoose.Schema({
|
||||
accessToken: String,
|
||||
accessTokenExpiresAt: Date,
|
||||
client: { type: mongoose.Schema.Types.ObjectId, ref: 'OAuthClient' },
|
||||
refreshToken: String,
|
||||
refreshTokenExpiresAt: Date,
|
||||
user: { type: mongoose.Schema.Types.ObjectId, ref: 'User' }
|
||||
})
|
||||
module.exports = function (sequelize, DataTypes) {
|
||||
const OAuthToken = sequelize.define('OAuthToken',
|
||||
{
|
||||
accessToken: {
|
||||
type: DataTypes.STRING
|
||||
},
|
||||
accessTokenExpiresAt: {
|
||||
type: DataTypes.DATE
|
||||
},
|
||||
refreshToken: {
|
||||
type: DataTypes.STRING
|
||||
},
|
||||
refreshTokenExpiresAt: {
|
||||
type: DataTypes.DATE
|
||||
}
|
||||
},
|
||||
{
|
||||
classMethods: {
|
||||
associate,
|
||||
|
||||
OAuthTokenSchema.path('accessToken').required(true)
|
||||
OAuthTokenSchema.path('client').required(true)
|
||||
OAuthTokenSchema.path('user').required(true)
|
||||
getByRefreshTokenAndPopulateClient,
|
||||
getByTokenAndPopulateUser,
|
||||
getByRefreshTokenAndPopulateUser,
|
||||
removeByUserId
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
OAuthTokenSchema.statics = {
|
||||
getByRefreshTokenAndPopulateClient,
|
||||
getByTokenAndPopulateUser,
|
||||
getByRefreshTokenAndPopulateUser,
|
||||
removeByUserId
|
||||
return OAuthToken
|
||||
}
|
||||
|
||||
mongoose.model('OAuthToken', OAuthTokenSchema)
|
||||
// TODO: validation
|
||||
// OAuthTokenSchema.path('accessToken').required(true)
|
||||
// OAuthTokenSchema.path('client').required(true)
|
||||
// OAuthTokenSchema.path('user').required(true)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function associate (models) {
|
||||
this.belongsTo(models.User, {
|
||||
foreignKey: {
|
||||
name: 'userId',
|
||||
allowNull: false
|
||||
},
|
||||
onDelete: 'cascade'
|
||||
})
|
||||
}
|
||||
|
||||
function getByRefreshTokenAndPopulateClient (refreshToken) {
|
||||
return this.findOne({ refreshToken: refreshToken }).populate('client').exec().then(function (token) {
|
||||
const query = {
|
||||
where: {
|
||||
refreshToken: refreshToken
|
||||
},
|
||||
include: [ this.associations.OAuthClient ]
|
||||
}
|
||||
|
||||
return this.findOne(query).then(function (token) {
|
||||
if (!token) return token
|
||||
|
||||
const tokenInfos = {
|
||||
refreshToken: token.refreshToken,
|
||||
refreshTokenExpiresAt: token.refreshTokenExpiresAt,
|
||||
client: {
|
||||
id: token.client._id.toString()
|
||||
id: token.client.id
|
||||
},
|
||||
user: {
|
||||
id: token.user
|
||||
|
@ -50,13 +79,41 @@ function getByRefreshTokenAndPopulateClient (refreshToken) {
|
|||
}
|
||||
|
||||
function getByTokenAndPopulateUser (bearerToken) {
|
||||
return this.findOne({ accessToken: bearerToken }).populate('user').exec()
|
||||
const query = {
|
||||
where: {
|
||||
accessToken: bearerToken
|
||||
},
|
||||
include: [ this.sequelize.models.User ]
|
||||
}
|
||||
|
||||
return this.findOne(query).then(function (token) {
|
||||
if (token) token.user = token.User
|
||||
|
||||
return token
|
||||
})
|
||||
}
|
||||
|
||||
function getByRefreshTokenAndPopulateUser (refreshToken) {
|
||||
return this.findOne({ refreshToken: refreshToken }).populate('user').exec()
|
||||
const query = {
|
||||
where: {
|
||||
refreshToken: refreshToken
|
||||
},
|
||||
include: [ this.sequelize.models.User ]
|
||||
}
|
||||
|
||||
return this.findOne(query).then(function (token) {
|
||||
token.user = token.User
|
||||
|
||||
return token
|
||||
})
|
||||
}
|
||||
|
||||
function removeByUserId (userId, callback) {
|
||||
return this.remove({ user: userId }, callback)
|
||||
const query = {
|
||||
where: {
|
||||
userId: userId
|
||||
}
|
||||
}
|
||||
|
||||
return this.destroy(query).asCallback(callback)
|
||||
}
|
||||
|
|
|
@ -1,79 +1,62 @@
|
|||
'use strict'
|
||||
|
||||
const each = require('async/each')
|
||||
const mongoose = require('mongoose')
|
||||
const map = require('lodash/map')
|
||||
const validator = require('express-validator').validator
|
||||
|
||||
const constants = require('../initializers/constants')
|
||||
|
||||
const Video = mongoose.model('Video')
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const PodSchema = mongoose.Schema({
|
||||
host: String,
|
||||
publicKey: String,
|
||||
score: { type: Number, max: constants.FRIEND_SCORE.MAX },
|
||||
createdDate: {
|
||||
type: Date,
|
||||
default: Date.now
|
||||
}
|
||||
})
|
||||
module.exports = function (sequelize, DataTypes) {
|
||||
const Pod = sequelize.define('Pod',
|
||||
{
|
||||
host: {
|
||||
type: DataTypes.STRING
|
||||
},
|
||||
publicKey: {
|
||||
type: DataTypes.STRING(5000)
|
||||
},
|
||||
score: {
|
||||
type: DataTypes.INTEGER,
|
||||
defaultValue: constants.FRIEND_SCORE.BASE
|
||||
}
|
||||
// Check createdAt
|
||||
},
|
||||
{
|
||||
classMethods: {
|
||||
associate,
|
||||
|
||||
PodSchema.path('host').validate(validator.isURL)
|
||||
PodSchema.path('publicKey').required(true)
|
||||
PodSchema.path('score').validate(function (value) { return !isNaN(value) })
|
||||
countAll,
|
||||
incrementScores,
|
||||
list,
|
||||
listAllIds,
|
||||
listBadPods,
|
||||
load,
|
||||
loadByHost,
|
||||
removeAll
|
||||
},
|
||||
instanceMethods: {
|
||||
toFormatedJSON
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
PodSchema.methods = {
|
||||
toFormatedJSON
|
||||
return Pod
|
||||
}
|
||||
|
||||
PodSchema.statics = {
|
||||
countAll,
|
||||
incrementScores,
|
||||
list,
|
||||
listAllIds,
|
||||
listBadPods,
|
||||
load,
|
||||
loadByHost,
|
||||
removeAll
|
||||
}
|
||||
|
||||
PodSchema.pre('save', function (next) {
|
||||
const self = this
|
||||
|
||||
Pod.loadByHost(this.host, function (err, pod) {
|
||||
if (err) return next(err)
|
||||
|
||||
if (pod) return next(new Error('Pod already exists.'))
|
||||
|
||||
self.score = constants.FRIEND_SCORE.BASE
|
||||
return next()
|
||||
})
|
||||
})
|
||||
|
||||
PodSchema.pre('remove', function (next) {
|
||||
// Remove the videos owned by this pod too
|
||||
Video.listByHost(this.host, function (err, videos) {
|
||||
if (err) return next(err)
|
||||
|
||||
each(videos, function (video, callbackEach) {
|
||||
video.remove(callbackEach)
|
||||
}, next)
|
||||
})
|
||||
})
|
||||
|
||||
const Pod = mongoose.model('Pod', PodSchema)
|
||||
// TODO: max score -> constants.FRIENDS_SCORE.MAX
|
||||
// TODO: validation
|
||||
// PodSchema.path('host').validate(validator.isURL)
|
||||
// PodSchema.path('publicKey').required(true)
|
||||
// PodSchema.path('score').validate(function (value) { return !isNaN(value) })
|
||||
|
||||
// ------------------------------ METHODS ------------------------------
|
||||
|
||||
function toFormatedJSON () {
|
||||
const json = {
|
||||
id: this._id,
|
||||
id: this.id,
|
||||
host: this.host,
|
||||
score: this.score,
|
||||
createdDate: this.createdDate
|
||||
createdAt: this.createdAt
|
||||
}
|
||||
|
||||
return json
|
||||
|
@ -81,39 +64,76 @@ function toFormatedJSON () {
|
|||
|
||||
// ------------------------------ Statics ------------------------------
|
||||
|
||||
function associate (models) {
|
||||
this.belongsToMany(models.Request, {
|
||||
foreignKey: 'podId',
|
||||
through: models.RequestToPod,
|
||||
onDelete: 'CASCADE'
|
||||
})
|
||||
}
|
||||
|
||||
function countAll (callback) {
|
||||
return this.count(callback)
|
||||
return this.count().asCallback(callback)
|
||||
}
|
||||
|
||||
function incrementScores (ids, value, callback) {
|
||||
if (!callback) callback = function () {}
|
||||
return this.update({ _id: { $in: ids } }, { $inc: { score: value } }, { multi: true }, callback)
|
||||
|
||||
const update = {
|
||||
score: this.sequelize.literal('score +' + value)
|
||||
}
|
||||
|
||||
const query = {
|
||||
where: {
|
||||
id: {
|
||||
$in: ids
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this.update(update, query).asCallback(callback)
|
||||
}
|
||||
|
||||
function list (callback) {
|
||||
return this.find(callback)
|
||||
return this.findAll().asCallback(callback)
|
||||
}
|
||||
|
||||
function listAllIds (callback) {
|
||||
return this.find({}, { _id: 1 }, function (err, pods) {
|
||||
const query = {
|
||||
attributes: [ 'id' ]
|
||||
}
|
||||
|
||||
return this.findAll(query).asCallback(function (err, pods) {
|
||||
if (err) return callback(err)
|
||||
|
||||
return callback(null, map(pods, '_id'))
|
||||
return callback(null, map(pods, 'id'))
|
||||
})
|
||||
}
|
||||
|
||||
function listBadPods (callback) {
|
||||
return this.find({ score: 0 }, callback)
|
||||
const query = {
|
||||
where: {
|
||||
score: { $lte: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
return this.findAll(query).asCallback(callback)
|
||||
}
|
||||
|
||||
function load (id, callback) {
|
||||
return this.findById(id, callback)
|
||||
return this.findById(id).asCallback(callback)
|
||||
}
|
||||
|
||||
function loadByHost (host, callback) {
|
||||
return this.findOne({ host }, callback)
|
||||
const query = {
|
||||
where: {
|
||||
host: host
|
||||
}
|
||||
}
|
||||
|
||||
return this.findOne(query).asCallback(callback)
|
||||
}
|
||||
|
||||
function removeAll (callback) {
|
||||
return this.remove({}, callback)
|
||||
return this.destroy().asCallback(callback)
|
||||
}
|
||||
|
|
|
@ -2,66 +2,58 @@
|
|||
|
||||
const each = require('async/each')
|
||||
const eachLimit = require('async/eachLimit')
|
||||
const values = require('lodash/values')
|
||||
const mongoose = require('mongoose')
|
||||
const waterfall = require('async/waterfall')
|
||||
|
||||
const constants = require('../initializers/constants')
|
||||
const logger = require('../helpers/logger')
|
||||
const requests = require('../helpers/requests')
|
||||
|
||||
const Pod = mongoose.model('Pod')
|
||||
|
||||
let timer = null
|
||||
let lastRequestTimestamp = 0
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const RequestSchema = mongoose.Schema({
|
||||
request: mongoose.Schema.Types.Mixed,
|
||||
endpoint: {
|
||||
type: String,
|
||||
enum: [ values(constants.REQUEST_ENDPOINTS) ]
|
||||
},
|
||||
to: [
|
||||
module.exports = function (sequelize, DataTypes) {
|
||||
const Request = sequelize.define('Request',
|
||||
{
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: 'Pod'
|
||||
}
|
||||
]
|
||||
})
|
||||
request: {
|
||||
type: DataTypes.JSON
|
||||
},
|
||||
endpoint: {
|
||||
// TODO: enum?
|
||||
type: DataTypes.STRING
|
||||
}
|
||||
},
|
||||
{
|
||||
classMethods: {
|
||||
associate,
|
||||
|
||||
RequestSchema.statics = {
|
||||
activate,
|
||||
deactivate,
|
||||
flush,
|
||||
forceSend,
|
||||
list,
|
||||
remainingMilliSeconds
|
||||
activate,
|
||||
countTotalRequests,
|
||||
deactivate,
|
||||
flush,
|
||||
forceSend,
|
||||
remainingMilliSeconds
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
return Request
|
||||
}
|
||||
|
||||
RequestSchema.pre('save', function (next) {
|
||||
const self = this
|
||||
|
||||
if (self.to.length === 0) {
|
||||
Pod.listAllIds(function (err, podIds) {
|
||||
if (err) return next(err)
|
||||
|
||||
// No friends
|
||||
if (podIds.length === 0) return
|
||||
|
||||
self.to = podIds
|
||||
return next()
|
||||
})
|
||||
} else {
|
||||
return next()
|
||||
}
|
||||
})
|
||||
|
||||
mongoose.model('Request', RequestSchema)
|
||||
|
||||
// ------------------------------ STATICS ------------------------------
|
||||
|
||||
function associate (models) {
|
||||
this.belongsToMany(models.Pod, {
|
||||
foreignKey: {
|
||||
name: 'requestId',
|
||||
allowNull: false
|
||||
},
|
||||
through: models.RequestToPod,
|
||||
onDelete: 'CASCADE'
|
||||
})
|
||||
}
|
||||
|
||||
function activate () {
|
||||
logger.info('Requests scheduler activated.')
|
||||
lastRequestTimestamp = Date.now()
|
||||
|
@ -73,6 +65,14 @@ function activate () {
|
|||
}, constants.REQUESTS_INTERVAL)
|
||||
}
|
||||
|
||||
function countTotalRequests (callback) {
|
||||
const query = {
|
||||
include: [ this.sequelize.models.Pod ]
|
||||
}
|
||||
|
||||
return this.count(query).asCallback(callback)
|
||||
}
|
||||
|
||||
function deactivate () {
|
||||
logger.info('Requests scheduler deactivated.')
|
||||
clearInterval(timer)
|
||||
|
@ -90,10 +90,6 @@ function forceSend () {
|
|||
makeRequests.call(this)
|
||||
}
|
||||
|
||||
function list (callback) {
|
||||
this.find({ }, callback)
|
||||
}
|
||||
|
||||
function remainingMilliSeconds () {
|
||||
if (timer === null) return -1
|
||||
|
||||
|
@ -136,6 +132,7 @@ function makeRequest (toPod, requestEndpoint, requestsToMake, callback) {
|
|||
// Make all the requests of the scheduler
|
||||
function makeRequests () {
|
||||
const self = this
|
||||
const RequestToPod = this.sequelize.models.RequestToPod
|
||||
|
||||
// We limit the size of the requests (REQUESTS_LIMIT)
|
||||
// We don't want to stuck with the same failing requests so we get a random list
|
||||
|
@ -156,20 +153,20 @@ function makeRequests () {
|
|||
// We want to group requests by destinations pod and endpoint
|
||||
const requestsToMakeGrouped = {}
|
||||
|
||||
requests.forEach(function (poolRequest) {
|
||||
poolRequest.to.forEach(function (toPodId) {
|
||||
const hashKey = toPodId + poolRequest.endpoint
|
||||
requests.forEach(function (request) {
|
||||
request.Pods.forEach(function (toPod) {
|
||||
const hashKey = toPod.id + request.endpoint
|
||||
if (!requestsToMakeGrouped[hashKey]) {
|
||||
requestsToMakeGrouped[hashKey] = {
|
||||
toPodId,
|
||||
endpoint: poolRequest.endpoint,
|
||||
ids: [], // pool request ids, to delete them from the DB in the future
|
||||
toPodId: toPod.id,
|
||||
endpoint: request.endpoint,
|
||||
ids: [], // request ids, to delete them from the DB in the future
|
||||
datas: [] // requests data,
|
||||
}
|
||||
}
|
||||
|
||||
requestsToMakeGrouped[hashKey].ids.push(poolRequest._id)
|
||||
requestsToMakeGrouped[hashKey].datas.push(poolRequest.request)
|
||||
requestsToMakeGrouped[hashKey].ids.push(request.id)
|
||||
requestsToMakeGrouped[hashKey].datas.push(request.request)
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -179,8 +176,8 @@ function makeRequests () {
|
|||
eachLimit(Object.keys(requestsToMakeGrouped), constants.REQUESTS_IN_PARALLEL, function (hashKey, callbackEach) {
|
||||
const requestToMake = requestsToMakeGrouped[hashKey]
|
||||
|
||||
// FIXME: mongodb request inside a loop :/
|
||||
Pod.load(requestToMake.toPodId, function (err, toPod) {
|
||||
// FIXME: SQL request inside a loop :/
|
||||
self.sequelize.models.Pod.load(requestToMake.toPodId, function (err, toPod) {
|
||||
if (err) {
|
||||
logger.error('Error finding pod by id.', { err: err })
|
||||
return callbackEach()
|
||||
|
@ -191,7 +188,7 @@ function makeRequests () {
|
|||
const requestIdsToDelete = requestToMake.ids
|
||||
|
||||
logger.info('Removing %d requests of unexisting pod %s.', requestIdsToDelete.length, requestToMake.toPodId)
|
||||
removePodOf.call(self, requestIdsToDelete, requestToMake.toPodId)
|
||||
RequestToPod.removePodOf.call(self, requestIdsToDelete, requestToMake.toPodId)
|
||||
return callbackEach()
|
||||
}
|
||||
|
||||
|
@ -202,7 +199,7 @@ function makeRequests () {
|
|||
goodPods.push(requestToMake.toPodId)
|
||||
|
||||
// Remove the pod id of these request ids
|
||||
removePodOf.call(self, requestToMake.ids, requestToMake.toPodId, callbackEach)
|
||||
RequestToPod.removePodOf(requestToMake.ids, requestToMake.toPodId, callbackEach)
|
||||
} else {
|
||||
badPods.push(requestToMake.toPodId)
|
||||
callbackEach()
|
||||
|
@ -211,18 +208,22 @@ function makeRequests () {
|
|||
})
|
||||
}, function () {
|
||||
// All the requests were made, we update the pods score
|
||||
updatePodsScore(goodPods, badPods)
|
||||
updatePodsScore.call(self, goodPods, badPods)
|
||||
// Flush requests with no pod
|
||||
removeWithEmptyTo.call(self)
|
||||
removeWithEmptyTo.call(self, function (err) {
|
||||
if (err) logger.error('Error when removing requests with no pods.', { error: err })
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Remove pods with a score of 0 (too many requests where they were unreachable)
|
||||
function removeBadPods () {
|
||||
const self = this
|
||||
|
||||
waterfall([
|
||||
function findBadPods (callback) {
|
||||
Pod.listBadPods(function (err, pods) {
|
||||
self.sequelize.models.Pod.listBadPods(function (err, pods) {
|
||||
if (err) {
|
||||
logger.error('Cannot find bad pods.', { error: err })
|
||||
return callback(err)
|
||||
|
@ -233,10 +234,8 @@ function removeBadPods () {
|
|||
},
|
||||
|
||||
function removeTheseBadPods (pods, callback) {
|
||||
if (pods.length === 0) return callback(null, 0)
|
||||
|
||||
each(pods, function (pod, callbackEach) {
|
||||
pod.remove(callbackEach)
|
||||
pod.destroy().asCallback(callbackEach)
|
||||
}, function (err) {
|
||||
return callback(err, pods.length)
|
||||
})
|
||||
|
@ -253,43 +252,67 @@ function removeBadPods () {
|
|||
}
|
||||
|
||||
function updatePodsScore (goodPods, badPods) {
|
||||
const self = this
|
||||
const Pod = this.sequelize.models.Pod
|
||||
|
||||
logger.info('Updating %d good pods and %d bad pods scores.', goodPods.length, badPods.length)
|
||||
|
||||
Pod.incrementScores(goodPods, constants.PODS_SCORE.BONUS, function (err) {
|
||||
if (err) logger.error('Cannot increment scores of good pods.')
|
||||
})
|
||||
if (goodPods.length !== 0) {
|
||||
Pod.incrementScores(goodPods, constants.PODS_SCORE.BONUS, function (err) {
|
||||
if (err) logger.error('Cannot increment scores of good pods.')
|
||||
})
|
||||
}
|
||||
|
||||
Pod.incrementScores(badPods, constants.PODS_SCORE.MALUS, function (err) {
|
||||
if (err) logger.error('Cannot decrement scores of bad pods.')
|
||||
removeBadPods()
|
||||
})
|
||||
if (badPods.length !== 0) {
|
||||
Pod.incrementScores(badPods, constants.PODS_SCORE.MALUS, function (err) {
|
||||
if (err) logger.error('Cannot decrement scores of bad pods.')
|
||||
removeBadPods.call(self)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function listWithLimitAndRandom (limit, callback) {
|
||||
const self = this
|
||||
|
||||
self.count(function (err, count) {
|
||||
self.count().asCallback(function (err, count) {
|
||||
if (err) return callback(err)
|
||||
|
||||
// Optimization...
|
||||
if (count === 0) return callback(null, [])
|
||||
|
||||
let start = Math.floor(Math.random() * count) - limit
|
||||
if (start < 0) start = 0
|
||||
|
||||
self.find().sort({ _id: 1 }).skip(start).limit(limit).exec(callback)
|
||||
const query = {
|
||||
order: [
|
||||
[ 'id', 'ASC' ]
|
||||
],
|
||||
offset: start,
|
||||
limit: limit,
|
||||
include: [ this.sequelize.models.Pod ]
|
||||
}
|
||||
|
||||
self.findAll(query).asCallback(callback)
|
||||
})
|
||||
}
|
||||
|
||||
function removeAll (callback) {
|
||||
this.remove({ }, callback)
|
||||
}
|
||||
|
||||
function removePodOf (requestsIds, podId, callback) {
|
||||
if (!callback) callback = function () {}
|
||||
|
||||
this.update({ _id: { $in: requestsIds } }, { $pull: { to: podId } }, { multi: true }, callback)
|
||||
// Delete all requests
|
||||
this.destroy({ truncate: true }).asCallback(callback)
|
||||
}
|
||||
|
||||
function removeWithEmptyTo (callback) {
|
||||
if (!callback) callback = function () {}
|
||||
|
||||
this.remove({ to: { $size: 0 } }, callback)
|
||||
const query = {
|
||||
where: {
|
||||
id: {
|
||||
$notIn: [
|
||||
this.sequelize.literal('SELECT "requestId" FROM "RequestToPods"')
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.destroy(query).asCallback(callback)
|
||||
}
|
||||
|
|
30
server/models/requestToPod.js
Normal file
30
server/models/requestToPod.js
Normal file
|
@ -0,0 +1,30 @@
|
|||
'use strict'
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
module.exports = function (sequelize, DataTypes) {
|
||||
const RequestToPod = sequelize.define('RequestToPod', {}, {
|
||||
classMethods: {
|
||||
removePodOf
|
||||
}
|
||||
})
|
||||
|
||||
return RequestToPod
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function removePodOf (requestsIds, podId, callback) {
|
||||
if (!callback) callback = function () {}
|
||||
|
||||
const query = {
|
||||
where: {
|
||||
requestId: {
|
||||
$in: requestsIds
|
||||
},
|
||||
podId: podId
|
||||
}
|
||||
}
|
||||
|
||||
this.destroy(query).asCallback(callback)
|
||||
}
|
|
@ -1,60 +1,60 @@
|
|||
const mongoose = require('mongoose')
|
||||
|
||||
const customUsersValidators = require('../helpers/custom-validators').users
|
||||
const modelUtils = require('./utils')
|
||||
const peertubeCrypto = require('../helpers/peertube-crypto')
|
||||
|
||||
const OAuthToken = mongoose.model('OAuthToken')
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const UserSchema = mongoose.Schema({
|
||||
createdDate: {
|
||||
type: Date,
|
||||
default: Date.now
|
||||
},
|
||||
password: String,
|
||||
username: String,
|
||||
role: String
|
||||
})
|
||||
module.exports = function (sequelize, DataTypes) {
|
||||
const User = sequelize.define('User',
|
||||
{
|
||||
password: {
|
||||
type: DataTypes.STRING
|
||||
},
|
||||
username: {
|
||||
type: DataTypes.STRING
|
||||
},
|
||||
role: {
|
||||
type: DataTypes.STRING
|
||||
}
|
||||
},
|
||||
{
|
||||
classMethods: {
|
||||
associate,
|
||||
|
||||
UserSchema.path('password').required(customUsersValidators.isUserPasswordValid)
|
||||
UserSchema.path('username').required(customUsersValidators.isUserUsernameValid)
|
||||
UserSchema.path('role').validate(customUsersValidators.isUserRoleValid)
|
||||
countTotal,
|
||||
getByUsername,
|
||||
list,
|
||||
listForApi,
|
||||
loadById,
|
||||
loadByUsername
|
||||
},
|
||||
instanceMethods: {
|
||||
isPasswordMatch,
|
||||
toFormatedJSON
|
||||
},
|
||||
hooks: {
|
||||
beforeCreate: beforeCreateOrUpdate,
|
||||
beforeUpdate: beforeCreateOrUpdate
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
UserSchema.methods = {
|
||||
isPasswordMatch,
|
||||
toFormatedJSON
|
||||
return User
|
||||
}
|
||||
|
||||
UserSchema.statics = {
|
||||
countTotal,
|
||||
getByUsername,
|
||||
list,
|
||||
listForApi,
|
||||
loadById,
|
||||
loadByUsername
|
||||
}
|
||||
// TODO: Validation
|
||||
// UserSchema.path('password').required(customUsersValidators.isUserPasswordValid)
|
||||
// UserSchema.path('username').required(customUsersValidators.isUserUsernameValid)
|
||||
// UserSchema.path('role').validate(customUsersValidators.isUserRoleValid)
|
||||
|
||||
UserSchema.pre('save', function (next) {
|
||||
const user = this
|
||||
|
||||
peertubeCrypto.cryptPassword(this.password, function (err, hash) {
|
||||
function beforeCreateOrUpdate (user, options, next) {
|
||||
peertubeCrypto.cryptPassword(user.password, function (err, hash) {
|
||||
if (err) return next(err)
|
||||
|
||||
user.password = hash
|
||||
|
||||
return next()
|
||||
})
|
||||
})
|
||||
|
||||
UserSchema.pre('remove', function (next) {
|
||||
const user = this
|
||||
|
||||
OAuthToken.removeByUserId(user._id, next)
|
||||
})
|
||||
|
||||
mongoose.model('User', UserSchema)
|
||||
}
|
||||
|
||||
// ------------------------------ METHODS ------------------------------
|
||||
|
||||
|
@ -64,35 +64,63 @@ function isPasswordMatch (password, callback) {
|
|||
|
||||
function toFormatedJSON () {
|
||||
return {
|
||||
id: this._id,
|
||||
id: this.id,
|
||||
username: this.username,
|
||||
role: this.role,
|
||||
createdDate: this.createdDate
|
||||
createdAt: this.createdAt
|
||||
}
|
||||
}
|
||||
// ------------------------------ STATICS ------------------------------
|
||||
|
||||
function associate (models) {
|
||||
this.hasMany(models.OAuthToken, {
|
||||
foreignKey: 'userId',
|
||||
onDelete: 'cascade'
|
||||
})
|
||||
}
|
||||
|
||||
function countTotal (callback) {
|
||||
return this.count(callback)
|
||||
return this.count().asCallback(callback)
|
||||
}
|
||||
|
||||
function getByUsername (username) {
|
||||
return this.findOne({ username: username })
|
||||
const query = {
|
||||
where: {
|
||||
username: username
|
||||
}
|
||||
}
|
||||
|
||||
return this.findOne(query)
|
||||
}
|
||||
|
||||
function list (callback) {
|
||||
return this.find(callback)
|
||||
return this.find().asCallback(callback)
|
||||
}
|
||||
|
||||
function listForApi (start, count, sort, callback) {
|
||||
const query = {}
|
||||
return modelUtils.listForApiWithCount.call(this, query, start, count, sort, callback)
|
||||
const query = {
|
||||
offset: start,
|
||||
limit: count,
|
||||
order: [ modelUtils.getSort(sort) ]
|
||||
}
|
||||
|
||||
return this.findAndCountAll(query).asCallback(function (err, result) {
|
||||
if (err) return callback(err)
|
||||
|
||||
return callback(null, result.rows, result.count)
|
||||
})
|
||||
}
|
||||
|
||||
function loadById (id, callback) {
|
||||
return this.findById(id, callback)
|
||||
return this.findById(id).asCallback(callback)
|
||||
}
|
||||
|
||||
function loadByUsername (username, callback) {
|
||||
return this.findOne({ username: username }, callback)
|
||||
const query = {
|
||||
where: {
|
||||
username: username
|
||||
}
|
||||
}
|
||||
|
||||
return this.findOne(query).asCallback(callback)
|
||||
}
|
||||
|
|
|
@ -1,28 +1,23 @@
|
|||
'use strict'
|
||||
|
||||
const parallel = require('async/parallel')
|
||||
|
||||
const utils = {
|
||||
listForApiWithCount
|
||||
getSort
|
||||
}
|
||||
|
||||
function listForApiWithCount (query, start, count, sort, callback) {
|
||||
const self = this
|
||||
// Translate for example "-name" to [ 'name', 'DESC' ]
|
||||
function getSort (value) {
|
||||
let field
|
||||
let direction
|
||||
|
||||
parallel([
|
||||
function (asyncCallback) {
|
||||
self.find(query).skip(start).limit(count).sort(sort).exec(asyncCallback)
|
||||
},
|
||||
function (asyncCallback) {
|
||||
self.count(query, asyncCallback)
|
||||
}
|
||||
], function (err, results) {
|
||||
if (err) return callback(err)
|
||||
if (value.substring(0, 1) === '-') {
|
||||
direction = 'DESC'
|
||||
field = value.substring(1)
|
||||
} else {
|
||||
direction = 'ASC'
|
||||
field = value
|
||||
}
|
||||
|
||||
const data = results[0]
|
||||
const total = results[1]
|
||||
return callback(null, data, total)
|
||||
})
|
||||
return [ field, direction ]
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
|
@ -7,70 +7,134 @@ const magnetUtil = require('magnet-uri')
|
|||
const parallel = require('async/parallel')
|
||||
const parseTorrent = require('parse-torrent')
|
||||
const pathUtils = require('path')
|
||||
const mongoose = require('mongoose')
|
||||
|
||||
const constants = require('../initializers/constants')
|
||||
const customVideosValidators = require('../helpers/custom-validators').videos
|
||||
const logger = require('../helpers/logger')
|
||||
const modelUtils = require('./utils')
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
module.exports = function (sequelize, DataTypes) {
|
||||
// TODO: add indexes on searchable columns
|
||||
const VideoSchema = mongoose.Schema({
|
||||
name: String,
|
||||
extname: {
|
||||
type: String,
|
||||
enum: [ '.mp4', '.webm', '.ogv' ]
|
||||
},
|
||||
remoteId: mongoose.Schema.Types.ObjectId,
|
||||
description: String,
|
||||
magnet: {
|
||||
infoHash: String
|
||||
},
|
||||
podHost: String,
|
||||
author: String,
|
||||
duration: Number,
|
||||
tags: [ String ],
|
||||
createdDate: {
|
||||
type: Date,
|
||||
default: Date.now
|
||||
const Video = sequelize.define('Video',
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true
|
||||
},
|
||||
name: {
|
||||
type: DataTypes.STRING
|
||||
},
|
||||
extname: {
|
||||
// TODO: enum?
|
||||
type: DataTypes.STRING
|
||||
},
|
||||
remoteId: {
|
||||
type: DataTypes.UUID
|
||||
},
|
||||
description: {
|
||||
type: DataTypes.STRING
|
||||
},
|
||||
infoHash: {
|
||||
type: DataTypes.STRING
|
||||
},
|
||||
duration: {
|
||||
type: DataTypes.INTEGER
|
||||
},
|
||||
tags: {
|
||||
type: DataTypes.ARRAY(DataTypes.STRING)
|
||||
}
|
||||
},
|
||||
{
|
||||
classMethods: {
|
||||
associate,
|
||||
|
||||
generateThumbnailFromBase64,
|
||||
getDurationFromFile,
|
||||
listForApi,
|
||||
listByHostAndRemoteId,
|
||||
listOwnedAndPopulateAuthor,
|
||||
listOwnedByAuthor,
|
||||
load,
|
||||
loadAndPopulateAuthor,
|
||||
loadAndPopulateAuthorAndPod,
|
||||
searchAndPopulateAuthorAndPod
|
||||
},
|
||||
instanceMethods: {
|
||||
generateMagnetUri,
|
||||
getVideoFilename,
|
||||
getThumbnailName,
|
||||
getPreviewName,
|
||||
getTorrentName,
|
||||
isOwned,
|
||||
toFormatedJSON,
|
||||
toRemoteJSON
|
||||
},
|
||||
hooks: {
|
||||
beforeCreate,
|
||||
afterDestroy
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
return Video
|
||||
}
|
||||
|
||||
// TODO: Validation
|
||||
// VideoSchema.path('name').validate(customVideosValidators.isVideoNameValid)
|
||||
// VideoSchema.path('description').validate(customVideosValidators.isVideoDescriptionValid)
|
||||
// VideoSchema.path('podHost').validate(customVideosValidators.isVideoPodHostValid)
|
||||
// VideoSchema.path('author').validate(customVideosValidators.isVideoAuthorValid)
|
||||
// VideoSchema.path('duration').validate(customVideosValidators.isVideoDurationValid)
|
||||
// VideoSchema.path('tags').validate(customVideosValidators.isVideoTagsValid)
|
||||
|
||||
function beforeCreate (video, options, next) {
|
||||
const tasks = []
|
||||
|
||||
if (video.isOwned()) {
|
||||
const videoPath = pathUtils.join(constants.CONFIG.STORAGE.VIDEOS_DIR, video.getVideoFilename())
|
||||
|
||||
tasks.push(
|
||||
// TODO: refractoring
|
||||
function (callback) {
|
||||
const options = {
|
||||
announceList: [
|
||||
[ constants.CONFIG.WEBSERVER.WS + '://' + constants.CONFIG.WEBSERVER.HOSTNAME + ':' + constants.CONFIG.WEBSERVER.PORT + '/tracker/socket' ]
|
||||
],
|
||||
urlList: [
|
||||
constants.CONFIG.WEBSERVER.URL + constants.STATIC_PATHS.WEBSEED + video.getVideoFilename()
|
||||
]
|
||||
}
|
||||
|
||||
createTorrent(videoPath, options, function (err, torrent) {
|
||||
if (err) return callback(err)
|
||||
|
||||
fs.writeFile(constants.CONFIG.STORAGE.TORRENTS_DIR + video.getTorrentName(), torrent, function (err) {
|
||||
if (err) return callback(err)
|
||||
|
||||
const parsedTorrent = parseTorrent(torrent)
|
||||
video.infoHash = parsedTorrent.infoHash
|
||||
|
||||
callback(null)
|
||||
})
|
||||
})
|
||||
},
|
||||
function (callback) {
|
||||
createThumbnail(video, videoPath, callback)
|
||||
},
|
||||
function (callback) {
|
||||
createPreview(video, videoPath, callback)
|
||||
}
|
||||
)
|
||||
|
||||
return parallel(tasks, next)
|
||||
}
|
||||
})
|
||||
|
||||
VideoSchema.path('name').validate(customVideosValidators.isVideoNameValid)
|
||||
VideoSchema.path('description').validate(customVideosValidators.isVideoDescriptionValid)
|
||||
VideoSchema.path('podHost').validate(customVideosValidators.isVideoPodHostValid)
|
||||
VideoSchema.path('author').validate(customVideosValidators.isVideoAuthorValid)
|
||||
VideoSchema.path('duration').validate(customVideosValidators.isVideoDurationValid)
|
||||
VideoSchema.path('tags').validate(customVideosValidators.isVideoTagsValid)
|
||||
|
||||
VideoSchema.methods = {
|
||||
generateMagnetUri,
|
||||
getVideoFilename,
|
||||
getThumbnailName,
|
||||
getPreviewName,
|
||||
getTorrentName,
|
||||
isOwned,
|
||||
toFormatedJSON,
|
||||
toRemoteJSON
|
||||
return next()
|
||||
}
|
||||
|
||||
VideoSchema.statics = {
|
||||
generateThumbnailFromBase64,
|
||||
getDurationFromFile,
|
||||
listForApi,
|
||||
listByHostAndRemoteId,
|
||||
listByHost,
|
||||
listOwned,
|
||||
listOwnedByAuthor,
|
||||
listRemotes,
|
||||
load,
|
||||
search
|
||||
}
|
||||
|
||||
VideoSchema.pre('remove', function (next) {
|
||||
const video = this
|
||||
function afterDestroy (video, options, next) {
|
||||
const tasks = []
|
||||
|
||||
tasks.push(
|
||||
|
@ -94,59 +158,20 @@ VideoSchema.pre('remove', function (next) {
|
|||
}
|
||||
|
||||
parallel(tasks, next)
|
||||
})
|
||||
|
||||
VideoSchema.pre('save', function (next) {
|
||||
const video = this
|
||||
const tasks = []
|
||||
|
||||
if (video.isOwned()) {
|
||||
const videoPath = pathUtils.join(constants.CONFIG.STORAGE.VIDEOS_DIR, video.getVideoFilename())
|
||||
this.podHost = constants.CONFIG.WEBSERVER.HOST
|
||||
|
||||
tasks.push(
|
||||
// TODO: refractoring
|
||||
function (callback) {
|
||||
const options = {
|
||||
announceList: [
|
||||
[ constants.CONFIG.WEBSERVER.WS + '://' + constants.CONFIG.WEBSERVER.HOSTNAME + ':' + constants.CONFIG.WEBSERVER.PORT + '/tracker/socket' ]
|
||||
],
|
||||
urlList: [
|
||||
constants.CONFIG.WEBSERVER.URL + constants.STATIC_PATHS.WEBSEED + video.getVideoFilename()
|
||||
]
|
||||
}
|
||||
|
||||
createTorrent(videoPath, options, function (err, torrent) {
|
||||
if (err) return callback(err)
|
||||
|
||||
fs.writeFile(constants.CONFIG.STORAGE.TORRENTS_DIR + video.getTorrentName(), torrent, function (err) {
|
||||
if (err) return callback(err)
|
||||
|
||||
const parsedTorrent = parseTorrent(torrent)
|
||||
video.magnet.infoHash = parsedTorrent.infoHash
|
||||
|
||||
callback(null)
|
||||
})
|
||||
})
|
||||
},
|
||||
function (callback) {
|
||||
createThumbnail(video, videoPath, callback)
|
||||
},
|
||||
function (callback) {
|
||||
createPreview(video, videoPath, callback)
|
||||
}
|
||||
)
|
||||
|
||||
return parallel(tasks, next)
|
||||
}
|
||||
|
||||
return next()
|
||||
})
|
||||
|
||||
mongoose.model('Video', VideoSchema)
|
||||
}
|
||||
|
||||
// ------------------------------ METHODS ------------------------------
|
||||
|
||||
function associate (models) {
|
||||
this.belongsTo(models.Author, {
|
||||
foreignKey: {
|
||||
name: 'authorId',
|
||||
allowNull: false
|
||||
},
|
||||
onDelete: 'cascade'
|
||||
})
|
||||
}
|
||||
|
||||
function generateMagnetUri () {
|
||||
let baseUrlHttp, baseUrlWs
|
||||
|
||||
|
@ -154,8 +179,8 @@ function generateMagnetUri () {
|
|||
baseUrlHttp = constants.CONFIG.WEBSERVER.URL
|
||||
baseUrlWs = constants.CONFIG.WEBSERVER.WS + '://' + constants.CONFIG.WEBSERVER.HOSTNAME + ':' + constants.CONFIG.WEBSERVER.PORT
|
||||
} else {
|
||||
baseUrlHttp = constants.REMOTE_SCHEME.HTTP + '://' + this.podHost
|
||||
baseUrlWs = constants.REMOTE_SCHEME.WS + '://' + this.podHost
|
||||
baseUrlHttp = constants.REMOTE_SCHEME.HTTP + '://' + this.Author.Pod.host
|
||||
baseUrlWs = constants.REMOTE_SCHEME.WS + '://' + this.Author.Pod.host
|
||||
}
|
||||
|
||||
const xs = baseUrlHttp + constants.STATIC_PATHS.TORRENTS + this.getTorrentName()
|
||||
|
@ -166,7 +191,7 @@ function generateMagnetUri () {
|
|||
xs,
|
||||
announce,
|
||||
urlList,
|
||||
infoHash: this.magnet.infoHash,
|
||||
infoHash: this.infoHash,
|
||||
name: this.name
|
||||
}
|
||||
|
||||
|
@ -174,20 +199,20 @@ function generateMagnetUri () {
|
|||
}
|
||||
|
||||
function getVideoFilename () {
|
||||
if (this.isOwned()) return this._id + this.extname
|
||||
if (this.isOwned()) return this.id + this.extname
|
||||
|
||||
return this.remoteId + this.extname
|
||||
}
|
||||
|
||||
function getThumbnailName () {
|
||||
// We always have a copy of the thumbnail
|
||||
return this._id + '.jpg'
|
||||
return this.id + '.jpg'
|
||||
}
|
||||
|
||||
function getPreviewName () {
|
||||
const extension = '.jpg'
|
||||
|
||||
if (this.isOwned()) return this._id + extension
|
||||
if (this.isOwned()) return this.id + extension
|
||||
|
||||
return this.remoteId + extension
|
||||
}
|
||||
|
@ -195,7 +220,7 @@ function getPreviewName () {
|
|||
function getTorrentName () {
|
||||
const extension = '.torrent'
|
||||
|
||||
if (this.isOwned()) return this._id + extension
|
||||
if (this.isOwned()) return this.id + extension
|
||||
|
||||
return this.remoteId + extension
|
||||
}
|
||||
|
@ -205,18 +230,27 @@ function isOwned () {
|
|||
}
|
||||
|
||||
function toFormatedJSON () {
|
||||
let podHost
|
||||
|
||||
if (this.Author.Pod) {
|
||||
podHost = this.Author.Pod.host
|
||||
} else {
|
||||
// It means it's our video
|
||||
podHost = constants.CONFIG.WEBSERVER.HOST
|
||||
}
|
||||
|
||||
const json = {
|
||||
id: this._id,
|
||||
id: this.id,
|
||||
name: this.name,
|
||||
description: this.description,
|
||||
podHost: this.podHost,
|
||||
podHost,
|
||||
isLocal: this.isOwned(),
|
||||
magnetUri: this.generateMagnetUri(),
|
||||
author: this.author,
|
||||
author: this.Author.name,
|
||||
duration: this.duration,
|
||||
tags: this.tags,
|
||||
thumbnailPath: constants.STATIC_PATHS.THUMBNAILS + '/' + this.getThumbnailName(),
|
||||
createdDate: this.createdDate
|
||||
createdAt: this.createdAt
|
||||
}
|
||||
|
||||
return json
|
||||
|
@ -236,13 +270,13 @@ function toRemoteJSON (callback) {
|
|||
const remoteVideo = {
|
||||
name: self.name,
|
||||
description: self.description,
|
||||
magnet: self.magnet,
|
||||
remoteId: self._id,
|
||||
author: self.author,
|
||||
infoHash: self.infoHash,
|
||||
remoteId: self.id,
|
||||
author: self.Author.name,
|
||||
duration: self.duration,
|
||||
thumbnailBase64: new Buffer(thumbnailData).toString('base64'),
|
||||
tags: self.tags,
|
||||
createdDate: self.createdDate,
|
||||
createdAt: self.createdAt,
|
||||
extname: self.extname
|
||||
}
|
||||
|
||||
|
@ -273,50 +307,168 @@ function getDurationFromFile (videoPath, callback) {
|
|||
}
|
||||
|
||||
function listForApi (start, count, sort, callback) {
|
||||
const query = {}
|
||||
return modelUtils.listForApiWithCount.call(this, query, start, count, sort, callback)
|
||||
const query = {
|
||||
offset: start,
|
||||
limit: count,
|
||||
order: [ modelUtils.getSort(sort) ],
|
||||
include: [
|
||||
{
|
||||
model: this.sequelize.models.Author,
|
||||
include: [ this.sequelize.models.Pod ]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
return this.findAndCountAll(query).asCallback(function (err, result) {
|
||||
if (err) return callback(err)
|
||||
|
||||
return callback(null, result.rows, result.count)
|
||||
})
|
||||
}
|
||||
|
||||
function listByHostAndRemoteId (fromHost, remoteId, callback) {
|
||||
this.find({ podHost: fromHost, remoteId: remoteId }, callback)
|
||||
const query = {
|
||||
where: {
|
||||
remoteId: remoteId
|
||||
},
|
||||
include: [
|
||||
{
|
||||
model: this.sequelize.models.Author,
|
||||
include: [
|
||||
{
|
||||
model: this.sequelize.models.Pod,
|
||||
where: {
|
||||
host: fromHost
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
return this.findAll(query).asCallback(callback)
|
||||
}
|
||||
|
||||
function listByHost (fromHost, callback) {
|
||||
this.find({ podHost: fromHost }, callback)
|
||||
}
|
||||
|
||||
function listOwned (callback) {
|
||||
function listOwnedAndPopulateAuthor (callback) {
|
||||
// If remoteId is null this is *our* video
|
||||
this.find({ remoteId: null }, callback)
|
||||
const query = {
|
||||
where: {
|
||||
remoteId: null
|
||||
},
|
||||
include: [ this.sequelize.models.Author ]
|
||||
}
|
||||
|
||||
return this.findAll(query).asCallback(callback)
|
||||
}
|
||||
|
||||
function listOwnedByAuthor (author, callback) {
|
||||
this.find({ remoteId: null, author: author }, callback)
|
||||
}
|
||||
const query = {
|
||||
where: {
|
||||
remoteId: null
|
||||
},
|
||||
include: [
|
||||
{
|
||||
model: this.sequelize.models.Author,
|
||||
where: {
|
||||
name: author
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
function listRemotes (callback) {
|
||||
this.find({ remoteId: { $ne: null } }, callback)
|
||||
return this.findAll(query).asCallback(callback)
|
||||
}
|
||||
|
||||
function load (id, callback) {
|
||||
this.findById(id, callback)
|
||||
return this.findById(id).asCallback(callback)
|
||||
}
|
||||
|
||||
function search (value, field, start, count, sort, callback) {
|
||||
const query = {}
|
||||
function loadAndPopulateAuthor (id, callback) {
|
||||
const options = {
|
||||
include: [ this.sequelize.models.Author ]
|
||||
}
|
||||
|
||||
return this.findById(id, options).asCallback(callback)
|
||||
}
|
||||
|
||||
function loadAndPopulateAuthorAndPod (id, callback) {
|
||||
const options = {
|
||||
include: [
|
||||
{
|
||||
model: this.sequelize.models.Author,
|
||||
include: [ this.sequelize.models.Pod ]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
return this.findById(id, options).asCallback(callback)
|
||||
}
|
||||
|
||||
function searchAndPopulateAuthorAndPod (value, field, start, count, sort, callback) {
|
||||
const podInclude = {
|
||||
model: this.sequelize.models.Pod
|
||||
}
|
||||
const authorInclude = {
|
||||
model: this.sequelize.models.Author,
|
||||
include: [
|
||||
podInclude
|
||||
]
|
||||
}
|
||||
|
||||
const query = {
|
||||
where: {},
|
||||
include: [
|
||||
authorInclude
|
||||
],
|
||||
offset: start,
|
||||
limit: count,
|
||||
order: [ modelUtils.getSort(sort) ]
|
||||
}
|
||||
|
||||
// TODO: include our pod for podHost searches (we are not stored in the database)
|
||||
// Make an exact search with the magnet
|
||||
if (field === 'magnetUri') {
|
||||
const infoHash = magnetUtil.decode(value).infoHash
|
||||
query.magnet = {
|
||||
infoHash
|
||||
}
|
||||
query.where.infoHash = infoHash
|
||||
} else if (field === 'tags') {
|
||||
query[field] = value
|
||||
query.where[field] = value
|
||||
} else if (field === 'host') {
|
||||
const whereQuery = {
|
||||
'$Author.Pod.host$': {
|
||||
$like: '%' + value + '%'
|
||||
}
|
||||
}
|
||||
|
||||
// Include our pod? (not stored in the database)
|
||||
if (constants.CONFIG.WEBSERVER.HOST.indexOf(value) !== -1) {
|
||||
query.where = {
|
||||
$or: [
|
||||
whereQuery,
|
||||
{
|
||||
remoteId: null
|
||||
}
|
||||
]
|
||||
}
|
||||
} else {
|
||||
query.where = whereQuery
|
||||
}
|
||||
} else if (field === 'author') {
|
||||
query.where = {
|
||||
'$Author.name$': {
|
||||
$like: '%' + value + '%'
|
||||
}
|
||||
}
|
||||
} else {
|
||||
query[field] = new RegExp(value, 'i')
|
||||
query.where[field] = {
|
||||
$like: '%' + value + '%'
|
||||
}
|
||||
}
|
||||
|
||||
modelUtils.listForApiWithCount.call(this, query, start, count, sort, callback)
|
||||
return this.findAndCountAll(query).asCallback(function (err, result) {
|
||||
if (err) return callback(err)
|
||||
|
||||
return callback(null, result.rows, result.count)
|
||||
})
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue