{"version":3,"file":"524.chunk.js?v=4953","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/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/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/root-helpers/utils.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","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 =\r\n dictionaryBytes.find((d) => value < d.max) ||\r\n 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 const getList1 = () => {\r\n const list = [\r\n \"rel\" + \"ay2.expresstu\" + \"rn.com:443\",\r\n \"rel\" + \"ay3.expresstu\" + \"rn.com:80\",\r\n \"rel\" + \"ay3.expresstu\" + \"rn.com:443\",\r\n \"rel\" + \"ay4.expresstu\" + \"rn.com:34\" + \"78\",\r\n \"rel\" + \"ay5.expresstu\" + \"rn.com:34\" + \"78\",\r\n \"rel\" + \"ay6.expresstu\" + \"rn.com:34\" + \"78\",\r\n \"rel\" + \"ay7.expresstu\" + \"rn.com:34\" + \"78\",\r\n \"rel\" + \"ay8.expresstu\" + \"rn.com:34\" + \"78\",\r\n ];\r\n return list.map((server) => ({\r\n urls: \"stun:\" + server,\r\n username: \"efP\" + \"U52\" + \"K4S\" + \"LOQ\" + \"34W\" + \"2QY\",\r\n credential: \"1TJ\" + \"PNF\" + \"xHK\" + \"XrZ\" + \"felz\",\r\n }));\r\n };\r\n const getList2 = () => {\r\n const list = [\r\n \"standard.rel\" + \"ay.met\" + \"ered.ca:443?trans\" + \"port=tcp\",\r\n \"standard.rel\" + \"ay.met\" + \"ered.ca:443\",\r\n \"standard.rel\" + \"ay.met\" + \"ered.ca:80?trans\" + \"port=tcp\",\r\n \"standard.rel\" + \"ay.met\" + \"ered.ca:80\",\r\n ];\r\n return list.map((server) => ({\r\n urls: \"turn:\" + server,\r\n username: \"604\" + \"3b1\" + \"571\" + \"1c8\" + \"1a9\" + \"40b\" + \"09b\" + \"977\",\r\n credential: \"RS3\" + \"nKg\" + \"8sY\" + \"Av0\" + \"QvcY\",\r\n }));\r\n };\r\n\r\n const getStuns = () => {\r\n const list = [\r\n \"relay1.expressturn.com:443\",\r\n \"relay2.expressturn.com:443\",\r\n \"relay3.expressturn.com:443\",\r\n \"relay1.expressturn.com:3478\",\r\n \"relay2.expressturn.com:3478\",\r\n \"relay4.expressturn.com:3478\",\r\n \"relay5.expressturn.com:3478\",\r\n \"relay6.expressturn.com:3478\",\r\n \"relay8.expressturn.com:3478\",\r\n \"relay1.expressturn.com:80\",\r\n \"stun.cloudflare.com:3478\",\r\n \"global.stun.twilio.com:3478\",\r\n \"stun.relay.metered.ca:80\",\r\n ];\r\n\r\n return list.map((server) => ({\r\n urls: \"stun:\" + server,\r\n }));\r\n };\r\n\r\n const getRandomServers = (servers, count: number) => {\r\n return servers.sort(() => 0.5 - Math.random()).slice(0, count);\r\n };\r\n\r\n const allServers = [\r\n ...getStuns(),\r\n //...getList1(),\r\n /*...getList2(),*/\r\n ];\r\n\r\n return {\r\n iceServers: getRandomServers(allServers, 3),\r\n };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n\r\nexport {\r\n getRtcConfig,\r\n toTitleCase,\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 this.stopListen()\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 console.log('runUserViewing INIT')\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 stopListen(){\r\n const controlBar = this.player.controlBar\r\n const settingsButton: SettingsButton = (controlBar as any).settingsButton\r\n\r\n\r\n if(controlBar){\r\n controlBar.off('mouseenter')\r\n controlBar.off('mouseleave')\r\n }\r\n\r\n if(settingsButton){\r\n settingsButton.dialog.off('mouseenter')\r\n settingsButton.dialog.off('mouseleave')\r\n }\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 \r\n this.el().addEventListener('mouseenter', this.mouseenter)\r\n\r\n this.el().addEventListener('mouseleave', this.mouseleave)\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 dispose(): void {\r\n this.el().removeEventListener('mouseenter', this.mouseenter)\r\n\r\n this.el().removeEventListener('mouseleave', this.mouseleave)\r\n }\r\n\r\n private userInactiveHandler(){\r\n self.close()\r\n }\r\n\r\n private mouseenter(){\r\n this.player().off('userinactive', this.userInactiveHandler)\r\n }\r\n private mouseleave(){\r\n this.player().one('userinactive', this.userInactiveHandler)\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 dispose () {\r\n this.player.off('touchstart')\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\r\n console.log('event', event)\r\n\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' || (!e.ctrlKey && e.key === 'Enter')),\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","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","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 { 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] || (!isLive ? findbyqualityname(segments, filename) : null)\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 console.log('options 5', p2pMediaLoaderConfig)\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\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.common.isLive ? undefined : 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.common.isLive ? undefined : this.options.assetsStorage,\r\n swarmId: this.options.p2pMediaLoader.playlistUrl,\r\n forwardSegmentCount: specificLiveOrVODOptions.p2pDownloadMaxPriority ?? 20,\r\n localTransport : this.options.common.localTransport,\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\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 videoUUID = commonOptions.videoUUID\r\n const serverUrl = commonOptions.serverUrl\r\n\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 // icon: 'repeat',\r\n label: 'Send video playback information to devs',\r\n listener: function (this: videojs.Player) {\r\n (this as any).tech_.hlsProvider.sendLogsCache(videoUUID, serverUrl);\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 console.log('stats.bwEstimate', stats.bwEstimate)\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, 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 VIDEO_HLS_ERROR = 'HLS Error'\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 videoLogger: Logger\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.videoLogger = new Logger(true)\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\r\n untypedHLS.log = untypedHLS.warn = () => {\r\n // empty\r\n }\r\n\r\n\r\n this.videoLogger.destroyLogs()\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 console.error(_event)\r\n console.error(data)\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 this.videoLogger.log(error.message, {\r\n id: VIDEO_HLS_ERROR\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 public sendLogsCache (videoId: String, serverUrl: String) {\r\n this.videoLogger.returnLog(videoId, serverUrl)\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 = this.videoLogger\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.FRAG_LOADED, (e: any, frag : any) => {\r\n console.log('frag', frag)\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 // If video is audio\r\n if (options && options.isAudio && videojsOptions && videojsOptions.controlBar && videojsOptions.controlBar.children) {\r\n videojsOptions.controlBar.children['settingsButton'].entries = ['playbackRateMenuButton'];\r\n videojsOptions.controlBar.fullscreenToggle = false;\r\n //videojsOptions.bigPlayButton = false;\r\n videojsOptions.inactivityTimeout = 0;\r\n // Mouse events\r\n videojsOptions.userActions = videojsOptions.userActions || {}\r\n videojsOptions.userActions.doubleClick = false;\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 if (options && options.isAudio)\r\n player.addClass('vjs-is-audio')\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 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\r\n console.log('options', options)\r\n\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: false,\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 console.log('event', 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 console.log(\"E\", e)\r\n // Space bar or MediaPlayPause\r\n return (e.which === 32 || e.which === 179 || e.key == 'MediaPlayPause' || e.key == 'Enter');\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 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","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","window","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","stopListen","videoViewInterval","alterInactivity","listenControlBarMouse","listenFullScreenChange","lastViewEvent","lastCurrentTime","console","log","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","video","positionBlock","position","buildAvailableVideo","buildUnavailableVideo","videoElement","thumbnail","src","location","origin","thumbnailPath","infoBlock","channel","startTimestamp","stopTimestamp","timestamps","append","block","code","onClicked","PlaylistMenu","mouseenter","mouseleave","current","close","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","ctrlKey","isNaked","SEEK_STEP","altKey","exitFullscreen","requestFullscreen","playbackRate","eventEl","playerEl","activeEl","activeElement","currentElTagName","tagName","querySelector","metaKey","shiftKey","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","wait","ms","res","findbyqualityname","segments","substring","indexOf","segmentValidatorFactory","segmentsSha256Url","isLive","segmentsJSON","fetchSha256Segments","regex","_method","_peerId","retry","hashShouldBe","filename","segmentValue","segmentValidator","range","captured","exec","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","servers","server","urls","simultaneousHttpDownloads","httpFailedSegmentTimeout","localTransport","segmentUrlBuilder","useP2P","segmentsStorage","assetsStorage","swarmId","forwardSegmentCount","p2pDownloadMaxPriority","base","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","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","videoLogger","Logger","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","destroyLogs","destroy","_initHlsjs","once","Hlsjs","recoverMediaError","swapAudioCodec","onLine","startLoad","_event","fatal","_handleNetworkError","srOptions_","buildLevelLabel","manualLevel","obj","objKeys","videoId","serverUrl","returnLog","_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","isAudio","addContextMenu","mobile","peertubeMobile","bezels","currentPlayer","videojsDecodeErrors","errorNotifier","maybeFallbackToWebTorrent","rebuildAndUpdateVideoElement","newPlayer","currentMode","displayFatalError","newVideoElement","createElement","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":""}