allow limiting video-comments rss feeds to an account or video channel
This commit is contained in:
parent
2c31866430
commit
00494d6e2a
5 changed files with 119 additions and 23 deletions
|
@ -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,
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -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'
|
||||||
|
|
|
@ -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'
|
||||||
|
|
Loading…
Reference in a new issue