aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/models
diff options
context:
space:
mode:
authorChocobozzz <florian.bigard@gmail.com>2017-05-22 20:58:25 +0200
committerChocobozzz <florian.bigard@gmail.com>2017-05-25 17:32:16 +0200
commite02643f32e4c97ca307f8fc5b69be79c40d70a3b (patch)
treeb7f6269913cd5a0e4f26a9461a043deb0c168be0 /server/models
parent65fcc3119c334b75dd13bcfdebf186afdc580a8f (diff)
downloadPeerTube-e02643f32e4c97ca307f8fc5b69be79c40d70a3b.tar.gz
PeerTube-e02643f32e4c97ca307f8fc5b69be79c40d70a3b.tar.zst
PeerTube-e02643f32e4c97ca307f8fc5b69be79c40d70a3b.zip
Type models
Diffstat (limited to 'server/models')
-rw-r--r--server/models/application-interface.ts23
-rw-r--r--server/models/application.ts38
-rw-r--r--server/models/author-interface.ts21
-rw-r--r--server/models/author.ts36
-rw-r--r--server/models/index.ts17
-rw-r--r--server/models/job-interface.ts23
-rw-r--r--server/models/job.ts29
-rw-r--r--server/models/oauth-client-interface.ts28
-rw-r--r--server/models/oauth-client.ts55
-rw-r--r--server/models/oauth-token-interface.ts34
-rw-r--r--server/models/oauth-token.ts75
-rw-r--r--server/models/pod-interface.ts46
-rw-r--r--server/models/pod.ts127
-rw-r--r--server/models/request-interface.ts32
-rw-r--r--server/models/request-to-pod-interface.ts20
-rw-r--r--server/models/request-to-pod.ts32
-rw-r--r--server/models/request-video-event-interface.ts26
-rw-r--r--server/models/request-video-event.ts72
-rw-r--r--server/models/request-video-qadu-interface.ts25
-rw-r--r--server/models/request-video-qadu.ts68
-rw-r--r--server/models/request.ts69
-rw-r--r--server/models/tag-interface.ts19
-rw-r--r--server/models/tag.ts38
-rw-r--r--server/models/user-interface.ts45
-rw-r--r--server/models/user-video-rate-interface.ts21
-rw-r--r--server/models/user-video-rate.ts48
-rw-r--r--server/models/user.ts110
-rw-r--r--server/models/utils.ts6
-rw-r--r--server/models/video-abuse-interface.ts24
-rw-r--r--server/models/video-abuse.ts95
-rw-r--r--server/models/video-blacklist-interface.ts31
-rw-r--r--server/models/video-blacklist.ts80
-rw-r--r--server/models/video-interface.ts75
-rw-r--r--server/models/video-tag-interface.ts18
-rw-r--r--server/models/video-tag.ts17
-rw-r--r--server/models/video.ts379
36 files changed, 1359 insertions, 543 deletions
diff --git a/server/models/application-interface.ts b/server/models/application-interface.ts
new file mode 100644
index 000000000..826d25df0
--- /dev/null
+++ b/server/models/application-interface.ts
@@ -0,0 +1,23 @@
1import * as Sequelize from 'sequelize'
2
3export namespace ApplicationMethods {
4 export type LoadMigrationVersion = (callback: (err: Error, version: number) => void) => void
5 export type UpdateMigrationVersion = (newVersion: number, transaction: any, callback: any) => void
6}
7
8export interface ApplicationClass {
9 loadMigrationVersion: ApplicationMethods.LoadMigrationVersion
10 updateMigrationVersion: ApplicationMethods.UpdateMigrationVersion
11}
12
13export interface ApplicationAttributes {
14 migrationVersion: number
15}
16
17export interface ApplicationInstance extends ApplicationClass, ApplicationAttributes, Sequelize.Instance<ApplicationAttributes> {
18 id: number
19 createdAt: Date
20 updatedAt: Date
21}
22
23export interface ApplicationModel extends ApplicationClass, Sequelize.Model<ApplicationInstance, ApplicationAttributes> {}
diff --git a/server/models/application.ts b/server/models/application.ts
index 38a57e327..acd0dfbf2 100644
--- a/server/models/application.ts
+++ b/server/models/application.ts
@@ -1,5 +1,20 @@
1module.exports = function (sequelize, DataTypes) { 1import * as Sequelize from 'sequelize'
2 const Application = sequelize.define('Application', 2
3import { addMethodsToModel } from './utils'
4import {
5 ApplicationClass,
6 ApplicationAttributes,
7 ApplicationInstance,
8
9 ApplicationMethods
10} from './application-interface'
11
12let Application: Sequelize.Model<ApplicationInstance, ApplicationAttributes>
13let loadMigrationVersion: ApplicationMethods.LoadMigrationVersion
14let updateMigrationVersion: ApplicationMethods.UpdateMigrationVersion
15
16export default function defineApplication (sequelize: Sequelize.Sequelize, DataTypes) {
17 Application = sequelize.define<ApplicationInstance, ApplicationAttributes>('Application',
3 { 18 {
4 migrationVersion: { 19 migrationVersion: {
5 type: DataTypes.INTEGER, 20 type: DataTypes.INTEGER,
@@ -9,34 +24,31 @@ module.exports = function (sequelize, DataTypes) {
9 isInt: true 24 isInt: true
10 } 25 }
11 } 26 }
12 },
13 {
14 classMethods: {
15 loadMigrationVersion,
16 updateMigrationVersion
17 }
18 } 27 }
19 ) 28 )
20 29
30 const classMethods = [ loadMigrationVersion, updateMigrationVersion ]
31 addMethodsToModel(Application, classMethods)
32
21 return Application 33 return Application
22} 34}
23 35
24// --------------------------------------------------------------------------- 36// ---------------------------------------------------------------------------
25 37
26function loadMigrationVersion (callback) { 38loadMigrationVersion = function (callback: (err: Error, version: number) => void) {
27 const query = { 39 const query = {
28 attributes: [ 'migrationVersion' ] 40 attributes: [ 'migrationVersion' ]
29 } 41 }
30 42
31 return this.findOne(query).asCallback(function (err, data) { 43 return Application.findOne(query).asCallback(function (err, data) {
32 const version = data ? data.migrationVersion : null 44 const version = data ? data.migrationVersion : null
33 45
34 return callback(err, version) 46 return callback(err, version)
35 }) 47 })
36} 48}
37 49
38function updateMigrationVersion (newVersion, transaction, callback) { 50updateMigrationVersion = function (newVersion: number, transaction: any, callback: any) {
39 const options: { where?: any, transaction?: any } = { 51 const options: Sequelize.UpdateOptions = {
40 where: {} 52 where: {}
41 } 53 }
42 54
@@ -46,5 +58,5 @@ function updateMigrationVersion (newVersion, transaction, callback) {
46 options.transaction = transaction 58 options.transaction = transaction
47 } 59 }
48 60
49 return this.update({ migrationVersion: newVersion }, options).asCallback(callback) 61 return Application.update({ migrationVersion: newVersion }, options).asCallback(callback)
50} 62}
diff --git a/server/models/author-interface.ts b/server/models/author-interface.ts
new file mode 100644
index 000000000..d2475c3bd
--- /dev/null
+++ b/server/models/author-interface.ts
@@ -0,0 +1,21 @@
1import * as Sequelize from 'sequelize'
2
3export namespace AuthorMethods {
4 export type FindOrCreateAuthor = (name, podId, userId, transaction, callback) => void
5}
6
7export interface AuthorClass {
8 findOrCreateAuthor: AuthorMethods.FindOrCreateAuthor
9}
10
11export interface AuthorAttributes {
12 name: string
13}
14
15export interface AuthorInstance extends AuthorClass, AuthorAttributes, Sequelize.Instance<AuthorAttributes> {
16 id: number
17 createdAt: Date
18 updatedAt: Date
19}
20
21export interface AuthorModel extends AuthorClass, Sequelize.Model<AuthorInstance, AuthorAttributes> {}
diff --git a/server/models/author.ts b/server/models/author.ts
index 4a7396929..b543d17a0 100644
--- a/server/models/author.ts
+++ b/server/models/author.ts
@@ -1,7 +1,21 @@
1import * as Sequelize from 'sequelize'
2
1import { isUserUsernameValid } from '../helpers' 3import { isUserUsernameValid } from '../helpers'
2 4
3module.exports = function (sequelize, DataTypes) { 5import { addMethodsToModel } from './utils'
4 const Author = sequelize.define('Author', 6import {
7 AuthorClass,
8 AuthorInstance,
9 AuthorAttributes,
10
11 AuthorMethods
12} from './author-interface'
13
14let Author: Sequelize.Model<AuthorInstance, AuthorAttributes>
15let findOrCreateAuthor: AuthorMethods.FindOrCreateAuthor
16
17export default function defineAuthor (sequelize: Sequelize.Sequelize, DataTypes) {
18 Author = sequelize.define<AuthorInstance, AuthorAttributes>('Author',
5 { 19 {
6 name: { 20 name: {
7 type: DataTypes.STRING, 21 type: DataTypes.STRING,
@@ -30,22 +44,20 @@ module.exports = function (sequelize, DataTypes) {
30 fields: [ 'name', 'podId' ], 44 fields: [ 'name', 'podId' ],
31 unique: true 45 unique: true
32 } 46 }
33 ], 47 ]
34 classMethods: {
35 associate,
36
37 findOrCreateAuthor
38 }
39 } 48 }
40 ) 49 )
41 50
51 const classMethods = [ associate, findOrCreateAuthor ]
52 addMethodsToModel(Author, classMethods)
53
42 return Author 54 return Author
43} 55}
44 56
45// --------------------------------------------------------------------------- 57// ---------------------------------------------------------------------------
46 58
47function associate (models) { 59function associate (models) {
48 this.belongsTo(models.Pod, { 60 Author.belongsTo(models.Pod, {
49 foreignKey: { 61 foreignKey: {
50 name: 'podId', 62 name: 'podId',
51 allowNull: true 63 allowNull: true
@@ -53,7 +65,7 @@ function associate (models) {
53 onDelete: 'cascade' 65 onDelete: 'cascade'
54 }) 66 })
55 67
56 this.belongsTo(models.User, { 68 Author.belongsTo(models.User, {
57 foreignKey: { 69 foreignKey: {
58 name: 'userId', 70 name: 'userId',
59 allowNull: true 71 allowNull: true
@@ -62,7 +74,7 @@ function associate (models) {
62 }) 74 })
63} 75}
64 76
65function findOrCreateAuthor (name, podId, userId, transaction, callback) { 77findOrCreateAuthor = function (name, podId, userId, transaction, callback) {
66 if (!callback) { 78 if (!callback) {
67 callback = transaction 79 callback = transaction
68 transaction = null 80 transaction = null
@@ -81,7 +93,7 @@ function findOrCreateAuthor (name, podId, userId, transaction, callback) {
81 93
82 if (transaction) query.transaction = transaction 94 if (transaction) query.transaction = transaction
83 95
84 this.findOrCreate(query).asCallback(function (err, result) { 96 Author.findOrCreate(query).asCallback(function (err, result) {
85 if (err) return callback(err) 97 if (err) return callback(err)
86 98
87 // [ instance, wasCreated ] 99 // [ instance, wasCreated ]
diff --git a/server/models/index.ts b/server/models/index.ts
new file mode 100644
index 000000000..432c0dc4a
--- /dev/null
+++ b/server/models/index.ts
@@ -0,0 +1,17 @@
1export * from './application-interface'
2export * from './author-interface'
3export * from './job-interface'
4export * from './oauth-client-interface'
5export * from './oauth-token-interface'
6export * from './pod-interface'
7export * from './request-interface'
8export * from './request-to-pod-interface'
9export * from './request-video-event-interface'
10export * from './request-video-qadu-interface'
11export * from './tag-interface'
12export * from './user-video-rate-interface'
13export * from './user-interface'
14export * from './video-abuse-interface'
15export * from './video-blacklist-interface'
16export * from './video-tag-interface'
17export * from './video-interface'
diff --git a/server/models/job-interface.ts b/server/models/job-interface.ts
new file mode 100644
index 000000000..ad4e2d2b0
--- /dev/null
+++ b/server/models/job-interface.ts
@@ -0,0 +1,23 @@
1import * as Sequelize from 'sequelize'
2
3export namespace JobMethods {
4 export type ListWithLimit = (limit, state, callback) => void
5}
6
7export interface JobClass {
8 listWithLimit: JobMethods.ListWithLimit
9}
10
11export interface JobAttributes {
12 state: string
13 handlerName: string
14 handlerInputData: object
15}
16
17export interface JobInstance extends JobClass, JobAttributes, Sequelize.Instance<JobAttributes> {
18 id: number
19 createdAt: Date
20 updatedAt: Date
21}
22
23export interface JobModel extends JobClass, Sequelize.Model<JobInstance, JobAttributes> {}
diff --git a/server/models/job.ts b/server/models/job.ts
index 6843e399b..982b51499 100644
--- a/server/models/job.ts
+++ b/server/models/job.ts
@@ -1,11 +1,22 @@
1import { values } from 'lodash' 1import { values } from 'lodash'
2import * as Sequelize from 'sequelize'
2 3
3import { JOB_STATES } from '../initializers' 4import { JOB_STATES } from '../initializers'
4 5
5// --------------------------------------------------------------------------- 6import { addMethodsToModel } from './utils'
7import {
8 JobClass,
9 JobInstance,
10 JobAttributes,
11
12 JobMethods
13} from './job-interface'
14
15let Job: Sequelize.Model<JobInstance, JobAttributes>
16let listWithLimit: JobMethods.ListWithLimit
6 17
7module.exports = function (sequelize, DataTypes) { 18export default function defineJob (sequelize: Sequelize.Sequelize, DataTypes) {
8 const Job = sequelize.define('Job', 19 Job = sequelize.define<JobInstance, JobAttributes>('Job',
9 { 20 {
10 state: { 21 state: {
11 type: DataTypes.ENUM(values(JOB_STATES)), 22 type: DataTypes.ENUM(values(JOB_STATES)),
@@ -25,19 +36,19 @@ module.exports = function (sequelize, DataTypes) {
25 { 36 {
26 fields: [ 'state' ] 37 fields: [ 'state' ]
27 } 38 }
28 ], 39 ]
29 classMethods: {
30 listWithLimit
31 }
32 } 40 }
33 ) 41 )
34 42
43 const classMethods = [ listWithLimit ]
44 addMethodsToModel(Job, classMethods)
45
35 return Job 46 return Job
36} 47}
37 48
38// --------------------------------------------------------------------------- 49// ---------------------------------------------------------------------------
39 50
40function listWithLimit (limit, state, callback) { 51listWithLimit = function (limit, state, callback) {
41 const query = { 52 const query = {
42 order: [ 53 order: [
43 [ 'id', 'ASC' ] 54 [ 'id', 'ASC' ]
@@ -48,5 +59,5 @@ function listWithLimit (limit, state, callback) {
48 } 59 }
49 } 60 }
50 61
51 return this.findAll(query).asCallback(callback) 62 return Job.findAll(query).asCallback(callback)
52} 63}
diff --git a/server/models/oauth-client-interface.ts b/server/models/oauth-client-interface.ts
new file mode 100644
index 000000000..4efd6212a
--- /dev/null
+++ b/server/models/oauth-client-interface.ts
@@ -0,0 +1,28 @@
1import * as Sequelize from 'sequelize'
2
3export namespace OAuthClientMethods {
4 export type CountTotal = (callback) => void
5 export type LoadFirstClient = (callback) => void
6 export type GetByIdAndSecret = (clientId, clientSecret) => void
7}
8
9export interface OAuthClientClass {
10 countTotal: OAuthClientMethods.CountTotal
11 loadFirstClient: OAuthClientMethods.LoadFirstClient
12 getByIdAndSecret: OAuthClientMethods.GetByIdAndSecret
13}
14
15export interface OAuthClientAttributes {
16 clientId: string
17 clientSecret: string
18 grants: string[]
19 redirectUris: string[]
20}
21
22export interface OAuthClientInstance extends OAuthClientClass, OAuthClientAttributes, Sequelize.Instance<OAuthClientAttributes> {
23 id: number
24 createdAt: Date
25 updatedAt: Date
26}
27
28export interface OAuthClientModel extends OAuthClientClass, Sequelize.Model<OAuthClientInstance, OAuthClientAttributes> {}
diff --git a/server/models/oauth-client.ts b/server/models/oauth-client.ts
index 3198a85ef..2cefb5cb9 100644
--- a/server/models/oauth-client.ts
+++ b/server/models/oauth-client.ts
@@ -1,5 +1,21 @@
1module.exports = function (sequelize, DataTypes) { 1import * as Sequelize from 'sequelize'
2 const OAuthClient = sequelize.define('OAuthClient', 2
3import { addMethodsToModel } from './utils'
4import {
5 OAuthClientClass,
6 OAuthClientInstance,
7 OAuthClientAttributes,
8
9 OAuthClientMethods
10} from './oauth-client-interface'
11
12let OAuthClient: Sequelize.Model<OAuthClientInstance, OAuthClientAttributes>
13let countTotal: OAuthClientMethods.CountTotal
14let loadFirstClient: OAuthClientMethods.LoadFirstClient
15let getByIdAndSecret: OAuthClientMethods.GetByIdAndSecret
16
17export default function (sequelize, DataTypes) {
18 OAuthClient = sequelize.define('OAuthClient',
3 { 19 {
4 clientId: { 20 clientId: {
5 type: DataTypes.STRING, 21 type: DataTypes.STRING,
@@ -26,29 +42,40 @@ module.exports = function (sequelize, DataTypes) {
26 fields: [ 'clientId', 'clientSecret' ], 42 fields: [ 'clientId', 'clientSecret' ],
27 unique: true 43 unique: true
28 } 44 }
29 ], 45 ]
30 classMethods: {
31 countTotal,
32 getByIdAndSecret,
33 loadFirstClient
34 }
35 } 46 }
36 ) 47 )
37 48
49 const classMethods = [
50 associate,
51
52 countTotal,
53 getByIdAndSecret,
54 loadFirstClient
55 ]
56 addMethodsToModel(OAuthClient, classMethods)
57
38 return OAuthClient 58 return OAuthClient
39} 59}
40 60
41// --------------------------------------------------------------------------- 61// ---------------------------------------------------------------------------
42 62
43function countTotal (callback) { 63function associate (models) {
44 return this.count().asCallback(callback) 64 OAuthClient.hasMany(models.OAuthToken, {
65 foreignKey: 'oAuthClientId',
66 onDelete: 'cascade'
67 })
68}
69
70countTotal = function (callback) {
71 return OAuthClient.count().asCallback(callback)
45} 72}
46 73
47function loadFirstClient (callback) { 74loadFirstClient = function (callback) {
48 return this.findOne().asCallback(callback) 75 return OAuthClient.findOne().asCallback(callback)
49} 76}
50 77
51function getByIdAndSecret (clientId, clientSecret) { 78getByIdAndSecret = function (clientId, clientSecret) {
52 const query = { 79 const query = {
53 where: { 80 where: {
54 clientId: clientId, 81 clientId: clientId,
@@ -56,5 +83,5 @@ function getByIdAndSecret (clientId, clientSecret) {
56 } 83 }
57 } 84 }
58 85
59 return this.findOne(query) 86 return OAuthClient.findOne(query)
60} 87}
diff --git a/server/models/oauth-token-interface.ts b/server/models/oauth-token-interface.ts
new file mode 100644
index 000000000..a0cd1ffe7
--- /dev/null
+++ b/server/models/oauth-token-interface.ts
@@ -0,0 +1,34 @@
1import * as Sequelize from 'sequelize'
2
3import { UserModel } from './user-interface'
4
5export namespace OAuthTokenMethods {
6 export type GetByRefreshTokenAndPopulateClient = (refreshToken) => void
7 export type GetByTokenAndPopulateUser = (bearerToken) => void
8 export type GetByRefreshTokenAndPopulateUser = (refreshToken) => any
9 export type RemoveByUserId = (userId, callback) => void
10}
11
12export interface OAuthTokenClass {
13 getByRefreshTokenAndPopulateClient: OAuthTokenMethods.GetByRefreshTokenAndPopulateClient
14 getByTokenAndPopulateUser: OAuthTokenMethods.GetByTokenAndPopulateUser
15 getByRefreshTokenAndPopulateUser: OAuthTokenMethods.GetByRefreshTokenAndPopulateUser
16 removeByUserId: OAuthTokenMethods.RemoveByUserId
17}
18
19export interface OAuthTokenAttributes {
20 accessToken: string
21 accessTokenExpiresAt: Date
22 refreshToken: string
23 refreshTokenExpiresAt: Date
24
25 User?: UserModel
26}
27
28export interface OAuthTokenInstance extends OAuthTokenClass, OAuthTokenAttributes, Sequelize.Instance<OAuthTokenAttributes> {
29 id: number
30 createdAt: Date
31 updatedAt: Date
32}
33
34export interface OAuthTokenModel extends OAuthTokenClass, Sequelize.Model<OAuthTokenInstance, OAuthTokenAttributes> {}
diff --git a/server/models/oauth-token.ts b/server/models/oauth-token.ts
index 74c9180eb..567df1c12 100644
--- a/server/models/oauth-token.ts
+++ b/server/models/oauth-token.ts
@@ -1,9 +1,24 @@
1import * as Sequelize from 'sequelize'
2
1import { logger } from '../helpers' 3import { logger } from '../helpers'
2 4
3// --------------------------------------------------------------------------- 5import { addMethodsToModel } from './utils'
6import {
7 OAuthTokenClass,
8 OAuthTokenInstance,
9 OAuthTokenAttributes,
10
11 OAuthTokenMethods
12} from './oauth-token-interface'
13
14let OAuthToken: Sequelize.Model<OAuthTokenInstance, OAuthTokenAttributes>
15let getByRefreshTokenAndPopulateClient: OAuthTokenMethods.GetByRefreshTokenAndPopulateClient
16let getByTokenAndPopulateUser: OAuthTokenMethods.GetByTokenAndPopulateUser
17let getByRefreshTokenAndPopulateUser: OAuthTokenMethods.GetByRefreshTokenAndPopulateUser
18let removeByUserId: OAuthTokenMethods.RemoveByUserId
4 19
5module.exports = function (sequelize, DataTypes) { 20export default function (sequelize, DataTypes) {
6 const OAuthToken = sequelize.define('OAuthToken', 21 OAuthToken = sequelize.define('OAuthToken',
7 { 22 {
8 accessToken: { 23 accessToken: {
9 type: DataTypes.STRING, 24 type: DataTypes.STRING,
@@ -38,25 +53,27 @@ module.exports = function (sequelize, DataTypes) {
38 { 53 {
39 fields: [ 'oAuthClientId' ] 54 fields: [ 'oAuthClientId' ]
40 } 55 }
41 ], 56 ]
42 classMethods: {
43 associate,
44
45 getByRefreshTokenAndPopulateClient,
46 getByTokenAndPopulateUser,
47 getByRefreshTokenAndPopulateUser,
48 removeByUserId
49 }
50 } 57 }
51 ) 58 )
52 59
60 const classMethods = [
61 associate,
62
63 getByRefreshTokenAndPopulateClient,
64 getByTokenAndPopulateUser,
65 getByRefreshTokenAndPopulateUser,
66 removeByUserId
67 ]
68 addMethodsToModel(OAuthToken, classMethods)
69
53 return OAuthToken 70 return OAuthToken
54} 71}
55 72
56// --------------------------------------------------------------------------- 73// ---------------------------------------------------------------------------
57 74
58function associate (models) { 75function associate (models) {
59 this.belongsTo(models.User, { 76 OAuthToken.belongsTo(models.User, {
60 foreignKey: { 77 foreignKey: {
61 name: 'userId', 78 name: 'userId',
62 allowNull: false 79 allowNull: false
@@ -64,7 +81,7 @@ function associate (models) {
64 onDelete: 'cascade' 81 onDelete: 'cascade'
65 }) 82 })
66 83
67 this.belongsTo(models.OAuthClient, { 84 OAuthToken.belongsTo(models.OAuthClient, {
68 foreignKey: { 85 foreignKey: {
69 name: 'oAuthClientId', 86 name: 'oAuthClientId',
70 allowNull: false 87 allowNull: false
@@ -73,25 +90,25 @@ function associate (models) {
73 }) 90 })
74} 91}
75 92
76function getByRefreshTokenAndPopulateClient (refreshToken) { 93getByRefreshTokenAndPopulateClient = function (refreshToken) {
77 const query = { 94 const query = {
78 where: { 95 where: {
79 refreshToken: refreshToken 96 refreshToken: refreshToken
80 }, 97 },
81 include: [ this.associations.OAuthClient ] 98 include: [ OAuthToken['sequelize'].models.OAuthClient ]
82 } 99 }
83 100
84 return this.findOne(query).then(function (token) { 101 return OAuthToken.findOne(query).then(function (token) {
85 if (!token) return token 102 if (!token) return token
86 103
87 const tokenInfos = { 104 const tokenInfos = {
88 refreshToken: token.refreshToken, 105 refreshToken: token.refreshToken,
89 refreshTokenExpiresAt: token.refreshTokenExpiresAt, 106 refreshTokenExpiresAt: token.refreshTokenExpiresAt,
90 client: { 107 client: {
91 id: token.client.id 108 id: token['client'].id
92 }, 109 },
93 user: { 110 user: {
94 id: token.user 111 id: token['user']
95 } 112 }
96 } 113 }
97 114
@@ -101,42 +118,42 @@ function getByRefreshTokenAndPopulateClient (refreshToken) {
101 }) 118 })
102} 119}
103 120
104function getByTokenAndPopulateUser (bearerToken) { 121getByTokenAndPopulateUser = function (bearerToken) {
105 const query = { 122 const query = {
106 where: { 123 where: {
107 accessToken: bearerToken 124 accessToken: bearerToken
108 }, 125 },
109 include: [ this.sequelize.models.User ] 126 include: [ OAuthToken['sequelize'].models.User ]
110 } 127 }
111 128
112 return this.findOne(query).then(function (token) { 129 return OAuthToken.findOne(query).then(function (token) {
113 if (token) token.user = token.User 130 if (token) token['user'] = token.User
114 131
115 return token 132 return token
116 }) 133 })
117} 134}
118 135
119function getByRefreshTokenAndPopulateUser (refreshToken) { 136getByRefreshTokenAndPopulateUser = function (refreshToken) {
120 const query = { 137 const query = {
121 where: { 138 where: {
122 refreshToken: refreshToken 139 refreshToken: refreshToken
123 }, 140 },
124 include: [ this.sequelize.models.User ] 141 include: [ OAuthToken['sequelize'].models.User ]
125 } 142 }
126 143
127 return this.findOne(query).then(function (token) { 144 return OAuthToken.findOne(query).then(function (token) {
128 token.user = token.User 145 token['user'] = token.User
129 146
130 return token 147 return token
131 }) 148 })
132} 149}
133 150
134function removeByUserId (userId, callback) { 151removeByUserId = function (userId, callback) {
135 const query = { 152 const query = {
136 where: { 153 where: {
137 userId: userId 154 userId: userId
138 } 155 }
139 } 156 }
140 157
141 return this.destroy(query).asCallback(callback) 158 return OAuthToken.destroy(query).asCallback(callback)
142} 159}
diff --git a/server/models/pod-interface.ts b/server/models/pod-interface.ts
new file mode 100644
index 000000000..14c88bec6
--- /dev/null
+++ b/server/models/pod-interface.ts
@@ -0,0 +1,46 @@
1import * as Sequelize from 'sequelize'
2
3export namespace PodMethods {
4 export type ToFormatedJSON = () => void
5
6 export type CountAll = (callback) => void
7 export type IncrementScores = (ids, value, callback) => void
8 export type List = (callback) => void
9 export type ListAllIds = (transaction, callback) => void
10 export type ListRandomPodIdsWithRequest = (limit, tableWithPods, tableWithPodsJoins, callback) => void
11 export type ListBadPods = (callback) => void
12 export type Load = (id, callback) => void
13 export type LoadByHost = (host, callback) => void
14 export type RemoveAll = (callback) => void
15 export type UpdatePodsScore = (goodPods, badPods) => void
16}
17
18export interface PodClass {
19 countAll: PodMethods.CountAll
20 incrementScores: PodMethods.IncrementScores
21 list: PodMethods.List
22 listAllIds: PodMethods.ListAllIds
23 listRandomPodIdsWithRequest: PodMethods.ListRandomPodIdsWithRequest
24 listBadPods: PodMethods.ListBadPods
25 load: PodMethods.Load
26 loadByHost: PodMethods.LoadByHost
27 removeAll: PodMethods.RemoveAll
28 updatePodsScore: PodMethods.UpdatePodsScore
29}
30
31export interface PodAttributes {
32 host?: string
33 publicKey?: string
34 score?: number | Sequelize.literal // Sequelize literal for 'score +' + value
35 email?: string
36}
37
38export interface PodInstance extends PodClass, PodAttributes, Sequelize.Instance<PodAttributes> {
39 id: number
40 createdAt: Date
41 updatedAt: Date
42
43 toFormatedJSON: PodMethods.ToFormatedJSON,
44}
45
46export interface PodModel extends PodClass, Sequelize.Model<PodInstance, PodAttributes> {}
diff --git a/server/models/pod.ts b/server/models/pod.ts
index 0e0262978..2df32e4a4 100644
--- a/server/models/pod.ts
+++ b/server/models/pod.ts
@@ -1,13 +1,34 @@
1import { each, waterfall } from 'async' 1import { each, waterfall } from 'async'
2import { map } from 'lodash' 2import { map } from 'lodash'
3import * as Sequelize from 'sequelize'
3 4
4import { FRIEND_SCORE, PODS_SCORE } from '../initializers' 5import { FRIEND_SCORE, PODS_SCORE } from '../initializers'
5import { logger, isHostValid } from '../helpers' 6import { logger, isHostValid } from '../helpers'
6 7
7// --------------------------------------------------------------------------- 8import { addMethodsToModel } from './utils'
8 9import {
9module.exports = function (sequelize, DataTypes) { 10 PodClass,
10 const Pod = sequelize.define('Pod', 11 PodInstance,
12 PodAttributes,
13
14 PodMethods
15} from './pod-interface'
16
17let Pod: Sequelize.Model<PodInstance, PodAttributes>
18let toFormatedJSON: PodMethods.ToFormatedJSON
19let countAll: PodMethods.CountAll
20let incrementScores: PodMethods.IncrementScores
21let list: PodMethods.List
22let listAllIds: PodMethods.ListAllIds
23let listRandomPodIdsWithRequest: PodMethods.ListRandomPodIdsWithRequest
24let listBadPods: PodMethods.ListBadPods
25let load: PodMethods.Load
26let loadByHost: PodMethods.LoadByHost
27let removeAll: PodMethods.RemoveAll
28let updatePodsScore: PodMethods.UpdatePodsScore
29
30export default function (sequelize, DataTypes) {
31 Pod = sequelize.define('Pod',
11 { 32 {
12 host: { 33 host: {
13 type: DataTypes.STRING, 34 type: DataTypes.STRING,
@@ -49,33 +70,33 @@ module.exports = function (sequelize, DataTypes) {
49 { 70 {
50 fields: [ 'score' ] 71 fields: [ 'score' ]
51 } 72 }
52 ], 73 ]
53 classMethods: {
54 associate,
55
56 countAll,
57 incrementScores,
58 list,
59 listAllIds,
60 listRandomPodIdsWithRequest,
61 listBadPods,
62 load,
63 loadByHost,
64 updatePodsScore,
65 removeAll
66 },
67 instanceMethods: {
68 toFormatedJSON
69 }
70 } 74 }
71 ) 75 )
72 76
77 const classMethods = [
78 associate,
79
80 countAll,
81 incrementScores,
82 list,
83 listAllIds,
84 listRandomPodIdsWithRequest,
85 listBadPods,
86 load,
87 loadByHost,
88 updatePodsScore,
89 removeAll
90 ]
91 const instanceMethods = [ toFormatedJSON ]
92 addMethodsToModel(Pod, classMethods, instanceMethods)
93
73 return Pod 94 return Pod
74} 95}
75 96
76// ------------------------------ METHODS ------------------------------ 97// ------------------------------ METHODS ------------------------------
77 98
78function toFormatedJSON () { 99toFormatedJSON = function () {
79 const json = { 100 const json = {
80 id: this.id, 101 id: this.id,
81 host: this.host, 102 host: this.host,
@@ -90,22 +111,22 @@ function toFormatedJSON () {
90// ------------------------------ Statics ------------------------------ 111// ------------------------------ Statics ------------------------------
91 112
92function associate (models) { 113function associate (models) {
93 this.belongsToMany(models.Request, { 114 Pod.belongsToMany(models.Request, {
94 foreignKey: 'podId', 115 foreignKey: 'podId',
95 through: models.RequestToPod, 116 through: models.RequestToPod,
96 onDelete: 'cascade' 117 onDelete: 'cascade'
97 }) 118 })
98} 119}
99 120
100function countAll (callback) { 121countAll = function (callback) {
101 return this.count().asCallback(callback) 122 return Pod.count().asCallback(callback)
102} 123}
103 124
104function incrementScores (ids, value, callback) { 125incrementScores = function (ids, value, callback) {
105 if (!callback) callback = function () { /* empty */ } 126 if (!callback) callback = function () { /* empty */ }
106 127
107 const update = { 128 const update = {
108 score: this.sequelize.literal('score +' + value) 129 score: Sequelize.literal('score +' + value)
109 } 130 }
110 131
111 const options = { 132 const options = {
@@ -118,14 +139,14 @@ function incrementScores (ids, value, callback) {
118 validate: false 139 validate: false
119 } 140 }
120 141
121 return this.update(update, options).asCallback(callback) 142 return Pod.update(update, options).asCallback(callback)
122} 143}
123 144
124function list (callback) { 145list = function (callback) {
125 return this.findAll().asCallback(callback) 146 return Pod.findAll().asCallback(callback)
126} 147}
127 148
128function listAllIds (transaction, callback) { 149listAllIds = function (transaction, callback) {
129 if (!callback) { 150 if (!callback) {
130 callback = transaction 151 callback = transaction
131 transaction = null 152 transaction = null
@@ -137,22 +158,20 @@ function listAllIds (transaction, callback) {
137 158
138 if (transaction) query.transaction = transaction 159 if (transaction) query.transaction = transaction
139 160
140 return this.findAll(query).asCallback(function (err, pods) { 161 return Pod.findAll(query).asCallback(function (err, pods) {
141 if (err) return callback(err) 162 if (err) return callback(err)
142 163
143 return callback(null, map(pods, 'id')) 164 return callback(null, map(pods, 'id'))
144 }) 165 })
145} 166}
146 167
147function listRandomPodIdsWithRequest (limit, tableWithPods, tableWithPodsJoins, callback) { 168listRandomPodIdsWithRequest = function (limit, tableWithPods, tableWithPodsJoins, callback) {
148 if (!callback) { 169 if (!callback) {
149 callback = tableWithPodsJoins 170 callback = tableWithPodsJoins
150 tableWithPodsJoins = '' 171 tableWithPodsJoins = ''
151 } 172 }
152 173
153 const self = this 174 Pod.count().asCallback(function (err, count) {
154
155 self.count().asCallback(function (err, count) {
156 if (err) return callback(err) 175 if (err) return callback(err)
157 176
158 // Optimization... 177 // Optimization...
@@ -171,13 +190,13 @@ function listRandomPodIdsWithRequest (limit, tableWithPods, tableWithPodsJoins,
171 where: { 190 where: {
172 id: { 191 id: {
173 $in: [ 192 $in: [
174 this.sequelize.literal(`SELECT DISTINCT "${tableWithPods}"."podId" FROM "${tableWithPods}" ${tableWithPodsJoins}`) 193 Sequelize.literal(`SELECT DISTINCT "${tableWithPods}"."podId" FROM "${tableWithPods}" ${tableWithPodsJoins}`)
175 ] 194 ]
176 } 195 }
177 } 196 }
178 } 197 }
179 198
180 return this.findAll(query).asCallback(function (err, pods) { 199 return Pod.findAll(query).asCallback(function (err, pods) {
181 if (err) return callback(err) 200 if (err) return callback(err)
182 201
183 return callback(null, map(pods, 'id')) 202 return callback(null, map(pods, 'id'))
@@ -185,49 +204,47 @@ function listRandomPodIdsWithRequest (limit, tableWithPods, tableWithPodsJoins,
185 }) 204 })
186} 205}
187 206
188function listBadPods (callback) { 207listBadPods = function (callback) {
189 const query = { 208 const query = {
190 where: { 209 where: {
191 score: { $lte: 0 } 210 score: { $lte: 0 }
192 } 211 }
193 } 212 }
194 213
195 return this.findAll(query).asCallback(callback) 214 return Pod.findAll(query).asCallback(callback)
196} 215}
197 216
198function load (id, callback) { 217load = function (id, callback) {
199 return this.findById(id).asCallback(callback) 218 return Pod.findById(id).asCallback(callback)
200} 219}
201 220
202function loadByHost (host, callback) { 221loadByHost = function (host, callback) {
203 const query = { 222 const query = {
204 where: { 223 where: {
205 host: host 224 host: host
206 } 225 }
207 } 226 }
208 227
209 return this.findOne(query).asCallback(callback) 228 return Pod.findOne(query).asCallback(callback)
210} 229}
211 230
212function removeAll (callback) { 231removeAll = function (callback) {
213 return this.destroy().asCallback(callback) 232 return Pod.destroy().asCallback(callback)
214} 233}
215 234
216function updatePodsScore (goodPods, badPods) { 235updatePodsScore = function (goodPods, badPods) {
217 const self = this
218
219 logger.info('Updating %d good pods and %d bad pods scores.', goodPods.length, badPods.length) 236 logger.info('Updating %d good pods and %d bad pods scores.', goodPods.length, badPods.length)
220 237
221 if (goodPods.length !== 0) { 238 if (goodPods.length !== 0) {
222 this.incrementScores(goodPods, PODS_SCORE.BONUS, function (err) { 239 incrementScores(goodPods, PODS_SCORE.BONUS, function (err) {
223 if (err) logger.error('Cannot increment scores of good pods.', { error: err }) 240 if (err) logger.error('Cannot increment scores of good pods.', { error: err })
224 }) 241 })
225 } 242 }
226 243
227 if (badPods.length !== 0) { 244 if (badPods.length !== 0) {
228 this.incrementScores(badPods, PODS_SCORE.MALUS, function (err) { 245 incrementScores(badPods, PODS_SCORE.MALUS, function (err) {
229 if (err) logger.error('Cannot decrement scores of bad pods.', { error: err }) 246 if (err) logger.error('Cannot decrement scores of bad pods.', { error: err })
230 removeBadPods.call(self) 247 removeBadPods()
231 }) 248 })
232 } 249 }
233} 250}
@@ -236,11 +253,9 @@ function updatePodsScore (goodPods, badPods) {
236 253
237// Remove pods with a score of 0 (too many requests where they were unreachable) 254// Remove pods with a score of 0 (too many requests where they were unreachable)
238function removeBadPods () { 255function removeBadPods () {
239 const self = this
240
241 waterfall([ 256 waterfall([
242 function findBadPods (callback) { 257 function findBadPods (callback) {
243 self.sequelize.models.Pod.listBadPods(function (err, pods) { 258 listBadPods(function (err, pods) {
244 if (err) { 259 if (err) {
245 logger.error('Cannot find bad pods.', { error: err }) 260 logger.error('Cannot find bad pods.', { error: err })
246 return callback(err) 261 return callback(err)
diff --git a/server/models/request-interface.ts b/server/models/request-interface.ts
new file mode 100644
index 000000000..2bba8ce7f
--- /dev/null
+++ b/server/models/request-interface.ts
@@ -0,0 +1,32 @@
1import * as Sequelize from 'sequelize'
2
3import { PodAttributes } from './pod-interface'
4
5export namespace RequestMethods {
6 export type CountTotalRequests = (callback) => void
7 export type ListWithLimitAndRandom = (limitPods, limitRequestsPerPod, callback) => void
8 export type RemoveWithEmptyTo = (callback) => void
9 export type RemoveAll = (callback) => void
10}
11
12export interface RequestClass {
13 countTotalRequests: RequestMethods.CountTotalRequests
14 listWithLimitAndRandom: RequestMethods.ListWithLimitAndRandom
15 removeWithEmptyTo: RequestMethods.RemoveWithEmptyTo
16 removeAll: RequestMethods.RemoveAll
17}
18
19export interface RequestAttributes {
20 request: object
21 endpoint: string
22}
23
24export interface RequestInstance extends Sequelize.Instance<RequestAttributes> {
25 id: number
26 createdAt: Date
27 updatedAt: Date
28
29 setPods: Sequelize.HasManySetAssociationsMixin<PodAttributes, number>
30}
31
32export interface RequestModel extends RequestClass, Sequelize.Model<RequestInstance, RequestAttributes> {}
diff --git a/server/models/request-to-pod-interface.ts b/server/models/request-to-pod-interface.ts
new file mode 100644
index 000000000..52116d6c4
--- /dev/null
+++ b/server/models/request-to-pod-interface.ts
@@ -0,0 +1,20 @@
1import * as Sequelize from 'sequelize'
2
3export namespace RequestToPodMethods {
4 export type RemoveByRequestIdsAndPod = (requestsIds, podId, callback) => void
5}
6
7export interface RequestToPodClass {
8 removeByRequestIdsAndPod: RequestToPodMethods.RemoveByRequestIdsAndPod
9}
10
11export interface RequestToPodAttributes {
12}
13
14export interface RequestToPodInstance extends Sequelize.Instance<RequestToPodAttributes> {
15 id: number
16 createdAt: Date
17 updatedAt: Date
18}
19
20export interface RequestToPodModel extends RequestToPodClass, Sequelize.Model<RequestToPodInstance, RequestToPodAttributes> {}
diff --git a/server/models/request-to-pod.ts b/server/models/request-to-pod.ts
index 479202e40..681f808b7 100644
--- a/server/models/request-to-pod.ts
+++ b/server/models/request-to-pod.ts
@@ -1,5 +1,19 @@
1module.exports = function (sequelize, DataTypes) { 1import * as Sequelize from 'sequelize'
2 const RequestToPod = sequelize.define('RequestToPod', {}, { 2
3import { addMethodsToModel } from './utils'
4import {
5 RequestToPodClass,
6 RequestToPodInstance,
7 RequestToPodAttributes,
8
9 RequestToPodMethods
10} from './request-to-pod-interface'
11
12let RequestToPod: Sequelize.Model<RequestToPodInstance, RequestToPodAttributes>
13let removeByRequestIdsAndPod: RequestToPodMethods.RemoveByRequestIdsAndPod
14
15export default function (sequelize, DataTypes) {
16 RequestToPod = sequelize.define('RequestToPod', {}, {
3 indexes: [ 17 indexes: [
4 { 18 {
5 fields: [ 'requestId' ] 19 fields: [ 'requestId' ]
@@ -11,18 +25,20 @@ module.exports = function (sequelize, DataTypes) {
11 fields: [ 'requestId', 'podId' ], 25 fields: [ 'requestId', 'podId' ],
12 unique: true 26 unique: true
13 } 27 }
14 ], 28 ]
15 classMethods: {
16 removeByRequestIdsAndPod
17 }
18 }) 29 })
19 30
31 const classMethods = [
32 removeByRequestIdsAndPod
33 ]
34 addMethodsToModel(RequestToPod, classMethods)
35
20 return RequestToPod 36 return RequestToPod
21} 37}
22 38
23// --------------------------------------------------------------------------- 39// ---------------------------------------------------------------------------
24 40
25function removeByRequestIdsAndPod (requestsIds, podId, callback) { 41removeByRequestIdsAndPod = function (requestsIds, podId, callback) {
26 if (!callback) callback = function () { /* empty */ } 42 if (!callback) callback = function () { /* empty */ }
27 43
28 const query = { 44 const query = {
@@ -34,5 +50,5 @@ function removeByRequestIdsAndPod (requestsIds, podId, callback) {
34 } 50 }
35 } 51 }
36 52
37 this.destroy(query).asCallback(callback) 53 RequestToPod.destroy(query).asCallback(callback)
38} 54}
diff --git a/server/models/request-video-event-interface.ts b/server/models/request-video-event-interface.ts
new file mode 100644
index 000000000..a31c7108f
--- /dev/null
+++ b/server/models/request-video-event-interface.ts
@@ -0,0 +1,26 @@
1import * as Sequelize from 'sequelize'
2
3export namespace RequestVideoEventMethods {
4 export type CountTotalRequests = (callback) => void
5 export type ListWithLimitAndRandom = (limitPods, limitRequestsPerPod, callback) => void
6 export type RemoveByRequestIdsAndPod = (ids, podId, callback) => void
7 export type RemoveAll = (callback) => void
8}
9
10export interface RequestVideoEventClass {
11 countTotalRequests: RequestVideoEventMethods.CountTotalRequests
12 listWithLimitAndRandom: RequestVideoEventMethods.ListWithLimitAndRandom
13 removeByRequestIdsAndPod: RequestVideoEventMethods.RemoveByRequestIdsAndPod
14 removeAll: RequestVideoEventMethods.RemoveAll
15}
16
17export interface RequestVideoEventAttributes {
18 type: string
19 count: number
20}
21
22export interface RequestVideoEventInstance extends Sequelize.Instance<RequestVideoEventAttributes> {
23 id: number
24}
25
26export interface RequestVideoEventModel extends RequestVideoEventClass, Sequelize.Model<RequestVideoEventInstance, RequestVideoEventAttributes> {}
diff --git a/server/models/request-video-event.ts b/server/models/request-video-event.ts
index c61525029..234e2a8a9 100644
--- a/server/models/request-video-event.ts
+++ b/server/models/request-video-event.ts
@@ -3,14 +3,28 @@
3*/ 3*/
4 4
5import { values } from 'lodash' 5import { values } from 'lodash'
6import * as Sequelize from 'sequelize'
6 7
7import { REQUEST_VIDEO_EVENT_TYPES } from '../initializers' 8import { REQUEST_VIDEO_EVENT_TYPES } from '../initializers'
8import { isVideoEventCountValid } from '../helpers' 9import { isVideoEventCountValid } from '../helpers'
9 10
10// --------------------------------------------------------------------------- 11import { addMethodsToModel } from './utils'
12import {
13 RequestVideoEventClass,
14 RequestVideoEventInstance,
15 RequestVideoEventAttributes,
16
17 RequestVideoEventMethods
18} from './request-video-event-interface'
19
20let RequestVideoEvent: Sequelize.Model<RequestVideoEventInstance, RequestVideoEventAttributes>
21let countTotalRequests: RequestVideoEventMethods.CountTotalRequests
22let listWithLimitAndRandom: RequestVideoEventMethods.ListWithLimitAndRandom
23let removeByRequestIdsAndPod: RequestVideoEventMethods.RemoveByRequestIdsAndPod
24let removeAll: RequestVideoEventMethods.RemoveAll
11 25
12module.exports = function (sequelize, DataTypes) { 26export default function (sequelize, DataTypes) {
13 const RequestVideoEvent = sequelize.define('RequestVideoEvent', 27 RequestVideoEvent = sequelize.define('RequestVideoEvent',
14 { 28 {
15 type: { 29 type: {
16 type: DataTypes.ENUM(values(REQUEST_VIDEO_EVENT_TYPES)), 30 type: DataTypes.ENUM(values(REQUEST_VIDEO_EVENT_TYPES)),
@@ -33,26 +47,27 @@ module.exports = function (sequelize, DataTypes) {
33 { 47 {
34 fields: [ 'videoId' ] 48 fields: [ 'videoId' ]
35 } 49 }
36 ], 50 ]
37 classMethods: {
38 associate,
39
40 listWithLimitAndRandom,
41
42 countTotalRequests,
43 removeAll,
44 removeByRequestIdsAndPod
45 }
46 } 51 }
47 ) 52 )
48 53
54 const classMethods = [
55 associate,
56
57 listWithLimitAndRandom,
58 countTotalRequests,
59 removeAll,
60 removeByRequestIdsAndPod
61 ]
62 addMethodsToModel(RequestVideoEvent, classMethods)
63
49 return RequestVideoEvent 64 return RequestVideoEvent
50} 65}
51 66
52// ------------------------------ STATICS ------------------------------ 67// ------------------------------ STATICS ------------------------------
53 68
54function associate (models) { 69function associate (models) {
55 this.belongsTo(models.Video, { 70 RequestVideoEvent.belongsTo(models.Video, {
56 foreignKey: { 71 foreignKey: {
57 name: 'videoId', 72 name: 'videoId',
58 allowNull: false 73 allowNull: false
@@ -61,14 +76,13 @@ function associate (models) {
61 }) 76 })
62} 77}
63 78
64function countTotalRequests (callback) { 79countTotalRequests = function (callback) {
65 const query = {} 80 const query = {}
66 return this.count(query).asCallback(callback) 81 return RequestVideoEvent.count(query).asCallback(callback)
67} 82}
68 83
69function listWithLimitAndRandom (limitPods, limitRequestsPerPod, callback) { 84listWithLimitAndRandom = function (limitPods, limitRequestsPerPod, callback) {
70 const self = this 85 const Pod = RequestVideoEvent['sequelize'].models.Pod
71 const Pod = this.sequelize.models.Pod
72 86
73 // We make a join between videos and authors to find the podId of our video event requests 87 // We make a join between videos and authors to find the podId of our video event requests
74 const podJoins = 'INNER JOIN "Videos" ON "Videos"."authorId" = "Authors"."id" ' + 88 const podJoins = 'INNER JOIN "Videos" ON "Videos"."authorId" = "Authors"."id" ' +
@@ -86,13 +100,13 @@ function listWithLimitAndRandom (limitPods, limitRequestsPerPod, callback) {
86 ], 100 ],
87 include: [ 101 include: [
88 { 102 {
89 model: self.sequelize.models.Video, 103 model: RequestVideoEvent['sequelize'].models.Video,
90 include: [ 104 include: [
91 { 105 {
92 model: self.sequelize.models.Author, 106 model: RequestVideoEvent['sequelize'].models.Author,
93 include: [ 107 include: [
94 { 108 {
95 model: self.sequelize.models.Pod, 109 model: RequestVideoEvent['sequelize'].models.Pod,
96 where: { 110 where: {
97 id: { 111 id: {
98 $in: podIds 112 $in: podIds
@@ -106,7 +120,7 @@ function listWithLimitAndRandom (limitPods, limitRequestsPerPod, callback) {
106 ] 120 ]
107 } 121 }
108 122
109 self.findAll(query).asCallback(function (err, requests) { 123 RequestVideoEvent.findAll(query).asCallback(function (err, requests) {
110 if (err) return callback(err) 124 if (err) return callback(err)
111 125
112 const requestsGrouped = groupAndTruncateRequests(requests, limitRequestsPerPod) 126 const requestsGrouped = groupAndTruncateRequests(requests, limitRequestsPerPod)
@@ -115,7 +129,7 @@ function listWithLimitAndRandom (limitPods, limitRequestsPerPod, callback) {
115 }) 129 })
116} 130}
117 131
118function removeByRequestIdsAndPod (ids, podId, callback) { 132removeByRequestIdsAndPod = function (ids, podId, callback) {
119 const query = { 133 const query = {
120 where: { 134 where: {
121 id: { 135 id: {
@@ -124,10 +138,10 @@ function removeByRequestIdsAndPod (ids, podId, callback) {
124 }, 138 },
125 include: [ 139 include: [
126 { 140 {
127 model: this.sequelize.models.Video, 141 model: RequestVideoEvent['sequelize'].models.Video,
128 include: [ 142 include: [
129 { 143 {
130 model: this.sequelize.models.Author, 144 model: RequestVideoEvent['sequelize'].models.Author,
131 where: { 145 where: {
132 podId 146 podId
133 } 147 }
@@ -137,12 +151,12 @@ function removeByRequestIdsAndPod (ids, podId, callback) {
137 ] 151 ]
138 } 152 }
139 153
140 this.destroy(query).asCallback(callback) 154 RequestVideoEvent.destroy(query).asCallback(callback)
141} 155}
142 156
143function removeAll (callback) { 157removeAll = function (callback) {
144 // Delete all requests 158 // Delete all requests
145 this.truncate({ cascade: true }).asCallback(callback) 159 RequestVideoEvent.truncate({ cascade: true }).asCallback(callback)
146} 160}
147 161
148// --------------------------------------------------------------------------- 162// ---------------------------------------------------------------------------
diff --git a/server/models/request-video-qadu-interface.ts b/server/models/request-video-qadu-interface.ts
new file mode 100644
index 000000000..6fe34ee91
--- /dev/null
+++ b/server/models/request-video-qadu-interface.ts
@@ -0,0 +1,25 @@
1import * as Sequelize from 'sequelize'
2
3export namespace RequestVideoQaduMethods {
4 export type CountTotalRequests = (callback) => void
5 export type ListWithLimitAndRandom = (limitPods, limitRequestsPerPod, callback) => void
6 export type RemoveByRequestIdsAndPod = (ids, podId, callback) => void
7 export type RemoveAll = (callback) => void
8}
9
10export interface RequestVideoQaduClass {
11 countTotalRequests: RequestVideoQaduMethods.CountTotalRequests
12 listWithLimitAndRandom: RequestVideoQaduMethods.ListWithLimitAndRandom
13 removeByRequestIdsAndPod: RequestVideoQaduMethods.RemoveByRequestIdsAndPod
14 removeAll: RequestVideoQaduMethods.RemoveAll
15}
16
17export interface RequestVideoQaduAttributes {
18 type: string
19}
20
21export interface RequestVideoQaduInstance extends Sequelize.Instance<RequestVideoQaduAttributes> {
22 id: number
23}
24
25export interface RequestVideoQaduModel extends RequestVideoQaduClass, Sequelize.Model<RequestVideoQaduInstance, RequestVideoQaduAttributes> {}
diff --git a/server/models/request-video-qadu.ts b/server/models/request-video-qadu.ts
index 2b1ed07c9..e914e06cd 100644
--- a/server/models/request-video-qadu.ts
+++ b/server/models/request-video-qadu.ts
@@ -10,13 +10,27 @@
10*/ 10*/
11 11
12import { values } from 'lodash' 12import { values } from 'lodash'
13import * as Sequelize from 'sequelize'
13 14
14import { REQUEST_VIDEO_QADU_TYPES } from '../initializers' 15import { REQUEST_VIDEO_QADU_TYPES } from '../initializers'
15 16
16// --------------------------------------------------------------------------- 17import { addMethodsToModel } from './utils'
18import {
19 RequestVideoQaduClass,
20 RequestVideoQaduInstance,
21 RequestVideoQaduAttributes,
22
23 RequestVideoQaduMethods
24} from './request-video-qadu-interface'
25
26let RequestVideoQadu: Sequelize.Model<RequestVideoQaduInstance, RequestVideoQaduAttributes>
27let countTotalRequests: RequestVideoQaduMethods.CountTotalRequests
28let listWithLimitAndRandom: RequestVideoQaduMethods.ListWithLimitAndRandom
29let removeByRequestIdsAndPod: RequestVideoQaduMethods.RemoveByRequestIdsAndPod
30let removeAll: RequestVideoQaduMethods.RemoveAll
17 31
18module.exports = function (sequelize, DataTypes) { 32export default function (sequelize, DataTypes) {
19 const RequestVideoQadu = sequelize.define('RequestVideoQadu', 33 RequestVideoQadu = sequelize.define('RequestVideoQadu',
20 { 34 {
21 type: { 35 type: {
22 type: DataTypes.ENUM(values(REQUEST_VIDEO_QADU_TYPES)), 36 type: DataTypes.ENUM(values(REQUEST_VIDEO_QADU_TYPES)),
@@ -32,26 +46,27 @@ module.exports = function (sequelize, DataTypes) {
32 { 46 {
33 fields: [ 'videoId' ] 47 fields: [ 'videoId' ]
34 } 48 }
35 ], 49 ]
36 classMethods: {
37 associate,
38
39 listWithLimitAndRandom,
40
41 countTotalRequests,
42 removeAll,
43 removeByRequestIdsAndPod
44 }
45 } 50 }
46 ) 51 )
47 52
53 const classMethods = [
54 associate,
55
56 listWithLimitAndRandom,
57 countTotalRequests,
58 removeAll,
59 removeByRequestIdsAndPod
60 ]
61 addMethodsToModel(RequestVideoQadu, classMethods)
62
48 return RequestVideoQadu 63 return RequestVideoQadu
49} 64}
50 65
51// ------------------------------ STATICS ------------------------------ 66// ------------------------------ STATICS ------------------------------
52 67
53function associate (models) { 68function associate (models) {
54 this.belongsTo(models.Pod, { 69 RequestVideoQadu.belongsTo(models.Pod, {
55 foreignKey: { 70 foreignKey: {
56 name: 'podId', 71 name: 'podId',
57 allowNull: false 72 allowNull: false
@@ -59,7 +74,7 @@ function associate (models) {
59 onDelete: 'CASCADE' 74 onDelete: 'CASCADE'
60 }) 75 })
61 76
62 this.belongsTo(models.Video, { 77 RequestVideoQadu.belongsTo(models.Video, {
63 foreignKey: { 78 foreignKey: {
64 name: 'videoId', 79 name: 'videoId',
65 allowNull: false 80 allowNull: false
@@ -68,14 +83,13 @@ function associate (models) {
68 }) 83 })
69} 84}
70 85
71function countTotalRequests (callback) { 86countTotalRequests = function (callback) {
72 const query = {} 87 const query = {}
73 return this.count(query).asCallback(callback) 88 return RequestVideoQadu.count(query).asCallback(callback)
74} 89}
75 90
76function listWithLimitAndRandom (limitPods, limitRequestsPerPod, callback) { 91listWithLimitAndRandom = function (limitPods, limitRequestsPerPod, callback) {
77 const self = this 92 const Pod = RequestVideoQadu['sequelize'].models.Pod
78 const Pod = this.sequelize.models.Pod
79 93
80 Pod.listRandomPodIdsWithRequest(limitPods, 'RequestVideoQadus', function (err, podIds) { 94 Pod.listRandomPodIdsWithRequest(limitPods, 'RequestVideoQadus', function (err, podIds) {
81 if (err) return callback(err) 95 if (err) return callback(err)
@@ -86,7 +100,7 @@ function listWithLimitAndRandom (limitPods, limitRequestsPerPod, callback) {
86 const query = { 100 const query = {
87 include: [ 101 include: [
88 { 102 {
89 model: self.sequelize.models.Pod, 103 model: RequestVideoQadu['sequelize'].models.Pod,
90 where: { 104 where: {
91 id: { 105 id: {
92 $in: podIds 106 $in: podIds
@@ -94,12 +108,12 @@ function listWithLimitAndRandom (limitPods, limitRequestsPerPod, callback) {
94 } 108 }
95 }, 109 },
96 { 110 {
97 model: self.sequelize.models.Video 111 model: RequestVideoQadu['sequelize'].models.Video
98 } 112 }
99 ] 113 ]
100 } 114 }
101 115
102 self.findAll(query).asCallback(function (err, requests) { 116 RequestVideoQadu.findAll(query).asCallback(function (err, requests) {
103 if (err) return callback(err) 117 if (err) return callback(err)
104 118
105 const requestsGrouped = groupAndTruncateRequests(requests, limitRequestsPerPod) 119 const requestsGrouped = groupAndTruncateRequests(requests, limitRequestsPerPod)
@@ -108,7 +122,7 @@ function listWithLimitAndRandom (limitPods, limitRequestsPerPod, callback) {
108 }) 122 })
109} 123}
110 124
111function removeByRequestIdsAndPod (ids, podId, callback) { 125removeByRequestIdsAndPod = function (ids, podId, callback) {
112 const query = { 126 const query = {
113 where: { 127 where: {
114 id: { 128 id: {
@@ -118,12 +132,12 @@ function removeByRequestIdsAndPod (ids, podId, callback) {
118 } 132 }
119 } 133 }
120 134
121 this.destroy(query).asCallback(callback) 135 RequestVideoQadu.destroy(query).asCallback(callback)
122} 136}
123 137
124function removeAll (callback) { 138removeAll = function (callback) {
125 // Delete all requests 139 // Delete all requests
126 this.truncate({ cascade: true }).asCallback(callback) 140 RequestVideoQadu.truncate({ cascade: true }).asCallback(callback)
127} 141}
128 142
129// --------------------------------------------------------------------------- 143// ---------------------------------------------------------------------------
diff --git a/server/models/request.ts b/server/models/request.ts
index 672f79d11..18fa291fa 100644
--- a/server/models/request.ts
+++ b/server/models/request.ts
@@ -1,11 +1,25 @@
1import { values } from 'lodash' 1import { values } from 'lodash'
2import * as Sequelize from 'sequelize'
2 3
3import { REQUEST_ENDPOINTS } from '../initializers' 4import { REQUEST_ENDPOINTS } from '../initializers'
4 5
5// --------------------------------------------------------------------------- 6import { addMethodsToModel } from './utils'
7import {
8 RequestClass,
9 RequestInstance,
10 RequestAttributes,
11
12 RequestMethods
13} from './request-interface'
14
15let Request: Sequelize.Model<RequestInstance, RequestAttributes>
16let countTotalRequests: RequestMethods.CountTotalRequests
17let listWithLimitAndRandom: RequestMethods.ListWithLimitAndRandom
18let removeWithEmptyTo: RequestMethods.RemoveWithEmptyTo
19let removeAll: RequestMethods.RemoveAll
6 20
7module.exports = function (sequelize, DataTypes) { 21export default function (sequelize, DataTypes) {
8 const Request = sequelize.define('Request', 22 Request = sequelize.define('Request',
9 { 23 {
10 request: { 24 request: {
11 type: DataTypes.JSON, 25 type: DataTypes.JSON,
@@ -15,27 +29,27 @@ module.exports = function (sequelize, DataTypes) {
15 type: DataTypes.ENUM(values(REQUEST_ENDPOINTS)), 29 type: DataTypes.ENUM(values(REQUEST_ENDPOINTS)),
16 allowNull: false 30 allowNull: false
17 } 31 }
18 },
19 {
20 classMethods: {
21 associate,
22
23 listWithLimitAndRandom,
24
25 countTotalRequests,
26 removeAll,
27 removeWithEmptyTo
28 }
29 } 32 }
30 ) 33 )
31 34
35 const classMethods = [
36 associate,
37
38 listWithLimitAndRandom,
39
40 countTotalRequests,
41 removeAll,
42 removeWithEmptyTo
43 ]
44 addMethodsToModel(Request, classMethods)
45
32 return Request 46 return Request
33} 47}
34 48
35// ------------------------------ STATICS ------------------------------ 49// ------------------------------ STATICS ------------------------------
36 50
37function associate (models) { 51function associate (models) {
38 this.belongsToMany(models.Pod, { 52 Request.belongsToMany(models.Pod, {
39 foreignKey: { 53 foreignKey: {
40 name: 'requestId', 54 name: 'requestId',
41 allowNull: false 55 allowNull: false
@@ -45,19 +59,18 @@ function associate (models) {
45 }) 59 })
46} 60}
47 61
48function countTotalRequests (callback) { 62countTotalRequests = function (callback) {
49 // We need to include Pod because there are no cascade delete when a pod is removed 63 // We need to include Pod because there are no cascade delete when a pod is removed
50 // So we could count requests that do not have existing pod anymore 64 // So we could count requests that do not have existing pod anymore
51 const query = { 65 const query = {
52 include: [ this.sequelize.models.Pod ] 66 include: [ Request['sequelize'].models.Pod ]
53 } 67 }
54 68
55 return this.count(query).asCallback(callback) 69 return Request.count(query).asCallback(callback)
56} 70}
57 71
58function listWithLimitAndRandom (limitPods, limitRequestsPerPod, callback) { 72listWithLimitAndRandom = function (limitPods, limitRequestsPerPod, callback) {
59 const self = this 73 const Pod = Request['sequelize'].models.Pod
60 const Pod = this.sequelize.models.Pod
61 74
62 Pod.listRandomPodIdsWithRequest(limitPods, 'RequestToPods', function (err, podIds) { 75 Pod.listRandomPodIdsWithRequest(limitPods, 'RequestToPods', function (err, podIds) {
63 if (err) return callback(err) 76 if (err) return callback(err)
@@ -73,7 +86,7 @@ function listWithLimitAndRandom (limitPods, limitRequestsPerPod, callback) {
73 ], 86 ],
74 include: [ 87 include: [
75 { 88 {
76 model: self.sequelize.models.Pod, 89 model: Request['sequelize'].models.Pod,
77 where: { 90 where: {
78 id: { 91 id: {
79 $in: podIds 92 $in: podIds
@@ -83,7 +96,7 @@ function listWithLimitAndRandom (limitPods, limitRequestsPerPod, callback) {
83 ] 96 ]
84 } 97 }
85 98
86 self.findAll(query).asCallback(function (err, requests) { 99 Request.findAll(query).asCallback(function (err, requests) {
87 if (err) return callback(err) 100 if (err) return callback(err)
88 101
89 const requestsGrouped = groupAndTruncateRequests(requests, limitRequestsPerPod) 102 const requestsGrouped = groupAndTruncateRequests(requests, limitRequestsPerPod)
@@ -92,25 +105,25 @@ function listWithLimitAndRandom (limitPods, limitRequestsPerPod, callback) {
92 }) 105 })
93} 106}
94 107
95function removeAll (callback) { 108removeAll = function (callback) {
96 // Delete all requests 109 // Delete all requests
97 this.truncate({ cascade: true }).asCallback(callback) 110 Request.truncate({ cascade: true }).asCallback(callback)
98} 111}
99 112
100function removeWithEmptyTo (callback) { 113removeWithEmptyTo = function (callback) {
101 if (!callback) callback = function () { /* empty */ } 114 if (!callback) callback = function () { /* empty */ }
102 115
103 const query = { 116 const query = {
104 where: { 117 where: {
105 id: { 118 id: {
106 $notIn: [ 119 $notIn: [
107 this.sequelize.literal('SELECT "requestId" FROM "RequestToPods"') 120 Sequelize.literal('SELECT "requestId" FROM "RequestToPods"')
108 ] 121 ]
109 } 122 }
110 } 123 }
111 } 124 }
112 125
113 this.destroy(query).asCallback(callback) 126 Request.destroy(query).asCallback(callback)
114} 127}
115 128
116// --------------------------------------------------------------------------- 129// ---------------------------------------------------------------------------
diff --git a/server/models/tag-interface.ts b/server/models/tag-interface.ts
new file mode 100644
index 000000000..f96e1e9c5
--- /dev/null
+++ b/server/models/tag-interface.ts
@@ -0,0 +1,19 @@
1import * as Sequelize from 'sequelize'
2
3export namespace TagMethods {
4 export type FindOrCreateTags = (tags, transaction, callback) => void
5}
6
7export interface TagClass {
8 findOrCreateTags: TagMethods.FindOrCreateTags
9}
10
11export interface TagAttributes {
12 name: string
13}
14
15export interface TagInstance extends TagClass, TagAttributes, Sequelize.Instance<TagAttributes> {
16 id: number
17}
18
19export interface TagModel extends TagClass, Sequelize.Model<TagInstance, TagAttributes> {}
diff --git a/server/models/tag.ts b/server/models/tag.ts
index 85a0442d2..b2a9c9f81 100644
--- a/server/models/tag.ts
+++ b/server/models/tag.ts
@@ -1,9 +1,20 @@
1import { each } from 'async' 1import { each } from 'async'
2import * as Sequelize from 'sequelize'
2 3
3// --------------------------------------------------------------------------- 4import { addMethodsToModel } from './utils'
5import {
6 TagClass,
7 TagInstance,
8 TagAttributes,
9
10 TagMethods
11} from './tag-interface'
4 12
5module.exports = function (sequelize, DataTypes) { 13let Tag: Sequelize.Model<TagInstance, TagAttributes>
6 const Tag = sequelize.define('Tag', 14let findOrCreateTags: TagMethods.FindOrCreateTags
15
16export default function (sequelize, DataTypes) {
17 Tag = sequelize.define('Tag',
7 { 18 {
8 name: { 19 name: {
9 type: DataTypes.STRING, 20 type: DataTypes.STRING,
@@ -17,35 +28,36 @@ module.exports = function (sequelize, DataTypes) {
17 fields: [ 'name' ], 28 fields: [ 'name' ],
18 unique: true 29 unique: true
19 } 30 }
20 ], 31 ]
21 classMethods: {
22 associate,
23
24 findOrCreateTags
25 }
26 } 32 }
27 ) 33 )
28 34
35 const classMethods = [
36 associate,
37
38 findOrCreateTags
39 ]
40 addMethodsToModel(Tag, classMethods)
41
29 return Tag 42 return Tag
30} 43}
31 44
32// --------------------------------------------------------------------------- 45// ---------------------------------------------------------------------------
33 46
34function associate (models) { 47function associate (models) {
35 this.belongsToMany(models.Video, { 48 Tag.belongsToMany(models.Video, {
36 foreignKey: 'tagId', 49 foreignKey: 'tagId',
37 through: models.VideoTag, 50 through: models.VideoTag,
38 onDelete: 'cascade' 51 onDelete: 'cascade'
39 }) 52 })
40} 53}
41 54
42function findOrCreateTags (tags, transaction, callback) { 55findOrCreateTags = function (tags, transaction, callback) {
43 if (!callback) { 56 if (!callback) {
44 callback = transaction 57 callback = transaction
45 transaction = null 58 transaction = null
46 } 59 }
47 60
48 const self = this
49 const tagInstances = [] 61 const tagInstances = []
50 62
51 each(tags, function (tag, callbackEach) { 63 each(tags, function (tag, callbackEach) {
@@ -60,7 +72,7 @@ function findOrCreateTags (tags, transaction, callback) {
60 72
61 if (transaction) query.transaction = transaction 73 if (transaction) query.transaction = transaction
62 74
63 self.findOrCreate(query).asCallback(function (err, res) { 75 Tag.findOrCreate(query).asCallback(function (err, res) {
64 if (err) return callbackEach(err) 76 if (err) return callbackEach(err)
65 77
66 // res = [ tag, isCreated ] 78 // res = [ tag, isCreated ]
diff --git a/server/models/user-interface.ts b/server/models/user-interface.ts
new file mode 100644
index 000000000..a504f42a1
--- /dev/null
+++ b/server/models/user-interface.ts
@@ -0,0 +1,45 @@
1import * as Sequelize from 'sequelize'
2
3export namespace UserMethods {
4 export type IsPasswordMatch = (password, callback) => void
5 export type ToFormatedJSON = () => void
6 export type IsAdmin = () => boolean
7
8 export type CountTotal = (callback) => void
9 export type GetByUsername = (username) => any
10 export type List = (callback) => void
11 export type ListForApi = (start, count, sort, callback) => void
12 export type LoadById = (id, callback) => void
13 export type LoadByUsername = (username, callback) => void
14 export type LoadByUsernameOrEmail = (username, email, callback) => void
15}
16
17export interface UserClass {
18 isPasswordMatch: UserMethods.IsPasswordMatch,
19 toFormatedJSON: UserMethods.ToFormatedJSON,
20 isAdmin: UserMethods.IsAdmin,
21
22 countTotal: UserMethods.CountTotal,
23 getByUsername: UserMethods.GetByUsername,
24 list: UserMethods.List,
25 listForApi: UserMethods.ListForApi,
26 loadById: UserMethods.LoadById,
27 loadByUsername: UserMethods.LoadByUsername,
28 loadByUsernameOrEmail: UserMethods.LoadByUsernameOrEmail
29}
30
31export interface UserAttributes {
32 password: string
33 username: string
34 email: string
35 displayNSFW?: boolean
36 role: string
37}
38
39export interface UserInstance extends UserClass, UserAttributes, Sequelize.Instance<UserAttributes> {
40 id: number
41 createdAt: Date
42 updatedAt: Date
43}
44
45export interface UserModel extends UserClass, Sequelize.Model<UserInstance, UserAttributes> {}
diff --git a/server/models/user-video-rate-interface.ts b/server/models/user-video-rate-interface.ts
new file mode 100644
index 000000000..57d2e2b91
--- /dev/null
+++ b/server/models/user-video-rate-interface.ts
@@ -0,0 +1,21 @@
1import * as Sequelize from 'sequelize'
2
3export namespace UserVideoRateMethods {
4 export type Load = (userId, videoId, transaction, callback) => void
5}
6
7export interface UserVideoRateClass {
8 load: UserVideoRateMethods.Load
9}
10
11export interface UserVideoRateAttributes {
12 type: string
13}
14
15export interface UserVideoRateInstance extends Sequelize.Instance<UserVideoRateAttributes> {
16 id: number
17 createdAt: Date
18 updatedAt: Date
19}
20
21export interface UserVideoRateModel extends UserVideoRateClass, Sequelize.Model<UserVideoRateInstance, UserVideoRateAttributes> {}
diff --git a/server/models/user-video-rate.ts b/server/models/user-video-rate.ts
index 6603c7862..87886d8d0 100644
--- a/server/models/user-video-rate.ts
+++ b/server/models/user-video-rate.ts
@@ -3,13 +3,24 @@
3 3
4*/ 4*/
5import { values } from 'lodash' 5import { values } from 'lodash'
6import * as Sequelize from 'sequelize'
6 7
7import { VIDEO_RATE_TYPES } from '../initializers' 8import { VIDEO_RATE_TYPES } from '../initializers'
8 9
9// --------------------------------------------------------------------------- 10import { addMethodsToModel } from './utils'
11import {
12 UserVideoRateClass,
13 UserVideoRateInstance,
14 UserVideoRateAttributes,
10 15
11module.exports = function (sequelize, DataTypes) { 16 UserVideoRateMethods
12 const UserVideoRate = sequelize.define('UserVideoRate', 17} from './user-video-rate-interface'
18
19let UserVideoRate: Sequelize.Model<UserVideoRateInstance, UserVideoRateAttributes>
20let load: UserVideoRateMethods.Load
21
22export default function (sequelize, DataTypes) {
23 UserVideoRate = sequelize.define('UserVideoRate',
13 { 24 {
14 type: { 25 type: {
15 type: DataTypes.ENUM(values(VIDEO_RATE_TYPES)), 26 type: DataTypes.ENUM(values(VIDEO_RATE_TYPES)),
@@ -22,22 +33,24 @@ module.exports = function (sequelize, DataTypes) {
22 fields: [ 'videoId', 'userId', 'type' ], 33 fields: [ 'videoId', 'userId', 'type' ],
23 unique: true 34 unique: true
24 } 35 }
25 ], 36 ]
26 classMethods: {
27 associate,
28
29 load
30 }
31 } 37 }
32 ) 38 )
33 39
40 const classMethods = [
41 associate,
42
43 load
44 ]
45 addMethodsToModel(UserVideoRate, classMethods)
46
34 return UserVideoRate 47 return UserVideoRate
35} 48}
36 49
37// ------------------------------ STATICS ------------------------------ 50// ------------------------------ STATICS ------------------------------
38 51
39function associate (models) { 52function associate (models) {
40 this.belongsTo(models.Video, { 53 UserVideoRate.belongsTo(models.Video, {
41 foreignKey: { 54 foreignKey: {
42 name: 'videoId', 55 name: 'videoId',
43 allowNull: false 56 allowNull: false
@@ -45,7 +58,7 @@ function associate (models) {
45 onDelete: 'CASCADE' 58 onDelete: 'CASCADE'
46 }) 59 })
47 60
48 this.belongsTo(models.User, { 61 UserVideoRate.belongsTo(models.User, {
49 foreignKey: { 62 foreignKey: {
50 name: 'userId', 63 name: 'userId',
51 allowNull: false 64 allowNull: false
@@ -54,21 +67,14 @@ function associate (models) {
54 }) 67 })
55} 68}
56 69
57function load (userId, videoId, transaction, callback) { 70load = function (userId, videoId, transaction, callback) {
58 if (!callback) { 71 const options: Sequelize.FindOptions = {
59 callback = transaction
60 transaction = null
61 }
62
63 const query = {
64 where: { 72 where: {
65 userId, 73 userId,
66 videoId 74 videoId
67 } 75 }
68 } 76 }
69
70 const options: any = {}
71 if (transaction) options.transaction = transaction 77 if (transaction) options.transaction = transaction
72 78
73 return this.findOne(query, options).asCallback(callback) 79 return UserVideoRate.findOne(options).asCallback(callback)
74} 80}
diff --git a/server/models/user.ts b/server/models/user.ts
index d63a50cc4..12ddaaeb7 100644
--- a/server/models/user.ts
+++ b/server/models/user.ts
@@ -1,4 +1,5 @@
1import { values } from 'lodash' 1import { values } from 'lodash'
2import * as Sequelize from 'sequelize'
2 3
3import { getSort } from './utils' 4import { getSort } from './utils'
4import { USER_ROLES } from '../initializers' 5import { USER_ROLES } from '../initializers'
@@ -10,10 +11,29 @@ import {
10 isUserDisplayNSFWValid 11 isUserDisplayNSFWValid
11} from '../helpers' 12} from '../helpers'
12 13
13// --------------------------------------------------------------------------- 14import { addMethodsToModel } from './utils'
14 15import {
15module.exports = function (sequelize, DataTypes) { 16 UserClass,
16 const User = sequelize.define('User', 17 UserInstance,
18 UserAttributes,
19
20 UserMethods
21} from './user-interface'
22
23let User: Sequelize.Model<UserInstance, UserAttributes>
24let isPasswordMatch: UserMethods.IsPasswordMatch
25let toFormatedJSON: UserMethods.ToFormatedJSON
26let isAdmin: UserMethods.IsAdmin
27let countTotal: UserMethods.CountTotal
28let getByUsername: UserMethods.GetByUsername
29let list: UserMethods.List
30let listForApi: UserMethods.ListForApi
31let loadById: UserMethods.LoadById
32let loadByUsername: UserMethods.LoadByUsername
33let loadByUsernameOrEmail: UserMethods.LoadByUsernameOrEmail
34
35export default function (sequelize, DataTypes) {
36 User = sequelize.define('User',
17 { 37 {
18 password: { 38 password: {
19 type: DataTypes.STRING, 39 type: DataTypes.STRING,
@@ -69,22 +89,6 @@ module.exports = function (sequelize, DataTypes) {
69 unique: true 89 unique: true
70 } 90 }
71 ], 91 ],
72 classMethods: {
73 associate,
74
75 countTotal,
76 getByUsername,
77 list,
78 listForApi,
79 loadById,
80 loadByUsername,
81 loadByUsernameOrEmail
82 },
83 instanceMethods: {
84 isPasswordMatch,
85 toFormatedJSON,
86 isAdmin
87 },
88 hooks: { 92 hooks: {
89 beforeCreate: beforeCreateOrUpdate, 93 beforeCreate: beforeCreateOrUpdate,
90 beforeUpdate: beforeCreateOrUpdate 94 beforeUpdate: beforeCreateOrUpdate
@@ -92,26 +96,46 @@ module.exports = function (sequelize, DataTypes) {
92 } 96 }
93 ) 97 )
94 98
99 const classMethods = [
100 associate,
101
102 countTotal,
103 getByUsername,
104 list,
105 listForApi,
106 loadById,
107 loadByUsername,
108 loadByUsernameOrEmail
109 ]
110 const instanceMethods = [
111 isPasswordMatch,
112 toFormatedJSON,
113 isAdmin
114 ]
115 addMethodsToModel(User, classMethods, instanceMethods)
116
95 return User 117 return User
96} 118}
97 119
98function beforeCreateOrUpdate (user, options, next) { 120function beforeCreateOrUpdate (user, options) {
99 cryptPassword(user.password, function (err, hash) { 121 return new Promise(function (resolve, reject) {
100 if (err) return next(err) 122 cryptPassword(user.password, function (err, hash) {
123 if (err) return reject(err)
101 124
102 user.password = hash 125 user.password = hash
103 126
104 return next() 127 return resolve()
128 })
105 }) 129 })
106} 130}
107 131
108// ------------------------------ METHODS ------------------------------ 132// ------------------------------ METHODS ------------------------------
109 133
110function isPasswordMatch (password, callback) { 134isPasswordMatch = function (password, callback) {
111 return comparePassword(password, this.password, callback) 135 return comparePassword(password, this.password, callback)
112} 136}
113 137
114function toFormatedJSON () { 138toFormatedJSON = function () {
115 return { 139 return {
116 id: this.id, 140 id: this.id,
117 username: this.username, 141 username: this.username,
@@ -122,76 +146,76 @@ function toFormatedJSON () {
122 } 146 }
123} 147}
124 148
125function isAdmin () { 149isAdmin = function () {
126 return this.role === USER_ROLES.ADMIN 150 return this.role === USER_ROLES.ADMIN
127} 151}
128 152
129// ------------------------------ STATICS ------------------------------ 153// ------------------------------ STATICS ------------------------------
130 154
131function associate (models) { 155function associate (models) {
132 this.hasOne(models.Author, { 156 User.hasOne(models.Author, {
133 foreignKey: 'userId', 157 foreignKey: 'userId',
134 onDelete: 'cascade' 158 onDelete: 'cascade'
135 }) 159 })
136 160
137 this.hasMany(models.OAuthToken, { 161 User.hasMany(models.OAuthToken, {
138 foreignKey: 'userId', 162 foreignKey: 'userId',
139 onDelete: 'cascade' 163 onDelete: 'cascade'
140 }) 164 })
141} 165}
142 166
143function countTotal (callback) { 167countTotal = function (callback) {
144 return this.count().asCallback(callback) 168 return this.count().asCallback(callback)
145} 169}
146 170
147function getByUsername (username) { 171getByUsername = function (username) {
148 const query = { 172 const query = {
149 where: { 173 where: {
150 username: username 174 username: username
151 } 175 }
152 } 176 }
153 177
154 return this.findOne(query) 178 return User.findOne(query)
155} 179}
156 180
157function list (callback) { 181list = function (callback) {
158 return this.find().asCallback(callback) 182 return User.find().asCallback(callback)
159} 183}
160 184
161function listForApi (start, count, sort, callback) { 185listForApi = function (start, count, sort, callback) {
162 const query = { 186 const query = {
163 offset: start, 187 offset: start,
164 limit: count, 188 limit: count,
165 order: [ getSort(sort) ] 189 order: [ getSort(sort) ]
166 } 190 }
167 191
168 return this.findAndCountAll(query).asCallback(function (err, result) { 192 return User.findAndCountAll(query).asCallback(function (err, result) {
169 if (err) return callback(err) 193 if (err) return callback(err)
170 194
171 return callback(null, result.rows, result.count) 195 return callback(null, result.rows, result.count)
172 }) 196 })
173} 197}
174 198
175function loadById (id, callback) { 199loadById = function (id, callback) {
176 return this.findById(id).asCallback(callback) 200 return User.findById(id).asCallback(callback)
177} 201}
178 202
179function loadByUsername (username, callback) { 203loadByUsername = function (username, callback) {
180 const query = { 204 const query = {
181 where: { 205 where: {
182 username: username 206 username: username
183 } 207 }
184 } 208 }
185 209
186 return this.findOne(query).asCallback(callback) 210 return User.findOne(query).asCallback(callback)
187} 211}
188 212
189function loadByUsernameOrEmail (username, email, callback) { 213loadByUsernameOrEmail = function (username, email, callback) {
190 const query = { 214 const query = {
191 where: { 215 where: {
192 $or: [ { username }, { email } ] 216 $or: [ { username }, { email } ]
193 } 217 }
194 } 218 }
195 219
196 return this.findOne(query).asCallback(callback) 220 return User.findOne(query).asCallback(callback)
197} 221}
diff --git a/server/models/utils.ts b/server/models/utils.ts
index 601811913..fd84a9239 100644
--- a/server/models/utils.ts
+++ b/server/models/utils.ts
@@ -14,8 +14,14 @@ function getSort (value) {
14 return [ field, direction ] 14 return [ field, direction ]
15} 15}
16 16
17function addMethodsToModel (model: any, classMethods: Function[], instanceMethods: Function[] = []) {
18 classMethods.forEach(m => model[m.name] = m)
19 instanceMethods.forEach(m => model.prototype[m.name] = m)
20}
21
17// --------------------------------------------------------------------------- 22// ---------------------------------------------------------------------------
18 23
19export { 24export {
25 addMethodsToModel,
20 getSort 26 getSort
21} 27}
diff --git a/server/models/video-abuse-interface.ts b/server/models/video-abuse-interface.ts
new file mode 100644
index 000000000..9b77fc6f5
--- /dev/null
+++ b/server/models/video-abuse-interface.ts
@@ -0,0 +1,24 @@
1import * as Sequelize from 'sequelize'
2
3export namespace VideoAbuseMethods {
4 export type toFormatedJSON = () => void
5
6 export type ListForApi = (start, count, sort, callback) => void
7}
8
9export interface VideoAbuseClass {
10 listForApi: VideoAbuseMethods.ListForApi
11}
12
13export interface VideoAbuseAttributes {
14 reporterUsername: string
15 reason: string
16}
17
18export interface VideoAbuseInstance extends Sequelize.Instance<VideoAbuseAttributes> {
19 id: number
20 createdAt: Date
21 updatedAt: Date
22}
23
24export interface VideoAbuseModel extends VideoAbuseClass, Sequelize.Model<VideoAbuseInstance, VideoAbuseAttributes> {}
diff --git a/server/models/video-abuse.ts b/server/models/video-abuse.ts
index 2a18a293d..92168439c 100644
--- a/server/models/video-abuse.ts
+++ b/server/models/video-abuse.ts
@@ -1,9 +1,22 @@
1import * as Sequelize from 'sequelize'
2
1import { CONFIG } from '../initializers' 3import { CONFIG } from '../initializers'
2import { isVideoAbuseReporterUsernameValid, isVideoAbuseReasonValid } from '../helpers' 4import { isVideoAbuseReporterUsernameValid, isVideoAbuseReasonValid } from '../helpers'
3import { getSort } from './utils'
4 5
5module.exports = function (sequelize, DataTypes) { 6import { addMethodsToModel, getSort } from './utils'
6 const VideoAbuse = sequelize.define('VideoAbuse', 7import {
8 VideoAbuseClass,
9 VideoAbuseInstance,
10 VideoAbuseAttributes,
11
12 VideoAbuseMethods
13} from './video-abuse-interface'
14
15let VideoAbuse: Sequelize.Model<VideoAbuseInstance, VideoAbuseAttributes>
16let listForApi: VideoAbuseMethods.ListForApi
17
18export default function (sequelize, DataTypes) {
19 VideoAbuse = sequelize.define('VideoAbuse',
7 { 20 {
8 reporterUsername: { 21 reporterUsername: {
9 type: DataTypes.STRING, 22 type: DataTypes.STRING,
@@ -34,25 +47,51 @@ module.exports = function (sequelize, DataTypes) {
34 { 47 {
35 fields: [ 'reporterPodId' ] 48 fields: [ 'reporterPodId' ]
36 } 49 }
37 ], 50 ]
38 classMethods: {
39 associate,
40
41 listForApi
42 },
43 instanceMethods: {
44 toFormatedJSON
45 }
46 } 51 }
47 ) 52 )
48 53
54 const classMethods = [
55 associate,
56
57 listForApi
58 ]
59 const instanceMethods = [
60 toFormatedJSON
61 ]
62 addMethodsToModel(VideoAbuse, classMethods, instanceMethods)
63
49 return VideoAbuse 64 return VideoAbuse
50} 65}
51 66
52// --------------------------------------------------------------------------- 67// ------------------------------ METHODS ------------------------------
68
69function toFormatedJSON () {
70 let reporterPodHost
71
72 if (this.Pod) {
73 reporterPodHost = this.Pod.host
74 } else {
75 // It means it's our video
76 reporterPodHost = CONFIG.WEBSERVER.HOST
77 }
78
79 const json = {
80 id: this.id,
81 reporterPodHost,
82 reason: this.reason,
83 reporterUsername: this.reporterUsername,
84 videoId: this.videoId,
85 createdAt: this.createdAt
86 }
87
88 return json
89}
90
91// ------------------------------ STATICS ------------------------------
53 92
54function associate (models) { 93function associate (models) {
55 this.belongsTo(models.Pod, { 94 VideoAbuse.belongsTo(models.Pod, {
56 foreignKey: { 95 foreignKey: {
57 name: 'reporterPodId', 96 name: 'reporterPodId',
58 allowNull: true 97 allowNull: true
@@ -60,7 +99,7 @@ function associate (models) {
60 onDelete: 'cascade' 99 onDelete: 'cascade'
61 }) 100 })
62 101
63 this.belongsTo(models.Video, { 102 VideoAbuse.belongsTo(models.Video, {
64 foreignKey: { 103 foreignKey: {
65 name: 'videoId', 104 name: 'videoId',
66 allowNull: false 105 allowNull: false
@@ -69,44 +108,24 @@ function associate (models) {
69 }) 108 })
70} 109}
71 110
72function listForApi (start, count, sort, callback) { 111listForApi = function (start, count, sort, callback) {
73 const query = { 112 const query = {
74 offset: start, 113 offset: start,
75 limit: count, 114 limit: count,
76 order: [ getSort(sort) ], 115 order: [ getSort(sort) ],
77 include: [ 116 include: [
78 { 117 {
79 model: this.sequelize.models.Pod, 118 model: VideoAbuse['sequelize'].models.Pod,
80 required: false 119 required: false
81 } 120 }
82 ] 121 ]
83 } 122 }
84 123
85 return this.findAndCountAll(query).asCallback(function (err, result) { 124 return VideoAbuse.findAndCountAll(query).asCallback(function (err, result) {
86 if (err) return callback(err) 125 if (err) return callback(err)
87 126
88 return callback(null, result.rows, result.count) 127 return callback(null, result.rows, result.count)
89 }) 128 })
90} 129}
91 130
92function toFormatedJSON () {
93 let reporterPodHost
94
95 if (this.Pod) {
96 reporterPodHost = this.Pod.host
97 } else {
98 // It means it's our video
99 reporterPodHost = CONFIG.WEBSERVER.HOST
100 }
101
102 const json = {
103 id: this.id,
104 reporterPodHost,
105 reason: this.reason,
106 reporterUsername: this.reporterUsername,
107 videoId: this.videoId,
108 createdAt: this.createdAt
109 }
110 131
111 return json
112}
diff --git a/server/models/video-blacklist-interface.ts b/server/models/video-blacklist-interface.ts
new file mode 100644
index 000000000..ae2cd6748
--- /dev/null
+++ b/server/models/video-blacklist-interface.ts
@@ -0,0 +1,31 @@
1import * as Sequelize from 'sequelize'
2
3export namespace BlacklistedVideoMethods {
4 export type ToFormatedJSON = () => void
5
6 export type CountTotal = (callback) => void
7 export type List = (callback) => void
8 export type ListForApi = (start, count, sort, callback) => void
9 export type LoadById = (id, callback) => void
10 export type LoadByVideoId = (id, callback) => void
11}
12
13export interface BlacklistedVideoClass {
14 toFormatedJSON: BlacklistedVideoMethods.ToFormatedJSON
15 countTotal: BlacklistedVideoMethods.CountTotal
16 list: BlacklistedVideoMethods.List
17 listForApi: BlacklistedVideoMethods.ListForApi
18 loadById: BlacklistedVideoMethods.LoadById
19 loadByVideoId: BlacklistedVideoMethods.LoadByVideoId
20}
21
22export interface BlacklistedVideoAttributes {
23}
24
25export interface BlacklistedVideoInstance extends BlacklistedVideoClass, BlacklistedVideoAttributes, Sequelize.Instance<BlacklistedVideoAttributes> {
26 id: number
27 createdAt: Date
28 updatedAt: Date
29}
30
31export interface BlacklistedVideoModel extends BlacklistedVideoClass, Sequelize.Model<BlacklistedVideoInstance, BlacklistedVideoAttributes> {}
diff --git a/server/models/video-blacklist.ts b/server/models/video-blacklist.ts
index 1f00702c7..fe72d5d46 100644
--- a/server/models/video-blacklist.ts
+++ b/server/models/video-blacklist.ts
@@ -1,9 +1,24 @@
1import { getSort } from './utils' 1import * as Sequelize from 'sequelize'
2 2
3// --------------------------------------------------------------------------- 3import { addMethodsToModel, getSort } from './utils'
4 4import {
5module.exports = function (sequelize, DataTypes) { 5 BlacklistedVideoClass,
6 const BlacklistedVideo = sequelize.define('BlacklistedVideo', 6 BlacklistedVideoInstance,
7 BlacklistedVideoAttributes,
8
9 BlacklistedVideoMethods
10} from './video-blacklist-interface'
11
12let BlacklistedVideo: Sequelize.Model<BlacklistedVideoInstance, BlacklistedVideoAttributes>
13let toFormatedJSON: BlacklistedVideoMethods.ToFormatedJSON
14let countTotal: BlacklistedVideoMethods.CountTotal
15let list: BlacklistedVideoMethods.List
16let listForApi: BlacklistedVideoMethods.ListForApi
17let loadById: BlacklistedVideoMethods.LoadById
18let loadByVideoId: BlacklistedVideoMethods.LoadByVideoId
19
20export default function (sequelize, DataTypes) {
21 BlacklistedVideo = sequelize.define('BlacklistedVideo',
7 {}, 22 {},
8 { 23 {
9 indexes: [ 24 indexes: [
@@ -11,29 +26,30 @@ module.exports = function (sequelize, DataTypes) {
11 fields: [ 'videoId' ], 26 fields: [ 'videoId' ],
12 unique: true 27 unique: true
13 } 28 }
14 ], 29 ]
15 classMethods: {
16 associate,
17
18 countTotal,
19 list,
20 listForApi,
21 loadById,
22 loadByVideoId
23 },
24 instanceMethods: {
25 toFormatedJSON
26 },
27 hooks: {}
28 } 30 }
29 ) 31 )
30 32
33 const classMethods = [
34 associate,
35
36 countTotal,
37 list,
38 listForApi,
39 loadById,
40 loadByVideoId
41 ]
42 const instanceMethods = [
43 toFormatedJSON
44 ]
45 addMethodsToModel(BlacklistedVideo, classMethods, instanceMethods)
46
31 return BlacklistedVideo 47 return BlacklistedVideo
32} 48}
33 49
34// ------------------------------ METHODS ------------------------------ 50// ------------------------------ METHODS ------------------------------
35 51
36function toFormatedJSON () { 52toFormatedJSON = function () {
37 return { 53 return {
38 id: this.id, 54 id: this.id,
39 videoId: this.videoId, 55 videoId: this.videoId,
@@ -44,44 +60,44 @@ function toFormatedJSON () {
44// ------------------------------ STATICS ------------------------------ 60// ------------------------------ STATICS ------------------------------
45 61
46function associate (models) { 62function associate (models) {
47 this.belongsTo(models.Video, { 63 BlacklistedVideo.belongsTo(models.Video, {
48 foreignKey: 'videoId', 64 foreignKey: 'videoId',
49 onDelete: 'cascade' 65 onDelete: 'cascade'
50 }) 66 })
51} 67}
52 68
53function countTotal (callback) { 69countTotal = function (callback) {
54 return this.count().asCallback(callback) 70 return BlacklistedVideo.count().asCallback(callback)
55} 71}
56 72
57function list (callback) { 73list = function (callback) {
58 return this.findAll().asCallback(callback) 74 return BlacklistedVideo.findAll().asCallback(callback)
59} 75}
60 76
61function listForApi (start, count, sort, callback) { 77listForApi = function (start, count, sort, callback) {
62 const query = { 78 const query = {
63 offset: start, 79 offset: start,
64 limit: count, 80 limit: count,
65 order: [ getSort(sort) ] 81 order: [ getSort(sort) ]
66 } 82 }
67 83
68 return this.findAndCountAll(query).asCallback(function (err, result) { 84 return BlacklistedVideo.findAndCountAll(query).asCallback(function (err, result) {
69 if (err) return callback(err) 85 if (err) return callback(err)
70 86
71 return callback(null, result.rows, result.count) 87 return callback(null, result.rows, result.count)
72 }) 88 })
73} 89}
74 90
75function loadById (id, callback) { 91loadById = function (id, callback) {
76 return this.findById(id).asCallback(callback) 92 return BlacklistedVideo.findById(id).asCallback(callback)
77} 93}
78 94
79function loadByVideoId (id, callback) { 95loadByVideoId = function (id, callback) {
80 const query = { 96 const query = {
81 where: { 97 where: {
82 videoId: id 98 videoId: id
83 } 99 }
84 } 100 }
85 101
86 return this.find(query).asCallback(callback) 102 return BlacklistedVideo.find(query).asCallback(callback)
87} 103}
diff --git a/server/models/video-interface.ts b/server/models/video-interface.ts
new file mode 100644
index 000000000..b8dbeea35
--- /dev/null
+++ b/server/models/video-interface.ts
@@ -0,0 +1,75 @@
1import * as Sequelize from 'sequelize'
2
3export namespace VideoMethods {
4 export type GenerateMagnetUri = () => void
5 export type GetVideoFilename = () => void
6 export type GetThumbnailName = () => void
7 export type GetPreviewName = () => void
8 export type GetTorrentName = () => void
9 export type IsOwned = () => void
10 export type ToFormatedJSON = () => void
11 export type ToAddRemoteJSON = (callback) => void
12 export type ToUpdateRemoteJSON = (callback) => void
13 export type TranscodeVideofile = (callback) => void
14
15 export type GenerateThumbnailFromData = (video, thumbnailData, callback) => void
16 export type GetDurationFromFile = (videoPath, callback) => void
17 export type List = (callback) => void
18 export type ListForApi = (start, count, sort, callback) => void
19 export type LoadByHostAndRemoteId = (fromHost, remoteId, callback) => void
20 export type ListOwnedAndPopulateAuthorAndTags = (callback) => void
21 export type ListOwnedByAuthor = (author, callback) => void
22 export type Load = (id, callback) => void
23 export type LoadAndPopulateAuthor = (id, callback) => void
24 export type LoadAndPopulateAuthorAndPodAndTags = (id, callback) => void
25 export type SearchAndPopulateAuthorAndPodAndTags = (value, field, start, count, sort, callback) => void
26}
27
28export interface VideoClass {
29 generateMagnetUri: VideoMethods.GenerateMagnetUri
30 getVideoFilename: VideoMethods.GetVideoFilename
31 getThumbnailName: VideoMethods.GetThumbnailName
32 getPreviewName: VideoMethods.GetPreviewName
33 getTorrentName: VideoMethods.GetTorrentName
34 isOwned: VideoMethods.IsOwned
35 toFormatedJSON: VideoMethods.ToFormatedJSON
36 toAddRemoteJSON: VideoMethods.ToAddRemoteJSON
37 toUpdateRemoteJSON: VideoMethods.ToUpdateRemoteJSON
38 transcodeVideofile: VideoMethods.TranscodeVideofile
39
40 generateThumbnailFromData: VideoMethods.GenerateThumbnailFromData
41 getDurationFromFile: VideoMethods.GetDurationFromFile
42 list: VideoMethods.List
43 listForApi: VideoMethods.ListForApi
44 loadByHostAndRemoteId: VideoMethods.LoadByHostAndRemoteId
45 listOwnedAndPopulateAuthorAndTags: VideoMethods.ListOwnedAndPopulateAuthorAndTags
46 listOwnedByAuthor: VideoMethods.ListOwnedByAuthor
47 load: VideoMethods.Load
48 loadAndPopulateAuthor: VideoMethods.LoadAndPopulateAuthor
49 loadAndPopulateAuthorAndPodAndTags: VideoMethods.LoadAndPopulateAuthorAndPodAndTags
50 searchAndPopulateAuthorAndPodAndTags: VideoMethods.SearchAndPopulateAuthorAndPodAndTags
51}
52
53export interface VideoAttributes {
54 name: string
55 extname: string
56 remoteId: string
57 category: number
58 licence: number
59 language: number
60 nsfw: boolean
61 description: string
62 infoHash?: string
63 duration: number
64 views?: number
65 likes?: number
66 dislikes?: number
67}
68
69export interface VideoInstance extends VideoClass, VideoAttributes, Sequelize.Instance<VideoAttributes> {
70 id: string
71 createdAt: Date
72 updatedAt: Date
73}
74
75export interface VideoModel extends VideoClass, Sequelize.Model<VideoInstance, VideoAttributes> {}
diff --git a/server/models/video-tag-interface.ts b/server/models/video-tag-interface.ts
new file mode 100644
index 000000000..468827b8c
--- /dev/null
+++ b/server/models/video-tag-interface.ts
@@ -0,0 +1,18 @@
1import * as Sequelize from 'sequelize'
2
3export namespace VideoTagMethods {
4}
5
6export interface VideoTagClass {
7}
8
9export interface VideoTagAttributes {
10}
11
12export interface VideoTagInstance extends Sequelize.Instance<VideoTagAttributes> {
13 id: number
14 createdAt: Date
15 updatedAt: Date
16}
17
18export interface VideoTagModel extends VideoTagClass, Sequelize.Model<VideoTagInstance, VideoTagAttributes> {}
diff --git a/server/models/video-tag.ts b/server/models/video-tag.ts
index 83ff6053f..2ccaf820d 100644
--- a/server/models/video-tag.ts
+++ b/server/models/video-tag.ts
@@ -1,5 +1,18 @@
1module.exports = function (sequelize, DataTypes) { 1import * as Sequelize from 'sequelize'
2 const VideoTag = sequelize.define('VideoTag', {}, { 2
3import { addMethodsToModel } from './utils'
4import {
5 VideoTagClass,
6 VideoTagInstance,
7 VideoTagAttributes,
8
9 VideoTagMethods
10} from './video-tag-interface'
11
12let VideoTag: Sequelize.Model<VideoTagInstance, VideoTagAttributes>
13
14export default function (sequelize, DataTypes) {
15 VideoTag = sequelize.define('VideoTag', {}, {
3 indexes: [ 16 indexes: [
4 { 17 {
5 fields: [ 'videoId' ] 18 fields: [ 'videoId' ]
diff --git a/server/models/video.ts b/server/models/video.ts
index 1e29f1355..9284dfeba 100644
--- a/server/models/video.ts
+++ b/server/models/video.ts
@@ -8,8 +8,9 @@ import { map, values } from 'lodash'
8import { parallel, series } from 'async' 8import { parallel, series } from 'async'
9import parseTorrent = require('parse-torrent') 9import parseTorrent = require('parse-torrent')
10import { join } from 'path' 10import { join } from 'path'
11import * as Sequelize from 'sequelize'
11 12
12const db = require('../initializers/database') 13import { database as db } from '../initializers/database'
13import { 14import {
14 logger, 15 logger,
15 isVideoNameValid, 16 isVideoNameValid,
@@ -32,12 +33,42 @@ import {
32 THUMBNAILS_SIZE 33 THUMBNAILS_SIZE
33} from '../initializers' 34} from '../initializers'
34import { JobScheduler, removeVideoToFriends } from '../lib' 35import { JobScheduler, removeVideoToFriends } from '../lib'
35import { getSort } from './utils'
36 36
37// --------------------------------------------------------------------------- 37import { addMethodsToModel, getSort } from './utils'
38 38import {
39module.exports = function (sequelize, DataTypes) { 39 VideoClass,
40 const Video = sequelize.define('Video', 40 VideoInstance,
41 VideoAttributes,
42
43 VideoMethods
44} from './video-interface'
45
46let Video: Sequelize.Model<VideoInstance, VideoAttributes>
47let generateMagnetUri: VideoMethods.GenerateMagnetUri
48let getVideoFilename: VideoMethods.GetVideoFilename
49let getThumbnailName: VideoMethods.GetThumbnailName
50let getPreviewName: VideoMethods.GetPreviewName
51let getTorrentName: VideoMethods.GetTorrentName
52let isOwned: VideoMethods.IsOwned
53let toFormatedJSON: VideoMethods.ToFormatedJSON
54let toAddRemoteJSON: VideoMethods.ToAddRemoteJSON
55let toUpdateRemoteJSON: VideoMethods.ToUpdateRemoteJSON
56let transcodeVideofile: VideoMethods.TranscodeVideofile
57
58let generateThumbnailFromData: VideoMethods.GenerateThumbnailFromData
59let getDurationFromFile: VideoMethods.GetDurationFromFile
60let list: VideoMethods.List
61let listForApi: VideoMethods.ListForApi
62let loadByHostAndRemoteId: VideoMethods.LoadByHostAndRemoteId
63let listOwnedAndPopulateAuthorAndTags: VideoMethods.ListOwnedAndPopulateAuthorAndTags
64let listOwnedByAuthor: VideoMethods.ListOwnedByAuthor
65let load: VideoMethods.Load
66let loadAndPopulateAuthor: VideoMethods.LoadAndPopulateAuthor
67let loadAndPopulateAuthorAndPodAndTags: VideoMethods.LoadAndPopulateAuthorAndPodAndTags
68let searchAndPopulateAuthorAndPodAndTags: VideoMethods.SearchAndPopulateAuthorAndPodAndTags
69
70export default function (sequelize, DataTypes) {
71 Video = sequelize.define('Video',
41 { 72 {
42 id: { 73 id: {
43 type: DataTypes.UUID, 74 type: DataTypes.UUID,
@@ -194,34 +225,6 @@ module.exports = function (sequelize, DataTypes) {
194 fields: [ 'likes' ] 225 fields: [ 'likes' ]
195 } 226 }
196 ], 227 ],
197 classMethods: {
198 associate,
199
200 generateThumbnailFromData,
201 getDurationFromFile,
202 list,
203 listForApi,
204 listOwnedAndPopulateAuthorAndTags,
205 listOwnedByAuthor,
206 load,
207 loadByHostAndRemoteId,
208 loadAndPopulateAuthor,
209 loadAndPopulateAuthorAndPodAndTags,
210 searchAndPopulateAuthorAndPodAndTags
211 },
212 instanceMethods: {
213 generateMagnetUri,
214 getVideoFilename,
215 getThumbnailName,
216 getPreviewName,
217 getTorrentName,
218 isOwned,
219 toFormatedJSON,
220 toAddRemoteJSON,
221 toUpdateRemoteJSON,
222 transcodeVideofile,
223 removeFromBlacklist
224 },
225 hooks: { 228 hooks: {
226 beforeValidate, 229 beforeValidate,
227 beforeCreate, 230 beforeCreate,
@@ -230,99 +233,139 @@ module.exports = function (sequelize, DataTypes) {
230 } 233 }
231 ) 234 )
232 235
236 const classMethods = [
237 associate,
238
239 generateThumbnailFromData,
240 getDurationFromFile,
241 list,
242 listForApi,
243 listOwnedAndPopulateAuthorAndTags,
244 listOwnedByAuthor,
245 load,
246 loadByHostAndRemoteId,
247 loadAndPopulateAuthor,
248 loadAndPopulateAuthorAndPodAndTags,
249 searchAndPopulateAuthorAndPodAndTags
250 ]
251 const instanceMethods = [
252 generateMagnetUri,
253 getVideoFilename,
254 getThumbnailName,
255 getPreviewName,
256 getTorrentName,
257 isOwned,
258 toFormatedJSON,
259 toAddRemoteJSON,
260 toUpdateRemoteJSON,
261 transcodeVideofile,
262 removeFromBlacklist
263 ]
264 addMethodsToModel(Video, classMethods, instanceMethods)
265
233 return Video 266 return Video
234} 267}
235 268
236function beforeValidate (video, options, next) { 269function beforeValidate (video, options) {
237 // Put a fake infoHash if it does not exists yet 270 // Put a fake infoHash if it does not exists yet
238 if (video.isOwned() && !video.infoHash) { 271 if (video.isOwned() && !video.infoHash) {
239 // 40 hexa length 272 // 40 hexa length
240 video.infoHash = '0123456789abcdef0123456789abcdef01234567' 273 video.infoHash = '0123456789abcdef0123456789abcdef01234567'
241 } 274 }
242
243 return next(null)
244} 275}
245 276
246function beforeCreate (video, options, next) { 277function beforeCreate (video, options) {
247 const tasks = [] 278 return new Promise(function (resolve, reject) {
248 279 const tasks = []
249 if (video.isOwned()) {
250 const videoPath = join(CONFIG.STORAGE.VIDEOS_DIR, video.getVideoFilename())
251
252 tasks.push(
253 function createVideoTorrent (callback) {
254 createTorrentFromVideo(video, videoPath, callback)
255 },
256
257 function createVideoThumbnail (callback) {
258 createThumbnail(video, videoPath, callback)
259 },
260 280
261 function createVideoPreview (callback) { 281 if (video.isOwned()) {
262 createPreview(video, videoPath, callback) 282 const videoPath = join(CONFIG.STORAGE.VIDEOS_DIR, video.getVideoFilename())
263 }
264 )
265 283
266 if (CONFIG.TRANSCODING.ENABLED === true) {
267 tasks.push( 284 tasks.push(
268 function createVideoTranscoderJob (callback) { 285 function createVideoTorrent (callback) {
269 const dataInput = { 286 createTorrentFromVideo(video, videoPath, callback)
270 id: video.id 287 },
271 } 288
289 function createVideoThumbnail (callback) {
290 createThumbnail(video, videoPath, callback)
291 },
272 292
273 JobScheduler.Instance.createJob(options.transaction, 'videoTranscoder', dataInput, callback) 293 function createVideoPreview (callback) {
294 createPreview(video, videoPath, callback)
274 } 295 }
275 ) 296 )
276 }
277 297
278 return parallel(tasks, next) 298 if (CONFIG.TRANSCODING.ENABLED === true) {
279 } 299 tasks.push(
300 function createVideoTranscoderJob (callback) {
301 const dataInput = {
302 id: video.id
303 }
280 304
281 return next() 305 JobScheduler.Instance.createJob(options.transaction, 'videoTranscoder', dataInput, callback)
282} 306 }
307 )
308 }
283 309
284function afterDestroy (video, options, next) { 310 return parallel(tasks, function (err) {
285 const tasks = [] 311 if (err) return reject(err)
286 312
287 tasks.push( 313 return resolve()
288 function (callback) { 314 })
289 removeThumbnail(video, callback)
290 } 315 }
291 )
292 316
293 if (video.isOwned()) { 317 return resolve()
318 })
319}
320
321function afterDestroy (video, options) {
322 return new Promise(function (resolve, reject) {
323 const tasks = []
324
294 tasks.push( 325 tasks.push(
295 function removeVideoFile (callback) { 326 function (callback) {
296 removeFile(video, callback) 327 removeThumbnail(video, callback)
297 }, 328 }
329 )
298 330
299 function removeVideoTorrent (callback) { 331 if (video.isOwned()) {
300 removeTorrent(video, callback) 332 tasks.push(
301 }, 333 function removeVideoFile (callback) {
334 removeFile(video, callback)
335 },
302 336
303 function removeVideoPreview (callback) { 337 function removeVideoTorrent (callback) {
304 removePreview(video, callback) 338 removeTorrent(video, callback)
305 }, 339 },
306 340
307 function removeVideoToFriends (callback) { 341 function removeVideoPreview (callback) {
308 const params = { 342 removePreview(video, callback)
309 remoteId: video.id 343 },
310 }
311 344
312 removeVideoToFriends(params) 345 function notifyFriends (callback) {
346 const params = {
347 remoteId: video.id
348 }
313 349
314 return callback() 350 removeVideoToFriends(params)
315 }
316 )
317 }
318 351
319 parallel(tasks, next) 352 return callback()
353 }
354 )
355 }
356
357 parallel(tasks, function (err) {
358 if (err) return reject(err)
359
360 return resolve()
361 })
362 })
320} 363}
321 364
322// ------------------------------ METHODS ------------------------------ 365// ------------------------------ METHODS ------------------------------
323 366
324function associate (models) { 367function associate (models) {
325 this.belongsTo(models.Author, { 368 Video.belongsTo(models.Author, {
326 foreignKey: { 369 foreignKey: {
327 name: 'authorId', 370 name: 'authorId',
328 allowNull: false 371 allowNull: false
@@ -330,13 +373,13 @@ function associate (models) {
330 onDelete: 'cascade' 373 onDelete: 'cascade'
331 }) 374 })
332 375
333 this.belongsToMany(models.Tag, { 376 Video.belongsToMany(models.Tag, {
334 foreignKey: 'videoId', 377 foreignKey: 'videoId',
335 through: models.VideoTag, 378 through: models.VideoTag,
336 onDelete: 'cascade' 379 onDelete: 'cascade'
337 }) 380 })
338 381
339 this.hasMany(models.VideoAbuse, { 382 Video.hasMany(models.VideoAbuse, {
340 foreignKey: { 383 foreignKey: {
341 name: 'videoId', 384 name: 'videoId',
342 allowNull: false 385 allowNull: false
@@ -345,7 +388,7 @@ function associate (models) {
345 }) 388 })
346} 389}
347 390
348function generateMagnetUri () { 391generateMagnetUri = function () {
349 let baseUrlHttp 392 let baseUrlHttp
350 let baseUrlWs 393 let baseUrlWs
351 394
@@ -372,18 +415,18 @@ function generateMagnetUri () {
372 return magnetUtil.encode(magnetHash) 415 return magnetUtil.encode(magnetHash)
373} 416}
374 417
375function getVideoFilename () { 418getVideoFilename = function () {
376 if (this.isOwned()) return this.id + this.extname 419 if (this.isOwned()) return this.id + this.extname
377 420
378 return this.remoteId + this.extname 421 return this.remoteId + this.extname
379} 422}
380 423
381function getThumbnailName () { 424getThumbnailName = function () {
382 // We always have a copy of the thumbnail 425 // We always have a copy of the thumbnail
383 return this.id + '.jpg' 426 return this.id + '.jpg'
384} 427}
385 428
386function getPreviewName () { 429getPreviewName = function () {
387 const extension = '.jpg' 430 const extension = '.jpg'
388 431
389 if (this.isOwned()) return this.id + extension 432 if (this.isOwned()) return this.id + extension
@@ -391,7 +434,7 @@ function getPreviewName () {
391 return this.remoteId + extension 434 return this.remoteId + extension
392} 435}
393 436
394function getTorrentName () { 437getTorrentName = function () {
395 const extension = '.torrent' 438 const extension = '.torrent'
396 439
397 if (this.isOwned()) return this.id + extension 440 if (this.isOwned()) return this.id + extension
@@ -399,11 +442,11 @@ function getTorrentName () {
399 return this.remoteId + extension 442 return this.remoteId + extension
400} 443}
401 444
402function isOwned () { 445isOwned = function () {
403 return this.remoteId === null 446 return this.remoteId === null
404} 447}
405 448
406function toFormatedJSON () { 449toFormatedJSON = function () {
407 let podHost 450 let podHost
408 451
409 if (this.Author.Pod) { 452 if (this.Author.Pod) {
@@ -453,43 +496,41 @@ function toFormatedJSON () {
453 return json 496 return json
454} 497}
455 498
456function toAddRemoteJSON (callback) { 499toAddRemoteJSON = function (callback) {
457 const self = this
458
459 // Get thumbnail data to send to the other pod 500 // Get thumbnail data to send to the other pod
460 const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, this.getThumbnailName()) 501 const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, this.getThumbnailName())
461 fs.readFile(thumbnailPath, function (err, thumbnailData) { 502 fs.readFile(thumbnailPath, (err, thumbnailData) => {
462 if (err) { 503 if (err) {
463 logger.error('Cannot read the thumbnail of the video') 504 logger.error('Cannot read the thumbnail of the video')
464 return callback(err) 505 return callback(err)
465 } 506 }
466 507
467 const remoteVideo = { 508 const remoteVideo = {
468 name: self.name, 509 name: this.name,
469 category: self.category, 510 category: this.category,
470 licence: self.licence, 511 licence: this.licence,
471 language: self.language, 512 language: this.language,
472 nsfw: self.nsfw, 513 nsfw: this.nsfw,
473 description: self.description, 514 description: this.description,
474 infoHash: self.infoHash, 515 infoHash: this.infoHash,
475 remoteId: self.id, 516 remoteId: this.id,
476 author: self.Author.name, 517 author: this.Author.name,
477 duration: self.duration, 518 duration: this.duration,
478 thumbnailData: thumbnailData.toString('binary'), 519 thumbnailData: thumbnailData.toString('binary'),
479 tags: map(self.Tags, 'name'), 520 tags: map(this.Tags, 'name'),
480 createdAt: self.createdAt, 521 createdAt: this.createdAt,
481 updatedAt: self.updatedAt, 522 updatedAt: this.updatedAt,
482 extname: self.extname, 523 extname: this.extname,
483 views: self.views, 524 views: this.views,
484 likes: self.likes, 525 likes: this.likes,
485 dislikes: self.dislikes 526 dislikes: this.dislikes
486 } 527 }
487 528
488 return callback(null, remoteVideo) 529 return callback(null, remoteVideo)
489 }) 530 })
490} 531}
491 532
492function toUpdateRemoteJSON (callback) { 533toUpdateRemoteJSON = function (callback) {
493 const json = { 534 const json = {
494 name: this.name, 535 name: this.name,
495 category: this.category, 536 category: this.category,
@@ -513,7 +554,7 @@ function toUpdateRemoteJSON (callback) {
513 return json 554 return json
514} 555}
515 556
516function transcodeVideofile (finalCallback) { 557transcodeVideofile = function (finalCallback) {
517 const video = this 558 const video = this
518 559
519 const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR 560 const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR
@@ -568,7 +609,7 @@ function transcodeVideofile (finalCallback) {
568 609
569// ------------------------------ STATICS ------------------------------ 610// ------------------------------ STATICS ------------------------------
570 611
571function generateThumbnailFromData (video, thumbnailData, callback) { 612generateThumbnailFromData = function (video, thumbnailData, callback) {
572 // Creating the thumbnail for a remote video 613 // Creating the thumbnail for a remote video
573 614
574 const thumbnailName = video.getThumbnailName() 615 const thumbnailName = video.getThumbnailName()
@@ -580,7 +621,7 @@ function generateThumbnailFromData (video, thumbnailData, callback) {
580 }) 621 })
581} 622}
582 623
583function getDurationFromFile (videoPath, callback) { 624getDurationFromFile = function (videoPath, callback) {
584 ffmpeg.ffprobe(videoPath, function (err, metadata) { 625 ffmpeg.ffprobe(videoPath, function (err, metadata) {
585 if (err) return callback(err) 626 if (err) return callback(err)
586 627
@@ -588,46 +629,46 @@ function getDurationFromFile (videoPath, callback) {
588 }) 629 })
589} 630}
590 631
591function list (callback) { 632list = function (callback) {
592 return this.findAll().asCallback(callback) 633 return Video.findAll().asCallback(callback)
593} 634}
594 635
595function listForApi (start, count, sort, callback) { 636listForApi = function (start, count, sort, callback) {
596 // Exclude Blakclisted videos from the list 637 // Exclude Blakclisted videos from the list
597 const query = { 638 const query = {
639 distinct: true,
598 offset: start, 640 offset: start,
599 limit: count, 641 limit: count,
600 distinct: true, // For the count, a video can have many tags 642 order: [ getSort(sort), [ Video['sequelize'].models.Tag, 'name', 'ASC' ] ],
601 order: [ getSort(sort), [ this.sequelize.models.Tag, 'name', 'ASC' ] ],
602 include: [ 643 include: [
603 { 644 {
604 model: this.sequelize.models.Author, 645 model: Video['sequelize'].models.Author,
605 include: [ { model: this.sequelize.models.Pod, required: false } ] 646 include: [ { model: Video['sequelize'].models.Pod, required: false } ]
606 }, 647 },
607 648
608 this.sequelize.models.Tag 649 Video['sequelize'].models.Tag
609 ], 650 ],
610 where: createBaseVideosWhere.call(this) 651 where: createBaseVideosWhere()
611 } 652 }
612 653
613 return this.findAndCountAll(query).asCallback(function (err, result) { 654 return Video.findAndCountAll(query).asCallback(function (err, result) {
614 if (err) return callback(err) 655 if (err) return callback(err)
615 656
616 return callback(null, result.rows, result.count) 657 return callback(null, result.rows, result.count)
617 }) 658 })
618} 659}
619 660
620function loadByHostAndRemoteId (fromHost, remoteId, callback) { 661loadByHostAndRemoteId = function (fromHost, remoteId, callback) {
621 const query = { 662 const query = {
622 where: { 663 where: {
623 remoteId: remoteId 664 remoteId: remoteId
624 }, 665 },
625 include: [ 666 include: [
626 { 667 {
627 model: this.sequelize.models.Author, 668 model: Video['sequelize'].models.Author,
628 include: [ 669 include: [
629 { 670 {
630 model: this.sequelize.models.Pod, 671 model: Video['sequelize'].models.Pod,
631 required: true, 672 required: true,
632 where: { 673 where: {
633 host: fromHost 674 host: fromHost
@@ -638,29 +679,29 @@ function loadByHostAndRemoteId (fromHost, remoteId, callback) {
638 ] 679 ]
639 } 680 }
640 681
641 return this.findOne(query).asCallback(callback) 682 return Video.findOne(query).asCallback(callback)
642} 683}
643 684
644function listOwnedAndPopulateAuthorAndTags (callback) { 685listOwnedAndPopulateAuthorAndTags = function (callback) {
645 // If remoteId is null this is *our* video 686 // If remoteId is null this is *our* video
646 const query = { 687 const query = {
647 where: { 688 where: {
648 remoteId: null 689 remoteId: null
649 }, 690 },
650 include: [ this.sequelize.models.Author, this.sequelize.models.Tag ] 691 include: [ Video['sequelize'].models.Author, Video['sequelize'].models.Tag ]
651 } 692 }
652 693
653 return this.findAll(query).asCallback(callback) 694 return Video.findAll(query).asCallback(callback)
654} 695}
655 696
656function listOwnedByAuthor (author, callback) { 697listOwnedByAuthor = function (author, callback) {
657 const query = { 698 const query = {
658 where: { 699 where: {
659 remoteId: null 700 remoteId: null
660 }, 701 },
661 include: [ 702 include: [
662 { 703 {
663 model: this.sequelize.models.Author, 704 model: Video['sequelize'].models.Author,
664 where: { 705 where: {
665 name: author 706 name: author
666 } 707 }
@@ -668,58 +709,58 @@ function listOwnedByAuthor (author, callback) {
668 ] 709 ]
669 } 710 }
670 711
671 return this.findAll(query).asCallback(callback) 712 return Video.findAll(query).asCallback(callback)
672} 713}
673 714
674function load (id, callback) { 715load = function (id, callback) {
675 return this.findById(id).asCallback(callback) 716 return Video.findById(id).asCallback(callback)
676} 717}
677 718
678function loadAndPopulateAuthor (id, callback) { 719loadAndPopulateAuthor = function (id, callback) {
679 const options = { 720 const options = {
680 include: [ this.sequelize.models.Author ] 721 include: [ Video['sequelize'].models.Author ]
681 } 722 }
682 723
683 return this.findById(id, options).asCallback(callback) 724 return Video.findById(id, options).asCallback(callback)
684} 725}
685 726
686function loadAndPopulateAuthorAndPodAndTags (id, callback) { 727loadAndPopulateAuthorAndPodAndTags = function (id, callback) {
687 const options = { 728 const options = {
688 include: [ 729 include: [
689 { 730 {
690 model: this.sequelize.models.Author, 731 model: Video['sequelize'].models.Author,
691 include: [ { model: this.sequelize.models.Pod, required: false } ] 732 include: [ { model: Video['sequelize'].models.Pod, required: false } ]
692 }, 733 },
693 this.sequelize.models.Tag 734 Video['sequelize'].models.Tag
694 ] 735 ]
695 } 736 }
696 737
697 return this.findById(id, options).asCallback(callback) 738 return Video.findById(id, options).asCallback(callback)
698} 739}
699 740
700function searchAndPopulateAuthorAndPodAndTags (value, field, start, count, sort, callback) { 741searchAndPopulateAuthorAndPodAndTags = function (value, field, start, count, sort, callback) {
701 const podInclude: any = { 742 const podInclude: any = {
702 model: this.sequelize.models.Pod, 743 model: Video['sequelize'].models.Pod,
703 required: false 744 required: false
704 } 745 }
705 746
706 const authorInclude: any = { 747 const authorInclude: any = {
707 model: this.sequelize.models.Author, 748 model: Video['sequelize'].models.Author,
708 include: [ 749 include: [
709 podInclude 750 podInclude
710 ] 751 ]
711 } 752 }
712 753
713 const tagInclude: any = { 754 const tagInclude: any = {
714 model: this.sequelize.models.Tag 755 model: Video['sequelize'].models.Tag
715 } 756 }
716 757
717 const query: any = { 758 const query: any = {
718 where: createBaseVideosWhere.call(this), 759 distinct: true,
760 where: createBaseVideosWhere(),
719 offset: start, 761 offset: start,
720 limit: count, 762 limit: count,
721 distinct: true, // For the count, a video can have many tags 763 order: [ getSort(sort), [ Video['sequelize'].models.Tag, 'name', 'ASC' ] ]
722 order: [ getSort(sort), [ this.sequelize.models.Tag, 'name', 'ASC' ] ]
723 } 764 }
724 765
725 // Make an exact search with the magnet 766 // Make an exact search with the magnet
@@ -727,8 +768,8 @@ function searchAndPopulateAuthorAndPodAndTags (value, field, start, count, sort,
727 const infoHash = magnetUtil.decode(value).infoHash 768 const infoHash = magnetUtil.decode(value).infoHash
728 query.where.infoHash = infoHash 769 query.where.infoHash = infoHash
729 } else if (field === 'tags') { 770 } else if (field === 'tags') {
730 const escapedValue = this.sequelize.escape('%' + value + '%') 771 const escapedValue = Video['sequelize'].escape('%' + value + '%')
731 query.where.id.$in = this.sequelize.literal( 772 query.where.id.$in = Video['sequelize'].literal(
732 '(SELECT "VideoTags"."videoId" FROM "Tags" INNER JOIN "VideoTags" ON "Tags"."id" = "VideoTags"."tagId" WHERE name LIKE ' + escapedValue + ')' 773 '(SELECT "VideoTags"."videoId" FROM "Tags" INNER JOIN "VideoTags" ON "Tags"."id" = "VideoTags"."tagId" WHERE name LIKE ' + escapedValue + ')'
733 ) 774 )
734 } else if (field === 'host') { 775 } else if (field === 'host') {
@@ -758,10 +799,10 @@ function searchAndPopulateAuthorAndPodAndTags (value, field, start, count, sort,
758 ] 799 ]
759 800
760 if (tagInclude.where) { 801 if (tagInclude.where) {
761 // query.include.push([ this.sequelize.models.Tag ]) 802 // query.include.push([ Video['sequelize'].models.Tag ])
762 } 803 }
763 804
764 return this.findAndCountAll(query).asCallback(function (err, result) { 805 return Video.findAndCountAll(query).asCallback(function (err, result) {
765 if (err) return callback(err) 806 if (err) return callback(err)
766 807
767 return callback(null, result.rows, result.count) 808 return callback(null, result.rows, result.count)
@@ -773,7 +814,7 @@ function searchAndPopulateAuthorAndPodAndTags (value, field, start, count, sort,
773function createBaseVideosWhere () { 814function createBaseVideosWhere () {
774 return { 815 return {
775 id: { 816 id: {
776 $notIn: this.sequelize.literal( 817 $notIn: Video['sequelize'].literal(
777 '(SELECT "BlacklistedVideos"."videoId" FROM "BlacklistedVideos")' 818 '(SELECT "BlacklistedVideos"."videoId" FROM "BlacklistedVideos")'
778 ) 819 )
779 } 820 }