From fe37e5232b78e1502d8eb4510b042564eaa20536 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Wed, 13 Dec 2023 10:06:25 +0100 Subject: [PATCH] Add ability to customize views/playback interval --- .../+video-watch/video-watch.component.ts | 13 +++++--- client/src/assets/player/peertube-player.ts | 1 + .../player/shared/metrics/metrics-plugin.ts | 7 ++--- .../player/types/peertube-player-options.ts | 2 ++ .../player/types/peertube-videojs-typings.ts | 1 + .../videos/shared/player-options-builder.ts | 9 ++++-- config/default.yaml | 14 +++++++++ config/dev.yaml | 5 ++++ config/production.yaml.example | 14 +++++++++ .../models/src/server/server-config.model.ts | 21 +++++++++++++ packages/tests/src/api/server/config.ts | 10 +++++++ .../core/initializers/checker-before-init.ts | 6 ++-- server/core/initializers/config.ts | 8 ++++- server/core/lib/server-config-manager.ts | 16 ++++++++++ support/doc/api/openapi.yaml | 30 +++++++++++++++++++ 15 files changed, 143 insertions(+), 14 deletions(-) diff --git a/client/src/app/+videos/+video-watch/video-watch.component.ts b/client/src/app/+videos/+video-watch/video-watch.component.ts index 7b8af3e32..23c0fba5b 100644 --- a/client/src/app/+videos/+video-watch/video-watch.component.ts +++ b/client/src/app/+videos/+video-watch/video-watch.component.ts @@ -113,8 +113,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy { private hotkeys: Hotkey[] = [] - private static VIEW_VIDEO_INTERVAL_MS = 5000 - constructor ( private route: ActivatedRoute, private router: Router, @@ -625,9 +623,16 @@ export class VideoWatchComponent implements OnInit, OnDestroy { instanceName: this.serverConfig.instance.name, language: this.localeId, - metricsUrl: environment.apiUrl + '/api/v1/metrics/playback', - videoViewIntervalMs: VideoWatchComponent.VIEW_VIDEO_INTERVAL_MS, + metricsUrl: this.serverConfig.openTelemetry.metrics.enabled + ? environment.apiUrl + '/api/v1/metrics/playback' + : null, + metricsInterval: this.serverConfig.openTelemetry.metrics.playbackStatsInterval, + + videoViewIntervalMs: this.isUserLoggedIn() + ? this.serverConfig.views.videos.watchingInterval.users + : this.serverConfig.views.videos.watchingInterval.anonymous, + authorizationHeader: () => this.authService.getRequestHeaderValue(), serverUrl: environment.originServerUrl || window.location.origin, diff --git a/client/src/assets/player/peertube-player.ts b/client/src/assets/player/peertube-player.ts index 6f0d30e1d..6e1c531cc 100644 --- a/client/src/assets/player/peertube-player.ts +++ b/client/src/assets/player/peertube-player.ts @@ -382,6 +382,7 @@ export class PeerTubePlayer { mode: () => this.currentLoadOptions.mode, metricsUrl: () => this.options.metricsUrl, + metricsInterval: () => this.options.metricsInterval, videoUUID: () => this.currentLoadOptions.videoUUID } } diff --git a/client/src/assets/player/shared/metrics/metrics-plugin.ts b/client/src/assets/player/shared/metrics/metrics-plugin.ts index a581d7931..00180cfbf 100644 --- a/client/src/assets/player/shared/metrics/metrics-plugin.ts +++ b/client/src/assets/player/shared/metrics/metrics-plugin.ts @@ -25,10 +25,6 @@ class MetricsPlugin extends Plugin { private metricsInterval: any - private readonly CONSTANTS = { - METRICS_INTERVAL: 15000 - } - constructor (player: videojs.Player, options: MetricsPluginOptions) { super(player) @@ -74,6 +70,7 @@ class MetricsPlugin extends Plugin { private runMetricsInterval () { if (this.metricsInterval) clearInterval(this.metricsInterval) + if (!this.options_.metricsUrl()) return this.metricsInterval = setInterval(() => { let resolution: number @@ -135,7 +132,7 @@ class MetricsPlugin extends Plugin { return fetch(this.options_.metricsUrl(), { method: 'POST', body: JSON.stringify(body), headers }) .catch(err => logger.error('Cannot send metrics to the server.', err)) - }, this.CONSTANTS.METRICS_INTERVAL) + }, this.options_.metricsInterval()) } private trackBytes () { diff --git a/client/src/assets/player/types/peertube-player-options.ts b/client/src/assets/player/types/peertube-player-options.ts index 32f26fa9e..4efabf672 100644 --- a/client/src/assets/player/types/peertube-player-options.ts +++ b/client/src/assets/player/types/peertube-player-options.ts @@ -30,6 +30,8 @@ export type PeerTubePlayerContructorOptions = { authorizationHeader: () => string metricsUrl: string + metricsInterval: number + serverUrl: string errorNotifier: (message: string) => void diff --git a/client/src/assets/player/types/peertube-videojs-typings.ts b/client/src/assets/player/types/peertube-videojs-typings.ts index 108a1035d..3aad7f492 100644 --- a/client/src/assets/player/types/peertube-videojs-typings.ts +++ b/client/src/assets/player/types/peertube-videojs-typings.ts @@ -135,6 +135,7 @@ type PeerTubePluginOptions = { type MetricsPluginOptions = { mode: () => PlayerMode metricsUrl: () => string + metricsInterval: () => number videoUUID: () => string } diff --git a/client/src/standalone/videos/shared/player-options-builder.ts b/client/src/standalone/videos/shared/player-options-builder.ts index a38895cd6..629476501 100644 --- a/client/src/standalone/videos/shared/player-options-builder.ts +++ b/client/src/standalone/videos/shared/player-options-builder.ts @@ -186,8 +186,13 @@ export class PlayerOptionsBuilder { playbackRate: this.playbackRate, inactivityTimeout: 2500, - videoViewIntervalMs: 5000, - metricsUrl: window.location.origin + '/api/v1/metrics/playback', + + videoViewIntervalMs: serverConfig.views.videos.watchingInterval.anonymous, + + metricsUrl: serverConfig.openTelemetry.metrics.enabled + ? window.location.origin + '/api/v1/metrics/playback' + : null, + metricsInterval: serverConfig.openTelemetry.metrics.playbackStatsInterval, authorizationHeader, diff --git a/config/default.yaml b/config/default.yaml index 81e22e350..7801f8d1b 100644 --- a/config/default.yaml +++ b/config/default.yaml @@ -254,6 +254,9 @@ open_telemetry: metrics: enabled: false + # How often viewers send playback stats to server + playback_stats_interval: '15 seconds' + http_request_duration: # You can disable HTTP request duration metric that can have a high tag cardinality enabled: true @@ -362,6 +365,17 @@ views: ip_view_expiration: '1 hour' + # How often the web browser sends "is watching" information to the server + # Increase the value or set null to disable it if you plan to have many viewers + watching_interval: + # Non logged-in viewers + anonymous: '5 seconds' + + # Logged-in users of your instance + # Unlike anonymous viewers, this endpoint is also used to store the "last watched video timecode" for your users + # Increasing this value reduces the accuracy of the video resume + users: '5 seconds' + # Used to get country location of views of local videos geo_ip: enabled: true diff --git a/config/dev.yaml b/config/dev.yaml index 921b9f913..2e0cf910c 100644 --- a/config/dev.yaml +++ b/config/dev.yaml @@ -38,6 +38,7 @@ log: open_telemetry: metrics: enabled: true + playback_stats_interval: '13 seconds' contact_form: enabled: true @@ -118,6 +119,10 @@ views: remote: max_age: -1 + watching_interval: + anonymous: '6 seconds' + users: '4 seconds' + geo_ip: enabled: true diff --git a/config/production.yaml.example b/config/production.yaml.example index 827e4219c..a1e0400c7 100644 --- a/config/production.yaml.example +++ b/config/production.yaml.example @@ -252,6 +252,9 @@ open_telemetry: metrics: enabled: false + # How often viewers send playback stats to server + playback_stats_interval: '15 seconds' + http_request_duration: # You can disable HTTP request duration metric that can have a high tag cardinality enabled: true @@ -360,6 +363,17 @@ views: ip_view_expiration: '1 hour' + # How often the web browser sends "is watching" information to the server + # Increase the value or set null to disable it if you plan to have many viewers + watching_interval: + # Non logged-in viewers + anonymous: '5 seconds' + + # Logged-in users of your instance + # Unlike anonymous viewers, this endpoint is also used to store the "last watched video timecode" for your users + # Increasing this value reduces the accuracy of the video resume + users: '5 seconds' + # Used to get country location of views of local videos geo_ip: enabled: true diff --git a/packages/models/src/server/server-config.model.ts b/packages/models/src/server/server-config.model.ts index a2a2bd5aa..a5012a2b2 100644 --- a/packages/models/src/server/server-config.model.ts +++ b/packages/models/src/server/server-config.model.ts @@ -300,6 +300,27 @@ export interface ServerConfig { homepage: { enabled: boolean } + + openTelemetry: { + metrics: { + enabled: boolean + + // milliseconds + playbackStatsInterval: number + } + } + + views: { + videos: { + watchingInterval: { + // milliseconds + anonymous: number + + // milliseconds + users: number + } + } + } } export type HTMLServerConfig = Omit diff --git a/packages/tests/src/api/server/config.ts b/packages/tests/src/api/server/config.ts index 37beb4597..58882488b 100644 --- a/packages/tests/src/api/server/config.ts +++ b/packages/tests/src/api/server/config.ts @@ -498,6 +498,16 @@ describe('Test config', function () { await setAccessTokensToServers([ server ]) }) + it('Should have the correct default config', async function () { + const data = await server.config.getConfig() + + expect(data.openTelemetry.metrics.enabled).to.be.false + expect(data.openTelemetry.metrics.playbackStatsInterval).to.equal(15000) + + expect(data.views.videos.watchingInterval.anonymous).to.equal(5000) + expect(data.views.videos.watchingInterval.users).to.equal(5000) + }) + it('Should have a correct config on a server with registration enabled', async function () { const data = await server.config.getConfig() diff --git a/server/core/initializers/checker-before-init.ts b/server/core/initializers/checker-before-init.ts index e6fd56461..6a7706eaa 100644 --- a/server/core/initializers/checker-before-init.ts +++ b/server/core/initializers/checker-before-init.ts @@ -19,8 +19,9 @@ function checkMissedConfig () { 'storage.redundancy', 'storage.tmp', 'storage.streaming_playlists', 'storage.plugins', 'storage.well_known', 'log.level', 'log.rotation.enabled', 'log.rotation.max_file_size', 'log.rotation.max_files', 'log.anonymize_ip', 'log.log_ping_requests', 'log.log_tracker_unknown_infohash', 'log.prettify_sql', 'log.accept_client_log', - 'open_telemetry.metrics.enabled', 'open_telemetry.metrics.prometheus_exporter.hostname', - 'open_telemetry.metrics.prometheus_exporter.port', 'open_telemetry.tracing.enabled', 'open_telemetry.tracing.jaeger_exporter.endpoint', + 'open_telemetry.metrics.enabled', 'open_telemetry.metrics.playback_stats_interval', + 'open_telemetry.metrics.prometheus_exporter.hostname', 'open_telemetry.metrics.prometheus_exporter.port', + 'open_telemetry.tracing.enabled', 'open_telemetry.tracing.jaeger_exporter.endpoint', 'open_telemetry.metrics.http_request_duration.enabled', 'user.history.videos.enabled', 'user.video_quota', 'user.video_quota_daily', 'video_channels.max_per_user', @@ -54,6 +55,7 @@ function checkMissedConfig () { 'followers.instance.enabled', 'followers.instance.manual_approval', 'tracker.enabled', 'tracker.private', 'tracker.reject_too_many_announces', 'history.videos.max_age', 'views.videos.remote.max_age', 'views.videos.local_buffer_update_interval', 'views.videos.ip_view_expiration', + 'views.videos.watching_interval.anonymous', 'views.videos.watching_interval.users', 'rates_limit.api.window', 'rates_limit.api.max', 'rates_limit.login.window', 'rates_limit.login.max', 'rates_limit.signup.window', 'rates_limit.signup.max', 'rates_limit.ask_send_email.window', 'rates_limit.ask_send_email.max', 'rates_limit.receive_client_log.window', 'rates_limit.receive_client_log.max', 'rates_limit.plugins.window', 'rates_limit.plugins.max', diff --git a/server/core/initializers/config.ts b/server/core/initializers/config.ts index a5380308a..3c64162a3 100644 --- a/server/core/initializers/config.ts +++ b/server/core/initializers/config.ts @@ -228,6 +228,8 @@ const CONFIG = { METRICS: { ENABLED: config.get('open_telemetry.metrics.enabled'), + PLAYBACK_STATS_INTERVAL: parseDurationToMs(config.get('open_telemetry.metrics.playback_stats_interval')), + HTTP_REQUEST_DURATION: { ENABLED: config.get('open_telemetry.metrics.http_request_duration.enabled') }, @@ -294,7 +296,11 @@ const CONFIG = { MAX_AGE: parseDurationToMs(config.get('views.videos.remote.max_age')) }, LOCAL_BUFFER_UPDATE_INTERVAL: parseDurationToMs(config.get('views.videos.local_buffer_update_interval')), - IP_VIEW_EXPIRATION: parseDurationToMs(config.get('views.videos.ip_view_expiration')) + IP_VIEW_EXPIRATION: parseDurationToMs(config.get('views.videos.ip_view_expiration')), + WATCHING_INTERVAL: { + ANONYMOUS: parseDurationToMs(config.get('views.videos.watching_interval.anonymous')), + USERS: parseDurationToMs(config.get('views.videos.watching_interval.users')) + } } }, GEO_IP: { diff --git a/server/core/lib/server-config-manager.ts b/server/core/lib/server-config-manager.ts index 8b3b957fe..e1969f720 100644 --- a/server/core/lib/server-config-manager.ts +++ b/server/core/lib/server-config-manager.ts @@ -274,6 +274,22 @@ class ServerConfigManager { homepage: { enabled: this.homepageEnabled + }, + + openTelemetry: { + metrics: { + enabled: CONFIG.OPEN_TELEMETRY.METRICS.ENABLED, + playbackStatsInterval: CONFIG.OPEN_TELEMETRY.METRICS.PLAYBACK_STATS_INTERVAL + } + }, + + views: { + videos: { + watchingInterval: { + anonymous: CONFIG.VIEWS.VIDEOS.WATCHING_INTERVAL.ANONYMOUS, + users: CONFIG.VIEWS.VIDEOS.WATCHING_INTERVAL.USERS + } + } } } } diff --git a/support/doc/api/openapi.yaml b/support/doc/api/openapi.yaml index 4f7e08f8c..78d43ce99 100644 --- a/support/doc/api/openapi.yaml +++ b/support/doc/api/openapi.yaml @@ -8126,6 +8126,36 @@ components: enabled: type: boolean + openTelemetry: + type: object + description: 'PeerTube >= 6.1' + properties: + metrics: + type: object + properties: + enabled: + type: boolean + playbackStatsInterval: + type: number + description: 'Milliseconds' + + views: + type: object + description: 'PeerTube >= 6.1' + properties: + views: + type: object + properties: + watchingInterval: + type: object + properties: + anonymous: + type: number + description: 'Milliseconds' + users: + type: number + description: 'Milliseconds' + SendClientLog: properties: message: