From 00494d6e2ae915741f47869dcd359d9728a0af91 Mon Sep 17 00:00:00 2001
From: Rigel Kent <sendmemail@rigelk.eu>
Date: Sat, 27 Jun 2020 13:12:30 +0200
Subject: allow limiting video-comments rss feeds to an account or video
 channel

---
 server/controllers/feeds.ts            | 55 +++++++++++++++++++++++++++-------
 server/middlewares/validators/feeds.ts |  6 ++++
 server/models/video/video-comment.ts   | 34 ++++++++++++++++-----
 3 files changed, 78 insertions(+), 17 deletions(-)

(limited to 'server')

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',
       'Content-Type'
     ]
   })(ROUTE_CACHE_LIFETIME.FEEDS)),
+  asyncMiddleware(videoFeedsValidator),
   asyncMiddleware(videoCommentsFeedsValidator),
   asyncMiddleware(generateVideoCommentsFeed)
 )
@@ -58,13 +59,36 @@ async function generateVideoCommentsFeed (req: express.Request, res: express.Res
   const start = 0
 
   const video = res.locals.videoAll
-  const videoId: number = video ? video.id : undefined
+  const account = res.locals.account
+  const videoChannel = res.locals.videoChannel
 
-  const comments = await VideoCommentModel.listForFeed(start, FEEDS.COUNT, videoId)
+  const comments = await VideoCommentModel.listForFeed({
+    start,
+    count: FEEDS.COUNT,
+    videoId: video ? video.id : undefined,
+    accountId: account ? account.id : undefined,
+    videoChannelId: videoChannel ? videoChannel.id : undefined
+  })
 
-  const name = video ? video.name : CONFIG.INSTANCE.NAME
-  const description = video ? video.description : CONFIG.INSTANCE.DESCRIPTION
-  const feed = initFeed(name, description)
+  let name: string
+  let description: string
+
+  if (videoChannel) {
+    name = videoChannel.getDisplayName()
+    description = videoChannel.description
+  } else if (account) {
+    name = account.getDisplayName()
+    description = account.description
+  } else {
+    name = video ? video.name : CONFIG.INSTANCE.NAME
+    description = video ? video.description : CONFIG.INSTANCE.DESCRIPTION
+  }
+  const feed = initFeed({
+    name,
+    description,
+    resourceType: 'video-comments',
+    queryString: new URL(WEBSERVER.URL + req.originalUrl).search
+  })
 
   // Adding video items to the feed, one at a time
   for (const comment of comments) {
@@ -116,7 +140,12 @@ async function generateVideoFeed (req: express.Request, res: express.Response) {
     description = CONFIG.INSTANCE.DESCRIPTION
   }
 
-  const feed = initFeed(name, description)
+  const feed = initFeed({
+    name,
+    description,
+    resourceType: 'videos',
+    queryString: new URL(WEBSERVER.URL + req.url).search
+  })
 
   const resultList = await VideoModel.listForApi({
     start,
@@ -207,8 +236,14 @@ async function generateVideoFeed (req: express.Request, res: express.Response) {
   return sendFeed(feed, req, res)
 }
 
-function initFeed (name: string, description: string) {
+function initFeed (parameters: {
+  name: string
+  description: string
+  resourceType?: 'videos' | 'video-comments'
+  queryString?: string
+}) {
   const webserverUrl = WEBSERVER.URL
+  const { name, description, resourceType, queryString } = parameters
 
   return new Feed({
     title: name,
@@ -222,9 +257,9 @@ function initFeed (name: string, description: string) {
     ` and potential licenses granted by each content's rightholder.`,
     generator: `Toraifōsu`, // ^.~
     feedLinks: {
-      json: `${webserverUrl}/feeds/videos.json`,
-      atom: `${webserverUrl}/feeds/videos.atom`,
-      rss: `${webserverUrl}/feeds/videos.xml`
+      json: `${webserverUrl}/feeds/${resourceType}.json${queryString}`,
+      atom: `${webserverUrl}/feeds/${resourceType}.atom${queryString}`,
+      rss: `${webserverUrl}/feeds/${resourceType}.xml${queryString}`
     },
     author: {
       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 = [
 
     if (areValidationErrors(req, res)) return
 
+    if (req.query.videoId && (req.query.videoChannelId || req.query.videoChannelName)) {
+      return res.status(400).send({
+        message: 'videoId cannot be mixed with a channel filter'
+      }).end()
+    }
+
     if (req.query.videoId && !await doesVideoExist(req.query.videoId, res)) return
 
     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> {
     return VideoCommentModel.findAndCountAll<MComment>(query)
   }
 
-  static async listForFeed (start: number, count: number, videoId?: number): Promise<MCommentOwnerVideoFeed[]> {
+  static async listForFeed (parameters: {
+    start: number
+    count: number
+    videoId?: number
+    accountId?: number
+    videoChannelId?: number
+  }): Promise<MCommentOwnerVideoFeed[]> {
     const serverActor = await getServerActor()
+    const { start, count, videoId, accountId, videoChannelId } = parameters
+
+    const accountExclusion = {
+      [Op.notIn]: Sequelize.literal(
+        '(' + buildBlockedAccountSQL([ serverActor.Account.id, '"Video->VideoChannel"."accountId"' ]) + ')'
+      )
+    }
+    const accountWhere = accountId
+      ? {
+          [Op.and]: {
+            ...accountExclusion,
+            [Op.eq]: accountId
+          }
+        }
+      : accountExclusion
+
+    const videoChannelWhere = videoChannelId ? { id: videoChannelId } : undefined
 
     const query = {
       order: [ [ 'createdAt', 'DESC' ] ] as Order,
@@ -436,11 +459,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
       limit: count,
       where: {
         deletedAt: null,
-        accountId: {
-          [Op.notIn]: Sequelize.literal(
-            '(' + buildBlockedAccountSQL([ serverActor.Account.id, '"Video->VideoChannel"."accountId"' ]) + ')'
-          )
-        }
+        accountId: accountWhere
       },
       include: [
         {
@@ -454,7 +473,8 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
             {
               attributes: [ 'accountId' ],
               model: VideoChannelModel.unscoped(),
-              required: true
+              required: true,
+              where: videoChannelWhere
             }
           ]
         }
-- 
cgit v1.2.3