diff options
Diffstat (limited to 'server/lib/live/live-segment-sha-store.ts')
-rw-r--r-- | server/lib/live/live-segment-sha-store.ts | 82 |
1 files changed, 50 insertions, 32 deletions
diff --git a/server/lib/live/live-segment-sha-store.ts b/server/lib/live/live-segment-sha-store.ts index 4af6f3ebf..4d03754a9 100644 --- a/server/lib/live/live-segment-sha-store.ts +++ b/server/lib/live/live-segment-sha-store.ts | |||
@@ -1,61 +1,79 @@ | |||
1 | import { writeJson } from 'fs-extra' | ||
1 | import { basename } from 'path' | 2 | import { basename } from 'path' |
3 | import { mapToJSON } from '@server/helpers/core-utils' | ||
2 | import { logger, loggerTagsFactory } from '@server/helpers/logger' | 4 | import { logger, loggerTagsFactory } from '@server/helpers/logger' |
5 | import { MStreamingPlaylistVideo } from '@server/types/models' | ||
3 | import { buildSha256Segment } from '../hls' | 6 | import { buildSha256Segment } from '../hls' |
7 | import { storeHLSFileFromPath } from '../object-storage' | ||
8 | import PQueue from 'p-queue' | ||
4 | 9 | ||
5 | const lTags = loggerTagsFactory('live') | 10 | const lTags = loggerTagsFactory('live') |
6 | 11 | ||
7 | class LiveSegmentShaStore { | 12 | class LiveSegmentShaStore { |
8 | 13 | ||
9 | private static instance: LiveSegmentShaStore | 14 | private readonly segmentsSha256 = new Map<string, string>() |
10 | 15 | ||
11 | private readonly segmentsSha256 = new Map<string, Map<string, string>>() | 16 | private readonly videoUUID: string |
12 | 17 | private readonly sha256Path: string | |
13 | private constructor () { | 18 | private readonly streamingPlaylist: MStreamingPlaylistVideo |
14 | } | 19 | private readonly sendToObjectStorage: boolean |
15 | 20 | private readonly writeQueue = new PQueue({ concurrency: 1 }) | |
16 | getSegmentsSha256 (videoUUID: string) { | 21 | |
17 | return this.segmentsSha256.get(videoUUID) | 22 | constructor (options: { |
23 | videoUUID: string | ||
24 | sha256Path: string | ||
25 | streamingPlaylist: MStreamingPlaylistVideo | ||
26 | sendToObjectStorage: boolean | ||
27 | }) { | ||
28 | this.videoUUID = options.videoUUID | ||
29 | this.sha256Path = options.sha256Path | ||
30 | this.streamingPlaylist = options.streamingPlaylist | ||
31 | this.sendToObjectStorage = options.sendToObjectStorage | ||
18 | } | 32 | } |
19 | 33 | ||
20 | async addSegmentSha (videoUUID: string, segmentPath: string) { | 34 | async addSegmentSha (segmentPath: string) { |
21 | const segmentName = basename(segmentPath) | 35 | logger.debug('Adding live sha segment %s.', segmentPath, lTags(this.videoUUID)) |
22 | logger.debug('Adding live sha segment %s.', segmentPath, lTags(videoUUID)) | ||
23 | 36 | ||
24 | const shaResult = await buildSha256Segment(segmentPath) | 37 | const shaResult = await buildSha256Segment(segmentPath) |
25 | 38 | ||
26 | if (!this.segmentsSha256.has(videoUUID)) { | 39 | const segmentName = basename(segmentPath) |
27 | this.segmentsSha256.set(videoUUID, new Map()) | 40 | this.segmentsSha256.set(segmentName, shaResult) |
28 | } | ||
29 | 41 | ||
30 | const filesMap = this.segmentsSha256.get(videoUUID) | 42 | try { |
31 | filesMap.set(segmentName, shaResult) | 43 | await this.writeToDisk() |
44 | } catch (err) { | ||
45 | logger.error('Cannot write sha segments to disk.', { err }) | ||
46 | } | ||
32 | } | 47 | } |
33 | 48 | ||
34 | removeSegmentSha (videoUUID: string, segmentPath: string) { | 49 | async removeSegmentSha (segmentPath: string) { |
35 | const segmentName = basename(segmentPath) | 50 | const segmentName = basename(segmentPath) |
36 | 51 | ||
37 | logger.debug('Removing live sha segment %s.', segmentPath, lTags(videoUUID)) | 52 | logger.debug('Removing live sha segment %s.', segmentPath, lTags(this.videoUUID)) |
38 | 53 | ||
39 | const filesMap = this.segmentsSha256.get(videoUUID) | 54 | if (!this.segmentsSha256.has(segmentName)) { |
40 | if (!filesMap) { | 55 | logger.warn('Unknown segment in files map for video %s and segment %s.', this.videoUUID, segmentPath, lTags(this.videoUUID)) |
41 | logger.warn('Unknown files map to remove sha for %s.', videoUUID, lTags(videoUUID)) | ||
42 | return | 56 | return |
43 | } | 57 | } |
44 | 58 | ||
45 | if (!filesMap.has(segmentName)) { | 59 | this.segmentsSha256.delete(segmentName) |
46 | logger.warn('Unknown segment in files map for video %s and segment %s.', videoUUID, segmentPath, lTags(videoUUID)) | ||
47 | return | ||
48 | } | ||
49 | 60 | ||
50 | filesMap.delete(segmentName) | 61 | await this.writeToDisk() |
51 | } | 62 | } |
52 | 63 | ||
53 | cleanupShaSegments (videoUUID: string) { | 64 | private writeToDisk () { |
54 | this.segmentsSha256.delete(videoUUID) | 65 | return this.writeQueue.add(async () => { |
55 | } | 66 | await writeJson(this.sha256Path, mapToJSON(this.segmentsSha256)) |
67 | |||
68 | if (this.sendToObjectStorage) { | ||
69 | const url = await storeHLSFileFromPath(this.streamingPlaylist, this.sha256Path) | ||
56 | 70 | ||
57 | static get Instance () { | 71 | if (this.streamingPlaylist.segmentsSha256Url !== url) { |
58 | return this.instance || (this.instance = new this()) | 72 | this.streamingPlaylist.segmentsSha256Url = url |
73 | await this.streamingPlaylist.save() | ||
74 | } | ||
75 | } | ||
76 | }) | ||
59 | } | 77 | } |
60 | } | 78 | } |
61 | 79 | ||