]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/models/video/video-share.ts
Fix live ending banner
[github/Chocobozzz/PeerTube.git] / server / models / video / video-share.ts
CommitLineData
74d249bc 1import { literal, Op, QueryTypes, Transaction } from 'sequelize'
4ba3b8ea
C
2import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript'
3import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
74dc3bca 4import { CONSTRAINTS_FIELDS } from '../../initializers/constants'
b49f22d8
C
5import { MActorDefault } from '../../types/models'
6import { MVideoShareActor, MVideoShareFull } from '../../types/models/video'
50d6de9c 7import { ActorModel } from '../activitypub/actor'
6b9c966f 8import { buildLocalActorIdsIn, throwIfNotValid } from '../utils'
3fd3ab2d 9import { VideoModel } from './video'
d8465018 10
d48ff09d
C
11enum ScopeNames {
12 FULL = 'FULL',
50d6de9c 13 WITH_ACTOR = 'WITH_ACTOR'
d48ff09d
C
14}
15
3acc5084 16@Scopes(() => ({
d48ff09d
C
17 [ScopeNames.FULL]: {
18 include: [
19 {
3acc5084 20 model: ActorModel,
d48ff09d
C
21 required: true
22 },
23 {
3acc5084 24 model: VideoModel,
d48ff09d
C
25 required: true
26 }
27 ]
28 },
50d6de9c 29 [ScopeNames.WITH_ACTOR]: {
d48ff09d
C
30 include: [
31 {
3acc5084 32 model: ActorModel,
d48ff09d
C
33 required: true
34 }
35 ]
36 }
3acc5084 37}))
3fd3ab2d
C
38@Table({
39 tableName: 'videoShare',
40 indexes: [
d8465018 41 {
50d6de9c 42 fields: [ 'actorId' ]
3fd3ab2d
C
43 },
44 {
45 fields: [ 'videoId' ]
4ba3b8ea
C
46 },
47 {
48 fields: [ 'url' ],
49 unique: true
d8465018 50 }
d8465018 51 ]
3fd3ab2d 52})
b49f22d8 53export class VideoShareModel extends Model {
4ba3b8ea
C
54
55 @AllowNull(false)
56 @Is('VideoShareUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'url'))
57 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_SHARE.URL.max))
58 url: string
59
3fd3ab2d
C
60 @CreatedAt
61 createdAt: Date
d8465018 62
3fd3ab2d
C
63 @UpdatedAt
64 updatedAt: Date
d8465018 65
50d6de9c 66 @ForeignKey(() => ActorModel)
3fd3ab2d 67 @Column
50d6de9c 68 actorId: number
d8465018 69
50d6de9c 70 @BelongsTo(() => ActorModel, {
d8465018 71 foreignKey: {
d8465018
C
72 allowNull: false
73 },
74 onDelete: 'cascade'
75 })
50d6de9c 76 Actor: ActorModel
d8465018 77
3fd3ab2d
C
78 @ForeignKey(() => VideoModel)
79 @Column
80 videoId: number
81
82 @BelongsTo(() => VideoModel, {
d8465018 83 foreignKey: {
3fd3ab2d 84 allowNull: false
d8465018
C
85 },
86 onDelete: 'cascade'
87 })
3fd3ab2d 88 Video: VideoModel
4e50b6a1 89
b49f22d8 90 static load (actorId: number | string, videoId: number | string, t?: Transaction): Promise<MVideoShareActor> {
50d6de9c 91 return VideoShareModel.scope(ScopeNames.WITH_ACTOR).findOne({
3fd3ab2d 92 where: {
50d6de9c 93 actorId,
3fd3ab2d
C
94 videoId
95 },
3fd3ab2d
C
96 transaction: t
97 })
d7d5611c
C
98 }
99
b49f22d8 100 static loadByUrl (url: string, t: Transaction): Promise<MVideoShareFull> {
9588d4f4 101 return VideoShareModel.scope(ScopeNames.FULL).findOne({
0f320037
C
102 where: {
103 url
104 },
105 transaction: t
106 })
107 }
108
b49f22d8 109 static loadActorsByShare (videoId: number, t: Transaction): Promise<MActorDefault[]> {
3fd3ab2d
C
110 const query = {
111 where: {
112 videoId
113 },
114 include: [
115 {
50d6de9c 116 model: ActorModel,
3fd3ab2d
C
117 required: true
118 }
119 ],
120 transaction: t
121 }
122
d48ff09d 123 return VideoShareModel.scope(ScopeNames.FULL).findAll(query)
891a8196 124 .then((res: MVideoShareFull[]) => res.map(r => r.Actor))
3fd3ab2d 125 }
265ba139 126
b49f22d8 127 static loadActorsWhoSharedVideosOf (actorOwnerId: number, t: Transaction): Promise<MActorDefault[]> {
891a8196
C
128 const safeOwnerId = parseInt(actorOwnerId + '', 10)
129
130 // /!\ On actor model
265ba139 131 const query = {
891a8196
C
132 where: {
133 [Op.and]: [
134 literal(
135 `EXISTS (` +
136 ` SELECT 1 FROM "videoShare" ` +
137 ` INNER JOIN "video" ON "videoShare"."videoId" = "video"."id" ` +
138 ` INNER JOIN "videoChannel" ON "videoChannel"."id" = "video"."channelId" ` +
139 ` INNER JOIN "account" ON "account"."id" = "videoChannel"."accountId" ` +
140 ` WHERE "videoShare"."actorId" = "ActorModel"."id" AND "account"."actorId" = ${safeOwnerId} ` +
141 ` LIMIT 1` +
142 `)`
143 )
144 ]
145 },
265ba139
C
146 transaction: t
147 }
148
891a8196 149 return ActorModel.findAll(query)
265ba139 150 }
2422c46b 151
b49f22d8 152 static loadActorsByVideoChannel (videoChannelId: number, t: Transaction): Promise<MActorDefault[]> {
891a8196
C
153 const safeChannelId = parseInt(videoChannelId + '', 10)
154
155 // /!\ On actor model
2422c46b 156 const query = {
891a8196
C
157 where: {
158 [Op.and]: [
159 literal(
160 `EXISTS (` +
161 ` SELECT 1 FROM "videoShare" ` +
162 ` INNER JOIN "video" ON "videoShare"."videoId" = "video"."id" ` +
163 ` WHERE "videoShare"."actorId" = "ActorModel"."id" AND "video"."channelId" = ${safeChannelId} ` +
164 ` LIMIT 1` +
165 `)`
166 )
167 ]
168 },
2422c46b
C
169 transaction: t
170 }
171
891a8196 172 return ActorModel.findAll(query)
2422c46b 173 }
8fffe21a 174
970ceac0 175 static listAndCountByVideoId (videoId: number, start: number, count: number, t?: Transaction) {
8fffe21a 176 const query = {
9a4a9b6c
C
177 offset: start,
178 limit: count,
8fffe21a
C
179 where: {
180 videoId
181 },
182 transaction: t
183 }
184
185 return VideoShareModel.findAndCountAll(query)
186 }
2ba92871 187
74d249bc
C
188 static listRemoteShareUrlsOfLocalVideos () {
189 const query = `SELECT "videoShare".url FROM "videoShare" ` +
190 `INNER JOIN actor ON actor.id = "videoShare"."actorId" AND actor."serverId" IS NOT NULL ` +
191 `INNER JOIN video ON video.id = "videoShare"."videoId" AND video.remote IS FALSE`
192
193 return VideoShareModel.sequelize.query<{ url: string }>(query, {
194 type: QueryTypes.SELECT,
195 raw: true
196 }).then(rows => rows.map(r => r.url))
197 }
198
2ba92871
C
199 static cleanOldSharesOf (videoId: number, beforeUpdatedAt: Date) {
200 const query = {
201 where: {
202 updatedAt: {
970ceac0 203 [Op.lt]: beforeUpdatedAt
2ba92871 204 },
6b9c966f
C
205 videoId,
206 actorId: {
207 [Op.notIn]: buildLocalActorIdsIn()
970ceac0 208 }
6b9c966f 209 }
2ba92871
C
210 }
211
212 return VideoShareModel.destroy(query)
213 }
d7d5611c 214}