aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/initializers
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2022-03-24 13:36:47 +0100
committerChocobozzz <chocobozzz@cpy.re>2022-04-15 09:49:35 +0200
commitb211106695bb82f6c32e53306081b5262c3d109d (patch)
treefa187de1c33b0956665f5362e29af6b0f6d8bb57 /server/initializers
parent69d48ee30c9d47cddf0c3c047dc99a99dcb6e894 (diff)
downloadPeerTube-b211106695bb82f6c32e53306081b5262c3d109d.tar.gz
PeerTube-b211106695bb82f6c32e53306081b5262c3d109d.tar.zst
PeerTube-b211106695bb82f6c32e53306081b5262c3d109d.zip
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
Diffstat (limited to 'server/initializers')
-rw-r--r--server/initializers/checker-before-init.ts1
-rw-r--r--server/initializers/config.ts6
-rw-r--r--server/initializers/constants.ts17
-rw-r--r--server/initializers/database.ts10
-rw-r--r--server/initializers/migrations/0705-local-video-viewers.ts52
5 files changed, 81 insertions, 5 deletions
diff --git a/server/initializers/checker-before-init.ts b/server/initializers/checker-before-init.ts
index 0f23a2d73..f2ef3d567 100644
--- a/server/initializers/checker-before-init.ts
+++ b/server/initializers/checker-before-init.ts
@@ -44,6 +44,7 @@ function checkMissedConfig () {
44 'history.videos.max_age', 'views.videos.remote.max_age', 'views.videos.local_buffer_update_interval', 'views.videos.ip_view_expiration', 44 'history.videos.max_age', 'views.videos.remote.max_age', 'views.videos.local_buffer_update_interval', 'views.videos.ip_view_expiration',
45 'rates_limit.login.window', 'rates_limit.login.max', 'rates_limit.ask_send_email.window', 'rates_limit.ask_send_email.max', 45 'rates_limit.login.window', 'rates_limit.login.max', 'rates_limit.ask_send_email.window', 'rates_limit.ask_send_email.max',
46 'theme.default', 46 'theme.default',
47 'geo_ip.enabled', 'geo_ip.country.database_url',
47 'remote_redundancy.videos.accept_from', 48 'remote_redundancy.videos.accept_from',
48 'federation.videos.federate_unlisted', 'federation.videos.cleanup_remote_interactions', 49 'federation.videos.federate_unlisted', 'federation.videos.cleanup_remote_interactions',
49 'peertube.check_latest_version.enabled', 'peertube.check_latest_version.url', 50 'peertube.check_latest_version.enabled', 'peertube.check_latest_version.url',
diff --git a/server/initializers/config.ts b/server/initializers/config.ts
index 122cb9472..d8f5f3496 100644
--- a/server/initializers/config.ts
+++ b/server/initializers/config.ts
@@ -215,6 +215,12 @@ const CONFIG = {
215 IP_VIEW_EXPIRATION: parseDurationToMs(config.get('views.videos.ip_view_expiration')) 215 IP_VIEW_EXPIRATION: parseDurationToMs(config.get('views.videos.ip_view_expiration'))
216 } 216 }
217 }, 217 },
218 GEO_IP: {
219 ENABLED: config.get<boolean>('geo_ip.enabled'),
220 COUNTRY: {
221 DATABASE_URL: config.get<string>('geo_ip.country.database_url')
222 }
223 },
218 PLUGINS: { 224 PLUGINS: {
219 INDEX: { 225 INDEX: {
220 ENABLED: config.get<boolean>('plugins.index.enabled'), 226 ENABLED: config.get<boolean>('plugins.index.enabled'),
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts
index 6bcefe0db..4929923dc 100644
--- a/server/initializers/constants.ts
+++ b/server/initializers/constants.ts
@@ -24,7 +24,7 @@ import { CONFIG, registerConfigChangedHandler } from './config'
24 24
25// --------------------------------------------------------------------------- 25// ---------------------------------------------------------------------------
26 26
27const LAST_MIGRATION_VERSION = 700 27const LAST_MIGRATION_VERSION = 705
28 28
29// --------------------------------------------------------------------------- 29// ---------------------------------------------------------------------------
30 30
@@ -228,6 +228,7 @@ const SCHEDULER_INTERVALS_MS = {
228 REMOVE_OLD_JOBS: 60000 * 60, // 1 hour 228 REMOVE_OLD_JOBS: 60000 * 60, // 1 hour
229 UPDATE_VIDEOS: 60000, // 1 minute 229 UPDATE_VIDEOS: 60000, // 1 minute
230 YOUTUBE_DL_UPDATE: 60000 * 60 * 24, // 1 day 230 YOUTUBE_DL_UPDATE: 60000 * 60 * 24, // 1 day
231 GEO_IP_UPDATE: 60000 * 60 * 24, // 1 day
231 VIDEO_VIEWS_BUFFER_UPDATE: CONFIG.VIEWS.VIDEOS.LOCAL_BUFFER_UPDATE_INTERVAL, 232 VIDEO_VIEWS_BUFFER_UPDATE: CONFIG.VIEWS.VIDEOS.LOCAL_BUFFER_UPDATE_INTERVAL,
232 CHECK_PLUGINS: CONFIG.PLUGINS.INDEX.CHECK_LATEST_VERSIONS_INTERVAL, 233 CHECK_PLUGINS: CONFIG.PLUGINS.INDEX.CHECK_LATEST_VERSIONS_INTERVAL,
233 CHECK_PEERTUBE_VERSION: 60000 * 60 * 24, // 1 day 234 CHECK_PEERTUBE_VERSION: 60000 * 60 * 24, // 1 day
@@ -366,9 +367,12 @@ const CONSTRAINTS_FIELDS = {
366 367
367const VIEW_LIFETIME = { 368const VIEW_LIFETIME = {
368 VIEW: CONFIG.VIEWS.VIDEOS.IP_VIEW_EXPIRATION, 369 VIEW: CONFIG.VIEWS.VIDEOS.IP_VIEW_EXPIRATION,
369 VIEWER: 60000 * 5 // 5 minutes 370 VIEWER: 60000 * 5, // 5 minutes
371 VIEWER_STATS: 60000 * 60 // 1 hour
370} 372}
371 373
374const MAX_LOCAL_VIEWER_WATCH_SECTIONS = 10
375
372let CONTACT_FORM_LIFETIME = 60000 * 60 // 1 hour 376let CONTACT_FORM_LIFETIME = 60000 * 60 // 1 hour
373 377
374const VIDEO_TRANSCODING_FPS: VideoTranscodingFPS = { 378const VIDEO_TRANSCODING_FPS: VideoTranscodingFPS = {
@@ -800,6 +804,12 @@ const SEARCH_INDEX = {
800 804
801// --------------------------------------------------------------------------- 805// ---------------------------------------------------------------------------
802 806
807const STATS_TIMESERIE = {
808 MAX_DAYS: 30
809}
810
811// ---------------------------------------------------------------------------
812
803// Special constants for a test instance 813// Special constants for a test instance
804if (isTestInstance() === true) { 814if (isTestInstance() === true) {
805 PRIVATE_RSA_KEY_SIZE = 1024 815 PRIVATE_RSA_KEY_SIZE = 1024
@@ -836,6 +846,7 @@ if (isTestInstance() === true) {
836 REDUNDANCY.VIDEOS.RANDOMIZED_FACTOR = 1 846 REDUNDANCY.VIDEOS.RANDOMIZED_FACTOR = 1
837 847
838 VIEW_LIFETIME.VIEWER = 1000 * 5 // 5 second 848 VIEW_LIFETIME.VIEWER = 1000 * 5 // 5 second
849 VIEW_LIFETIME.VIEWER_STATS = 1000 * 5 // 5 second
839 CONTACT_FORM_LIFETIME = 1000 // 1 second 850 CONTACT_FORM_LIFETIME = 1000 // 1 second
840 851
841 JOB_ATTEMPTS['email'] = 1 852 JOB_ATTEMPTS['email'] = 1
@@ -907,6 +918,7 @@ export {
907 LAST_MIGRATION_VERSION, 918 LAST_MIGRATION_VERSION,
908 OAUTH_LIFETIME, 919 OAUTH_LIFETIME,
909 CUSTOM_HTML_TAG_COMMENTS, 920 CUSTOM_HTML_TAG_COMMENTS,
921 STATS_TIMESERIE,
910 BROADCAST_CONCURRENCY, 922 BROADCAST_CONCURRENCY,
911 AUDIT_LOG_FILENAME, 923 AUDIT_LOG_FILENAME,
912 PAGINATION, 924 PAGINATION,
@@ -949,6 +961,7 @@ export {
949 ABUSE_STATES, 961 ABUSE_STATES,
950 LRU_CACHE, 962 LRU_CACHE,
951 REQUEST_TIMEOUTS, 963 REQUEST_TIMEOUTS,
964 MAX_LOCAL_VIEWER_WATCH_SECTIONS,
952 USER_PASSWORD_RESET_LIFETIME, 965 USER_PASSWORD_RESET_LIFETIME,
953 USER_PASSWORD_CREATE_LIFETIME, 966 USER_PASSWORD_CREATE_LIFETIME,
954 MEMOIZE_TTL, 967 MEMOIZE_TTL,
diff --git a/server/initializers/database.ts b/server/initializers/database.ts
index 0e690f6ae..7a7ba61f4 100644
--- a/server/initializers/database.ts
+++ b/server/initializers/database.ts
@@ -1,10 +1,14 @@
1import { QueryTypes, Transaction } from 'sequelize' 1import { QueryTypes, Transaction } from 'sequelize'
2import { Sequelize as SequelizeTypescript } from 'sequelize-typescript' 2import { Sequelize as SequelizeTypescript } from 'sequelize-typescript'
3import { ActorCustomPageModel } from '@server/models/account/actor-custom-page'
3import { TrackerModel } from '@server/models/server/tracker' 4import { TrackerModel } from '@server/models/server/tracker'
4import { VideoTrackerModel } from '@server/models/server/video-tracker' 5import { VideoTrackerModel } from '@server/models/server/video-tracker'
5import { UserModel } from '@server/models/user/user' 6import { UserModel } from '@server/models/user/user'
6import { UserNotificationModel } from '@server/models/user/user-notification' 7import { UserNotificationModel } from '@server/models/user/user-notification'
7import { UserVideoHistoryModel } from '@server/models/user/user-video-history' 8import { UserVideoHistoryModel } from '@server/models/user/user-video-history'
9import { VideoJobInfoModel } from '@server/models/video/video-job-info'
10import { LocalVideoViewerModel } from '@server/models/view/local-video-viewer'
11import { LocalVideoViewerWatchSectionModel } from '@server/models/view/local-video-viewer-watch-section'
8import { isTestInstance } from '../helpers/core-utils' 12import { isTestInstance } from '../helpers/core-utils'
9import { logger } from '../helpers/logger' 13import { logger } from '../helpers/logger'
10import { AbuseModel } from '../models/abuse/abuse' 14import { AbuseModel } from '../models/abuse/abuse'
@@ -42,10 +46,8 @@ import { VideoPlaylistElementModel } from '../models/video/video-playlist-elemen
42import { VideoShareModel } from '../models/video/video-share' 46import { VideoShareModel } from '../models/video/video-share'
43import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist' 47import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist'
44import { VideoTagModel } from '../models/video/video-tag' 48import { VideoTagModel } from '../models/video/video-tag'
45import { VideoViewModel } from '../models/video/video-view' 49import { VideoViewModel } from '../models/view/video-view'
46import { CONFIG } from './config' 50import { CONFIG } from './config'
47import { ActorCustomPageModel } from '@server/models/account/actor-custom-page'
48import { VideoJobInfoModel } from '@server/models/video/video-job-info'
49 51
50require('pg').defaults.parseInt8 = true // Avoid BIGINT to be converted to string 52require('pg').defaults.parseInt8 = true // Avoid BIGINT to be converted to string
51 53
@@ -140,6 +142,8 @@ async function initDatabaseModels (silent: boolean) {
140 VideoStreamingPlaylistModel, 142 VideoStreamingPlaylistModel,
141 VideoPlaylistModel, 143 VideoPlaylistModel,
142 VideoPlaylistElementModel, 144 VideoPlaylistElementModel,
145 LocalVideoViewerModel,
146 LocalVideoViewerWatchSectionModel,
143 ThumbnailModel, 147 ThumbnailModel,
144 TrackerModel, 148 TrackerModel,
145 VideoTrackerModel, 149 VideoTrackerModel,
diff --git a/server/initializers/migrations/0705-local-video-viewers.ts b/server/initializers/migrations/0705-local-video-viewers.ts
new file mode 100644
index 000000000..123402641
--- /dev/null
+++ b/server/initializers/migrations/0705-local-video-viewers.ts
@@ -0,0 +1,52 @@
1import * as Sequelize from 'sequelize'
2
3async function up (utils: {
4 transaction: Sequelize.Transaction
5 queryInterface: Sequelize.QueryInterface
6 sequelize: Sequelize.Sequelize
7 db: any
8}): Promise<void> {
9 const { transaction } = utils
10
11 {
12 const query = `
13 CREATE TABLE IF NOT EXISTS "localVideoViewer" (
14 "id" serial,
15 "startDate" timestamp with time zone NOT NULL,
16 "endDate" timestamp with time zone NOT NULL,
17 "watchTime" integer NOT NULL,
18 "country" varchar(255),
19 "uuid" uuid NOT NULL,
20 "url" varchar(255) NOT NULL,
21 "videoId" integer NOT NULL REFERENCES "video" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
22 "createdAt" timestamp with time zone NOT NULL,
23 PRIMARY KEY ("id")
24 );
25 `
26 await utils.sequelize.query(query, { transaction })
27 }
28
29 {
30 const query = `
31 CREATE TABLE IF NOT EXISTS "localVideoViewerWatchSection" (
32 "id" serial,
33 "watchStart" integer NOT NULL,
34 "watchEnd" integer NOT NULL,
35 "localVideoViewerId" integer NOT NULL REFERENCES "localVideoViewer" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
36 "createdAt" timestamp with time zone NOT NULL,
37 PRIMARY KEY ("id")
38 );
39 `
40 await utils.sequelize.query(query, { transaction })
41 }
42
43}
44
45function down () {
46 throw new Error('Not implemented.')
47}
48
49export {
50 up,
51 down
52}