video-views-manager.ts 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. import { VideoViewEvent } from '@peertube/peertube-models'
  2. import { sha256 } from '@peertube/peertube-node-utils'
  3. import { logger, loggerTagsFactory } from '@server/helpers/logger.js'
  4. import { CONFIG } from '@server/initializers/config.js'
  5. import { MVideo, MVideoImmutable } from '@server/types/models/index.js'
  6. import { VideoScope, VideoViewerCounters, VideoViewerStats, VideoViews, ViewerScope } from './shared/index.js'
  7. /**
  8. * If processing a local view:
  9. * - We update viewer information (segments watched, watch time etc)
  10. * - We add +1 to video viewers counter if this is a new viewer
  11. * - We add +1 to video views counter if this is a new view and if the user watched enough seconds
  12. * - We send AP message to notify about this viewer and this view
  13. * - We update last video time for the user if authenticated
  14. *
  15. * If processing a remote view:
  16. * - We add +1 to video viewers counter
  17. * - We add +1 to video views counter
  18. *
  19. * A viewer is a someone that watched one or multiple sections of a video
  20. * A viewer that watched only a few seconds of a video may not increment the video views counter
  21. * Viewers statistics are sent to origin instance using the `WatchAction` ActivityPub object
  22. *
  23. */
  24. const lTags = loggerTagsFactory('views')
  25. export class VideoViewsManager {
  26. private static instance: VideoViewsManager
  27. private videoViewerStats: VideoViewerStats
  28. private videoViewerCounters: VideoViewerCounters
  29. private videoViews: VideoViews
  30. private constructor () {
  31. }
  32. init () {
  33. this.videoViewerStats = new VideoViewerStats()
  34. this.videoViewerCounters = new VideoViewerCounters()
  35. this.videoViews = new VideoViews()
  36. }
  37. async processLocalView (options: {
  38. video: MVideoImmutable
  39. currentTime: number
  40. ip: string | null
  41. sessionId?: string
  42. viewEvent?: VideoViewEvent
  43. }) {
  44. const { video, ip, viewEvent, currentTime } = options
  45. let sessionId = options.sessionId
  46. if (!sessionId || CONFIG.VIEWS.VIDEOS.TRUST_VIEWER_SESSION_ID !== true) {
  47. sessionId = sha256(CONFIG.SECRETS + '-' + ip)
  48. }
  49. logger.debug(`Processing local view for ${video.url}, ip ${ip} and session id ${sessionId}.`, lTags())
  50. await this.videoViewerStats.addLocalViewer({ video, ip, sessionId, viewEvent, currentTime })
  51. const successViewer = await this.videoViewerCounters.addLocalViewer({ video, sessionId })
  52. // Do it after added local viewer to fetch updated information
  53. const watchTime = await this.videoViewerStats.getWatchTime(video.id, sessionId)
  54. const successView = await this.videoViews.addLocalView({ video, watchTime, sessionId })
  55. return { successView, successViewer }
  56. }
  57. async processRemoteView (options: {
  58. video: MVideo
  59. viewerId: string | null
  60. viewerExpires?: Date
  61. viewerResultCounter?: number
  62. }) {
  63. const { video, viewerId, viewerExpires, viewerResultCounter } = options
  64. logger.debug('Processing remote view for %s.', video.url, { viewerExpires, viewerId, ...lTags() })
  65. // Viewer
  66. if (viewerExpires) {
  67. if (video.remote === false) {
  68. this.videoViewerCounters.addRemoteViewerOnLocalVideo({ video, viewerId, viewerExpires })
  69. return
  70. }
  71. this.videoViewerCounters.addRemoteViewerOnRemoteVideo({ video, viewerId, viewerExpires, viewerResultCounter })
  72. return
  73. }
  74. // Just a view
  75. await this.videoViews.addRemoteView({ video })
  76. }
  77. getTotalViewersOf (video: MVideo) {
  78. return this.videoViewerCounters.getTotalViewersOf(video)
  79. }
  80. getTotalViewers (options: {
  81. viewerScope: ViewerScope
  82. videoScope: VideoScope
  83. }) {
  84. return this.videoViewerCounters.getTotalViewers(options)
  85. }
  86. buildViewerExpireTime () {
  87. return this.videoViewerCounters.buildViewerExpireTime()
  88. }
  89. processViewerStats () {
  90. return this.videoViewerStats.processViewerStats()
  91. }
  92. static get Instance () {
  93. return this.instance || (this.instance = new this())
  94. }
  95. }