diff options
author | Chocobozzz <florian.bigard@gmail.com> | 2017-11-09 17:51:58 +0100 |
---|---|---|
committer | Chocobozzz <florian.bigard@gmail.com> | 2017-11-27 19:40:51 +0100 |
commit | e4f97babf701481b55cc10fb3448feab5f97c867 (patch) | |
tree | af37402a594dc5ff09f71ecb0687e8cfe4cdb471 /server/models/video | |
parent | 343ad675f2a26c15b86150a9a3552e619d5d44f4 (diff) | |
download | PeerTube-e4f97babf701481b55cc10fb3448feab5f97c867.tar.gz PeerTube-e4f97babf701481b55cc10fb3448feab5f97c867.tar.zst PeerTube-e4f97babf701481b55cc10fb3448feab5f97c867.zip |
Begin activitypub
Diffstat (limited to 'server/models/video')
-rw-r--r-- | server/models/video/author-interface.ts | 45 | ||||
-rw-r--r-- | server/models/video/author.ts | 171 | ||||
-rw-r--r-- | server/models/video/video-channel-interface.ts | 38 | ||||
-rw-r--r-- | server/models/video/video-channel.ts | 100 | ||||
-rw-r--r-- | server/models/video/video-interface.ts | 60 | ||||
-rw-r--r-- | server/models/video/video.ts | 378 |
6 files changed, 300 insertions, 492 deletions
diff --git a/server/models/video/author-interface.ts b/server/models/video/author-interface.ts deleted file mode 100644 index fc69ff3c2..000000000 --- a/server/models/video/author-interface.ts +++ /dev/null | |||
@@ -1,45 +0,0 @@ | |||
1 | import * as Sequelize from 'sequelize' | ||
2 | import * as Promise from 'bluebird' | ||
3 | |||
4 | import { PodInstance } from '../pod/pod-interface' | ||
5 | import { RemoteVideoAuthorCreateData } from '../../../shared/models/pods/remote-video/remote-video-author-create-request.model' | ||
6 | import { VideoChannelInstance } from './video-channel-interface' | ||
7 | |||
8 | export namespace AuthorMethods { | ||
9 | export type Load = (id: number) => Promise<AuthorInstance> | ||
10 | export type LoadByUUID = (uuid: string) => Promise<AuthorInstance> | ||
11 | export type LoadAuthorByPodAndUUID = (uuid: string, podId: number, transaction: Sequelize.Transaction) => Promise<AuthorInstance> | ||
12 | export type ListOwned = () => Promise<AuthorInstance[]> | ||
13 | |||
14 | export type ToAddRemoteJSON = (this: AuthorInstance) => RemoteVideoAuthorCreateData | ||
15 | export type IsOwned = (this: AuthorInstance) => boolean | ||
16 | } | ||
17 | |||
18 | export interface AuthorClass { | ||
19 | loadAuthorByPodAndUUID: AuthorMethods.LoadAuthorByPodAndUUID | ||
20 | load: AuthorMethods.Load | ||
21 | loadByUUID: AuthorMethods.LoadByUUID | ||
22 | listOwned: AuthorMethods.ListOwned | ||
23 | } | ||
24 | |||
25 | export interface AuthorAttributes { | ||
26 | name: string | ||
27 | uuid?: string | ||
28 | |||
29 | podId?: number | ||
30 | userId?: number | ||
31 | } | ||
32 | |||
33 | export interface AuthorInstance extends AuthorClass, AuthorAttributes, Sequelize.Instance<AuthorAttributes> { | ||
34 | isOwned: AuthorMethods.IsOwned | ||
35 | toAddRemoteJSON: AuthorMethods.ToAddRemoteJSON | ||
36 | |||
37 | id: number | ||
38 | createdAt: Date | ||
39 | updatedAt: Date | ||
40 | |||
41 | Pod: PodInstance | ||
42 | VideoChannels: VideoChannelInstance[] | ||
43 | } | ||
44 | |||
45 | export interface AuthorModel extends AuthorClass, Sequelize.Model<AuthorInstance, AuthorAttributes> {} | ||
diff --git a/server/models/video/author.ts b/server/models/video/author.ts deleted file mode 100644 index 43f84c3ea..000000000 --- a/server/models/video/author.ts +++ /dev/null | |||
@@ -1,171 +0,0 @@ | |||
1 | import * as Sequelize from 'sequelize' | ||
2 | |||
3 | import { isUserUsernameValid } from '../../helpers' | ||
4 | import { removeVideoAuthorToFriends } from '../../lib' | ||
5 | |||
6 | import { addMethodsToModel } from '../utils' | ||
7 | import { | ||
8 | AuthorInstance, | ||
9 | AuthorAttributes, | ||
10 | |||
11 | AuthorMethods | ||
12 | } from './author-interface' | ||
13 | |||
14 | let Author: Sequelize.Model<AuthorInstance, AuthorAttributes> | ||
15 | let loadAuthorByPodAndUUID: AuthorMethods.LoadAuthorByPodAndUUID | ||
16 | let load: AuthorMethods.Load | ||
17 | let loadByUUID: AuthorMethods.LoadByUUID | ||
18 | let listOwned: AuthorMethods.ListOwned | ||
19 | let isOwned: AuthorMethods.IsOwned | ||
20 | let toAddRemoteJSON: AuthorMethods.ToAddRemoteJSON | ||
21 | |||
22 | export default function defineAuthor (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) { | ||
23 | Author = sequelize.define<AuthorInstance, AuthorAttributes>('Author', | ||
24 | { | ||
25 | uuid: { | ||
26 | type: DataTypes.UUID, | ||
27 | defaultValue: DataTypes.UUIDV4, | ||
28 | allowNull: false, | ||
29 | validate: { | ||
30 | isUUID: 4 | ||
31 | } | ||
32 | }, | ||
33 | name: { | ||
34 | type: DataTypes.STRING, | ||
35 | allowNull: false, | ||
36 | validate: { | ||
37 | usernameValid: value => { | ||
38 | const res = isUserUsernameValid(value) | ||
39 | if (res === false) throw new Error('Username is not valid.') | ||
40 | } | ||
41 | } | ||
42 | } | ||
43 | }, | ||
44 | { | ||
45 | indexes: [ | ||
46 | { | ||
47 | fields: [ 'name' ] | ||
48 | }, | ||
49 | { | ||
50 | fields: [ 'podId' ] | ||
51 | }, | ||
52 | { | ||
53 | fields: [ 'userId' ], | ||
54 | unique: true | ||
55 | }, | ||
56 | { | ||
57 | fields: [ 'name', 'podId' ], | ||
58 | unique: true | ||
59 | } | ||
60 | ], | ||
61 | hooks: { afterDestroy } | ||
62 | } | ||
63 | ) | ||
64 | |||
65 | const classMethods = [ | ||
66 | associate, | ||
67 | loadAuthorByPodAndUUID, | ||
68 | load, | ||
69 | loadByUUID, | ||
70 | listOwned | ||
71 | ] | ||
72 | const instanceMethods = [ | ||
73 | isOwned, | ||
74 | toAddRemoteJSON | ||
75 | ] | ||
76 | addMethodsToModel(Author, classMethods, instanceMethods) | ||
77 | |||
78 | return Author | ||
79 | } | ||
80 | |||
81 | // --------------------------------------------------------------------------- | ||
82 | |||
83 | function associate (models) { | ||
84 | Author.belongsTo(models.Pod, { | ||
85 | foreignKey: { | ||
86 | name: 'podId', | ||
87 | allowNull: true | ||
88 | }, | ||
89 | onDelete: 'cascade' | ||
90 | }) | ||
91 | |||
92 | Author.belongsTo(models.User, { | ||
93 | foreignKey: { | ||
94 | name: 'userId', | ||
95 | allowNull: true | ||
96 | }, | ||
97 | onDelete: 'cascade' | ||
98 | }) | ||
99 | |||
100 | Author.hasMany(models.VideoChannel, { | ||
101 | foreignKey: { | ||
102 | name: 'authorId', | ||
103 | allowNull: false | ||
104 | }, | ||
105 | onDelete: 'cascade', | ||
106 | hooks: true | ||
107 | }) | ||
108 | } | ||
109 | |||
110 | function afterDestroy (author: AuthorInstance) { | ||
111 | if (author.isOwned()) { | ||
112 | const removeVideoAuthorToFriendsParams = { | ||
113 | uuid: author.uuid | ||
114 | } | ||
115 | |||
116 | return removeVideoAuthorToFriends(removeVideoAuthorToFriendsParams) | ||
117 | } | ||
118 | |||
119 | return undefined | ||
120 | } | ||
121 | |||
122 | toAddRemoteJSON = function (this: AuthorInstance) { | ||
123 | const json = { | ||
124 | uuid: this.uuid, | ||
125 | name: this.name | ||
126 | } | ||
127 | |||
128 | return json | ||
129 | } | ||
130 | |||
131 | isOwned = function (this: AuthorInstance) { | ||
132 | return this.podId === null | ||
133 | } | ||
134 | |||
135 | // ------------------------------ STATICS ------------------------------ | ||
136 | |||
137 | listOwned = function () { | ||
138 | const query: Sequelize.FindOptions<AuthorAttributes> = { | ||
139 | where: { | ||
140 | podId: null | ||
141 | } | ||
142 | } | ||
143 | |||
144 | return Author.findAll(query) | ||
145 | } | ||
146 | |||
147 | load = function (id: number) { | ||
148 | return Author.findById(id) | ||
149 | } | ||
150 | |||
151 | loadByUUID = function (uuid: string) { | ||
152 | const query: Sequelize.FindOptions<AuthorAttributes> = { | ||
153 | where: { | ||
154 | uuid | ||
155 | } | ||
156 | } | ||
157 | |||
158 | return Author.findOne(query) | ||
159 | } | ||
160 | |||
161 | loadAuthorByPodAndUUID = function (uuid: string, podId: number, transaction: Sequelize.Transaction) { | ||
162 | const query: Sequelize.FindOptions<AuthorAttributes> = { | ||
163 | where: { | ||
164 | podId, | ||
165 | uuid | ||
166 | }, | ||
167 | transaction | ||
168 | } | ||
169 | |||
170 | return Author.find(query) | ||
171 | } | ||
diff --git a/server/models/video/video-channel-interface.ts b/server/models/video/video-channel-interface.ts index b8d3e0f42..477f97cd4 100644 --- a/server/models/video/video-channel-interface.ts +++ b/server/models/video/video-channel-interface.ts | |||
@@ -1,42 +1,42 @@ | |||
1 | import * as Sequelize from 'sequelize' | 1 | import * as Sequelize from 'sequelize' |
2 | import * as Promise from 'bluebird' | 2 | import * as Promise from 'bluebird' |
3 | 3 | ||
4 | import { ResultList, RemoteVideoChannelCreateData, RemoteVideoChannelUpdateData } from '../../../shared' | 4 | import { ResultList } from '../../../shared' |
5 | 5 | ||
6 | // Don't use barrel, import just what we need | 6 | // Don't use barrel, import just what we need |
7 | import { VideoChannel as FormattedVideoChannel } from '../../../shared/models/videos/video-channel.model' | 7 | import { VideoChannel as FormattedVideoChannel } from '../../../shared/models/videos/video-channel.model' |
8 | import { AuthorInstance } from './author-interface' | ||
9 | import { VideoInstance } from './video-interface' | 8 | import { VideoInstance } from './video-interface' |
9 | import { AccountInstance } from '../account/account-interface' | ||
10 | import { VideoChannelObject } from '../../../shared/models/activitypub/objects/video-channel-object' | ||
10 | 11 | ||
11 | export namespace VideoChannelMethods { | 12 | export namespace VideoChannelMethods { |
12 | export type ToFormattedJSON = (this: VideoChannelInstance) => FormattedVideoChannel | 13 | export type ToFormattedJSON = (this: VideoChannelInstance) => FormattedVideoChannel |
13 | export type ToAddRemoteJSON = (this: VideoChannelInstance) => RemoteVideoChannelCreateData | 14 | export type ToActivityPubObject = (this: VideoChannelInstance) => VideoChannelObject |
14 | export type ToUpdateRemoteJSON = (this: VideoChannelInstance) => RemoteVideoChannelUpdateData | ||
15 | export type IsOwned = (this: VideoChannelInstance) => boolean | 15 | export type IsOwned = (this: VideoChannelInstance) => boolean |
16 | 16 | ||
17 | export type CountByAuthor = (authorId: number) => Promise<number> | 17 | export type CountByAccount = (accountId: number) => Promise<number> |
18 | export type ListOwned = () => Promise<VideoChannelInstance[]> | 18 | export type ListOwned = () => Promise<VideoChannelInstance[]> |
19 | export type ListForApi = (start: number, count: number, sort: string) => Promise< ResultList<VideoChannelInstance> > | 19 | export type ListForApi = (start: number, count: number, sort: string) => Promise< ResultList<VideoChannelInstance> > |
20 | export type LoadByIdAndAuthor = (id: number, authorId: number) => Promise<VideoChannelInstance> | 20 | export type LoadByIdAndAccount = (id: number, accountId: number) => Promise<VideoChannelInstance> |
21 | export type ListByAuthor = (authorId: number) => Promise< ResultList<VideoChannelInstance> > | 21 | export type ListByAccount = (accountId: number) => Promise< ResultList<VideoChannelInstance> > |
22 | export type LoadAndPopulateAuthor = (id: number) => Promise<VideoChannelInstance> | 22 | export type LoadAndPopulateAccount = (id: number) => Promise<VideoChannelInstance> |
23 | export type LoadByUUIDAndPopulateAuthor = (uuid: string) => Promise<VideoChannelInstance> | 23 | export type LoadByUUIDAndPopulateAccount = (uuid: string) => Promise<VideoChannelInstance> |
24 | export type LoadByUUID = (uuid: string, t?: Sequelize.Transaction) => Promise<VideoChannelInstance> | 24 | export type LoadByUUID = (uuid: string, t?: Sequelize.Transaction) => Promise<VideoChannelInstance> |
25 | export type LoadByHostAndUUID = (uuid: string, podHost: string, t?: Sequelize.Transaction) => Promise<VideoChannelInstance> | 25 | export type LoadByHostAndUUID = (uuid: string, podHost: string, t?: Sequelize.Transaction) => Promise<VideoChannelInstance> |
26 | export type LoadAndPopulateAuthorAndVideos = (id: number) => Promise<VideoChannelInstance> | 26 | export type LoadAndPopulateAccountAndVideos = (id: number) => Promise<VideoChannelInstance> |
27 | } | 27 | } |
28 | 28 | ||
29 | export interface VideoChannelClass { | 29 | export interface VideoChannelClass { |
30 | countByAuthor: VideoChannelMethods.CountByAuthor | 30 | countByAccount: VideoChannelMethods.CountByAccount |
31 | listForApi: VideoChannelMethods.ListForApi | 31 | listForApi: VideoChannelMethods.ListForApi |
32 | listByAuthor: VideoChannelMethods.ListByAuthor | 32 | listByAccount: VideoChannelMethods.ListByAccount |
33 | listOwned: VideoChannelMethods.ListOwned | 33 | listOwned: VideoChannelMethods.ListOwned |
34 | loadByIdAndAuthor: VideoChannelMethods.LoadByIdAndAuthor | 34 | loadByIdAndAccount: VideoChannelMethods.LoadByIdAndAccount |
35 | loadByUUID: VideoChannelMethods.LoadByUUID | 35 | loadByUUID: VideoChannelMethods.LoadByUUID |
36 | loadByHostAndUUID: VideoChannelMethods.LoadByHostAndUUID | 36 | loadByHostAndUUID: VideoChannelMethods.LoadByHostAndUUID |
37 | loadAndPopulateAuthor: VideoChannelMethods.LoadAndPopulateAuthor | 37 | loadAndPopulateAccount: VideoChannelMethods.LoadAndPopulateAccount |
38 | loadByUUIDAndPopulateAuthor: VideoChannelMethods.LoadByUUIDAndPopulateAuthor | 38 | loadByUUIDAndPopulateAccount: VideoChannelMethods.LoadByUUIDAndPopulateAccount |
39 | loadAndPopulateAuthorAndVideos: VideoChannelMethods.LoadAndPopulateAuthorAndVideos | 39 | loadAndPopulateAccountAndVideos: VideoChannelMethods.LoadAndPopulateAccountAndVideos |
40 | } | 40 | } |
41 | 41 | ||
42 | export interface VideoChannelAttributes { | 42 | export interface VideoChannelAttributes { |
@@ -45,8 +45,9 @@ export interface VideoChannelAttributes { | |||
45 | name: string | 45 | name: string |
46 | description: string | 46 | description: string |
47 | remote: boolean | 47 | remote: boolean |
48 | url: string | ||
48 | 49 | ||
49 | Author?: AuthorInstance | 50 | Account?: AccountInstance |
50 | Videos?: VideoInstance[] | 51 | Videos?: VideoInstance[] |
51 | } | 52 | } |
52 | 53 | ||
@@ -57,8 +58,7 @@ export interface VideoChannelInstance extends VideoChannelClass, VideoChannelAtt | |||
57 | 58 | ||
58 | isOwned: VideoChannelMethods.IsOwned | 59 | isOwned: VideoChannelMethods.IsOwned |
59 | toFormattedJSON: VideoChannelMethods.ToFormattedJSON | 60 | toFormattedJSON: VideoChannelMethods.ToFormattedJSON |
60 | toAddRemoteJSON: VideoChannelMethods.ToAddRemoteJSON | 61 | toActivityPubObject: VideoChannelMethods.ToActivityPubObject |
61 | toUpdateRemoteJSON: VideoChannelMethods.ToUpdateRemoteJSON | ||
62 | } | 62 | } |
63 | 63 | ||
64 | export interface VideoChannelModel extends VideoChannelClass, Sequelize.Model<VideoChannelInstance, VideoChannelAttributes> {} | 64 | export interface VideoChannelModel extends VideoChannelClass, Sequelize.Model<VideoChannelInstance, VideoChannelAttributes> {} |
diff --git a/server/models/video/video-channel.ts b/server/models/video/video-channel.ts index 46c2db63f..c17828f3e 100644 --- a/server/models/video/video-channel.ts +++ b/server/models/video/video-channel.ts | |||
@@ -13,19 +13,18 @@ import { | |||
13 | 13 | ||
14 | let VideoChannel: Sequelize.Model<VideoChannelInstance, VideoChannelAttributes> | 14 | let VideoChannel: Sequelize.Model<VideoChannelInstance, VideoChannelAttributes> |
15 | let toFormattedJSON: VideoChannelMethods.ToFormattedJSON | 15 | let toFormattedJSON: VideoChannelMethods.ToFormattedJSON |
16 | let toAddRemoteJSON: VideoChannelMethods.ToAddRemoteJSON | 16 | let toActivityPubObject: VideoChannelMethods.ToActivityPubObject |
17 | let toUpdateRemoteJSON: VideoChannelMethods.ToUpdateRemoteJSON | ||
18 | let isOwned: VideoChannelMethods.IsOwned | 17 | let isOwned: VideoChannelMethods.IsOwned |
19 | let countByAuthor: VideoChannelMethods.CountByAuthor | 18 | let countByAccount: VideoChannelMethods.CountByAccount |
20 | let listOwned: VideoChannelMethods.ListOwned | 19 | let listOwned: VideoChannelMethods.ListOwned |
21 | let listForApi: VideoChannelMethods.ListForApi | 20 | let listForApi: VideoChannelMethods.ListForApi |
22 | let listByAuthor: VideoChannelMethods.ListByAuthor | 21 | let listByAccount: VideoChannelMethods.ListByAccount |
23 | let loadByIdAndAuthor: VideoChannelMethods.LoadByIdAndAuthor | 22 | let loadByIdAndAccount: VideoChannelMethods.LoadByIdAndAccount |
24 | let loadByUUID: VideoChannelMethods.LoadByUUID | 23 | let loadByUUID: VideoChannelMethods.LoadByUUID |
25 | let loadAndPopulateAuthor: VideoChannelMethods.LoadAndPopulateAuthor | 24 | let loadAndPopulateAccount: VideoChannelMethods.LoadAndPopulateAccount |
26 | let loadByUUIDAndPopulateAuthor: VideoChannelMethods.LoadByUUIDAndPopulateAuthor | 25 | let loadByUUIDAndPopulateAccount: VideoChannelMethods.LoadByUUIDAndPopulateAccount |
27 | let loadByHostAndUUID: VideoChannelMethods.LoadByHostAndUUID | 26 | let loadByHostAndUUID: VideoChannelMethods.LoadByHostAndUUID |
28 | let loadAndPopulateAuthorAndVideos: VideoChannelMethods.LoadAndPopulateAuthorAndVideos | 27 | let loadAndPopulateAccountAndVideos: VideoChannelMethods.LoadAndPopulateAccountAndVideos |
29 | 28 | ||
30 | export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) { | 29 | export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) { |
31 | VideoChannel = sequelize.define<VideoChannelInstance, VideoChannelAttributes>('VideoChannel', | 30 | VideoChannel = sequelize.define<VideoChannelInstance, VideoChannelAttributes>('VideoChannel', |
@@ -62,12 +61,19 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da | |||
62 | type: DataTypes.BOOLEAN, | 61 | type: DataTypes.BOOLEAN, |
63 | allowNull: false, | 62 | allowNull: false, |
64 | defaultValue: false | 63 | defaultValue: false |
64 | }, | ||
65 | url: { | ||
66 | type: DataTypes.STRING, | ||
67 | allowNull: false, | ||
68 | validate: { | ||
69 | isUrl: true | ||
70 | } | ||
65 | } | 71 | } |
66 | }, | 72 | }, |
67 | { | 73 | { |
68 | indexes: [ | 74 | indexes: [ |
69 | { | 75 | { |
70 | fields: [ 'authorId' ] | 76 | fields: [ 'accountId' ] |
71 | } | 77 | } |
72 | ], | 78 | ], |
73 | hooks: { | 79 | hooks: { |
@@ -80,21 +86,20 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da | |||
80 | associate, | 86 | associate, |
81 | 87 | ||
82 | listForApi, | 88 | listForApi, |
83 | listByAuthor, | 89 | listByAccount, |
84 | listOwned, | 90 | listOwned, |
85 | loadByIdAndAuthor, | 91 | loadByIdAndAccount, |
86 | loadAndPopulateAuthor, | 92 | loadAndPopulateAccount, |
87 | loadByUUIDAndPopulateAuthor, | 93 | loadByUUIDAndPopulateAccount, |
88 | loadByUUID, | 94 | loadByUUID, |
89 | loadByHostAndUUID, | 95 | loadByHostAndUUID, |
90 | loadAndPopulateAuthorAndVideos, | 96 | loadAndPopulateAccountAndVideos, |
91 | countByAuthor | 97 | countByAccount |
92 | ] | 98 | ] |
93 | const instanceMethods = [ | 99 | const instanceMethods = [ |
94 | isOwned, | 100 | isOwned, |
95 | toFormattedJSON, | 101 | toFormattedJSON, |
96 | toAddRemoteJSON, | 102 | toActivityPubObject, |
97 | toUpdateRemoteJSON | ||
98 | ] | 103 | ] |
99 | addMethodsToModel(VideoChannel, classMethods, instanceMethods) | 104 | addMethodsToModel(VideoChannel, classMethods, instanceMethods) |
100 | 105 | ||
@@ -118,10 +123,10 @@ toFormattedJSON = function (this: VideoChannelInstance) { | |||
118 | updatedAt: this.updatedAt | 123 | updatedAt: this.updatedAt |
119 | } | 124 | } |
120 | 125 | ||
121 | if (this.Author !== undefined) { | 126 | if (this.Account !== undefined) { |
122 | json['owner'] = { | 127 | json['owner'] = { |
123 | name: this.Author.name, | 128 | name: this.Account.name, |
124 | uuid: this.Author.uuid | 129 | uuid: this.Account.uuid |
125 | } | 130 | } |
126 | } | 131 | } |
127 | 132 | ||
@@ -132,27 +137,14 @@ toFormattedJSON = function (this: VideoChannelInstance) { | |||
132 | return json | 137 | return json |
133 | } | 138 | } |
134 | 139 | ||
135 | toAddRemoteJSON = function (this: VideoChannelInstance) { | 140 | toActivityPubObject = function (this: VideoChannelInstance) { |
136 | const json = { | ||
137 | uuid: this.uuid, | ||
138 | name: this.name, | ||
139 | description: this.description, | ||
140 | createdAt: this.createdAt, | ||
141 | updatedAt: this.updatedAt, | ||
142 | ownerUUID: this.Author.uuid | ||
143 | } | ||
144 | |||
145 | return json | ||
146 | } | ||
147 | |||
148 | toUpdateRemoteJSON = function (this: VideoChannelInstance) { | ||
149 | const json = { | 141 | const json = { |
150 | uuid: this.uuid, | 142 | uuid: this.uuid, |
151 | name: this.name, | 143 | name: this.name, |
152 | description: this.description, | 144 | description: this.description, |
153 | createdAt: this.createdAt, | 145 | createdAt: this.createdAt, |
154 | updatedAt: this.updatedAt, | 146 | updatedAt: this.updatedAt, |
155 | ownerUUID: this.Author.uuid | 147 | ownerUUID: this.Account.uuid |
156 | } | 148 | } |
157 | 149 | ||
158 | return json | 150 | return json |
@@ -161,9 +153,9 @@ toUpdateRemoteJSON = function (this: VideoChannelInstance) { | |||
161 | // ------------------------------ STATICS ------------------------------ | 153 | // ------------------------------ STATICS ------------------------------ |
162 | 154 | ||
163 | function associate (models) { | 155 | function associate (models) { |
164 | VideoChannel.belongsTo(models.Author, { | 156 | VideoChannel.belongsTo(models.Account, { |
165 | foreignKey: { | 157 | foreignKey: { |
166 | name: 'authorId', | 158 | name: 'accountId', |
167 | allowNull: false | 159 | allowNull: false |
168 | }, | 160 | }, |
169 | onDelete: 'CASCADE' | 161 | onDelete: 'CASCADE' |
@@ -190,10 +182,10 @@ function afterDestroy (videoChannel: VideoChannelInstance) { | |||
190 | return undefined | 182 | return undefined |
191 | } | 183 | } |
192 | 184 | ||
193 | countByAuthor = function (authorId: number) { | 185 | countByAccount = function (accountId: number) { |
194 | const query = { | 186 | const query = { |
195 | where: { | 187 | where: { |
196 | authorId | 188 | accountId |
197 | } | 189 | } |
198 | } | 190 | } |
199 | 191 | ||
@@ -205,7 +197,7 @@ listOwned = function () { | |||
205 | where: { | 197 | where: { |
206 | remote: false | 198 | remote: false |
207 | }, | 199 | }, |
208 | include: [ VideoChannel['sequelize'].models.Author ] | 200 | include: [ VideoChannel['sequelize'].models.Account ] |
209 | } | 201 | } |
210 | 202 | ||
211 | return VideoChannel.findAll(query) | 203 | return VideoChannel.findAll(query) |
@@ -218,7 +210,7 @@ listForApi = function (start: number, count: number, sort: string) { | |||
218 | order: [ getSort(sort) ], | 210 | order: [ getSort(sort) ], |
219 | include: [ | 211 | include: [ |
220 | { | 212 | { |
221 | model: VideoChannel['sequelize'].models.Author, | 213 | model: VideoChannel['sequelize'].models.Account, |
222 | required: true, | 214 | required: true, |
223 | include: [ { model: VideoChannel['sequelize'].models.Pod, required: false } ] | 215 | include: [ { model: VideoChannel['sequelize'].models.Pod, required: false } ] |
224 | } | 216 | } |
@@ -230,14 +222,14 @@ listForApi = function (start: number, count: number, sort: string) { | |||
230 | }) | 222 | }) |
231 | } | 223 | } |
232 | 224 | ||
233 | listByAuthor = function (authorId: number) { | 225 | listByAccount = function (accountId: number) { |
234 | const query = { | 226 | const query = { |
235 | order: [ getSort('createdAt') ], | 227 | order: [ getSort('createdAt') ], |
236 | include: [ | 228 | include: [ |
237 | { | 229 | { |
238 | model: VideoChannel['sequelize'].models.Author, | 230 | model: VideoChannel['sequelize'].models.Account, |
239 | where: { | 231 | where: { |
240 | id: authorId | 232 | id: accountId |
241 | }, | 233 | }, |
242 | required: true, | 234 | required: true, |
243 | include: [ { model: VideoChannel['sequelize'].models.Pod, required: false } ] | 235 | include: [ { model: VideoChannel['sequelize'].models.Pod, required: false } ] |
@@ -269,7 +261,7 @@ loadByHostAndUUID = function (fromHost: string, uuid: string, t?: Sequelize.Tran | |||
269 | }, | 261 | }, |
270 | include: [ | 262 | include: [ |
271 | { | 263 | { |
272 | model: VideoChannel['sequelize'].models.Author, | 264 | model: VideoChannel['sequelize'].models.Account, |
273 | include: [ | 265 | include: [ |
274 | { | 266 | { |
275 | model: VideoChannel['sequelize'].models.Pod, | 267 | model: VideoChannel['sequelize'].models.Pod, |
@@ -288,15 +280,15 @@ loadByHostAndUUID = function (fromHost: string, uuid: string, t?: Sequelize.Tran | |||
288 | return VideoChannel.findOne(query) | 280 | return VideoChannel.findOne(query) |
289 | } | 281 | } |
290 | 282 | ||
291 | loadByIdAndAuthor = function (id: number, authorId: number) { | 283 | loadByIdAndAccount = function (id: number, accountId: number) { |
292 | const options = { | 284 | const options = { |
293 | where: { | 285 | where: { |
294 | id, | 286 | id, |
295 | authorId | 287 | accountId |
296 | }, | 288 | }, |
297 | include: [ | 289 | include: [ |
298 | { | 290 | { |
299 | model: VideoChannel['sequelize'].models.Author, | 291 | model: VideoChannel['sequelize'].models.Account, |
300 | include: [ { model: VideoChannel['sequelize'].models.Pod, required: false } ] | 292 | include: [ { model: VideoChannel['sequelize'].models.Pod, required: false } ] |
301 | } | 293 | } |
302 | ] | 294 | ] |
@@ -305,11 +297,11 @@ loadByIdAndAuthor = function (id: number, authorId: number) { | |||
305 | return VideoChannel.findOne(options) | 297 | return VideoChannel.findOne(options) |
306 | } | 298 | } |
307 | 299 | ||
308 | loadAndPopulateAuthor = function (id: number) { | 300 | loadAndPopulateAccount = function (id: number) { |
309 | const options = { | 301 | const options = { |
310 | include: [ | 302 | include: [ |
311 | { | 303 | { |
312 | model: VideoChannel['sequelize'].models.Author, | 304 | model: VideoChannel['sequelize'].models.Account, |
313 | include: [ { model: VideoChannel['sequelize'].models.Pod, required: false } ] | 305 | include: [ { model: VideoChannel['sequelize'].models.Pod, required: false } ] |
314 | } | 306 | } |
315 | ] | 307 | ] |
@@ -318,14 +310,14 @@ loadAndPopulateAuthor = function (id: number) { | |||
318 | return VideoChannel.findById(id, options) | 310 | return VideoChannel.findById(id, options) |
319 | } | 311 | } |
320 | 312 | ||
321 | loadByUUIDAndPopulateAuthor = function (uuid: string) { | 313 | loadByUUIDAndPopulateAccount = function (uuid: string) { |
322 | const options = { | 314 | const options = { |
323 | where: { | 315 | where: { |
324 | uuid | 316 | uuid |
325 | }, | 317 | }, |
326 | include: [ | 318 | include: [ |
327 | { | 319 | { |
328 | model: VideoChannel['sequelize'].models.Author, | 320 | model: VideoChannel['sequelize'].models.Account, |
329 | include: [ { model: VideoChannel['sequelize'].models.Pod, required: false } ] | 321 | include: [ { model: VideoChannel['sequelize'].models.Pod, required: false } ] |
330 | } | 322 | } |
331 | ] | 323 | ] |
@@ -334,11 +326,11 @@ loadByUUIDAndPopulateAuthor = function (uuid: string) { | |||
334 | return VideoChannel.findOne(options) | 326 | return VideoChannel.findOne(options) |
335 | } | 327 | } |
336 | 328 | ||
337 | loadAndPopulateAuthorAndVideos = function (id: number) { | 329 | loadAndPopulateAccountAndVideos = function (id: number) { |
338 | const options = { | 330 | const options = { |
339 | include: [ | 331 | include: [ |
340 | { | 332 | { |
341 | model: VideoChannel['sequelize'].models.Author, | 333 | model: VideoChannel['sequelize'].models.Account, |
342 | include: [ { model: VideoChannel['sequelize'].models.Pod, required: false } ] | 334 | include: [ { model: VideoChannel['sequelize'].models.Pod, required: false } ] |
343 | }, | 335 | }, |
344 | VideoChannel['sequelize'].models.Video | 336 | VideoChannel['sequelize'].models.Video |
diff --git a/server/models/video/video-interface.ts b/server/models/video/video-interface.ts index cfe65f9aa..e62e25a82 100644 --- a/server/models/video/video-interface.ts +++ b/server/models/video/video-interface.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import * as Sequelize from 'sequelize' | 1 | import * as Sequelize from 'sequelize' |
2 | import * as Promise from 'bluebird' | 2 | import * as Bluebird from 'bluebird' |
3 | 3 | ||
4 | import { TagAttributes, TagInstance } from './tag-interface' | 4 | import { TagAttributes, TagInstance } from './tag-interface' |
5 | import { VideoFileAttributes, VideoFileInstance } from './video-file-interface' | 5 | import { VideoFileAttributes, VideoFileInstance } from './video-file-interface' |
@@ -13,6 +13,7 @@ import { RemoteVideoUpdateData } from '../../../shared/models/pods/remote-video/ | |||
13 | import { RemoteVideoCreateData } from '../../../shared/models/pods/remote-video/remote-video-create-request.model' | 13 | import { RemoteVideoCreateData } from '../../../shared/models/pods/remote-video/remote-video-create-request.model' |
14 | import { ResultList } from '../../../shared/models/result-list.model' | 14 | import { ResultList } from '../../../shared/models/result-list.model' |
15 | import { VideoChannelInstance } from './video-channel-interface' | 15 | import { VideoChannelInstance } from './video-channel-interface' |
16 | import { VideoTorrentObject } from '../../../shared/models/activitypub/objects/video-torrent-object' | ||
16 | 17 | ||
17 | export namespace VideoMethods { | 18 | export namespace VideoMethods { |
18 | export type GetThumbnailName = (this: VideoInstance) => string | 19 | export type GetThumbnailName = (this: VideoInstance) => string |
@@ -29,8 +30,7 @@ export namespace VideoMethods { | |||
29 | export type GetVideoFilePath = (this: VideoInstance, videoFile: VideoFileInstance) => string | 30 | export type GetVideoFilePath = (this: VideoInstance, videoFile: VideoFileInstance) => string |
30 | export type CreateTorrentAndSetInfoHash = (this: VideoInstance, videoFile: VideoFileInstance) => Promise<void> | 31 | export type CreateTorrentAndSetInfoHash = (this: VideoInstance, videoFile: VideoFileInstance) => Promise<void> |
31 | 32 | ||
32 | export type ToAddRemoteJSON = (this: VideoInstance) => Promise<RemoteVideoCreateData> | 33 | export type ToActivityPubObject = (this: VideoInstance) => VideoTorrentObject |
33 | export type ToUpdateRemoteJSON = (this: VideoInstance) => RemoteVideoUpdateData | ||
34 | 34 | ||
35 | export type OptimizeOriginalVideofile = (this: VideoInstance) => Promise<void> | 35 | export type OptimizeOriginalVideofile = (this: VideoInstance) => Promise<void> |
36 | export type TranscodeOriginalVideofile = (this: VideoInstance, resolution: number) => Promise<void> | 36 | export type TranscodeOriginalVideofile = (this: VideoInstance, resolution: number) => Promise<void> |
@@ -40,31 +40,35 @@ export namespace VideoMethods { | |||
40 | export type GetPreviewPath = (this: VideoInstance) => string | 40 | export type GetPreviewPath = (this: VideoInstance) => string |
41 | export type GetDescriptionPath = (this: VideoInstance) => string | 41 | export type GetDescriptionPath = (this: VideoInstance) => string |
42 | export type GetTruncatedDescription = (this: VideoInstance) => string | 42 | export type GetTruncatedDescription = (this: VideoInstance) => string |
43 | export type GetCategoryLabel = (this: VideoInstance) => string | ||
44 | export type GetLicenceLabel = (this: VideoInstance) => string | ||
45 | export type GetLanguageLabel = (this: VideoInstance) => string | ||
43 | 46 | ||
44 | // Return thumbnail name | 47 | // Return thumbnail name |
45 | export type GenerateThumbnailFromData = (video: VideoInstance, thumbnailData: string) => Promise<string> | 48 | export type GenerateThumbnailFromData = (video: VideoInstance, thumbnailData: string) => Promise<string> |
46 | 49 | ||
47 | export type List = () => Promise<VideoInstance[]> | 50 | export type List = () => Bluebird<VideoInstance[]> |
48 | export type ListOwnedAndPopulateAuthorAndTags = () => Promise<VideoInstance[]> | 51 | export type ListOwnedAndPopulateAccountAndTags = () => Bluebird<VideoInstance[]> |
49 | export type ListOwnedByAuthor = (author: string) => Promise<VideoInstance[]> | 52 | export type ListOwnedByAccount = (account: string) => Bluebird<VideoInstance[]> |
50 | 53 | ||
51 | export type ListForApi = (start: number, count: number, sort: string) => Promise< ResultList<VideoInstance> > | 54 | export type ListForApi = (start: number, count: number, sort: string) => Bluebird< ResultList<VideoInstance> > |
52 | export type ListUserVideosForApi = (userId: number, start: number, count: number, sort: string) => Promise< ResultList<VideoInstance> > | 55 | export type ListUserVideosForApi = (userId: number, start: number, count: number, sort: string) => Bluebird< ResultList<VideoInstance> > |
53 | export type SearchAndPopulateAuthorAndPodAndTags = ( | 56 | export type SearchAndPopulateAccountAndPodAndTags = ( |
54 | value: string, | 57 | value: string, |
55 | field: string, | 58 | field: string, |
56 | start: number, | 59 | start: number, |
57 | count: number, | 60 | count: number, |
58 | sort: string | 61 | sort: string |
59 | ) => Promise< ResultList<VideoInstance> > | 62 | ) => Bluebird< ResultList<VideoInstance> > |
60 | 63 | ||
61 | export type Load = (id: number) => Promise<VideoInstance> | 64 | export type Load = (id: number) => Bluebird<VideoInstance> |
62 | export type LoadByUUID = (uuid: string, t?: Sequelize.Transaction) => Promise<VideoInstance> | 65 | export type LoadByUUID = (uuid: string, t?: Sequelize.Transaction) => Bluebird<VideoInstance> |
63 | export type LoadLocalVideoByUUID = (uuid: string, t?: Sequelize.Transaction) => Promise<VideoInstance> | 66 | export type LoadByUrl = (url: string, t?: Sequelize.Transaction) => Bluebird<VideoInstance> |
64 | export type LoadByHostAndUUID = (fromHost: string, uuid: string, t?: Sequelize.Transaction) => Promise<VideoInstance> | 67 | export type LoadLocalVideoByUUID = (uuid: string, t?: Sequelize.Transaction) => Bluebird<VideoInstance> |
65 | export type LoadAndPopulateAuthor = (id: number) => Promise<VideoInstance> | 68 | export type LoadByHostAndUUID = (fromHost: string, uuid: string, t?: Sequelize.Transaction) => Bluebird<VideoInstance> |
66 | export type LoadAndPopulateAuthorAndPodAndTags = (id: number) => Promise<VideoInstance> | 69 | export type LoadAndPopulateAccount = (id: number) => Bluebird<VideoInstance> |
67 | export type LoadByUUIDAndPopulateAuthorAndPodAndTags = (uuid: string) => Promise<VideoInstance> | 70 | export type LoadAndPopulateAccountAndPodAndTags = (id: number) => Bluebird<VideoInstance> |
71 | export type LoadByUUIDAndPopulateAccountAndPodAndTags = (uuid: string) => Bluebird<VideoInstance> | ||
68 | 72 | ||
69 | export type RemoveThumbnail = (this: VideoInstance) => Promise<void> | 73 | export type RemoveThumbnail = (this: VideoInstance) => Promise<void> |
70 | export type RemovePreview = (this: VideoInstance) => Promise<void> | 74 | export type RemovePreview = (this: VideoInstance) => Promise<void> |
@@ -77,16 +81,17 @@ export interface VideoClass { | |||
77 | list: VideoMethods.List | 81 | list: VideoMethods.List |
78 | listForApi: VideoMethods.ListForApi | 82 | listForApi: VideoMethods.ListForApi |
79 | listUserVideosForApi: VideoMethods.ListUserVideosForApi | 83 | listUserVideosForApi: VideoMethods.ListUserVideosForApi |
80 | listOwnedAndPopulateAuthorAndTags: VideoMethods.ListOwnedAndPopulateAuthorAndTags | 84 | listOwnedAndPopulateAccountAndTags: VideoMethods.ListOwnedAndPopulateAccountAndTags |
81 | listOwnedByAuthor: VideoMethods.ListOwnedByAuthor | 85 | listOwnedByAccount: VideoMethods.ListOwnedByAccount |
82 | load: VideoMethods.Load | 86 | load: VideoMethods.Load |
83 | loadAndPopulateAuthor: VideoMethods.LoadAndPopulateAuthor | 87 | loadAndPopulateAccount: VideoMethods.LoadAndPopulateAccount |
84 | loadAndPopulateAuthorAndPodAndTags: VideoMethods.LoadAndPopulateAuthorAndPodAndTags | 88 | loadAndPopulateAccountAndPodAndTags: VideoMethods.LoadAndPopulateAccountAndPodAndTags |
85 | loadByHostAndUUID: VideoMethods.LoadByHostAndUUID | 89 | loadByHostAndUUID: VideoMethods.LoadByHostAndUUID |
86 | loadByUUID: VideoMethods.LoadByUUID | 90 | loadByUUID: VideoMethods.LoadByUUID |
91 | loadByUrl: VideoMethods.LoadByUrl | ||
87 | loadLocalVideoByUUID: VideoMethods.LoadLocalVideoByUUID | 92 | loadLocalVideoByUUID: VideoMethods.LoadLocalVideoByUUID |
88 | loadByUUIDAndPopulateAuthorAndPodAndTags: VideoMethods.LoadByUUIDAndPopulateAuthorAndPodAndTags | 93 | loadByUUIDAndPopulateAccountAndPodAndTags: VideoMethods.LoadByUUIDAndPopulateAccountAndPodAndTags |
89 | searchAndPopulateAuthorAndPodAndTags: VideoMethods.SearchAndPopulateAuthorAndPodAndTags | 94 | searchAndPopulateAccountAndPodAndTags: VideoMethods.SearchAndPopulateAccountAndPodAndTags |
90 | } | 95 | } |
91 | 96 | ||
92 | export interface VideoAttributes { | 97 | export interface VideoAttributes { |
@@ -104,7 +109,9 @@ export interface VideoAttributes { | |||
104 | likes?: number | 109 | likes?: number |
105 | dislikes?: number | 110 | dislikes?: number |
106 | remote: boolean | 111 | remote: boolean |
112 | url: string | ||
107 | 113 | ||
114 | parentId?: number | ||
108 | channelId?: number | 115 | channelId?: number |
109 | 116 | ||
110 | VideoChannel?: VideoChannelInstance | 117 | VideoChannel?: VideoChannelInstance |
@@ -132,16 +139,18 @@ export interface VideoInstance extends VideoClass, VideoAttributes, Sequelize.In | |||
132 | removePreview: VideoMethods.RemovePreview | 139 | removePreview: VideoMethods.RemovePreview |
133 | removeThumbnail: VideoMethods.RemoveThumbnail | 140 | removeThumbnail: VideoMethods.RemoveThumbnail |
134 | removeTorrent: VideoMethods.RemoveTorrent | 141 | removeTorrent: VideoMethods.RemoveTorrent |
135 | toAddRemoteJSON: VideoMethods.ToAddRemoteJSON | 142 | toActivityPubObject: VideoMethods.ToActivityPubObject |
136 | toFormattedJSON: VideoMethods.ToFormattedJSON | 143 | toFormattedJSON: VideoMethods.ToFormattedJSON |
137 | toFormattedDetailsJSON: VideoMethods.ToFormattedDetailsJSON | 144 | toFormattedDetailsJSON: VideoMethods.ToFormattedDetailsJSON |
138 | toUpdateRemoteJSON: VideoMethods.ToUpdateRemoteJSON | ||
139 | optimizeOriginalVideofile: VideoMethods.OptimizeOriginalVideofile | 145 | optimizeOriginalVideofile: VideoMethods.OptimizeOriginalVideofile |
140 | transcodeOriginalVideofile: VideoMethods.TranscodeOriginalVideofile | 146 | transcodeOriginalVideofile: VideoMethods.TranscodeOriginalVideofile |
141 | getOriginalFileHeight: VideoMethods.GetOriginalFileHeight | 147 | getOriginalFileHeight: VideoMethods.GetOriginalFileHeight |
142 | getEmbedPath: VideoMethods.GetEmbedPath | 148 | getEmbedPath: VideoMethods.GetEmbedPath |
143 | getDescriptionPath: VideoMethods.GetDescriptionPath | 149 | getDescriptionPath: VideoMethods.GetDescriptionPath |
144 | getTruncatedDescription: VideoMethods.GetTruncatedDescription | 150 | getTruncatedDescription: VideoMethods.GetTruncatedDescription |
151 | getCategoryLabel: VideoMethods.GetCategoryLabel | ||
152 | getLicenceLabel: VideoMethods.GetLicenceLabel | ||
153 | getLanguageLabel: VideoMethods.GetLanguageLabel | ||
145 | 154 | ||
146 | setTags: Sequelize.HasManySetAssociationsMixin<TagAttributes, string> | 155 | setTags: Sequelize.HasManySetAssociationsMixin<TagAttributes, string> |
147 | addVideoFile: Sequelize.HasManyAddAssociationMixin<VideoFileAttributes, string> | 156 | addVideoFile: Sequelize.HasManyAddAssociationMixin<VideoFileAttributes, string> |
@@ -149,3 +158,4 @@ export interface VideoInstance extends VideoClass, VideoAttributes, Sequelize.In | |||
149 | } | 158 | } |
150 | 159 | ||
151 | export interface VideoModel extends VideoClass, Sequelize.Model<VideoInstance, VideoAttributes> {} | 160 | export interface VideoModel extends VideoClass, Sequelize.Model<VideoInstance, VideoAttributes> {} |
161 | |||
diff --git a/server/models/video/video.ts b/server/models/video/video.ts index 02dde1726..94af1ece5 100644 --- a/server/models/video/video.ts +++ b/server/models/video/video.ts | |||
@@ -5,7 +5,6 @@ import { map, maxBy, truncate } from 'lodash' | |||
5 | import * as parseTorrent from 'parse-torrent' | 5 | import * as parseTorrent from 'parse-torrent' |
6 | import { join } from 'path' | 6 | import { join } from 'path' |
7 | import * as Sequelize from 'sequelize' | 7 | import * as Sequelize from 'sequelize' |
8 | import * as Promise from 'bluebird' | ||
9 | 8 | ||
10 | import { TagInstance } from './tag-interface' | 9 | import { TagInstance } from './tag-interface' |
11 | import { | 10 | import { |
@@ -52,6 +51,7 @@ import { | |||
52 | 51 | ||
53 | VideoMethods | 52 | VideoMethods |
54 | } from './video-interface' | 53 | } from './video-interface' |
54 | import { VideoTorrentObject } from '../../../shared/models/activitypub/objects/video-torrent-object' | ||
55 | 55 | ||
56 | let Video: Sequelize.Model<VideoInstance, VideoAttributes> | 56 | let Video: Sequelize.Model<VideoInstance, VideoAttributes> |
57 | let getOriginalFile: VideoMethods.GetOriginalFile | 57 | let getOriginalFile: VideoMethods.GetOriginalFile |
@@ -64,8 +64,7 @@ let getTorrentFileName: VideoMethods.GetTorrentFileName | |||
64 | let isOwned: VideoMethods.IsOwned | 64 | let isOwned: VideoMethods.IsOwned |
65 | let toFormattedJSON: VideoMethods.ToFormattedJSON | 65 | let toFormattedJSON: VideoMethods.ToFormattedJSON |
66 | let toFormattedDetailsJSON: VideoMethods.ToFormattedDetailsJSON | 66 | let toFormattedDetailsJSON: VideoMethods.ToFormattedDetailsJSON |
67 | let toAddRemoteJSON: VideoMethods.ToAddRemoteJSON | 67 | let toActivityPubObject: VideoMethods.ToActivityPubObject |
68 | let toUpdateRemoteJSON: VideoMethods.ToUpdateRemoteJSON | ||
69 | let optimizeOriginalVideofile: VideoMethods.OptimizeOriginalVideofile | 68 | let optimizeOriginalVideofile: VideoMethods.OptimizeOriginalVideofile |
70 | let transcodeOriginalVideofile: VideoMethods.TranscodeOriginalVideofile | 69 | let transcodeOriginalVideofile: VideoMethods.TranscodeOriginalVideofile |
71 | let createPreview: VideoMethods.CreatePreview | 70 | let createPreview: VideoMethods.CreatePreview |
@@ -76,21 +75,25 @@ let getOriginalFileHeight: VideoMethods.GetOriginalFileHeight | |||
76 | let getEmbedPath: VideoMethods.GetEmbedPath | 75 | let getEmbedPath: VideoMethods.GetEmbedPath |
77 | let getDescriptionPath: VideoMethods.GetDescriptionPath | 76 | let getDescriptionPath: VideoMethods.GetDescriptionPath |
78 | let getTruncatedDescription: VideoMethods.GetTruncatedDescription | 77 | let getTruncatedDescription: VideoMethods.GetTruncatedDescription |
78 | let getCategoryLabel: VideoMethods.GetCategoryLabel | ||
79 | let getLicenceLabel: VideoMethods.GetLicenceLabel | ||
80 | let getLanguageLabel: VideoMethods.GetLanguageLabel | ||
79 | 81 | ||
80 | let generateThumbnailFromData: VideoMethods.GenerateThumbnailFromData | 82 | let generateThumbnailFromData: VideoMethods.GenerateThumbnailFromData |
81 | let list: VideoMethods.List | 83 | let list: VideoMethods.List |
82 | let listForApi: VideoMethods.ListForApi | 84 | let listForApi: VideoMethods.ListForApi |
83 | let listUserVideosForApi: VideoMethods.ListUserVideosForApi | 85 | let listUserVideosForApi: VideoMethods.ListUserVideosForApi |
84 | let loadByHostAndUUID: VideoMethods.LoadByHostAndUUID | 86 | let loadByHostAndUUID: VideoMethods.LoadByHostAndUUID |
85 | let listOwnedAndPopulateAuthorAndTags: VideoMethods.ListOwnedAndPopulateAuthorAndTags | 87 | let listOwnedAndPopulateAccountAndTags: VideoMethods.ListOwnedAndPopulateAccountAndTags |
86 | let listOwnedByAuthor: VideoMethods.ListOwnedByAuthor | 88 | let listOwnedByAccount: VideoMethods.ListOwnedByAccount |
87 | let load: VideoMethods.Load | 89 | let load: VideoMethods.Load |
88 | let loadByUUID: VideoMethods.LoadByUUID | 90 | let loadByUUID: VideoMethods.LoadByUUID |
91 | let loadByUrl: VideoMethods.LoadByUrl | ||
89 | let loadLocalVideoByUUID: VideoMethods.LoadLocalVideoByUUID | 92 | let loadLocalVideoByUUID: VideoMethods.LoadLocalVideoByUUID |
90 | let loadAndPopulateAuthor: VideoMethods.LoadAndPopulateAuthor | 93 | let loadAndPopulateAccount: VideoMethods.LoadAndPopulateAccount |
91 | let loadAndPopulateAuthorAndPodAndTags: VideoMethods.LoadAndPopulateAuthorAndPodAndTags | 94 | let loadAndPopulateAccountAndPodAndTags: VideoMethods.LoadAndPopulateAccountAndPodAndTags |
92 | let loadByUUIDAndPopulateAuthorAndPodAndTags: VideoMethods.LoadByUUIDAndPopulateAuthorAndPodAndTags | 95 | let loadByUUIDAndPopulateAccountAndPodAndTags: VideoMethods.LoadByUUIDAndPopulateAccountAndPodAndTags |
93 | let searchAndPopulateAuthorAndPodAndTags: VideoMethods.SearchAndPopulateAuthorAndPodAndTags | 96 | let searchAndPopulateAccountAndPodAndTags: VideoMethods.SearchAndPopulateAccountAndPodAndTags |
94 | let removeThumbnail: VideoMethods.RemoveThumbnail | 97 | let removeThumbnail: VideoMethods.RemoveThumbnail |
95 | let removePreview: VideoMethods.RemovePreview | 98 | let removePreview: VideoMethods.RemovePreview |
96 | let removeFile: VideoMethods.RemoveFile | 99 | let removeFile: VideoMethods.RemoveFile |
@@ -219,6 +222,13 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da | |||
219 | type: DataTypes.BOOLEAN, | 222 | type: DataTypes.BOOLEAN, |
220 | allowNull: false, | 223 | allowNull: false, |
221 | defaultValue: false | 224 | defaultValue: false |
225 | }, | ||
226 | url: { | ||
227 | type: DataTypes.STRING, | ||
228 | allowNull: false, | ||
229 | validate: { | ||
230 | isUrl: true | ||
231 | } | ||
222 | } | 232 | } |
223 | }, | 233 | }, |
224 | { | 234 | { |
@@ -243,6 +253,9 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da | |||
243 | }, | 253 | }, |
244 | { | 254 | { |
245 | fields: [ 'channelId' ] | 255 | fields: [ 'channelId' ] |
256 | }, | ||
257 | { | ||
258 | fields: [ 'parentId' ] | ||
246 | } | 259 | } |
247 | ], | 260 | ], |
248 | hooks: { | 261 | hooks: { |
@@ -258,16 +271,16 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da | |||
258 | list, | 271 | list, |
259 | listForApi, | 272 | listForApi, |
260 | listUserVideosForApi, | 273 | listUserVideosForApi, |
261 | listOwnedAndPopulateAuthorAndTags, | 274 | listOwnedAndPopulateAccountAndTags, |
262 | listOwnedByAuthor, | 275 | listOwnedByAccount, |
263 | load, | 276 | load, |
264 | loadAndPopulateAuthor, | 277 | loadAndPopulateAccount, |
265 | loadAndPopulateAuthorAndPodAndTags, | 278 | loadAndPopulateAccountAndPodAndTags, |
266 | loadByHostAndUUID, | 279 | loadByHostAndUUID, |
267 | loadByUUID, | 280 | loadByUUID, |
268 | loadLocalVideoByUUID, | 281 | loadLocalVideoByUUID, |
269 | loadByUUIDAndPopulateAuthorAndPodAndTags, | 282 | loadByUUIDAndPopulateAccountAndPodAndTags, |
270 | searchAndPopulateAuthorAndPodAndTags | 283 | searchAndPopulateAccountAndPodAndTags |
271 | ] | 284 | ] |
272 | const instanceMethods = [ | 285 | const instanceMethods = [ |
273 | createPreview, | 286 | createPreview, |
@@ -286,16 +299,18 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da | |||
286 | removePreview, | 299 | removePreview, |
287 | removeThumbnail, | 300 | removeThumbnail, |
288 | removeTorrent, | 301 | removeTorrent, |
289 | toAddRemoteJSON, | 302 | toActivityPubObject, |
290 | toFormattedJSON, | 303 | toFormattedJSON, |
291 | toFormattedDetailsJSON, | 304 | toFormattedDetailsJSON, |
292 | toUpdateRemoteJSON, | ||
293 | optimizeOriginalVideofile, | 305 | optimizeOriginalVideofile, |
294 | transcodeOriginalVideofile, | 306 | transcodeOriginalVideofile, |
295 | getOriginalFileHeight, | 307 | getOriginalFileHeight, |
296 | getEmbedPath, | 308 | getEmbedPath, |
297 | getTruncatedDescription, | 309 | getTruncatedDescription, |
298 | getDescriptionPath | 310 | getDescriptionPath, |
311 | getCategoryLabel, | ||
312 | getLicenceLabel, | ||
313 | getLanguageLabel | ||
299 | ] | 314 | ] |
300 | addMethodsToModel(Video, classMethods, instanceMethods) | 315 | addMethodsToModel(Video, classMethods, instanceMethods) |
301 | 316 | ||
@@ -313,6 +328,14 @@ function associate (models) { | |||
313 | onDelete: 'cascade' | 328 | onDelete: 'cascade' |
314 | }) | 329 | }) |
315 | 330 | ||
331 | Video.belongsTo(models.VideoChannel, { | ||
332 | foreignKey: { | ||
333 | name: 'parentId', | ||
334 | allowNull: true | ||
335 | }, | ||
336 | onDelete: 'cascade' | ||
337 | }) | ||
338 | |||
316 | Video.belongsToMany(models.Tag, { | 339 | Video.belongsToMany(models.Tag, { |
317 | foreignKey: 'videoId', | 340 | foreignKey: 'videoId', |
318 | through: models.VideoTag, | 341 | through: models.VideoTag, |
@@ -423,7 +446,7 @@ getVideoFilePath = function (this: VideoInstance, videoFile: VideoFileInstance) | |||
423 | return join(CONFIG.STORAGE.VIDEOS_DIR, this.getVideoFilename(videoFile)) | 446 | return join(CONFIG.STORAGE.VIDEOS_DIR, this.getVideoFilename(videoFile)) |
424 | } | 447 | } |
425 | 448 | ||
426 | createTorrentAndSetInfoHash = function (this: VideoInstance, videoFile: VideoFileInstance) { | 449 | createTorrentAndSetInfoHash = async function (this: VideoInstance, videoFile: VideoFileInstance) { |
427 | const options = { | 450 | const options = { |
428 | announceList: [ | 451 | announceList: [ |
429 | [ CONFIG.WEBSERVER.WS + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT + '/tracker/socket' ] | 452 | [ CONFIG.WEBSERVER.WS + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT + '/tracker/socket' ] |
@@ -433,18 +456,15 @@ createTorrentAndSetInfoHash = function (this: VideoInstance, videoFile: VideoFil | |||
433 | ] | 456 | ] |
434 | } | 457 | } |
435 | 458 | ||
436 | return createTorrentPromise(this.getVideoFilePath(videoFile), options) | 459 | const torrent = await createTorrentPromise(this.getVideoFilePath(videoFile), options) |
437 | .then(torrent => { | ||
438 | const filePath = join(CONFIG.STORAGE.TORRENTS_DIR, this.getTorrentFileName(videoFile)) | ||
439 | logger.info('Creating torrent %s.', filePath) | ||
440 | 460 | ||
441 | return writeFilePromise(filePath, torrent).then(() => torrent) | 461 | const filePath = join(CONFIG.STORAGE.TORRENTS_DIR, this.getTorrentFileName(videoFile)) |
442 | }) | 462 | logger.info('Creating torrent %s.', filePath) |
443 | .then(torrent => { | ||
444 | const parsedTorrent = parseTorrent(torrent) | ||
445 | 463 | ||
446 | videoFile.infoHash = parsedTorrent.infoHash | 464 | await writeFilePromise(filePath, torrent) |
447 | }) | 465 | |
466 | const parsedTorrent = parseTorrent(torrent) | ||
467 | videoFile.infoHash = parsedTorrent.infoHash | ||
448 | } | 468 | } |
449 | 469 | ||
450 | getEmbedPath = function (this: VideoInstance) { | 470 | getEmbedPath = function (this: VideoInstance) { |
@@ -462,40 +482,28 @@ getPreviewPath = function (this: VideoInstance) { | |||
462 | toFormattedJSON = function (this: VideoInstance) { | 482 | toFormattedJSON = function (this: VideoInstance) { |
463 | let podHost | 483 | let podHost |
464 | 484 | ||
465 | if (this.VideoChannel.Author.Pod) { | 485 | if (this.VideoChannel.Account.Pod) { |
466 | podHost = this.VideoChannel.Author.Pod.host | 486 | podHost = this.VideoChannel.Account.Pod.host |
467 | } else { | 487 | } else { |
468 | // It means it's our video | 488 | // It means it's our video |
469 | podHost = CONFIG.WEBSERVER.HOST | 489 | podHost = CONFIG.WEBSERVER.HOST |
470 | } | 490 | } |
471 | 491 | ||
472 | // Maybe our pod is not up to date and there are new categories since our version | ||
473 | let categoryLabel = VIDEO_CATEGORIES[this.category] | ||
474 | if (!categoryLabel) categoryLabel = 'Misc' | ||
475 | |||
476 | // Maybe our pod is not up to date and there are new licences since our version | ||
477 | let licenceLabel = VIDEO_LICENCES[this.licence] | ||
478 | if (!licenceLabel) licenceLabel = 'Unknown' | ||
479 | |||
480 | // Language is an optional attribute | ||
481 | let languageLabel = VIDEO_LANGUAGES[this.language] | ||
482 | if (!languageLabel) languageLabel = 'Unknown' | ||
483 | |||
484 | const json = { | 492 | const json = { |
485 | id: this.id, | 493 | id: this.id, |
486 | uuid: this.uuid, | 494 | uuid: this.uuid, |
487 | name: this.name, | 495 | name: this.name, |
488 | category: this.category, | 496 | category: this.category, |
489 | categoryLabel, | 497 | categoryLabel: this.getCategoryLabel(), |
490 | licence: this.licence, | 498 | licence: this.licence, |
491 | licenceLabel, | 499 | licenceLabel: this.getLicenceLabel(), |
492 | language: this.language, | 500 | language: this.language, |
493 | languageLabel, | 501 | languageLabel: this.getLanguageLabel(), |
494 | nsfw: this.nsfw, | 502 | nsfw: this.nsfw, |
495 | description: this.getTruncatedDescription(), | 503 | description: this.getTruncatedDescription(), |
496 | podHost, | 504 | podHost, |
497 | isLocal: this.isOwned(), | 505 | isLocal: this.isOwned(), |
498 | author: this.VideoChannel.Author.name, | 506 | account: this.VideoChannel.Account.name, |
499 | duration: this.duration, | 507 | duration: this.duration, |
500 | views: this.views, | 508 | views: this.views, |
501 | likes: this.likes, | 509 | likes: this.likes, |
@@ -552,75 +560,75 @@ toFormattedDetailsJSON = function (this: VideoInstance) { | |||
552 | return Object.assign(formattedJson, detailsJson) | 560 | return Object.assign(formattedJson, detailsJson) |
553 | } | 561 | } |
554 | 562 | ||
555 | toAddRemoteJSON = function (this: VideoInstance) { | 563 | toActivityPubObject = function (this: VideoInstance) { |
556 | // Get thumbnail data to send to the other pod | 564 | const { baseUrlHttp, baseUrlWs } = getBaseUrls(this) |
557 | const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, this.getThumbnailName()) | ||
558 | 565 | ||
559 | return readFileBufferPromise(thumbnailPath).then(thumbnailData => { | 566 | const tag = this.Tags.map(t => ({ |
560 | const remoteVideo = { | 567 | type: 'Hashtag', |
561 | uuid: this.uuid, | 568 | name: t.name |
562 | name: this.name, | 569 | })) |
563 | category: this.category, | 570 | |
564 | licence: this.licence, | 571 | const url = [] |
565 | language: this.language, | 572 | for (const file of this.VideoFiles) { |
566 | nsfw: this.nsfw, | 573 | url.push({ |
567 | truncatedDescription: this.getTruncatedDescription(), | 574 | type: 'Link', |
568 | channelUUID: this.VideoChannel.uuid, | 575 | mimeType: 'video/' + file.extname, |
569 | duration: this.duration, | 576 | url: getVideoFileUrl(this, file, baseUrlHttp), |
570 | thumbnailData: thumbnailData.toString('binary'), | 577 | width: file.resolution, |
571 | tags: map<TagInstance, string>(this.Tags, 'name'), | 578 | size: file.size |
572 | createdAt: this.createdAt, | 579 | }) |
573 | updatedAt: this.updatedAt, | ||
574 | views: this.views, | ||
575 | likes: this.likes, | ||
576 | dislikes: this.dislikes, | ||
577 | privacy: this.privacy, | ||
578 | files: [] | ||
579 | } | ||
580 | 580 | ||
581 | this.VideoFiles.forEach(videoFile => { | 581 | url.push({ |
582 | remoteVideo.files.push({ | 582 | type: 'Link', |
583 | infoHash: videoFile.infoHash, | 583 | mimeType: 'application/x-bittorrent', |
584 | resolution: videoFile.resolution, | 584 | url: getTorrentUrl(this, file, baseUrlHttp), |
585 | extname: videoFile.extname, | 585 | width: file.resolution |
586 | size: videoFile.size | ||
587 | }) | ||
588 | }) | 586 | }) |
589 | 587 | ||
590 | return remoteVideo | 588 | url.push({ |
591 | }) | 589 | type: 'Link', |
592 | } | 590 | mimeType: 'application/x-bittorrent;x-scheme-handler/magnet', |
591 | url: generateMagnetUri(this, file, baseUrlHttp, baseUrlWs), | ||
592 | width: file.resolution | ||
593 | }) | ||
594 | } | ||
593 | 595 | ||
594 | toUpdateRemoteJSON = function (this: VideoInstance) { | 596 | const videoObject: VideoTorrentObject = { |
595 | const json = { | 597 | type: 'Video', |
596 | uuid: this.uuid, | ||
597 | name: this.name, | 598 | name: this.name, |
598 | category: this.category, | 599 | // https://www.w3.org/TR/activitystreams-vocabulary/#dfn-duration |
599 | licence: this.licence, | 600 | duration: 'PT' + this.duration + 'S', |
600 | language: this.language, | 601 | uuid: this.uuid, |
601 | nsfw: this.nsfw, | 602 | tag, |
602 | truncatedDescription: this.getTruncatedDescription(), | 603 | category: { |
603 | duration: this.duration, | 604 | id: this.category, |
604 | tags: map<TagInstance, string>(this.Tags, 'name'), | 605 | label: this.getCategoryLabel() |
605 | createdAt: this.createdAt, | 606 | }, |
606 | updatedAt: this.updatedAt, | 607 | licence: { |
608 | id: this.licence, | ||
609 | name: this.getLicenceLabel() | ||
610 | }, | ||
611 | language: { | ||
612 | id: this.language, | ||
613 | name: this.getLanguageLabel() | ||
614 | }, | ||
607 | views: this.views, | 615 | views: this.views, |
608 | likes: this.likes, | 616 | nsfw: this.nsfw, |
609 | dislikes: this.dislikes, | 617 | published: this.createdAt, |
610 | privacy: this.privacy, | 618 | updated: this.updatedAt, |
611 | files: [] | 619 | mediaType: 'text/markdown', |
620 | content: this.getTruncatedDescription(), | ||
621 | icon: { | ||
622 | type: 'Image', | ||
623 | url: getThumbnailUrl(this, baseUrlHttp), | ||
624 | mediaType: 'image/jpeg', | ||
625 | width: THUMBNAILS_SIZE.width, | ||
626 | height: THUMBNAILS_SIZE.height | ||
627 | }, | ||
628 | url | ||
612 | } | 629 | } |
613 | 630 | ||
614 | this.VideoFiles.forEach(videoFile => { | 631 | return videoObject |
615 | json.files.push({ | ||
616 | infoHash: videoFile.infoHash, | ||
617 | resolution: videoFile.resolution, | ||
618 | extname: videoFile.extname, | ||
619 | size: videoFile.size | ||
620 | }) | ||
621 | }) | ||
622 | |||
623 | return json | ||
624 | } | 632 | } |
625 | 633 | ||
626 | getTruncatedDescription = function (this: VideoInstance) { | 634 | getTruncatedDescription = function (this: VideoInstance) { |
@@ -631,7 +639,7 @@ getTruncatedDescription = function (this: VideoInstance) { | |||
631 | return truncate(this.description, options) | 639 | return truncate(this.description, options) |
632 | } | 640 | } |
633 | 641 | ||
634 | optimizeOriginalVideofile = function (this: VideoInstance) { | 642 | optimizeOriginalVideofile = async function (this: VideoInstance) { |
635 | const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR | 643 | const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR |
636 | const newExtname = '.mp4' | 644 | const newExtname = '.mp4' |
637 | const inputVideoFile = this.getOriginalFile() | 645 | const inputVideoFile = this.getOriginalFile() |
@@ -643,40 +651,32 @@ optimizeOriginalVideofile = function (this: VideoInstance) { | |||
643 | outputPath: videoOutputPath | 651 | outputPath: videoOutputPath |
644 | } | 652 | } |
645 | 653 | ||
646 | return transcode(transcodeOptions) | 654 | try { |
647 | .then(() => { | 655 | // Could be very long! |
648 | return unlinkPromise(videoInputPath) | 656 | await transcode(transcodeOptions) |
649 | }) | ||
650 | .then(() => { | ||
651 | // Important to do this before getVideoFilename() to take in account the new file extension | ||
652 | inputVideoFile.set('extname', newExtname) | ||
653 | 657 | ||
654 | return renamePromise(videoOutputPath, this.getVideoFilePath(inputVideoFile)) | 658 | await unlinkPromise(videoInputPath) |
655 | }) | ||
656 | .then(() => { | ||
657 | return statPromise(this.getVideoFilePath(inputVideoFile)) | ||
658 | }) | ||
659 | .then(stats => { | ||
660 | return inputVideoFile.set('size', stats.size) | ||
661 | }) | ||
662 | .then(() => { | ||
663 | return this.createTorrentAndSetInfoHash(inputVideoFile) | ||
664 | }) | ||
665 | .then(() => { | ||
666 | return inputVideoFile.save() | ||
667 | }) | ||
668 | .then(() => { | ||
669 | return undefined | ||
670 | }) | ||
671 | .catch(err => { | ||
672 | // Auto destruction... | ||
673 | this.destroy().catch(err => logger.error('Cannot destruct video after transcoding failure.', err)) | ||
674 | 659 | ||
675 | throw err | 660 | // Important to do this before getVideoFilename() to take in account the new file extension |
676 | }) | 661 | inputVideoFile.set('extname', newExtname) |
662 | |||
663 | await renamePromise(videoOutputPath, this.getVideoFilePath(inputVideoFile)) | ||
664 | const stats = await statPromise(this.getVideoFilePath(inputVideoFile)) | ||
665 | |||
666 | inputVideoFile.set('size', stats.size) | ||
667 | |||
668 | await this.createTorrentAndSetInfoHash(inputVideoFile) | ||
669 | await inputVideoFile.save() | ||
670 | |||
671 | } catch (err) { | ||
672 | // Auto destruction... | ||
673 | this.destroy().catch(err => logger.error('Cannot destruct video after transcoding failure.', err)) | ||
674 | |||
675 | throw err | ||
676 | } | ||
677 | } | 677 | } |
678 | 678 | ||
679 | transcodeOriginalVideofile = function (this: VideoInstance, resolution: VideoResolution) { | 679 | transcodeOriginalVideofile = async function (this: VideoInstance, resolution: VideoResolution) { |
680 | const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR | 680 | const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR |
681 | const extname = '.mp4' | 681 | const extname = '.mp4' |
682 | 682 | ||
@@ -696,25 +696,18 @@ transcodeOriginalVideofile = function (this: VideoInstance, resolution: VideoRes | |||
696 | outputPath: videoOutputPath, | 696 | outputPath: videoOutputPath, |
697 | resolution | 697 | resolution |
698 | } | 698 | } |
699 | return transcode(transcodeOptions) | ||
700 | .then(() => { | ||
701 | return statPromise(videoOutputPath) | ||
702 | }) | ||
703 | .then(stats => { | ||
704 | newVideoFile.set('size', stats.size) | ||
705 | 699 | ||
706 | return undefined | 700 | await transcode(transcodeOptions) |
707 | }) | 701 | |
708 | .then(() => { | 702 | const stats = await statPromise(videoOutputPath) |
709 | return this.createTorrentAndSetInfoHash(newVideoFile) | 703 | |
710 | }) | 704 | newVideoFile.set('size', stats.size) |
711 | .then(() => { | 705 | |
712 | return newVideoFile.save() | 706 | await this.createTorrentAndSetInfoHash(newVideoFile) |
713 | }) | 707 | |
714 | .then(() => { | 708 | await newVideoFile.save() |
715 | return this.VideoFiles.push(newVideoFile) | 709 | |
716 | }) | 710 | this.VideoFiles.push(newVideoFile) |
717 | .then(() => undefined) | ||
718 | } | 711 | } |
719 | 712 | ||
720 | getOriginalFileHeight = function (this: VideoInstance) { | 713 | getOriginalFileHeight = function (this: VideoInstance) { |
@@ -727,6 +720,31 @@ getDescriptionPath = function (this: VideoInstance) { | |||
727 | return `/api/${API_VERSION}/videos/${this.uuid}/description` | 720 | return `/api/${API_VERSION}/videos/${this.uuid}/description` |
728 | } | 721 | } |
729 | 722 | ||
723 | getCategoryLabel = function (this: VideoInstance) { | ||
724 | let categoryLabel = VIDEO_CATEGORIES[this.category] | ||
725 | |||
726 | // Maybe our pod is not up to date and there are new categories since our version | ||
727 | if (!categoryLabel) categoryLabel = 'Misc' | ||
728 | |||
729 | return categoryLabel | ||
730 | } | ||
731 | |||
732 | getLicenceLabel = function (this: VideoInstance) { | ||
733 | let licenceLabel = VIDEO_LICENCES[this.licence] | ||
734 | // Maybe our pod is not up to date and there are new licences since our version | ||
735 | if (!licenceLabel) licenceLabel = 'Unknown' | ||
736 | |||
737 | return licenceLabel | ||
738 | } | ||
739 | |||
740 | getLanguageLabel = function (this: VideoInstance) { | ||
741 | // Language is an optional attribute | ||
742 | let languageLabel = VIDEO_LANGUAGES[this.language] | ||
743 | if (!languageLabel) languageLabel = 'Unknown' | ||
744 | |||
745 | return languageLabel | ||
746 | } | ||
747 | |||
730 | removeThumbnail = function (this: VideoInstance) { | 748 | removeThumbnail = function (this: VideoInstance) { |
731 | const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, this.getThumbnailName()) | 749 | const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, this.getThumbnailName()) |
732 | return unlinkPromise(thumbnailPath) | 750 | return unlinkPromise(thumbnailPath) |
@@ -779,7 +797,7 @@ listUserVideosForApi = function (userId: number, start: number, count: number, s | |||
779 | required: true, | 797 | required: true, |
780 | include: [ | 798 | include: [ |
781 | { | 799 | { |
782 | model: Video['sequelize'].models.Author, | 800 | model: Video['sequelize'].models.Account, |
783 | where: { | 801 | where: { |
784 | userId | 802 | userId |
785 | }, | 803 | }, |
@@ -810,7 +828,7 @@ listForApi = function (start: number, count: number, sort: string) { | |||
810 | model: Video['sequelize'].models.VideoChannel, | 828 | model: Video['sequelize'].models.VideoChannel, |
811 | include: [ | 829 | include: [ |
812 | { | 830 | { |
813 | model: Video['sequelize'].models.Author, | 831 | model: Video['sequelize'].models.Account, |
814 | include: [ | 832 | include: [ |
815 | { | 833 | { |
816 | model: Video['sequelize'].models.Pod, | 834 | model: Video['sequelize'].models.Pod, |
@@ -846,7 +864,7 @@ loadByHostAndUUID = function (fromHost: string, uuid: string, t?: Sequelize.Tran | |||
846 | model: Video['sequelize'].models.VideoChannel, | 864 | model: Video['sequelize'].models.VideoChannel, |
847 | include: [ | 865 | include: [ |
848 | { | 866 | { |
849 | model: Video['sequelize'].models.Author, | 867 | model: Video['sequelize'].models.Account, |
850 | include: [ | 868 | include: [ |
851 | { | 869 | { |
852 | model: Video['sequelize'].models.Pod, | 870 | model: Video['sequelize'].models.Pod, |
@@ -867,7 +885,7 @@ loadByHostAndUUID = function (fromHost: string, uuid: string, t?: Sequelize.Tran | |||
867 | return Video.findOne(query) | 885 | return Video.findOne(query) |
868 | } | 886 | } |
869 | 887 | ||
870 | listOwnedAndPopulateAuthorAndTags = function () { | 888 | listOwnedAndPopulateAccountAndTags = function () { |
871 | const query = { | 889 | const query = { |
872 | where: { | 890 | where: { |
873 | remote: false | 891 | remote: false |
@@ -876,7 +894,7 @@ listOwnedAndPopulateAuthorAndTags = function () { | |||
876 | Video['sequelize'].models.VideoFile, | 894 | Video['sequelize'].models.VideoFile, |
877 | { | 895 | { |
878 | model: Video['sequelize'].models.VideoChannel, | 896 | model: Video['sequelize'].models.VideoChannel, |
879 | include: [ Video['sequelize'].models.Author ] | 897 | include: [ Video['sequelize'].models.Account ] |
880 | }, | 898 | }, |
881 | Video['sequelize'].models.Tag | 899 | Video['sequelize'].models.Tag |
882 | ] | 900 | ] |
@@ -885,7 +903,7 @@ listOwnedAndPopulateAuthorAndTags = function () { | |||
885 | return Video.findAll(query) | 903 | return Video.findAll(query) |
886 | } | 904 | } |
887 | 905 | ||
888 | listOwnedByAuthor = function (author: string) { | 906 | listOwnedByAccount = function (account: string) { |
889 | const query = { | 907 | const query = { |
890 | where: { | 908 | where: { |
891 | remote: false | 909 | remote: false |
@@ -898,9 +916,9 @@ listOwnedByAuthor = function (author: string) { | |||
898 | model: Video['sequelize'].models.VideoChannel, | 916 | model: Video['sequelize'].models.VideoChannel, |
899 | include: [ | 917 | include: [ |
900 | { | 918 | { |
901 | model: Video['sequelize'].models.Author, | 919 | model: Video['sequelize'].models.Account, |
902 | where: { | 920 | where: { |
903 | name: author | 921 | name: account |
904 | } | 922 | } |
905 | } | 923 | } |
906 | ] | 924 | ] |
@@ -942,13 +960,13 @@ loadLocalVideoByUUID = function (uuid: string, t?: Sequelize.Transaction) { | |||
942 | return Video.findOne(query) | 960 | return Video.findOne(query) |
943 | } | 961 | } |
944 | 962 | ||
945 | loadAndPopulateAuthor = function (id: number) { | 963 | loadAndPopulateAccount = function (id: number) { |
946 | const options = { | 964 | const options = { |
947 | include: [ | 965 | include: [ |
948 | Video['sequelize'].models.VideoFile, | 966 | Video['sequelize'].models.VideoFile, |
949 | { | 967 | { |
950 | model: Video['sequelize'].models.VideoChannel, | 968 | model: Video['sequelize'].models.VideoChannel, |
951 | include: [ Video['sequelize'].models.Author ] | 969 | include: [ Video['sequelize'].models.Account ] |
952 | } | 970 | } |
953 | ] | 971 | ] |
954 | } | 972 | } |
@@ -956,14 +974,14 @@ loadAndPopulateAuthor = function (id: number) { | |||
956 | return Video.findById(id, options) | 974 | return Video.findById(id, options) |
957 | } | 975 | } |
958 | 976 | ||
959 | loadAndPopulateAuthorAndPodAndTags = function (id: number) { | 977 | loadAndPopulateAccountAndPodAndTags = function (id: number) { |
960 | const options = { | 978 | const options = { |
961 | include: [ | 979 | include: [ |
962 | { | 980 | { |
963 | model: Video['sequelize'].models.VideoChannel, | 981 | model: Video['sequelize'].models.VideoChannel, |
964 | include: [ | 982 | include: [ |
965 | { | 983 | { |
966 | model: Video['sequelize'].models.Author, | 984 | model: Video['sequelize'].models.Account, |
967 | include: [ { model: Video['sequelize'].models.Pod, required: false } ] | 985 | include: [ { model: Video['sequelize'].models.Pod, required: false } ] |
968 | } | 986 | } |
969 | ] | 987 | ] |
@@ -976,7 +994,7 @@ loadAndPopulateAuthorAndPodAndTags = function (id: number) { | |||
976 | return Video.findById(id, options) | 994 | return Video.findById(id, options) |
977 | } | 995 | } |
978 | 996 | ||
979 | loadByUUIDAndPopulateAuthorAndPodAndTags = function (uuid: string) { | 997 | loadByUUIDAndPopulateAccountAndPodAndTags = function (uuid: string) { |
980 | const options = { | 998 | const options = { |
981 | where: { | 999 | where: { |
982 | uuid | 1000 | uuid |
@@ -986,7 +1004,7 @@ loadByUUIDAndPopulateAuthorAndPodAndTags = function (uuid: string) { | |||
986 | model: Video['sequelize'].models.VideoChannel, | 1004 | model: Video['sequelize'].models.VideoChannel, |
987 | include: [ | 1005 | include: [ |
988 | { | 1006 | { |
989 | model: Video['sequelize'].models.Author, | 1007 | model: Video['sequelize'].models.Account, |
990 | include: [ { model: Video['sequelize'].models.Pod, required: false } ] | 1008 | include: [ { model: Video['sequelize'].models.Pod, required: false } ] |
991 | } | 1009 | } |
992 | ] | 1010 | ] |
@@ -999,20 +1017,20 @@ loadByUUIDAndPopulateAuthorAndPodAndTags = function (uuid: string) { | |||
999 | return Video.findOne(options) | 1017 | return Video.findOne(options) |
1000 | } | 1018 | } |
1001 | 1019 | ||
1002 | searchAndPopulateAuthorAndPodAndTags = function (value: string, field: string, start: number, count: number, sort: string) { | 1020 | searchAndPopulateAccountAndPodAndTags = function (value: string, field: string, start: number, count: number, sort: string) { |
1003 | const podInclude: Sequelize.IncludeOptions = { | 1021 | const podInclude: Sequelize.IncludeOptions = { |
1004 | model: Video['sequelize'].models.Pod, | 1022 | model: Video['sequelize'].models.Pod, |
1005 | required: false | 1023 | required: false |
1006 | } | 1024 | } |
1007 | 1025 | ||
1008 | const authorInclude: Sequelize.IncludeOptions = { | 1026 | const accountInclude: Sequelize.IncludeOptions = { |
1009 | model: Video['sequelize'].models.Author, | 1027 | model: Video['sequelize'].models.Account, |
1010 | include: [ podInclude ] | 1028 | include: [ podInclude ] |
1011 | } | 1029 | } |
1012 | 1030 | ||
1013 | const videoChannelInclude: Sequelize.IncludeOptions = { | 1031 | const videoChannelInclude: Sequelize.IncludeOptions = { |
1014 | model: Video['sequelize'].models.VideoChannel, | 1032 | model: Video['sequelize'].models.VideoChannel, |
1015 | include: [ authorInclude ], | 1033 | include: [ accountInclude ], |
1016 | required: true | 1034 | required: true |
1017 | } | 1035 | } |
1018 | 1036 | ||
@@ -1045,8 +1063,8 @@ searchAndPopulateAuthorAndPodAndTags = function (value: string, field: string, s | |||
1045 | } | 1063 | } |
1046 | } | 1064 | } |
1047 | podInclude.required = true | 1065 | podInclude.required = true |
1048 | } else if (field === 'author') { | 1066 | } else if (field === 'account') { |
1049 | authorInclude.where = { | 1067 | accountInclude.where = { |
1050 | name: { | 1068 | name: { |
1051 | [Sequelize.Op.iLike]: '%' + value + '%' | 1069 | [Sequelize.Op.iLike]: '%' + value + '%' |
1052 | } | 1070 | } |
@@ -1090,13 +1108,17 @@ function getBaseUrls (video: VideoInstance) { | |||
1090 | baseUrlHttp = CONFIG.WEBSERVER.URL | 1108 | baseUrlHttp = CONFIG.WEBSERVER.URL |
1091 | baseUrlWs = CONFIG.WEBSERVER.WS + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT | 1109 | baseUrlWs = CONFIG.WEBSERVER.WS + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT |
1092 | } else { | 1110 | } else { |
1093 | baseUrlHttp = REMOTE_SCHEME.HTTP + '://' + video.VideoChannel.Author.Pod.host | 1111 | baseUrlHttp = REMOTE_SCHEME.HTTP + '://' + video.VideoChannel.Account.Pod.host |
1094 | baseUrlWs = REMOTE_SCHEME.WS + '://' + video.VideoChannel.Author.Pod.host | 1112 | baseUrlWs = REMOTE_SCHEME.WS + '://' + video.VideoChannel.Account.Pod.host |
1095 | } | 1113 | } |
1096 | 1114 | ||
1097 | return { baseUrlHttp, baseUrlWs } | 1115 | return { baseUrlHttp, baseUrlWs } |
1098 | } | 1116 | } |
1099 | 1117 | ||
1118 | function getThumbnailUrl (video: VideoInstance, baseUrlHttp: string) { | ||
1119 | return baseUrlHttp + STATIC_PATHS.THUMBNAILS + video.getThumbnailName() | ||
1120 | } | ||
1121 | |||
1100 | function getTorrentUrl (video: VideoInstance, videoFile: VideoFileInstance, baseUrlHttp: string) { | 1122 | function getTorrentUrl (video: VideoInstance, videoFile: VideoFileInstance, baseUrlHttp: string) { |
1101 | return baseUrlHttp + STATIC_PATHS.TORRENTS + video.getTorrentFileName(videoFile) | 1123 | return baseUrlHttp + STATIC_PATHS.TORRENTS + video.getTorrentFileName(videoFile) |
1102 | } | 1124 | } |