From b211106695bb82f6c32e53306081b5262c3d109d Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 24 Mar 2022 13:36:47 +0100 Subject: Support video views/viewers stats in server * Add "currentTime" and "event" body params to view endpoint * Merge watching and view endpoints * Introduce WatchAction AP activity * Add tables to store viewer information of local videos * Add endpoints to fetch video views/viewers stats of local videos * Refactor views/viewers handlers * Support "views" and "viewers" counters for both VOD and live videos --- shared/server-commands/server/server.ts | 8 +++- shared/server-commands/videos/history-command.ts | 19 -------- shared/server-commands/videos/index.ts | 1 + .../server-commands/videos/video-stats-command.ts | 48 ++++++++++++++++++++ shared/server-commands/videos/videos-command.ts | 17 -------- shared/server-commands/videos/views-command.ts | 51 ++++++++++++++++++++++ 6 files changed, 107 insertions(+), 37 deletions(-) create mode 100644 shared/server-commands/videos/video-stats-command.ts create mode 100644 shared/server-commands/videos/views-command.ts (limited to 'shared/server-commands') diff --git a/shared/server-commands/server/server.ts b/shared/server-commands/server/server.ts index 2bf31b5a4..0ad818a11 100644 --- a/shared/server-commands/server/server.ts +++ b/shared/server-commands/server/server.ts @@ -25,10 +25,12 @@ import { PlaylistsCommand, ServicesCommand, StreamingPlaylistsCommand, + VideosCommand, VideoStudioCommand, - VideosCommand + ViewsCommand } from '../videos' import { CommentsCommand } from '../videos/comments-command' +import { VideoStatsCommand } from '../videos/video-stats-command' import { ConfigCommand } from './config-command' import { ContactFormCommand } from './contact-form-command' import { DebugCommand } from './debug-command' @@ -127,6 +129,8 @@ export class PeerTubeServer { objectStorage?: ObjectStorageCommand videoStudio?: VideoStudioCommand videos?: VideosCommand + videoStats?: VideoStatsCommand + views?: ViewsCommand constructor (options: { serverNumber: number } | { url: string }) { if ((options as any).url) { @@ -397,5 +401,7 @@ export class PeerTubeServer { this.videos = new VideosCommand(this) this.objectStorage = new ObjectStorageCommand(this) this.videoStudio = new VideoStudioCommand(this) + this.videoStats = new VideoStatsCommand(this) + this.views = new ViewsCommand(this) } } diff --git a/shared/server-commands/videos/history-command.ts b/shared/server-commands/videos/history-command.ts index e9dc63462..d27afcff2 100644 --- a/shared/server-commands/videos/history-command.ts +++ b/shared/server-commands/videos/history-command.ts @@ -3,25 +3,6 @@ import { AbstractCommand, OverrideCommandOptions } from '../shared' export class HistoryCommand extends AbstractCommand { - watchVideo (options: OverrideCommandOptions & { - videoId: number | string - currentTime: number - }) { - const { videoId, currentTime } = options - - const path = '/api/v1/videos/' + videoId + '/watching' - const fields = { currentTime } - - return this.putBodyRequest({ - ...options, - - path, - fields, - implicitToken: true, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - list (options: OverrideCommandOptions & { search?: string } = {}) { diff --git a/shared/server-commands/videos/index.ts b/shared/server-commands/videos/index.ts index c9ef6134d..b861731fb 100644 --- a/shared/server-commands/videos/index.ts +++ b/shared/server-commands/videos/index.ts @@ -13,4 +13,5 @@ export * from './services-command' export * from './streaming-playlists-command' export * from './comments-command' export * from './video-studio-command' +export * from './views-command' export * from './videos-command' diff --git a/shared/server-commands/videos/video-stats-command.ts b/shared/server-commands/videos/video-stats-command.ts new file mode 100644 index 000000000..90f7ffeaf --- /dev/null +++ b/shared/server-commands/videos/video-stats-command.ts @@ -0,0 +1,48 @@ +import { HttpStatusCode, VideoStatsOverall, VideoStatsRetention, VideoStatsTimeserie, VideoStatsTimeserieMetric } from '@shared/models' +import { AbstractCommand, OverrideCommandOptions } from '../shared' + +export class VideoStatsCommand extends AbstractCommand { + + getOverallStats (options: OverrideCommandOptions & { + videoId: number | string + }) { + const path = '/api/v1/videos/' + options.videoId + '/stats/overall' + + return this.getRequestBody({ + ...options, + path, + + implicitToken: true, + defaultExpectedStatus: HttpStatusCode.OK_200 + }) + } + + getTimeserieStats (options: OverrideCommandOptions & { + videoId: number | string + metric: VideoStatsTimeserieMetric + }) { + const path = '/api/v1/videos/' + options.videoId + '/stats/timeseries/' + options.metric + + return this.getRequestBody({ + ...options, + path, + + implicitToken: true, + defaultExpectedStatus: HttpStatusCode.OK_200 + }) + } + + getRetentionStats (options: OverrideCommandOptions & { + videoId: number | string + }) { + const path = '/api/v1/videos/' + options.videoId + '/stats/retention' + + return this.getRequestBody({ + ...options, + path, + + implicitToken: true, + defaultExpectedStatus: HttpStatusCode.OK_200 + }) + } +} diff --git a/shared/server-commands/videos/videos-command.ts b/shared/server-commands/videos/videos-command.ts index 21753ddc4..2ac426f76 100644 --- a/shared/server-commands/videos/videos-command.ts +++ b/shared/server-commands/videos/videos-command.ts @@ -107,23 +107,6 @@ export class VideosCommand extends AbstractCommand { // --------------------------------------------------------------------------- - view (options: OverrideCommandOptions & { - id: number | string - xForwardedFor?: string - }) { - const { id, xForwardedFor } = options - const path = '/api/v1/videos/' + id + '/views' - - return this.postBodyRequest({ - ...options, - - path, - xForwardedFor, - implicitToken: false, - defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - } - rate (options: OverrideCommandOptions & { id: number | string rating: UserVideoRateType diff --git a/shared/server-commands/videos/views-command.ts b/shared/server-commands/videos/views-command.ts new file mode 100644 index 000000000..01113f798 --- /dev/null +++ b/shared/server-commands/videos/views-command.ts @@ -0,0 +1,51 @@ +/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/no-floating-promises */ +import { HttpStatusCode, VideoViewEvent } from '@shared/models' +import { AbstractCommand, OverrideCommandOptions } from '../shared' + +export class ViewsCommand extends AbstractCommand { + + view (options: OverrideCommandOptions & { + id: number | string + currentTime?: number + viewEvent?: VideoViewEvent + xForwardedFor?: string + }) { + const { id, xForwardedFor, viewEvent, currentTime } = options + const path = '/api/v1/videos/' + id + '/views' + + return this.postBodyRequest({ + ...options, + + path, + xForwardedFor, + fields: { + currentTime: currentTime ?? 1, + viewEvent + }, + implicitToken: false, + defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 + }) + } + + async simulateView (options: OverrideCommandOptions & { + id: number | string + xForwardedFor?: string + }) { + await this.view({ ...options, currentTime: 0 }) + await this.view({ ...options, currentTime: 5 }) + } + + async simulateViewer (options: OverrideCommandOptions & { + id: number | string + currentTimes: number[] + xForwardedFor?: string + }) { + let viewEvent: VideoViewEvent = 'seek' + + for (const currentTime of options.currentTimes) { + await this.view({ ...options, currentTime, viewEvent }) + + viewEvent = undefined + } + } +} -- cgit v1.2.3