mirror of
https://github.com/complexcaresolutions/cms.c2sgmbh.git
synced 2026-03-17 18:34:13 +00:00
feat(youtube): add getVideoStatistics to YouTubeClient
Add batch video statistics retrieval method that fetches view counts, like counts, and comment counts for up to 50 videos per request. Includes unit tests covering normal operation, empty input, missing statistics defaults, null API response, and error propagation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
097bc5225c
commit
065e75b014
2 changed files with 231 additions and 0 deletions
|
|
@ -201,6 +201,37 @@ export class YouTubeClient {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Video-Statistiken für mehrere Videos abrufen (Batch)
|
||||
* YouTube API erlaubt max. 50 IDs pro Request
|
||||
*/
|
||||
async getVideoStatistics(videoIds: string[]): Promise<Array<{
|
||||
id: string
|
||||
views: number
|
||||
likes: number
|
||||
comments: number
|
||||
}>> {
|
||||
if (videoIds.length === 0) return []
|
||||
|
||||
try {
|
||||
const response = await this.youtube.videos.list({
|
||||
part: ['statistics'],
|
||||
id: videoIds,
|
||||
maxResults: 50,
|
||||
})
|
||||
|
||||
return (response.data.items || []).map((item) => ({
|
||||
id: item.id!,
|
||||
views: parseInt(item.statistics?.viewCount || '0', 10),
|
||||
likes: parseInt(item.statistics?.likeCount || '0', 10),
|
||||
comments: parseInt(item.statistics?.commentCount || '0', 10),
|
||||
}))
|
||||
} catch (error) {
|
||||
console.error('Error fetching video statistics:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Kanal-Statistiken abrufen
|
||||
*/
|
||||
|
|
|
|||
200
tests/unit/youtube/youtube-client-stats.unit.spec.ts
Normal file
200
tests/unit/youtube/youtube-client-stats.unit.spec.ts
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
/**
|
||||
* YouTubeClient.getVideoStatistics Unit Tests
|
||||
*
|
||||
* Tests the batch video statistics retrieval method that fetches
|
||||
* view counts, like counts, and comment counts for multiple videos.
|
||||
*/
|
||||
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
|
||||
const mockVideosList = vi.fn()
|
||||
|
||||
vi.mock('googleapis', () => {
|
||||
class MockOAuth2 {
|
||||
setCredentials(): void {
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
google: {
|
||||
auth: {
|
||||
OAuth2: MockOAuth2,
|
||||
},
|
||||
youtube: () => ({
|
||||
videos: {
|
||||
list: mockVideosList,
|
||||
},
|
||||
commentThreads: { list: vi.fn() },
|
||||
comments: {
|
||||
insert: vi.fn(),
|
||||
setModerationStatus: vi.fn(),
|
||||
delete: vi.fn(),
|
||||
},
|
||||
channels: { list: vi.fn() },
|
||||
}),
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
const mockPayload = {} as import('payload').Payload
|
||||
|
||||
describe('YouTubeClient.getVideoStatistics', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
it('should return parsed statistics for multiple videos', async () => {
|
||||
mockVideosList.mockResolvedValueOnce({
|
||||
data: {
|
||||
items: [
|
||||
{
|
||||
id: 'vid1',
|
||||
statistics: {
|
||||
viewCount: '1500',
|
||||
likeCount: '120',
|
||||
commentCount: '45',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'vid2',
|
||||
statistics: {
|
||||
viewCount: '3200',
|
||||
likeCount: '250',
|
||||
commentCount: '80',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
const { YouTubeClient } = await import(
|
||||
'@/lib/integrations/youtube/YouTubeClient'
|
||||
)
|
||||
|
||||
const client = new YouTubeClient(
|
||||
{
|
||||
clientId: 'test-id',
|
||||
clientSecret: 'test-secret',
|
||||
accessToken: 'test-token',
|
||||
refreshToken: 'test-refresh',
|
||||
},
|
||||
mockPayload,
|
||||
)
|
||||
|
||||
const result = await client.getVideoStatistics(['vid1', 'vid2'])
|
||||
|
||||
expect(result).toEqual([
|
||||
{ id: 'vid1', views: 1500, likes: 120, comments: 45 },
|
||||
{ id: 'vid2', views: 3200, likes: 250, comments: 80 },
|
||||
])
|
||||
|
||||
expect(mockVideosList).toHaveBeenCalledWith({
|
||||
part: ['statistics'],
|
||||
id: ['vid1', 'vid2'],
|
||||
maxResults: 50,
|
||||
})
|
||||
})
|
||||
|
||||
it('should return empty array for empty input without calling API', async () => {
|
||||
const { YouTubeClient } = await import(
|
||||
'@/lib/integrations/youtube/YouTubeClient'
|
||||
)
|
||||
|
||||
const client = new YouTubeClient(
|
||||
{
|
||||
clientId: 'test-id',
|
||||
clientSecret: 'test-secret',
|
||||
accessToken: 'test-token',
|
||||
refreshToken: 'test-refresh',
|
||||
},
|
||||
mockPayload,
|
||||
)
|
||||
|
||||
const result = await client.getVideoStatistics([])
|
||||
|
||||
expect(result).toEqual([])
|
||||
expect(mockVideosList).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should default missing statistics to zero', async () => {
|
||||
mockVideosList.mockResolvedValueOnce({
|
||||
data: {
|
||||
items: [
|
||||
{
|
||||
id: 'vid3',
|
||||
statistics: {},
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
const { YouTubeClient } = await import(
|
||||
'@/lib/integrations/youtube/YouTubeClient'
|
||||
)
|
||||
|
||||
const client = new YouTubeClient(
|
||||
{
|
||||
clientId: 'test-id',
|
||||
clientSecret: 'test-secret',
|
||||
accessToken: 'test-token',
|
||||
refreshToken: 'test-refresh',
|
||||
},
|
||||
mockPayload,
|
||||
)
|
||||
|
||||
const result = await client.getVideoStatistics(['vid3'])
|
||||
|
||||
expect(result).toEqual([
|
||||
{ id: 'vid3', views: 0, likes: 0, comments: 0 },
|
||||
])
|
||||
})
|
||||
|
||||
it('should handle null items in API response', async () => {
|
||||
mockVideosList.mockResolvedValueOnce({
|
||||
data: {
|
||||
items: null,
|
||||
},
|
||||
})
|
||||
|
||||
const { YouTubeClient } = await import(
|
||||
'@/lib/integrations/youtube/YouTubeClient'
|
||||
)
|
||||
|
||||
const client = new YouTubeClient(
|
||||
{
|
||||
clientId: 'test-id',
|
||||
clientSecret: 'test-secret',
|
||||
accessToken: 'test-token',
|
||||
refreshToken: 'test-refresh',
|
||||
},
|
||||
mockPayload,
|
||||
)
|
||||
|
||||
const result = await client.getVideoStatistics(['vid-missing'])
|
||||
|
||||
expect(result).toEqual([])
|
||||
})
|
||||
|
||||
it('should propagate API errors', async () => {
|
||||
mockVideosList.mockRejectedValueOnce(new Error('API quota exceeded'))
|
||||
|
||||
const { YouTubeClient } = await import(
|
||||
'@/lib/integrations/youtube/YouTubeClient'
|
||||
)
|
||||
|
||||
const client = new YouTubeClient(
|
||||
{
|
||||
clientId: 'test-id',
|
||||
clientSecret: 'test-secret',
|
||||
accessToken: 'test-token',
|
||||
refreshToken: 'test-refresh',
|
||||
},
|
||||
mockPayload,
|
||||
)
|
||||
|
||||
await expect(
|
||||
client.getVideoStatistics(['vid1']),
|
||||
).rejects.toThrow('API quota exceeded')
|
||||
})
|
||||
})
|
||||
Loading…
Reference in a new issue