[Youtube] Added channel view count and subscriber count badges (#6333)
Co-authored-by: Pierre-Yves B <PyvesDev@gmail.com>
This commit is contained in:
@@ -10,18 +10,26 @@ const documentation = `
|
||||
<code>https://www.youtube.com/t/terms</code></p>`
|
||||
|
||||
const schema = Joi.object({
|
||||
items: Joi.array()
|
||||
.items(
|
||||
Joi.object({
|
||||
statistics: Joi.object({
|
||||
pageInfo: Joi.object({
|
||||
totalResults: nonNegativeInteger,
|
||||
resultsPerPage: nonNegativeInteger,
|
||||
}).required(),
|
||||
items: Joi.array().items(
|
||||
Joi.object({
|
||||
statistics: Joi.alternatives(
|
||||
Joi.object({
|
||||
viewCount: nonNegativeInteger,
|
||||
likeCount: nonNegativeInteger,
|
||||
dislikeCount: nonNegativeInteger,
|
||||
commentCount: nonNegativeInteger,
|
||||
}).required(),
|
||||
})
|
||||
)
|
||||
.required(),
|
||||
}),
|
||||
Joi.object({
|
||||
viewCount: nonNegativeInteger,
|
||||
subscriberCount: nonNegativeInteger,
|
||||
})
|
||||
),
|
||||
})
|
||||
),
|
||||
}).required()
|
||||
|
||||
class YouTubeBase extends BaseJsonService {
|
||||
@@ -39,38 +47,49 @@ class YouTubeBase extends BaseJsonService {
|
||||
namedLogo: 'youtube',
|
||||
}
|
||||
|
||||
static renderSingleStat({ statistics, statisticName, videoId }) {
|
||||
static renderSingleStat({ statistics, statisticName, id }) {
|
||||
return {
|
||||
label: `${statisticName}s`,
|
||||
message: metric(statistics[`${statisticName}Count`]),
|
||||
style: 'social',
|
||||
link: `https://www.youtube.com/watch?v=${encodeURIComponent(videoId)}`,
|
||||
link: `https://www.youtube.com/${this.type}/${encodeURIComponent(id)}`,
|
||||
}
|
||||
}
|
||||
|
||||
async fetch({ videoId }) {
|
||||
async fetch({ id }) {
|
||||
return this._requestJson(
|
||||
this.authHelper.withQueryStringAuth(
|
||||
{ passKey: 'key' },
|
||||
{
|
||||
schema,
|
||||
url: 'https://www.googleapis.com/youtube/v3/videos',
|
||||
url: `https://www.googleapis.com/youtube/v3/${this.constructor.type}s`,
|
||||
options: {
|
||||
qs: { id: videoId, part: 'statistics' },
|
||||
qs: { id, part: 'statistics' },
|
||||
},
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
async handle({ videoId }, queryParams) {
|
||||
const json = await this.fetch({ videoId })
|
||||
if (json.items.length === 0) {
|
||||
throw new NotFound({ prettyMessage: 'video not found' })
|
||||
async handle({ channelId, videoId }, queryParams) {
|
||||
const id = channelId || videoId
|
||||
const json = await this.fetch({ id })
|
||||
if (json.pageInfo.totalResults === 0) {
|
||||
throw new NotFound({
|
||||
prettyMessage: `${this.constructor.type} not found`,
|
||||
})
|
||||
}
|
||||
const statistics = json.items[0].statistics
|
||||
return this.constructor.render({ statistics, videoId }, queryParams)
|
||||
return this.constructor.render({ statistics, id }, queryParams)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { documentation, YouTubeBase }
|
||||
class YouTubeVideoBase extends YouTubeBase {
|
||||
static type = 'video'
|
||||
}
|
||||
|
||||
class YouTubeChannelBase extends YouTubeBase {
|
||||
static type = 'channel'
|
||||
}
|
||||
|
||||
module.exports = { documentation, YouTubeVideoBase, YouTubeChannelBase }
|
||||
|
||||
31
services/youtube/youtube-channel-views.service.js
Normal file
31
services/youtube/youtube-channel-views.service.js
Normal file
@@ -0,0 +1,31 @@
|
||||
'use strict'
|
||||
|
||||
const { documentation, YouTubeChannelBase } = require('./youtube-base')
|
||||
|
||||
module.exports = class YouTubeChannelViews extends YouTubeChannelBase {
|
||||
static route = {
|
||||
base: 'youtube/channel/views',
|
||||
pattern: ':channelId',
|
||||
}
|
||||
|
||||
static get examples() {
|
||||
const preview = this.render({
|
||||
statistics: { viewCount: 30543 },
|
||||
id: 'UC8butISFwT-Wl7EV0hUK0BQ',
|
||||
})
|
||||
// link[] is not allowed in examples
|
||||
delete preview.link
|
||||
return [
|
||||
{
|
||||
title: 'YouTube Channel Views',
|
||||
namedParams: { channelId: 'UC8butISFwT-Wl7EV0hUK0BQ' },
|
||||
staticPreview: preview,
|
||||
documentation,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
static render({ statistics, id }) {
|
||||
return super.renderSingleStat({ statistics, statisticName: 'view', id })
|
||||
}
|
||||
}
|
||||
25
services/youtube/youtube-channel-views.tester.js
Normal file
25
services/youtube/youtube-channel-views.tester.js
Normal file
@@ -0,0 +1,25 @@
|
||||
'use strict'
|
||||
|
||||
const t = (module.exports = require('../tester').createServiceTester())
|
||||
const { noToken } = require('../test-helpers')
|
||||
const { isMetric } = require('../test-validators')
|
||||
const noYouTubeToken = noToken(require('./youtube-channel-views.service'))
|
||||
|
||||
t.create('channel view count')
|
||||
.skipWhen(noYouTubeToken)
|
||||
.get('/UC8butISFwT-Wl7EV0hUK0BQ.json')
|
||||
.expectBadge({
|
||||
label: 'views',
|
||||
message: isMetric,
|
||||
color: 'red',
|
||||
link: ['https://www.youtube.com/channel/UC8butISFwT-Wl7EV0hUK0BQ'],
|
||||
})
|
||||
|
||||
t.create('channel not found')
|
||||
.skipWhen(noYouTubeToken)
|
||||
.get('/doesnotexist.json')
|
||||
.expectBadge({
|
||||
label: 'youtube',
|
||||
message: 'channel not found',
|
||||
color: 'red',
|
||||
})
|
||||
@@ -1,8 +1,8 @@
|
||||
'use strict'
|
||||
|
||||
const { documentation, YouTubeBase } = require('./youtube-base')
|
||||
const { documentation, YouTubeVideoBase } = require('./youtube-base')
|
||||
|
||||
module.exports = class YouTubeComments extends YouTubeBase {
|
||||
module.exports = class YouTubeComments extends YouTubeVideoBase {
|
||||
static route = {
|
||||
base: 'youtube/comments',
|
||||
pattern: ':videoId',
|
||||
@@ -11,7 +11,7 @@ module.exports = class YouTubeComments extends YouTubeBase {
|
||||
static get examples() {
|
||||
const preview = this.render({
|
||||
statistics: { commentCount: 209 },
|
||||
videoId: 'wGJHwc5ksMA',
|
||||
id: 'wGJHwc5ksMA',
|
||||
})
|
||||
// link[] is not allowed in examples
|
||||
delete preview.link
|
||||
@@ -25,11 +25,7 @@ module.exports = class YouTubeComments extends YouTubeBase {
|
||||
]
|
||||
}
|
||||
|
||||
static render({ statistics, videoId }) {
|
||||
return super.renderSingleStat({
|
||||
statistics,
|
||||
statisticName: 'comment',
|
||||
videoId,
|
||||
})
|
||||
static render({ statistics, id }) {
|
||||
return super.renderSingleStat({ statistics, statisticName: 'comment', id })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ t.create('video comment count')
|
||||
label: 'comments',
|
||||
message: isMetric,
|
||||
color: 'red',
|
||||
link: ['https://www.youtube.com/watch?v=wGJHwc5ksMA'],
|
||||
link: ['https://www.youtube.com/video/wGJHwc5ksMA'],
|
||||
})
|
||||
|
||||
t.create('video not found')
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
const Joi = require('joi')
|
||||
const { metric } = require('../text-formatters')
|
||||
const { documentation, YouTubeBase } = require('./youtube-base')
|
||||
const { documentation, YouTubeVideoBase } = require('./youtube-base')
|
||||
|
||||
const documentationWithDislikes = `
|
||||
${documentation}
|
||||
@@ -16,7 +16,7 @@ const queryParamSchema = Joi.object({
|
||||
withDislikes: Joi.equal(''),
|
||||
}).required()
|
||||
|
||||
module.exports = class YouTubeLikes extends YouTubeBase {
|
||||
module.exports = class YouTubeLikes extends YouTubeVideoBase {
|
||||
static route = {
|
||||
base: 'youtube/likes',
|
||||
pattern: ':videoId',
|
||||
@@ -26,16 +26,14 @@ module.exports = class YouTubeLikes extends YouTubeBase {
|
||||
static get examples() {
|
||||
const previewLikes = this.render({
|
||||
statistics: { likeCount: 7 },
|
||||
videoId: 'abBdk8bSPKU',
|
||||
id: 'abBdk8bSPKU',
|
||||
})
|
||||
const previewVotes = this.render(
|
||||
{
|
||||
statistics: { likeCount: 10236, dislikeCount: 396 },
|
||||
videoId: 'pU9Q6oiQNd0',
|
||||
id: 'pU9Q6oiQNd0',
|
||||
},
|
||||
{
|
||||
withDislikes: '',
|
||||
}
|
||||
{ withDislikes: '' }
|
||||
)
|
||||
// link[] is not allowed in examples
|
||||
delete previewLikes.link
|
||||
@@ -59,11 +57,11 @@ module.exports = class YouTubeLikes extends YouTubeBase {
|
||||
]
|
||||
}
|
||||
|
||||
static render({ statistics, videoId }, queryParams) {
|
||||
static render({ statistics, id }, queryParams) {
|
||||
let renderedBadge = super.renderSingleStat({
|
||||
statistics,
|
||||
statisticName: 'like',
|
||||
videoId,
|
||||
id,
|
||||
})
|
||||
if (queryParams && typeof queryParams.withDislikes !== 'undefined') {
|
||||
renderedBadge = {
|
||||
|
||||
@@ -13,7 +13,7 @@ t.create('video like count')
|
||||
label: 'likes',
|
||||
message: isMetric,
|
||||
color: 'red',
|
||||
link: ['https://www.youtube.com/watch?v=pU9Q6oiQNd0'],
|
||||
link: ['https://www.youtube.com/video/pU9Q6oiQNd0'],
|
||||
})
|
||||
|
||||
t.create('video vote count')
|
||||
@@ -25,7 +25,7 @@ t.create('video vote count')
|
||||
/^([1-9][0-9]*[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY]) 👍 ([1-9][0-9]*[kMGTPEZY]?|[1-9]\.[1-9][kMGTPEZY]) 👎$/
|
||||
),
|
||||
color: 'red',
|
||||
link: ['https://www.youtube.com/watch?v=pU9Q6oiQNd0'],
|
||||
link: ['https://www.youtube.com/video/pU9Q6oiQNd0'],
|
||||
})
|
||||
|
||||
t.create('video not found')
|
||||
|
||||
35
services/youtube/youtube-subscribers.service.js
Normal file
35
services/youtube/youtube-subscribers.service.js
Normal file
@@ -0,0 +1,35 @@
|
||||
'use strict'
|
||||
|
||||
const { documentation, YouTubeChannelBase } = require('./youtube-base')
|
||||
|
||||
module.exports = class YouTubeSubscribes extends YouTubeChannelBase {
|
||||
static route = {
|
||||
base: 'youtube/channel/subscribers',
|
||||
pattern: ':channelId',
|
||||
}
|
||||
|
||||
static get examples() {
|
||||
const preview = this.render({
|
||||
statistics: { subscriberCount: 14577 },
|
||||
id: 'UC8butISFwT-Wl7EV0hUK0BQ',
|
||||
})
|
||||
// link[] is not allowed in examples
|
||||
delete preview.link
|
||||
return [
|
||||
{
|
||||
title: 'YouTube Channel Subscribers',
|
||||
namedParams: { channelId: 'UC8butISFwT-Wl7EV0hUK0BQ' },
|
||||
staticPreview: preview,
|
||||
documentation,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
static render({ statistics, id }) {
|
||||
return super.renderSingleStat({
|
||||
statistics,
|
||||
statisticName: 'subscriber',
|
||||
id,
|
||||
})
|
||||
}
|
||||
}
|
||||
25
services/youtube/youtube-subscribers.tester.js
Normal file
25
services/youtube/youtube-subscribers.tester.js
Normal file
@@ -0,0 +1,25 @@
|
||||
'use strict'
|
||||
|
||||
const t = (module.exports = require('../tester').createServiceTester())
|
||||
const { noToken } = require('../test-helpers')
|
||||
const { isMetric } = require('../test-validators')
|
||||
const noYouTubeToken = noToken(require('./youtube-subscribers.service'))
|
||||
|
||||
t.create('subscriber count')
|
||||
.skipWhen(noYouTubeToken)
|
||||
.get('/UC8butISFwT-Wl7EV0hUK0BQ.json')
|
||||
.expectBadge({
|
||||
label: 'subscribers',
|
||||
message: isMetric,
|
||||
color: 'red',
|
||||
link: ['https://www.youtube.com/channel/UC8butISFwT-Wl7EV0hUK0BQ'],
|
||||
})
|
||||
|
||||
t.create('channel not found')
|
||||
.skipWhen(noYouTubeToken)
|
||||
.get('/doesnotexist.json')
|
||||
.expectBadge({
|
||||
label: 'youtube',
|
||||
message: 'channel not found',
|
||||
color: 'red',
|
||||
})
|
||||
@@ -1,8 +1,8 @@
|
||||
'use strict'
|
||||
|
||||
const { documentation, YouTubeBase } = require('./youtube-base')
|
||||
const { documentation, YouTubeVideoBase } = require('./youtube-base')
|
||||
|
||||
module.exports = class YouTubeViews extends YouTubeBase {
|
||||
module.exports = class YouTubeViews extends YouTubeVideoBase {
|
||||
static route = {
|
||||
base: 'youtube/views',
|
||||
pattern: ':videoId',
|
||||
@@ -11,7 +11,7 @@ module.exports = class YouTubeViews extends YouTubeBase {
|
||||
static get examples() {
|
||||
const preview = this.render({
|
||||
statistics: { viewCount: 14577 },
|
||||
videoId: 'abBdk8bSPKU',
|
||||
id: 'abBdk8bSPKU',
|
||||
})
|
||||
// link[] is not allowed in examples
|
||||
delete preview.link
|
||||
@@ -25,11 +25,7 @@ module.exports = class YouTubeViews extends YouTubeBase {
|
||||
]
|
||||
}
|
||||
|
||||
static render({ statistics, videoId }) {
|
||||
return super.renderSingleStat({
|
||||
statistics,
|
||||
statisticName: 'view',
|
||||
videoId,
|
||||
})
|
||||
static render({ statistics, id }) {
|
||||
return super.renderSingleStat({ statistics, statisticName: 'view', id })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ t.create('video view count')
|
||||
label: 'views',
|
||||
message: isMetric,
|
||||
color: 'red',
|
||||
link: ['https://www.youtube.com/watch?v=abBdk8bSPKU'],
|
||||
link: ['https://www.youtube.com/video/abBdk8bSPKU'],
|
||||
})
|
||||
|
||||
t.create('video not found')
|
||||
|
||||
Reference in New Issue
Block a user