aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorRigel Kent <sendmemail@rigelk.eu>2020-06-27 13:12:30 +0200
committerRigel Kent <sendmemail@rigelk.eu>2020-06-27 13:20:59 +0200
commit00494d6e2ae915741f47869dcd359d9728a0af91 (patch)
tree9642f3d5bf1565d3b8d60d3ad06495fefce80c23
parent2c318664305fd2723586ad939e64f958d9d447ff (diff)
downloadPeerTube-00494d6e2ae915741f47869dcd359d9728a0af91.tar.gz
PeerTube-00494d6e2ae915741f47869dcd359d9728a0af91.tar.zst
PeerTube-00494d6e2ae915741f47869dcd359d9728a0af91.zip
allow limiting video-comments rss feeds to an account or video channel
-rw-r--r--server/controllers/feeds.ts55
-rw-r--r--server/middlewares/validators/feeds.ts6
-rw-r--r--server/models/video/video-comment.ts34
-rw-r--r--shared/extra-utils/users/users.ts2
-rw-r--r--support/doc/api/openapi.yaml45
5 files changed, 119 insertions, 23 deletions
diff --git a/server/controllers/feeds.ts b/server/controllers/feeds.ts
index cb82bfc6d..bfcd3fe36 100644
--- a/server/controllers/feeds.ts
+++ b/server/controllers/feeds.ts
@@ -27,6 +27,7 @@ feedsRouter.get('/feeds/video-comments.:format',
27 'Content-Type' 27 'Content-Type'
28 ] 28 ]
29 })(ROUTE_CACHE_LIFETIME.FEEDS)), 29 })(ROUTE_CACHE_LIFETIME.FEEDS)),
30 asyncMiddleware(videoFeedsValidator),
30 asyncMiddleware(videoCommentsFeedsValidator), 31 asyncMiddleware(videoCommentsFeedsValidator),
31 asyncMiddleware(generateVideoCommentsFeed) 32 asyncMiddleware(generateVideoCommentsFeed)
32) 33)
@@ -58,13 +59,36 @@ async function generateVideoCommentsFeed (req: express.Request, res: express.Res
58 const start = 0 59 const start = 0
59 60
60 const video = res.locals.videoAll 61 const video = res.locals.videoAll
61 const videoId: number = video ? video.id : undefined 62 const account = res.locals.account
63 const videoChannel = res.locals.videoChannel
62 64
63 const comments = await VideoCommentModel.listForFeed(start, FEEDS.COUNT, videoId) 65 const comments = await VideoCommentModel.listForFeed({
66 start,
67 count: FEEDS.COUNT,
68 videoId: video ? video.id : undefined,
69 accountId: account ? account.id : undefined,
70 videoChannelId: videoChannel ? videoChannel.id : undefined
71 })
64 72
65 const name = video ? video.name : CONFIG.INSTANCE.NAME 73 let name: string
66 const description = video ? video.description : CONFIG.INSTANCE.DESCRIPTION 74 let description: string
67 const feed = initFeed(name, description) 75
76 if (videoChannel) {
77 name = videoChannel.getDisplayName()
78 description = videoChannel.description
79 } else if (account) {
80 name = account.getDisplayName()
81 description = account.description
82 } else {
83 name = video ? video.name : CONFIG.INSTANCE.NAME
84 description = video ? video.description : CONFIG.INSTANCE.DESCRIPTION
85 }
86 const feed = initFeed({
87 name,
88 description,
89 resourceType: 'video-comments',
90 queryString: new URL(WEBSERVER.URL + req.originalUrl).search
91 })
68 92
69 // Adding video items to the feed, one at a time 93 // Adding video items to the feed, one at a time
70 for (const comment of comments) { 94 for (const comment of comments) {
@@ -116,7 +140,12 @@ async function generateVideoFeed (req: express.Request, res: express.Response) {
116 description = CONFIG.INSTANCE.DESCRIPTION 140 description = CONFIG.INSTANCE.DESCRIPTION
117 } 141 }
118 142
119 const feed = initFeed(name, description) 143 const feed = initFeed({
144 name,
145 description,
146 resourceType: 'videos',
147 queryString: new URL(WEBSERVER.URL + req.url).search
148 })
120 149
121 const resultList = await VideoModel.listForApi({ 150 const resultList = await VideoModel.listForApi({
122 start, 151 start,
@@ -207,8 +236,14 @@ async function generateVideoFeed (req: express.Request, res: express.Response) {
207 return sendFeed(feed, req, res) 236 return sendFeed(feed, req, res)
208} 237}
209 238
210function initFeed (name: string, description: string) { 239function initFeed (parameters: {
240 name: string
241 description: string
242 resourceType?: 'videos' | 'video-comments'
243 queryString?: string
244}) {
211 const webserverUrl = WEBSERVER.URL 245 const webserverUrl = WEBSERVER.URL
246 const { name, description, resourceType, queryString } = parameters
212 247
213 return new Feed({ 248 return new Feed({
214 title: name, 249 title: name,
@@ -222,9 +257,9 @@ function initFeed (name: string, description: string) {
222 ` and potential licenses granted by each content's rightholder.`, 257 ` and potential licenses granted by each content's rightholder.`,
223 generator: `Toraifōsu`, // ^.~ 258 generator: `Toraifōsu`, // ^.~
224 feedLinks: { 259 feedLinks: {
225 json: `${webserverUrl}/feeds/videos.json`, 260 json: `${webserverUrl}/feeds/${resourceType}.json${queryString}`,
226 atom: `${webserverUrl}/feeds/videos.atom`, 261 atom: `${webserverUrl}/feeds/${resourceType}.atom${queryString}`,
227 rss: `${webserverUrl}/feeds/videos.xml` 262 rss: `${webserverUrl}/feeds/${resourceType}.xml${queryString}`
228 }, 263 },
229 author: { 264 author: {
230 name: 'Instance admin of ' + CONFIG.INSTANCE.NAME, 265 name: 'Instance admin of ' + CONFIG.INSTANCE.NAME,
diff --git a/server/middlewares/validators/feeds.ts b/server/middlewares/validators/feeds.ts
index f34c2b174..c3de0f5fe 100644
--- a/server/middlewares/validators/feeds.ts
+++ b/server/middlewares/validators/feeds.ts
@@ -70,6 +70,12 @@ const videoCommentsFeedsValidator = [
70 70
71 if (areValidationErrors(req, res)) return 71 if (areValidationErrors(req, res)) return
72 72
73 if (req.query.videoId && (req.query.videoChannelId || req.query.videoChannelName)) {
74 return res.status(400).send({
75 message: 'videoId cannot be mixed with a channel filter'
76 }).end()
77 }
78
73 if (req.query.videoId && !await doesVideoExist(req.query.videoId, res)) return 79 if (req.query.videoId && !await doesVideoExist(req.query.videoId, res)) return
74 80
75 return next() 81 return next()
diff --git a/server/models/video/video-comment.ts b/server/models/video/video-comment.ts
index 091cc2a88..c465eb3e7 100644
--- a/server/models/video/video-comment.ts
+++ b/server/models/video/video-comment.ts
@@ -427,8 +427,31 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
427 return VideoCommentModel.findAndCountAll<MComment>(query) 427 return VideoCommentModel.findAndCountAll<MComment>(query)
428 } 428 }
429 429
430 static async listForFeed (start: number, count: number, videoId?: number): Promise<MCommentOwnerVideoFeed[]> { 430 static async listForFeed (parameters: {
431 start: number
432 count: number
433 videoId?: number
434 accountId?: number
435 videoChannelId?: number
436 }): Promise<MCommentOwnerVideoFeed[]> {
431 const serverActor = await getServerActor() 437 const serverActor = await getServerActor()
438 const { start, count, videoId, accountId, videoChannelId } = parameters
439
440 const accountExclusion = {
441 [Op.notIn]: Sequelize.literal(
442 '(' + buildBlockedAccountSQL([ serverActor.Account.id, '"Video->VideoChannel"."accountId"' ]) + ')'
443 )
444 }
445 const accountWhere = accountId
446 ? {
447 [Op.and]: {
448 ...accountExclusion,
449 [Op.eq]: accountId
450 }
451 }
452 : accountExclusion
453
454 const videoChannelWhere = videoChannelId ? { id: videoChannelId } : undefined
432 455
433 const query = { 456 const query = {
434 order: [ [ 'createdAt', 'DESC' ] ] as Order, 457 order: [ [ 'createdAt', 'DESC' ] ] as Order,
@@ -436,11 +459,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
436 limit: count, 459 limit: count,
437 where: { 460 where: {
438 deletedAt: null, 461 deletedAt: null,
439 accountId: { 462 accountId: accountWhere
440 [Op.notIn]: Sequelize.literal(
441 '(' + buildBlockedAccountSQL([ serverActor.Account.id, '"Video->VideoChannel"."accountId"' ]) + ')'
442 )
443 }
444 }, 463 },
445 include: [ 464 include: [
446 { 465 {
@@ -454,7 +473,8 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
454 { 473 {
455 attributes: [ 'accountId' ], 474 attributes: [ 'accountId' ],
456 model: VideoChannelModel.unscoped(), 475 model: VideoChannelModel.unscoped(),
457 required: true 476 required: true,
477 where: videoChannelWhere
458 } 478 }
459 ] 479 ]
460 } 480 }
diff --git a/shared/extra-utils/users/users.ts b/shared/extra-utils/users/users.ts
index 08b7743a6..766189dfe 100644
--- a/shared/extra-utils/users/users.ts
+++ b/shared/extra-utils/users/users.ts
@@ -29,7 +29,7 @@ function createUser (parameters: CreateUserArgs) {
29 videoQuota = 1000000, 29 videoQuota = 1000000,
30 videoQuotaDaily = -1, 30 videoQuotaDaily = -1,
31 role = UserRole.USER, 31 role = UserRole.USER,
32 specialStatus = 200 32 specialStatus = 201
33 } = parameters 33 } = parameters
34 34
35 const path = '/api/v1/users' 35 const path = '/api/v1/users'
diff --git a/support/doc/api/openapi.yaml b/support/doc/api/openapi.yaml
index 3b06a2568..186d7d37d 100644
--- a/support/doc/api/openapi.yaml
+++ b/support/doc/api/openapi.yaml
@@ -1147,7 +1147,8 @@ paths:
1147 description: Whether or not we wait transcoding before publish the video 1147 description: Whether or not we wait transcoding before publish the video
1148 type: string 1148 type: string
1149 support: 1149 support:
1150 description: Text describing how to support the video uploader 1150 description: A text tell the audience how to support the video creator
1151 example: Please support my work on <insert crowdfunding plateform>! <3
1151 type: string 1152 type: string
1152 nsfw: 1153 nsfw:
1153 description: Whether or not this video contains sensitive content 1154 description: Whether or not this video contains sensitive content
@@ -1305,7 +1306,8 @@ paths:
1305 description: Whether or not we wait transcoding before publish the video 1306 description: Whether or not we wait transcoding before publish the video
1306 type: string 1307 type: string
1307 support: 1308 support:
1308 description: Text describing how to support the video uploader 1309 description: A text tell the audience how to support the video creator
1310 example: Please support my work on <insert crowdfunding plateform>! <3
1309 type: string 1311 type: string
1310 nsfw: 1312 nsfw:
1311 description: Whether or not this video contains sensitive content 1313 description: Whether or not this video contains sensitive content
@@ -1422,7 +1424,8 @@ paths:
1422 description: Whether or not we wait transcoding before publish the video 1424 description: Whether or not we wait transcoding before publish the video
1423 type: string 1425 type: string
1424 support: 1426 support:
1425 description: Text describing how to support the video uploader 1427 description: A text tell the audience how to support the video creator
1428 example: Please support my work on <insert crowdfunding plateform>! <3
1426 type: string 1429 type: string
1427 nsfw: 1430 nsfw:
1428 description: Whether or not this video contains sensitive content 1431 description: Whether or not this video contains sensitive content
@@ -2723,7 +2726,7 @@ paths:
2723 - name: format 2726 - name: format
2724 in: path 2727 in: path
2725 required: true 2728 required: true
2726 description: 'format expected (we focus on making `rss` the most featureful ; it serves Media RSS)' 2729 description: 'format expected (we focus on making `rss` the most featureful ; it serves [Media RSS](https://www.rssboard.org/media-rss))'
2727 schema: 2730 schema:
2728 type: string 2731 type: string
2729 enum: 2732 enum:
@@ -2739,6 +2742,26 @@ paths:
2739 description: 'limit listing to a specific video' 2742 description: 'limit listing to a specific video'
2740 schema: 2743 schema:
2741 type: string 2744 type: string
2745 - name: accountId
2746 in: query
2747 description: 'limit listing to a specific account'
2748 schema:
2749 type: string
2750 - name: accountName
2751 in: query
2752 description: 'limit listing to a specific account'
2753 schema:
2754 type: string
2755 - name: videoChannelId
2756 in: query
2757 description: 'limit listing to a specific video channel'
2758 schema:
2759 type: string
2760 - name: videoChannelName
2761 in: query
2762 description: 'limit listing to a specific video channel'
2763 schema:
2764 type: string
2742 responses: 2765 responses:
2743 '204': 2766 '204':
2744 description: successful operation 2767 description: successful operation
@@ -2763,6 +2786,13 @@ paths:
2763 application/json: 2786 application/json:
2764 schema: 2787 schema:
2765 type: object 2788 type: object
2789 '400':
2790 x-summary: field inconsistencies
2791 description: >
2792 Arises when:
2793 - videoId filter is mixed with a channel filter
2794 '404':
2795 description: video, video channel or account not found
2766 '406': 2796 '406':
2767 description: accept header unsupported 2797 description: accept header unsupported
2768 '/feeds/videos.{format}': 2798 '/feeds/videos.{format}':
@@ -2781,7 +2811,7 @@ paths:
2781 - name: format 2811 - name: format
2782 in: path 2812 in: path
2783 required: true 2813 required: true
2784 description: 'format expected (we focus on making `rss` the most featureful ; it serves Media RSS)' 2814 description: 'format expected (we focus on making `rss` the most featureful ; it serves [Media RSS](https://www.rssboard.org/media-rss))'
2785 schema: 2815 schema:
2786 type: string 2816 type: string
2787 enum: 2817 enum:
@@ -2842,6 +2872,8 @@ paths:
2842 application/json: 2872 application/json:
2843 schema: 2873 schema:
2844 type: object 2874 type: object
2875 '404':
2876 description: video channel or account not found
2845 '406': 2877 '406':
2846 description: accept header unsupported 2878 description: accept header unsupported
2847 /plugins: 2879 /plugins:
@@ -3775,6 +3807,7 @@ components:
3775 type: string 3807 type: string
3776 support: 3808 support:
3777 type: string 3809 type: string
3810 description: A text tell the audience how to support the video creator
3778 example: Please support my work on <insert crowdfunding plateform>! <3 3811 example: Please support my work on <insert crowdfunding plateform>! <3
3779 channel: 3812 channel:
3780 $ref: '#/components/schemas/VideoChannel' 3813 $ref: '#/components/schemas/VideoChannel'
@@ -4806,6 +4839,7 @@ components:
4806 support: 4839 support:
4807 type: string 4840 type: string
4808 description: 'A text shown by default on all videos of this channel, to tell the audience how to support it' 4841 description: 'A text shown by default on all videos of this channel, to tell the audience how to support it'
4842 example: Please support my work on <insert crowdfunding plateform>! <3
4809 required: 4843 required:
4810 - name 4844 - name
4811 - displayName 4845 - displayName
@@ -4818,6 +4852,7 @@ components:
4818 support: 4852 support:
4819 type: string 4853 type: string
4820 description: 'A text shown by default on all videos of this channel, to tell the audience how to support it' 4854 description: 'A text shown by default on all videos of this channel, to tell the audience how to support it'
4855 example: Please support my work on <insert crowdfunding plateform>! <3
4821 bulkVideosSupportUpdate: 4856 bulkVideosSupportUpdate:
4822 type: boolean 4857 type: boolean
4823 description: 'Update the support field for all videos of this channel' 4858 description: 'Update the support field for all videos of this channel'