]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - client/src/assets/player/utils.ts
Refactor video links building
[github/Chocobozzz/PeerTube.git] / client / src / assets / player / utils.ts
1 import { Video, VideoFile, VideoPlaylist } from '@shared/models'
2 import { escapeHTML } from '@shared/core-utils/renderer'
3
4 function toTitleCase (str: string) {
5 return str.charAt(0).toUpperCase() + str.slice(1)
6 }
7
8 function isWebRTCDisabled () {
9 return !!((window as any).RTCPeerConnection || (window as any).mozRTCPeerConnection || (window as any).webkitRTCPeerConnection) === false
10 }
11
12 function isIOS () {
13 if (/iPad|iPhone|iPod/.test(navigator.platform)) {
14 return true
15 }
16
17 // Detect iPad Desktop mode
18 return !!(navigator.maxTouchPoints &&
19 navigator.maxTouchPoints > 2 &&
20 /MacIntel/.test(navigator.platform))
21 }
22
23 function isSafari () {
24 return /^((?!chrome|android).)*safari/i.test(navigator.userAgent)
25 }
26
27 // https://github.com/danrevah/ngx-pipes/blob/master/src/pipes/math/bytes.ts
28 // Don't import all Angular stuff, just copy the code with shame
29 const dictionaryBytes: Array<{max: number, type: string}> = [
30 { max: 1024, type: 'B' },
31 { max: 1048576, type: 'KB' },
32 { max: 1073741824, type: 'MB' },
33 { max: 1.0995116e12, type: 'GB' }
34 ]
35 function bytes (value: number) {
36 const format = dictionaryBytes.find(d => value < d.max) || dictionaryBytes[dictionaryBytes.length - 1]
37 const calc = Math.floor(value / (format.max / 1024)).toString()
38
39 return [ calc, format.type ]
40 }
41
42 function isMobile () {
43 return /iPhone|iPad|iPod|Android/i.test(navigator.userAgent)
44 }
45
46 function buildPlaylistLink (playlist: Pick<VideoPlaylist, 'shortUUID'>, base?: string) {
47 return (base ?? window.location.origin) + '/w/p/' + playlist.shortUUID
48 }
49
50 function buildVideoLink (video: Pick<Video, 'shortUUID'>, base?: string) {
51 return (base ?? window.location.origin) + '/w/' + video.shortUUID
52 }
53
54 function buildPlaylistEmbedLink (playlist: Pick<VideoPlaylist, 'uuid'>, base?: string) {
55 return (base ?? window.location.origin) + '/video-playlists/embed/' + playlist.uuid
56 }
57
58 function buildVideoEmbedLink (video: Pick<Video, 'uuid'>, base?: string) {
59 return (base ?? window.location.origin) + '/videos/embed/' + video.uuid
60 }
61
62 function decorateVideoLink (options: {
63 url: string
64
65 startTime?: number
66 stopTime?: number
67
68 subtitle?: string
69
70 loop?: boolean
71 autoplay?: boolean
72 muted?: boolean
73
74 // Embed options
75 title?: boolean
76 warningTitle?: boolean
77 controls?: boolean
78 peertubeLink?: boolean
79 }) {
80 const { url } = options
81
82 const params = generateParams(window.location.search)
83
84 if (options.startTime !== undefined && options.startTime !== null) {
85 const startTimeInt = Math.floor(options.startTime)
86 params.set('start', secondsToTime(startTimeInt))
87 }
88
89 if (options.stopTime) {
90 const stopTimeInt = Math.floor(options.stopTime)
91 params.set('stop', secondsToTime(stopTimeInt))
92 }
93
94 if (options.subtitle) params.set('subtitle', options.subtitle)
95
96 if (options.loop === true) params.set('loop', '1')
97 if (options.autoplay === true) params.set('autoplay', '1')
98 if (options.muted === true) params.set('muted', '1')
99 if (options.title === false) params.set('title', '0')
100 if (options.warningTitle === false) params.set('warningTitle', '0')
101 if (options.controls === false) params.set('controls', '0')
102 if (options.peertubeLink === false) params.set('peertubeLink', '0')
103
104 return buildUrl(url, params)
105 }
106
107 function decoratePlaylistLink (options: {
108 url: string
109
110 playlistPosition?: number
111 }) {
112 const { url } = options
113
114 const params = generateParams(window.location.search)
115
116 if (options.playlistPosition) params.set('playlistPosition', '' + options.playlistPosition)
117
118 return buildUrl(url, params)
119 }
120
121 function buildUrl (url: string, params: URLSearchParams) {
122 let hasParams = false
123 params.forEach(() => hasParams = true)
124
125 if (hasParams) return url + '?' + params.toString()
126
127 return url
128 }
129
130 function generateParams (url: string) {
131 const params = new URLSearchParams(window.location.search)
132 // Unused parameters in embed
133 params.delete('videoId')
134 params.delete('resume')
135
136 return params
137 }
138
139 function timeToInt (time: number | string) {
140 if (!time) return 0
141 if (typeof time === 'number') return time
142
143 const reg = /^((\d+)[h:])?((\d+)[m:])?((\d+)s?)?$/
144 const matches = time.match(reg)
145
146 if (!matches) return 0
147
148 const hours = parseInt(matches[2] || '0', 10)
149 const minutes = parseInt(matches[4] || '0', 10)
150 const seconds = parseInt(matches[6] || '0', 10)
151
152 return hours * 3600 + minutes * 60 + seconds
153 }
154
155 function secondsToTime (seconds: number, full = false, symbol?: string) {
156 let time = ''
157
158 if (seconds === 0 && !full) return '0s'
159
160 const hourSymbol = (symbol || 'h')
161 const minuteSymbol = (symbol || 'm')
162 const secondsSymbol = full ? '' : 's'
163
164 const hours = Math.floor(seconds / 3600)
165 if (hours >= 1) time = hours + hourSymbol
166 else if (full) time = '0' + hourSymbol
167
168 seconds %= 3600
169 const minutes = Math.floor(seconds / 60)
170 if (minutes >= 1 && minutes < 10 && full) time += '0' + minutes + minuteSymbol
171 else if (minutes >= 1) time += minutes + minuteSymbol
172 else if (full) time += '00' + minuteSymbol
173
174 seconds %= 60
175 if (seconds >= 1 && seconds < 10 && full) time += '0' + seconds + secondsSymbol
176 else if (seconds >= 1) time += seconds + secondsSymbol
177 else if (full) time += '00'
178
179 return time
180 }
181
182 function buildVideoOrPlaylistEmbed (embedUrl: string, embedTitle: string) {
183 const title = escapeHTML(embedTitle)
184 return '<iframe width="560" height="315" ' +
185 'sandbox="allow-same-origin allow-scripts allow-popups" ' +
186 'title="' + title + '" ' +
187 'src="' + embedUrl + '" ' +
188 'frameborder="0" allowfullscreen>' +
189 '</iframe>'
190 }
191
192 function videoFileMaxByResolution (files: VideoFile[]) {
193 let max = files[0]
194
195 for (let i = 1; i < files.length; i++) {
196 const file = files[i]
197 if (max.resolution.id < file.resolution.id) max = file
198 }
199
200 return max
201 }
202
203 function videoFileMinByResolution (files: VideoFile[]) {
204 let min = files[0]
205
206 for (let i = 1; i < files.length; i++) {
207 const file = files[i]
208 if (min.resolution.id > file.resolution.id) min = file
209 }
210
211 return min
212 }
213
214 function getRtcConfig () {
215 return {
216 iceServers: [
217 {
218 urls: 'stun:stun.stunprotocol.org'
219 },
220 {
221 urls: 'stun:stun.framasoft.org'
222 }
223 ]
224 }
225 }
226
227 // ---------------------------------------------------------------------------
228
229 export {
230 getRtcConfig,
231 toTitleCase,
232 timeToInt,
233 secondsToTime,
234 isWebRTCDisabled,
235
236 buildPlaylistLink,
237 buildVideoLink,
238 decorateVideoLink,
239 decoratePlaylistLink,
240 buildPlaylistEmbedLink,
241 buildVideoEmbedLink,
242
243 buildVideoOrPlaylistEmbed,
244 videoFileMaxByResolution,
245 videoFileMinByResolution,
246 isMobile,
247 bytes,
248 isIOS,
249 isSafari
250 }