]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/lib/peertube-socket.ts
Merge remote-tracking branch 'weblate/develop' into develop
[github/Chocobozzz/PeerTube.git] / server / lib / peertube-socket.ts
CommitLineData
41fb13c3
C
1import { Server as HTTPServer } from 'http'
2import { Namespace, Server as SocketServer, Socket } from 'socket.io'
3import { isIdValid } from '@server/helpers/custom-validators/misc'
51353d9a 4import { MVideo, MVideoImmutable } from '@server/types/models'
0c9668f7 5import { MRunner } from '@server/types/models/runners'
26d6bf65 6import { UserNotificationModelForApi } from '@server/types/models/user'
a5cf76af
C
7import { LiveVideoEventPayload, LiveVideoEventType } from '@shared/models'
8import { logger } from '../helpers/logger'
0c9668f7
C
9import { authenticateRunnerSocket, authenticateSocket } from '../middlewares'
10import { Debounce } from '@server/helpers/debounce'
cef534ed
C
11
12class PeerTubeSocket {
13
14 private static instance: PeerTubeSocket
15
41fb13c3
C
16 private userNotificationSockets: { [ userId: number ]: Socket[] } = {}
17 private liveVideosNamespace: Namespace
0c9668f7 18 private readonly runnerSockets = new Set<Socket>()
cef534ed
C
19
20 private constructor () {}
21
41fb13c3
C
22 init (server: HTTPServer) {
23 const io = new SocketServer(server)
cef534ed
C
24
25 io.of('/user-notifications')
26 .use(authenticateSocket)
27 .on('connection', socket => {
fbd51e69 28 const userId = socket.handshake.auth.user.id
cef534ed 29
0c9668f7 30 logger.debug('User %d connected to the notification system.', userId)
cef534ed 31
1b42d73f
C
32 if (!this.userNotificationSockets[userId]) this.userNotificationSockets[userId] = []
33
34 this.userNotificationSockets[userId].push(socket)
cef534ed
C
35
36 socket.on('disconnect', () => {
37 logger.debug('User %d disconnected from SocketIO notifications.', userId)
38
1b42d73f 39 this.userNotificationSockets[userId] = this.userNotificationSockets[userId].filter(s => s !== socket)
cef534ed
C
40 })
41 })
a5cf76af
C
42
43 this.liveVideosNamespace = io.of('/live-videos')
44 .on('connection', socket => {
bd54ad19
C
45 socket.on('subscribe', ({ videoId }) => {
46 if (!isIdValid(videoId)) return
47
1a578165 48 /* eslint-disable @typescript-eslint/no-floating-promises */
bd54ad19
C
49 socket.join(videoId)
50 })
51
52 socket.on('unsubscribe', ({ videoId }) => {
53 if (!isIdValid(videoId)) return
54
1a578165 55 /* eslint-disable @typescript-eslint/no-floating-promises */
bd54ad19
C
56 socket.leave(videoId)
57 })
a5cf76af 58 })
0c9668f7
C
59
60 io.of('/runners')
61 .use(authenticateRunnerSocket)
62 .on('connection', socket => {
63 const runner: MRunner = socket.handshake.auth.runner
64
65 logger.debug(`New runner "${runner.name}" connected to the notification system.`)
66
67 this.runnerSockets.add(socket)
68
69 socket.on('disconnect', () => {
70 logger.debug(`Runner "${runner.name}" disconnected from the notification system.`)
71
72 this.runnerSockets.delete(socket)
73 })
74 })
cef534ed
C
75 }
76
453e83ea 77 sendNotification (userId: number, notification: UserNotificationModelForApi) {
1b42d73f 78 const sockets = this.userNotificationSockets[userId]
1b42d73f 79 if (!sockets) return
cef534ed 80
a5cf76af
C
81 logger.debug('Sending user notification to user %d.', userId)
82
20ec0384 83 const notificationMessage = notification.toFormattedJSON()
1b42d73f 84 for (const socket of sockets) {
20ec0384 85 socket.emit('new-notification', notificationMessage)
1b42d73f 86 }
cef534ed
C
87 }
88
a5cf76af
C
89 sendVideoLiveNewState (video: MVideo) {
90 const data: LiveVideoEventPayload = { state: video.state }
91 const type: LiveVideoEventType = 'state-change'
92
a800dbf3
C
93 logger.debug('Sending video live new state notification of %s.', video.url, { state: video.state })
94
95 this.liveVideosNamespace
96 .in(video.id)
97 .emit(type, data)
98 }
99
51353d9a
C
100 sendVideoViewsUpdate (video: MVideoImmutable, numViewers: number) {
101 const data: LiveVideoEventPayload = { viewers: numViewers, views: numViewers }
a800dbf3
C
102 const type: LiveVideoEventType = 'views-change'
103
51353d9a 104 logger.debug('Sending video live views update notification of %s.', video.url, { viewers: numViewers })
a5cf76af
C
105
106 this.liveVideosNamespace
107 .in(video.id)
108 .emit(type, data)
109 }
110
0c9668f7
C
111 @Debounce({ timeoutMS: 1000 })
112 sendAvailableJobsPingToRunners () {
113 logger.debug(`Sending available-jobs notification to ${this.runnerSockets.size} runner sockets`)
114
115 for (const runners of this.runnerSockets) {
116 runners.emit('available-jobs')
117 }
118 }
119
cef534ed
C
120 static get Instance () {
121 return this.instance || (this.instance = new this())
122 }
123}
124
125// ---------------------------------------------------------------------------
126
127export {
128 PeerTubeSocket
129}