1
0
Fork 0

allow limiting video-comments rss feeds to an account or video channel

This commit is contained in:
Rigel Kent 2020-06-27 13:12:30 +02:00
parent 2c31866430
commit 00494d6e2a
No known key found for this signature in database
GPG key ID: 5E53E96A494E452F
5 changed files with 119 additions and 23 deletions

View file

@ -27,6 +27,7 @@ feedsRouter.get('/feeds/video-comments.:format',
'Content-Type' 'Content-Type'
] ]
})(ROUTE_CACHE_LIFETIME.FEEDS)), })(ROUTE_CACHE_LIFETIME.FEEDS)),
asyncMiddleware(videoFeedsValidator),
asyncMiddleware(videoCommentsFeedsValidator), asyncMiddleware(videoCommentsFeedsValidator),
asyncMiddleware(generateVideoCommentsFeed) asyncMiddleware(generateVideoCommentsFeed)
) )
@ -58,13 +59,36 @@ async function generateVideoCommentsFeed (req: express.Request, res: express.Res
const start = 0 const start = 0
const video = res.locals.videoAll 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 let name: string
const description = video ? video.description : CONFIG.INSTANCE.DESCRIPTION let description: string
const feed = initFeed(name, description)
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 // Adding video items to the feed, one at a time
for (const comment of comments) { for (const comment of comments) {
@ -116,7 +140,12 @@ async function generateVideoFeed (req: express.Request, res: express.Response) {
description = CONFIG.INSTANCE.DESCRIPTION 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({ const resultList = await VideoModel.listForApi({
start, start,
@ -207,8 +236,14 @@ async function generateVideoFeed (req: express.Request, res: express.Response) {
return sendFeed(feed, req, res) 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 webserverUrl = WEBSERVER.URL
const { name, description, resourceType, queryString } = parameters
return new Feed({ return new Feed({
title: name, title: name,
@ -222,9 +257,9 @@ function initFeed (name: string, description: string) {
` and potential licenses granted by each content's rightholder.`, ` and potential licenses granted by each content's rightholder.`,
generator: `Toraifōsu`, // ^.~ generator: `Toraifōsu`, // ^.~
feedLinks: { feedLinks: {
json: `${webserverUrl}/feeds/videos.json`, json: `${webserverUrl}/feeds/${resourceType}.json${queryString}`,
atom: `${webserverUrl}/feeds/videos.atom`, atom: `${webserverUrl}/feeds/${resourceType}.atom${queryString}`,
rss: `${webserverUrl}/feeds/videos.xml` rss: `${webserverUrl}/feeds/${resourceType}.xml${queryString}`
}, },
author: { author: {
name: 'Instance admin of ' + CONFIG.INSTANCE.NAME, name: 'Instance admin of ' + CONFIG.INSTANCE.NAME,

View file

@ -70,6 +70,12 @@ const videoCommentsFeedsValidator = [
if (areValidationErrors(req, res)) return 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 if (req.query.videoId && !await doesVideoExist(req.query.videoId, res)) return
return next() return next()

View file

@ -427,8 +427,31 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
return VideoCommentModel.findAndCountAll<MComment>(query) 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 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 = { const query = {
order: [ [ 'createdAt', 'DESC' ] ] as Order, order: [ [ 'createdAt', 'DESC' ] ] as Order,
@ -436,11 +459,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
limit: count, limit: count,
where: { where: {
deletedAt: null, deletedAt: null,
accountId: { accountId: accountWhere
[Op.notIn]: Sequelize.literal(
'(' + buildBlockedAccountSQL([ serverActor.Account.id, '"Video->VideoChannel"."accountId"' ]) + ')'
)
}
}, },
include: [ include: [
{ {
@ -454,7 +473,8 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
{ {
attributes: [ 'accountId' ], attributes: [ 'accountId' ],
model: VideoChannelModel.unscoped(), model: VideoChannelModel.unscoped(),
required: true required: true,
where: videoChannelWhere
} }
] ]
} }

View file

@ -29,7 +29,7 @@ function createUser (parameters: CreateUserArgs) {
videoQuota = 1000000, videoQuota = 1000000,
videoQuotaDaily = -1, videoQuotaDaily = -1,
role = UserRole.USER, role = UserRole.USER,
specialStatus = 200 specialStatus = 201
} = parameters } = parameters
const path = '/api/v1/users' const path = '/api/v1/users'

View file

@ -1147,7 +1147,8 @@ paths:
description: Whether or not we wait transcoding before publish the video description: Whether or not we wait transcoding before publish the video
type: string type: string
support: support:
description: Text describing how to support the video uploader description: A text tell the audience how to support the video creator
example: Please support my work on <insert crowdfunding plateform>! <3
type: string type: string
nsfw: nsfw:
description: Whether or not this video contains sensitive content description: Whether or not this video contains sensitive content
@ -1305,7 +1306,8 @@ paths:
description: Whether or not we wait transcoding before publish the video description: Whether or not we wait transcoding before publish the video
type: string type: string
support: support:
description: Text describing how to support the video uploader description: A text tell the audience how to support the video creator
example: Please support my work on <insert crowdfunding plateform>! <3
type: string type: string
nsfw: nsfw:
description: Whether or not this video contains sensitive content description: Whether or not this video contains sensitive content
@ -1422,7 +1424,8 @@ paths:
description: Whether or not we wait transcoding before publish the video description: Whether or not we wait transcoding before publish the video
type: string type: string
support: support:
description: Text describing how to support the video uploader description: A text tell the audience how to support the video creator
example: Please support my work on <insert crowdfunding plateform>! <3
type: string type: string
nsfw: nsfw:
description: Whether or not this video contains sensitive content description: Whether or not this video contains sensitive content
@ -2723,7 +2726,7 @@ paths:
- name: format - name: format
in: path in: path
required: true required: true
description: 'format expected (we focus on making `rss` the most featureful ; it serves Media RSS)' description: 'format expected (we focus on making `rss` the most featureful ; it serves [Media RSS](https://www.rssboard.org/media-rss))'
schema: schema:
type: string type: string
enum: enum:
@ -2739,6 +2742,26 @@ paths:
description: 'limit listing to a specific video' description: 'limit listing to a specific video'
schema: schema:
type: string type: string
- name: accountId
in: query
description: 'limit listing to a specific account'
schema:
type: string
- name: accountName
in: query
description: 'limit listing to a specific account'
schema:
type: string
- name: videoChannelId
in: query
description: 'limit listing to a specific video channel'
schema:
type: string
- name: videoChannelName
in: query
description: 'limit listing to a specific video channel'
schema:
type: string
responses: responses:
'204': '204':
description: successful operation description: successful operation
@ -2763,6 +2786,13 @@ paths:
application/json: application/json:
schema: schema:
type: object type: object
'400':
x-summary: field inconsistencies
description: >
Arises when:
- videoId filter is mixed with a channel filter
'404':
description: video, video channel or account not found
'406': '406':
description: accept header unsupported description: accept header unsupported
'/feeds/videos.{format}': '/feeds/videos.{format}':
@ -2781,7 +2811,7 @@ paths:
- name: format - name: format
in: path in: path
required: true required: true
description: 'format expected (we focus on making `rss` the most featureful ; it serves Media RSS)' description: 'format expected (we focus on making `rss` the most featureful ; it serves [Media RSS](https://www.rssboard.org/media-rss))'
schema: schema:
type: string type: string
enum: enum:
@ -2842,6 +2872,8 @@ paths:
application/json: application/json:
schema: schema:
type: object type: object
'404':
description: video channel or account not found
'406': '406':
description: accept header unsupported description: accept header unsupported
/plugins: /plugins:
@ -3775,6 +3807,7 @@ components:
type: string type: string
support: support:
type: string type: string
description: A text tell the audience how to support the video creator
example: Please support my work on <insert crowdfunding plateform>! <3 example: Please support my work on <insert crowdfunding plateform>! <3
channel: channel:
$ref: '#/components/schemas/VideoChannel' $ref: '#/components/schemas/VideoChannel'
@ -4806,6 +4839,7 @@ components:
support: support:
type: string type: string
description: 'A text shown by default on all videos of this channel, to tell the audience how to support it' description: 'A text shown by default on all videos of this channel, to tell the audience how to support it'
example: Please support my work on <insert crowdfunding plateform>! <3
required: required:
- name - name
- displayName - displayName
@ -4818,6 +4852,7 @@ components:
support: support:
type: string type: string
description: 'A text shown by default on all videos of this channel, to tell the audience how to support it' description: 'A text shown by default on all videos of this channel, to tell the audience how to support it'
example: Please support my work on <insert crowdfunding plateform>! <3
bulkVideosSupportUpdate: bulkVideosSupportUpdate:
type: boolean type: boolean
description: 'Update the support field for all videos of this channel' description: 'Update the support field for all videos of this channel'