]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - client/src/assets/player/p2p-media-loader/segment-validator.ts
Delay notification when waiting for a live
[github/Chocobozzz/PeerTube.git] / client / src / assets / player / p2p-media-loader / segment-validator.ts
CommitLineData
210856a7 1import { wait } from '@root-helpers/utils'
09209296
C
2import { Segment } from 'p2p-media-loader-core'
3import { basename } from 'path'
4
c6c0fa6c
C
5type SegmentsJSON = { [filename: string]: string | { [byterange: string]: string } }
6
210856a7
C
7const maxRetries = 3
8
25b7c847 9function segmentValidatorFactory (segmentsSha256Url: string, isLive: boolean) {
c6c0fa6c 10 let segmentsJSON = fetchSha256Segments(segmentsSha256Url)
4c280004 11 const regex = /bytes=(\d+)-(\d+)/
09209296 12
501af82d 13 return async function segmentValidator (segment: Segment, _method: string, _peerId: string, retry = 1) {
25b7c847
C
14 // Wait for hash generation from the server
15 if (isLive) await wait(1000)
16
4c280004 17 const filename = basename(segment.url)
09209296 18
c6c0fa6c
C
19 const segmentValue = (await segmentsJSON)[filename]
20
210856a7 21 if (!segmentValue && retry > maxRetries) {
c6c0fa6c
C
22 throw new Error(`Unknown segment name ${filename} in segment validator`)
23 }
24
25 if (!segmentValue) {
210856a7 26 console.log('Refetching sha segments for %s.', filename)
c6c0fa6c 27
25b7c847
C
28 await wait(1000)
29
c6c0fa6c 30 segmentsJSON = fetchSha256Segments(segmentsSha256Url)
501af82d 31 await segmentValidator(segment, _method, _peerId, retry + 1)
210856a7 32
c6c0fa6c
C
33 return
34 }
35
36 let hashShouldBe: string
37 let range = ''
38
39 if (typeof segmentValue === 'string') {
40 hashShouldBe = segmentValue
41 } else {
42 const captured = regex.exec(segment.range)
43 range = captured[1] + '-' + captured[2]
44
45 hashShouldBe = segmentValue[range]
46 }
4c280004 47
09209296 48 if (hashShouldBe === undefined) {
4c280004 49 throw new Error(`Unknown segment name ${filename}/${range} in segment validator`)
09209296
C
50 }
51
6d61da4e 52 const calculatedSha = await sha256Hex(segment.data)
09209296 53 if (calculatedSha !== hashShouldBe) {
4c280004
C
54 throw new Error(
55 `Hashes does not correspond for segment ${filename}/${range}` +
56 `(expected: ${hashShouldBe} instead of ${calculatedSha})`
57 )
09209296
C
58 }
59 }
60}
61
62// ---------------------------------------------------------------------------
63
64export {
65 segmentValidatorFactory
66}
67
68// ---------------------------------------------------------------------------
69
70function fetchSha256Segments (url: string) {
71 return fetch(url)
c6c0fa6c 72 .then(res => res.json() as Promise<SegmentsJSON>)
09209296
C
73 .catch(err => {
74 console.error('Cannot get sha256 segments', err)
75 return {}
76 })
77}
78
6d61da4e 79async function sha256Hex (data?: ArrayBuffer) {
09209296
C
80 if (!data) return undefined
81
6d61da4e
C
82 if (window.crypto.subtle) {
83 return window.crypto.subtle.digest('SHA-256', data)
84 .then(data => bufferToHex(data))
85 }
86
87 // Fallback for non HTTPS context
88 const shaModule = await import('sha.js')
89 return new shaModule.sha256().update(Buffer.from(data)).digest('hex')
09209296
C
90}
91
92// Thanks: https://stackoverflow.com/a/53307879
6d61da4e 93function bufferToHex (buffer?: ArrayBuffer) {
09209296
C
94 if (!buffer) return ''
95
96 let s = ''
97 const h = '0123456789abcdef'
98 const o = new Uint8Array(buffer)
99
100 o.forEach((v: any) => s += h[ v >> 4 ] + h[ v & 15 ])
101
102 return s
103}