aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--client/angular.json15
-rw-r--r--server/lib/activitypub/process/process-dislike.ts21
-rw-r--r--server/lib/activitypub/process/process-like.ts24
-rw-r--r--server/lib/activitypub/video-comments.ts2
-rw-r--r--server/lib/video-comment.ts6
-rw-r--r--server/models/account/account-video-rate.ts36
-rw-r--r--server/models/video/video-comment.ts19
-rw-r--r--server/models/video/video-share.ts29
-rw-r--r--shared/extra-utils/videos/videos.ts3
9 files changed, 119 insertions, 36 deletions
diff --git a/client/angular.json b/client/angular.json
index 8a709e269..cce930b82 100644
--- a/client/angular.json
+++ b/client/angular.json
@@ -44,6 +44,21 @@
44 "buildOptimizer": true, 44 "buildOptimizer": true,
45 "serviceWorker": true, 45 "serviceWorker": true,
46 "ngswConfigPath": "src/ngsw-config.json", 46 "ngswConfigPath": "src/ngsw-config.json",
47 "budgets": [
48 {
49 "type": "initial",
50 "type": "initial",
51 "maximumWarning": "2mb",
52 "maximumWarning": "2mb",
53 "maximumError": "5mb"
54 "maximumError": "5mb"
55 },
56 {
57 "type": "anyComponentStyle",
58 "maximumWarning": "6kb",
59 "maximumError": "10kb"
60 }
61 ],
47 "fileReplacements": [ 62 "fileReplacements": [
48 { 63 {
49 "replace": "src/environments/environment.ts", 64 "replace": "src/environments/environment.ts",
diff --git a/server/lib/activitypub/process/process-dislike.ts b/server/lib/activitypub/process/process-dislike.ts
index bfd69e07a..ed8afd3d2 100644
--- a/server/lib/activitypub/process/process-dislike.ts
+++ b/server/lib/activitypub/process/process-dislike.ts
@@ -29,20 +29,21 @@ async function processDislike (activity: ActivityCreate | ActivityDislike, byAct
29 const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: dislikeObject }) 29 const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: dislikeObject })
30 30
31 return sequelizeTypescript.transaction(async t => { 31 return sequelizeTypescript.transaction(async t => {
32 const rate = { 32 const url = getVideoDislikeActivityPubUrl(byActor, video)
33
34 const existingRate = await AccountVideoRateModel.loadByAccountAndVideoOrUrl(byAccount.id, video.id, url)
35 if (existingRate && existingRate.type === 'dislike') return
36
37 await AccountVideoRateModel.create({
33 type: 'dislike' as 'dislike', 38 type: 'dislike' as 'dislike',
34 videoId: video.id, 39 videoId: video.id,
35 accountId: byAccount.id 40 accountId: byAccount.id,
36 } 41 url
42 }, { transaction: t })
37 43
38 const [ , created ] = await AccountVideoRateModel.findOrCreate({ 44 await video.increment('dislikes', { transaction: t })
39 where: rate,
40 defaults: Object.assign({}, rate, { url: getVideoDislikeActivityPubUrl(byActor, video) }),
41 transaction: t
42 })
43 if (created === true) await video.increment('dislikes', { transaction: t })
44 45
45 if (video.isOwned() && created === true) { 46 if (video.isOwned()) {
46 // Don't resend the activity to the sender 47 // Don't resend the activity to the sender
47 const exceptions = [ byActor ] 48 const exceptions = [ byActor ]
48 49
diff --git a/server/lib/activitypub/process/process-like.ts b/server/lib/activitypub/process/process-like.ts
index 2a04167d7..8b97aae55 100644
--- a/server/lib/activitypub/process/process-like.ts
+++ b/server/lib/activitypub/process/process-like.ts
@@ -29,19 +29,21 @@ async function processLikeVideo (byActor: ActorModel, activity: ActivityLike) {
29 const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: videoUrl }) 29 const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: videoUrl })
30 30
31 return sequelizeTypescript.transaction(async t => { 31 return sequelizeTypescript.transaction(async t => {
32 const rate = { 32 const url = getVideoLikeActivityPubUrl(byActor, video)
33
34 const existingRate = await AccountVideoRateModel.loadByAccountAndVideoOrUrl(byAccount.id, video.id, url)
35 if (existingRate && existingRate.type === 'like') return
36
37 await AccountVideoRateModel.create({
33 type: 'like' as 'like', 38 type: 'like' as 'like',
34 videoId: video.id, 39 videoId: video.id,
35 accountId: byAccount.id 40 accountId: byAccount.id,
36 } 41 url
37 const [ , created ] = await AccountVideoRateModel.findOrCreate({ 42 }, { transaction: t })
38 where: rate, 43
39 defaults: Object.assign({}, rate, { url: getVideoLikeActivityPubUrl(byActor, video) }), 44 await video.increment('likes', { transaction: t })
40 transaction: t 45
41 }) 46 if (video.isOwned()) {
42 if (created === true) await video.increment('likes', { transaction: t })
43
44 if (video.isOwned() && created === true) {
45 // Don't resend the activity to the sender 47 // Don't resend the activity to the sender
46 const exceptions = [ byActor ] 48 const exceptions = [ byActor ]
47 49
diff --git a/server/lib/activitypub/video-comments.ts b/server/lib/activitypub/video-comments.ts
index 2f26ddefd..921abdb8d 100644
--- a/server/lib/activitypub/video-comments.ts
+++ b/server/lib/activitypub/video-comments.ts
@@ -54,7 +54,7 @@ async function addVideoComment (videoInstance: VideoModel, commentUrl: string) {
54 }) 54 })
55 55
56 if (sanitizeAndCheckVideoCommentObject(body) === false) { 56 if (sanitizeAndCheckVideoCommentObject(body) === false) {
57 logger.debug('Remote video comment JSON is not valid.', { body }) 57 logger.debug('Remote video comment JSON %s is not valid.', commentUrl, { body })
58 return { created: false } 58 return { created: false }
59 } 59 }
60 60
diff --git a/server/lib/video-comment.ts b/server/lib/video-comment.ts
index bfe22d225..449aa74cb 100644
--- a/server/lib/video-comment.ts
+++ b/server/lib/video-comment.ts
@@ -27,10 +27,10 @@ async function createVideoComment (obj: {
27 inReplyToCommentId, 27 inReplyToCommentId,
28 videoId: obj.video.id, 28 videoId: obj.video.id,
29 accountId: obj.account.id, 29 accountId: obj.account.id,
30 url: 'fake url' 30 url: new Date().toISOString()
31 }, { transaction: t, validate: false } as any) // FIXME: sequelize typings 31 }, { transaction: t, validate: false })
32 32
33 comment.set('url', getVideoCommentActivityPubUrl(obj.video, comment)) 33 comment.url = getVideoCommentActivityPubUrl(obj.video, comment)
34 34
35 const savedComment = await comment.save({ transaction: t }) 35 const savedComment = await comment.save({ transaction: t })
36 savedComment.InReplyToVideoComment = obj.inReplyToComment 36 savedComment.InReplyToVideoComment = obj.inReplyToComment
diff --git a/server/models/account/account-video-rate.ts b/server/models/account/account-video-rate.ts
index 85af9e378..d5c214ecb 100644
--- a/server/models/account/account-video-rate.ts
+++ b/server/models/account/account-video-rate.ts
@@ -89,6 +89,25 @@ export class AccountVideoRateModel extends Model<AccountVideoRateModel> {
89 return AccountVideoRateModel.findOne(options) 89 return AccountVideoRateModel.findOne(options)
90 } 90 }
91 91
92 static loadByAccountAndVideoOrUrl (accountId: number, videoId: number, url: string, transaction?: Transaction) {
93 const options: FindOptions = {
94 where: {
95 [ Op.or]: [
96 {
97 accountId,
98 videoId
99 },
100 {
101 url
102 }
103 ]
104 }
105 }
106 if (transaction) options.transaction = transaction
107
108 return AccountVideoRateModel.findOne(options)
109 }
110
92 static listByAccountForApi (options: { 111 static listByAccountForApi (options: {
93 start: number, 112 start: number,
94 count: number, 113 count: number,
@@ -202,6 +221,23 @@ export class AccountVideoRateModel extends Model<AccountVideoRateModel> {
202 videoId, 221 videoId,
203 type 222 type
204 }, 223 },
224 include: [
225 {
226 model: AccountModel.unscoped(),
227 required: true,
228 include: [
229 {
230 model: ActorModel.unscoped(),
231 required: true,
232 where: {
233 serverId: {
234 [Op.ne]: null
235 }
236 }
237 }
238 ]
239 }
240 ],
205 transaction: t 241 transaction: t
206 } 242 }
207 243
diff --git a/server/models/video/video-comment.ts b/server/models/video/video-comment.ts
index 536b6cb3e..28e5818cd 100644
--- a/server/models/video/video-comment.ts
+++ b/server/models/video/video-comment.ts
@@ -472,7 +472,24 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
472 [Op.lt]: beforeUpdatedAt 472 [Op.lt]: beforeUpdatedAt
473 }, 473 },
474 videoId 474 videoId
475 } 475 },
476 include: [
477 {
478 required: true,
479 model: AccountModel.unscoped(),
480 include: [
481 {
482 required: true,
483 model: ActorModel.unscoped(),
484 where: {
485 serverId: {
486 [Op.ne]: null
487 }
488 }
489 }
490 ]
491 }
492 ]
476 } 493 }
477 494
478 return VideoCommentModel.destroy(query) 495 return VideoCommentModel.destroy(query)
diff --git a/server/models/video/video-share.ts b/server/models/video/video-share.ts
index fda2d7cea..3bab3c027 100644
--- a/server/models/video/video-share.ts
+++ b/server/models/video/video-share.ts
@@ -1,4 +1,3 @@
1import * as Sequelize from 'sequelize'
2import * as Bluebird from 'bluebird' 1import * as Bluebird from 'bluebird'
3import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' 2import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript'
4import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' 3import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
@@ -8,6 +7,7 @@ import { ActorModel } from '../activitypub/actor'
8import { throwIfNotValid } from '../utils' 7import { throwIfNotValid } from '../utils'
9import { VideoModel } from './video' 8import { VideoModel } from './video'
10import { VideoChannelModel } from './video-channel' 9import { VideoChannelModel } from './video-channel'
10import { Op, Transaction } from 'sequelize'
11 11
12enum ScopeNames { 12enum ScopeNames {
13 FULL = 'FULL', 13 FULL = 'FULL',
@@ -88,7 +88,7 @@ export class VideoShareModel extends Model<VideoShareModel> {
88 }) 88 })
89 Video: VideoModel 89 Video: VideoModel
90 90
91 static load (actorId: number, videoId: number, t?: Sequelize.Transaction) { 91 static load (actorId: number, videoId: number, t?: Transaction) {
92 return VideoShareModel.scope(ScopeNames.WITH_ACTOR).findOne({ 92 return VideoShareModel.scope(ScopeNames.WITH_ACTOR).findOne({
93 where: { 93 where: {
94 actorId, 94 actorId,
@@ -98,7 +98,7 @@ export class VideoShareModel extends Model<VideoShareModel> {
98 }) 98 })
99 } 99 }
100 100
101 static loadByUrl (url: string, t: Sequelize.Transaction) { 101 static loadByUrl (url: string, t: Transaction) {
102 return VideoShareModel.scope(ScopeNames.FULL).findOne({ 102 return VideoShareModel.scope(ScopeNames.FULL).findOne({
103 where: { 103 where: {
104 url 104 url
@@ -107,7 +107,7 @@ export class VideoShareModel extends Model<VideoShareModel> {
107 }) 107 })
108 } 108 }
109 109
110 static loadActorsByShare (videoId: number, t: Sequelize.Transaction) { 110 static loadActorsByShare (videoId: number, t: Transaction) {
111 const query = { 111 const query = {
112 where: { 112 where: {
113 videoId 113 videoId
@@ -125,7 +125,7 @@ export class VideoShareModel extends Model<VideoShareModel> {
125 .then(res => res.map(r => r.Actor)) 125 .then(res => res.map(r => r.Actor))
126 } 126 }
127 127
128 static loadActorsWhoSharedVideosOf (actorOwnerId: number, t: Sequelize.Transaction): Bluebird<ActorModel[]> { 128 static loadActorsWhoSharedVideosOf (actorOwnerId: number, t: Transaction): Bluebird<ActorModel[]> {
129 const query = { 129 const query = {
130 attributes: [], 130 attributes: [],
131 include: [ 131 include: [
@@ -163,7 +163,7 @@ export class VideoShareModel extends Model<VideoShareModel> {
163 .then(res => res.map(r => r.Actor)) 163 .then(res => res.map(r => r.Actor))
164 } 164 }
165 165
166 static loadActorsByVideoChannel (videoChannelId: number, t: Sequelize.Transaction): Bluebird<ActorModel[]> { 166 static loadActorsByVideoChannel (videoChannelId: number, t: Transaction): Bluebird<ActorModel[]> {
167 const query = { 167 const query = {
168 attributes: [], 168 attributes: [],
169 include: [ 169 include: [
@@ -188,7 +188,7 @@ export class VideoShareModel extends Model<VideoShareModel> {
188 .then(res => res.map(r => r.Actor)) 188 .then(res => res.map(r => r.Actor))
189 } 189 }
190 190
191 static listAndCountByVideoId (videoId: number, start: number, count: number, t?: Sequelize.Transaction) { 191 static listAndCountByVideoId (videoId: number, start: number, count: number, t?: Transaction) {
192 const query = { 192 const query = {
193 offset: start, 193 offset: start,
194 limit: count, 194 limit: count,
@@ -205,10 +205,21 @@ export class VideoShareModel extends Model<VideoShareModel> {
205 const query = { 205 const query = {
206 where: { 206 where: {
207 updatedAt: { 207 updatedAt: {
208 [Sequelize.Op.lt]: beforeUpdatedAt 208 [Op.lt]: beforeUpdatedAt
209 }, 209 },
210 videoId 210 videoId
211 } 211 },
212 include: [
213 {
214 model: ActorModel.unscoped(),
215 required: true,
216 where: {
217 serverId: {
218 [ Op.ne ]: null
219 }
220 }
221 }
222 ]
212 } 223 }
213 224
214 return VideoShareModel.destroy(query) 225 return VideoShareModel.destroy(query)
diff --git a/shared/extra-utils/videos/videos.ts b/shared/extra-utils/videos/videos.ts
index c78563232..1533f37ab 100644
--- a/shared/extra-utils/videos/videos.ts
+++ b/shared/extra-utils/videos/videos.ts
@@ -379,7 +379,8 @@ async function uploadVideo (url: string, accessToken: string, videoAttributesArg
379 req.field('licence', attributes.licence.toString()) 379 req.field('licence', attributes.licence.toString())
380 } 380 }
381 381
382 for (let i = 0; i < attributes.tags.length; i++) { 382 const tags = attributes.tags || []
383 for (let i = 0; i < tags.length; i++) {
383 req.field('tags[' + i + ']', attributes.tags[i]) 384 req.field('tags[' + i + ']', attributes.tags[i])
384 } 385 }
385 386