{"version":3,"file":"713.chunk.js?v=7670","mappings":"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","sources":["webpack://peertube-client/./src/assets/player/shared/upnext/end-card.ts","webpack://peertube-client/./src/assets/player/shared/upnext/upnext-plugin.ts","webpack://peertube-client/../shared/core-utils/common/date.ts","webpack://peertube-client/../shared/core-utils/common/object.ts","webpack://peertube-client/../shared/core-utils/common/url.ts","webpack://peertube-client/../shared/core-utils/i18n/i18n.ts","webpack://peertube-client/../shared/core-utils/renderer/markdown.ts","webpack://peertube-client/./src/assets/player/shared/common/utils.ts","webpack://peertube-client/./src/assets/player/shared/stats/stats-card.ts","webpack://peertube-client/./src/assets/player/shared/stats/stats-plugin.ts","webpack://peertube-client/./src/root-helpers/web-browser.ts","webpack://peertube-client/./src/assets/player/shared/bezels/pause-bezel.ts","webpack://peertube-client/./src/assets/player/shared/bezels/bezels-plugin.ts","webpack://peertube-client/./src/assets/player/peertube-player-local-storage.ts","webpack://peertube-client/./src/assets/player/shared/peertube/peertube-plugin.ts","webpack://peertube-client/./src/assets/player/shared/resolutions/peertube-resolutions-plugin.ts","webpack://peertube-client/./src/assets/player/shared/control-bar/next-previous-video-button.ts","webpack://peertube-client/./src/assets/player/shared/control-bar/p2p-info-button.ts","webpack://peertube-client/./src/assets/player/shared/control-bar/picture-in-picture-bastyon.ts","webpack://peertube-client/./src/assets/player/shared/control-bar/peertube-load-progress-bar.ts","webpack://peertube-client/./src/assets/player/shared/control-bar/theater-button.ts","webpack://peertube-client/./src/assets/player/shared/settings/resolution-menu-item.ts","webpack://peertube-client/./src/assets/player/shared/settings/resolution-menu-button.ts","webpack://peertube-client/./src/assets/player/shared/settings/settings-dialog.ts","webpack://peertube-client/./src/assets/player/shared/settings/settings-menu-item.ts","webpack://peertube-client/./src/assets/player/shared/settings/settings-menu-button.ts","webpack://peertube-client/./src/assets/player/shared/settings/settings-panel.ts","webpack://peertube-client/./src/assets/player/shared/settings/settings-panel-child.ts","webpack://peertube-client/./src/assets/player/shared/playlist/playlist-button.ts","webpack://peertube-client/./src/assets/player/shared/playlist/playlist-menu-item.ts","webpack://peertube-client/./src/assets/player/shared/playlist/playlist-menu.ts","webpack://peertube-client/./src/assets/player/shared/playlist/playlist-plugin.ts","webpack://peertube-client/./src/assets/player/shared/mobile/peertube-mobile-plugin.ts","webpack://peertube-client/./src/assets/player/shared/mobile/peertube-mobile-buttons.ts","webpack://peertube-client/./src/assets/player/shared/hotkeys/peertube-hotkeys-plugin.ts","webpack://peertube-client/./src/root-helpers/utils.ts","webpack://peertube-client/./src/assets/player/shared/manager-options/control-bar-options-builder.ts","webpack://peertube-client/./src/assets/player/shared/p2p-media-loader/redundancy-url-manager.ts","webpack://peertube-client/./src/assets/player/shared/p2p-media-loader/segment-url-builder.ts","webpack://peertube-client/./src/assets/player/shared/p2p-media-loader/segment-validator.ts","webpack://peertube-client/./src/assets/player/shared/manager-options/hls-options-builder.ts","webpack://peertube-client/./src/assets/player/shared/manager-options/webtorrent-options-builder.ts","webpack://peertube-client/./src/assets/player/shared/manager-options/manager-options-builder.ts","webpack://peertube-client/./src/assets/player/shared/p2p-media-loader/cap-level-controller.ts","webpack://peertube-client/./src/assets/player/shared/p2p-media-loader/abr-controler.ts","webpack://peertube-client/./src/assets/player/shared/p2p-media-loader/hls-plugin.ts","webpack://peertube-client/./src/assets/player/shared/p2p-media-loader/p2p-media-loader-plugin.ts","webpack://peertube-client/./src/assets/player/peertube-player-manager.ts","webpack://peertube-client/./src/shims/path.ts","webpack://peertube-client/./src/assets/player/shared/videojs-helpers/guid.js","webpack://peertube-client/./src/assets/player/shared/videojs-helpers/fn.js","webpack://peertube-client/./src/assets/player/shared/videojs-helpers/hotkeys.js"],"sourcesContent":["import videojs from 'video.js'\r\n\r\nfunction getMainTemplate (options: any) {\r\n return `\r\n
\r\n ${options.headText}\r\n
\r\n
\r\n
\r\n \r\n \r\n \r\n \r\n
\r\n \r\n \r\n \r\n \r\n ${options.suspendedText}\r\n \r\n `\r\n}\r\n\r\nexport interface EndCardOptions extends videojs.ComponentOptions {\r\n next: () => void\r\n getTitle: () => string\r\n timeout: number\r\n cancelText: string\r\n headText: string\r\n suspendedText: string\r\n condition: () => boolean\r\n suspended: () => boolean\r\n}\r\n\r\nconst Component = videojs.getComponent('Component')\r\nclass EndCard extends Component {\r\n options_: EndCardOptions\r\n\r\n dashOffsetTotal = 586\r\n dashOffsetStart = 293\r\n interval = 50\r\n upNextEvents = new videojs.EventTarget()\r\n ticks = 0\r\n totalTicks: number\r\n\r\n container: HTMLDivElement\r\n title: HTMLElement\r\n autoplayRing: HTMLElement\r\n cancelButton: HTMLElement\r\n suspendedMessage: HTMLElement\r\n nextButton: HTMLElement\r\n\r\n constructor (player: videojs.Player, options: EndCardOptions) {\r\n super(player, options)\r\n\r\n this.totalTicks = this.options_.timeout / this.interval\r\n\r\n player.on('ended', (_: any) => {\r\n if (!this.options_.condition()) return\r\n\r\n player.addClass('vjs-upnext--showing')\r\n this.showCard((canceled: boolean) => {\r\n player.removeClass('vjs-upnext--showing')\r\n this.container.style.display = 'none'\r\n if (!canceled) {\r\n this.options_.next()\r\n }\r\n })\r\n })\r\n\r\n player.on('playing', () => {\r\n this.upNextEvents.trigger('playing')\r\n })\r\n }\r\n\r\n createEl () {\r\n const container = super.createEl('div', {\r\n className: 'vjs-upnext-content',\r\n innerHTML: getMainTemplate(this.options_)\r\n }) as HTMLDivElement\r\n\r\n this.container = container\r\n container.style.display = 'none'\r\n\r\n this.autoplayRing = container.getElementsByClassName('vjs-upnext-svg-autoplay-ring')[0] as HTMLElement\r\n this.title = container.getElementsByClassName('vjs-upnext-title')[0] as HTMLElement\r\n this.cancelButton = container.getElementsByClassName('vjs-upnext-cancel-button')[0] as HTMLElement\r\n this.suspendedMessage = container.getElementsByClassName('vjs-upnext-suspended')[0] as HTMLElement\r\n this.nextButton = container.getElementsByClassName('vjs-upnext-autoplay-icon')[0] as HTMLElement\r\n\r\n this.cancelButton.onclick = () => {\r\n this.upNextEvents.trigger('cancel')\r\n }\r\n\r\n this.nextButton.onclick = () => {\r\n this.upNextEvents.trigger('next')\r\n }\r\n\r\n return container\r\n }\r\n\r\n showCard (cb: (value: boolean) => void) {\r\n let timeout: any\r\n\r\n this.autoplayRing.setAttribute('stroke-dasharray', `${this.dashOffsetStart}`)\r\n this.autoplayRing.setAttribute('stroke-dashoffset', `${-this.dashOffsetStart}`)\r\n\r\n this.title.innerHTML = this.options_.getTitle()\r\n\r\n this.upNextEvents.one('cancel', () => {\r\n clearTimeout(timeout)\r\n cb(true)\r\n })\r\n\r\n this.upNextEvents.one('playing', () => {\r\n clearTimeout(timeout)\r\n cb(true)\r\n })\r\n\r\n this.upNextEvents.one('next', () => {\r\n clearTimeout(timeout)\r\n cb(false)\r\n })\r\n\r\n const goToPercent = (percent: number) => {\r\n const newOffset = Math.max(-this.dashOffsetTotal, -this.dashOffsetStart - percent * this.dashOffsetTotal / 2 / 100)\r\n this.autoplayRing.setAttribute('stroke-dashoffset', '' + newOffset)\r\n }\r\n\r\n const tick = () => {\r\n goToPercent((this.ticks++) * 100 / this.totalTicks)\r\n }\r\n\r\n const update = () => {\r\n if (this.options_.suspended()) {\r\n this.suspendedMessage.innerText = this.options_.suspendedText\r\n goToPercent(0)\r\n this.ticks = 0\r\n timeout = setTimeout(update.bind(this), 300) // checks once supsended can be a bit longer\r\n } else if (this.ticks >= this.totalTicks) {\r\n clearTimeout(timeout)\r\n cb(false)\r\n } else {\r\n this.suspendedMessage.innerText = ''\r\n tick()\r\n timeout = setTimeout(update.bind(this), this.interval)\r\n }\r\n }\r\n\r\n this.container.style.display = 'block'\r\n timeout = setTimeout(update.bind(this), this.interval)\r\n }\r\n}\r\n\r\nvideojs.registerComponent('EndCard', EndCard)\r\n","import videojs from 'video.js'\r\nimport { EndCardOptions } from './end-card'\r\n\r\nconst Plugin = videojs.getPlugin('plugin')\r\n\r\nclass UpNextPlugin extends Plugin {\r\n\r\n constructor (player: videojs.Player, options: Partial = {}) {\r\n const settings = {\r\n next: options.next,\r\n getTitle: options.getTitle,\r\n timeout: options.timeout || 5000,\r\n cancelText: options.cancelText || 'Cancel',\r\n headText: options.headText || 'Up Next',\r\n suspendedText: options.suspendedText || 'Autoplay is suspended',\r\n condition: options.condition,\r\n suspended: options.suspended\r\n }\r\n\r\n super(player)\r\n\r\n this.player.ready(() => {\r\n player.addClass('vjs-upnext')\r\n })\r\n\r\n player.addChild('EndCard', settings)\r\n }\r\n}\r\n\r\nvideojs.registerPlugin('upnext', UpNextPlugin)\r\nexport { UpNextPlugin }\r\n","function isToday (d: Date) {\r\n const today = new Date()\r\n\r\n return areDatesEqual(d, today)\r\n}\r\n\r\nfunction isYesterday (d: Date) {\r\n const yesterday = new Date()\r\n yesterday.setDate(yesterday.getDate() - 1)\r\n\r\n return areDatesEqual(d, yesterday)\r\n}\r\n\r\nfunction isThisWeek (d: Date) {\r\n const minDateOfThisWeek = new Date()\r\n minDateOfThisWeek.setHours(0, 0, 0)\r\n\r\n // getDay() -> Sunday - Saturday : 0 - 6\r\n // We want to start our week on Monday\r\n let dayOfWeek = minDateOfThisWeek.getDay() - 1\r\n if (dayOfWeek < 0) dayOfWeek = 6 // Sunday\r\n\r\n minDateOfThisWeek.setDate(minDateOfThisWeek.getDate() - dayOfWeek)\r\n\r\n return d >= minDateOfThisWeek\r\n}\r\n\r\nfunction isThisMonth (d: Date) {\r\n const thisMonth = new Date().getMonth()\r\n\r\n return d.getMonth() === thisMonth\r\n}\r\n\r\nfunction isLastMonth (d: Date) {\r\n const now = new Date()\r\n\r\n return getDaysDifferences(now, d) <= 30\r\n}\r\n\r\nfunction isLastWeek (d: Date) {\r\n const now = new Date()\r\n\r\n return getDaysDifferences(now, d) <= 7\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n\r\nfunction timeToInt (time: number | string) {\r\n if (!time) return 0\r\n if (typeof time === 'number') return time\r\n\r\n const reg = /^((\\d+)[h:])?((\\d+)[m:])?((\\d+)s?)?$/\r\n const matches = time.match(reg)\r\n\r\n if (!matches) return 0\r\n\r\n const hours = parseInt(matches[2] || '0', 10)\r\n const minutes = parseInt(matches[4] || '0', 10)\r\n const seconds = parseInt(matches[6] || '0', 10)\r\n\r\n return hours * 3600 + minutes * 60 + seconds\r\n}\r\n\r\nfunction secondsToTime (seconds: number, full = false, symbol?: string) {\r\n let time = ''\r\n\r\n if (seconds === 0 && !full) return '0s'\r\n\r\n const hourSymbol = (symbol || 'h')\r\n const minuteSymbol = (symbol || 'm')\r\n const secondsSymbol = full ? '' : 's'\r\n\r\n const hours = Math.floor(seconds / 3600)\r\n if (hours >= 1) time = hours + hourSymbol\r\n else if (full) time = '0' + hourSymbol\r\n\r\n seconds %= 3600\r\n const minutes = Math.floor(seconds / 60)\r\n if (minutes >= 1 && minutes < 10 && full) time += '0' + minutes + minuteSymbol\r\n else if (minutes >= 1) time += minutes + minuteSymbol\r\n else if (full) time += '00' + minuteSymbol\r\n\r\n seconds %= 60\r\n if (seconds >= 1 && seconds < 10 && full) time += '0' + seconds + secondsSymbol\r\n else if (seconds >= 1) time += seconds + secondsSymbol\r\n else if (full) time += '00'\r\n\r\n return time\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n\r\nexport {\r\n isYesterday,\r\n isThisWeek,\r\n isThisMonth,\r\n isToday,\r\n isLastMonth,\r\n isLastWeek,\r\n timeToInt,\r\n secondsToTime\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n\r\nfunction areDatesEqual (d1: Date, d2: Date) {\r\n return d1.getFullYear() === d2.getFullYear() &&\r\n d1.getMonth() === d2.getMonth() &&\r\n d1.getDate() === d2.getDate()\r\n}\r\n\r\nfunction getDaysDifferences (d1: Date, d2: Date) {\r\n return (d1.getTime() - d2.getTime()) / (86400000)\r\n}\r\n","function pick (object: O, keys: K[]): Pick {\r\n const result: any = {}\r\n\r\n for (const key of keys) {\r\n if (Object.prototype.hasOwnProperty.call(object, key)) {\r\n result[key] = object[key]\r\n }\r\n }\r\n\r\n return result\r\n}\r\n\r\nfunction getKeys (object: O, keys: K[]): K[] {\r\n return (Object.keys(object) as K[]).filter(k => keys.includes(k))\r\n}\r\n\r\nfunction sortObjectComparator (key: string, order: 'asc' | 'desc') {\r\n return (a: any, b: any) => {\r\n if (a[key] < b[key]) {\r\n return order === 'asc' ? -1 : 1\r\n }\r\n\r\n if (a[key] > b[key]) {\r\n return order === 'asc' ? 1 : -1\r\n }\r\n\r\n return 0\r\n }\r\n}\r\n\r\nexport {\r\n pick,\r\n getKeys,\r\n sortObjectComparator\r\n}\r\n","import { Video, VideoPlaylist } from '../../models'\r\nimport { secondsToTime } from './date'\r\n\r\nfunction buildPlaylistLink (playlist: Pick, base?: string) {\r\n return (base ?? window.location.origin) + buildPlaylistWatchPath(playlist)\r\n}\r\n\r\nfunction buildPlaylistWatchPath (playlist: Pick) {\r\n return '/w/p/' + playlist.shortUUID\r\n}\r\n\r\nfunction buildVideoWatchPath (video: Pick) {\r\n return '/w/' + video.shortUUID\r\n}\r\n\r\nfunction buildVideoLink (video: Pick, base?: string) {\r\n return (base ?? window.location.origin) + buildVideoWatchPath(video)\r\n}\r\n\r\nfunction buildPlaylistEmbedPath (playlist: Pick) {\r\n return '/video-playlists/embed/' + playlist.uuid\r\n}\r\n\r\nfunction buildPlaylistEmbedLink (playlist: Pick, base?: string) {\r\n return (base ?? window.location.origin) + buildPlaylistEmbedPath(playlist)\r\n}\r\n\r\nfunction buildVideoEmbedPath (video: Pick) {\r\n return '/videos/embed/' + video.uuid\r\n}\r\n\r\nfunction buildVideoEmbedLink (video: Pick, base?: string) {\r\n return (base ?? window.location.origin) + buildVideoEmbedPath(video)\r\n}\r\n\r\nfunction decorateVideoLink (options: {\r\n url: string\r\n\r\n startTime?: number\r\n stopTime?: number\r\n\r\n subtitle?: string\r\n\r\n loop?: boolean\r\n autoplay?: boolean\r\n muted?: boolean\r\n\r\n // Embed options\r\n title?: boolean\r\n warningTitle?: boolean\r\n\r\n controls?: boolean\r\n controlBar?: boolean\r\n\r\n peertubeLink?: boolean\r\n p2p?: boolean\r\n}) {\r\n const { url } = options\r\n\r\n const params = new URLSearchParams()\r\n\r\n if (options.startTime !== undefined && options.startTime !== null) {\r\n const startTimeInt = Math.floor(options.startTime)\r\n params.set('start', secondsToTime(startTimeInt))\r\n }\r\n\r\n if (options.stopTime) {\r\n const stopTimeInt = Math.floor(options.stopTime)\r\n params.set('stop', secondsToTime(stopTimeInt))\r\n }\r\n\r\n if (options.subtitle) params.set('subtitle', options.subtitle)\r\n\r\n if (options.loop === true) params.set('loop', '1')\r\n if (options.autoplay === true) params.set('autoplay', '1')\r\n if (options.muted === true) params.set('muted', '1')\r\n if (options.title === false) params.set('title', '0')\r\n if (options.warningTitle === false) params.set('warningTitle', '0')\r\n\r\n if (options.controls === false) params.set('controls', '0')\r\n if (options.controlBar === false) params.set('controlBar', '0')\r\n\r\n if (options.peertubeLink === false) params.set('peertubeLink', '0')\r\n if (options.p2p !== undefined) params.set('p2p', options.p2p ? '1' : '0')\r\n\r\n return buildUrl(url, params)\r\n}\r\n\r\nfunction decoratePlaylistLink (options: {\r\n url: string\r\n\r\n playlistPosition?: number\r\n}) {\r\n const { url } = options\r\n\r\n const params = new URLSearchParams()\r\n\r\n if (options.playlistPosition) params.set('playlistPosition', '' + options.playlistPosition)\r\n\r\n return buildUrl(url, params)\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n\r\nexport {\r\n buildPlaylistLink,\r\n buildVideoLink,\r\n\r\n buildVideoWatchPath,\r\n buildPlaylistWatchPath,\r\n\r\n buildPlaylistEmbedPath,\r\n buildVideoEmbedPath,\r\n\r\n buildPlaylistEmbedLink,\r\n buildVideoEmbedLink,\r\n\r\n decorateVideoLink,\r\n decoratePlaylistLink\r\n}\r\n\r\nfunction buildUrl (url: string, params: URLSearchParams) {\r\n let hasParams = false\r\n params.forEach(() => { hasParams = true })\r\n\r\n if (hasParams) return url + '?' + params.toString()\r\n\r\n return url\r\n}\r\n","export const LOCALE_FILES = [ 'player', 'server' ]\r\n\r\nexport const I18N_LOCALES = {\r\n // Always first to avoid issues when using express acceptLanguages function when no accept language header is set\r\n 'en-US': 'English',\r\n\r\n 'ar': 'العربية',\r\n 'ca-ES': 'Català',\r\n 'cs-CZ': 'Čeština',\r\n 'de-DE': 'Deutsch',\r\n 'el-GR': 'ελληνικά',\r\n 'eo': 'Esperanto',\r\n 'es-ES': 'Español',\r\n 'eu-ES': 'Euskara',\r\n 'fi-FI': 'suomi',\r\n 'fr-FR': 'Français',\r\n 'gd': 'Gàidhlig',\r\n 'gl-ES': 'galego',\r\n 'hr': 'hrvatski',\r\n 'hu-HU': 'magyar',\r\n 'fa-IR': 'فارسی',\r\n 'it-IT': 'Italiano',\r\n 'ja-JP': '日本語',\r\n 'kab': 'Taqbaylit',\r\n 'nl-NL': 'Nederlands',\r\n 'oc': 'Occitan',\r\n 'pl-PL': 'Polski',\r\n 'pt-BR': 'Português (Brasil)',\r\n 'pt-PT': 'Português (Portugal)',\r\n 'ru-RU': 'русский',\r\n 'sq': 'Shqip',\r\n 'sv-SE': 'Svenska',\r\n 'nn': 'norsk nynorsk',\r\n 'nb-NO': 'norsk bokmål',\r\n 'th-TH': 'ไทย',\r\n 'vi-VN': 'Tiếng Việt',\r\n 'tok': 'Toki Pona',\r\n 'zh-Hans-CN': '简体中文(中国)',\r\n 'zh-Hant-TW': '繁體中文(台灣)'\r\n}\r\n\r\nconst I18N_LOCALE_ALIAS = {\r\n 'ar-001': 'ar',\r\n 'ca': 'ca-ES',\r\n 'cs': 'cs-CZ',\r\n 'de': 'de-DE',\r\n 'el': 'el-GR',\r\n 'en': 'en-US',\r\n 'es': 'es-ES',\r\n 'eu': 'eu-ES',\r\n 'fi': 'fi-FI',\r\n 'gl': 'gl-ES',\r\n 'fa': 'fa-IR',\r\n 'fr': 'fr-FR',\r\n 'hu': 'hu-HU',\r\n 'it': 'it-IT',\r\n 'ja': 'ja-JP',\r\n 'nl': 'nl-NL',\r\n 'pl': 'pl-PL',\r\n 'pt': 'pt-BR',\r\n 'nb': 'nb-NO',\r\n 'ru': 'ru-RU',\r\n 'sv': 'sv-SE',\r\n 'th': 'th-TH',\r\n 'vi': 'vi-VN',\r\n 'zh-CN': 'zh-Hans-CN',\r\n 'zh-Hans': 'zh-Hans-CN',\r\n 'zh-Hant': 'zh-Hant-TW',\r\n 'zh-TW': 'zh-Hant-TW',\r\n 'zh': 'zh-Hans-CN'\r\n}\r\n\r\nexport const POSSIBLE_LOCALES = Object.keys(I18N_LOCALES)\r\n .concat(Object.keys(I18N_LOCALE_ALIAS))\r\n\r\nexport function getDefaultLocale () {\r\n return 'en-US'\r\n}\r\n\r\nexport function isDefaultLocale (locale: string) {\r\n return getCompleteLocale(locale) === getCompleteLocale(getDefaultLocale())\r\n}\r\n\r\nexport function peertubeTranslate (str: string, translations?: { [ id: string ]: string }) {\r\n if (!translations || !translations[str]) return str\r\n\r\n return translations[str]\r\n}\r\n\r\nconst possiblePaths = POSSIBLE_LOCALES.map(l => '/' + l)\r\nexport function is18nPath (path: string) {\r\n return possiblePaths.includes(path)\r\n}\r\n\r\nexport function is18nLocale (locale: string) {\r\n return POSSIBLE_LOCALES.includes(locale)\r\n}\r\n\r\nexport function getCompleteLocale (locale: string) {\r\n if (!locale) return locale\r\n\r\n if (I18N_LOCALE_ALIAS[locale]) return I18N_LOCALE_ALIAS[locale]\r\n\r\n return locale\r\n}\r\n\r\nexport function getShortLocale (locale: string) {\r\n if (locale.includes('-') === false) return locale\r\n\r\n return locale.split('-')[0]\r\n}\r\n\r\nexport function buildFileLocale (locale: string) {\r\n return getCompleteLocale(locale)\r\n}\r\n","export const TEXT_RULES = [\r\n 'linkify',\r\n 'autolink',\r\n 'emphasis',\r\n 'link',\r\n 'newline',\r\n 'entity',\r\n 'list'\r\n]\r\n\r\nexport const TEXT_WITH_HTML_RULES = TEXT_RULES.concat([\r\n 'html_inline',\r\n 'html_block'\r\n])\r\n\r\nexport const ENHANCED_RULES = TEXT_RULES.concat([ 'image' ])\r\nexport const ENHANCED_WITH_HTML_RULES = TEXT_WITH_HTML_RULES.concat([ 'image' ])\r\n\r\nexport const COMPLETE_RULES = ENHANCED_WITH_HTML_RULES.concat([\r\n 'block',\r\n 'inline',\r\n 'heading',\r\n 'paragraph'\r\n])\r\n","import { VideoFile } from '@shared/models'\r\n\r\nfunction toTitleCase (str: string) {\r\n return str.charAt(0).toUpperCase() + str.slice(1)\r\n}\r\n\r\nconst dictionaryBytes = [\r\n { max: 1024, type: 'B', decimals: 0 },\r\n { max: 1048576, type: 'KB', decimals: 0 },\r\n { max: 1073741824, type: 'MB', decimals: 0 },\r\n { max: 1.0995116e12, type: 'GB', decimals: 1 }\r\n]\r\nfunction bytes (value: number) {\r\n const format = dictionaryBytes.find(d => value < d.max) || dictionaryBytes[dictionaryBytes.length - 1]\r\n const calc = (value / (format.max / 1024)).toFixed(format.decimals)\r\n\r\n return [ calc, format.type ]\r\n}\r\n\r\nfunction videoFileMaxByResolution (files: VideoFile[]) {\r\n let max = files[0]\r\n\r\n for (let i = 1; i < files.length; i++) {\r\n const file = files[i]\r\n if (max.resolution.id < file.resolution.id) max = file\r\n }\r\n\r\n return max\r\n}\r\n\r\nfunction videoFileMinByResolution (files: VideoFile[]) {\r\n let min = files[0]\r\n\r\n for (let i = 1; i < files.length; i++) {\r\n const file = files[i]\r\n if (min.resolution.id > file.resolution.id) min = file\r\n }\r\n\r\n return min\r\n}\r\n\r\nfunction getRtcConfig () {\r\n return {\r\n iceServers: [\r\n {\r\n urls: \"stun:turn.pocketnet.app\",\r\n username: \"stunuser\",\r\n credential: \"q1w2e3r4t5ASD!@#\",\r\n },\r\n {\r\n urls: \"turn:turn.pocketnet.app\",\r\n username: \"stunuser\",\r\n credential: \"q1w2e3r4t5ASD!@#\",\r\n },\r\n ]\r\n }\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n\r\nexport {\r\n getRtcConfig,\r\n toTitleCase,\r\n\r\n videoFileMaxByResolution,\r\n videoFileMinByResolution,\r\n bytes\r\n}\r\n","import videojs from 'video.js'\r\nimport { logger } from '@root-helpers/logger'\r\nimport { secondsToTime } from '@shared/core-utils'\r\nimport { PlayerNetworkInfo as EventPlayerNetworkInfo } from '../../types'\r\nimport { bytes } from '../common'\r\n\r\ninterface StatsCardOptions extends videojs.ComponentOptions {\r\n videoUUID: string\r\n videoIsLive: boolean\r\n mode: 'webtorrent' | 'p2p-media-loader' | 'localvideo'\r\n p2pEnabled: boolean\r\n}\r\n\r\ninterface PlayerNetworkInfo {\r\n downloadSpeed?: string\r\n uploadSpeed?: string\r\n totalDownloaded?: string\r\n totalUploaded?: string\r\n numPeers?: number\r\n averageBandwidth?: string\r\n\r\n downloadedFromServer?: string\r\n downloadedFromPeers?: string\r\n}\r\n\r\ninterface InfoElement {\r\n root: HTMLElement\r\n value: HTMLElement\r\n}\r\n\r\nconst Component = videojs.getComponent('Component')\r\nclass StatsCard extends Component {\r\n options_: StatsCardOptions\r\n\r\n updateInterval: any\r\n\r\n mode: 'webtorrent' | 'p2p-media-loader'\r\n\r\n metadataStore: any = {}\r\n\r\n intervalMs = 300\r\n playerNetworkInfo: PlayerNetworkInfo = {}\r\n\r\n private containerEl: HTMLDivElement\r\n private infoListEl: HTMLDivElement\r\n\r\n private playerMode: InfoElement\r\n private p2p: InfoElement\r\n private uuid: InfoElement\r\n private viewport: InfoElement\r\n private resolution: InfoElement\r\n private volume: InfoElement\r\n private codecs: InfoElement\r\n private color: InfoElement\r\n private connection: InfoElement\r\n\r\n private network: InfoElement\r\n private transferred: InfoElement\r\n private download: InfoElement\r\n\r\n private bufferProgress: InfoElement\r\n private bufferState: InfoElement\r\n\r\n private liveLatency: InfoElement\r\n\r\n createEl () {\r\n this.containerEl = videojs.dom.createEl('div', {\r\n className: 'vjs-stats-content'\r\n }) as HTMLDivElement\r\n this.containerEl.style.display = 'none'\r\n\r\n this.infoListEl = videojs.dom.createEl('div', {\r\n className: 'vjs-stats-list'\r\n }) as HTMLDivElement\r\n\r\n const closeButton = videojs.dom.createEl('button', {\r\n className: 'vjs-stats-close',\r\n tabindex: '0',\r\n title: 'Close stats',\r\n innerText: '[x]'\r\n }, { 'aria-label': 'Close stats' }) as HTMLElement\r\n closeButton.onclick = () => this.hide()\r\n\r\n this.containerEl.appendChild(closeButton)\r\n this.containerEl.appendChild(this.infoListEl)\r\n\r\n this.populateInfoBlocks()\r\n\r\n this.player_.on('p2pInfo', (event: any, data: EventPlayerNetworkInfo) => {\r\n if (!data) return // HTTP fallback\r\n\r\n this.mode = data.source\r\n\r\n const p2pStats = data.p2p\r\n const httpStats = data.http\r\n\r\n this.playerNetworkInfo.downloadSpeed = bytes(p2pStats.downloadSpeed + httpStats.downloadSpeed).join(' ')\r\n this.playerNetworkInfo.uploadSpeed = bytes(p2pStats.uploadSpeed + httpStats.uploadSpeed).join(' ')\r\n this.playerNetworkInfo.totalDownloaded = bytes(p2pStats.downloaded + httpStats.downloaded).join(' ')\r\n this.playerNetworkInfo.totalUploaded = bytes(p2pStats.uploaded + httpStats.uploaded).join(' ')\r\n this.playerNetworkInfo.numPeers = p2pStats.numPeers\r\n this.playerNetworkInfo.averageBandwidth = bytes(data.bandwidthEstimate).join(' ') + '/s'\r\n\r\n if (data.source === 'p2p-media-loader') {\r\n this.playerNetworkInfo.downloadedFromServer = bytes(httpStats.downloaded).join(' ')\r\n this.playerNetworkInfo.downloadedFromPeers = bytes(p2pStats.downloaded).join(' ')\r\n }\r\n })\r\n\r\n return this.containerEl\r\n }\r\n\r\n toggle () {\r\n if (this.updateInterval) this.hide()\r\n else this.show()\r\n }\r\n\r\n show () {\r\n this.containerEl.style.display = 'block'\r\n\r\n this.updateInterval = setInterval(async () => {\r\n try {\r\n const options = this.buildHLSOptions()\r\n \r\n \r\n /*this.mode === 'p2p-media-loader'\r\n ? this.buildHLSOptions()\r\n : await this.buildWebTorrentOptions();*/\r\n\r\n this.populateInfoValues(options)\r\n \r\n } catch (err) {\r\n logger.error('Cannot update stats.', err)\r\n clearInterval(this.updateInterval)\r\n }\r\n }, this.intervalMs)\r\n }\r\n\r\n hide () {\r\n clearInterval(this.updateInterval)\r\n this.containerEl.style.display = 'none'\r\n }\r\n\r\n private buildHLSOptions () {\r\n const p2pMediaLoader = this.player_.p2pMediaLoader()\r\n const level = p2pMediaLoader.getCurrentLevel()\r\n\r\n const codecs = level?.videoCodec || level?.audioCodec\r\n ? `${level?.videoCodec || ''} / ${level?.audioCodec || ''}`\r\n : undefined\r\n\r\n const resolution = `${level?.height}p${level?.attrs['FRAME-RATE'] || ''}`\r\n const buffer = this.timeRangesToString(this.player().buffered())\r\n\r\n let progress: number\r\n let latency: string\r\n\r\n if (this.options_.videoIsLive) {\r\n latency = secondsToTime(p2pMediaLoader.getLiveLatency())\r\n } else {\r\n progress = this.player().bufferedPercent()\r\n }\r\n\r\n return {\r\n playerNetworkInfo: this.playerNetworkInfo,\r\n resolution,\r\n codecs,\r\n buffer,\r\n latency,\r\n progress\r\n }\r\n }\r\n\r\n /*private async buildWebTorrentOptions () {\r\n const videoFile = this.player_.webtorrent().getCurrentVideoFile()\r\n\r\n if (!this.metadataStore[videoFile.fileUrl]) {\r\n this.metadataStore[videoFile.fileUrl] = await fetch(videoFile.metadataUrl).then(res => res.json())\r\n }\r\n\r\n const metadata = this.metadataStore[videoFile.fileUrl]\r\n\r\n let colorSpace = 'unknown'\r\n let codecs = 'unknown'\r\n\r\n if (metadata?.streams[0]) {\r\n const stream = metadata.streams[0]\r\n\r\n colorSpace = stream['color_space'] !== 'unknown'\r\n ? stream['color_space']\r\n : 'bt709'\r\n\r\n codecs = stream['codec_name'] || 'avc1'\r\n }\r\n\r\n const resolution = videoFile?.resolution.label + videoFile?.fps\r\n const buffer = this.timeRangesToString(this.player().buffered())\r\n const progress = this.player_.webtorrent().getTorrent()?.progress\r\n\r\n return {\r\n playerNetworkInfo: this.playerNetworkInfo,\r\n progress,\r\n colorSpace,\r\n codecs,\r\n resolution,\r\n buffer\r\n }\r\n }*/\r\n\r\n private populateInfoBlocks () {\r\n this.playerMode = this.buildInfoRow(this.player().localize('Player mode'))\r\n this.p2p = this.buildInfoRow(this.player().localize('P2P'))\r\n this.uuid = this.buildInfoRow(this.player().localize('Video UUID'))\r\n this.viewport = this.buildInfoRow(this.player().localize('Viewport / Frames'))\r\n this.resolution = this.buildInfoRow(this.player().localize('Resolution'))\r\n this.volume = this.buildInfoRow(this.player().localize('Volume'))\r\n this.codecs = this.buildInfoRow(this.player().localize('Codecs'))\r\n this.color = this.buildInfoRow(this.player().localize('Color'))\r\n this.connection = this.buildInfoRow(this.player().localize('Connection Speed'))\r\n\r\n this.network = this.buildInfoRow(this.player().localize('Network Activity'))\r\n this.transferred = this.buildInfoRow(this.player().localize('Total Transfered'))\r\n this.download = this.buildInfoRow(this.player().localize('Download Breakdown'))\r\n\r\n this.bufferProgress = this.buildInfoRow(this.player().localize('Buffer Progress'))\r\n this.bufferState = this.buildInfoRow(this.player().localize('Buffer State'))\r\n\r\n this.liveLatency = this.buildInfoRow(this.player().localize('Live Latency'))\r\n\r\n this.infoListEl.appendChild(this.playerMode.root)\r\n this.infoListEl.appendChild(this.p2p.root)\r\n this.infoListEl.appendChild(this.uuid.root)\r\n this.infoListEl.appendChild(this.viewport.root)\r\n this.infoListEl.appendChild(this.resolution.root)\r\n this.infoListEl.appendChild(this.volume.root)\r\n this.infoListEl.appendChild(this.codecs.root)\r\n this.infoListEl.appendChild(this.color.root)\r\n this.infoListEl.appendChild(this.connection.root)\r\n this.infoListEl.appendChild(this.network.root)\r\n this.infoListEl.appendChild(this.transferred.root)\r\n this.infoListEl.appendChild(this.download.root)\r\n this.infoListEl.appendChild(this.bufferProgress.root)\r\n this.infoListEl.appendChild(this.bufferState.root)\r\n this.infoListEl.appendChild(this.liveLatency.root)\r\n }\r\n\r\n private populateInfoValues (options: {\r\n playerNetworkInfo: PlayerNetworkInfo\r\n progress: number\r\n codecs: string\r\n resolution: string\r\n buffer: string\r\n\r\n latency?: string\r\n colorSpace?: string\r\n }) {\r\n const { playerNetworkInfo, progress, colorSpace, codecs, resolution, buffer, latency } = options\r\n const player = this.player()\r\n\r\n const videoQuality: VideoPlaybackQuality = player.getVideoPlaybackQuality()\r\n const vw = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0)\r\n const vh = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0)\r\n const pr = (window.devicePixelRatio || 1).toFixed(2)\r\n const frames = `${vw}x${vh}*${pr} / ${videoQuality.droppedVideoFrames} dropped of ${videoQuality.totalVideoFrames}`\r\n\r\n const duration = player.duration()\r\n\r\n let volume = `${Math.round(player.volume() * 100)}`\r\n if (player.muted()) volume += ' (muted)'\r\n\r\n const networkActivity = playerNetworkInfo.downloadSpeed\r\n ? `${playerNetworkInfo.downloadSpeed} ⇓ / ${playerNetworkInfo.uploadSpeed} ⇑`\r\n : undefined\r\n\r\n const totalTransferred = playerNetworkInfo.totalDownloaded\r\n ? `${playerNetworkInfo.totalDownloaded} ⇓ / ${playerNetworkInfo.totalUploaded} ⇑`\r\n : undefined\r\n const downloadBreakdown = playerNetworkInfo.downloadedFromServer\r\n ? `${playerNetworkInfo.downloadedFromServer} from servers · ${playerNetworkInfo.downloadedFromPeers} from peers`\r\n : undefined\r\n\r\n const bufferProgress = progress !== undefined\r\n ? `${(progress * 100).toFixed(1)}% (${(progress * duration).toFixed(1)}s)`\r\n : undefined\r\n\r\n this.setInfoValue(this.playerMode, this.mode || 'HTTP')\r\n this.setInfoValue(this.p2p, player.localize(this.options_.p2pEnabled ? 'enabled' : 'disabled'))\r\n this.setInfoValue(this.uuid, this.options_.videoUUID)\r\n\r\n this.setInfoValue(this.viewport, frames)\r\n this.setInfoValue(this.resolution, resolution)\r\n this.setInfoValue(this.volume, volume)\r\n this.setInfoValue(this.codecs, codecs)\r\n this.setInfoValue(this.color, colorSpace)\r\n this.setInfoValue(this.connection, playerNetworkInfo.averageBandwidth)\r\n\r\n this.setInfoValue(this.network, networkActivity)\r\n this.setInfoValue(this.transferred, totalTransferred)\r\n this.setInfoValue(this.download, downloadBreakdown)\r\n\r\n this.setInfoValue(this.bufferProgress, bufferProgress)\r\n this.setInfoValue(this.bufferState, buffer)\r\n\r\n this.setInfoValue(this.liveLatency, latency)\r\n }\r\n\r\n private setInfoValue (el: InfoElement, value: string) {\r\n if (!value) {\r\n el.root.style.display = 'none'\r\n return\r\n }\r\n\r\n el.root.style.display = 'block'\r\n\r\n if (el.value.innerHTML === value) return\r\n el.value.innerHTML = value\r\n }\r\n\r\n private buildInfoRow (labelText: string, valueHTML?: string) {\r\n const root = videojs.dom.createEl('div') as HTMLElement\r\n root.style.display = 'none'\r\n\r\n const label = videojs.dom.createEl('div', { innerText: labelText }) as HTMLElement\r\n const value = videojs.dom.createEl('span', { innerHTML: valueHTML }) as HTMLElement\r\n\r\n root.appendChild(label)\r\n root.appendChild(value)\r\n\r\n return { root, value }\r\n }\r\n\r\n private timeRangesToString (r: videojs.TimeRange) {\r\n let result = ''\r\n\r\n for (let i = 0; i < r.length; i++) {\r\n const start = Math.floor(r.start(i))\r\n const end = Math.floor(r.end(i))\r\n\r\n result += `[${secondsToTime(start)}, ${secondsToTime(end)}] `\r\n }\r\n\r\n return result\r\n }\r\n}\r\n\r\nvideojs.registerComponent('StatsCard', StatsCard)\r\n\r\nexport {\r\n StatsCard,\r\n StatsCardOptions\r\n}\r\n","import videojs from 'video.js'\r\nimport { StatsCard, StatsCardOptions } from './stats-card'\r\n\r\nconst Plugin = videojs.getPlugin('plugin')\r\n\r\nclass StatsForNerdsPlugin extends Plugin {\r\n private statsCard: StatsCard\r\n\r\n constructor (player: videojs.Player, options: StatsCardOptions) {\r\n const settings = {\r\n ...options\r\n }\r\n\r\n super(player)\r\n\r\n this.player.ready(() => {\r\n player.addClass('vjs-stats-for-nerds')\r\n })\r\n\r\n this.statsCard = new StatsCard(player, options)\r\n\r\n player.addChild(this.statsCard, settings)\r\n }\r\n\r\n show () {\r\n this.statsCard.show()\r\n }\r\n}\r\n\r\nvideojs.registerPlugin('stats', StatsForNerdsPlugin)\r\nexport { StatsForNerdsPlugin }\r\n","function isIOS () {\r\n if (/iPad|iPhone|iPod/.test(navigator.platform)) {\r\n return true\r\n }\r\n\r\n // Detect iPad Desktop mode\r\n return !!(navigator.maxTouchPoints &&\r\n navigator.maxTouchPoints > 2 &&\r\n navigator.platform.includes('MacIntel'))\r\n}\r\n\r\nfunction isSafari () {\r\n return /^((?!chrome|android).)*safari/i.test(navigator.userAgent)\r\n}\r\n\r\nfunction isMobile () {\r\n return /iPhone|iPad|iPod|Android/i.test(navigator.userAgent)\r\n}\r\n\r\nexport {\r\n isIOS,\r\n isSafari,\r\n isMobile\r\n}\r\n","import videojs from 'video.js'\r\nimport { isMobile } from '@root-helpers/web-browser'\r\n\r\nfunction getPauseBezel () {\r\n return `\r\n
\r\n
\r\n
\r\n \r\n \r\n \r\n \r\n
\r\n
\r\n
\r\n `\r\n}\r\n\r\nfunction getPlayBezel () {\r\n return `\r\n
\r\n
\r\n
\r\n \r\n \r\n \r\n \r\n
\r\n
\r\n
\r\n `\r\n}\r\n\r\nconst Component = videojs.getComponent('Component')\r\nclass PauseBezel extends Component {\r\n container: HTMLDivElement\r\n\r\n constructor (player: videojs.Player, options?: videojs.ComponentOptions) {\r\n super(player, options)\r\n\r\n // Hide bezels on mobile since we already have our mobile overlay\r\n if (isMobile()) return\r\n\r\n player.on('pause', (_: any) => {\r\n if (player.seeking() || player.ended()) return\r\n this.container.innerHTML = getPauseBezel()\r\n this.showBezel()\r\n })\r\n\r\n player.on('play', (_: any) => {\r\n if (player.seeking()) return\r\n this.container.innerHTML = getPlayBezel()\r\n this.showBezel()\r\n })\r\n }\r\n\r\n createEl () {\r\n this.container = super.createEl('div', {\r\n className: 'vjs-bezels-content'\r\n }) as HTMLDivElement\r\n\r\n this.container.style.display = 'none'\r\n\r\n return this.container\r\n }\r\n\r\n showBezel () {\r\n this.container.style.display = 'inherit'\r\n\r\n setTimeout(() => {\r\n this.container.style.display = 'none'\r\n }, 500) // matching the animation duration\r\n }\r\n}\r\n\r\nvideojs.registerComponent('PauseBezel', PauseBezel)\r\n","import videojs from 'video.js'\r\nimport './pause-bezel'\r\n\r\nconst Plugin = videojs.getPlugin('plugin')\r\n\r\nclass BezelsPlugin extends Plugin {\r\n\r\n constructor (player: videojs.Player, options?: videojs.ComponentOptions) {\r\n super(player)\r\n\r\n this.player.ready(() => {\r\n player.addClass('vjs-bezels')\r\n })\r\n\r\n player.addChild('PauseBezel', options)\r\n }\r\n}\r\n\r\nvideojs.registerPlugin('bezels', BezelsPlugin)\r\n\r\nexport { BezelsPlugin }\r\n","import { logger } from '@root-helpers/logger'\r\n\r\nfunction getStoredVolume () {\r\n const value = getLocalStorage('volume')\r\n if (value !== null && value !== undefined) {\r\n const valueNumber = parseFloat(value)\r\n if (isNaN(valueNumber)) return undefined\r\n\r\n return valueNumber\r\n }\r\n\r\n return undefined\r\n}\r\n\r\nfunction getStoredMute () {\r\n const value = getLocalStorage('mute')\r\n if (value !== null && value !== undefined) return value === 'true'\r\n\r\n return undefined\r\n}\r\n\r\nfunction getStoredTheater () {\r\n const value = getLocalStorage('theater-enabled')\r\n if (value !== null && value !== undefined) return value === 'true'\r\n\r\n return false\r\n}\r\n\r\nfunction saveVolumeInStore (value: number) {\r\n return setLocalStorage('volume', value.toString())\r\n}\r\n\r\nfunction saveMuteInStore (value: boolean) {\r\n return setLocalStorage('mute', value.toString())\r\n}\r\n\r\nfunction saveTheaterInStore (enabled: boolean) {\r\n return setLocalStorage('theater-enabled', enabled.toString())\r\n}\r\n\r\nfunction saveAverageBandwidth (value: number) {\r\n /** used to choose the most fitting resolution */\r\n return setLocalStorage('average-bandwidth', value.toString())\r\n}\r\n\r\nfunction getAverageBandwidthInStore () {\r\n const value = getLocalStorage('average-bandwidth')\r\n if (value !== null && value !== undefined) {\r\n const valueNumber = parseInt(value, 10)\r\n if (isNaN(valueNumber)) return undefined\r\n\r\n return valueNumber\r\n }\r\n\r\n return undefined\r\n}\r\n\r\nfunction saveLastSubtitle (language: string) {\r\n return setLocalStorage('last-subtitle', language)\r\n}\r\n\r\nfunction getStoredLastSubtitle () {\r\n return getLocalStorage('last-subtitle')\r\n}\r\n\r\nfunction saveVideoWatchHistory (videoUUID: string, duration: number) {\r\n return setLocalStorage(`video-watch-history`, JSON.stringify({\r\n ...getStoredVideoWatchHistory(),\r\n\r\n [videoUUID]: {\r\n duration,\r\n date: `${(new Date()).toISOString()}`\r\n }\r\n }))\r\n}\r\n\r\nfunction getStoredVideoWatchHistory (videoUUID?: string) {\r\n let data\r\n\r\n try {\r\n const value = getLocalStorage('video-watch-history')\r\n if (!value) return {}\r\n\r\n data = JSON.parse(value)\r\n } catch (error) {\r\n logger.error('Cannot parse video watch history from local storage/', error)\r\n }\r\n\r\n data = data || {}\r\n\r\n if (videoUUID) return data[videoUUID]\r\n\r\n return data\r\n}\r\n\r\nfunction cleanupVideoWatch () {\r\n const data = getStoredVideoWatchHistory()\r\n if (!data) return\r\n\r\n const newData = Object.keys(data).reduce((acc, videoUUID) => {\r\n const date = Date.parse(data[videoUUID].date)\r\n\r\n const diff = Math.ceil(((new Date()).getTime() - date) / (1000 * 3600 * 24))\r\n\r\n if (diff > 30) return acc\r\n\r\n return {\r\n ...acc,\r\n [videoUUID]: data[videoUUID]\r\n }\r\n }, {})\r\n\r\n setLocalStorage('video-watch-history', JSON.stringify(newData))\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n\r\nexport {\r\n getStoredVolume,\r\n getStoredMute,\r\n getStoredTheater,\r\n saveVolumeInStore,\r\n saveMuteInStore,\r\n saveTheaterInStore,\r\n saveAverageBandwidth,\r\n getAverageBandwidthInStore,\r\n saveLastSubtitle,\r\n getStoredLastSubtitle,\r\n saveVideoWatchHistory,\r\n getStoredVideoWatchHistory,\r\n cleanupVideoWatch\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n\r\nconst KEY_PREFIX = 'peertube-videojs-'\r\n\r\nfunction getLocalStorage (key: string) {\r\n try {\r\n return localStorage.getItem(KEY_PREFIX + key)\r\n } catch {\r\n return undefined\r\n }\r\n}\r\n\r\nfunction setLocalStorage (key: string, value: string) {\r\n try {\r\n localStorage.setItem(KEY_PREFIX + key, value)\r\n } catch { /* empty */\r\n }\r\n}\r\n","import debug from 'debug'\r\nimport videojs from 'video.js'\r\nimport { logger } from '@root-helpers/logger'\r\nimport { isMobile } from '@root-helpers/web-browser'\r\nimport { timeToInt } from '@shared/core-utils'\r\nimport { VideoView, VideoViewEvent } from '@shared/models/videos'\r\nimport {\r\n getStoredLastSubtitle,\r\n getStoredMute,\r\n getStoredVolume,\r\n saveLastSubtitle,\r\n saveMuteInStore,\r\n saveVideoWatchHistory,\r\n saveVolumeInStore\r\n} from '../../peertube-player-local-storage'\r\nimport { PeerTubePluginOptions, VideoJSCaption } from '../../types'\r\nimport { SettingsButton } from '../settings/settings-menu-button'\r\n\r\nconst debugLogger = debug('peertube:player:peertube')\r\n\r\nconst Plugin = videojs.getPlugin('plugin')\r\n\r\nclass PeerTubePlugin extends Plugin {\r\n private readonly videoViewUrl: string\r\n private readonly authorizationHeader: string\r\n\r\n private readonly videoUUID: string\r\n private readonly startTime: number\r\n\r\n private readonly CONSTANTS = {\r\n USER_VIEW_VIDEO_INTERVAL: 30000 // Every 60 seconds, notify the user is watching the video\r\n }\r\n\r\n //private videoCaptions: VideoJSCaption[]\r\n private defaultSubtitle: string\r\n\r\n private videoViewInterval: any\r\n\r\n private menuOpened = false\r\n private mouseInControlBar = false\r\n private mouseInSettings = false\r\n private readonly initialInactivityTimeout: number\r\n\r\n constructor (player: videojs.Player, options?: PeerTubePluginOptions) {\r\n super(player)\r\n\r\n this.videoViewUrl = options.videoViewUrl\r\n this.authorizationHeader = options.authorizationHeader\r\n this.videoUUID = options.videoUUID\r\n this.startTime = timeToInt(options.startTime)\r\n\r\n //this.videoCaptions = options.videoCaptions\r\n this.initialInactivityTimeout = this.player.options_.inactivityTimeout\r\n\r\n if (options.autoplay) this.player.addClass('vjs-has-autoplay')\r\n\r\n this.player.on('autoplay-failure', () => {\r\n this.player.removeClass('vjs-has-autoplay')\r\n })\r\n\r\n this.player.ready(() => {\r\n const playerOptions = this.player.options_\r\n\r\n const volume = getStoredVolume()\r\n if (volume !== undefined) this.player.volume(volume)\r\n\r\n const muted = playerOptions.muted !== undefined ? playerOptions.muted : getStoredMute()\r\n if (muted !== undefined) this.player.muted(muted)\r\n\r\n this.defaultSubtitle = options.subtitle || getStoredLastSubtitle()\r\n\r\n this.player.on('volumechange', () => {\r\n saveVolumeInStore(this.player.volume())\r\n saveMuteInStore(this.player.muted())\r\n })\r\n\r\n if (options.stopTime) {\r\n const stopTime = timeToInt(options.stopTime)\r\n const self = this\r\n\r\n this.player.on('timeupdate', function onTimeUpdate () {\r\n if (self.player.currentTime() > stopTime) {\r\n self.player.pause()\r\n self.player.trigger('stopped')\r\n\r\n self.player.off('timeupdate', onTimeUpdate)\r\n }\r\n })\r\n }\r\n\r\n this.player.textTracks().addEventListener('change', () => {\r\n const showing = this.player.textTracks().tracks_.find(t => {\r\n return t.kind === 'captions' && t.mode === 'showing'\r\n })\r\n\r\n if (!showing) {\r\n saveLastSubtitle('off')\r\n return\r\n }\r\n\r\n saveLastSubtitle(showing.language)\r\n })\r\n\r\n //this.player.on('sourcechange', () => this.initCaptions())\r\n\r\n this.player.duration(options.videoDuration)\r\n\r\n this.initializePlayer()\r\n this.runUserViewing()\r\n })\r\n }\r\n\r\n dispose () {\r\n if (this.videoViewInterval) clearInterval(this.videoViewInterval)\r\n }\r\n\r\n onMenuOpened () {\r\n this.menuOpened = true\r\n this.alterInactivity()\r\n }\r\n\r\n onMenuClosed () {\r\n this.menuOpened = false\r\n this.alterInactivity()\r\n }\r\n\r\n displayFatalError () {\r\n this.player.addClass('vjs-error-display-enabled')\r\n }\r\n\r\n hideFatalError () {\r\n this.player.removeClass('vjs-error-display-enabled')\r\n }\r\n\r\n private initializePlayer () {\r\n if (isMobile()) this.player.addClass('vjs-is-mobile')\r\n\r\n //this.initSmoothProgressBar()\r\n\r\n //this.initCaptions()\r\n\r\n this.listenControlBarMouse()\r\n\r\n this.listenFullScreenChange()\r\n }\r\n\r\n private runUserViewing () {\r\n let lastCurrentTime = this.startTime\r\n let lastViewEvent: VideoViewEvent\r\n\r\n this.player.one('play', () => {\r\n this.notifyUserIsWatching(Math.round(this.startTime), lastViewEvent)\r\n })\r\n\r\n this.player.on('seeked', () => {\r\n // Don't take into account small seek events\r\n if (Math.abs(this.player.currentTime() - lastCurrentTime) < 3) return\r\n\r\n lastViewEvent = 'seek'\r\n })\r\n\r\n \r\n\r\n this.player.one('ended', () => {\r\n\r\n const currentTime = Math.round(this.player.duration())\r\n lastCurrentTime = currentTime\r\n\r\n this.notifyUserIsWatching(currentTime, lastViewEvent)\r\n\r\n lastViewEvent = undefined\r\n })\r\n\r\n this.videoViewInterval = setInterval(() => {\r\n const currentTime = Math.round(this.player.currentTime())\r\n\r\n // No need to update\r\n if (currentTime === lastCurrentTime) return\r\n\r\n lastCurrentTime = currentTime\r\n\r\n this.notifyUserIsWatching(currentTime, lastViewEvent)\r\n .catch(err => logger.error('Cannot notify user is watching.', err))\r\n\r\n lastViewEvent = undefined\r\n\r\n // Server won't save history, so save the video position in local storage\r\n if (!this.authorizationHeader) {\r\n saveVideoWatchHistory(this.videoUUID, currentTime)\r\n }\r\n }, this.CONSTANTS.USER_VIEW_VIDEO_INTERVAL)\r\n }\r\n\r\n private notifyUserIsWatching (currentTime: number, viewEvent: VideoViewEvent) {\r\n if (!this.videoViewUrl) return Promise.resolve(undefined)\r\n\r\n const body: VideoView = {\r\n currentTime,\r\n viewEvent\r\n }\r\n\r\n const headers = new Headers({\r\n 'Content-type': 'application/json; charset=UTF-8'\r\n })\r\n\r\n if (this.authorizationHeader) headers.set('Authorization', this.authorizationHeader)\r\n\r\n return fetch(this.videoViewUrl, { method: 'POST', body: JSON.stringify(body), headers }).catch(e => {\r\n return Promise.resolve()\r\n })\r\n }\r\n\r\n private listenFullScreenChange () {\r\n this.player.on('fullscreenchange', () => {\r\n if (this.player.isFullscreen()) this.player.focus()\r\n })\r\n }\r\n\r\n private listenControlBarMouse () {\r\n const controlBar = this.player.controlBar\r\n const settingsButton: SettingsButton = (controlBar as any).settingsButton\r\n\r\n controlBar.on('mouseenter', () => {\r\n this.mouseInControlBar = true\r\n this.alterInactivity()\r\n })\r\n\r\n controlBar.on('mouseleave', () => {\r\n this.mouseInControlBar = false\r\n this.alterInactivity()\r\n })\r\n\r\n settingsButton.dialog.on('mouseenter', () => {\r\n this.mouseInSettings = true\r\n this.alterInactivity()\r\n })\r\n\r\n settingsButton.dialog.on('mouseleave', () => {\r\n this.mouseInSettings = false\r\n this.alterInactivity()\r\n })\r\n }\r\n\r\n private alterInactivity () {\r\n if (this.menuOpened || this.mouseInSettings || this.mouseInControlBar) {\r\n this.setInactivityTimeout(0)\r\n return\r\n }\r\n\r\n this.setInactivityTimeout(this.initialInactivityTimeout)\r\n this.player.reportUserActivity(true)\r\n }\r\n\r\n private setInactivityTimeout (timeout: number) {\r\n (this.player as any).cache_.inactivityTimeout = timeout\r\n this.player.options_.inactivityTimeout = timeout\r\n\r\n debugLogger('Set player inactivity to ' + timeout)\r\n }\r\n\r\n}\r\n\r\nvideojs.registerPlugin('peertube', PeerTubePlugin)\r\nexport { PeerTubePlugin }\r\n","import videojs from 'video.js'\r\nimport { PeerTubeResolution } from '../../types'\r\n\r\nconst Plugin = videojs.getPlugin('plugin')\r\n\r\nclass PeerTubeResolutionsPlugin extends Plugin {\r\n private currentSelection: PeerTubeResolution\r\n private resolutions: PeerTubeResolution[] = []\r\n\r\n private autoResolutionChosenId: number\r\n private autoResolutionEnabled = true\r\n\r\n add (resolutions: PeerTubeResolution[]) {\r\n for (const r of resolutions) {\r\n this.resolutions.push(r)\r\n }\r\n\r\n this.currentSelection = this.getSelected()\r\n\r\n this.sort()\r\n this.trigger('resolutionsAdded')\r\n }\r\n\r\n getResolutions () {\r\n return this.resolutions\r\n }\r\n\r\n getSelected () {\r\n return this.resolutions.find(r => r.selected)\r\n }\r\n\r\n getAutoResolutionChosen () {\r\n return this.resolutions.find(r => r.id === this.autoResolutionChosenId)\r\n }\r\n\r\n select (options: {\r\n id: number\r\n byEngine: boolean\r\n autoResolutionChosenId?: number\r\n }) {\r\n const { id, autoResolutionChosenId, byEngine } = options\r\n\r\n if (this.currentSelection?.id === id && this.autoResolutionChosenId === autoResolutionChosenId) return\r\n\r\n this.autoResolutionChosenId = autoResolutionChosenId\r\n\r\n for (const r of this.resolutions) {\r\n r.selected = r.id === id\r\n\r\n if (r.selected) {\r\n this.currentSelection = r\r\n\r\n if (!byEngine) r.selectCallback()\r\n }\r\n }\r\n\r\n this.trigger('resolutionChanged')\r\n }\r\n\r\n disableAutoResolution () {\r\n this.autoResolutionEnabled = false\r\n this.trigger('autoResolutionEnabledChanged')\r\n }\r\n\r\n enabledAutoResolution () {\r\n this.autoResolutionEnabled = true\r\n this.trigger('autoResolutionEnabledChanged')\r\n }\r\n\r\n isAutoResolutionEnabeld () {\r\n return this.autoResolutionEnabled\r\n }\r\n\r\n private sort () {\r\n this.resolutions.sort((a, b) => {\r\n if (a.id === -1) return 1\r\n if (b.id === -1) return -1\r\n\r\n if (a.height > b.height) return -1\r\n if (a.height === b.height) return 0\r\n return 1\r\n })\r\n }\r\n\r\n}\r\n\r\nvideojs.registerPlugin('peertubeResolutions', PeerTubeResolutionsPlugin)\r\nexport { PeerTubeResolutionsPlugin }\r\n","import videojs from 'video.js'\r\nimport { NextPreviousVideoButtonOptions } from '../../types'\r\n\r\nconst Button = videojs.getComponent('Button')\r\n\r\nclass NextPreviousVideoButton extends Button {\r\n private readonly nextPreviousVideoButtonOptions: NextPreviousVideoButtonOptions\r\n\r\n constructor (player: videojs.Player, options?: NextPreviousVideoButtonOptions) {\r\n super(player, options as any)\r\n\r\n this.nextPreviousVideoButtonOptions = options\r\n\r\n this.update()\r\n }\r\n\r\n createEl () {\r\n const type = (this.options_ as NextPreviousVideoButtonOptions).type\r\n\r\n const button = videojs.dom.createEl('button', {\r\n className: 'vjs-' + type + '-video'\r\n }) as HTMLButtonElement\r\n const nextIcon = videojs.dom.createEl('span', {\r\n className: 'icon icon-' + type\r\n })\r\n button.appendChild(nextIcon)\r\n\r\n if (type === 'next') {\r\n button.title = this.player_.localize('Next video')\r\n } else {\r\n button.title = this.player_.localize('Previous video')\r\n }\r\n\r\n return button\r\n }\r\n\r\n handleClick () {\r\n this.nextPreviousVideoButtonOptions.handler()\r\n }\r\n\r\n update () {\r\n const disabled = this.nextPreviousVideoButtonOptions.isDisabled()\r\n\r\n if (disabled) this.addClass('vjs-disabled')\r\n else this.removeClass('vjs-disabled')\r\n }\r\n}\r\n\r\nvideojs.registerComponent('NextVideoButton', NextPreviousVideoButton)\r\nvideojs.registerComponent('PreviousVideoButton', NextPreviousVideoButton)\r\n","import videojs from 'video.js'\r\nimport { PeerTubeP2PInfoButtonOptions, PlayerNetworkInfo } from '../../types'\r\nimport { bytes } from '../common'\r\n\r\nconst Button = videojs.getComponent('Button')\r\nclass P2pInfoButton extends Button {\r\n\r\n constructor (player: videojs.Player, options?: PeerTubeP2PInfoButtonOptions) {\r\n super(player, options as any)\r\n }\r\n\r\n createEl () {\r\n const div = videojs.dom.createEl('div', {\r\n className: 'vjs-peertube'\r\n })\r\n const subDivWebtorrent = videojs.dom.createEl('div', {\r\n className: 'vjs-peertube-hidden' // Hide the stats before we get the info\r\n }) as HTMLDivElement\r\n div.appendChild(subDivWebtorrent)\r\n\r\n // Stop here if P2P is not enabled\r\n const p2pEnabled = (this.options_ as PeerTubeP2PInfoButtonOptions).p2pEnabled\r\n if (!p2pEnabled) return div as HTMLButtonElement\r\n\r\n const downloadIcon = videojs.dom.createEl('span', {\r\n className: 'icon icon-download'\r\n })\r\n subDivWebtorrent.appendChild(downloadIcon)\r\n\r\n const downloadSpeedText = videojs.dom.createEl('span', {\r\n className: 'download-speed-text'\r\n })\r\n const downloadSpeedNumber = videojs.dom.createEl('span', {\r\n className: 'download-speed-number'\r\n })\r\n const downloadSpeedUnit = videojs.dom.createEl('span')\r\n downloadSpeedText.appendChild(downloadSpeedNumber)\r\n downloadSpeedText.appendChild(downloadSpeedUnit)\r\n subDivWebtorrent.appendChild(downloadSpeedText)\r\n\r\n const uploadIcon = videojs.dom.createEl('span', {\r\n className: 'icon icon-upload'\r\n })\r\n subDivWebtorrent.appendChild(uploadIcon)\r\n\r\n const uploadSpeedText = videojs.dom.createEl('span', {\r\n className: 'upload-speed-text'\r\n })\r\n const uploadSpeedNumber = videojs.dom.createEl('span', {\r\n className: 'upload-speed-number'\r\n })\r\n const uploadSpeedUnit = videojs.dom.createEl('span')\r\n uploadSpeedText.appendChild(uploadSpeedNumber)\r\n uploadSpeedText.appendChild(uploadSpeedUnit)\r\n subDivWebtorrent.appendChild(uploadSpeedText)\r\n\r\n const peersText = videojs.dom.createEl('span', {\r\n className: 'peers-text'\r\n })\r\n const peersNumber = videojs.dom.createEl('span', {\r\n className: 'peers-number'\r\n })\r\n subDivWebtorrent.appendChild(peersNumber)\r\n subDivWebtorrent.appendChild(peersText)\r\n\r\n const subDivHttp = videojs.dom.createEl('div', {\r\n className: 'vjs-peertube-hidden'\r\n })\r\n const subDivHttpText = videojs.dom.createEl('span', {\r\n className: 'http-fallback',\r\n textContent: 'HTTP'\r\n })\r\n\r\n subDivHttp.appendChild(subDivHttpText)\r\n div.appendChild(subDivHttp)\r\n\r\n this.player_.on('p2pInfo', (event: any, data: PlayerNetworkInfo) => {\r\n // We are in HTTP fallback\r\n if (!data) {\r\n subDivHttp.className = 'vjs-peertube-displayed'\r\n subDivWebtorrent.className = 'vjs-peertube-hidden'\r\n\r\n return\r\n }\r\n\r\n const p2pStats = data.p2p\r\n const httpStats = data.http\r\n\r\n const downloadSpeed = bytes(p2pStats.downloadSpeed + httpStats.downloadSpeed)\r\n const uploadSpeed = bytes(p2pStats.uploadSpeed + httpStats.uploadSpeed)\r\n const totalDownloaded = bytes(p2pStats.downloaded + httpStats.downloaded)\r\n const totalUploaded = bytes(p2pStats.uploaded + httpStats.uploaded)\r\n const numPeers = p2pStats.numPeers\r\n\r\n subDivWebtorrent.title = this.player().localize('Total downloaded: ') + totalDownloaded.join(' ') + '\\n'\r\n\r\n if (data.source === 'p2p-media-loader') {\r\n const downloadedFromServer = bytes(httpStats.downloaded).join(' ')\r\n const downloadedFromPeers = bytes(p2pStats.downloaded).join(' ')\r\n\r\n subDivWebtorrent.title +=\r\n ' * ' + this.player().localize('From servers: ') + downloadedFromServer + '\\n' +\r\n ' * ' + this.player().localize('From peers: ') + downloadedFromPeers + '\\n'\r\n }\r\n subDivWebtorrent.title += this.player().localize('Total uploaded: ') + totalUploaded.join(' ')\r\n\r\n downloadSpeedNumber.textContent = downloadSpeed[0]\r\n downloadSpeedUnit.textContent = ' ' + downloadSpeed[1]\r\n\r\n uploadSpeedNumber.textContent = uploadSpeed[0]\r\n uploadSpeedUnit.textContent = ' ' + uploadSpeed[1]\r\n\r\n peersNumber.textContent = numPeers.toString()\r\n peersText.textContent = ' ' + (numPeers > 1 ? this.player().localize('peers') : this.player_.localize('peer'))\r\n\r\n subDivHttp.className = 'vjs-peertube-hidden'\r\n subDivWebtorrent.className = 'vjs-peertube-displayed'\r\n })\r\n\r\n return div as HTMLButtonElement\r\n }\r\n}\r\n\r\nvideojs.registerComponent('P2PInfoButton', P2pInfoButton)\r\n","/**\r\n * @file picture-in-picture-toggle.js\r\n */\r\n /*import Button from '../button.js';\r\n import Component from '../component.js';\r\n import document from 'global/document';*/\r\n\r\n import videojs from \"video.js\";\r\n\r\n const Button = videojs.getComponent(\"Button\");\r\n const MenuButton = videojs.getComponent(\"MenuButton\");\r\n \r\n /**\r\n * Toggle Picture-in-Picture mode\r\n *\r\n * @extends Button\r\n */\r\n class PictureInPictureBastyon extends MenuButton {\r\n \r\n \r\n constructor(player : any, options : any) {\r\n super(player, options);\r\n \r\n this.controlText('Mini Player')\r\n }\r\n \r\n createEl(){\r\n return this.buildElement();\r\n }\r\n \r\n handleClick(event : any) {\r\n this.player_.trigger('pictureInPictureRequest', event)\r\n }\r\n \r\n private buildElement() {\r\n \r\n const el = super.createEl();\r\n \r\n el.classList.add(\"vjs-picture-in-picture-control\");\r\n \r\n return el as HTMLButtonElement;\r\n }\r\n \r\n }\r\n \r\n videojs.registerComponent(\"PictureInPictureBastyon\", PictureInPictureBastyon);\r\n ","import videojs from 'video.js'\r\n\r\nconst Component = videojs.getComponent('Component')\r\n\r\nclass PeerTubeLoadProgressBar extends Component {\r\n\r\n constructor (player: videojs.Player, options?: videojs.ComponentOptions) {\r\n super(player, options)\r\n\r\n this.on(player, 'progress', this.update)\r\n }\r\n\r\n createEl () {\r\n return super.createEl('div', {\r\n className: 'vjs-load-progress',\r\n innerHTML: `${this.localize('Loaded')}: 0%`\r\n })\r\n }\r\n\r\n dispose () {\r\n super.dispose()\r\n }\r\n\r\n update () {\r\n return\r\n /*const torrent = this.player().webtorrent().getTorrent()\r\n if (!torrent) return\r\n\r\n // @ts-ignore\r\n (this.el() as HTMLElement).style['transform-origin'] = 'left'\r\n (this.el() as HTMLElement).style['transform'] = 'scaleX('+(torrent.progress).toFixed(2)+')'*/\r\n\r\n //(this.el() as HTMLElement).style.width = (torrent.progress * 100) + '%'\r\n }\r\n\r\n}\r\n\r\nComponent.registerComponent('PeerTubeLoadProgressBar', PeerTubeLoadProgressBar)\r\n","import videojs from 'video.js'\r\nimport { getStoredTheater, saveTheaterInStore } from '../../peertube-player-local-storage'\r\n\r\nconst Button = videojs.getComponent('Button')\r\nclass TheaterButton extends Button {\r\n\r\n private static readonly THEATER_MODE_CLASS = 'vjs-theater-enabled'\r\n\r\n constructor (player: videojs.Player, options: videojs.ComponentOptions) {\r\n super(player, options)\r\n\r\n const enabled = getStoredTheater()\r\n if (enabled === true) {\r\n this.player().addClass(TheaterButton.THEATER_MODE_CLASS)\r\n\r\n this.handleTheaterChange()\r\n }\r\n\r\n this.controlText('Theater mode')\r\n\r\n this.player().theaterEnabled = enabled\r\n }\r\n\r\n buildCSSClass () {\r\n return `vjs-theater-control ${super.buildCSSClass()}`\r\n }\r\n\r\n handleTheaterChange () {\r\n const theaterEnabled = this.isTheaterEnabled()\r\n\r\n if (theaterEnabled) {\r\n this.controlText('Normal mode')\r\n } else {\r\n this.controlText('Theater mode')\r\n }\r\n\r\n saveTheaterInStore(theaterEnabled)\r\n\r\n this.player_.trigger('theaterChange', theaterEnabled)\r\n }\r\n\r\n handleClick () {\r\n this.player_.toggleClass(TheaterButton.THEATER_MODE_CLASS)\r\n\r\n this.handleTheaterChange()\r\n }\r\n\r\n private isTheaterEnabled () {\r\n return this.player_.hasClass(TheaterButton.THEATER_MODE_CLASS)\r\n }\r\n}\r\n\r\nvideojs.registerComponent('TheaterButton', TheaterButton)\r\n","import videojs from 'video.js'\r\n\r\nconst MenuItem = videojs.getComponent('MenuItem')\r\n\r\nexport interface ResolutionMenuItemOptions extends videojs.MenuItemOptions {\r\n resolutionId: number\r\n}\r\n\r\nclass ResolutionMenuItem extends MenuItem {\r\n private readonly resolutionId: number\r\n private readonly label: string\r\n\r\n private autoResolutionEnabled: boolean\r\n private autoResolutionChosen: string\r\n\r\n constructor (player: videojs.Player, options?: ResolutionMenuItemOptions) {\r\n options.selectable = true\r\n\r\n super(player, options)\r\n\r\n this.autoResolutionEnabled = true\r\n this.autoResolutionChosen = ''\r\n\r\n this.resolutionId = options.resolutionId\r\n this.label = options.label\r\n\r\n player.peertubeResolutions().on('resolutionChanged', () => this.updateSelection())\r\n\r\n // We only want to disable the \"Auto\" item\r\n if (this.resolutionId === -1) {\r\n player.peertubeResolutions().on('autoResolutionEnabledChanged', () => this.updateAutoResolution())\r\n }\r\n }\r\n\r\n handleClick (event: any) {\r\n // Auto button disabled?\r\n if (this.autoResolutionEnabled === false && this.resolutionId === -1) return\r\n\r\n super.handleClick(event)\r\n\r\n this.player().peertubeResolutions().select({ id: this.resolutionId, byEngine: false })\r\n }\r\n\r\n updateSelection () {\r\n const selectedResolution = this.player().peertubeResolutions().getSelected()\r\n\r\n if (this.resolutionId === -1) {\r\n this.autoResolutionChosen = this.player().peertubeResolutions().getAutoResolutionChosen()?.label\r\n }\r\n\r\n this.selected(this.resolutionId === selectedResolution.id)\r\n }\r\n\r\n updateAutoResolution () {\r\n const enabled = this.player().peertubeResolutions().isAutoResolutionEnabeld()\r\n\r\n // Check if the auto resolution is enabled or not\r\n if (enabled === false) {\r\n this.addClass('disabled')\r\n } else {\r\n this.removeClass('disabled')\r\n }\r\n\r\n this.autoResolutionEnabled = enabled\r\n }\r\n\r\n getLabel () {\r\n if (this.resolutionId === -1) {\r\n return this.label + ' ' + this.autoResolutionChosen + ''\r\n }\r\n\r\n return this.label\r\n }\r\n}\r\nvideojs.registerComponent('ResolutionMenuItem', ResolutionMenuItem)\r\n\r\nexport { ResolutionMenuItem }\r\n","import videojs from 'video.js'\r\nimport { ResolutionMenuItem } from './resolution-menu-item'\r\n\r\nconst Menu = videojs.getComponent('Menu')\r\nconst MenuButton = videojs.getComponent('MenuButton')\r\nclass ResolutionMenuButton extends MenuButton {\r\n labelEl_: HTMLElement\r\n\r\n constructor (player: videojs.Player, options?: videojs.MenuButtonOptions) {\r\n super(player, options)\r\n\r\n this.controlText('Quality')\r\n\r\n player.peertubeResolutions().on('resolutionsAdded', () => this.buildQualities())\r\n\r\n // For parent\r\n player.peertubeResolutions().on('resolutionChanged', () => {\r\n setTimeout(() => this.trigger('labelUpdated'))\r\n })\r\n }\r\n\r\n createEl () {\r\n const el = super.createEl()\r\n\r\n this.labelEl_ = videojs.dom.createEl('div', {\r\n className: 'vjs-resolution-value'\r\n }) as HTMLElement\r\n\r\n el.appendChild(this.labelEl_)\r\n\r\n return el\r\n }\r\n\r\n updateARIAAttributes () {\r\n this.el().setAttribute('aria-label', 'Quality')\r\n }\r\n\r\n createMenu () {\r\n return new Menu(this.player_)\r\n }\r\n\r\n buildCSSClass () {\r\n return super.buildCSSClass() + ' vjs-resolution-button'\r\n }\r\n\r\n buildWrapperCSSClass () {\r\n return 'vjs-resolution-control ' + super.buildWrapperCSSClass()\r\n }\r\n\r\n private addClickListener (component: any) {\r\n component.on('click', () => {\r\n const children = this.menu.children()\r\n\r\n for (const child of children) {\r\n if (component !== child) {\r\n (child as videojs.MenuItem).selected(false)\r\n }\r\n }\r\n })\r\n }\r\n\r\n private buildQualities () {\r\n for (const d of this.player().peertubeResolutions().getResolutions()) {\r\n const label = d.label === '0p'\r\n ? this.player().localize('Audio-only')\r\n : d.label\r\n\r\n this.menu.addChild(new ResolutionMenuItem(\r\n this.player_,\r\n {\r\n id: d.id + '',\r\n resolutionId: d.id,\r\n label,\r\n selected: d.selected\r\n })\r\n )\r\n }\r\n\r\n for (const m of this.menu.children()) {\r\n this.addClickListener(m)\r\n }\r\n\r\n this.trigger('menuChanged')\r\n }\r\n}\r\n\r\nvideojs.registerComponent('ResolutionMenuButton', ResolutionMenuButton)\r\n","import videojs from 'video.js'\r\n\r\nconst Component = videojs.getComponent('Component')\r\n\r\nclass SettingsDialog extends Component {\r\n constructor (player: videojs.Player) {\r\n super(player)\r\n\r\n this.hide()\r\n }\r\n\r\n /**\r\n * Create the component's DOM element\r\n *\r\n */\r\n createEl () {\r\n const uniqueId = this.id()\r\n const dialogLabelId = 'TTsettingsDialogLabel-' + uniqueId\r\n const dialogDescriptionId = 'TTsettingsDialogDescription-' + uniqueId\r\n\r\n return super.createEl('div', {\r\n className: 'vjs-settings-dialog vjs-modal-overlay',\r\n innerHTML: '',\r\n tabIndex: -1\r\n }, {\r\n role: 'dialog',\r\n 'aria-labelledby': dialogLabelId,\r\n 'aria-describedby': dialogDescriptionId\r\n })\r\n }\r\n}\r\n\r\nComponent.registerComponent('SettingsDialog', SettingsDialog)\r\n\r\nexport { SettingsDialog }\r\n","import videojs from 'video.js'\r\nimport { toTitleCase } from '../common'\r\nimport { SettingsDialog } from './settings-dialog'\r\nimport { SettingsButton } from './settings-menu-button'\r\nimport { SettingsPanel } from './settings-panel'\r\nimport { SettingsPanelChild } from './settings-panel-child'\r\n\r\nconst MenuItem = videojs.getComponent('MenuItem')\r\nconst component = videojs.getComponent('Component')\r\n\r\nexport interface SettingsMenuItemOptions extends videojs.MenuItemOptions {\r\n entry: string\r\n menuButton: SettingsButton\r\n}\r\n\r\nclass SettingsMenuItem extends MenuItem {\r\n settingsButton: SettingsButton\r\n dialog: SettingsDialog\r\n mainMenu: videojs.Menu\r\n panel: SettingsPanel\r\n panelChild: SettingsPanelChild\r\n panelChildEl: HTMLElement\r\n size: number[]\r\n menuToLoad: string\r\n subMenu: SettingsButton\r\n\r\n submenuClickHandler: typeof SettingsMenuItem.prototype.onSubmenuClick\r\n transitionEndHandler: typeof SettingsMenuItem.prototype.onTransitionEnd\r\n\r\n settingsSubMenuTitleEl_: HTMLElement\r\n settingsSubMenuValueEl_: HTMLElement\r\n settingsSubMenuEl_: HTMLElement\r\n\r\n constructor (player: videojs.Player, options?: SettingsMenuItemOptions) {\r\n super(player, options)\r\n\r\n this.settingsButton = options.menuButton\r\n this.dialog = this.settingsButton.dialog\r\n this.mainMenu = this.settingsButton.menu\r\n this.panel = this.dialog.getChild('settingsPanel')\r\n this.panelChild = this.panel.getChild('settingsPanelChild')\r\n this.panelChildEl = this.panelChild.el() as HTMLElement\r\n\r\n this.size = null\r\n\r\n // keep state of what menu type is loading next\r\n this.menuToLoad = 'mainmenu'\r\n\r\n const subMenuName = toTitleCase(options.entry)\r\n const SubMenuComponent = videojs.getComponent(subMenuName)\r\n\r\n if (!SubMenuComponent) {\r\n throw new Error(`Component ${subMenuName} does not exist`)\r\n }\r\n\r\n const newOptions = Object.assign({}, options, { entry: options.menuButton, menuButton: this })\r\n\r\n this.subMenu = new SubMenuComponent(this.player(), newOptions) as SettingsButton\r\n const subMenuClass = this.subMenu.buildCSSClass().split(' ')[0]\r\n this.settingsSubMenuEl_.className += ' ' + subMenuClass\r\n\r\n this.eventHandlers()\r\n\r\n player.ready(() => {\r\n // Voodoo magic for IOS\r\n setTimeout(() => {\r\n // Player was destroyed\r\n if (!this.player_) return\r\n\r\n this.build()\r\n\r\n // Update on rate change\r\n player.on('ratechange', this.submenuClickHandler)\r\n\r\n if (subMenuName === 'CaptionsButton') {\r\n // Hack to regenerate captions on HTTP fallback\r\n player.on('captionsChanged', () => {\r\n setTimeout(() => {\r\n this.settingsSubMenuEl_.innerHTML = ''\r\n this.settingsSubMenuEl_.appendChild(this.subMenu.menu.el())\r\n this.update()\r\n this.bindClickEvents()\r\n }, 0)\r\n })\r\n }\r\n\r\n this.reset()\r\n }, 0)\r\n })\r\n }\r\n\r\n eventHandlers () {\r\n this.submenuClickHandler = this.onSubmenuClick.bind(this)\r\n this.transitionEndHandler = this.onTransitionEnd.bind(this)\r\n }\r\n\r\n onSubmenuClick (event: any) {\r\n let target = null\r\n\r\n if (event.type === 'tap') {\r\n target = event.target\r\n } else {\r\n target = event.currentTarget || event.target\r\n }\r\n\r\n if (target?.classList.contains('vjs-back-button')) {\r\n this.loadMainMenu()\r\n return\r\n }\r\n\r\n // To update the sub menu value on click, setTimeout is needed because\r\n // updating the value is not instant\r\n setTimeout(() => this.update(event), 0)\r\n\r\n // Seems like videojs adds a vjs-hidden class on the caption menu after a click\r\n // We don't need it\r\n this.subMenu.menu.removeClass('vjs-hidden')\r\n }\r\n\r\n /**\r\n * Create the component's DOM element\r\n *\r\n */\r\n createEl () {\r\n const el = videojs.dom.createEl('li', {\r\n className: 'vjs-menu-item',\r\n tabIndex: -1\r\n })\r\n\r\n this.settingsSubMenuTitleEl_ = videojs.dom.createEl('div', {\r\n className: 'vjs-settings-sub-menu-title'\r\n }) as HTMLElement\r\n\r\n el.appendChild(this.settingsSubMenuTitleEl_)\r\n\r\n this.settingsSubMenuValueEl_ = videojs.dom.createEl('div', {\r\n className: 'vjs-settings-sub-menu-value'\r\n }) as HTMLElement\r\n\r\n el.appendChild(this.settingsSubMenuValueEl_)\r\n\r\n this.settingsSubMenuEl_ = videojs.dom.createEl('div', {\r\n className: 'vjs-settings-sub-menu'\r\n }) as HTMLElement\r\n\r\n return el as HTMLLIElement\r\n }\r\n\r\n /**\r\n * Handle click on menu item\r\n *\r\n * @method handleClick\r\n */\r\n handleClick (event: videojs.EventTarget.Event) {\r\n this.menuToLoad = 'submenu'\r\n // Remove open class to ensure only the open submenu gets this class\r\n videojs.dom.removeClass(this.el(), 'open')\r\n\r\n super.handleClick(event);\r\n\r\n (this.mainMenu.el() as HTMLElement).style.opacity = '0'\r\n // Whether to add or remove vjs-hidden class on the settingsSubMenuEl element\r\n if (videojs.dom.hasClass(this.settingsSubMenuEl_, 'vjs-hidden')) {\r\n videojs.dom.removeClass(this.settingsSubMenuEl_, 'vjs-hidden')\r\n\r\n // animation not played without timeout\r\n setTimeout(() => {\r\n this.settingsSubMenuEl_.style.opacity = '1'\r\n this.settingsSubMenuEl_.style.marginRight = '0px'\r\n }, 0)\r\n\r\n this.settingsButton.setDialogSize(this.size)\r\n\r\n const firstChild = this.subMenu.menu.children()[0]\r\n if (firstChild) firstChild.focus()\r\n } else {\r\n videojs.dom.addClass(this.settingsSubMenuEl_, 'vjs-hidden')\r\n }\r\n }\r\n\r\n /**\r\n * Create back button\r\n *\r\n * @method createBackButton\r\n */\r\n createBackButton () {\r\n const button = this.subMenu.menu.addChild('MenuItem', {}, 0)\r\n\r\n button.addClass('vjs-back-button');\r\n (button.el() as HTMLElement).innerHTML = this.player().localize(this.subMenu.controlText())\r\n }\r\n\r\n /**\r\n * Add/remove prefixed event listener for CSS Transition\r\n *\r\n * @method PrefixedEvent\r\n */\r\n PrefixedEvent (element: any, type: any, callback: any, action = 'addEvent') {\r\n const prefix = [ 'webkit', 'moz', 'MS', 'o', '' ]\r\n\r\n for (let p = 0; p < prefix.length; p++) {\r\n if (!prefix[p]) {\r\n type = type.toLowerCase()\r\n }\r\n\r\n if (action === 'addEvent') {\r\n element.addEventListener(prefix[p] + type, callback, false)\r\n } else if (action === 'removeEvent') {\r\n element.removeEventListener(prefix[p] + type, callback, false)\r\n }\r\n }\r\n }\r\n\r\n onTransitionEnd (event: any) {\r\n if (event.propertyName !== 'margin-right') {\r\n return\r\n }\r\n\r\n if (this.menuToLoad === 'mainmenu') {\r\n // hide submenu\r\n videojs.dom.addClass(this.settingsSubMenuEl_, 'vjs-hidden')\r\n\r\n // reset opacity to 0\r\n this.settingsSubMenuEl_.style.opacity = '0'\r\n }\r\n }\r\n\r\n reset () {\r\n videojs.dom.addClass(this.settingsSubMenuEl_, 'vjs-hidden')\r\n this.settingsSubMenuEl_.style.opacity = '0'\r\n this.setMargin()\r\n }\r\n\r\n loadMainMenu () {\r\n const mainMenuEl = this.mainMenu.el() as HTMLElement\r\n this.menuToLoad = 'mainmenu'\r\n this.mainMenu.show()\r\n mainMenuEl.style.opacity = '0'\r\n\r\n // back button will always take you to main menu, so set dialog sizes\r\n const mainMenuAny = this.mainMenu as any\r\n this.settingsButton.setDialogSize([ mainMenuAny.width, mainMenuAny.height ])\r\n\r\n // animation not triggered without timeout (some async stuff ?!?)\r\n setTimeout(() => {\r\n // animate margin and opacity before hiding the submenu\r\n // this triggers CSS Transition event\r\n this.setMargin()\r\n mainMenuEl.style.opacity = '1'\r\n\r\n const firstChild = this.mainMenu.children()[0]\r\n if (firstChild) firstChild.focus()\r\n }, 0)\r\n }\r\n\r\n build () {\r\n this.subMenu.on('labelUpdated', () => {\r\n this.update()\r\n })\r\n this.subMenu.on('menuChanged', () => {\r\n this.bindClickEvents()\r\n this.setSize()\r\n this.update()\r\n })\r\n\r\n this.settingsSubMenuTitleEl_.innerHTML = this.player().localize(this.subMenu.controlText())\r\n this.settingsSubMenuEl_.appendChild(this.subMenu.menu.el())\r\n this.panelChildEl.appendChild(this.settingsSubMenuEl_)\r\n this.update()\r\n\r\n this.createBackButton()\r\n this.setSize()\r\n this.bindClickEvents()\r\n\r\n // prefixed event listeners for CSS TransitionEnd\r\n this.PrefixedEvent(\r\n this.settingsSubMenuEl_,\r\n 'TransitionEnd',\r\n this.transitionEndHandler,\r\n 'addEvent'\r\n )\r\n }\r\n\r\n update (event?: any) {\r\n let target: HTMLElement = null\r\n const subMenu = this.subMenu.name()\r\n\r\n if (event && event.type === 'tap') {\r\n target = event.target\r\n } else if (event) {\r\n target = event.currentTarget\r\n }\r\n\r\n // Playback rate menu button doesn't get a vjs-selected class\r\n // or sets options_['selected'] on the selected playback rate.\r\n // Thus we get the submenu value based on the labelEl of playbackRateMenuButton\r\n if (subMenu === 'PlaybackRateMenuButton') {\r\n const html = (this.subMenu as any).labelEl_.innerHTML\r\n\r\n setTimeout(() => {\r\n this.settingsSubMenuValueEl_.innerHTML = html\r\n }, 250)\r\n } else {\r\n // Loop through the submenu items to find the selected child\r\n for (const subMenuItem of this.subMenu.menu.children_) {\r\n if (!(subMenuItem instanceof component)) {\r\n continue\r\n }\r\n\r\n if (subMenuItem.hasClass('vjs-selected')) {\r\n const subMenuItemUntyped = subMenuItem as any\r\n\r\n // Prefer to use the function\r\n if (typeof subMenuItemUntyped.getLabel === 'function') {\r\n this.settingsSubMenuValueEl_.innerHTML = subMenuItemUntyped.getLabel()\r\n break\r\n }\r\n\r\n this.settingsSubMenuValueEl_.innerHTML = this.player().localize(subMenuItemUntyped.options_.label)\r\n }\r\n }\r\n }\r\n\r\n if (target && !target.classList.contains('vjs-back-button')) {\r\n this.settingsButton.hideDialog()\r\n }\r\n }\r\n\r\n bindClickEvents () {\r\n for (const item of this.subMenu.menu.children()) {\r\n if (!(item instanceof component)) {\r\n continue\r\n }\r\n item.on([ 'tap', 'click' ], this.submenuClickHandler)\r\n }\r\n }\r\n\r\n // save size of submenus on first init\r\n // if number of submenu items change dynamically more logic will be needed\r\n setSize () {\r\n this.dialog.removeClass('vjs-hidden')\r\n videojs.dom.removeClass(this.settingsSubMenuEl_, 'vjs-hidden')\r\n this.size = this.settingsButton.getComponentSize(this.settingsSubMenuEl_)\r\n this.setMargin()\r\n this.dialog.addClass('vjs-hidden')\r\n videojs.dom.addClass(this.settingsSubMenuEl_, 'vjs-hidden')\r\n }\r\n\r\n setMargin () {\r\n if (!this.size) return\r\n\r\n const [ width ] = this.size\r\n\r\n this.settingsSubMenuEl_.style.marginRight = `-${width}px`\r\n }\r\n\r\n /**\r\n * Hide the sub menu\r\n */\r\n hideSubMenu () {\r\n // after removing settings item this.el_ === null\r\n if (!this.el()) {\r\n return\r\n }\r\n\r\n if (videojs.dom.hasClass(this.el(), 'open')) {\r\n videojs.dom.addClass(this.settingsSubMenuEl_, 'vjs-hidden')\r\n videojs.dom.removeClass(this.el(), 'open')\r\n }\r\n }\r\n\r\n}\r\n\r\n(SettingsMenuItem as any).prototype.contentElType = 'button'\r\nvideojs.registerComponent('SettingsMenuItem', SettingsMenuItem)\r\n\r\nexport { SettingsMenuItem }\r\n","import videojs from 'video.js'\r\nimport { toTitleCase } from '../common'\r\nimport { SettingsDialog } from './settings-dialog'\r\nimport { SettingsMenuItem } from './settings-menu-item'\r\nimport { SettingsPanel } from './settings-panel'\r\nimport { SettingsPanelChild } from './settings-panel-child'\r\n\r\nconst Button = videojs.getComponent('Button')\r\nconst Menu = videojs.getComponent('Menu')\r\nconst Component = videojs.getComponent('Component')\r\n\r\nexport interface SettingsButtonOptions extends videojs.ComponentOptions {\r\n entries: any[]\r\n setup?: {\r\n maxHeightOffset: number\r\n }\r\n}\r\n\r\nclass SettingsButton extends Button {\r\n dialog: SettingsDialog\r\n dialogEl: HTMLElement\r\n menu: videojs.Menu\r\n panel: SettingsPanel\r\n panelChild: SettingsPanelChild\r\n\r\n addSettingsItemHandler: typeof SettingsButton.prototype.onAddSettingsItem\r\n disposeSettingsItemHandler: typeof SettingsButton.prototype.onDisposeSettingsItem\r\n documentClickHandler: typeof SettingsButton.prototype.onDocumentClick\r\n userInactiveHandler: typeof SettingsButton.prototype.onUserInactive\r\n\r\n private settingsButtonOptions: SettingsButtonOptions\r\n\r\n constructor (player: videojs.Player, options?: SettingsButtonOptions) {\r\n super(player, options)\r\n\r\n this.settingsButtonOptions = options\r\n\r\n this.controlText('Settings')\r\n\r\n this.dialog = this.player().addChild('settingsDialog')\r\n this.dialogEl = this.dialog.el() as HTMLElement\r\n this.menu = null\r\n this.panel = this.dialog.addChild('settingsPanel')\r\n this.panelChild = this.panel.addChild('settingsPanelChild')\r\n\r\n this.addClass('vjs-settings')\r\n this.el().setAttribute('aria-label', 'Settings Button')\r\n\r\n // Event handlers\r\n this.addSettingsItemHandler = this.onAddSettingsItem.bind(this)\r\n this.disposeSettingsItemHandler = this.onDisposeSettingsItem.bind(this)\r\n this.documentClickHandler = this.onDocumentClick.bind(this)\r\n this.userInactiveHandler = this.onUserInactive.bind(this)\r\n\r\n this.buildMenu()\r\n this.bindEvents()\r\n\r\n // Prepare the dialog\r\n this.player().one('play', () => this.hideDialog())\r\n }\r\n\r\n onDocumentClick (event: MouseEvent) {\r\n const element = event.target as HTMLElement\r\n\r\n if (element?.classList?.contains('vjs-settings') || element?.parentElement?.classList?.contains('vjs-settings')) {\r\n return\r\n }\r\n\r\n if (!this.dialog.hasClass('vjs-hidden')) {\r\n this.hideDialog()\r\n }\r\n }\r\n\r\n onDisposeSettingsItem (event: any, name: string) {\r\n if (name === undefined) {\r\n const children = this.menu.children()\r\n\r\n while (children.length > 0) {\r\n children[0].dispose()\r\n this.menu.removeChild(children[0])\r\n }\r\n\r\n this.addClass('vjs-hidden')\r\n } else {\r\n const item = this.menu.getChild(name)\r\n\r\n if (item) {\r\n item.dispose()\r\n this.menu.removeChild(item)\r\n }\r\n }\r\n\r\n this.hideDialog()\r\n\r\n if (this.settingsButtonOptions.entries.length === 0) {\r\n this.addClass('vjs-hidden')\r\n }\r\n }\r\n\r\n dispose () {\r\n document.removeEventListener('click', this.documentClickHandler)\r\n\r\n if (this.isInIframe()) {\r\n window.removeEventListener('blur', this.documentClickHandler)\r\n }\r\n }\r\n\r\n onAddSettingsItem (event: any, data: any) {\r\n const [ entry, options ] = data\r\n\r\n this.addMenuItem(entry, options)\r\n this.removeClass('vjs-hidden')\r\n }\r\n\r\n onUserInactive () {\r\n if (!this.dialog.hasClass('vjs-hidden')) {\r\n this.hideDialog()\r\n }\r\n }\r\n\r\n bindEvents () {\r\n document.addEventListener('click', this.documentClickHandler)\r\n if (this.isInIframe()) {\r\n window.addEventListener('blur', this.documentClickHandler)\r\n }\r\n\r\n this.player().on('addsettingsitem', this.addSettingsItemHandler)\r\n this.player().on('disposesettingsitem', this.disposeSettingsItemHandler)\r\n this.player().on('userinactive', this.userInactiveHandler)\r\n }\r\n\r\n buildCSSClass () {\r\n return `vjs-icon-settings ${super.buildCSSClass()}`\r\n }\r\n\r\n handleClick () {\r\n if (this.dialog.hasClass('vjs-hidden')) {\r\n this.showDialog()\r\n } else {\r\n this.hideDialog()\r\n }\r\n }\r\n\r\n showDialog () {\r\n this.player().peertube().onMenuOpened();\r\n\r\n (this.menu.el() as HTMLElement).style.opacity = '1'\r\n\r\n this.dialog.show()\r\n this.el().setAttribute('aria-expanded', 'true')\r\n\r\n this.setDialogSize(this.getComponentSize(this.menu))\r\n\r\n const firstChild = this.menu.children()[0]\r\n if (firstChild) firstChild.focus()\r\n }\r\n\r\n hideDialog () {\r\n this.player_.peertube().onMenuClosed()\r\n\r\n this.dialog.hide()\r\n this.el().setAttribute('aria-expanded', 'false')\r\n\r\n this.setDialogSize(this.getComponentSize(this.menu));\r\n (this.menu.el() as HTMLElement).style.opacity = '1'\r\n this.resetChildren()\r\n }\r\n\r\n getComponentSize (element: videojs.Component | HTMLElement) {\r\n let width: number = null\r\n let height: number = null\r\n\r\n // Could be component or just DOM element\r\n if (element instanceof Component) {\r\n const el = element.el() as HTMLElement\r\n\r\n width = el.offsetWidth\r\n height = el.offsetHeight;\r\n\r\n (element as any).width = width;\r\n (element as any).height = height\r\n } else {\r\n width = element.offsetWidth\r\n height = element.offsetHeight\r\n }\r\n\r\n return [ width, height ]\r\n }\r\n\r\n setDialogSize ([ width, height ]: number[]) {\r\n if (typeof height !== 'number') {\r\n return\r\n }\r\n\r\n const offset = this.settingsButtonOptions.setup.maxHeightOffset\r\n const maxHeight = (this.player().el() as HTMLElement).offsetHeight - offset\r\n\r\n const panelEl = this.panel.el() as HTMLElement\r\n\r\n if (height > maxHeight) {\r\n height = maxHeight\r\n width += 17\r\n panelEl.style.maxHeight = `${height}px`\r\n } else if (panelEl.style.maxHeight !== '') {\r\n panelEl.style.maxHeight = ''\r\n }\r\n\r\n this.dialogEl.style.width = `${width}px`\r\n this.dialogEl.style.height = `${height}px`\r\n }\r\n\r\n buildMenu () {\r\n this.menu = new Menu(this.player())\r\n this.menu.addClass('vjs-main-menu')\r\n const entries = this.settingsButtonOptions.entries\r\n\r\n if (entries.length === 0) {\r\n this.addClass('vjs-hidden')\r\n this.panelChild.addChild(this.menu)\r\n return\r\n }\r\n\r\n for (const entry of entries) {\r\n this.addMenuItem(entry, this.settingsButtonOptions)\r\n }\r\n\r\n this.panelChild.addChild(this.menu)\r\n }\r\n\r\n addMenuItem (entry: any, options: any) {\r\n const openSubMenu = function (this: any) {\r\n if (videojs.dom.hasClass(this.el_, 'open')) {\r\n videojs.dom.removeClass(this.el_, 'open')\r\n } else {\r\n videojs.dom.addClass(this.el_, 'open')\r\n }\r\n }\r\n\r\n options.name = toTitleCase(entry)\r\n\r\n const newOptions = Object.assign({}, options, { entry, menuButton: this })\r\n const settingsMenuItem = new SettingsMenuItem(this.player(), newOptions)\r\n\r\n this.menu.addChild(settingsMenuItem)\r\n\r\n // Hide children to avoid sub menus stacking on top of each other\r\n // or having multiple menus open\r\n settingsMenuItem.on('click', videojs.bind(this, this.hideChildren))\r\n\r\n // Whether to add or remove selected class on the settings sub menu element\r\n settingsMenuItem.on('click', openSubMenu)\r\n }\r\n\r\n resetChildren () {\r\n for (const menuChild of this.menu.children()) {\r\n (menuChild as SettingsMenuItem).reset()\r\n }\r\n }\r\n\r\n /**\r\n * Hide all the sub menus\r\n */\r\n hideChildren () {\r\n for (const menuChild of this.menu.children()) {\r\n (menuChild as SettingsMenuItem).hideSubMenu()\r\n }\r\n }\r\n\r\n isInIframe () {\r\n return window.self !== window.top\r\n }\r\n\r\n}\r\n\r\nComponent.registerComponent('SettingsButton', SettingsButton)\r\n\r\nexport { SettingsButton }\r\n","import videojs from 'video.js'\r\n\r\nconst Component = videojs.getComponent('Component')\r\n\r\nclass SettingsPanel extends Component {\r\n\r\n createEl () {\r\n return super.createEl('div', {\r\n className: 'vjs-settings-panel',\r\n innerHTML: '',\r\n tabIndex: -1\r\n })\r\n }\r\n}\r\n\r\nComponent.registerComponent('SettingsPanel', SettingsPanel)\r\n\r\nexport { SettingsPanel }\r\n","import videojs from 'video.js'\r\n\r\nconst Component = videojs.getComponent('Component')\r\n\r\nclass SettingsPanelChild extends Component {\r\n\r\n createEl () {\r\n return super.createEl('div', {\r\n className: 'vjs-settings-panel-child',\r\n innerHTML: '',\r\n tabIndex: -1\r\n })\r\n }\r\n}\r\n\r\nComponent.registerComponent('SettingsPanelChild', SettingsPanelChild)\r\n\r\nexport { SettingsPanelChild }\r\n","import videojs from 'video.js'\r\nimport { PlaylistPluginOptions } from '../../types'\r\nimport { PlaylistMenu } from './playlist-menu'\r\n\r\nconst ClickableComponent = videojs.getComponent('ClickableComponent')\r\n\r\nclass PlaylistButton extends ClickableComponent {\r\n private playlistInfoElement: HTMLElement\r\n private wrapper: HTMLElement\r\n\r\n constructor (player: videojs.Player, options?: PlaylistPluginOptions & { playlistMenu: PlaylistMenu }) {\r\n super(player, options as any)\r\n }\r\n\r\n createEl () {\r\n this.wrapper = super.createEl('div', {\r\n className: 'vjs-playlist-button',\r\n innerHTML: '',\r\n tabIndex: -1\r\n }) as HTMLElement\r\n\r\n const icon = super.createEl('div', {\r\n className: 'vjs-playlist-icon',\r\n innerHTML: '',\r\n tabIndex: -1\r\n })\r\n\r\n this.playlistInfoElement = super.createEl('div', {\r\n className: 'vjs-playlist-info',\r\n innerHTML: '',\r\n tabIndex: -1\r\n }) as HTMLElement\r\n\r\n this.wrapper.appendChild(icon)\r\n this.wrapper.appendChild(this.playlistInfoElement)\r\n\r\n this.update()\r\n\r\n return this.wrapper\r\n }\r\n\r\n update () {\r\n const options = this.options_ as PlaylistPluginOptions\r\n\r\n this.playlistInfoElement.innerHTML = options.getCurrentPosition() + '/' + options.playlist.videosLength\r\n this.wrapper.title = this.player().localize('Playlist: {1}', [ options.playlist.displayName ])\r\n }\r\n\r\n handleClick () {\r\n const playlistMenu = this.getPlaylistMenu()\r\n playlistMenu.open()\r\n }\r\n\r\n private getPlaylistMenu () {\r\n return (this.options_ as any).playlistMenu as PlaylistMenu\r\n }\r\n}\r\n\r\nvideojs.registerComponent('PlaylistButton', PlaylistButton)\r\n\r\nexport { PlaylistButton }\r\n","import videojs from 'video.js'\r\nimport { secondsToTime } from '@shared/core-utils'\r\nimport { VideoPlaylistElement } from '@shared/models'\r\nimport { PlaylistItemOptions } from '../../types'\r\n\r\nconst Component = videojs.getComponent('Component')\r\n\r\nclass PlaylistMenuItem extends Component {\r\n private element: VideoPlaylistElement\r\n\r\n constructor (player: videojs.Player, options?: PlaylistItemOptions) {\r\n super(player, options as any)\r\n\r\n this.emitTapEvents()\r\n\r\n this.element = options.element\r\n\r\n this.on([ 'click', 'tap' ], () => this.switchPlaylistItem())\r\n this.on('keydown', event => this.handleKeyDown(event))\r\n }\r\n\r\n createEl () {\r\n const options = this.options_ as PlaylistItemOptions\r\n\r\n const li = super.createEl('li', {\r\n className: 'vjs-playlist-menu-item',\r\n innerHTML: ''\r\n }) as HTMLElement\r\n\r\n if (!options.element.video) {\r\n li.classList.add('vjs-disabled')\r\n }\r\n\r\n const positionBlock = super.createEl('div', {\r\n className: 'item-position-block'\r\n }) as HTMLElement\r\n\r\n const position = super.createEl('div', {\r\n className: 'item-position',\r\n innerHTML: options.element.position\r\n })\r\n\r\n positionBlock.appendChild(position)\r\n li.appendChild(positionBlock)\r\n\r\n if (options.element.video) {\r\n this.buildAvailableVideo(li, positionBlock, options)\r\n } else {\r\n this.buildUnavailableVideo(li)\r\n }\r\n\r\n return li\r\n }\r\n\r\n setSelected (selected: boolean) {\r\n if (selected) this.addClass('vjs-selected')\r\n else this.removeClass('vjs-selected')\r\n }\r\n\r\n getElement () {\r\n return this.element\r\n }\r\n\r\n private buildAvailableVideo (li: HTMLElement, positionBlock: HTMLElement, options: PlaylistItemOptions) {\r\n const videoElement = options.element\r\n\r\n const player = super.createEl('div', {\r\n className: 'item-player'\r\n })\r\n\r\n positionBlock.appendChild(player)\r\n\r\n const thumbnail = super.createEl('img', {\r\n src: window.location.origin + videoElement.video.thumbnailPath\r\n })\r\n\r\n const infoBlock = super.createEl('div', {\r\n className: 'info-block'\r\n })\r\n\r\n const title = super.createEl('div', {\r\n innerHTML: videoElement.video.name,\r\n className: 'title'\r\n })\r\n\r\n const channel = super.createEl('div', {\r\n innerHTML: videoElement.video.channel.displayName,\r\n className: 'channel'\r\n })\r\n\r\n infoBlock.appendChild(title)\r\n infoBlock.appendChild(channel)\r\n\r\n if (videoElement.startTimestamp || videoElement.stopTimestamp) {\r\n let html = ''\r\n\r\n if (videoElement.startTimestamp) html += secondsToTime(videoElement.startTimestamp)\r\n if (videoElement.stopTimestamp) html += ' - ' + secondsToTime(videoElement.stopTimestamp)\r\n\r\n const timestamps = super.createEl('div', {\r\n innerHTML: html,\r\n className: 'timestamps'\r\n })\r\n\r\n infoBlock.append(timestamps)\r\n }\r\n\r\n li.append(thumbnail)\r\n li.append(infoBlock)\r\n }\r\n\r\n private buildUnavailableVideo (li: HTMLElement) {\r\n const block = super.createEl('div', {\r\n className: 'item-unavailable',\r\n innerHTML: this.player().localize('Unavailable video')\r\n })\r\n\r\n li.appendChild(block)\r\n }\r\n\r\n private handleKeyDown (event: KeyboardEvent) {\r\n if (event.code === 'Space' || event.code === 'Enter') {\r\n this.switchPlaylistItem()\r\n }\r\n }\r\n\r\n private switchPlaylistItem () {\r\n const options = this.options_ as PlaylistItemOptions\r\n\r\n options.onClicked()\r\n }\r\n}\r\n\r\nComponent.registerComponent('PlaylistMenuItem', PlaylistMenuItem)\r\n\r\nexport { PlaylistMenuItem }\r\n","import videojs from 'video.js'\r\nimport { VideoPlaylistElement } from '@shared/models'\r\nimport { PlaylistPluginOptions } from '../../types'\r\nimport { PlaylistMenuItem } from './playlist-menu-item'\r\n\r\nconst Component = videojs.getComponent('Component')\r\n\r\nclass PlaylistMenu extends Component {\r\n private menuItems: PlaylistMenuItem[]\r\n\r\n constructor (player: videojs.Player, options?: PlaylistPluginOptions) {\r\n super(player, options as any)\r\n\r\n const self = this\r\n\r\n function userInactiveHandler () {\r\n self.close()\r\n }\r\n\r\n this.el().addEventListener('mouseenter', () => {\r\n this.player().off('userinactive', userInactiveHandler)\r\n })\r\n\r\n this.el().addEventListener('mouseleave', () => {\r\n this.player().one('userinactive', userInactiveHandler)\r\n })\r\n\r\n this.player().on('click', event => {\r\n let current = event.target as HTMLElement\r\n\r\n do {\r\n if (\r\n current.classList.contains('vjs-playlist-menu') ||\r\n current.classList.contains('vjs-playlist-button')\r\n ) {\r\n return\r\n }\r\n\r\n current = current.parentElement\r\n } while (current)\r\n\r\n this.close()\r\n })\r\n }\r\n\r\n createEl () {\r\n this.menuItems = []\r\n\r\n const options = this.getOptions()\r\n\r\n const menu = super.createEl('div', {\r\n className: 'vjs-playlist-menu',\r\n innerHTML: '',\r\n tabIndex: -1\r\n })\r\n\r\n const header = super.createEl('div', {\r\n className: 'header'\r\n })\r\n\r\n const headerLeft = super.createEl('div')\r\n\r\n const leftTitle = super.createEl('div', {\r\n innerHTML: options.playlist.displayName,\r\n className: 'title'\r\n })\r\n\r\n const playlistChannel = options.playlist.videoChannel\r\n const leftSubtitle = super.createEl('div', {\r\n innerHTML: playlistChannel\r\n ? this.player().localize('By {1}', [ playlistChannel.displayName ])\r\n : '',\r\n className: 'channel'\r\n })\r\n\r\n headerLeft.appendChild(leftTitle)\r\n headerLeft.appendChild(leftSubtitle)\r\n\r\n const tick = super.createEl('div', {\r\n className: 'cross'\r\n })\r\n tick.addEventListener('click', () => this.close())\r\n\r\n header.appendChild(headerLeft)\r\n header.appendChild(tick)\r\n\r\n const list = super.createEl('ol')\r\n\r\n for (const playlistElement of options.elements) {\r\n const item = new PlaylistMenuItem(this.player(), {\r\n element: playlistElement,\r\n onClicked: () => this.onItemClicked(playlistElement)\r\n })\r\n\r\n list.appendChild(item.el())\r\n\r\n this.menuItems.push(item)\r\n }\r\n\r\n menu.appendChild(header)\r\n menu.appendChild(list)\r\n\r\n return menu\r\n }\r\n\r\n update () {\r\n const options = this.getOptions()\r\n\r\n this.updateSelected(options.getCurrentPosition())\r\n }\r\n\r\n open () {\r\n this.player().addClass('playlist-menu-displayed')\r\n }\r\n\r\n close () {\r\n this.player().removeClass('playlist-menu-displayed')\r\n }\r\n\r\n updateSelected (newPosition: number) {\r\n for (const item of this.menuItems) {\r\n item.setSelected(item.getElement().position === newPosition)\r\n }\r\n }\r\n\r\n private getOptions () {\r\n return this.options_ as PlaylistPluginOptions\r\n }\r\n\r\n private onItemClicked (element: VideoPlaylistElement) {\r\n this.getOptions().onItemClicked(element)\r\n }\r\n}\r\n\r\nComponent.registerComponent('PlaylistMenu', PlaylistMenu)\r\n\r\nexport { PlaylistMenu }\r\n","import videojs from 'video.js'\r\nimport { PlaylistPluginOptions } from '../../types'\r\nimport { PlaylistButton } from './playlist-button'\r\nimport { PlaylistMenu } from './playlist-menu'\r\n\r\nconst Plugin = videojs.getPlugin('plugin')\r\n\r\nclass PlaylistPlugin extends Plugin {\r\n private playlistMenu: PlaylistMenu\r\n private playlistButton: PlaylistButton\r\n private options: PlaylistPluginOptions\r\n\r\n constructor (player: videojs.Player, options?: PlaylistPluginOptions) {\r\n super(player, options)\r\n\r\n this.options = options\r\n\r\n this.player.ready(() => {\r\n player.addClass('vjs-playlist')\r\n })\r\n\r\n this.playlistMenu = new PlaylistMenu(player, options)\r\n this.playlistButton = new PlaylistButton(player, { ...options, playlistMenu: this.playlistMenu })\r\n\r\n player.addChild(this.playlistMenu, options)\r\n player.addChild(this.playlistButton, options)\r\n }\r\n\r\n updateSelected () {\r\n this.playlistMenu.updateSelected(this.options.getCurrentPosition())\r\n }\r\n}\r\n\r\nvideojs.registerPlugin('playlist', PlaylistPlugin)\r\nexport { PlaylistPlugin }\r\n","import debug from 'debug'\r\nimport videojs from 'video.js'\r\nimport { logger } from '@root-helpers/logger'\r\nimport { PeerTubeMobileButtons } from './peertube-mobile-buttons'\r\n\r\nconst debugLogger = debug('peertube:player:mobile')\r\n\r\nconst Plugin = videojs.getPlugin('plugin')\r\n\r\nclass PeerTubeMobilePlugin extends Plugin {\r\n private static readonly DOUBLE_TAP_DELAY_MS = 250\r\n private static readonly SET_CURRENT_TIME_DELAY = 1000\r\n\r\n private peerTubeMobileButtons: PeerTubeMobileButtons\r\n\r\n private seekAmount = 0\r\n\r\n private lastTapEvent: TouchEvent\r\n private tapTimeout: ReturnType\r\n private newActiveState: boolean\r\n\r\n private setCurrentTimeTimeout: ReturnType\r\n\r\n constructor (player: videojs.Player, options: videojs.PlayerOptions) {\r\n super(player, options)\r\n\r\n this.peerTubeMobileButtons = player.addChild('PeerTubeMobileButtons', { reportTouchActivity: false }) as PeerTubeMobileButtons\r\n\r\n if (videojs.browser.IS_ANDROID && screen.orientation) {\r\n this.handleFullscreenRotation()\r\n }\r\n\r\n if (!this.player.options_.userActions) this.player.options_.userActions = {};\r\n\r\n // FIXME: typings\r\n (this.player.options_.userActions as any).click = false\r\n this.player.options_.userActions.doubleClick = false\r\n\r\n this.player.one('play', () => {\r\n this.initTouchStartEvents()\r\n })\r\n }\r\n\r\n private handleFullscreenRotation () {\r\n this.player.on('fullscreenchange', () => {\r\n if (!this.player.isFullscreen() || this.isPortraitVideo()) return\r\n\r\n screen.orientation.lock('landscape')\r\n .catch(err => logger.error('Cannot lock screen to landscape.', err))\r\n })\r\n }\r\n\r\n private isPortraitVideo () {\r\n return this.player.videoWidth() < this.player.videoHeight()\r\n }\r\n\r\n private initTouchStartEvents () {\r\n const handleTouchStart = (event: TouchEvent) => {\r\n if (this.tapTimeout) {\r\n clearTimeout(this.tapTimeout)\r\n this.tapTimeout = undefined\r\n }\r\n\r\n if (this.lastTapEvent && event.timeStamp - this.lastTapEvent.timeStamp < PeerTubeMobilePlugin.DOUBLE_TAP_DELAY_MS) {\r\n debugLogger('Detected double tap')\r\n\r\n this.lastTapEvent = undefined\r\n this.onDoubleTap(event)\r\n return\r\n }\r\n\r\n this.newActiveState = !this.player.userActive()\r\n\r\n this.tapTimeout = setTimeout(() => {\r\n debugLogger('No double tap detected, set user active state to %s.', this.newActiveState)\r\n\r\n this.player.userActive(this.newActiveState)\r\n }, PeerTubeMobilePlugin.DOUBLE_TAP_DELAY_MS)\r\n\r\n this.lastTapEvent = event\r\n }\r\n\r\n this.player.on('touchstart', (event: TouchEvent) => {\r\n // Only enable user active on player touch, we listen event on peertube mobile buttons to disable it\r\n if (this.player.userActive()) return\r\n\r\n handleTouchStart(event)\r\n })\r\n\r\n this.peerTubeMobileButtons.el().addEventListener('touchstart', (event: TouchEvent) => {\r\n // Prevent mousemove/click events firing on the player, that conflict with our user active logic\r\n event.preventDefault()\r\n\r\n handleTouchStart(event)\r\n }, { passive: false })\r\n }\r\n\r\n private onDoubleTap (event: TouchEvent) {\r\n const playerWidth = this.player.currentWidth()\r\n\r\n const rect = this.findPlayerTarget((event.target as HTMLElement)).getBoundingClientRect()\r\n const offsetX = event.targetTouches[0].pageX - rect.left\r\n\r\n debugLogger('Calculating double tap zone (player width: %d, offset X: %d)', playerWidth, offsetX)\r\n\r\n if (offsetX > 0.66 * playerWidth) {\r\n if (this.seekAmount < 0) this.seekAmount = 0\r\n\r\n this.seekAmount += 10\r\n\r\n debugLogger('Will forward %d seconds', this.seekAmount)\r\n } else if (offsetX < 0.33 * playerWidth) {\r\n if (this.seekAmount > 0) this.seekAmount = 0\r\n\r\n this.seekAmount -= 10\r\n debugLogger('Will rewind %d seconds', this.seekAmount)\r\n }\r\n\r\n this.peerTubeMobileButtons.displayFastSeek(this.seekAmount)\r\n\r\n this.scheduleSetCurrentTime()\r\n }\r\n\r\n private findPlayerTarget (target: HTMLElement): HTMLElement {\r\n if (target.classList.contains('video-js')) return target\r\n\r\n return this.findPlayerTarget(target.parentElement)\r\n }\r\n\r\n private scheduleSetCurrentTime () {\r\n this.player.pause()\r\n this.player.addClass('vjs-fast-seeking')\r\n\r\n if (this.setCurrentTimeTimeout) clearTimeout(this.setCurrentTimeTimeout)\r\n\r\n this.setCurrentTimeTimeout = setTimeout(() => {\r\n let newTime = this.player.currentTime() + this.seekAmount\r\n this.seekAmount = 0\r\n\r\n newTime = Math.max(0, newTime)\r\n newTime = Math.min(this.player.duration(), newTime)\r\n\r\n this.player.currentTime(newTime)\r\n this.seekAmount = 0\r\n this.peerTubeMobileButtons.displayFastSeek(0)\r\n\r\n this.player.removeClass('vjs-fast-seeking')\r\n this.player.userActive(false)\r\n\r\n this.player.play()\r\n }, PeerTubeMobilePlugin.SET_CURRENT_TIME_DELAY)\r\n }\r\n}\r\n\r\nvideojs.registerPlugin('peertubeMobile', PeerTubeMobilePlugin)\r\nexport { PeerTubeMobilePlugin }\r\n","import videojs from 'video.js'\r\n\r\nconst Component = videojs.getComponent('Component')\r\nclass PeerTubeMobileButtons extends Component {\r\n\r\n private rewind: Element\r\n private forward: Element\r\n private rewindText: Element\r\n private forwardText: Element\r\n\r\n createEl () {\r\n const container = super.createEl('div', {\r\n className: 'vjs-mobile-buttons-overlay'\r\n }) as HTMLDivElement\r\n\r\n const mainButton = super.createEl('div', {\r\n className: 'main-button'\r\n }) as HTMLDivElement\r\n\r\n mainButton.addEventListener('touchstart', e => {\r\n e.stopPropagation()\r\n\r\n if (this.player_.paused() || this.player_.ended()) {\r\n this.player_.play()\r\n return\r\n }\r\n\r\n this.player_.pause()\r\n })\r\n\r\n this.rewind = super.createEl('div', { className: 'rewind-button vjs-hidden' })\r\n this.forward = super.createEl('div', { className: 'forward-button vjs-hidden' })\r\n\r\n for (let i = 0; i < 3; i++) {\r\n this.rewind.appendChild(super.createEl('span', { className: 'icon' }))\r\n this.forward.appendChild(super.createEl('span', { className: 'icon' }))\r\n }\r\n\r\n this.rewindText = this.rewind.appendChild(super.createEl('div', { className: 'text' }))\r\n this.forwardText = this.forward.appendChild(super.createEl('div', { className: 'text' }))\r\n\r\n container.appendChild(this.rewind)\r\n container.appendChild(mainButton)\r\n container.appendChild(this.forward)\r\n\r\n return container\r\n }\r\n\r\n displayFastSeek (amount: number) {\r\n if (amount === 0) {\r\n this.hideRewind()\r\n this.hideForward()\r\n return\r\n }\r\n\r\n if (amount > 0) {\r\n this.hideRewind()\r\n this.displayForward(amount)\r\n return\r\n }\r\n\r\n if (amount < 0) {\r\n this.hideForward()\r\n this.displayRewind(amount)\r\n return\r\n }\r\n }\r\n\r\n private hideRewind () {\r\n this.rewind.classList.add('vjs-hidden')\r\n this.rewindText.textContent = ''\r\n }\r\n\r\n private displayRewind (amount: number) {\r\n this.rewind.classList.remove('vjs-hidden')\r\n this.rewindText.textContent = this.player().localize('{1} seconds', [ amount + '' ])\r\n }\r\n\r\n private hideForward () {\r\n this.forward.classList.add('vjs-hidden')\r\n this.forwardText.textContent = ''\r\n }\r\n\r\n private displayForward (amount: number) {\r\n this.forward.classList.remove('vjs-hidden')\r\n this.forwardText.textContent = this.player().localize('{1} seconds', [ amount + '' ])\r\n }\r\n}\r\n\r\nvideojs.registerComponent('PeerTubeMobileButtons', PeerTubeMobileButtons)\r\n\r\nexport {\r\n PeerTubeMobileButtons\r\n}\r\n","import videojs from 'video.js'\r\n\r\ntype KeyHandler = { accept: (event: KeyboardEvent) => boolean, cb: (e: KeyboardEvent) => void }\r\n\r\nconst Plugin = videojs.getPlugin('plugin')\r\n\r\nclass PeerTubeHotkeysPlugin extends Plugin {\r\n private static readonly VOLUME_STEP = 0.1\r\n private static readonly SEEK_STEP = 5\r\n\r\n private readonly handleKeyFunction: (event: KeyboardEvent) => void\r\n\r\n private readonly handlers: KeyHandler[]\r\n\r\n constructor (player: videojs.Player, options: videojs.PlayerOptions) {\r\n super(player, options)\r\n\r\n this.handlers = this.buildHandlers()\r\n\r\n this.handleKeyFunction = (event: KeyboardEvent) => this.onKeyDown(event)\r\n document.addEventListener('keydown', this.handleKeyFunction)\r\n }\r\n\r\n dispose () {\r\n document.removeEventListener('keydown', this.handleKeyFunction)\r\n }\r\n\r\n private onKeyDown (event: KeyboardEvent) {\r\n if (!this.isValidKeyTarget(event.target as HTMLElement)) return\r\n\r\n for (const handler of this.handlers) {\r\n if (handler.accept(event)) {\r\n handler.cb(event)\r\n return\r\n }\r\n }\r\n }\r\n\r\n private buildHandlers () {\r\n const handlers: KeyHandler[] = [\r\n // Play\r\n {\r\n accept: e => (e.key === ' ' || e.key === 'MediaPlayPause'),\r\n cb: e => {\r\n e.preventDefault()\r\n e.stopPropagation()\r\n\r\n if (this.player.paused()) this.player.play()\r\n else this.player.pause()\r\n }\r\n },\r\n\r\n // Increase volume\r\n {\r\n accept: e => this.isNaked(e, 'ArrowUp'),\r\n cb: e => {\r\n e.preventDefault()\r\n this.player.volume(this.player.volume() + PeerTubeHotkeysPlugin.VOLUME_STEP)\r\n }\r\n },\r\n\r\n // Decrease volume\r\n {\r\n accept: e => this.isNaked(e, 'ArrowDown'),\r\n cb: e => {\r\n e.preventDefault()\r\n this.player.volume(this.player.volume() - PeerTubeHotkeysPlugin.VOLUME_STEP)\r\n }\r\n },\r\n\r\n // Rewind\r\n {\r\n accept: e => this.isNaked(e, 'ArrowLeft') || this.isNaked(e, 'MediaRewind'),\r\n cb: e => {\r\n e.preventDefault()\r\n\r\n const target = Math.max(0, this.player.currentTime() - PeerTubeHotkeysPlugin.SEEK_STEP)\r\n this.player.currentTime(target)\r\n }\r\n },\r\n\r\n // Forward\r\n {\r\n accept: e => this.isNaked(e, 'ArrowRight') || this.isNaked(e, 'MediaForward'),\r\n cb: e => {\r\n e.preventDefault()\r\n\r\n const target = Math.min(this.player.duration(), this.player.currentTime() + PeerTubeHotkeysPlugin.SEEK_STEP)\r\n this.player.currentTime(target)\r\n }\r\n },\r\n\r\n // Fullscreen\r\n {\r\n // f key or Ctrl + Enter\r\n accept: e => this.isNaked(e, 'f') || (!e.altKey && e.ctrlKey && e.key === 'Enter'),\r\n cb: e => {\r\n e.preventDefault()\r\n\r\n if (this.player.isFullscreen()) this.player.exitFullscreen()\r\n else this.player.requestFullscreen()\r\n }\r\n },\r\n\r\n // Mute\r\n {\r\n accept: e => this.isNaked(e, 'm'),\r\n cb: e => {\r\n e.preventDefault()\r\n\r\n this.player.muted(!this.player.muted())\r\n }\r\n },\r\n\r\n // Increase playback rate\r\n {\r\n accept: e => e.key === '>',\r\n cb: () => {\r\n const target = Math.min(this.player.playbackRate() + 0.1, 5)\r\n\r\n this.player.playbackRate(parseFloat(target.toFixed(2)))\r\n }\r\n },\r\n\r\n // Decrease playback rate\r\n {\r\n accept: e => e.key === '<',\r\n cb: () => {\r\n const target = Math.max(this.player.playbackRate() - 0.1, 0.10)\r\n\r\n this.player.playbackRate(parseFloat(target.toFixed(2)))\r\n }\r\n },\r\n\r\n // Previous frame\r\n {\r\n accept: e => e.key === ',',\r\n cb: () => {\r\n this.player.pause()\r\n\r\n // Calculate movement distance (assuming 30 fps)\r\n const dist = 1 / 30\r\n this.player.currentTime(this.player.currentTime() - dist)\r\n }\r\n },\r\n\r\n // Next frame\r\n {\r\n accept: e => e.key === '.',\r\n cb: () => {\r\n this.player.pause()\r\n\r\n // Calculate movement distance (assuming 30 fps)\r\n const dist = 1 / 30\r\n this.player.currentTime(this.player.currentTime() + dist)\r\n }\r\n }\r\n ]\r\n\r\n // 0-9 key handlers\r\n for (let i = 0; i < 10; i++) {\r\n handlers.push({\r\n accept: e => e.key === i + '' && !e.ctrlKey, // If using ctrl key, it's a web browser hotkey\r\n cb: e => {\r\n e.preventDefault()\r\n\r\n this.player.currentTime(this.player.duration() * i * 0.1)\r\n }\r\n })\r\n }\r\n\r\n return handlers\r\n }\r\n\r\n private isValidKeyTarget (eventEl: HTMLElement) {\r\n const playerEl = this.player.el()\r\n const activeEl = document.activeElement\r\n const currentElTagName = eventEl.tagName.toLowerCase()\r\n\r\n return (\r\n activeEl === playerEl ||\r\n activeEl === playerEl.querySelector('.vjs-tech') ||\r\n activeEl === playerEl.querySelector('.vjs-control-bar') ||\r\n eventEl.id === 'content' ||\r\n currentElTagName === 'body' ||\r\n currentElTagName === 'video'\r\n )\r\n }\r\n\r\n private isNaked (event: KeyboardEvent, key: string) {\r\n return (!event.ctrlKey && !event.altKey && !event.metaKey && !event.shiftKey && event.key === key)\r\n }\r\n}\r\n\r\nvideojs.registerPlugin('peerTubeHotkeysPlugin', PeerTubeHotkeysPlugin)\r\nexport { PeerTubeHotkeysPlugin }\r\n","function copyToClipboard (text: string) {\r\n const el = document.createElement('textarea')\r\n el.value = text\r\n el.setAttribute('readonly', '')\r\n el.style.position = 'absolute'\r\n el.style.left = '-9999px'\r\n document.body.appendChild(el)\r\n el.select()\r\n document.execCommand('copy')\r\n document.body.removeChild(el)\r\n}\r\n\r\nfunction wait (ms: number) {\r\n return new Promise(res => {\r\n setTimeout(() => res(), ms)\r\n })\r\n}\r\n\r\nexport {\r\n copyToClipboard,\r\n wait\r\n}\r\n","import {\r\n CommonOptions,\r\n NextPreviousVideoButtonOptions,\r\n PeerTubeLinkButtonOptions,\r\n PeertubePlayerManagerOptions,\r\n PlayerMode\r\n} from '../../types'\r\n\r\nexport class ControlBarOptionsBuilder {\r\n private options: CommonOptions\r\n\r\n constructor (\r\n globalOptions: PeertubePlayerManagerOptions,\r\n private mode: PlayerMode\r\n ) {\r\n this.options = globalOptions.common\r\n }\r\n\r\n getChildrenOptions () {\r\n const children = {}\r\n\r\n if (this.options.previousVideo) {\r\n Object.assign(children, this.getPreviousVideo())\r\n }\r\n\r\n Object.assign(children, { playToggle: {} })\r\n\r\n if (this.options.nextVideo) {\r\n Object.assign(children, this.getNextVideo())\r\n }\r\n\r\n Object.assign(children, {\r\n currentTimeDisplay: {},\r\n timeDivider: {},\r\n durationDisplay: {},\r\n liveDisplay: {},\r\n\r\n flexibleWidthSpacer: {},\r\n\r\n ...this.getProgressControl(),\r\n\r\n p2PInfoButton: {\r\n p2pEnabled: this.options.p2pEnabled\r\n },\r\n\r\n muteToggle: {},\r\n volumeControl: {},\r\n\r\n ...this.getSettingsButton()\r\n })\r\n\r\n /*if (this.options.peertubeLink === true) {\r\n Object.assign(children, {\r\n peerTubeLinkButton: {\r\n shortUUID: this.options.videoShortUUID,\r\n //instanceName: this.options.instanceName\r\n } as PeerTubeLinkButtonOptions\r\n })\r\n }*/\r\n\r\n Object.assign(children, {\r\n PictureInPictureBastyon: {}\r\n })\r\n\r\n if (this.options.theaterButton === true) {\r\n Object.assign(children, {\r\n theaterButton: {}\r\n })\r\n }\r\n\r\n Object.assign(children, {\r\n fullscreenToggle: {}\r\n })\r\n\r\n return children\r\n }\r\n\r\n private getSettingsButton () {\r\n const settingEntries: string[] = []\r\n\r\n settingEntries.push('playbackRateMenuButton')\r\n\r\n //if (this.options.captions === true) settingEntries.push('captionsButton')\r\n\r\n settingEntries.push('resolutionMenuButton')\r\n\r\n return {\r\n settingsButton: {\r\n setup: {\r\n maxHeightOffset: 40\r\n },\r\n entries: settingEntries\r\n }\r\n }\r\n }\r\n\r\n private getProgressControl () {\r\n const loadProgressBar = 'loadProgressBar'\r\n\r\n return {\r\n progressControl: {\r\n children: {\r\n seekBar: {\r\n children: {\r\n [loadProgressBar]: {},\r\n mouseTimeDisplay: {},\r\n playProgressBar: {}\r\n }\r\n }\r\n }\r\n }\r\n }\r\n }\r\n\r\n private getPreviousVideo () {\r\n const buttonOptions: NextPreviousVideoButtonOptions = {\r\n type: 'previous',\r\n handler: this.options.previousVideo,\r\n isDisabled: () => {\r\n if (!this.options.hasPreviousVideo) return false\r\n\r\n return !this.options.hasPreviousVideo()\r\n }\r\n }\r\n\r\n return { previousVideoButton: buttonOptions }\r\n }\r\n\r\n private getNextVideo () {\r\n const buttonOptions: NextPreviousVideoButtonOptions = {\r\n type: 'next',\r\n handler: this.options.nextVideo,\r\n isDisabled: () => {\r\n if (!this.options.hasNextVideo) return false\r\n\r\n return !this.options.hasNextVideo()\r\n }\r\n }\r\n\r\n return { nextVideoButton: buttonOptions }\r\n }\r\n}\r\n","import { basename, dirname } from 'path'\r\nimport { logger } from '@root-helpers/logger'\r\n\r\nclass RedundancyUrlManager {\r\n\r\n constructor (private baseUrls: string[] = []) {\r\n // empty\r\n }\r\n\r\n removeBySegmentUrl (segmentUrl: string) {\r\n logger.info(`Removing redundancy of segment URL ${segmentUrl}.`)\r\n\r\n const baseUrl = dirname(segmentUrl)\r\n\r\n this.baseUrls = this.baseUrls.filter(u => u !== baseUrl && u !== baseUrl + '/')\r\n }\r\n\r\n buildUrl (url: string) {\r\n const max = this.baseUrls.length + 1\r\n const i = this.getRandomInt(max)\r\n\r\n if (i === max - 1) return url\r\n\r\n const newBaseUrl = this.baseUrls[i]\r\n const slashPart = newBaseUrl.endsWith('/') ? '' : '/'\r\n\r\n return newBaseUrl + slashPart + basename(url)\r\n }\r\n\r\n countBaseUrls () {\r\n return this.baseUrls.length\r\n }\r\n\r\n private getRandomInt (max: number) {\r\n return Math.floor(Math.random() * Math.floor(max))\r\n }\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n\r\nexport {\r\n RedundancyUrlManager\r\n}\r\n","import { Segment } from 'p2p-media-loader-core-basyton'\r\nimport { RedundancyUrlManager } from './redundancy-url-manager'\r\n\r\nfunction segmentUrlBuilderFactory (redundancyUrlManager: RedundancyUrlManager) {\r\n return function segmentBuilder (segment: Segment) {\r\n return redundancyUrlManager.buildUrl(segment.url)\r\n }\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n\r\nexport {\r\n segmentUrlBuilderFactory\r\n}\r\n","import { basename } from 'path'\r\nimport { Segment } from 'p2p-media-loader-core-basyton'\r\nimport { logger } from '@root-helpers/logger'\r\nimport { wait } from '@root-helpers/utils'\r\n\r\ntype SegmentsJSON = { [filename: string]: string | { [byterange: string]: string } }\r\n\r\nconst maxRetries = 3\r\n\r\nfunction findbyqualityname(segments : any, name : string){\r\n\r\n var result = undefined\r\n\r\n name = name.substring(36)\r\n\r\n\r\n\r\n for (var key in segments) {\r\n if (segments.hasOwnProperty(key) && key.indexOf(name) > -1) {\r\n result = segments[key]\r\n }\r\n }\r\n\r\n\r\n return result\r\n}\r\n\r\nfunction segmentValidatorFactory (segmentsSha256Url: string, isLive: boolean) {\r\n let segmentsJSON = fetchSha256Segments(segmentsSha256Url)\r\n const regex = /bytes=(\\d+)-(\\d+)/\r\n\r\n return async function segmentValidator (segment: Segment, _method: string, _peerId: string, retry = 1) {\r\n // Wait for hash generation from the server\r\n if (isLive) await wait(1000)\r\n\r\n const filename = basename(segment.url)\r\n\r\n const segments = (await segmentsJSON)\r\n\r\n const segmentValue = segments[filename] || findbyqualityname(segments, filename)\r\n\r\n\r\n\r\n if (!segmentValue && retry > maxRetries) {\r\n throw new Error(`Unknown segment name ${filename} in segment validator`)\r\n }\r\n\r\n if (!segmentValue) {\r\n logger.info(`Refetching sha segments for ${filename}`)\r\n\r\n await wait(1000)\r\n\r\n segmentsJSON = fetchSha256Segments(segmentsSha256Url)\r\n await segmentValidator(segment, _method, _peerId, retry + 1)\r\n\r\n return\r\n }\r\n\r\n let hashShouldBe: string\r\n let range = ''\r\n\r\n if (typeof segmentValue === 'string') {\r\n hashShouldBe = segmentValue\r\n } else {\r\n const captured = regex.exec(segment.range)\r\n range = captured[1] + '-' + captured[2]\r\n\r\n hashShouldBe = segmentValue[range]\r\n }\r\n\r\n if (hashShouldBe === undefined) {\r\n throw new Error(`Unknown segment name ${filename}/${range} in segment validator`)\r\n }\r\n\r\n\r\n console.log('segment.data', segment.url, range, segment.data)\r\n\r\n const calculatedSha = await sha256Hex(segment.data)\r\n if (calculatedSha !== hashShouldBe) {\r\n\r\n \r\n\r\n throw new Error(\r\n `Hashes does not correspond for segment ${filename}/${range}` +\r\n `(expected: ${hashShouldBe} instead of ${calculatedSha})`\r\n )\r\n }\r\n }\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n\r\nexport {\r\n segmentValidatorFactory\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n\r\nfunction fetchSha256Segments (url: string) {\r\n return fetch(url)\r\n .then(res => res.json() as Promise)\r\n .catch(err => {\r\n logger.error('Cannot get sha256 segments', err)\r\n return {}\r\n })\r\n}\r\n\r\nasync function sha256Hex (data?: ArrayBuffer) {\r\n if (!data) return undefined\r\n\r\n if (window.crypto.subtle) {\r\n return window.crypto.subtle.digest('SHA-256', data)\r\n .then(data => bufferToHex(data))\r\n }\r\n\r\n // Fallback for non HTTPS context\r\n const shaModule = (await import('sha.js') as any).default\r\n // eslint-disable-next-line new-cap\r\n return new shaModule.sha256().update(data).digest('hex')\r\n}\r\n\r\n// Thanks: https://stackoverflow.com/a/53307879\r\nfunction bufferToHex (buffer?: ArrayBuffer) {\r\n if (!buffer) return ''\r\n\r\n let s = ''\r\n const h = '0123456789abcdef'\r\n const o = new Uint8Array(buffer)\r\n\r\n o.forEach((v: any) => {\r\n s += h[v >> 4] + h[v & 15]\r\n })\r\n\r\n return s\r\n}\r\n","import { HybridLoaderSettings } from 'p2p-media-loader-core-basyton'\r\nimport { HlsJsEngineSettings } from 'p2p-media-loader-hlsjs-basyton'\r\nimport { logger } from '@root-helpers/logger'\r\nimport { LiveVideoLatencyMode } from '@shared/models'\r\nimport { getAverageBandwidthInStore } from '../../peertube-player-local-storage'\r\nimport { P2PMediaLoader, P2PMediaLoaderPluginOptions } from '../../types'\r\nimport { PeertubePlayerManagerOptions } from '../../types/manager-options'\r\nimport { getRtcConfig } from '../common'\r\nimport { RedundancyUrlManager } from '../p2p-media-loader/redundancy-url-manager'\r\nimport { segmentUrlBuilderFactory } from '../p2p-media-loader/segment-url-builder'\r\nimport { segmentValidatorFactory } from '../p2p-media-loader/segment-validator'\r\n//import CapLevelController from './peertube-cap-level-controller'\r\n\r\nexport class HLSOptionsBuilder {\r\n\r\n constructor (\r\n private options: PeertubePlayerManagerOptions,\r\n private p2pMediaLoaderModule?: any\r\n ) {\r\n\r\n }\r\n\r\n getPluginOptions () {\r\n const commonOptions = this.options.common\r\n\r\n const redundancyUrlManager = new RedundancyUrlManager(this.options.p2pMediaLoader.redundancyBaseUrls)\r\n\r\n const p2pMediaLoaderConfig = this.getP2PMediaLoaderOptions(redundancyUrlManager)\r\n const loader = new this.p2pMediaLoaderModule.Engine(p2pMediaLoaderConfig).createLoaderClass() as P2PMediaLoader\r\n\r\n const p2pMediaLoader: P2PMediaLoaderPluginOptions = {\r\n redundancyUrlManager,\r\n type: 'application/x-mpegURL',\r\n startTime: commonOptions.startTime,\r\n src: this.options.p2pMediaLoader.playlistUrl,\r\n loader\r\n }\r\n\r\n const hlsjs = {\r\n levelLabelHandler: (level: { height: number, width: number }) => {\r\n const resolution = Math.min(level.height || 0, level.width || 0)\r\n\r\n const file = this.options.p2pMediaLoader.videoFiles.find(f => f.resolution.id === resolution)\r\n // We don't have files for live videos\r\n if (!file) return level.height\r\n\r\n let label = file.resolution.label\r\n if (file.fps >= 50) label += file.fps\r\n\r\n return label\r\n }\r\n }\r\n\r\n const html5 = {\r\n hlsjsConfig: this.getHLSJSOptions(loader)\r\n }\r\n\r\n return { p2pMediaLoader, hlsjs, html5 }\r\n }\r\n\r\n // ---------------------------------------------------------------------------\r\n\r\n private getP2PMediaLoaderOptions (redundancyUrlManager: RedundancyUrlManager): HlsJsEngineSettings {\r\n let consumeOnly = false\r\n if ((navigator as any)?.connection?.type === 'cellular' /*|| (window as any).cordova*/) {\r\n logger.info('We are on a cellular connection: disabling seeding.')\r\n consumeOnly = true\r\n }\r\n\r\n const trackerAnnounce = this.options.p2pMediaLoader.trackerAnnounce\r\n .filter(t => t.startsWith('ws'))\r\n\r\n const specificLiveOrVODOptions = this.options.common.isLive\r\n ? this.getP2PMediaLoaderLiveOptions()\r\n : this.getP2PMediaLoaderVODOptions()\r\n\r\n return {\r\n loader: {\r\n trackerAnnounce,\r\n rtcConfig: getRtcConfig(),\r\n\r\n simultaneousHttpDownloads: 1,\r\n httpFailedSegmentTimeout: 1000,\r\n\r\n segmentValidator: !this.options.common.localTransport ? segmentValidatorFactory(this.options.p2pMediaLoader.segmentsSha256Url, this.options.common.isLive) : undefined,\r\n segmentUrlBuilder: segmentUrlBuilderFactory(redundancyUrlManager),\r\n\r\n useP2P: this.options.common.p2pEnabled,\r\n consumeOnly,\r\n segmentsStorage : this.options.segmentsStorage,\r\n\r\n localTransport : this.options.common.localTransport,\r\n\r\n ...specificLiveOrVODOptions\r\n },\r\n segments: {\r\n assetsStorage : this.options.assetsStorage,\r\n swarmId: this.options.p2pMediaLoader.playlistUrl,\r\n forwardSegmentCount: specificLiveOrVODOptions.p2pDownloadMaxPriority ?? 20\r\n }\r\n }\r\n }\r\n\r\n private getP2PMediaLoaderLiveOptions (): Partial {\r\n const base = {\r\n requiredSegmentsPriority: 1\r\n }\r\n\r\n const latencyMode = this.options.common.liveOptions.latencyMode\r\n\r\n switch (latencyMode) {\r\n case LiveVideoLatencyMode.SMALL_LATENCY:\r\n return {\r\n ...base,\r\n\r\n useP2P: false,\r\n httpDownloadProbability: 1\r\n }\r\n\r\n case LiveVideoLatencyMode.HIGH_LATENCY:\r\n return base\r\n\r\n default:\r\n return base\r\n }\r\n }\r\n\r\n private getP2PMediaLoaderVODOptions (): Partial {\r\n return {\r\n requiredSegmentsPriority: 3,\r\n skipSegmentBuilderPriority: 1,\r\n\r\n cachedSegmentExpiration: 10 * 60 * 1000,\r\n cachedSegmentsCount: 30,\r\n\r\n httpDownloadMaxPriority: 9,\r\n httpDownloadProbability: 0.06,\r\n httpDownloadProbabilitySkipIfNoPeers: true,\r\n\r\n p2pDownloadMaxPriority: 50\r\n }\r\n }\r\n\r\n // ---------------------------------------------------------------------------\r\n\r\n private getHLSJSOptions (loader: P2PMediaLoader) {\r\n const specificLiveOrVODOptions = this.options.common.isLive\r\n ? this.getHLSLiveOptions()\r\n : this.getHLSVODOptions()\r\n\r\n //autoLevelEnabled\r\n\r\n const base = {\r\n capLevelToPlayerSize: true,\r\n autoStartLoad: false,\r\n\r\n loader,\r\n\r\n ...specificLiveOrVODOptions\r\n }\r\n\r\n\r\n const averageBandwidth = getAverageBandwidthInStore()\r\n if (!averageBandwidth) return base\r\n\r\n return {\r\n ...base,\r\n\r\n abrEwmaDefaultEstimate: averageBandwidth * 8, // We want bit/s\r\n backBufferLength: 90,\r\n startLevel: -1,\r\n testBandwidth: false,\r\n debug: false,\r\n\r\n //autoLevelEnabled : !(this.options.common.videoDuration > 0 && this.options.common.videoDuration < 60000)\r\n\r\n // capLevelController : CapLevelController\r\n }\r\n }\r\n\r\n private getHLSLiveOptions () {\r\n const latencyMode = this.options.common.liveOptions.latencyMode\r\n\r\n switch (latencyMode) {\r\n case LiveVideoLatencyMode.SMALL_LATENCY:\r\n return {\r\n liveSyncDurationCount: 2\r\n }\r\n\r\n case LiveVideoLatencyMode.HIGH_LATENCY:\r\n return {\r\n liveSyncDurationCount: 10\r\n }\r\n\r\n default:\r\n return {\r\n liveSyncDurationCount: 5\r\n }\r\n }\r\n }\r\n\r\n private getHLSVODOptions () {\r\n return {\r\n liveSyncDurationCount: 5\r\n }\r\n }\r\n}\r\n","import { PeertubePlayerManagerOptions } from '../../types'\r\n\r\nexport class WebTorrentOptionsBuilder {\r\n\r\n constructor (\r\n private options: PeertubePlayerManagerOptions,\r\n private autoPlayValue: any\r\n ) {\r\n\r\n }\r\n\r\n getPluginOptions () {\r\n const commonOptions = this.options.common\r\n const webtorrentOptions = this.options.webtorrent\r\n const p2pMediaLoaderOptions = this.options.p2pMediaLoader\r\n\r\n const autoplay = this.autoPlayValue === 'play'\r\n\r\n const webtorrent = {\r\n autoplay,\r\n\r\n playerRefusedP2P: commonOptions.p2pEnabled === false,\r\n videoDuration: commonOptions.videoDuration,\r\n playerElement: commonOptions.playerElement,\r\n\r\n videoFiles: webtorrentOptions.videoFiles.length !== 0\r\n ? webtorrentOptions.videoFiles\r\n // The WebTorrent plugin won't be able to play these files, but it will fallback to HTTP mode\r\n : p2pMediaLoaderOptions?.videoFiles || [],\r\n\r\n startTime: commonOptions.startTime\r\n }\r\n\r\n return { webtorrent }\r\n }\r\n}\r\n","import videojs from 'video.js'\r\nimport { copyToClipboard } from '@root-helpers/utils'\r\nimport { isIOS, isSafari } from '@root-helpers/web-browser'\r\nimport { buildVideoLink, decorateVideoLink, pick } from '@shared/core-utils'\r\nimport { isDefaultLocale } from '@shared/core-utils/i18n'\r\nimport { VideoJSPluginOptions } from '../../types'\r\nimport { CommonOptions, PeertubePlayerManagerOptions, PlayerMode } from '../../types/manager-options'\r\nimport { ControlBarOptionsBuilder } from './control-bar-options-builder'\r\nimport { HLSOptionsBuilder } from './hls-options-builder'\r\nimport { WebTorrentOptionsBuilder } from './webtorrent-options-builder'\r\n\r\nexport class ManagerOptionsBuilder {\r\n\r\n constructor (\r\n private mode: PlayerMode,\r\n private options: PeertubePlayerManagerOptions,\r\n private p2pMediaLoaderModule?: any\r\n ) {\r\n\r\n }\r\n\r\n getVideojsOptions (alreadyPlayed: boolean): videojs.PlayerOptions {\r\n const commonOptions = this.options.common\r\n\r\n let autoplay = this.getAutoPlayValue(commonOptions.autoplay, alreadyPlayed)\r\n const html5 = {\r\n preloadTextTracks: false\r\n }\r\n\r\n const plugins: VideoJSPluginOptions = {\r\n peertube: {\r\n mode: this.mode,\r\n autoplay, // Use peertube plugin autoplay because we could get the file by webtorrent\r\n\r\n ...pick(commonOptions, [\r\n 'videoViewUrl',\r\n 'authorizationHeader',\r\n 'startTime',\r\n 'videoDuration',\r\n 'subtitle',\r\n // 'videoCaptions',\r\n 'stopTime',\r\n 'isLive',\r\n 'videoUUID',\r\n ])\r\n }\r\n }\r\n\r\n /*if (commonOptions.playlist) {\r\n plugins.playlist = commonOptions.playlist\r\n }*/\r\n\r\n\r\n\r\n if (this.mode === 'p2p-media-loader') {\r\n const hlsOptionsBuilder = new HLSOptionsBuilder(this.options, this.p2pMediaLoaderModule)\r\n const options = hlsOptionsBuilder.getPluginOptions()\r\n\r\n Object.assign(plugins, pick(options, [ 'hlsjs', 'p2pMediaLoader' ]))\r\n Object.assign(html5, options.html5)\r\n } else if (this.mode === 'webtorrent') {\r\n const webtorrentOptionsBuilder = new WebTorrentOptionsBuilder(this.options, this.getAutoPlayValue(autoplay, alreadyPlayed))\r\n\r\n Object.assign(plugins, webtorrentOptionsBuilder.getPluginOptions())\r\n\r\n // WebTorrent plugin handles autoplay, because we do some hackish stuff in there\r\n autoplay = false\r\n }\r\n\r\n Object.assign(plugins, {\r\n hotkeys : {}\r\n })\r\n\r\n const controlBarOptionsBuilder = new ControlBarOptionsBuilder(this.options, this.mode)\r\n\r\n const videojsOptions = {\r\n html5,\r\n\r\n // We don't use text track settings for now\r\n textTrackSettings: false as any, // FIXME: typings\r\n controls: commonOptions.controls !== undefined ? commonOptions.controls : true,\r\n loop: commonOptions.loop !== undefined ? commonOptions.loop : false,\r\n\r\n muted: commonOptions.muted !== undefined\r\n ? commonOptions.muted\r\n : undefined, // Undefined so the player knows it has to check the local storage\r\n\r\n autoplay: this.getAutoPlayValue(autoplay, alreadyPlayed),\r\n\r\n poster: commonOptions.poster,\r\n inactivityTimeout: commonOptions.inactivityTimeout,\r\n playbackRates: [ 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2 ],\r\n\r\n plugins,\r\n\r\n sources : commonOptions.sources,\r\n\r\n controlBar: {\r\n children: controlBarOptionsBuilder.getChildrenOptions() as any // FIXME: typings\r\n },\r\n\r\n \r\n }\r\n\r\n if (commonOptions.language && !isDefaultLocale(commonOptions.language)) {\r\n Object.assign(videojsOptions, { language: commonOptions.language })\r\n }\r\n\r\n return videojsOptions\r\n }\r\n\r\n private getAutoPlayValue (autoplay: any, alreadyPlayed: boolean) {\r\n if (autoplay !== true) return autoplay\r\n\r\n // On first play, disable autoplay to avoid issues\r\n // But if the player already played videos, we can safely autoplay next ones\r\n if (isIOS() || isSafari()) {\r\n return alreadyPlayed ? 'play' : false\r\n }\r\n\r\n return 'play'\r\n }\r\n\r\n getContextMenuOptions (player: videojs.Player, commonOptions: CommonOptions) {\r\n const content = () => {\r\n const isLoopEnabled = player.options_['loop']\r\n\r\n const items = [\r\n {\r\n icon: 'repeat',\r\n label: player.localize('Play in loop') + (isLoopEnabled ? '' : ''),\r\n listener: function () {\r\n player.options_['loop'] = !isLoopEnabled\r\n }\r\n },\r\n {\r\n label: player.localize('Copy the video URL'),\r\n listener: function () {\r\n copyToClipboard(buildVideoLink({ shortUUID: commonOptions.videoShortUUID }))\r\n }\r\n },\r\n {\r\n label: player.localize('Copy the video URL at the current time'),\r\n listener: function (this: videojs.Player) {\r\n const url = buildVideoLink({ shortUUID: commonOptions.videoShortUUID })\r\n\r\n copyToClipboard(decorateVideoLink({ url, startTime: this.currentTime() }))\r\n }\r\n },\r\n /*{\r\n icon: 'code',\r\n label: player.localize('Copy embed code'),\r\n listener: () => {\r\n copyToClipboard(buildVideoOrPlaylistEmbed(commonOptions.embedUrl, commonOptions.embedTitle))\r\n }\r\n }*/\r\n ]\r\n\r\n /*if (this.mode === 'webtorrent') {\r\n items.push({\r\n label: player.localize('Copy magnet URI'),\r\n listener: function (this: videojs.Player) {\r\n copyToClipboard(this.webtorrent().getCurrentVideoFile().magnetUri)\r\n }\r\n })\r\n }*/\r\n\r\n items.push({\r\n icon: 'info',\r\n label: player.localize('Stats for nerds'),\r\n listener: () => {\r\n player.stats().show()\r\n }\r\n })\r\n\r\n return items.map(i => ({\r\n ...i,\r\n label: `` + i.label\r\n }))\r\n }\r\n\r\n return { content }\r\n }\r\n}\r\n","//@ts-nocheck\r\n\r\nimport { Events } from 'hls.js/src/events';\r\n\r\nimport type {\r\n BufferCodecsData,\r\n MediaAttachingData,\r\n FPSDropLevelCappingData,\r\n} from 'hls.js/src/types/events';\r\nimport type { ComponentAPI } from 'hls.js/src/types/component-api';\r\nimport type Hls from \"hls.js\";\r\n\r\n\r\nclass CapLevelController implements ComponentAPI {\r\n public autoLevelCapping: number;\r\n public firstLevel: number;\r\n public media: HTMLVideoElement | null;\r\n public restrictedLevels: Array;\r\n public timer: number | undefined;\r\n public paused: Boolean\r\n\r\n private hls: Hls;\r\n\r\n private streamController?: any;\r\n public clientRect: { width: number; height: number } | null;\r\n public clientRectLast: { width: number; height: number } | null;\r\n\r\n constructor(hls: Hls) {\r\n this.hls = hls;\r\n this.autoLevelCapping = Number.POSITIVE_INFINITY;\r\n this.firstLevel = -1;\r\n this.media = null;\r\n this.restrictedLevels = [];\r\n this.timer = undefined;\r\n this.clientRect = null;\r\n this.paused = true\r\n\r\n /*this.hls.pauseCapping = () => {\r\n this.paused = true\r\n }\r\n\r\n this.hls.resumeCapping = () => {\r\n this.paused = false\r\n }*/\r\n\r\n this.registerListeners();\r\n }\r\n\r\n public setStreamController(streamController: StreamController) {\r\n this.streamController = streamController;\r\n }\r\n\r\n public destroy() {\r\n this.unregisterListener();\r\n if (this.hls.config.capLevelToPlayerSize) {\r\n this.stopCapping();\r\n }\r\n this.media = null;\r\n this.clientRect = null;\r\n // @ts-ignore\r\n this.hls = this.streamController = null;\r\n }\r\n\r\n protected registerListeners() {\r\n const { hls } = this;\r\n hls.on(Events.FPS_DROP_LEVEL_CAPPING, this.onFpsDropLevelCapping, this);\r\n hls.on(Events.MEDIA_ATTACHING, this.onMediaAttaching, this);\r\n hls.on(Events.MANIFEST_PARSED, this.onManifestParsed, this);\r\n hls.on(Events.BUFFER_CODECS, this.onBufferCodecs, this);\r\n hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this);\r\n }\r\n\r\n protected unregisterListener() {\r\n const { hls } = this;\r\n hls.off(Events.FPS_DROP_LEVEL_CAPPING, this.onFpsDropLevelCapping, this);\r\n hls.off(Events.MEDIA_ATTACHING, this.onMediaAttaching, this);\r\n hls.off(Events.MANIFEST_PARSED, this.onManifestParsed, this);\r\n hls.off(Events.BUFFER_CODECS, this.onBufferCodecs, this);\r\n hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this);\r\n }\r\n\r\n protected onFpsDropLevelCapping(\r\n event: Events.FPS_DROP_LEVEL_CAPPING,\r\n data: FPSDropLevelCappingData\r\n ) {\r\n // Don't add a restricted level more than once\r\n if (\r\n CapLevelController.isLevelAllowed(\r\n data.droppedLevel,\r\n this.restrictedLevels\r\n )\r\n ) {\r\n this.restrictedLevels.push(data.droppedLevel);\r\n }\r\n }\r\n\r\n protected onMediaAttaching(\r\n event: Events.MEDIA_ATTACHING,\r\n data: MediaAttachingData\r\n ) {\r\n this.media = data.media instanceof HTMLVideoElement ? data.media : null;\r\n }\r\n\r\n protected onManifestParsed(\r\n event: any,\r\n data: any\r\n ) {\r\n const hls = this.hls;\r\n this.restrictedLevels = [];\r\n this.firstLevel = data.firstLevel;\r\n if (hls.config.capLevelToPlayerSize && data.video) {\r\n // Start capping immediately if the manifest has signaled video codecs\r\n this.startCapping();\r\n }\r\n }\r\n\r\n // Only activate capping when playing a video stream; otherwise, multi-bitrate audio-only streams will be restricted\r\n // to the first level\r\n protected onBufferCodecs(\r\n event: Events.BUFFER_CODECS,\r\n data: BufferCodecsData\r\n ) {\r\n const hls = this.hls;\r\n if (hls.config.capLevelToPlayerSize && data.video) {\r\n // If the manifest did not signal a video codec capping has been deferred until we're certain video is present\r\n this.startCapping();\r\n }\r\n }\r\n\r\n protected onMediaDetaching() {\r\n this.stopCapping();\r\n }\r\n\r\n detectPlayerSize() {\r\n if (this.media && this.mediaHeight > 0 && this.mediaWidth > 0) {\r\n const levels = this.hls.levels;\r\n \r\n if (levels.length) {\r\n const hls = this.hls;\r\n hls.autoLevelCapping = this.getMaxLevel(levels.length - 1);\r\n\r\n if (\r\n hls.autoLevelCapping > this.autoLevelCapping &&\r\n this.streamController\r\n ) {\r\n\r\n // if auto level capping has a higher value for the previous one, flush the buffer using nextLevelSwitch\r\n // usually happen when the user go to the fullscreen mode.\r\n this.streamController.nextLevelSwitch();\r\n }\r\n this.autoLevelCapping = hls.autoLevelCapping;\r\n }\r\n }\r\n }\r\n\r\n /*\r\n * returns level should be the one with the dimensions equal or greater than the media (player) dimensions (so the video will be downscaled)\r\n */\r\n getMaxLevel(capLevelIndex: number): number {\r\n const levels = this.hls.levels;\r\n if (!levels.length) {\r\n return -1;\r\n }\r\n\r\n const validLevels = levels.filter(\r\n (level, index) =>\r\n CapLevelController.isLevelAllowed(index, this.restrictedLevels) &&\r\n index <= capLevelIndex\r\n );\r\n\r\n\r\n\r\n this.clientRect = null;\r\n return CapLevelController.getMaxLevelByMediaSize(\r\n validLevels,\r\n this.mediaWidth,\r\n this.mediaHeight\r\n );\r\n }\r\n\r\n capp(){\r\n this.autoLevelCapping = Number.POSITIVE_INFINITY;\r\n this.hls.firstLevel = this.getMaxLevel(this.firstLevel);\r\n \r\n this.detectPlayerSize();\r\n }\r\n\r\n startCapping() {\r\n if (this.timer) {\r\n // Don't reset capping if started twice; this can happen if the manifest signals a video codec\r\n return;\r\n }\r\n this.autoLevelCapping = Number.POSITIVE_INFINITY;\r\n this.hls.firstLevel = this.getMaxLevel(this.firstLevel);\r\n self.clearInterval(this.timer);\r\n this.timer = self.setInterval(this.detectPlayerSize.bind(this), 10000);\r\n this.detectPlayerSize();\r\n }\r\n\r\n stopCapping() {\r\n this.restrictedLevels = [];\r\n this.firstLevel = -1;\r\n //this.autoLevelCapping = Number.POSITIVE_INFINITY;\r\n \r\n if (this.timer) {\r\n self.clearInterval(this.timer);\r\n this.timer = undefined;\r\n }\r\n }\r\n\r\n\r\n getDimensions(): { width: number; height: number } {\r\n\r\n if (this.paused && this.clientRectLast){\r\n return this.clientRectLast\r\n }\r\n\r\n if (this.clientRect) {\r\n return this.clientRect;\r\n }\r\n const media = this.media;\r\n const boundsRect = {\r\n width: 0,\r\n height: 0,\r\n };\r\n\r\n if (media) {\r\n const clientRect = media.getBoundingClientRect();\r\n boundsRect.width = clientRect.width;\r\n boundsRect.height = clientRect.height;\r\n if (!boundsRect.width && !boundsRect.height) {\r\n // When the media element has no width or height (equivalent to not being in the DOM),\r\n // then use its width and height attributes (media.width, media.height)\r\n boundsRect.width =\r\n clientRect.right - clientRect.left || media.width || 0;\r\n boundsRect.height =\r\n clientRect.bottom - clientRect.top || media.height || 0;\r\n }\r\n }\r\n\r\n this.clientRectLast = this.clientRect = boundsRect;\r\n \r\n return boundsRect;\r\n }\r\n\r\n get mediaWidth(): number {\r\n return this.getDimensions().width * this.contentScaleFactor;\r\n }\r\n\r\n get mediaHeight(): number {\r\n return this.getDimensions().height * this.contentScaleFactor;\r\n }\r\n\r\n get contentScaleFactor(): number {\r\n let pixelRatio = 1;\r\n try {\r\n pixelRatio = self.devicePixelRatio;\r\n } catch (e) {\r\n /* no-op */\r\n }\r\n\r\n if (pixelRatio > 1.5) pixelRatio = 1.5\r\n\r\n return pixelRatio;\r\n }\r\n\r\n static isLevelAllowed(\r\n level: number,\r\n restrictedLevels: Array = []\r\n ): boolean {\r\n return restrictedLevels.indexOf(level) === -1;\r\n }\r\n\r\n static getMaxLevelByMediaSize(\r\n levels: Array,\r\n width: number,\r\n height: number\r\n ): number {\r\n if (!levels || !levels.length) {\r\n return -1;\r\n }\r\n\r\n // Levels can have the same dimensions but differing bandwidths - since levels are ordered, we can look to the next\r\n // to determine whether we've chosen the greatest bandwidth for the media's dimensions\r\n const atGreatestBandiwdth = (curLevel : any, nextLevel : any) => {\r\n if (!nextLevel) {\r\n return true;\r\n }\r\n\r\n return (\r\n curLevel.width !== nextLevel.width ||\r\n curLevel.height !== nextLevel.height\r\n );\r\n };\r\n\r\n\r\n // If we run through the loop without breaking, the media's dimensions are greater than every level, so default to\r\n // the max level\r\n let maxLevelIndex = levels.length - 1;\r\n\r\n for (let i = 0; i < levels.length; i += 1) {\r\n const level = levels[i];\r\n if (\r\n (level.width >= width || level.height >= height) &&\r\n atGreatestBandiwdth(level, levels[i + 1])\r\n ) {\r\n maxLevelIndex = i;\r\n break;\r\n }\r\n }\r\n\r\n return maxLevelIndex;\r\n }\r\n}\r\n\r\nexport default CapLevelController;\r\n","import EwmaBandWidthEstimator from 'hls.js/src/utils/ewma-bandwidth-estimator';\r\nimport { Events } from 'hls.js/src/events';\r\nimport { BufferHelper } from 'hls.js/src/utils/buffer-helper';\r\nimport { ErrorDetails } from 'hls.js/src/errors';\r\nimport { PlaylistLevelType } from 'hls.js/src/types/loader';\r\nimport { logger } from 'hls.js/src/utils/logger';\r\n\r\n\r\nimport type { Bufferable } from 'hls.js/src/utils/buffer-helper';\r\n//import type { LoaderStats } from 'hls.js/src/types/loader';\r\nimport type Hls from \"hls.js\";\r\nimport type {\r\n FragLoadingData,\r\n FragLoadedData,\r\n FragBufferedData,\r\n ErrorData,\r\n LevelLoadedData,\r\n} from 'hls.js/src/types/events';\r\nimport type { ComponentAPI } from 'hls.js/src/types/component-api';\r\n\r\nclass AbrController implements ComponentAPI {\r\n protected hls: Hls;\r\n private lastLoadedFragLevel: number = 0;\r\n private _nextAutoLevel: number = -1;\r\n private timer?: number;\r\n private onCheck: Function = this._abandonRulesCheck.bind(this);\r\n private fragCurrent: any | null = null;\r\n private partCurrent: any | null = null;\r\n private bitrateTestDelay: number = 0;\r\n\r\n public readonly bwEstimator: EwmaBandWidthEstimator;\r\n\r\n constructor(hls: Hls) {\r\n this.hls = hls;\r\n\r\n const config = hls.config;\r\n this.bwEstimator = new EwmaBandWidthEstimator(\r\n config.abrEwmaSlowVoD,\r\n config.abrEwmaFastVoD,\r\n config.abrEwmaDefaultEstimate\r\n );\r\n\r\n this.registerListeners();\r\n }\r\n\r\n protected registerListeners() {\r\n const { hls } = this;\r\n hls.on(Events.FRAG_LOADING, this.onFragLoading as any, this);\r\n hls.on(Events.FRAG_LOADED, this.onFragLoaded as any, this);\r\n hls.on(Events.FRAG_BUFFERED, this.onFragBuffered as any, this);\r\n hls.on(Events.LEVEL_LOADED, this.onLevelLoaded as any, this);\r\n hls.on(Events.ERROR, this.onError as any, this);\r\n }\r\n\r\n protected unregisterListeners() {\r\n const { hls } = this;\r\n hls.off(Events.FRAG_LOADING, this.onFragLoading as any, this);\r\n hls.off(Events.FRAG_LOADED, this.onFragLoaded as any, this);\r\n hls.off(Events.FRAG_BUFFERED, this.onFragBuffered as any, this);\r\n hls.off(Events.LEVEL_LOADED, this.onLevelLoaded as any, this);\r\n hls.off(Events.ERROR, this.onError as any, this);\r\n }\r\n\r\n public destroy() {\r\n this.unregisterListeners();\r\n this.clearTimer();\r\n // @ts-ignore\r\n this.hls = this.onCheck = null;\r\n this.fragCurrent = this.partCurrent = null;\r\n }\r\n\r\n protected onFragLoading(event: Events.FRAG_LOADING, data: FragLoadingData) {\r\n const frag = data.frag;\r\n if (frag.type === PlaylistLevelType.MAIN) {\r\n if (!this.timer) {\r\n this.fragCurrent = frag;\r\n this.partCurrent = data.part ?? null;\r\n this.timer = self.setInterval(this.onCheck, 100);\r\n }\r\n }\r\n }\r\n\r\n protected onLevelLoaded(event: Events.LEVEL_LOADED, data: LevelLoadedData) {\r\n const config = this.hls.config;\r\n if (data.details.live) {\r\n this.bwEstimator.update(config.abrEwmaSlowLive, config.abrEwmaFastLive);\r\n } else {\r\n this.bwEstimator.update(config.abrEwmaSlowVoD, config.abrEwmaFastVoD);\r\n }\r\n }\r\n\r\n /*\r\n This method monitors the download rate of the current fragment, and will downswitch if that fragment will not load\r\n quickly enough to prevent underbuffering\r\n */\r\n private _abandonRulesCheck() {\r\n const { fragCurrent: frag, partCurrent: part, hls } = this;\r\n const { autoLevelEnabled, config, media } = hls;\r\n if (!frag || !media) {\r\n return;\r\n }\r\n\r\n const stats: any = part ? part.stats : frag.stats;\r\n const duration = part ? part.duration : frag.duration;\r\n // If frag loading is aborted, complete, or from lowest level, stop timer and return\r\n if (\r\n stats.aborted ||\r\n (stats.loaded && stats.loaded === stats.total) ||\r\n frag.level === 0\r\n ) {\r\n this.clearTimer();\r\n // reset forced auto level value so that next level will be selected\r\n this._nextAutoLevel = -1;\r\n return;\r\n }\r\n\r\n // This check only runs if we're in ABR mode and actually playing\r\n if (\r\n !autoLevelEnabled ||\r\n media.paused ||\r\n !media.playbackRate ||\r\n !media.readyState\r\n ) {\r\n return;\r\n }\r\n\r\n const bufferInfo = hls.mainForwardBufferInfo;\r\n if (bufferInfo === null) {\r\n return;\r\n }\r\n\r\n const requestDelay = performance.now() - stats.loading.start;\r\n const playbackRate = Math.abs(media.playbackRate);\r\n // In order to work with a stable bandwidth, only begin monitoring bandwidth after half of the fragment has been loaded\r\n if (requestDelay <= (500 * duration) / playbackRate) {\r\n return;\r\n }\r\n\r\n const loadedFirstByte = stats.loaded && stats.loading.first;\r\n const bwEstimate: number = this.bwEstimator.getEstimate();\r\n const { levels, minAutoLevel } = hls;\r\n const level = levels[frag.level];\r\n const expectedLen =\r\n stats.total ||\r\n Math.max(stats.loaded, Math.round((duration * level.maxBitrate) / 8));\r\n const loadRate = loadedFirstByte ? (stats.loaded * 1000) / requestDelay : 0;\r\n\r\n // fragLoadDelay is an estimate of the time (in seconds) it will take to buffer the remainder of the fragment\r\n const fragLoadedDelay = loadRate\r\n ? (expectedLen - stats.loaded) / loadRate\r\n : (expectedLen * 8) / bwEstimate;\r\n\r\n // bufferStarvationDelay is an estimate of the amount time (in seconds) it will take to exhaust the buffer\r\n const bufferStarvationDelay = bufferInfo.len / playbackRate;\r\n\r\n // Only downswitch if the time to finish loading the current fragment is greater than the amount of buffer left\r\n if (fragLoadedDelay <= bufferStarvationDelay) {\r\n return;\r\n }\r\n\r\n let fragLevelNextLoadedDelay: number = Number.POSITIVE_INFINITY;\r\n let nextLoadLevel: number;\r\n // Iterate through lower level and try to find the largest one that avoids rebuffering\r\n for (\r\n nextLoadLevel = frag.level - 1;\r\n nextLoadLevel > minAutoLevel;\r\n nextLoadLevel--\r\n ) {\r\n // compute time to load next fragment at lower level\r\n // 0.8 : consider only 80% of current bw to be conservative\r\n // 8 = bits per byte (bps/Bps)\r\n const levelNextBitrate = levels[nextLoadLevel].maxBitrate;\r\n fragLevelNextLoadedDelay = loadRate\r\n ? (duration * levelNextBitrate) / (8 * 0.8 * loadRate)\r\n : (duration * levelNextBitrate) / bwEstimate;\r\n\r\n if (fragLevelNextLoadedDelay < bufferStarvationDelay) {\r\n break;\r\n }\r\n }\r\n // Only emergency switch down if it takes less time to load a new fragment at lowest level instead of continuing\r\n // to load the current one\r\n if (fragLevelNextLoadedDelay >= fragLoadedDelay) {\r\n return;\r\n }\r\n logger.warn(`Fragment ${frag.sn}${\r\n part ? ' part ' + part.index : ''\r\n } of level ${\r\n frag.level\r\n } is loading too slowly and will cause an underbuffer; aborting and switching to level ${nextLoadLevel}\r\n Current BW estimate: ${\r\n Number.isFinite(bwEstimate) ? (bwEstimate / 1024).toFixed(3) : 'Unknown'\r\n } Kb/s\r\n Estimated load time for current fragment: ${fragLoadedDelay.toFixed(3)} s\r\n Estimated load time for the next fragment: ${fragLevelNextLoadedDelay.toFixed(\r\n 3\r\n )} s\r\n Time to underbuffer: ${bufferStarvationDelay.toFixed(3)} s`);\r\n hls.nextLoadLevel = nextLoadLevel;\r\n if (loadedFirstByte) {\r\n // If there has been loading progress, sample bandwidth\r\n this.bwEstimator.sample(requestDelay, stats.loaded);\r\n }\r\n this.clearTimer();\r\n if (frag.loader) {\r\n this.fragCurrent = this.partCurrent = null;\r\n frag.loader.abort();\r\n }\r\n hls.trigger(Events.FRAG_LOAD_EMERGENCY_ABORTED, { frag, part, stats });\r\n }\r\n\r\n protected onFragLoaded(\r\n event: Events.FRAG_LOADED,\r\n { frag, part }: FragLoadedData\r\n ) {\r\n if (\r\n frag.type === PlaylistLevelType.MAIN &&\r\n Number.isFinite(frag.sn as number)\r\n ) {\r\n const stats = part ? part.stats : frag.stats;\r\n const duration = part ? part.duration : frag.duration;\r\n // stop monitoring bw once frag loaded\r\n this.clearTimer();\r\n // store level id after successful fragment load\r\n this.lastLoadedFragLevel = frag.level;\r\n // reset forced auto level value so that next level will be selected\r\n this._nextAutoLevel = -1;\r\n\r\n // compute level average bitrate\r\n if (this.hls.config.abrMaxWithRealBitrate) {\r\n const level = this.hls.levels[frag.level];\r\n const loadedBytes =\r\n (level.loaded ? level.loaded.bytes : 0) + stats.loaded;\r\n const loadedDuration =\r\n (level.loaded ? level.loaded.duration : 0) + duration;\r\n level.loaded = { bytes: loadedBytes, duration: loadedDuration };\r\n level.realBitrate = Math.round((8 * loadedBytes) / loadedDuration);\r\n }\r\n if (frag.bitrateTest) {\r\n const fragBufferedData: FragBufferedData = {\r\n stats,\r\n frag,\r\n part,\r\n id: frag.type,\r\n };\r\n this.onFragBuffered(Events.FRAG_BUFFERED, fragBufferedData);\r\n }\r\n }\r\n }\r\n\r\n protected onFragBuffered(\r\n event: Events.FRAG_BUFFERED,\r\n data: any //FragBufferedData\r\n ) {\r\n const { frag, part } = data;\r\n const stats = part ? part.stats : frag.stats;\r\n\r\n if (stats.aborted) {\r\n return;\r\n }\r\n // Only count non-alt-audio frags which were actually buffered in our BW calculations\r\n if (frag.type !== PlaylistLevelType.MAIN || frag.sn === 'initSegment') {\r\n return;\r\n }\r\n // Use the difference between parsing and request instead of buffering and request to compute fragLoadingProcessing;\r\n // rationale is that buffer appending only happens once media is attached. This can happen when config.startFragPrefetch\r\n // is used. If we used buffering in that case, our BW estimate sample will be very large.\r\n const processingMs = stats.parsing.end - stats.loading.start;\r\n\r\n if(stats.bwEstimateSample)\r\n this.bwEstimator.sample(processingMs, stats.loaded);\r\n\r\n stats.bwEstimate = this.bwEstimator.getEstimate();\r\n \r\n if (frag.bitrateTest) {\r\n this.bitrateTestDelay = processingMs / 1000;\r\n } else {\r\n this.bitrateTestDelay = 0;\r\n }\r\n }\r\n\r\n protected onError(event: Events.ERROR, data: ErrorData) {\r\n // stop timer in case of frag loading error\r\n switch (data.details) {\r\n case ErrorDetails.FRAG_LOAD_ERROR:\r\n case ErrorDetails.FRAG_LOAD_TIMEOUT:\r\n this.clearTimer();\r\n break;\r\n default:\r\n break;\r\n }\r\n }\r\n\r\n clearTimer() {\r\n self.clearInterval(this.timer);\r\n this.timer = undefined;\r\n }\r\n\r\n // return next auto level\r\n get nextAutoLevel() {\r\n\r\n const forcedAutoLevel = this._nextAutoLevel;\r\n const bwEstimator = this.bwEstimator;\r\n // in case next auto level has been forced, and bw not available or not reliable, return forced value\r\n if (forcedAutoLevel !== -1 && !bwEstimator.canEstimate()) {\r\n return forcedAutoLevel;\r\n }\r\n\r\n // compute next level using ABR logic\r\n let nextABRAutoLevel = this.getNextABRAutoLevel();\r\n\r\n\r\n // use forced auto level when ABR selected level has errored\r\n if (forcedAutoLevel !== -1 && this.hls.levels[nextABRAutoLevel].loadError) {\r\n return forcedAutoLevel;\r\n }\r\n // if forced auto level has been defined, use it to cap ABR computed quality level\r\n if (forcedAutoLevel !== -1) {\r\n nextABRAutoLevel = Math.min(forcedAutoLevel, nextABRAutoLevel);\r\n }\r\n\r\n return nextABRAutoLevel;\r\n }\r\n\r\n private getNextABRAutoLevel() {\r\n const { fragCurrent, partCurrent, hls } = this;\r\n const { maxAutoLevel, config, minAutoLevel, media } = hls;\r\n const currentFragDuration = partCurrent\r\n ? partCurrent.duration\r\n : fragCurrent\r\n ? fragCurrent.duration\r\n : 0;\r\n const pos = media ? media.currentTime : 0;\r\n\r\n // playbackRate is the absolute value of the playback rate; if media.playbackRate is 0, we use 1 to load as\r\n // if we're playing back at the normal rate.\r\n const playbackRate =\r\n media && media.playbackRate !== 0 ? Math.abs(media.playbackRate) : 1.0;\r\n const avgbw = this.bwEstimator\r\n ? this.bwEstimator.getEstimate()\r\n : config.abrEwmaDefaultEstimate;\r\n // bufferStarvationDelay is the wall-clock time left until the playback buffer is exhausted.\r\n const bufferInfo = hls.mainForwardBufferInfo;\r\n const bufferStarvationDelay =\r\n (bufferInfo ? bufferInfo.len : 0) / playbackRate;\r\n\r\n // First, look to see if we can find a level matching with our avg bandwidth AND that could also guarantee no rebuffering at all\r\n let bestLevel = this.findBestLevel(\r\n avgbw,\r\n minAutoLevel,\r\n maxAutoLevel,\r\n bufferStarvationDelay,\r\n config.abrBandWidthFactor,\r\n config.abrBandWidthUpFactor\r\n );\r\n if (bestLevel >= 0) {\r\n return bestLevel;\r\n }\r\n logger.trace(\r\n `${\r\n bufferStarvationDelay ? 'rebuffering expected' : 'buffer is empty'\r\n }, finding optimal quality level`\r\n );\r\n // not possible to get rid of rebuffering ... let's try to find level that will guarantee less than maxStarvationDelay of rebuffering\r\n // if no matching level found, logic will return 0\r\n let maxStarvationDelay = currentFragDuration\r\n ? Math.min(currentFragDuration, config.maxStarvationDelay)\r\n : config.maxStarvationDelay;\r\n let bwFactor = config.abrBandWidthFactor;\r\n let bwUpFactor = config.abrBandWidthUpFactor;\r\n\r\n if (!bufferStarvationDelay) {\r\n // in case buffer is empty, let's check if previous fragment was loaded to perform a bitrate test\r\n const bitrateTestDelay = this.bitrateTestDelay;\r\n if (bitrateTestDelay) {\r\n // if it is the case, then we need to adjust our max starvation delay using maxLoadingDelay config value\r\n // max video loading delay used in automatic start level selection :\r\n // in that mode ABR controller will ensure that video loading time (ie the time to fetch the first fragment at lowest quality level +\r\n // the time to fetch the fragment at the appropriate quality level is less than ```maxLoadingDelay``` )\r\n // cap maxLoadingDelay and ensure it is not bigger 'than bitrate test' frag duration\r\n const maxLoadingDelay = currentFragDuration\r\n ? Math.min(currentFragDuration, config.maxLoadingDelay)\r\n : config.maxLoadingDelay;\r\n maxStarvationDelay = maxLoadingDelay - bitrateTestDelay;\r\n logger.trace(\r\n `bitrate test took ${Math.round(\r\n 1000 * bitrateTestDelay\r\n )}ms, set first fragment max fetchDuration to ${Math.round(\r\n 1000 * maxStarvationDelay\r\n )} ms`\r\n );\r\n // don't use conservative factor on bitrate test\r\n bwFactor = bwUpFactor = 1;\r\n }\r\n }\r\n bestLevel = this.findBestLevel(\r\n avgbw,\r\n minAutoLevel,\r\n maxAutoLevel,\r\n bufferStarvationDelay + maxStarvationDelay,\r\n bwFactor,\r\n bwUpFactor\r\n );\r\n return Math.max(bestLevel, 0);\r\n }\r\n\r\n private findBestLevel(\r\n currentBw: number,\r\n minAutoLevel: number,\r\n maxAutoLevel: number,\r\n maxFetchDuration: number,\r\n bwFactor: number,\r\n bwUpFactor: number\r\n ): number {\r\n const {\r\n fragCurrent,\r\n partCurrent,\r\n lastLoadedFragLevel: currentLevel,\r\n } = this;\r\n const { levels } = this.hls;\r\n const level = levels[currentLevel];\r\n const live = !!level?.details?.live;\r\n const currentCodecSet = level?.codecSet;\r\n\r\n const currentFragDuration = partCurrent\r\n ? partCurrent.duration\r\n : fragCurrent\r\n ? fragCurrent.duration\r\n : 0;\r\n for (let i = maxAutoLevel; i >= minAutoLevel; i--) {\r\n const levelInfo = levels[i];\r\n\r\n if (\r\n !levelInfo ||\r\n (currentCodecSet && levelInfo.codecSet !== currentCodecSet)\r\n ) {\r\n continue;\r\n }\r\n\r\n const levelDetails = levelInfo.details;\r\n const avgDuration =\r\n (partCurrent\r\n ? levelDetails?.partTarget\r\n : levelDetails?.averagetargetduration) || currentFragDuration;\r\n\r\n let adjustedbw: number;\r\n // follow algorithm captured from stagefright :\r\n // https://android.googlesource.com/platform/frameworks/av/+/master/media/libstagefright/httplive/LiveSession.cpp\r\n // Pick the highest bandwidth stream below or equal to estimated bandwidth.\r\n // consider only 80% of the available bandwidth, but if we are switching up,\r\n // be even more conservative (70%) to avoid overestimating and immediately\r\n // switching back.\r\n if (i <= currentLevel) {\r\n adjustedbw = bwFactor * currentBw;\r\n } else {\r\n adjustedbw = bwUpFactor * currentBw;\r\n }\r\n\r\n const bitrate: number = levels[i].maxBitrate;\r\n const fetchDuration: number = (bitrate * avgDuration) / adjustedbw;\r\n\r\n logger.trace(\r\n `level/adjustedbw/bitrate/avgDuration/maxFetchDuration/fetchDuration: ${i}/${Math.round(\r\n adjustedbw\r\n )}/${bitrate}/${avgDuration}/${maxFetchDuration}/${fetchDuration}`\r\n );\r\n\r\n\r\n\r\n // if adjusted bw is greater than level bitrate AND\r\n if (\r\n adjustedbw > bitrate &&\r\n // fragment fetchDuration unknown OR live stream OR fragment fetchDuration less than max allowed fetch duration, then this level matches\r\n // we don't account for max Fetch Duration for live streams, this is to avoid switching down when near the edge of live sliding window ...\r\n // special case to support startLevel = -1 (bitrateTest) on live streams : in that case we should not exit loop so that findBestLevel will return -1\r\n (fetchDuration === 0 ||\r\n !Number.isFinite(fetchDuration) ||\r\n (live && !this.bitrateTestDelay) ||\r\n fetchDuration < maxFetchDuration)\r\n ) {\r\n\r\n // as we are looping from highest to lowest, this will return the best achievable quality level\r\n return i;\r\n }\r\n }\r\n // not enough time budget even with quality level 0 ... rebuffering might happen\r\n return -1;\r\n }\r\n\r\n set nextAutoLevel(nextLevel) {\r\n this._nextAutoLevel = nextLevel;\r\n }\r\n}\r\n\r\nexport default AbrController;\r\n","// Thanks https://github.com/streamroot/videojs-hlsjs-plugin\r\n// We duplicated this plugin to choose the hls.js version we want, because streamroot only provide a bundled file\r\n\r\nimport Hlsjs, { ErrorData, HlsConfig, Level, LevelSwitchingData, ManifestParsedData } from 'hls.js'\r\nimport videojs from 'video.js'\r\nimport { logger } from '@root-helpers/logger'\r\nimport { HlsjsConfigHandlerOptions, PeerTubeResolution, VideoJSTechHLS } from '../../types'\r\nimport CapLevelController from './cap-level-controller'\r\nimport AbrController from './abr-controler'\r\n\r\ntype ErrorCounts = {\r\n [ type: string ]: number\r\n}\r\n\r\ntype Metadata = {\r\n levels: Level[]\r\n}\r\n\r\ntype HookFn = (player: videojs.Player, hljs: Hlsjs) => void\r\n\r\nconst registerSourceHandler = function (vjs: typeof videojs) {\r\n if (!Hlsjs.isSupported()) {\r\n logger.warn('Hls.js is not supported in this browser!')\r\n return\r\n }\r\n\r\n const html5 = vjs.getTech('Html5')\r\n\r\n if (!html5) {\r\n logger.error('No Hml5 tech found in videojs')\r\n return\r\n }\r\n\r\n // FIXME: typings\r\n (html5 as any).registerSourceHandler({\r\n canHandleSource: function (source: videojs.Tech.SourceObject) {\r\n const hlsTypeRE = /^application\\/x-mpegURL|application\\/vnd\\.apple\\.mpegurl$/i\r\n const hlsExtRE = /\\.m3u8/i\r\n\r\n if (hlsTypeRE.test(source.type)) return 'probably'\r\n if (hlsExtRE.test(source.src)) return 'maybe'\r\n\r\n return ''\r\n },\r\n\r\n handleSource: function (source: videojs.Tech.SourceObject, tech: VideoJSTechHLS) {\r\n if (tech.hlsProvider) {\r\n tech.hlsProvider.dispose()\r\n }\r\n\r\n tech.hlsProvider = new Html5Hlsjs(vjs, source, tech)\r\n\r\n return tech.hlsProvider\r\n }\r\n }, 0);\r\n\r\n // FIXME: typings\r\n (vjs as any).Html5Hlsjs = Html5Hlsjs\r\n}\r\n\r\nfunction hlsjsConfigHandler (this: videojs.Player, options: HlsjsConfigHandlerOptions) {\r\n const player = this\r\n\r\n if (!options) return\r\n\r\n if (!player.srOptions_) {\r\n player.srOptions_ = {}\r\n }\r\n\r\n if (!player.srOptions_.hlsjsConfig) {\r\n player.srOptions_.hlsjsConfig = options.hlsjsConfig\r\n }\r\n\r\n \r\n\r\n if (options.levelLabelHandler && !player.srOptions_.levelLabelHandler) {\r\n player.srOptions_.levelLabelHandler = options.levelLabelHandler\r\n }\r\n}\r\n\r\nconst registerConfigPlugin = function (vjs: typeof videojs) {\r\n // Used in Brightcove since we don't pass options directly there\r\n const registerVjsPlugin = vjs.registerPlugin || vjs.plugin\r\n registerVjsPlugin('hlsjs', hlsjsConfigHandler)\r\n}\r\n\r\nclass Html5Hlsjs {\r\n private static readonly hooks: { [id: string]: HookFn[] } = {}\r\n\r\n private readonly videoElement: HTMLVideoElement\r\n private readonly errorCounts: ErrorCounts = {}\r\n private readonly player: videojs.Player\r\n private readonly tech: videojs.Tech\r\n private readonly source: videojs.Tech.SourceObject\r\n private readonly vjs: typeof videojs\r\n\r\n private maxNetworkErrorRecovery = 10\r\n\r\n private hls: Hlsjs\r\n private hlsjsConfig: Partial = null\r\n\r\n private _duration: number = null\r\n private metadata: Metadata = null\r\n private isLive: boolean = null\r\n private dvrDuration: number = null\r\n private edgeMargin: number = null\r\n\r\n private handlers: { [ id in 'play' ]: EventListener } = {\r\n play: null\r\n }\r\n\r\n constructor (vjs: typeof videojs, source: videojs.Tech.SourceObject, tech: videojs.Tech) {\r\n this.vjs = vjs\r\n this.source = source\r\n\r\n this.tech = tech;\r\n (this.tech as any).name_ = 'Hlsjs'\r\n\r\n this.videoElement = tech.el() as HTMLVideoElement\r\n this.player = vjs((tech.options_ as any).playerId)\r\n\r\n /*this.player.on('seeking', (e : any) => {\r\n console.log(\"E\", e)\r\n })*/\r\n\r\n this.videoElement.addEventListener('error', event => {\r\n let errorTxt: string\r\n const mediaError = ((event.currentTarget || event.target) as HTMLVideoElement).error\r\n\r\n if (!mediaError) return\r\n\r\n logger.info(mediaError)\r\n switch (mediaError.code) {\r\n case mediaError.MEDIA_ERR_ABORTED:\r\n errorTxt = 'You aborted the video playback'\r\n break\r\n case mediaError.MEDIA_ERR_DECODE:\r\n errorTxt = 'The video playback was aborted due to a corruption problem or because the video used features ' +\r\n 'your browser did not support'\r\n this._handleMediaError(mediaError)\r\n break\r\n case mediaError.MEDIA_ERR_NETWORK:\r\n errorTxt = 'A network error caused the video download to fail part-way'\r\n break\r\n case mediaError.MEDIA_ERR_SRC_NOT_SUPPORTED:\r\n errorTxt = 'The video could not be loaded, either because the server or network failed or because the format is not supported'\r\n break\r\n\r\n default:\r\n errorTxt = mediaError.message\r\n }\r\n\r\n logger.error(`MEDIA_ERROR: ${errorTxt}`)\r\n })\r\n\r\n this.initialize()\r\n }\r\n\r\n duration () {\r\n if (this._duration === Infinity) return Infinity\r\n if (!isNaN(this.videoElement.duration)) return this.videoElement.duration\r\n\r\n return this._duration || 0\r\n }\r\n\r\n seekable () {\r\n if (this.hls.media) {\r\n if (!this.isLive) {\r\n return this.vjs.createTimeRanges(0, this.hls.media.duration)\r\n }\r\n\r\n // Video.js doesn't seem to like floating point timeranges\r\n const startTime = Math.round(this.hls.media.duration - this.dvrDuration)\r\n const endTime = Math.round(this.hls.media.duration - this.edgeMargin)\r\n\r\n return this.vjs.createTimeRanges(startTime, endTime)\r\n }\r\n\r\n return this.vjs.createTimeRanges()\r\n }\r\n\r\n // See comment for `initialize` method.\r\n dispose () {\r\n this.videoElement.removeEventListener('play', this.handlers.play)\r\n\r\n // FIXME: https://github.com/video-dev/hls.js/issues/4092\r\n const untypedHLS = this.hls as any\r\n untypedHLS.log = untypedHLS.warn = () => {\r\n // empty\r\n }\r\n\r\n\r\n this.hls.destroy()\r\n }\r\n\r\n static addHook (type: string, callback: HookFn) {\r\n Html5Hlsjs.hooks[type] = this.hooks[type] || []\r\n Html5Hlsjs.hooks[type].push(callback)\r\n }\r\n\r\n static removeHook (type: string, callback: HookFn) {\r\n if (Html5Hlsjs.hooks[type] === undefined) return false\r\n\r\n const index = Html5Hlsjs.hooks[type].indexOf(callback)\r\n if (index === -1) return false\r\n\r\n Html5Hlsjs.hooks[type].splice(index, 1)\r\n\r\n return true\r\n }\r\n\r\n private _executeHooksFor (type: string) {\r\n if (Html5Hlsjs.hooks[type] === undefined) {\r\n return\r\n }\r\n\r\n // ES3 and IE < 9\r\n for (let i = 0; i < Html5Hlsjs.hooks[type].length; i++) {\r\n Html5Hlsjs.hooks[type][i](this.player, this.hls)\r\n }\r\n }\r\n\r\n private _handleMediaError (error: any) {\r\n\r\n if(error.code == 3){\r\n\r\n var time = this.player.currentTime() + 2\r\n\r\n this.dispose()\r\n this._initHlsjs()\r\n\r\n this.player.currentTime(time)\r\n this.player.play()\r\n this.hls.once(Hlsjs.Events.FRAG_LOADED, () => {\r\n this.player.play()\r\n })\r\n\r\n\r\n return\r\n }\r\n\r\n if (this.errorCounts[Hlsjs.ErrorTypes.MEDIA_ERROR] === 1) {\r\n logger.info('trying to recover media error')\r\n this.hls.recoverMediaError()\r\n return\r\n }\r\n\r\n if (this.errorCounts[Hlsjs.ErrorTypes.MEDIA_ERROR] === 2) {\r\n logger.info('2nd try to recover media error (by swapping audio codec')\r\n this.hls.swapAudioCodec()\r\n this.hls.recoverMediaError()\r\n return\r\n }\r\n\r\n if (this.errorCounts[Hlsjs.ErrorTypes.MEDIA_ERROR] > 2) {\r\n logger.info('bubbling media error up to VIDEOJS')\r\n this.hls.destroy()\r\n this.tech.error = () => error\r\n this.tech.trigger('error')\r\n }\r\n }\r\n\r\n private _handleNetworkError (error: any) {\r\n\r\n if (navigator.onLine === false) return\r\n \r\n if (this.errorCounts[Hlsjs.ErrorTypes.NETWORK_ERROR] <= this.maxNetworkErrorRecovery) {\r\n logger.info('trying to recover network error')\r\n\r\n // Wait 1 second and retry\r\n setTimeout(() => this.hls.startLoad(), 1000)\r\n\r\n // Reset error count on success\r\n this.hls.once(Hlsjs.Events.FRAG_LOADED, () => {\r\n this.errorCounts[Hlsjs.ErrorTypes.NETWORK_ERROR] = 0\r\n })\r\n\r\n return\r\n }\r\n\r\n logger.info('bubbling network error up to VIDEOJS')\r\n this.hls.destroy()\r\n this.tech.error = () => error\r\n this.tech.trigger('error')\r\n }\r\n\r\n private _onError (_event: any, data: ErrorData) {\r\n const error: { message: string, code?: number } = {\r\n message: `HLS.js error: ${data.type} - fatal: ${data.fatal} - ${data.details}`\r\n }\r\n\r\n // increment/set error count\r\n if (this.errorCounts[data.type]) this.errorCounts[data.type] += 1\r\n else this.errorCounts[data.type] = 1\r\n\r\n if(!data.fatal) logger.warn(error.message)\r\n else logger.error(error.message, { data })\r\n\r\n if (data.type === Hlsjs.ErrorTypes.NETWORK_ERROR) {\r\n error.code = 2\r\n this._handleNetworkError(error)\r\n } else if (data.fatal && data.type === Hlsjs.ErrorTypes.MEDIA_ERROR && data.details !== 'manifestIncompatibleCodecsError') {\r\n error.code = 3\r\n this._handleMediaError(error)\r\n } else if (data.fatal) {\r\n this.hls.destroy()\r\n logger.info('bubbling error up to VIDEOJS')\r\n this.tech.error = () => error as any\r\n this.tech.trigger('error')\r\n }\r\n }\r\n\r\n private buildLevelLabel (level: Level) {\r\n if (this.player.srOptions_.levelLabelHandler) {\r\n return this.player.srOptions_.levelLabelHandler(level as any)\r\n }\r\n\r\n if (level.height) return level.height + 'p'\r\n if (level.width) return Math.round(level.width * 9 / 16) + 'p'\r\n if (level.bitrate) return (level.bitrate / 1000) + 'kbps'\r\n\r\n return '0'\r\n }\r\n\r\n private _notifyVideoQualities () {\r\n if (!this.metadata) return\r\n\r\n const resolutions: PeerTubeResolution[] = []\r\n\r\n this.metadata.levels.forEach((level, index) => {\r\n resolutions.push({\r\n id: index,\r\n height: level.height,\r\n width: level.width,\r\n bitrate: level.bitrate,\r\n label: this.buildLevelLabel(level),\r\n selected: level.id === this.hls.manualLevel,\r\n\r\n selectCallback: () => {\r\n this.hls.currentLevel = index\r\n }\r\n })\r\n })\r\n\r\n resolutions.push({\r\n id: -1,\r\n label: this.player.localize('Auto'),\r\n selected: true,\r\n selectCallback: () => this.hls.currentLevel = -1\r\n })\r\n\r\n this.player.peertubeResolutions().add(resolutions)\r\n }\r\n\r\n private _startLoad () {\r\n this.hls.startLoad(-1)\r\n this.videoElement.removeEventListener('play', this.handlers.play)\r\n }\r\n\r\n private _oneLevelObjClone (obj: { [ id: string ]: any }) {\r\n const result = {}\r\n const objKeys = Object.keys(obj)\r\n for (let i = 0; i < objKeys.length; i++) {\r\n result[objKeys[i]] = obj[objKeys[i]]\r\n }\r\n\r\n return result\r\n }\r\n\r\n private _onMetaData (_event: any, data: ManifestParsedData) {\r\n // This could arrive before 'loadedqualitydata' handlers is registered, remember it so we can raise it later\r\n this.metadata = data\r\n this._notifyVideoQualities()\r\n }\r\n\r\n private _initHlsjs () {\r\n const techOptions = this.tech.options_ as HlsjsConfigHandlerOptions\r\n const srOptions_ = this.player.srOptions_\r\n\r\n const hlsjsConfigRef = srOptions_?.hlsjsConfig || techOptions.hlsjsConfig\r\n // Hls.js will write to the reference thus change the object for later streams\r\n this.hlsjsConfig = hlsjsConfigRef ? this._oneLevelObjClone(hlsjsConfigRef) : {}\r\n\r\n if ([ '', 'auto' ].includes(this.videoElement.preload) && !this.videoElement.autoplay && this.hlsjsConfig.autoStartLoad === undefined) {\r\n this.hlsjsConfig.autoStartLoad = false\r\n }\r\n\r\n // If the user explicitly sets autoStartLoad to false, we're not going to enter the if block above\r\n // That's why we have a separate if block here to set the 'play' listener\r\n if (this.hlsjsConfig.autoStartLoad === false) {\r\n this.handlers.play = this._startLoad.bind(this)\r\n this.videoElement.addEventListener('play', this.handlers.play)\r\n }\r\n\r\n\r\n //@ts-ignore\r\n this.hlsjsConfig.capLevelController = CapLevelController\r\n this.hlsjsConfig.abrController = AbrController as any\r\n this.hlsjsConfig.abrBandWidthUpFactor = 0.3\r\n\r\n //if(!data.details.live && data.details.totalduration )\r\n\r\n //this.hlsjsConfig.debug = true\r\n\r\n this.hls = new Hlsjs(this.hlsjsConfig)\r\n\r\n \r\n\r\n //@ts-ignore\r\n this.player.hls = this.hls;\r\n //(this.player as any).hls = this.hls;\r\n\r\n //this._executeHooksFor('beforeinitialize')\r\n\r\n this.hls.on(Hlsjs.Events.ERROR, (event, data) => this._onError(event, data))\r\n this.hls.on(Hlsjs.Events.MANIFEST_PARSED, (event, data) => this._onMetaData(event, data))\r\n this.hls.on(Hlsjs.Events.LEVEL_LOADED, (event, data) => {\r\n // The DVR plugin will auto seek to \"live edge\" on start up\r\n if (this.hlsjsConfig.liveSyncDuration) {\r\n this.edgeMargin = this.hlsjsConfig.liveSyncDuration\r\n } else if (this.hlsjsConfig.liveSyncDurationCount) {\r\n this.edgeMargin = this.hlsjsConfig.liveSyncDurationCount * data.details.targetduration\r\n }\r\n\r\n this.isLive = data.details.live\r\n this.dvrDuration = data.details.totalduration\r\n\r\n this._duration = this.isLive ? Infinity : data.details.totalduration\r\n\r\n this.player.duration(Math.round(this._duration))\r\n\r\n //if(this._duration < 60){\r\n //this.player.peertubeResolutions().disableAutoResolution()\r\n //}\r\n\r\n // Increase network error recovery for lives since they can be broken (server restart, stream interruption etc)\r\n if (this.isLive) this.maxNetworkErrorRecovery = 300\r\n })\r\n\r\n this.hls.once(Hlsjs.Events.FRAG_LOADED, () => {\r\n // Emit custom 'loadedmetadata' event for parity with `videojs-contrib-hls`\r\n // Ref: https://github.com/videojs/videojs-contrib-hls#loadedmetadata\r\n this.tech.trigger('loadedmetadata')\r\n })\r\n\r\n this.hls.on(Hlsjs.Events.LEVEL_SWITCHING, (_e, data: LevelSwitchingData) => {\r\n\r\n const resolutionId = this.hls.autoLevelEnabled\r\n ? -1\r\n : data.level\r\n\r\n const autoResolutionChosenId = this.hls.autoLevelEnabled\r\n ? data.level\r\n : -1\r\n\r\n this.player.peertubeResolutions().select({ id: resolutionId, autoResolutionChosenId, byEngine: true })\r\n })\r\n\r\n this.hls.attachMedia(this.videoElement)\r\n\r\n this.hls.loadSource(this.source.src)\r\n }\r\n\r\n private initialize () {\r\n this._initHlsjs()\r\n }\r\n}\r\n\r\nexport {\r\n Html5Hlsjs,\r\n registerSourceHandler,\r\n registerConfigPlugin\r\n}\r\n","import Hlsjs from 'hls.js'\r\nimport videojs from 'video.js'\r\nimport { Events, Segment } from 'p2p-media-loader-core-basyton'\r\nimport { Engine, initHlsJsPlayer, initVideoJsContribHlsJsPlayer } from 'p2p-media-loader-hlsjs-basyton'\r\nimport { timeToInt } from '@shared/core-utils'\r\nimport { P2PMediaLoaderPluginOptions, PlayerNetworkInfo } from '../../types'\r\nimport { registerConfigPlugin, registerSourceHandler } from './hls-plugin'\r\nimport { logger } from '@root-helpers/logger'\r\n\r\nregisterConfigPlugin(videojs)\r\nregisterSourceHandler(videojs)\r\n\r\nconst Plugin = videojs.getPlugin('plugin')\r\nclass P2pMediaLoaderPlugin extends Plugin {\r\n\r\n private readonly CONSTANTS = {\r\n INFO_SCHEDULER: 1000 // Don't change this\r\n }\r\n private readonly options: P2PMediaLoaderPluginOptions\r\n\r\n private hlsjs: Hlsjs\r\n private p2pEngine: Engine\r\n private statsP2PBytes = {\r\n pendingDownload: [] as number[],\r\n pendingUpload: [] as number[],\r\n numPeers: 0,\r\n totalDownload: 0,\r\n totalUpload: 0\r\n }\r\n private statsHTTPBytes = {\r\n pendingDownload: [] as number[],\r\n pendingUpload: [] as number[],\r\n totalDownload: 0,\r\n totalUpload: 0\r\n }\r\n private startTime: number\r\n\r\n private networkInfoInterval: any\r\n\r\n constructor (player: videojs.Player, options?: P2PMediaLoaderPluginOptions | any) {\r\n super(player)\r\n\r\n this.options = options\r\n\r\n if(!this.options) return\r\n\r\n\r\n // FIXME: typings https://github.com/Microsoft/TypeScript/issues/14080\r\n if (!(videojs as any).Html5Hlsjs) {\r\n logger.warn('HLS.js does not seem to be supported. Try to fallback to built in HLS.')\r\n\r\n if (!player.canPlayType('application/vnd.apple.mpegurl')) {\r\n const message = 'Cannot fallback to built-in HLS'\r\n logger.warn(message)\r\n\r\n player.ready(() => player.trigger('error', new Error(message)))\r\n return\r\n }\r\n } else {\r\n \r\n\r\n initVideoJsContribHlsJsPlayer(player)\r\n }\r\n\r\n this.startTime = timeToInt(this.options.startTime)\r\n\r\n player.src({\r\n type: this.options.type,\r\n src: this.options.src\r\n })\r\n\r\n player.ready(() => {\r\n this.initializeCore()\r\n\r\n\r\n this.hlsjs = (player as any).hls\r\n\r\n if ((videojs as any).Html5Hlsjs) {\r\n this.initializePlugin()\r\n }\r\n })\r\n }\r\n\r\n dispose () {\r\n\r\n if (this.hlsjs) this.hlsjs.destroy()\r\n if (this.p2pEngine) this.p2pEngine.destroy()\r\n\r\n clearInterval(this.networkInfoInterval)\r\n }\r\n\r\n getCurrentLevel () {\r\n return this.hlsjs.levels[this.hlsjs.currentLevel]\r\n }\r\n\r\n getLiveLatency () {\r\n return Math.round(this.hlsjs.latency)\r\n }\r\n\r\n getHLSJS () {\r\n return this.hlsjs\r\n }\r\n\r\n private initializeCore () {\r\n this.player.one('play', () => {\r\n this.player.addClass('vjs-has-big-play-button-clicked')\r\n })\r\n\r\n this.player.one('canplay', () => {\r\n if (this.startTime) {\r\n this.player.currentTime(this.startTime)\r\n }\r\n })\r\n }\r\n\r\n private initializePlugin () {\r\n initHlsJsPlayer(this.hlsjs)\r\n\r\n this.p2pEngine = this.options.loader.getEngine()\r\n\r\n this.p2pEngine.on(Events.SegmentError, (segment: Segment, err) => {\r\n logger.error(`Segment ${segment.id} error.`, err)\r\n\r\n if(segment.requestUrl)\r\n this.options.redundancyUrlManager.removeBySegmentUrl(segment.requestUrl)\r\n })\r\n\r\n this.statsP2PBytes.numPeers = 1 + this.options.redundancyUrlManager.countBaseUrls()\r\n\r\n this.runStats()\r\n }\r\n\r\n private runStats () {\r\n this.p2pEngine.on(Events.PieceBytesDownloaded, (method: string, _segment, bytes: number) => {\r\n const elem = method === 'p2p' ? this.statsP2PBytes : this.statsHTTPBytes\r\n\r\n elem.pendingDownload.push(bytes)\r\n elem.totalDownload += bytes\r\n })\r\n\r\n this.p2pEngine.on(Events.PieceBytesUploaded, (method: string, _segment, bytes: number) => {\r\n const elem = method === 'p2p' ? this.statsP2PBytes : this.statsHTTPBytes\r\n\r\n elem.pendingUpload.push(bytes)\r\n elem.totalUpload += bytes\r\n })\r\n\r\n this.p2pEngine.on(Events.PeerConnect, () => this.statsP2PBytes.numPeers++)\r\n this.p2pEngine.on(Events.PeerClose, () => this.statsP2PBytes.numPeers--)\r\n\r\n this.networkInfoInterval = setInterval(() => {\r\n const p2pDownloadSpeed = this.arraySum(this.statsP2PBytes.pendingDownload)\r\n const p2pUploadSpeed = this.arraySum(this.statsP2PBytes.pendingUpload)\r\n\r\n const httpDownloadSpeed = this.arraySum(this.statsHTTPBytes.pendingDownload)\r\n const httpUploadSpeed = this.arraySum(this.statsHTTPBytes.pendingUpload)\r\n\r\n this.statsP2PBytes.pendingDownload = []\r\n this.statsP2PBytes.pendingUpload = []\r\n this.statsHTTPBytes.pendingDownload = []\r\n this.statsHTTPBytes.pendingUpload = []\r\n\r\n return this.player.trigger('p2pInfo', {\r\n source: 'p2p-media-loader',\r\n http: {\r\n downloadSpeed: httpDownloadSpeed,\r\n uploadSpeed: httpUploadSpeed,\r\n downloaded: this.statsHTTPBytes.totalDownload,\r\n uploaded: this.statsHTTPBytes.totalUpload\r\n },\r\n p2p: {\r\n downloadSpeed: p2pDownloadSpeed,\r\n uploadSpeed: p2pUploadSpeed,\r\n numPeers: this.statsP2PBytes.numPeers,\r\n downloaded: this.statsP2PBytes.totalDownload,\r\n uploaded: this.statsP2PBytes.totalUpload\r\n },\r\n bandwidthEstimate: (this.hlsjs as any).bandwidthEstimate / 8\r\n } as PlayerNetworkInfo)\r\n }, this.CONSTANTS.INFO_SCHEDULER)\r\n }\r\n\r\n private arraySum (data: number[]) {\r\n return data.reduce((a: number, b: number) => a + b, 0)\r\n }\r\n}\r\n\r\nvideojs.registerPlugin('p2pMediaLoader', P2pMediaLoaderPlugin)\r\nexport { P2pMediaLoaderPlugin }\r\n","import '@peertube/videojs-contextmenu'\r\nimport './shared/upnext/end-card'\r\nimport './shared/upnext/upnext-plugin'\r\nimport './shared/stats/stats-card'\r\nimport './shared/stats/stats-plugin'\r\nimport './shared/bezels/bezels-plugin'\r\nimport './shared/peertube/peertube-plugin'\r\nimport './shared/resolutions/peertube-resolutions-plugin'\r\nimport './shared/control-bar/next-previous-video-button'\r\nimport './shared/control-bar/p2p-info-button'\r\n//import './shared/control-bar/peertube-link-button'\r\nimport './shared/control-bar/picture-in-picture-bastyon'\r\nimport './shared/control-bar/peertube-load-progress-bar'\r\nimport './shared/control-bar/theater-button'\r\nimport './shared/settings/resolution-menu-button'\r\nimport './shared/settings/resolution-menu-item'\r\nimport './shared/settings/settings-dialog'\r\nimport './shared/settings/settings-menu-button'\r\nimport './shared/settings/settings-menu-item'\r\nimport './shared/settings/settings-panel'\r\nimport './shared/settings/settings-panel-child'\r\nimport './shared/playlist/playlist-plugin'\r\nimport './shared/mobile/peertube-mobile-plugin'\r\nimport './shared/mobile/peertube-mobile-buttons'\r\nimport './shared/hotkeys/peertube-hotkeys-plugin'\r\nimport \"./shared/videojs-helpers/hotkeys.js\";\r\n\r\nimport videojs from 'video.js'\r\nimport { logger } from '@root-helpers/logger'\r\nimport { isMobile } from '@root-helpers/web-browser'\r\nimport { saveAverageBandwidth } from './peertube-player-local-storage'\r\nimport { ManagerOptionsBuilder } from './shared/manager-options'\r\nimport { TranslationsManager } from './translations-manager'\r\nimport { CommonOptions, PeertubePlayerManagerOptions, PlayerMode, PlayerNetworkInfo } from './types'\r\nimport './shared/p2p-media-loader/p2p-media-loader-plugin'\r\nimport * as p2pMediaLoaderModule from 'p2p-media-loader-hlsjs-basyton'\r\n\r\n\r\n\r\nconst Fn: any = require('./shared/videojs-helpers/fn.js');\r\n\r\n\r\nconst Slider = videojs.getComponent('Slider') as any\r\nconst SeekBar = videojs.getComponent('SeekBar') as any\r\n\r\nSlider.prototype.update = function(){\r\n\r\n // In VolumeBar init we have a setTimeout for update that pops and update\r\n // to the end of the execution stack. The player is destroyed before then\r\n // update will cause an error\r\n // If there's no bar...\r\n if (!this.el_ || !this.bar) {\r\n return;\r\n }\r\n\r\n // clamp progress between 0 and 1\r\n // and only round to four decimal places, as we round to two below\r\n const progress = this.getProgress();\r\n\r\n if (progress === this.progress_) {\r\n return progress;\r\n }\r\n\r\n this.progress_ = progress;\r\n\r\n // Set the new bar width or height\r\n var el = this.bar.el()\r\n\r\n if(!this.vertical()){\r\n //el.style['transform-origin'] = 'left'\r\n el.style['transform'] = 'scaleX('+(progress).toFixed(2)+')'\r\n }\r\n else{\r\n el.style['transform-origin'] = 'bottom'\r\n el.style['transform'] = 'scaleY('+(progress).toFixed(2)+')'\r\n }\r\n\r\n return progress;\r\n}\r\n\r\nSeekBar.prototype.getPercent = function getPercent () {\r\n const time = this.player_.currentTime()\r\n const percent = time / this.player_.duration()\r\n return percent >= 1 ? 1 : percent\r\n}\r\n\r\nSeekBar.prototype.setEventHandlers_ = function () {\r\n\r\n this.update_ = Fn.bind(this, this.update);\r\n this.update = Fn.throttle(this.update_, Fn.UPDATE_REFRESH_INTERVAL);\r\n\r\n this.on(this.player_, ['ended', 'durationchange', 'timeupdate'], this.update);\r\n if (this.player_.liveTracker) {\r\n this.on(this.player_.liveTracker, 'liveedgechange', this.update);\r\n }\r\n\r\n // when playing, let's ensure we smoothly update the play progress bar\r\n // via an interval\r\n this.updateInterval = null;\r\n\r\n this.enableIntervalHandler_ = (e :any) => this.enableInterval_(e);\r\n this.disableIntervalHandler_ = (e :any) => this.disableInterval_(e);\r\n\r\n this.on(this.player_, ['playing'], this.enableIntervalHandler_);\r\n\r\n this.on(this.player_, ['ended', 'pause', 'waiting'], this.disableIntervalHandler_);\r\n\r\n // we don't need to update the play progress if the document is hidden,\r\n // also, this causes the CPU to spike and eventually crash the page on IE11.\r\n if ('hidden' in document && 'visibilityState' in document) {\r\n this.on(document, 'visibilitychange', this.toggleVisibility_);\r\n }\r\n}\r\n\r\nSeekBar.prototype.enableInterval_ = function() {\r\n if (this.updateInterval) {\r\n return;\r\n\r\n }\r\n this.updateInterval = this.setInterval(this.update, Fn.UPDATE_REFRESH_INTERVAL);\r\n}\r\n\r\nSeekBar.prototype.update = function (event : any) {\r\n if (document.visibilityState === 'hidden') {\r\n return;\r\n }\r\n\r\n const percent = this.getPercent();\r\n\r\n var el = this.bar.el()\r\n\r\n el.style['transform-origin'] = 'left'\r\n el.style['transform'] = 'scaleX('+(percent).toFixed(2)+')'\r\n\r\n return percent;\r\n \r\n}\r\n\r\nconst CaptionsButton = videojs.getComponent('CaptionsButton') as any\r\n// Change Captions to Subtitles/CC\r\nCaptionsButton.prototype.controlText_ = 'Subtitles/CC'\r\n// We just want to display 'Off' instead of 'captions off', keep a space so the variable == true (hacky I know)\r\nCaptionsButton.prototype.label_ = ' '\r\n\r\nexport class PeertubePlayerManager {\r\n private static playerElementClassName: string\r\n private static onPlayerChange: (player: videojs.Player) => void\r\n private static alreadyPlayed = false\r\n //private static pluginsManager: PluginsManager\r\n\r\n private static videojsDecodeErrors = 0\r\n\r\n\r\n private static p2pMediaLoaderModule: any\r\n\r\n static initState () {\r\n this.alreadyPlayed = false\r\n }\r\n\r\n static async initialize (mode: PlayerMode, options: PeertubePlayerManagerOptions, onPlayerChange: (player: videojs.Player) => void) {\r\n //this.pluginsManager = options.pluginsManager\r\n\r\n this.onPlayerChange = onPlayerChange\r\n this.playerElementClassName = options.common.playerElement.className\r\n\r\n //if (mode === 'webtorrent') await import('./shared/webtorrent/webtorrent-plugin')\r\n\r\n\r\n if (mode === 'p2p-media-loader') {\r\n this.p2pMediaLoaderModule = p2pMediaLoaderModule\r\n }\r\n\r\n\r\n return this.buildPlayer(mode, options)\r\n }\r\n\r\n private static async buildPlayer (mode: PlayerMode, options: PeertubePlayerManagerOptions): Promise {\r\n const videojsOptionsBuilder = new ManagerOptionsBuilder(mode, options, this.p2pMediaLoaderModule)\r\n const videojsOptions = videojsOptionsBuilder.getVideojsOptions(this.alreadyPlayed)\r\n\r\n /*const videojsOptions = await this.pluginsManager.runHook(\r\n 'filter:internal.player.videojs.options.result',\r\n videojsOptionsBuilder.getVideojsOptions(this.alreadyPlayed)\r\n )*/\r\n\r\n const self = this\r\n return new Promise(res => {\r\n videojs(options.common.playerElement, videojsOptions, function (this: videojs.Player) {\r\n const player = this\r\n\r\n //let alreadyFallback = false\r\n\r\n const handleError = () => {\r\n //if (alreadyFallback) return\r\n //alreadyFallback = true\r\n\r\n if (mode === 'p2p-media-loader') {\r\n //self.tryToRecoverHLSError(player.error(), player, options)\r\n } else {\r\n /// remove torrent /// self.maybeFallbackToWebTorrent(mode, player, options)\r\n }\r\n }\r\n\r\n player.one('error', () => handleError())\r\n\r\n player.one('play', () => {\r\n self.alreadyPlayed = true\r\n })\r\n\r\n self.addContextMenu(videojsOptionsBuilder, player, options.common)\r\n\r\n if (isMobile() || options.mobile) player.peertubeMobile()\r\n //if (options.common.enableHotkeys === true) player.peerTubeHotkeysPlugin()\r\n if (options.common.controlBar === false) player.controlBar.addClass('control-bar-hidden')\r\n\r\n player.bezels()\r\n\r\n if(mode != 'localvideo'){\r\n \r\n player.stats({\r\n videoUUID: options.common.videoUUID,\r\n videoIsLive: options.common.isLive,\r\n mode,\r\n p2pEnabled: options.common.p2pEnabled\r\n })\r\n\r\n player.on('p2pInfo', (_, data: PlayerNetworkInfo) => {\r\n if (data.source !== 'p2p-media-loader' || isNaN(data.bandwidthEstimate)) return\r\n\r\n saveAverageBandwidth(data.bandwidthEstimate)\r\n })\r\n\r\n }\r\n else{\r\n player.on('durationchange', () => {\r\n console.log(\"?????????????\")\r\n if(player.duration() != options.common.videoDuration)\r\n player.duration(options.common.videoDuration)\r\n })\r\n\r\n }\r\n\r\n return res(player)\r\n })\r\n })\r\n }\r\n\r\n private static async tryToRecoverHLSError (err: any, currentPlayer: videojs.Player, options: PeertubePlayerManagerOptions) {\r\n if (err.code === 3) { // Decode error\r\n\r\n // Display a notification to user\r\n if (this.videojsDecodeErrors === 0) {\r\n options.common.errorNotifier(currentPlayer.localize('The video failed to play, will try to fast forward.'))\r\n }\r\n\r\n if (this.videojsDecodeErrors === 20) {\r\n this.maybeFallbackToWebTorrent('p2p-media-loader', currentPlayer, options)\r\n return\r\n }\r\n\r\n logger.info('Fast forwarding HLS to recover from an error.')\r\n\r\n this.videojsDecodeErrors++\r\n\r\n options.common.startTime = currentPlayer.currentTime() + 2\r\n options.common.autoplay = true\r\n this.rebuildAndUpdateVideoElement(currentPlayer, options.common)\r\n\r\n const newPlayer = await this.buildPlayer('p2p-media-loader', options)\r\n this.onPlayerChange(newPlayer)\r\n } else {\r\n this.maybeFallbackToWebTorrent('p2p-media-loader', currentPlayer, options)\r\n }\r\n }\r\n\r\n private static async maybeFallbackToWebTorrent (\r\n currentMode: PlayerMode,\r\n currentPlayer: videojs.Player,\r\n options: PeertubePlayerManagerOptions\r\n ) {\r\n\r\n if (options.webtorrent.videoFiles.length === 0 || currentMode === 'webtorrent') {\r\n currentPlayer.peertube().displayFatalError()\r\n return\r\n }\r\n\r\n logger.info('Fallback to webtorrent.')\r\n\r\n this.rebuildAndUpdateVideoElement(currentPlayer, options.common)\r\n\r\n //await import('./shared/webtorrent/webtorrent-plugin')\r\n\r\n const newPlayer = await this.buildPlayer('webtorrent', options)\r\n this.onPlayerChange(newPlayer)\r\n }\r\n\r\n private static rebuildAndUpdateVideoElement (player: videojs.Player, commonOptions: CommonOptions) {\r\n const newVideoElement = document.createElement('video')\r\n newVideoElement.className = this.playerElementClassName\r\n\r\n // VideoJS wraps our video element inside a div\r\n let currentParentPlayerElement = commonOptions.playerElement.parentNode\r\n // Fix on IOS, don't ask me why\r\n if (!currentParentPlayerElement) currentParentPlayerElement = document.getElementById(commonOptions.playerElement.id).parentNode\r\n\r\n currentParentPlayerElement.parentNode.insertBefore(newVideoElement, currentParentPlayerElement)\r\n\r\n commonOptions.playerElement = newVideoElement\r\n commonOptions.onPlayerElementChange(newVideoElement)\r\n\r\n player.dispose()\r\n\r\n return newVideoElement\r\n }\r\n\r\n private static addContextMenu (optionsBuilder: ManagerOptionsBuilder, player: videojs.Player, commonOptions: CommonOptions) {\r\n const options = optionsBuilder.getContextMenuOptions(player, commonOptions)\r\n\r\n player.contextmenuUI(options)\r\n }\r\n}\r\n\r\n// ############################################################################\r\n\r\nexport {\r\n videojs\r\n}\r\n","module.exports = require('path-browserify')\r\n","/**\r\n * @file guid.js\r\n * @module guid\r\n */\r\n\r\n// Default value for GUIDs. This allows us to reset the GUID counter in tests.\r\n//\r\n// The initial GUID is 3 because some users have come to rely on the first\r\n// default player ID ending up as `vjs_video_3`.\r\n//\r\n// See: https://github.com/videojs/video.js/pull/6216\r\nconst _initialGuid = 3;\r\n\r\n/**\r\n * Unique ID for an element or function\r\n *\r\n * @type {Number}\r\n */\r\nlet _guid = _initialGuid;\r\n\r\n/**\r\n * Get a unique auto-incrementing ID by number that has not been returned before.\r\n *\r\n * @return {number}\r\n * A new unique ID.\r\n */\r\nexport function newGUID() {\r\n return _guid++;\r\n}\r\n\r\n/**\r\n * Reset the unique auto-incrementing ID for testing only.\r\n */\r\nexport function resetGuidInTestsOnly() {\r\n _guid = _initialGuid;\r\n}","\r\n/**\r\n * @file fn.js\r\n * @module fn\r\n */\r\n import { newGUID } from './guid.js';\r\n import window from 'global/window';\r\n \r\n export const UPDATE_REFRESH_INTERVAL = 30;\r\n \r\n /**\r\n * Bind (a.k.a proxy or context). A simple method for changing the context of\r\n * a function.\r\n *\r\n * It also stores a unique id on the function so it can be easily removed from\r\n * events.\r\n *\r\n * @function\r\n * @param {Mixed} context\r\n * The object to bind as scope.\r\n *\r\n * @param {Function} fn\r\n * The function to be bound to a scope.\r\n *\r\n * @param {number} [uid]\r\n * An optional unique ID for the function to be set\r\n *\r\n * @return {Function}\r\n * The new function that will be bound into the context given\r\n */\r\n export const bind = function(context, fn, uid) {\r\n // Make sure the function has a unique ID\r\n if (!fn.guid) {\r\n fn.guid = newGUID();\r\n }\r\n \r\n // Create the new function that changes the context\r\n const bound = fn.bind(context);\r\n \r\n // Allow for the ability to individualize this function\r\n // Needed in the case where multiple objects might share the same prototype\r\n // IF both items add an event listener with the same function, then you try to remove just one\r\n // it will remove both because they both have the same guid.\r\n // when using this, you need to use the bind method when you remove the listener as well.\r\n // currently used in text tracks\r\n bound.guid = (uid) ? uid + '_' + fn.guid : fn.guid;\r\n \r\n return bound;\r\n };\r\n \r\n /**\r\n * Wraps the given function, `fn`, with a new function that only invokes `fn`\r\n * at most once per every `wait` milliseconds.\r\n *\r\n * @function\r\n * @param {Function} fn\r\n * The function to be throttled.\r\n *\r\n * @param {number} wait\r\n * The number of milliseconds by which to throttle.\r\n *\r\n * @return {Function}\r\n */\r\n export const throttle = function(fn, wait) {\r\n let last = window.performance.now();\r\n \r\n const throttled = function(...args) {\r\n const now = window.performance.now();\r\n \r\n if (now - last >= wait) {\r\n fn(...args);\r\n last = now;\r\n }\r\n };\r\n \r\n return throttled;\r\n };\r\n \r\n /**\r\n * Creates a debounced function that delays invoking `func` until after `wait`\r\n * milliseconds have elapsed since the last time the debounced function was\r\n * invoked.\r\n *\r\n * Inspired by lodash and underscore implementations.\r\n *\r\n * @function\r\n * @param {Function} func\r\n * The function to wrap with debounce behavior.\r\n *\r\n * @param {number} wait\r\n * The number of milliseconds to wait after the last invocation.\r\n *\r\n * @param {boolean} [immediate]\r\n * Whether or not to invoke the function immediately upon creation.\r\n *\r\n * @param {Object} [context=window]\r\n * The \"context\" in which the debounced function should debounce. For\r\n * example, if this function should be tied to a Video.js player,\r\n * the player can be passed here. Alternatively, defaults to the\r\n * global `window` object.\r\n *\r\n * @return {Function}\r\n * A debounced function.\r\n */\r\n export const debounce = function(func, wait, immediate, context = window) {\r\n let timeout;\r\n \r\n const cancel = () => {\r\n context.clearTimeout(timeout);\r\n timeout = null;\r\n };\r\n \r\n /* eslint-disable consistent-this */\r\n const debounced = function() {\r\n const self = this;\r\n const args = arguments;\r\n \r\n let later = function() {\r\n timeout = null;\r\n later = null;\r\n if (!immediate) {\r\n func.apply(self, args);\r\n }\r\n };\r\n \r\n if (!timeout && immediate) {\r\n func.apply(self, args);\r\n }\r\n \r\n context.clearTimeout(timeout);\r\n timeout = context.setTimeout(later, wait);\r\n };\r\n /* eslint-enable consistent-this */\r\n \r\n debounced.cancel = cancel;\r\n \r\n return debounced;\r\n };","/*\r\n * Video.js Hotkeys\r\n * https://github.com/ctd1500/videojs-hotkeys\r\n *\r\n * Copyright (c) 2015 Chris Dougherty\r\n * Licensed under the Apache-2.0 license.\r\n */\r\n\r\n;(function(root, factory) {\r\n if (typeof window !== 'undefined' && window.videojs) {\r\n factory(window.videojs);\r\n } else if (typeof define === 'function' && define.amd) {\r\n define('videojs-hotkeys', ['video.js'], function (module) {\r\n return factory(module.default || module);\r\n });\r\n } else if (typeof module !== 'undefined' && module.exports) {\r\n module.exports = factory(require('video.js'));\r\n }\r\n }(this, function (videojs) {\r\n \"use strict\";\r\n if (typeof window !== 'undefined') {\r\n window['videojs_hotkeys'] = { version: \"0.2.27\" };\r\n }\r\n\r\n \r\n var hotkeys = function(options) {\r\n var player = this;\r\n var pEl = player.el();\r\n var doc = document;\r\n var def_options = {\r\n volumeStep: 0.1,\r\n seekStep: 5,\r\n enableMute: true,\r\n enableVolumeScroll: true,\r\n enableHoverScroll: false,\r\n enableFullscreen: true,\r\n enableNumbers: true,\r\n enableJogStyle: false,\r\n alwaysCaptureHotkeys: false,\r\n captureDocumentHotkeys: false,\r\n documentHotkeysFocusElementFilter: function () { return false },\r\n enableModifiersForNumbers: true,\r\n enableInactiveFocus: true,\r\n skipInitialFocus: false,\r\n playPauseKey: playPauseKey,\r\n rewindKey: rewindKey,\r\n forwardKey: forwardKey,\r\n volumeUpKey: volumeUpKey,\r\n volumeDownKey: volumeDownKey,\r\n muteKey: muteKey,\r\n fullscreenKey: fullscreenKey,\r\n customKeys: {}\r\n };\r\n \r\n var cPlay = 1,\r\n cRewind = 2,\r\n cForward = 3,\r\n cVolumeUp = 4,\r\n cVolumeDown = 5,\r\n cMute = 6,\r\n cFullscreen = 7;\r\n \r\n // Use built-in merge function from Video.js v5.0+ or v4.4.0+\r\n var mergeOptions = videojs.mergeOptions || videojs.util.mergeOptions;\r\n options = mergeOptions(def_options, options || {});\r\n \r\n var volumeStep = options.volumeStep,\r\n seekStep = options.seekStep,\r\n enableMute = options.enableMute,\r\n enableVolumeScroll = options.enableVolumeScroll,\r\n enableHoverScroll = options.enableHoverScroll,\r\n enableFull = options.enableFullscreen,\r\n enableNumbers = options.enableNumbers,\r\n enableJogStyle = options.enableJogStyle,\r\n alwaysCaptureHotkeys = options.alwaysCaptureHotkeys,\r\n captureDocumentHotkeys = options.captureDocumentHotkeys,\r\n documentHotkeysFocusElementFilter = options.documentHotkeysFocusElementFilter,\r\n enableModifiersForNumbers = options.enableModifiersForNumbers,\r\n enableInactiveFocus = options.enableInactiveFocus,\r\n skipInitialFocus = options.skipInitialFocus;\r\n \r\n var videojsVer = videojs.VERSION;\r\n \r\n // Set default player tabindex to handle keydown and doubleclick events\r\n if (!pEl.hasAttribute('tabIndex')) {\r\n pEl.setAttribute('tabIndex', '-1');\r\n }\r\n \r\n // Remove player outline to fix video performance issue\r\n pEl.style.outline = \"none\";\r\n \r\n if (alwaysCaptureHotkeys || !player.autoplay()) {\r\n if (!skipInitialFocus) {\r\n player.one('play', function() {\r\n pEl.focus(); // Fixes the .vjs-big-play-button handing focus back to body instead of the player\r\n });\r\n }\r\n }\r\n \r\n if (enableInactiveFocus) {\r\n player.on('userinactive', function() {\r\n // When the control bar fades, re-apply focus to the player if last focus was a control button\r\n var cancelFocusingPlayer = function() {\r\n clearTimeout(focusingPlayerTimeout);\r\n };\r\n var focusingPlayerTimeout = setTimeout(function() {\r\n player.off('useractive', cancelFocusingPlayer);\r\n var activeElement = doc.activeElement;\r\n var controlBar = pEl.querySelector('.vjs-control-bar');\r\n if (activeElement && activeElement.parentElement == controlBar) {\r\n pEl.focus();\r\n }\r\n }, 10);\r\n \r\n player.one('useractive', cancelFocusingPlayer);\r\n });\r\n }\r\n \r\n player.on('play', function() {\r\n // Fix allowing the YouTube plugin to have hotkey support.\r\n var ifblocker = pEl.querySelector('.iframeblocker');\r\n if (ifblocker && ifblocker.style.display === '') {\r\n ifblocker.style.display = \"block\";\r\n ifblocker.style.bottom = \"39px\";\r\n }\r\n });\r\n \r\n var keyDown = function keyDown(event) {\r\n var ewhich = event.which, wasPlaying, seekTime;\r\n var ePreventDefault = event.preventDefault.bind(event);\r\n var duration = player.duration();\r\n // When controls are disabled, hotkeys will be disabled as well\r\n if (player.controls()) {\r\n \r\n // Don't catch keys if any control buttons are focused, unless alwaysCaptureHotkeys is true\r\n var activeEl = doc.activeElement;\r\n if (\r\n alwaysCaptureHotkeys ||\r\n (captureDocumentHotkeys && documentHotkeysFocusElementFilter(activeEl)) ||\r\n \r\n activeEl == pEl ||\r\n activeEl == pEl.querySelector('.vjs-tech') ||\r\n activeEl == pEl.querySelector('.vjs-control-bar') ||\r\n activeEl == pEl.querySelector('.iframeblocker')\r\n ) {\r\n \r\n switch (checkKeys(event, player)) {\r\n // Spacebar toggles play/pause\r\n case cPlay:\r\n ePreventDefault();\r\n if (alwaysCaptureHotkeys || captureDocumentHotkeys) {\r\n // Prevent control activation with space\r\n event.stopPropagation();\r\n }\r\n \r\n if (player.paused()) {\r\n silencePromise(player.play());\r\n } else {\r\n player.pause();\r\n }\r\n break;\r\n \r\n // Seeking with the left/right arrow keys\r\n case cRewind: // Seek Backward\r\n wasPlaying = !player.paused();\r\n ePreventDefault();\r\n /*if (wasPlaying) {\r\n player.pause();\r\n }*/\r\n seekTime = player.currentTime() - seekStepD(event);\r\n // The flash player tech will allow you to seek into negative\r\n // numbers and break the seekbar, so try to prevent that.\r\n if (seekTime <= 0) {\r\n seekTime = 0;\r\n }\r\n\r\n player.currentTime(seekTime);\r\n /* if (wasPlaying) {\r\n silencePromise(player.play());\r\n }*/\r\n break;\r\n case cForward: // Seek Forward\r\n wasPlaying = !player.paused();\r\n ePreventDefault();\r\n /*if (wasPlaying) {\r\n player.pause();\r\n }*/\r\n seekTime = player.currentTime() + seekStepD(event);\r\n // Fixes the player not sending the end event if you\r\n // try to seek past the duration on the seekbar.\r\n if (seekTime >= duration) {\r\n seekTime = wasPlaying ? duration - .001 : duration;\r\n }\r\n player.currentTime(seekTime);\r\n /*if (wasPlaying) {\r\n silencePromise(player.play());\r\n }*/\r\n break;\r\n \r\n // Volume control with the up/down arrow keys\r\n case cVolumeDown:\r\n ePreventDefault();\r\n if (!enableJogStyle) {\r\n player.volume(player.volume() - volumeStep);\r\n } else {\r\n seekTime = player.currentTime() - 1;\r\n if (player.currentTime() <= 1) {\r\n seekTime = 0;\r\n }\r\n player.currentTime(seekTime);\r\n }\r\n break;\r\n case cVolumeUp:\r\n ePreventDefault();\r\n if (!enableJogStyle) {\r\n player.volume(player.volume() + volumeStep);\r\n } else {\r\n seekTime = player.currentTime() + 1;\r\n if (seekTime >= duration) {\r\n seekTime = duration;\r\n }\r\n player.currentTime(seekTime);\r\n }\r\n break;\r\n \r\n // Toggle Mute with the M key\r\n case cMute:\r\n if (enableMute) {\r\n player.muted(!player.muted());\r\n }\r\n break;\r\n \r\n // Toggle Fullscreen with the F key\r\n case cFullscreen:\r\n if (enableFull) {\r\n if (player.isFullscreen()) {\r\n player.exitFullscreen();\r\n } else {\r\n player.requestFullscreen();\r\n }\r\n }\r\n break;\r\n \r\n default:\r\n // Number keys from 0-9 skip to a percentage of the video. 0 is 0% and 9 is 90%\r\n if ((ewhich > 47 && ewhich < 59) || (ewhich > 95 && ewhich < 106)) {\r\n // Do not handle if enableModifiersForNumbers set to false and keys are Ctrl, Cmd or Alt\r\n if (enableModifiersForNumbers || !(event.metaKey || event.ctrlKey || event.altKey)) {\r\n if (enableNumbers) {\r\n var sub = 48;\r\n if (ewhich > 95) {\r\n sub = 96;\r\n }\r\n var number = ewhich - sub;\r\n ePreventDefault();\r\n player.currentTime(player.duration() * number * 0.1);\r\n }\r\n }\r\n }\r\n \r\n // Handle any custom hotkeys\r\n for (var customKey in options.customKeys) {\r\n var customHotkey = options.customKeys[customKey];\r\n // Check for well formed custom keys\r\n if (customHotkey && customHotkey.key && customHotkey.handler) {\r\n // Check if the custom key's condition matches\r\n if (customHotkey.key(event)) {\r\n ePreventDefault();\r\n customHotkey.handler(player, options, event);\r\n }\r\n }\r\n }\r\n }\r\n }\r\n }\r\n };\r\n \r\n var doubleClick = function doubleClick(event) {\r\n // Video.js added double-click fullscreen in 7.1.0\r\n if (videojsVer != null && videojsVer <= \"7.1.0\") {\r\n // When controls are disabled, hotkeys will be disabled as well\r\n if (player.controls()) {\r\n \r\n // Don't catch clicks if any control buttons are focused\r\n var activeEl = event.relatedTarget || event.toElement || doc.activeElement;\r\n if (activeEl == pEl ||\r\n activeEl == pEl.querySelector('.vjs-tech') ||\r\n activeEl == pEl.querySelector('.iframeblocker')) {\r\n \r\n if (enableFull) {\r\n if (player.isFullscreen()) {\r\n player.exitFullscreen();\r\n } else {\r\n player.requestFullscreen();\r\n }\r\n }\r\n }\r\n }\r\n }\r\n };\r\n \r\n var volumeHover = false;\r\n var volumeSelector = pEl.querySelector('.vjs-volume-menu-button') || pEl.querySelector('.vjs-volume-panel');\r\n if (volumeSelector != null) {\r\n volumeSelector.onmouseover = function() { volumeHover = true; };\r\n volumeSelector.onmouseout = function() { volumeHover = false; };\r\n }\r\n \r\n var mouseScroll = function mouseScroll(event) {\r\n if (enableHoverScroll) {\r\n // If we leave this undefined then it can match non-existent elements below\r\n var activeEl = 0;\r\n } else {\r\n var activeEl = doc.activeElement;\r\n }\r\n \r\n // When controls are disabled, hotkeys will be disabled as well\r\n if (player.controls()) {\r\n if (alwaysCaptureHotkeys ||\r\n activeEl == pEl ||\r\n activeEl == pEl.querySelector('.vjs-tech') ||\r\n activeEl == pEl.querySelector('.iframeblocker') ||\r\n activeEl == pEl.querySelector('.vjs-control-bar') ||\r\n volumeHover) {\r\n \r\n if (enableVolumeScroll) {\r\n event = window.event || event;\r\n var delta = Math.max(-1, Math.min(1, (event.wheelDelta || -event.detail)));\r\n event.preventDefault();\r\n \r\n if (delta == 1) {\r\n player.volume(player.volume() + volumeStep);\r\n } else if (delta == -1) {\r\n player.volume(player.volume() - volumeStep);\r\n }\r\n }\r\n }\r\n }\r\n };\r\n \r\n var checkKeys = function checkKeys(e, player) {\r\n // Allow some modularity in defining custom hotkeys\r\n \r\n // Play/Pause check\r\n if (options.playPauseKey(e, player)) {\r\n return cPlay;\r\n }\r\n \r\n // Seek Backward check\r\n if (options.rewindKey(e, player)) {\r\n return cRewind;\r\n }\r\n \r\n // Seek Forward check\r\n if (options.forwardKey(e, player)) {\r\n return cForward;\r\n }\r\n \r\n // Volume Up check\r\n if (options.volumeUpKey(e, player)) {\r\n return cVolumeUp;\r\n }\r\n \r\n // Volume Down check\r\n if (options.volumeDownKey(e, player)) {\r\n return cVolumeDown;\r\n }\r\n \r\n // Mute check\r\n if (options.muteKey(e, player)) {\r\n return cMute;\r\n }\r\n \r\n // Fullscreen check\r\n if (options.fullscreenKey(e, player)) {\r\n return cFullscreen;\r\n }\r\n };\r\n \r\n function playPauseKey(e) {\r\n // Space bar or MediaPlayPause\r\n return (e.which === 32 || e.which === 179);\r\n }\r\n \r\n function rewindKey(e) {\r\n // Left Arrow or MediaRewind\r\n return (e.which === 37 || e.which === 177);\r\n }\r\n \r\n function forwardKey(e) {\r\n // Right Arrow or MediaForward\r\n return (e.which === 39 || e.which === 176);\r\n }\r\n \r\n function volumeUpKey(e) {\r\n // Up Arrow\r\n return (e.which === 38);\r\n }\r\n \r\n function volumeDownKey(e) {\r\n // Down Arrow\r\n return (e.which === 40);\r\n }\r\n \r\n function muteKey(e) {\r\n // M key\r\n return (e.which === 77);\r\n }\r\n \r\n function fullscreenKey(e) {\r\n // F key\r\n return (e.which === 70);\r\n }\r\n \r\n function seekStepD(e) {\r\n // SeekStep caller, returns an int, or a function returning an int\r\n return (typeof seekStep === \"function\" ? seekStep(e) : seekStep);\r\n }\r\n \r\n function silencePromise(value) {\r\n if (value != null && typeof value.then === 'function') {\r\n value.then(null, function(e) {});\r\n }\r\n }\r\n\r\n\r\n var active = false\r\n\r\n\r\n player.on('enablehotkeys', function(){\r\n\r\n\r\n player.on('keydown', keyDown);\r\n player.on('dblclick', doubleClick);\r\n player.on('mousewheel', mouseScroll);\r\n player.on(\"DOMMouseScroll\", mouseScroll);\r\n \r\n if (captureDocumentHotkeys) {\r\n document.addEventListener('keydown', keyDown);\r\n }\r\n\r\n active = true\r\n })\r\n\r\n var off = function(){\r\n\r\n if(active){\r\n player.off('keydown', keyDown);\r\n player.off('dblclick', doubleClick);\r\n player.off('mousewheel', mouseScroll);\r\n player.off(\"DOMMouseScroll\", mouseScroll);\r\n \r\n if (captureDocumentHotkeys) {\r\n document.removeEventListener('keydown', keyDown);\r\n }\r\n }\r\n\r\n active = false\r\n \r\n }\r\n \r\n player.on('disablehotkeys', off)\r\n player.on('dispose', function(){\r\n\r\n off()\r\n\r\n player = null\r\n pEl = null\r\n })\r\n \r\n return this;\r\n };\r\n \r\n var registerPlugin = videojs.registerPlugin || videojs.plugin;\r\n registerPlugin('hotkeys', hotkeys);\r\n }));"],"names":["EndCard","player","options","dashOffsetTotal","dashOffsetStart","interval","upNextEvents","videojs","ticks","totalTicks","options_","timeout","on","_","condition","addClass","showCard","canceled","removeClass","container","style","display","next","trigger","className","innerHTML","this","headText","cancelText","suspendedText","autoplayRing","getElementsByClassName","title","cancelButton","suspendedMessage","nextButton","onclick","cb","setAttribute","getTitle","one","clearTimeout","goToPercent","percent","newOffset","Math","max","tick","update","suspended","innerText","setTimeout","bind","UpNextPlugin","settings","ready","addChild","timeToInt","time","matches","match","parseInt","secondsToTime","seconds","full","symbol","hourSymbol","minuteSymbol","secondsSymbol","hours","floor","minutes","pick","object","keys","result","key","Object","prototype","hasOwnProperty","call","buildVideoLink","video","base","window","location","origin","shortUUID","buildVideoWatchPath","I18N_LOCALE_ALIAS","getCompleteLocale","locale","concat","map","l","TEXT_RULES","TEXT_WITH_HTML_RULES","toTitleCase","str","charAt","toUpperCase","slice","dictionaryBytes","type","decimals","bytes","value","format","find","d","length","toFixed","StatsCard","metadataStore","intervalMs","playerNetworkInfo","containerEl","infoListEl","closeButton","tabindex","hide","appendChild","populateInfoBlocks","player_","event","data","mode","source","p2pStats","p2p","httpStats","http","downloadSpeed","join","uploadSpeed","totalDownloaded","downloaded","totalUploaded","uploaded","numPeers","averageBandwidth","bandwidthEstimate","downloadedFromServer","downloadedFromPeers","updateInterval","show","setInterval","buildHLSOptions","populateInfoValues","err","logger","clearInterval","progress","latency","p2pMediaLoader","level","getCurrentLevel","codecs","videoCodec","audioCodec","undefined","resolution","height","attrs","buffer","timeRangesToString","buffered","videoIsLive","getLiveLatency","bufferedPercent","playerMode","buildInfoRow","localize","uuid","viewport","volume","color","connection","network","transferred","download","bufferProgress","bufferState","liveLatency","root","colorSpace","videoQuality","getVideoPlaybackQuality","frames","document","documentElement","clientWidth","innerWidth","clientHeight","innerHeight","devicePixelRatio","droppedVideoFrames","totalVideoFrames","duration","round","muted","networkActivity","totalTransferred","downloadBreakdown","setInfoValue","p2pEnabled","videoUUID","el","labelText","valueHTML","label","r","i","start","end","StatsForNerdsPlugin","statsCard","isMobile","test","navigator","userAgent","PauseBezel","seeking","ended","showBezel","BezelsPlugin","KEY_PREFIX","getLocalStorage","localStorage","getItem","setLocalStorage","setItem","_a","debugLogger","debug","PeerTubePlugin","CONSTANTS","USER_VIEW_VIDEO_INTERVAL","menuOpened","mouseInControlBar","mouseInSettings","videoViewUrl","authorizationHeader","startTime","initialInactivityTimeout","inactivityTimeout","autoplay","playerOptions","valueNumber","parseFloat","isNaN","getStoredVolume","getStoredMute","defaultSubtitle","subtitle","toString","stopTime","self","onTimeUpdate","currentTime","pause","off","textTracks","addEventListener","showing","tracks_","t","kind","language","videoDuration","initializePlayer","runUserViewing","videoViewInterval","alterInactivity","listenControlBarMouse","listenFullScreenChange","lastViewEvent","lastCurrentTime","notifyUserIsWatching","abs","catch","JSON","stringify","parse","error","getStoredVideoWatchHistory","date","Date","toISOString","viewEvent","Promise","resolve","body","headers","Headers","set","fetch","method","e","isFullscreen","focus","controlBar","settingsButton","dialog","setInactivityTimeout","reportUserActivity","cache_","PeerTubeResolutionsPlugin","resolutions","autoResolutionEnabled","push","currentSelection","getSelected","sort","selected","id","autoResolutionChosenId","byEngine","selectCallback","a","b","NextPreviousVideoButton","nextPreviousVideoButtonOptions","button","nextIcon","handler","isDisabled","P2pInfoButton","div","subDivWebtorrent","downloadIcon","downloadSpeedText","downloadSpeedNumber","downloadSpeedUnit","uploadIcon","uploadSpeedText","uploadSpeedNumber","uploadSpeedUnit","peersText","peersNumber","subDivHttp","subDivHttpText","textContent","PictureInPictureBastyon","controlText","buildElement","classList","add","Component","registerComponent","PeerTubeLoadProgressBar","TheaterButton","enabled","getStoredTheater","THEATER_MODE_CLASS","handleTheaterChange","theaterEnabled","isTheaterEnabled","toggleClass","hasClass","ResolutionMenuItem","selectable","autoResolutionChosen","resolutionId","peertubeResolutions","updateSelection","updateAutoResolution","select","selectedResolution","getAutoResolutionChosen","isAutoResolutionEnabeld","Menu","ResolutionMenuButton","buildQualities","labelEl_","component","children","menu","child","getResolutions","m","addClickListener","SettingsDialog","uniqueId","dialogLabelId","dialogDescriptionId","tabIndex","role","MenuItem","SettingsMenuItem","menuButton","mainMenu","panel","getChild","panelChild","panelChildEl","size","menuToLoad","subMenuName","entry","SubMenuComponent","Error","newOptions","assign","subMenu","subMenuClass","buildCSSClass","split","settingsSubMenuEl_","eventHandlers","build","submenuClickHandler","bindClickEvents","reset","onSubmenuClick","transitionEndHandler","onTransitionEnd","target","currentTarget","contains","loadMainMenu","settingsSubMenuTitleEl_","settingsSubMenuValueEl_","opacity","marginRight","setDialogSize","firstChild","element","callback","action","prefix","p","toLowerCase","removeEventListener","propertyName","setMargin","mainMenuEl","mainMenuAny","width","setSize","createBackButton","PrefixedEvent","name","html","subMenuItem","children_","subMenuItemUntyped","getLabel","hideDialog","item","getComponentSize","contentElType","Button","SettingsButton","settingsButtonOptions","dialogEl","addSettingsItemHandler","onAddSettingsItem","disposeSettingsItemHandler","onDisposeSettingsItem","documentClickHandler","onDocumentClick","userInactiveHandler","onUserInactive","buildMenu","bindEvents","parentElement","_b","_c","dispose","removeChild","entries","isInIframe","addMenuItem","showDialog","peertube","onMenuOpened","onMenuClosed","resetChildren","offsetWidth","offsetHeight","offset","setup","maxHeightOffset","maxHeight","panelEl","settingsMenuItem","hideChildren","el_","menuChild","hideSubMenu","top","SettingsPanel","SettingsPanelChild","PlaylistButton","wrapper","icon","playlistInfoElement","getCurrentPosition","playlist","videosLength","displayName","getPlaylistMenu","open","playlistMenu","PlaylistMenuItem","emitTapEvents","switchPlaylistItem","handleKeyDown","li","positionBlock","position","buildAvailableVideo","buildUnavailableVideo","videoElement","thumbnail","src","thumbnailPath","infoBlock","channel","startTimestamp","stopTimestamp","timestamps","append","block","code","onClicked","PlaylistMenu","close","current","menuItems","getOptions","header","headerLeft","leftTitle","playlistChannel","videoChannel","leftSubtitle","list","playlistElement","elements","onItemClicked","updateSelected","newPosition","setSelected","getElement","PlaylistPlugin","playlistButton","PeerTubeMobilePlugin","seekAmount","peerTubeMobileButtons","reportTouchActivity","screen","orientation","handleFullscreenRotation","userActions","click","doubleClick","initTouchStartEvents","isPortraitVideo","lock","videoWidth","videoHeight","handleTouchStart","tapTimeout","lastTapEvent","timeStamp","DOUBLE_TAP_DELAY_MS","onDoubleTap","newActiveState","userActive","preventDefault","passive","playerWidth","currentWidth","rect","findPlayerTarget","getBoundingClientRect","offsetX","targetTouches","pageX","left","displayFastSeek","scheduleSetCurrentTime","setCurrentTimeTimeout","newTime","min","play","SET_CURRENT_TIME_DELAY","PeerTubeMobileButtons","mainButton","stopPropagation","paused","rewind","forward","rewindText","forwardText","amount","hideRewind","hideForward","displayForward","displayRewind","remove","PeerTubeHotkeysPlugin","handlers","buildHandlers","handleKeyFunction","onKeyDown","isValidKeyTarget","accept","isNaked","VOLUME_STEP","SEEK_STEP","altKey","ctrlKey","exitFullscreen","requestFullscreen","playbackRate","eventEl","playerEl","activeEl","activeElement","currentElTagName","tagName","querySelector","metaKey","shiftKey","copyToClipboard","text","createElement","execCommand","wait","ms","res","ControlBarOptionsBuilder","globalOptions","common","previousVideo","getPreviousVideo","playToggle","nextVideo","getNextVideo","currentTimeDisplay","timeDivider","durationDisplay","liveDisplay","flexibleWidthSpacer","getProgressControl","p2PInfoButton","muteToggle","volumeControl","getSettingsButton","theaterButton","fullscreenToggle","settingEntries","progressControl","seekBar","mouseTimeDisplay","playProgressBar","previousVideoButton","hasPreviousVideo","nextVideoButton","hasNextVideo","RedundancyUrlManager","baseUrls","segmentUrl","baseUrl","dirname","filter","u","url","getRandomInt","newBaseUrl","slashPart","endsWith","basename","random","segmentUrlBuilderFactory","redundancyUrlManager","segment","buildUrl","findbyqualityname","segments","substring","indexOf","segmentValidatorFactory","segmentsSha256Url","isLive","segmentsJSON","fetchSha256Segments","regex","_method","_peerId","retry","hashShouldBe","filename","segmentValue","segmentValidator","range","captured","exec","console","log","calculatedSha","sha256Hex","then","json","crypto","subtle","digest","bufferToHex","default","sha256","s","h","Uint8Array","forEach","v","HLSOptionsBuilder","p2pMediaLoaderModule","commonOptions","redundancyBaseUrls","p2pMediaLoaderConfig","getP2PMediaLoaderOptions","loader","Engine","createLoaderClass","playlistUrl","hlsjs","levelLabelHandler","file","videoFiles","f","fps","html5","hlsjsConfig","getHLSJSOptions","consumeOnly","trackerAnnounce","startsWith","specificLiveOrVODOptions","getP2PMediaLoaderLiveOptions","getP2PMediaLoaderVODOptions","rtcConfig","iceServers","urls","username","credential","simultaneousHttpDownloads","httpFailedSegmentTimeout","localTransport","segmentUrlBuilder","useP2P","segmentsStorage","assetsStorage","swarmId","forwardSegmentCount","p2pDownloadMaxPriority","requiredSegmentsPriority","liveOptions","latencyMode","httpDownloadProbability","skipSegmentBuilderPriority","cachedSegmentExpiration","cachedSegmentsCount","httpDownloadMaxPriority","httpDownloadProbabilitySkipIfNoPeers","getHLSLiveOptions","getHLSVODOptions","capLevelToPlayerSize","autoStartLoad","getAverageBandwidthInStore","abrEwmaDefaultEstimate","backBufferLength","startLevel","testBandwidth","liveSyncDurationCount","WebTorrentOptionsBuilder","autoPlayValue","webtorrentOptions","webtorrent","p2pMediaLoaderOptions","playerRefusedP2P","playerElement","ManagerOptionsBuilder","alreadyPlayed","getAutoPlayValue","preloadTextTracks","plugins","getPluginOptions","webtorrentOptionsBuilder","hotkeys","controlBarOptionsBuilder","videojsOptions","textTrackSettings","controls","loop","poster","playbackRates","sources","getChildrenOptions","platform","maxTouchPoints","includes","content","isLoopEnabled","items","listener","videoShortUUID","params","URLSearchParams","startTimeInt","stopTimeInt","warningTitle","peertubeLink","hasParams","decorateVideoLink","stats","CapLevelController","hls","autoLevelCapping","Number","POSITIVE_INFINITY","firstLevel","media","restrictedLevels","timer","clientRect","registerListeners","levels","maxLevelIndex","curLevel","nextLevel","streamController","unregisterListener","config","stopCapping","Events","onFpsDropLevelCapping","onMediaAttaching","onManifestParsed","onBufferCodecs","onMediaDetaching","isLevelAllowed","droppedLevel","HTMLVideoElement","startCapping","mediaHeight","mediaWidth","getMaxLevel","nextLevelSwitch","capLevelIndex","validLevels","index","getMaxLevelByMediaSize","detectPlayerSize","clientRectLast","boundsRect","right","bottom","getDimensions","contentScaleFactor","pixelRatio","AbrController","lastLoadedFragLevel","_nextAutoLevel","onCheck","_abandonRulesCheck","fragCurrent","partCurrent","bitrateTestDelay","bwEstimator","EwmaBandWidthEstimator","abrEwmaSlowVoD","abrEwmaFastVoD","onFragLoading","onFragLoaded","onFragBuffered","onLevelLoaded","onError","unregisterListeners","clearTimer","frag","PlaylistLevelType","part","details","live","abrEwmaSlowLive","abrEwmaFastLive","autoLevelEnabled","aborted","loaded","total","readyState","bufferInfo","mainForwardBufferInfo","requestDelay","performance","now","loading","loadedFirstByte","first","bwEstimate","getEstimate","minAutoLevel","expectedLen","maxBitrate","loadRate","fragLoadedDelay","bufferStarvationDelay","len","nextLoadLevel","fragLevelNextLoadedDelay","levelNextBitrate","sn","isFinite","sample","abort","abrMaxWithRealBitrate","loadedBytes","loadedDuration","realBitrate","bitrateTest","processingMs","parsing","bwEstimateSample","ErrorDetails","forcedAutoLevel","canEstimate","nextABRAutoLevel","getNextABRAutoLevel","loadError","maxAutoLevel","currentFragDuration","avgbw","bestLevel","findBestLevel","abrBandWidthFactor","abrBandWidthUpFactor","maxStarvationDelay","bwFactor","bwUpFactor","maxLoadingDelay","currentBw","maxFetchDuration","currentLevel","currentCodecSet","codecSet","levelInfo","adjustedbw","levelDetails","avgDuration","partTarget","averagetargetduration","bitrate","fetchDuration","Html5Hlsjs","vjs","tech","errorCounts","maxNetworkErrorRecovery","_duration","metadata","dvrDuration","edgeMargin","name_","playerId","errorTxt","mediaError","MEDIA_ERR_ABORTED","MEDIA_ERR_DECODE","_handleMediaError","MEDIA_ERR_NETWORK","MEDIA_ERR_SRC_NOT_SUPPORTED","message","initialize","hooks","splice","Infinity","createTimeRanges","endTime","untypedHLS","warn","destroy","_initHlsjs","once","Hlsjs","recoverMediaError","swapAudioCodec","onLine","startLoad","_event","fatal","_handleNetworkError","srOptions_","buildLevelLabel","manualLevel","obj","objKeys","_notifyVideoQualities","hlsjsConfigRef","_oneLevelObjClone","preload","_startLoad","capLevelController","abrController","_onError","_onMetaData","liveSyncDuration","targetduration","totalduration","_e","attachMedia","loadSource","registerPlugin","plugin","getTech","registerSourceHandler","canHandleSource","handleSource","hlsProvider","P2pMediaLoaderPlugin","INFO_SCHEDULER","statsP2PBytes","pendingDownload","pendingUpload","totalDownload","totalUpload","statsHTTPBytes","initVideoJsContribHlsJsPlayer","canPlayType","initializeCore","initializePlugin","p2pEngine","networkInfoInterval","initHlsJsPlayer","getEngine","requestUrl","removeBySegmentUrl","countBaseUrls","runStats","_segment","elem","p2pDownloadSpeed","arraySum","p2pUploadSpeed","httpDownloadSpeed","httpUploadSpeed","reduce","Fn","require","Slider","SeekBar","bar","getProgress","progress_","vertical","getPercent","setEventHandlers_","update_","throttle","UPDATE_REFRESH_INTERVAL","liveTracker","enableIntervalHandler_","enableInterval_","disableIntervalHandler_","disableInterval_","toggleVisibility_","visibilityState","CaptionsButton","controlText_","label_","PeertubePlayerManager","onPlayerChange","playerElementClassName","buildPlayer","videojsOptionsBuilder","getVideojsOptions","addContextMenu","mobile","peertubeMobile","bezels","currentPlayer","videojsDecodeErrors","errorNotifier","maybeFallbackToWebTorrent","rebuildAndUpdateVideoElement","newPlayer","currentMode","displayFatalError","newVideoElement","currentParentPlayerElement","parentNode","getElementById","insertBefore","onPlayerElementChange","optionsBuilder","getContextMenuOptions","contextmenuUI","module","exports","_guid","context","fn","uid","guid","bound","last","debounce","func","immediate","cancel","debounced","args","arguments","later","apply","factory","version","pEl","doc","volumeStep","mergeOptions","util","seekStep","enableMute","enableVolumeScroll","enableHoverScroll","enableFullscreen","enableNumbers","enableJogStyle","alwaysCaptureHotkeys","captureDocumentHotkeys","documentHotkeysFocusElementFilter","enableModifiersForNumbers","enableInactiveFocus","skipInitialFocus","playPauseKey","which","rewindKey","forwardKey","volumeUpKey","volumeDownKey","muteKey","fullscreenKey","customKeys","enableFull","videojsVer","VERSION","hasAttribute","outline","cancelFocusingPlayer","focusingPlayerTimeout","ifblocker","keyDown","wasPlaying","seekTime","ewhich","ePreventDefault","checkKeys","seekStepD","sub","number","customKey","customHotkey","relatedTarget","toElement","volumeHover","volumeSelector","onmouseover","onmouseout","mouseScroll","delta","wheelDelta","detail","active","define"],"sourceRoot":""}