aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/models
diff options
context:
space:
mode:
Diffstat (limited to 'server/models')
-rw-r--r--server/models/account/account.ts26
-rw-r--r--server/models/account/user.ts3
-rw-r--r--server/models/activitypub/actor.ts9
-rw-r--r--server/models/server/server.ts13
-rw-r--r--server/models/video/video-channel.ts29
-rw-r--r--server/models/video/video-comment.ts15
-rw-r--r--server/models/video/video.ts194
7 files changed, 165 insertions, 124 deletions
diff --git a/server/models/account/account.ts b/server/models/account/account.ts
index f81c50180..20724ae0c 100644
--- a/server/models/account/account.ts
+++ b/server/models/account/account.ts
@@ -1,9 +1,10 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import { 2import {
3 AfterDestroy, AllowNull, BelongsTo, Column, CreatedAt, DefaultScope, ForeignKey, HasMany, Model, Table, 3 AllowNull, BeforeDestroy, BelongsTo, Column, CreatedAt, DefaultScope, ForeignKey, HasMany, Model, Table,
4 UpdatedAt 4 UpdatedAt
5} from 'sequelize-typescript' 5} from 'sequelize-typescript'
6import { Account } from '../../../shared/models/actors' 6import { Account } from '../../../shared/models/actors'
7import { logger } from '../../helpers/logger'
7import { sendDeleteActor } from '../../lib/activitypub/send' 8import { sendDeleteActor } from '../../lib/activitypub/send'
8import { ActorModel } from '../activitypub/actor' 9import { ActorModel } from '../activitypub/actor'
9import { ApplicationModel } from '../application/application' 10import { ApplicationModel } from '../application/application'
@@ -11,6 +12,7 @@ import { AvatarModel } from '../avatar/avatar'
11import { ServerModel } from '../server/server' 12import { ServerModel } from '../server/server'
12import { getSort } from '../utils' 13import { getSort } from '../utils'
13import { VideoChannelModel } from '../video/video-channel' 14import { VideoChannelModel } from '../video/video-channel'
15import { VideoCommentModel } from '../video/video-comment'
14import { UserModel } from './user' 16import { UserModel } from './user'
15 17
16@DefaultScope({ 18@DefaultScope({
@@ -80,7 +82,7 @@ export class AccountModel extends Model<AccountModel> {
80 }, 82 },
81 onDelete: 'cascade' 83 onDelete: 'cascade'
82 }) 84 })
83 Account: ApplicationModel 85 Application: ApplicationModel
84 86
85 @HasMany(() => VideoChannelModel, { 87 @HasMany(() => VideoChannelModel, {
86 foreignKey: { 88 foreignKey: {
@@ -91,10 +93,24 @@ export class AccountModel extends Model<AccountModel> {
91 }) 93 })
92 VideoChannels: VideoChannelModel[] 94 VideoChannels: VideoChannelModel[]
93 95
94 @AfterDestroy 96 @HasMany(() => VideoCommentModel, {
95 static sendDeleteIfOwned (instance: AccountModel) { 97 foreignKey: {
98 allowNull: false
99 },
100 onDelete: 'cascade',
101 hooks: true
102 })
103 VideoComments: VideoCommentModel[]
104
105 @BeforeDestroy
106 static async sendDeleteIfOwned (instance: AccountModel, options) {
107 if (!instance.Actor) {
108 instance.Actor = await instance.$get('Actor', { transaction: options.transaction }) as ActorModel
109 }
110
96 if (instance.isOwned()) { 111 if (instance.isOwned()) {
97 return sendDeleteActor(instance.Actor, undefined) 112 logger.debug('Sending delete of actor of account %s.', instance.Actor.url)
113 return sendDeleteActor(instance.Actor, options.transaction)
98 } 114 }
99 115
100 return undefined 116 return undefined
diff --git a/server/models/account/user.ts b/server/models/account/user.ts
index e37fd4d3b..8eb88062a 100644
--- a/server/models/account/user.ts
+++ b/server/models/account/user.ts
@@ -94,7 +94,8 @@ export class UserModel extends Model<UserModel> {
94 94
95 @HasOne(() => AccountModel, { 95 @HasOne(() => AccountModel, {
96 foreignKey: 'userId', 96 foreignKey: 'userId',
97 onDelete: 'cascade' 97 onDelete: 'cascade',
98 hooks: true
98 }) 99 })
99 Account: AccountModel 100 Account: AccountModel
100 101
diff --git a/server/models/activitypub/actor.ts b/server/models/activitypub/actor.ts
index b7be9c32c..408d4df23 100644
--- a/server/models/activitypub/actor.ts
+++ b/server/models/activitypub/actor.ts
@@ -155,7 +155,8 @@ export class ActorModel extends Model<ActorModel> {
155 foreignKey: { 155 foreignKey: {
156 allowNull: true 156 allowNull: true
157 }, 157 },
158 onDelete: 'set null' 158 onDelete: 'set null',
159 hooks: true
159 }) 160 })
160 Avatar: AvatarModel 161 Avatar: AvatarModel
161 162
@@ -194,7 +195,8 @@ export class ActorModel extends Model<ActorModel> {
194 foreignKey: { 195 foreignKey: {
195 allowNull: true 196 allowNull: true
196 }, 197 },
197 onDelete: 'cascade' 198 onDelete: 'cascade',
199 hooks: true
198 }) 200 })
199 Account: AccountModel 201 Account: AccountModel
200 202
@@ -202,7 +204,8 @@ export class ActorModel extends Model<ActorModel> {
202 foreignKey: { 204 foreignKey: {
203 allowNull: true 205 allowNull: true
204 }, 206 },
205 onDelete: 'cascade' 207 onDelete: 'cascade',
208 hooks: true
206 }) 209 })
207 VideoChannel: VideoChannelModel 210 VideoChannel: VideoChannelModel
208 211
diff --git a/server/models/server/server.ts b/server/models/server/server.ts
index c43146156..9749f503e 100644
--- a/server/models/server/server.ts
+++ b/server/models/server/server.ts
@@ -1,5 +1,6 @@
1import { AllowNull, Column, CreatedAt, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' 1import { AllowNull, Column, CreatedAt, HasMany, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
2import { isHostValid } from '../../helpers/custom-validators/servers' 2import { isHostValid } from '../../helpers/custom-validators/servers'
3import { ActorModel } from '../activitypub/actor'
3import { throwIfNotValid } from '../utils' 4import { throwIfNotValid } from '../utils'
4 5
5@Table({ 6@Table({
@@ -23,4 +24,14 @@ export class ServerModel extends Model<ServerModel> {
23 24
24 @UpdatedAt 25 @UpdatedAt
25 updatedAt: Date 26 updatedAt: Date
27
28 @HasMany(() => ActorModel, {
29 foreignKey: {
30 name: 'serverId',
31 allowNull: true
32 },
33 onDelete: 'CASCADE',
34 hooks: true
35 })
36 Actors: ActorModel[]
26} 37}
diff --git a/server/models/video/video-channel.ts b/server/models/video/video-channel.ts
index e2cbf0422..7c161c864 100644
--- a/server/models/video/video-channel.ts
+++ b/server/models/video/video-channel.ts
@@ -1,20 +1,10 @@
1import { 1import {
2 AfterDestroy, 2 AllowNull, BeforeDestroy, BelongsTo, Column, CreatedAt, DefaultScope, ForeignKey, HasMany, Is, Model, Scopes, Table,
3 AllowNull,
4 BelongsTo,
5 Column,
6 CreatedAt,
7 DefaultScope,
8 ForeignKey,
9 HasMany,
10 Is,
11 Model,
12 Scopes,
13 Table,
14 UpdatedAt 3 UpdatedAt
15} from 'sequelize-typescript' 4} from 'sequelize-typescript'
16import { ActivityPubActor } from '../../../shared/models/activitypub' 5import { ActivityPubActor } from '../../../shared/models/activitypub'
17import { isVideoChannelDescriptionValid, isVideoChannelNameValid } from '../../helpers/custom-validators/video-channels' 6import { isVideoChannelDescriptionValid, isVideoChannelNameValid } from '../../helpers/custom-validators/video-channels'
7import { logger } from '../../helpers/logger'
18import { sendDeleteActor } from '../../lib/activitypub/send' 8import { sendDeleteActor } from '../../lib/activitypub/send'
19import { AccountModel } from '../account/account' 9import { AccountModel } from '../account/account'
20import { ActorModel } from '../activitypub/actor' 10import { ActorModel } from '../activitypub/actor'
@@ -116,14 +106,21 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
116 name: 'channelId', 106 name: 'channelId',
117 allowNull: false 107 allowNull: false
118 }, 108 },
119 onDelete: 'CASCADE' 109 onDelete: 'CASCADE',
110 hooks: true
120 }) 111 })
121 Videos: VideoModel[] 112 Videos: VideoModel[]
122 113
123 @AfterDestroy 114 @BeforeDestroy
124 static sendDeleteIfOwned (instance: VideoChannelModel) { 115 static async sendDeleteIfOwned (instance: VideoChannelModel, options) {
116 if (!instance.Actor) {
117 instance.Actor = await instance.$get('Actor', { transaction: options.transaction }) as ActorModel
118 }
119
125 if (instance.Actor.isOwned()) { 120 if (instance.Actor.isOwned()) {
126 return sendDeleteActor(instance.Actor, undefined) 121 logger.debug('Sending delete of actor of video channel %s.', instance.Actor.url)
122
123 return sendDeleteActor(instance.Actor, options.transaction)
127 } 124 }
128 125
129 return undefined 126 return undefined
diff --git a/server/models/video/video-comment.ts b/server/models/video/video-comment.ts
index c10d7c7c8..ab909b0b8 100644
--- a/server/models/video/video-comment.ts
+++ b/server/models/video/video-comment.ts
@@ -1,6 +1,6 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import { 2import {
3 AfterDestroy, AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, IFindOptions, Is, Model, Scopes, Table, 3 AllowNull, BeforeDestroy, BelongsTo, Column, CreatedAt, DataType, ForeignKey, IFindOptions, Is, Model, Scopes, Table,
4 UpdatedAt 4 UpdatedAt
5} from 'sequelize-typescript' 5} from 'sequelize-typescript'
6import { ActivityTagObject } from '../../../shared/models/activitypub/objects/common-objects' 6import { ActivityTagObject } from '../../../shared/models/activitypub/objects/common-objects'
@@ -175,10 +175,17 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
175 }) 175 })
176 Account: AccountModel 176 Account: AccountModel
177 177
178 @AfterDestroy 178 @BeforeDestroy
179 static async sendDeleteIfOwned (instance: VideoCommentModel) { 179 static async sendDeleteIfOwned (instance: VideoCommentModel, options) {
180 if (!instance.Account || !instance.Account.Actor) {
181 instance.Account = await instance.$get('Account', {
182 include: [ ActorModel ],
183 transaction: options.transaction
184 }) as AccountModel
185 }
186
180 if (instance.isOwned()) { 187 if (instance.isOwned()) {
181 await sendDeleteVideoComment(instance, undefined) 188 await sendDeleteVideoComment(instance, options.transaction)
182 } 189 }
183 } 190 }
184 191
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index 3e2b4ce64..514edfd9c 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -5,10 +5,9 @@ import * as parseTorrent from 'parse-torrent'
5import { join } from 'path' 5import { join } from 'path'
6import * as Sequelize from 'sequelize' 6import * as Sequelize from 'sequelize'
7import { 7import {
8 AfterDestroy, AllowNull, BelongsTo, BelongsToMany, Column, CreatedAt, DataType, Default, ForeignKey, HasMany, IFindOptions, Is, 8 AfterDestroy, AllowNull, BeforeDestroy, BelongsTo, BelongsToMany, Column, CreatedAt, DataType, Default, ForeignKey, HasMany,
9 IsInt, IsUUID, Min, Model, Scopes, Table, UpdatedAt 9 IFindOptions, Is, IsInt, IsUUID, Min, Model, Scopes, Table, UpdatedAt
10} from 'sequelize-typescript' 10} from 'sequelize-typescript'
11import { IIncludeOptions } from 'sequelize-typescript/lib/interfaces/IIncludeOptions'
12import { VideoPrivacy, VideoResolution } from '../../../shared' 11import { VideoPrivacy, VideoResolution } from '../../../shared'
13import { VideoTorrentObject } from '../../../shared/models/activitypub/objects' 12import { VideoTorrentObject } from '../../../shared/models/activitypub/objects'
14import { Video, VideoDetails } from '../../../shared/models/videos' 13import { Video, VideoDetails } from '../../../shared/models/videos'
@@ -22,6 +21,7 @@ import {
22} from '../../helpers/custom-validators/videos' 21} from '../../helpers/custom-validators/videos'
23import { generateImageFromVideoFile, getVideoFileHeight, transcode } from '../../helpers/ffmpeg-utils' 22import { generateImageFromVideoFile, getVideoFileHeight, transcode } from '../../helpers/ffmpeg-utils'
24import { logger } from '../../helpers/logger' 23import { logger } from '../../helpers/logger'
24import { getServerActor } from '../../helpers/utils'
25import { 25import {
26 API_VERSION, CONFIG, CONSTRAINTS_FIELDS, PREVIEWS_SIZE, REMOTE_SCHEME, STATIC_PATHS, THUMBNAILS_SIZE, VIDEO_CATEGORIES, 26 API_VERSION, CONFIG, CONSTRAINTS_FIELDS, PREVIEWS_SIZE, REMOTE_SCHEME, STATIC_PATHS, THUMBNAILS_SIZE, VIDEO_CATEGORIES,
27 VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES 27 VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES
@@ -31,6 +31,7 @@ import { sendDeleteVideo } from '../../lib/activitypub/send'
31import { AccountModel } from '../account/account' 31import { AccountModel } from '../account/account'
32import { AccountVideoRateModel } from '../account/account-video-rate' 32import { AccountVideoRateModel } from '../account/account-video-rate'
33import { ActorModel } from '../activitypub/actor' 33import { ActorModel } from '../activitypub/actor'
34import { ActorFollowModel } from '../activitypub/actor-follow'
34import { ServerModel } from '../server/server' 35import { ServerModel } from '../server/server'
35import { getSort, throwIfNotValid } from '../utils' 36import { getSort, throwIfNotValid } from '../utils'
36import { TagModel } from './tag' 37import { TagModel } from './tag'
@@ -43,7 +44,6 @@ import { VideoTagModel } from './video-tag'
43 44
44enum ScopeNames { 45enum ScopeNames {
45 AVAILABLE_FOR_LIST = 'AVAILABLE_FOR_LIST', 46 AVAILABLE_FOR_LIST = 'AVAILABLE_FOR_LIST',
46 WITH_ACCOUNT_API = 'WITH_ACCOUNT_API',
47 WITH_ACCOUNT_DETAILS = 'WITH_ACCOUNT_DETAILS', 47 WITH_ACCOUNT_DETAILS = 'WITH_ACCOUNT_DETAILS',
48 WITH_TAGS = 'WITH_TAGS', 48 WITH_TAGS = 'WITH_TAGS',
49 WITH_FILES = 'WITH_FILES', 49 WITH_FILES = 'WITH_FILES',
@@ -53,34 +53,60 @@ enum ScopeNames {
53} 53}
54 54
55@Scopes({ 55@Scopes({
56 [ScopeNames.AVAILABLE_FOR_LIST]: { 56 [ScopeNames.AVAILABLE_FOR_LIST]: (actorId: number) => ({
57 subQuery: false,
57 where: { 58 where: {
58 id: { 59 id: {
59 [Sequelize.Op.notIn]: Sequelize.literal( 60 [Sequelize.Op.notIn]: Sequelize.literal(
60 '(SELECT "videoBlacklist"."videoId" FROM "videoBlacklist")' 61 '(SELECT "videoBlacklist"."videoId" FROM "videoBlacklist")'
61 ) 62 )
62 }, 63 },
63 privacy: VideoPrivacy.PUBLIC 64 privacy: VideoPrivacy.PUBLIC,
64 } 65 [Sequelize.Op.or]: [
65 }, 66 {
66 [ScopeNames.WITH_ACCOUNT_API]: { 67 '$VideoChannel.Account.Actor.serverId$': null
68 },
69 {
70 '$VideoChannel.Account.Actor.followers.actorId$': actorId
71 },
72 {
73 id: {
74 [ Sequelize.Op.in ]: Sequelize.literal(
75 '(' +
76 'SELECT "videoShare"."videoId" FROM "videoShare" ' +
77 'INNER JOIN "actorFollow" ON "actorFollow"."targetActorId" = "videoShare"."actorId" ' +
78 'WHERE "actorFollow"."actorId" = ' + parseInt(actorId.toString(), 10) +
79 ')'
80 )
81 }
82 }
83 ]
84 },
67 include: [ 85 include: [
68 { 86 {
69 model: () => VideoChannelModel.unscoped(), 87 attributes: [ 'name', 'description' ],
88 model: VideoChannelModel.unscoped(),
70 required: true, 89 required: true,
71 include: [ 90 include: [
72 { 91 {
73 attributes: [ 'name' ], 92 attributes: [ 'name' ],
74 model: () => AccountModel.unscoped(), 93 model: AccountModel.unscoped(),
75 required: true, 94 required: true,
76 include: [ 95 include: [
77 { 96 {
78 attributes: [ 'serverId' ], 97 attributes: [ 'serverId' ],
79 model: () => ActorModel.unscoped(), 98 model: ActorModel.unscoped(),
80 required: true, 99 required: true,
81 include: [ 100 include: [
82 { 101 {
83 model: () => ServerModel.unscoped(), 102 attributes: [ 'host' ],
103 model: ServerModel.unscoped(),
104 required: false
105 },
106 {
107 attributes: [ ],
108 model: ActorFollowModel.unscoped(),
109 as: 'followers',
84 required: false 110 required: false
85 } 111 }
86 ] 112 ]
@@ -90,7 +116,7 @@ enum ScopeNames {
90 ] 116 ]
91 } 117 }
92 ] 118 ]
93 }, 119 }),
94 [ScopeNames.WITH_ACCOUNT_DETAILS]: { 120 [ScopeNames.WITH_ACCOUNT_DETAILS]: {
95 include: [ 121 include: [
96 { 122 {
@@ -347,23 +373,46 @@ export class VideoModel extends Model<VideoModel> {
347 name: 'videoId', 373 name: 'videoId',
348 allowNull: false 374 allowNull: false
349 }, 375 },
350 onDelete: 'cascade' 376 onDelete: 'cascade',
377 hooks: true
351 }) 378 })
352 VideoComments: VideoCommentModel[] 379 VideoComments: VideoCommentModel[]
353 380
381 @BeforeDestroy
382 static async sendDelete (instance: VideoModel, options) {
383 if (instance.isOwned()) {
384 if (!instance.VideoChannel) {
385 instance.VideoChannel = await instance.$get('VideoChannel', {
386 include: [
387 {
388 model: AccountModel,
389 include: [ ActorModel ]
390 }
391 ],
392 transaction: options.transaction
393 }) as VideoChannelModel
394 }
395
396 logger.debug('Sending delete of video %s.', instance.url)
397
398 return sendDeleteVideo(instance, options.transaction)
399 }
400
401 return undefined
402 }
403
354 @AfterDestroy 404 @AfterDestroy
355 static removeFilesAndSendDelete (instance: VideoModel) { 405 static async removeFilesAndSendDelete (instance: VideoModel) {
356 const tasks = [] 406 const tasks: Promise<any>[] = []
357 407
358 tasks.push( 408 tasks.push(instance.removeThumbnail())
359 instance.removeThumbnail()
360 )
361 409
362 if (instance.isOwned()) { 410 if (instance.isOwned()) {
363 tasks.push( 411 if (!Array.isArray(instance.VideoFiles)) {
364 instance.removePreview(), 412 instance.VideoFiles = await instance.$get('VideoFiles') as VideoFileModel[]
365 sendDeleteVideo(instance, undefined) 413 }
366 ) 414
415 tasks.push(instance.removePreview())
367 416
368 // Remove physical files and torrents 417 // Remove physical files and torrents
369 instance.VideoFiles.forEach(file => { 418 instance.VideoFiles.forEach(file => {
@@ -500,14 +549,16 @@ export class VideoModel extends Model<VideoModel> {
500 }) 549 })
501 } 550 }
502 551
503 static listForApi (start: number, count: number, sort: string) { 552 static async listForApi (start: number, count: number, sort: string) {
504 const query = { 553 const query = {
505 offset: start, 554 offset: start,
506 limit: count, 555 limit: count,
507 order: [ getSort(sort) ] 556 order: [ getSort(sort) ]
508 } 557 }
509 558
510 return VideoModel.scope([ ScopeNames.AVAILABLE_FOR_LIST, ScopeNames.WITH_ACCOUNT_API ]) 559 const serverActor = await getServerActor()
560
561 return VideoModel.scope({ method: [ ScopeNames.AVAILABLE_FOR_LIST, serverActor.id ] })
511 .findAndCountAll(query) 562 .findAndCountAll(query)
512 .then(({ rows, count }) => { 563 .then(({ rows, count }) => {
513 return { 564 return {
@@ -517,6 +568,29 @@ export class VideoModel extends Model<VideoModel> {
517 }) 568 })
518 } 569 }
519 570
571 static async searchAndPopulateAccountAndServerAndTags (value: string, start: number, count: number, sort: string) {
572 const query: IFindOptions<VideoModel> = {
573 offset: start,
574 limit: count,
575 order: [ getSort(sort) ],
576 where: {
577 name: {
578 [Sequelize.Op.iLike]: '%' + value + '%'
579 }
580 }
581 }
582
583 const serverActor = await getServerActor()
584
585 return VideoModel.scope({ method: [ ScopeNames.AVAILABLE_FOR_LIST, serverActor.id ] })
586 .findAndCountAll(query).then(({ rows, count }) => {
587 return {
588 data: rows,
589 total: count
590 }
591 })
592 }
593
520 static load (id: number) { 594 static load (id: number) {
521 return VideoModel.findById(id) 595 return VideoModel.findById(id)
522 } 596 }
@@ -603,74 +677,6 @@ export class VideoModel extends Model<VideoModel> {
603 .findOne(options) 677 .findOne(options)
604 } 678 }
605 679
606 static searchAndPopulateAccountAndServerAndTags (value: string, start: number, count: number, sort: string) {
607 const serverInclude: IIncludeOptions = {
608 model: ServerModel,
609 required: false
610 }
611
612 const accountInclude: IIncludeOptions = {
613 model: AccountModel,
614 include: [
615 {
616 model: ActorModel,
617 required: true,
618 include: [ serverInclude ]
619 }
620 ]
621 }
622
623 const videoChannelInclude: IIncludeOptions = {
624 model: VideoChannelModel,
625 include: [ accountInclude ],
626 required: true
627 }
628
629 const tagInclude: IIncludeOptions = {
630 model: TagModel
631 }
632
633 const query: IFindOptions<VideoModel> = {
634 distinct: true, // Because we have tags
635 offset: start,
636 limit: count,
637 order: [ getSort(sort) ],
638 where: {}
639 }
640
641 // TODO: search on tags too
642 // const escapedValue = Video['sequelize'].escape('%' + value + '%')
643 // query.where['id'][Sequelize.Op.in] = Video['sequelize'].literal(
644 // `(SELECT "VideoTags"."videoId"
645 // FROM "Tags"
646 // INNER JOIN "VideoTags" ON "Tags"."id" = "VideoTags"."tagId"
647 // WHERE name ILIKE ${escapedValue}
648 // )`
649 // )
650
651 // TODO: search on account too
652 // accountInclude.where = {
653 // name: {
654 // [Sequelize.Op.iLike]: '%' + value + '%'
655 // }
656 // }
657 query.where['name'] = {
658 [Sequelize.Op.iLike]: '%' + value + '%'
659 }
660
661 query.include = [
662 videoChannelInclude, tagInclude
663 ]
664
665 return VideoModel.scope([ ScopeNames.AVAILABLE_FOR_LIST ])
666 .findAndCountAll(query).then(({ rows, count }) => {
667 return {
668 data: rows,
669 total: count
670 }
671 })
672 }
673
674 getOriginalFile () { 680 getOriginalFile () {
675 if (Array.isArray(this.VideoFiles) === false) return undefined 681 if (Array.isArray(this.VideoFiles) === false) return undefined
676 682