]>
Commit | Line | Data |
---|---|---|
3fd3ab2d | 1 | import { |
06a05d5f C |
2 | AllowNull, |
3 | BeforeDestroy, | |
4 | BelongsTo, | |
5 | Column, | |
6 | CreatedAt, | |
7 | DataType, | |
8 | Default, | |
9 | DefaultScope, | |
10 | ForeignKey, | |
6dd9de95 | 11 | HasMany, |
06a05d5f C |
12 | Is, |
13 | Model, | |
14 | Scopes, | |
f37dc0dd | 15 | Sequelize, |
06a05d5f C |
16 | Table, |
17 | UpdatedAt | |
3fd3ab2d | 18 | } from 'sequelize-typescript' |
50d6de9c | 19 | import { ActivityPubActor } from '../../../shared/models/activitypub' |
418d092a | 20 | import { VideoChannel, VideoChannelSummary } from '../../../shared/models/videos' |
2422c46b | 21 | import { |
06a05d5f C |
22 | isVideoChannelDescriptionValid, |
23 | isVideoChannelNameValid, | |
2422c46b C |
24 | isVideoChannelSupportValid |
25 | } from '../../helpers/custom-validators/video-channels' | |
c5a893d5 | 26 | import { sendDeleteActor } from '../../lib/activitypub/send' |
bfbd9128 | 27 | import { AccountModel, ScopeNames as AccountModelScopeNames, SummaryOptions as AccountSummaryOptions } from '../account/account' |
f37dc0dd | 28 | import { ActorModel, unusedActorAttributesForAPI } from '../activitypub/actor' |
418d092a | 29 | import { buildServerIdsFollowedBy, buildTrigramSearchIndex, createSimilarityAttribute, getSort, throwIfNotValid } from '../utils' |
3fd3ab2d | 30 | import { VideoModel } from './video' |
74dc3bca | 31 | import { CONSTRAINTS_FIELDS, WEBSERVER } from '../../initializers/constants' |
8a19bee1 | 32 | import { ServerModel } from '../server/server' |
8165d00a | 33 | import { FindOptions, Op, literal, ScopeOptions } from 'sequelize' |
418d092a C |
34 | import { AvatarModel } from '../avatar/avatar' |
35 | import { VideoPlaylistModel } from './video-playlist' | |
453e83ea C |
36 | import * as Bluebird from 'bluebird' |
37 | import { | |
38 | MChannelAccountDefault, | |
39 | MChannelActor, | |
b5fecbf4 C |
40 | MChannelActorAccountDefaultVideos, |
41 | MChannelAP, | |
42 | MChannelFormattable, | |
43 | MChannelSummaryFormattable | |
453e83ea | 44 | } from '../../typings/models/video' |
f37dc0dd | 45 | |
418d092a | 46 | export enum ScopeNames { |
453e83ea | 47 | FOR_API = 'FOR_API', |
8165d00a | 48 | SUMMARY = 'SUMMARY', |
d48ff09d | 49 | WITH_ACCOUNT = 'WITH_ACCOUNT', |
50d6de9c | 50 | WITH_ACTOR = 'WITH_ACTOR', |
418d092a | 51 | WITH_VIDEOS = 'WITH_VIDEOS', |
8165d00a | 52 | WITH_STATS = 'WITH_STATS' |
d48ff09d C |
53 | } |
54 | ||
f37dc0dd C |
55 | type AvailableForListOptions = { |
56 | actorId: number | |
57 | } | |
58 | ||
8165d00a RK |
59 | type AvailableWithStatsOptions = { |
60 | daysPrior: number | |
61 | } | |
62 | ||
bfbd9128 C |
63 | export type SummaryOptions = { |
64 | withAccount?: boolean // Default: false | |
65 | withAccountBlockerIds?: number[] | |
66 | } | |
67 | ||
3acc5084 | 68 | @DefaultScope(() => ({ |
50d6de9c C |
69 | include: [ |
70 | { | |
3acc5084 | 71 | model: ActorModel, |
50d6de9c C |
72 | required: true |
73 | } | |
74 | ] | |
3acc5084 C |
75 | })) |
76 | @Scopes(() => ({ | |
453e83ea | 77 | [ScopeNames.FOR_API]: (options: AvailableForListOptions) => { |
f37dc0dd | 78 | // Only list local channels OR channels that are on an instance followed by actorId |
418d092a | 79 | const inQueryInstanceFollow = buildServerIdsFollowedBy(options.actorId) |
f37dc0dd C |
80 | |
81 | return { | |
82 | include: [ | |
83 | { | |
84 | attributes: { | |
85 | exclude: unusedActorAttributesForAPI | |
86 | }, | |
87 | model: ActorModel, | |
88 | where: { | |
1735c825 | 89 | [Op.or]: [ |
c305467c | 90 | { |
f37dc0dd C |
91 | serverId: null |
92 | }, | |
93 | { | |
94 | serverId: { | |
a1587156 | 95 | [Op.in]: Sequelize.literal(inQueryInstanceFollow) |
f37dc0dd | 96 | } |
c305467c C |
97 | } |
98 | ] | |
50d6de9c | 99 | } |
f37dc0dd C |
100 | }, |
101 | { | |
102 | model: AccountModel, | |
103 | required: true, | |
104 | include: [ | |
105 | { | |
106 | attributes: { | |
107 | exclude: unusedActorAttributesForAPI | |
108 | }, | |
109 | model: ActorModel, // Default scope includes avatar and server | |
110 | required: true | |
111 | } | |
112 | ] | |
113 | } | |
114 | ] | |
115 | } | |
116 | }, | |
8165d00a RK |
117 | [ScopeNames.SUMMARY]: (options: SummaryOptions = {}) => { |
118 | const base: FindOptions = { | |
119 | attributes: [ 'id', 'name', 'description', 'actorId' ], | |
120 | include: [ | |
121 | { | |
122 | attributes: [ 'id', 'preferredUsername', 'url', 'serverId', 'avatarId' ], | |
123 | model: ActorModel.unscoped(), | |
124 | required: true, | |
125 | include: [ | |
126 | { | |
127 | attributes: [ 'host' ], | |
128 | model: ServerModel.unscoped(), | |
129 | required: false | |
130 | }, | |
131 | { | |
132 | model: AvatarModel.unscoped(), | |
133 | required: false | |
134 | } | |
135 | ] | |
136 | } | |
137 | ] | |
138 | } | |
139 | ||
140 | if (options.withAccount === true) { | |
141 | base.include.push({ | |
142 | model: AccountModel.scope({ | |
143 | method: [ AccountModelScopeNames.SUMMARY, { withAccountBlockerIds: options.withAccountBlockerIds } as AccountSummaryOptions ] | |
144 | }), | |
145 | required: true | |
146 | }) | |
147 | } | |
148 | ||
149 | return base | |
150 | }, | |
f37dc0dd C |
151 | [ScopeNames.WITH_ACCOUNT]: { |
152 | include: [ | |
153 | { | |
3acc5084 | 154 | model: AccountModel, |
f37dc0dd | 155 | required: true |
d48ff09d C |
156 | } |
157 | ] | |
158 | }, | |
8165d00a | 159 | [ScopeNames.WITH_ACTOR]: { |
d48ff09d | 160 | include: [ |
8165d00a | 161 | ActorModel |
d48ff09d | 162 | ] |
50d6de9c | 163 | }, |
8165d00a | 164 | [ScopeNames.WITH_VIDEOS]: { |
50d6de9c | 165 | include: [ |
8165d00a | 166 | VideoModel |
50d6de9c | 167 | ] |
8165d00a | 168 | }, |
3d527ba1 RK |
169 | [ScopeNames.WITH_STATS]: (options: AvailableWithStatsOptions = { daysPrior: 30 }) => { |
170 | const daysPrior = parseInt(options.daysPrior + '', 10) | |
171 | ||
172 | return { | |
173 | attributes: { | |
174 | include: [ | |
1ba471c5 C |
175 | [ |
176 | literal('(SELECT COUNT(*) FROM "video" WHERE "channelId" = "VideoChannelModel"."id")'), | |
177 | 'videosCount' | |
178 | ], | |
3d527ba1 RK |
179 | [ |
180 | literal( | |
181 | '(' + | |
182 | `SELECT string_agg(concat_ws('|', t.day, t.views), ',') ` + | |
183 | 'FROM ( ' + | |
184 | 'WITH ' + | |
185 | 'days AS ( ' + | |
186 | `SELECT generate_series(date_trunc('day', now()) - '${daysPrior} day'::interval, ` + | |
187 | `date_trunc('day', now()), '1 day'::interval) AS day ` + | |
8165d00a | 188 | ') ' + |
5a61ffbb C |
189 | 'SELECT days.day AS day, COALESCE(SUM("videoView".views), 0) AS views ' + |
190 | 'FROM days ' + | |
191 | 'LEFT JOIN (' + | |
192 | '"videoView" INNER JOIN "video" ON "videoView"."videoId" = "video"."id" ' + | |
193 | 'AND "video"."channelId" = "VideoChannelModel"."id"' + | |
194 | `) ON date_trunc('day', "videoView"."startDate") = date_trunc('day', days.day) ` + | |
195 | 'GROUP BY day ' + | |
196 | 'ORDER BY day ' + | |
197 | ') t' + | |
3d527ba1 RK |
198 | ')' |
199 | ), | |
200 | 'viewsPerDay' | |
201 | ] | |
8165d00a | 202 | ] |
3d527ba1 | 203 | } |
8165d00a | 204 | } |
3d527ba1 | 205 | } |
3acc5084 | 206 | })) |
3fd3ab2d C |
207 | @Table({ |
208 | tableName: 'videoChannel', | |
0374b6b5 C |
209 | indexes: [ |
210 | buildTrigramSearchIndex('video_channel_name_trigram', 'name'), | |
211 | ||
212 | { | |
213 | fields: [ 'accountId' ] | |
214 | }, | |
215 | { | |
216 | fields: [ 'actorId' ] | |
217 | } | |
218 | ] | |
3fd3ab2d C |
219 | }) |
220 | export class VideoChannelModel extends Model<VideoChannelModel> { | |
72c7248b | 221 | |
3fd3ab2d C |
222 | @AllowNull(false) |
223 | @Is('VideoChannelName', value => throwIfNotValid(value, isVideoChannelNameValid, 'name')) | |
224 | @Column | |
225 | name: string | |
72c7248b | 226 | |
3fd3ab2d | 227 | @AllowNull(true) |
2422c46b | 228 | @Default(null) |
1735c825 | 229 | @Is('VideoChannelDescription', value => throwIfNotValid(value, isVideoChannelDescriptionValid, 'description', true)) |
a10fc78b | 230 | @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_CHANNELS.DESCRIPTION.max)) |
3fd3ab2d | 231 | description: string |
72c7248b | 232 | |
2422c46b C |
233 | @AllowNull(true) |
234 | @Default(null) | |
1735c825 | 235 | @Is('VideoChannelSupport', value => throwIfNotValid(value, isVideoChannelSupportValid, 'support', true)) |
a10fc78b | 236 | @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_CHANNELS.SUPPORT.max)) |
2422c46b C |
237 | support: string |
238 | ||
3fd3ab2d C |
239 | @CreatedAt |
240 | createdAt: Date | |
72c7248b | 241 | |
3fd3ab2d C |
242 | @UpdatedAt |
243 | updatedAt: Date | |
4e50b6a1 | 244 | |
fadf619a C |
245 | @ForeignKey(() => ActorModel) |
246 | @Column | |
247 | actorId: number | |
248 | ||
249 | @BelongsTo(() => ActorModel, { | |
250 | foreignKey: { | |
251 | allowNull: false | |
252 | }, | |
253 | onDelete: 'cascade' | |
254 | }) | |
255 | Actor: ActorModel | |
256 | ||
3fd3ab2d C |
257 | @ForeignKey(() => AccountModel) |
258 | @Column | |
259 | accountId: number | |
4e50b6a1 | 260 | |
3fd3ab2d C |
261 | @BelongsTo(() => AccountModel, { |
262 | foreignKey: { | |
263 | allowNull: false | |
264 | }, | |
6b738c7a | 265 | hooks: true |
3fd3ab2d C |
266 | }) |
267 | Account: AccountModel | |
72c7248b | 268 | |
3fd3ab2d | 269 | @HasMany(() => VideoModel, { |
72c7248b | 270 | foreignKey: { |
3fd3ab2d | 271 | name: 'channelId', |
72c7248b C |
272 | allowNull: false |
273 | }, | |
f05a1c30 C |
274 | onDelete: 'CASCADE', |
275 | hooks: true | |
72c7248b | 276 | }) |
3fd3ab2d | 277 | Videos: VideoModel[] |
72c7248b | 278 | |
418d092a C |
279 | @HasMany(() => VideoPlaylistModel, { |
280 | foreignKey: { | |
07b1a18a | 281 | allowNull: true |
418d092a | 282 | }, |
df0b219d | 283 | onDelete: 'CASCADE', |
418d092a C |
284 | hooks: true |
285 | }) | |
286 | VideoPlaylists: VideoPlaylistModel[] | |
287 | ||
f05a1c30 C |
288 | @BeforeDestroy |
289 | static async sendDeleteIfOwned (instance: VideoChannelModel, options) { | |
290 | if (!instance.Actor) { | |
e6122097 | 291 | instance.Actor = await instance.$get('Actor', { transaction: options.transaction }) |
f05a1c30 C |
292 | } |
293 | ||
c5a893d5 | 294 | if (instance.Actor.isOwned()) { |
c5a893d5 C |
295 | return sendDeleteActor(instance.Actor, options.transaction) |
296 | } | |
297 | ||
298 | return undefined | |
3fd3ab2d | 299 | } |
72c7248b | 300 | |
3fd3ab2d C |
301 | static countByAccount (accountId: number) { |
302 | const query = { | |
303 | where: { | |
304 | accountId | |
305 | } | |
72c7248b | 306 | } |
3fd3ab2d C |
307 | |
308 | return VideoChannelModel.count(query) | |
72c7248b C |
309 | } |
310 | ||
f37dc0dd | 311 | static listForApi (actorId: number, start: number, count: number, sort: string) { |
3fd3ab2d C |
312 | const query = { |
313 | offset: start, | |
314 | limit: count, | |
3bb6c526 | 315 | order: getSort(sort) |
3fd3ab2d | 316 | } |
72c7248b | 317 | |
f37dc0dd | 318 | const scopes = { |
453e83ea | 319 | method: [ ScopeNames.FOR_API, { actorId } as AvailableForListOptions ] |
f37dc0dd | 320 | } |
50d6de9c | 321 | return VideoChannelModel |
f37dc0dd C |
322 | .scope(scopes) |
323 | .findAndCountAll(query) | |
324 | .then(({ rows, count }) => { | |
325 | return { total: count, data: rows } | |
326 | }) | |
327 | } | |
328 | ||
453e83ea | 329 | static listLocalsForSitemap (sort: string): Bluebird<MChannelActor[]> { |
2feebf3e C |
330 | const query = { |
331 | attributes: [ ], | |
332 | offset: 0, | |
333 | order: getSort(sort), | |
334 | include: [ | |
335 | { | |
336 | attributes: [ 'preferredUsername', 'serverId' ], | |
337 | model: ActorModel.unscoped(), | |
338 | where: { | |
339 | serverId: null | |
340 | } | |
341 | } | |
342 | ] | |
343 | } | |
344 | ||
345 | return VideoChannelModel | |
346 | .unscoped() | |
347 | .findAll(query) | |
348 | } | |
349 | ||
f37dc0dd C |
350 | static searchForApi (options: { |
351 | actorId: number | |
352 | search: string | |
353 | start: number | |
354 | count: number | |
355 | sort: string | |
356 | }) { | |
357 | const attributesInclude = [] | |
358 | const escapedSearch = VideoModel.sequelize.escape(options.search) | |
359 | const escapedLikeSearch = VideoModel.sequelize.escape('%' + options.search + '%') | |
360 | attributesInclude.push(createSimilarityAttribute('VideoChannelModel.name', options.search)) | |
361 | ||
362 | const query = { | |
363 | attributes: { | |
364 | include: attributesInclude | |
365 | }, | |
366 | offset: options.start, | |
367 | limit: options.count, | |
368 | order: getSort(options.sort), | |
369 | where: { | |
1735c825 | 370 | [Op.or]: [ |
c3c2ab1c C |
371 | Sequelize.literal( |
372 | 'lower(immutable_unaccent("VideoChannelModel"."name")) % lower(immutable_unaccent(' + escapedSearch + '))' | |
373 | ), | |
374 | Sequelize.literal( | |
375 | 'lower(immutable_unaccent("VideoChannelModel"."name")) LIKE lower(immutable_unaccent(' + escapedLikeSearch + '))' | |
f37dc0dd | 376 | ) |
c3c2ab1c | 377 | ] |
f37dc0dd C |
378 | } |
379 | } | |
380 | ||
381 | const scopes = { | |
453e83ea | 382 | method: [ ScopeNames.FOR_API, { actorId: options.actorId } as AvailableForListOptions ] |
f37dc0dd C |
383 | } |
384 | return VideoChannelModel | |
385 | .scope(scopes) | |
50d6de9c | 386 | .findAndCountAll(query) |
3fd3ab2d C |
387 | .then(({ rows, count }) => { |
388 | return { total: count, data: rows } | |
389 | }) | |
72c7248b C |
390 | } |
391 | ||
91b66319 | 392 | static listByAccount (options: { |
a1587156 C |
393 | accountId: number |
394 | start: number | |
395 | count: number | |
91b66319 | 396 | sort: string |
8165d00a | 397 | withStats?: boolean |
91b66319 | 398 | }) { |
3fd3ab2d | 399 | const query = { |
91b66319 C |
400 | offset: options.start, |
401 | limit: options.count, | |
402 | order: getSort(options.sort), | |
3fd3ab2d C |
403 | include: [ |
404 | { | |
405 | model: AccountModel, | |
406 | where: { | |
91b66319 | 407 | id: options.accountId |
3fd3ab2d | 408 | }, |
50d6de9c | 409 | required: true |
3fd3ab2d C |
410 | } |
411 | ] | |
412 | } | |
72c7248b | 413 | |
8165d00a RK |
414 | const scopes: string | ScopeOptions | (string | ScopeOptions)[] = [ ScopeNames.WITH_ACTOR ] |
415 | ||
5a61ffbb | 416 | if (options.withStats === true) { |
8165d00a RK |
417 | scopes.push({ |
418 | method: [ ScopeNames.WITH_STATS, { daysPrior: 30 } as AvailableWithStatsOptions ] | |
419 | }) | |
420 | } | |
421 | ||
50d6de9c | 422 | return VideoChannelModel |
8165d00a | 423 | .scope(scopes) |
50d6de9c | 424 | .findAndCountAll(query) |
3fd3ab2d C |
425 | .then(({ rows, count }) => { |
426 | return { total: count, data: rows } | |
427 | }) | |
72c7248b C |
428 | } |
429 | ||
0283eaac | 430 | static loadByIdAndPopulateAccount (id: number): Bluebird<MChannelAccountDefault> { |
5cf84858 C |
431 | return VideoChannelModel.unscoped() |
432 | .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ]) | |
9b39106d | 433 | .findByPk(id) |
5cf84858 C |
434 | } |
435 | ||
0283eaac | 436 | static loadByIdAndAccount (id: number, accountId: number): Bluebird<MChannelAccountDefault> { |
8a19bee1 | 437 | const query = { |
3fd3ab2d C |
438 | where: { |
439 | id, | |
440 | accountId | |
d48ff09d | 441 | } |
571389d4 | 442 | } |
3fd3ab2d | 443 | |
5cf84858 | 444 | return VideoChannelModel.unscoped() |
50d6de9c | 445 | .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ]) |
8a19bee1 | 446 | .findOne(query) |
0d0e8dd0 C |
447 | } |
448 | ||
0283eaac | 449 | static loadAndPopulateAccount (id: number): Bluebird<MChannelAccountDefault> { |
5cf84858 | 450 | return VideoChannelModel.unscoped() |
50d6de9c | 451 | .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ]) |
9b39106d | 452 | .findByPk(id) |
3fd3ab2d | 453 | } |
0d0e8dd0 | 454 | |
453e83ea | 455 | static loadByUrlAndPopulateAccount (url: string): Bluebird<MChannelAccountDefault> { |
f37dc0dd C |
456 | const query = { |
457 | include: [ | |
458 | { | |
459 | model: ActorModel, | |
460 | required: true, | |
461 | where: { | |
462 | url | |
463 | } | |
464 | } | |
465 | ] | |
466 | } | |
467 | ||
468 | return VideoChannelModel | |
469 | .scope([ ScopeNames.WITH_ACCOUNT ]) | |
8a19bee1 | 470 | .findOne(query) |
72c7248b C |
471 | } |
472 | ||
92bf2f62 C |
473 | static loadByNameWithHostAndPopulateAccount (nameWithHost: string) { |
474 | const [ name, host ] = nameWithHost.split('@') | |
475 | ||
6dd9de95 | 476 | if (!host || host === WEBSERVER.HOST) return VideoChannelModel.loadLocalByNameAndPopulateAccount(name) |
92bf2f62 C |
477 | |
478 | return VideoChannelModel.loadByNameAndHostAndPopulateAccount(name, host) | |
479 | } | |
480 | ||
0283eaac | 481 | static loadLocalByNameAndPopulateAccount (name: string): Bluebird<MChannelAccountDefault> { |
8a19bee1 | 482 | const query = { |
3fd3ab2d | 483 | include: [ |
8a19bee1 C |
484 | { |
485 | model: ActorModel, | |
486 | required: true, | |
487 | where: { | |
488 | preferredUsername: name, | |
489 | serverId: null | |
490 | } | |
491 | } | |
3fd3ab2d C |
492 | ] |
493 | } | |
72c7248b | 494 | |
5cf84858 | 495 | return VideoChannelModel.unscoped() |
8a19bee1 C |
496 | .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ]) |
497 | .findOne(query) | |
72c7248b C |
498 | } |
499 | ||
0283eaac | 500 | static loadByNameAndHostAndPopulateAccount (name: string, host: string): Bluebird<MChannelAccountDefault> { |
06a05d5f C |
501 | const query = { |
502 | include: [ | |
503 | { | |
504 | model: ActorModel, | |
505 | required: true, | |
506 | where: { | |
8a19bee1 C |
507 | preferredUsername: name |
508 | }, | |
509 | include: [ | |
510 | { | |
511 | model: ServerModel, | |
512 | required: true, | |
513 | where: { host } | |
514 | } | |
515 | ] | |
06a05d5f C |
516 | } |
517 | ] | |
518 | } | |
519 | ||
5cf84858 | 520 | return VideoChannelModel.unscoped() |
8a19bee1 C |
521 | .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ]) |
522 | .findOne(query) | |
523 | } | |
524 | ||
453e83ea | 525 | static loadAndPopulateAccountAndVideos (id: number): Bluebird<MChannelActorAccountDefaultVideos> { |
8a19bee1 C |
526 | const options = { |
527 | include: [ | |
528 | VideoModel | |
529 | ] | |
530 | } | |
531 | ||
5cf84858 | 532 | return VideoChannelModel.unscoped() |
8a19bee1 | 533 | .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT, ScopeNames.WITH_VIDEOS ]) |
9b39106d | 534 | .findByPk(id, options) |
06a05d5f C |
535 | } |
536 | ||
1ca9f7c3 C |
537 | toFormattedSummaryJSON (this: MChannelSummaryFormattable): VideoChannelSummary { |
538 | const actor = this.Actor.toFormattedSummaryJSON() | |
539 | ||
540 | return { | |
541 | id: this.id, | |
542 | name: actor.name, | |
543 | displayName: this.getDisplayName(), | |
544 | url: actor.url, | |
545 | host: actor.host, | |
546 | avatar: actor.avatar | |
547 | } | |
548 | } | |
549 | ||
550 | toFormattedJSON (this: MChannelFormattable): VideoChannel { | |
1ba471c5 C |
551 | const viewsPerDayString = this.get('viewsPerDay') as string |
552 | const videosCount = this.get('videosCount') as number | |
553 | ||
554 | let viewsPerDay: { date: Date, views: number }[] | |
555 | ||
556 | if (viewsPerDayString) { | |
557 | viewsPerDay = viewsPerDayString.split(',') | |
558 | .map(v => { | |
559 | const [ dateString, amount ] = v.split('|') | |
560 | ||
561 | return { | |
562 | date: new Date(dateString), | |
563 | views: +amount | |
564 | } | |
565 | }) | |
566 | } | |
8165d00a | 567 | |
50d6de9c | 568 | const actor = this.Actor.toFormattedJSON() |
6b738c7a | 569 | const videoChannel = { |
3fd3ab2d | 570 | id: this.id, |
749c7247 | 571 | displayName: this.getDisplayName(), |
3fd3ab2d | 572 | description: this.description, |
2422c46b | 573 | support: this.support, |
50d6de9c | 574 | isLocal: this.Actor.isOwned(), |
3fd3ab2d | 575 | createdAt: this.createdAt, |
6b738c7a | 576 | updatedAt: this.updatedAt, |
8165d00a | 577 | ownerAccount: undefined, |
1ba471c5 C |
578 | videosCount, |
579 | viewsPerDay | |
6b738c7a C |
580 | } |
581 | ||
a4f99a76 | 582 | if (this.Account) videoChannel.ownerAccount = this.Account.toFormattedJSON() |
72c7248b | 583 | |
6b738c7a | 584 | return Object.assign(actor, videoChannel) |
72c7248b C |
585 | } |
586 | ||
b5fecbf4 | 587 | toActivityPubObject (this: MChannelAP): ActivityPubActor { |
8424c402 | 588 | const obj = this.Actor.toActivityPubObject(this.name) |
50d6de9c C |
589 | |
590 | return Object.assign(obj, { | |
591 | summary: this.description, | |
2422c46b | 592 | support: this.support, |
50d6de9c C |
593 | attributedTo: [ |
594 | { | |
595 | type: 'Person' as 'Person', | |
596 | id: this.Account.Actor.url | |
597 | } | |
598 | ] | |
599 | }) | |
72c7248b | 600 | } |
749c7247 C |
601 | |
602 | getDisplayName () { | |
603 | return this.name | |
604 | } | |
744d0eca C |
605 | |
606 | isOutdated () { | |
607 | return this.Actor.isOutdated() | |
608 | } | |
72c7248b | 609 | } |