aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/models
diff options
context:
space:
mode:
Diffstat (limited to 'server/models')
-rw-r--r--server/models/account/account.ts1
-rw-r--r--server/models/activitypub/actor.ts7
-rw-r--r--server/models/video/video-import.ts175
-rw-r--r--server/models/video/video.ts19
4 files changed, 195 insertions, 7 deletions
diff --git a/server/models/account/account.ts b/server/models/account/account.ts
index d674d8d22..66f5dcf2e 100644
--- a/server/models/account/account.ts
+++ b/server/models/account/account.ts
@@ -16,7 +16,6 @@ import {
16} from 'sequelize-typescript' 16} from 'sequelize-typescript'
17import { Account } from '../../../shared/models/actors' 17import { Account } from '../../../shared/models/actors'
18import { isAccountDescriptionValid } from '../../helpers/custom-validators/accounts' 18import { isAccountDescriptionValid } from '../../helpers/custom-validators/accounts'
19import { logger } from '../../helpers/logger'
20import { sendDeleteActor } from '../../lib/activitypub/send' 19import { sendDeleteActor } from '../../lib/activitypub/send'
21import { ActorModel } from '../activitypub/actor' 20import { ActorModel } from '../activitypub/actor'
22import { ApplicationModel } from '../application/application' 21import { ApplicationModel } from '../application/application'
diff --git a/server/models/activitypub/actor.ts b/server/models/activitypub/actor.ts
index 267032e2a..35d7c35e8 100644
--- a/server/models/activitypub/actor.ts
+++ b/server/models/activitypub/actor.ts
@@ -91,6 +91,9 @@ enum ScopeNames {
91 fields: [ 'inboxUrl', 'sharedInboxUrl' ] 91 fields: [ 'inboxUrl', 'sharedInboxUrl' ]
92 }, 92 },
93 { 93 {
94 fields: [ 'sharedInboxUrl' ]
95 },
96 {
94 fields: [ 'serverId' ] 97 fields: [ 'serverId' ]
95 }, 98 },
96 { 99 {
@@ -454,6 +457,10 @@ export class ActorModel extends Model<ActorModel> {
454 return 'acct:' + this.preferredUsername + '@' + this.getHost() 457 return 'acct:' + this.preferredUsername + '@' + this.getHost()
455 } 458 }
456 459
460 getIdentifier () {
461 return this.Server ? `${this.preferredUsername}@${this.Server.host}` : this.preferredUsername
462 }
463
457 getHost () { 464 getHost () {
458 return this.Server ? this.Server.host : CONFIG.WEBSERVER.HOST 465 return this.Server ? this.Server.host : CONFIG.WEBSERVER.HOST
459 } 466 }
diff --git a/server/models/video/video-import.ts b/server/models/video/video-import.ts
new file mode 100644
index 000000000..eca87163d
--- /dev/null
+++ b/server/models/video/video-import.ts
@@ -0,0 +1,175 @@
1import {
2 AfterUpdate,
3 AllowNull,
4 BelongsTo,
5 Column,
6 CreatedAt,
7 DataType,
8 Default,
9 DefaultScope,
10 ForeignKey,
11 Is,
12 Model,
13 Table,
14 UpdatedAt
15} from 'sequelize-typescript'
16import { CONSTRAINTS_FIELDS, VIDEO_IMPORT_STATES } from '../../initializers'
17import { getSort, throwIfNotValid } from '../utils'
18import { VideoModel } from './video'
19import { isVideoImportStateValid, isVideoImportTargetUrlValid } from '../../helpers/custom-validators/video-imports'
20import { VideoImport, VideoImportState } from '../../../shared'
21import { VideoChannelModel } from './video-channel'
22import { AccountModel } from '../account/account'
23import { TagModel } from './tag'
24
25@DefaultScope({
26 include: [
27 {
28 model: () => VideoModel,
29 required: false,
30 include: [
31 {
32 model: () => VideoChannelModel,
33 required: true,
34 include: [
35 {
36 model: () => AccountModel,
37 required: true
38 }
39 ]
40 },
41 {
42 model: () => TagModel
43 }
44 ]
45 }
46 ]
47})
48
49@Table({
50 tableName: 'videoImport',
51 indexes: [
52 {
53 fields: [ 'videoId' ],
54 unique: true
55 }
56 ]
57})
58export class VideoImportModel extends Model<VideoImportModel> {
59 @CreatedAt
60 createdAt: Date
61
62 @UpdatedAt
63 updatedAt: Date
64
65 @AllowNull(false)
66 @Is('VideoImportTargetUrl', value => throwIfNotValid(value, isVideoImportTargetUrlValid, 'targetUrl'))
67 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_IMPORTS.URL.max))
68 targetUrl: string
69
70 @AllowNull(false)
71 @Default(null)
72 @Is('VideoImportState', value => throwIfNotValid(value, isVideoImportStateValid, 'state'))
73 @Column
74 state: VideoImportState
75
76 @AllowNull(true)
77 @Default(null)
78 @Column(DataType.TEXT)
79 error: string
80
81 @ForeignKey(() => VideoModel)
82 @Column
83 videoId: number
84
85 @BelongsTo(() => VideoModel, {
86 foreignKey: {
87 allowNull: true
88 },
89 onDelete: 'set null'
90 })
91 Video: VideoModel
92
93 @AfterUpdate
94 static deleteVideoIfFailed (instance: VideoImportModel, options) {
95 if (instance.state === VideoImportState.FAILED) {
96 return instance.Video.destroy({ transaction: options.transaction })
97 }
98
99 return undefined
100 }
101
102 static loadAndPopulateVideo (id: number) {
103 return VideoImportModel.findById(id)
104 }
105
106 static listUserVideoImportsForApi (accountId: number, start: number, count: number, sort: string) {
107 const query = {
108 distinct: true,
109 offset: start,
110 limit: count,
111 order: getSort(sort),
112 include: [
113 {
114 model: VideoModel,
115 required: false,
116 include: [
117 {
118 model: VideoChannelModel,
119 required: true,
120 include: [
121 {
122 model: AccountModel,
123 required: true,
124 where: {
125 id: accountId
126 }
127 }
128 ]
129 },
130 {
131 model: TagModel,
132 required: false
133 }
134 ]
135 }
136 ]
137 }
138
139 return VideoImportModel.unscoped()
140 .findAndCountAll(query)
141 .then(({ rows, count }) => {
142 return {
143 data: rows,
144 total: count
145 }
146 })
147 }
148
149 toFormattedJSON (): VideoImport {
150 const videoFormatOptions = {
151 additionalAttributes: { state: true, waitTranscoding: true, scheduledUpdate: true }
152 }
153 const video = this.Video
154 ? Object.assign(this.Video.toFormattedJSON(videoFormatOptions), {
155 tags: this.Video.Tags.map(t => t.name)
156 })
157 : undefined
158
159 return {
160 id: this.id,
161 targetUrl: this.targetUrl,
162 state: {
163 id: this.state,
164 label: VideoImportModel.getStateLabel(this.state)
165 },
166 error: this.error,
167 updatedAt: this.updatedAt.toISOString(),
168 createdAt: this.createdAt.toISOString(),
169 video
170 }
171 }
172 private static getStateLabel (id: number) {
173 return VIDEO_IMPORT_STATES[id] || 'Unknown'
174 }
175}
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index a6c4620b2..39fe21007 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -377,7 +377,7 @@ type AvailableForListOptions = {
377 include: [ 377 include: [
378 { 378 {
379 model: () => VideoFileModel.unscoped(), 379 model: () => VideoFileModel.unscoped(),
380 required: true 380 required: false
381 } 381 }
382 ] 382 ]
383 }, 383 },
@@ -957,8 +957,10 @@ export class VideoModel extends Model<VideoModel> {
957 }) 957 })
958 } 958 }
959 959
960 static load (id: number) { 960 static load (id: number, t?: Sequelize.Transaction) {
961 return VideoModel.findById(id) 961 const options = t ? { transaction: t } : undefined
962
963 return VideoModel.findById(id, options)
962 } 964 }
963 965
964 static loadByUrlAndPopulateAccount (url: string, t?: Sequelize.Transaction) { 966 static loadByUrlAndPopulateAccount (url: string, t?: Sequelize.Transaction) {
@@ -1353,7 +1355,8 @@ export class VideoModel extends Model<VideoModel> {
1353 mimeType: VIDEO_EXT_MIMETYPE[file.extname], 1355 mimeType: VIDEO_EXT_MIMETYPE[file.extname],
1354 href: this.getVideoFileUrl(file, baseUrlHttp), 1356 href: this.getVideoFileUrl(file, baseUrlHttp),
1355 width: file.resolution, 1357 width: file.resolution,
1356 size: file.size 1358 size: file.size,
1359 fps: file.fps
1357 }) 1360 })
1358 1361
1359 url.push({ 1362 url.push({
@@ -1569,21 +1572,25 @@ export class VideoModel extends Model<VideoModel> {
1569 removeThumbnail () { 1572 removeThumbnail () {
1570 const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, this.getThumbnailName()) 1573 const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, this.getThumbnailName())
1571 return unlinkPromise(thumbnailPath) 1574 return unlinkPromise(thumbnailPath)
1575 .catch(err => logger.warn('Cannot delete thumbnail %s.', thumbnailPath, { err }))
1572 } 1576 }
1573 1577
1574 removePreview () { 1578 removePreview () {
1575 // Same name than video thumbnail 1579 const previewPath = join(CONFIG.STORAGE.PREVIEWS_DIR + this.getPreviewName())
1576 return unlinkPromise(CONFIG.STORAGE.PREVIEWS_DIR + this.getPreviewName()) 1580 return unlinkPromise(previewPath)
1581 .catch(err => logger.warn('Cannot delete preview %s.', previewPath, { err }))
1577 } 1582 }
1578 1583
1579 removeFile (videoFile: VideoFileModel) { 1584 removeFile (videoFile: VideoFileModel) {
1580 const filePath = join(CONFIG.STORAGE.VIDEOS_DIR, this.getVideoFilename(videoFile)) 1585 const filePath = join(CONFIG.STORAGE.VIDEOS_DIR, this.getVideoFilename(videoFile))
1581 return unlinkPromise(filePath) 1586 return unlinkPromise(filePath)
1587 .catch(err => logger.warn('Cannot delete file %s.', filePath, { err }))
1582 } 1588 }
1583 1589
1584 removeTorrent (videoFile: VideoFileModel) { 1590 removeTorrent (videoFile: VideoFileModel) {
1585 const torrentPath = join(CONFIG.STORAGE.TORRENTS_DIR, this.getTorrentFileName(videoFile)) 1591 const torrentPath = join(CONFIG.STORAGE.TORRENTS_DIR, this.getTorrentFileName(videoFile))
1586 return unlinkPromise(torrentPath) 1592 return unlinkPromise(torrentPath)
1593 .catch(err => logger.warn('Cannot delete torrent %s.', torrentPath, { err }))
1587 } 1594 }
1588 1595
1589 getActivityStreamDuration () { 1596 getActivityStreamDuration () {