Handle subtitles in player
This commit is contained in:
parent
40e87e9ecc
commit
16f7022b06
|
@ -213,6 +213,7 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
|
||||||
servicesTwitterUsername: this.customConfig.services.twitter.username,
|
servicesTwitterUsername: this.customConfig.services.twitter.username,
|
||||||
servicesTwitterWhitelisted: this.customConfig.services.twitter.whitelisted,
|
servicesTwitterWhitelisted: this.customConfig.services.twitter.whitelisted,
|
||||||
cachePreviewsSize: this.customConfig.cache.previews.size,
|
cachePreviewsSize: this.customConfig.cache.previews.size,
|
||||||
|
cacheCaptionsSize: this.customConfig.cache.captions.size,
|
||||||
signupEnabled: this.customConfig.signup.enabled,
|
signupEnabled: this.customConfig.signup.enabled,
|
||||||
signupLimit: this.customConfig.signup.limit,
|
signupLimit: this.customConfig.signup.limit,
|
||||||
adminEmail: this.customConfig.admin.email,
|
adminEmail: this.customConfig.admin.email,
|
||||||
|
|
|
@ -6,11 +6,11 @@ import { peertubeLocalStorage } from '@app/shared/misc/peertube-local-storage'
|
||||||
import { VideoSupportComponent } from '@app/videos/+video-watch/modal/video-support.component'
|
import { VideoSupportComponent } from '@app/videos/+video-watch/modal/video-support.component'
|
||||||
import { MetaService } from '@ngx-meta/core'
|
import { MetaService } from '@ngx-meta/core'
|
||||||
import { NotificationsService } from 'angular2-notifications'
|
import { NotificationsService } from 'angular2-notifications'
|
||||||
import { Subscription } from 'rxjs'
|
import { forkJoin, Subscription } from 'rxjs'
|
||||||
import * as videojs from 'video.js'
|
import * as videojs from 'video.js'
|
||||||
import 'videojs-hotkeys'
|
import 'videojs-hotkeys'
|
||||||
import * as WebTorrent from 'webtorrent'
|
import * as WebTorrent from 'webtorrent'
|
||||||
import { UserVideoRateType, VideoPrivacy, VideoRateType, VideoState } from '../../../../../shared'
|
import { ResultList, UserVideoRateType, VideoPrivacy, VideoRateType, VideoState } from '../../../../../shared'
|
||||||
import '../../../assets/player/peertube-videojs-plugin'
|
import '../../../assets/player/peertube-videojs-plugin'
|
||||||
import { AuthService, ConfirmService } from '../../core'
|
import { AuthService, ConfirmService } from '../../core'
|
||||||
import { RestExtractor, VideoBlacklistService } from '../../shared'
|
import { RestExtractor, VideoBlacklistService } from '../../shared'
|
||||||
|
@ -26,6 +26,9 @@ import { ServerService } from '@app/core'
|
||||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||||
import { environment } from '../../../environments/environment'
|
import { environment } from '../../../environments/environment'
|
||||||
import { getDevLocale, isOnDevLocale } from '@app/shared/i18n/i18n-utils'
|
import { getDevLocale, isOnDevLocale } from '@app/shared/i18n/i18n-utils'
|
||||||
|
import { VideoCaptionService } from '@app/shared/video-caption'
|
||||||
|
import { VideoCaption } from '../../../../../shared/models/videos/video-caption.model'
|
||||||
|
import { VideoJSCaption } from '../../../assets/player/peertube-videojs-typings'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-video-watch',
|
selector: 'my-video-watch',
|
||||||
|
@ -74,6 +77,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
private markdownService: MarkdownService,
|
private markdownService: MarkdownService,
|
||||||
private zone: NgZone,
|
private zone: NgZone,
|
||||||
private redirectService: RedirectService,
|
private redirectService: RedirectService,
|
||||||
|
private videoCaptionService: VideoCaptionService,
|
||||||
private i18n: I18n,
|
private i18n: I18n,
|
||||||
@Inject(LOCALE_ID) private localeId: string
|
@Inject(LOCALE_ID) private localeId: string
|
||||||
) {}
|
) {}
|
||||||
|
@ -109,14 +113,18 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
if (this.player) this.player.pause()
|
if (this.player) this.player.pause()
|
||||||
|
|
||||||
// Video did change
|
// Video did change
|
||||||
this.videoService
|
forkJoin(
|
||||||
.getVideo(uuid)
|
this.videoService.getVideo(uuid),
|
||||||
.pipe(catchError(err => this.restExtractor.redirectTo404IfNotFound(err, [ 400, 404 ])))
|
this.videoCaptionService.listCaptions(uuid)
|
||||||
.subscribe(video => {
|
)
|
||||||
const startTime = this.route.snapshot.queryParams.start
|
.pipe(
|
||||||
this.onVideoFetched(video, startTime)
|
catchError(err => this.restExtractor.redirectTo404IfNotFound(err, [ 400, 404 ]))
|
||||||
.catch(err => this.handleError(err))
|
)
|
||||||
})
|
.subscribe(([ video, captionsResult ]) => {
|
||||||
|
const startTime = this.route.snapshot.queryParams.start
|
||||||
|
this.onVideoFetched(video, captionsResult.data, startTime)
|
||||||
|
.catch(err => this.handleError(err))
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -331,7 +339,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private async onVideoFetched (video: VideoDetails, startTime = 0) {
|
private async onVideoFetched (video: VideoDetails, videoCaptions: VideoCaption[], startTime = 0) {
|
||||||
this.video = video
|
this.video = video
|
||||||
|
|
||||||
// Re init attributes
|
// Re init attributes
|
||||||
|
@ -358,10 +366,17 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
this.playerElement.setAttribute('playsinline', 'true')
|
this.playerElement.setAttribute('playsinline', 'true')
|
||||||
playerElementWrapper.appendChild(this.playerElement)
|
playerElementWrapper.appendChild(this.playerElement)
|
||||||
|
|
||||||
|
const playerCaptions = videoCaptions.map(c => ({
|
||||||
|
label: c.language.label,
|
||||||
|
language: c.language.id,
|
||||||
|
src: environment.apiUrl + c.captionPath
|
||||||
|
}))
|
||||||
|
|
||||||
const videojsOptions = getVideojsOptions({
|
const videojsOptions = getVideojsOptions({
|
||||||
autoplay: this.isAutoplay(),
|
autoplay: this.isAutoplay(),
|
||||||
inactivityTimeout: 2500,
|
inactivityTimeout: 2500,
|
||||||
videoFiles: this.video.files,
|
videoFiles: this.video.files,
|
||||||
|
videoCaptions: playerCaptions,
|
||||||
playerElement: this.playerElement,
|
playerElement: this.playerElement,
|
||||||
videoViewUrl: this.video.privacy.id !== VideoPrivacy.PRIVATE ? this.videoService.getVideoViewUrl(this.video.uuid) : null,
|
videoViewUrl: this.video.privacy.id !== VideoPrivacy.PRIVATE ? this.videoService.getVideoViewUrl(this.video.uuid) : null,
|
||||||
videoDuration: this.video.duration,
|
videoDuration: this.video.duration,
|
||||||
|
|
|
@ -11,12 +11,16 @@ import './webtorrent-info-button'
|
||||||
import './peertube-videojs-plugin'
|
import './peertube-videojs-plugin'
|
||||||
import './peertube-load-progress-bar'
|
import './peertube-load-progress-bar'
|
||||||
import './theater-button'
|
import './theater-button'
|
||||||
import { videojsUntyped } from './peertube-videojs-typings'
|
import { VideoJSCaption, videojsUntyped } from './peertube-videojs-typings'
|
||||||
import { buildVideoEmbed, buildVideoLink, copyToClipboard } from './utils'
|
import { buildVideoEmbed, buildVideoLink, copyToClipboard } from './utils'
|
||||||
import { getCompleteLocale, getShortLocale, is18nLocale, isDefaultLocale } from '../../../../shared/models/i18n/i18n'
|
import { getCompleteLocale, getShortLocale, is18nLocale, isDefaultLocale } from '../../../../shared/models/i18n/i18n'
|
||||||
|
|
||||||
// Change 'Playback Rate' to 'Speed' (smaller for our settings menu)
|
// Change 'Playback Rate' to 'Speed' (smaller for our settings menu)
|
||||||
videojsUntyped.getComponent('PlaybackRateMenuButton').prototype.controlText_ = 'Speed'
|
videojsUntyped.getComponent('PlaybackRateMenuButton').prototype.controlText_ = 'Speed'
|
||||||
|
// Change Captions to Subtitles/CC
|
||||||
|
videojsUntyped.getComponent('CaptionsButton').prototype.controlText_ = 'Subtitles/CC'
|
||||||
|
// We just want to display 'Off' instead of 'captions off', keep a space so the variable == true (hacky I know)
|
||||||
|
videojsUntyped.getComponent('CaptionsButton').prototype.label_ = ' '
|
||||||
|
|
||||||
function getVideojsOptions (options: {
|
function getVideojsOptions (options: {
|
||||||
autoplay: boolean,
|
autoplay: boolean,
|
||||||
|
@ -30,11 +34,14 @@ function getVideojsOptions (options: {
|
||||||
poster: string,
|
poster: string,
|
||||||
startTime: number
|
startTime: number
|
||||||
theaterMode: boolean,
|
theaterMode: boolean,
|
||||||
|
videoCaptions: VideoJSCaption[],
|
||||||
controls?: boolean,
|
controls?: boolean,
|
||||||
muted?: boolean,
|
muted?: boolean,
|
||||||
loop?: boolean
|
loop?: boolean
|
||||||
}) {
|
}) {
|
||||||
const videojsOptions = {
|
const videojsOptions = {
|
||||||
|
// We don't use text track settings for now
|
||||||
|
textTrackSettings: false,
|
||||||
controls: options.controls !== undefined ? options.controls : true,
|
controls: options.controls !== undefined ? options.controls : true,
|
||||||
muted: options.controls !== undefined ? options.muted : false,
|
muted: options.controls !== undefined ? options.muted : false,
|
||||||
loop: options.loop !== undefined ? options.loop : false,
|
loop: options.loop !== undefined ? options.loop : false,
|
||||||
|
@ -45,6 +52,7 @@ function getVideojsOptions (options: {
|
||||||
plugins: {
|
plugins: {
|
||||||
peertube: {
|
peertube: {
|
||||||
autoplay: options.autoplay, // Use peertube plugin autoplay because we get the file by webtorrent
|
autoplay: options.autoplay, // Use peertube plugin autoplay because we get the file by webtorrent
|
||||||
|
videoCaptions: options.videoCaptions,
|
||||||
videoFiles: options.videoFiles,
|
videoFiles: options.videoFiles,
|
||||||
playerElement: options.playerElement,
|
playerElement: options.playerElement,
|
||||||
videoViewUrl: options.videoViewUrl,
|
videoViewUrl: options.videoViewUrl,
|
||||||
|
@ -71,8 +79,16 @@ function getVideojsOptions (options: {
|
||||||
|
|
||||||
function getControlBarChildren (options: {
|
function getControlBarChildren (options: {
|
||||||
peertubeLink: boolean
|
peertubeLink: boolean
|
||||||
theaterMode: boolean
|
theaterMode: boolean,
|
||||||
|
videoCaptions: VideoJSCaption[]
|
||||||
}) {
|
}) {
|
||||||
|
const settingEntries = []
|
||||||
|
|
||||||
|
// Keep an order
|
||||||
|
settingEntries.push('playbackRateMenuButton')
|
||||||
|
if (options.videoCaptions.length !== 0) settingEntries.push('captionsButton')
|
||||||
|
settingEntries.push('resolutionMenuButton')
|
||||||
|
|
||||||
const children = {
|
const children = {
|
||||||
'playToggle': {},
|
'playToggle': {},
|
||||||
'currentTimeDisplay': {},
|
'currentTimeDisplay': {},
|
||||||
|
@ -102,10 +118,7 @@ function getControlBarChildren (options: {
|
||||||
setup: {
|
setup: {
|
||||||
maxHeightOffset: 40
|
maxHeightOffset: 40
|
||||||
},
|
},
|
||||||
entries: [
|
entries: settingEntries
|
||||||
'resolutionMenuButton',
|
|
||||||
'playbackRateMenuButton'
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import * as WebTorrent from 'webtorrent'
|
||||||
import { VideoFile } from '../../../../shared/models/videos/video.model'
|
import { VideoFile } from '../../../../shared/models/videos/video.model'
|
||||||
import { renderVideo } from './video-renderer'
|
import { renderVideo } from './video-renderer'
|
||||||
import './settings-menu-button'
|
import './settings-menu-button'
|
||||||
import { PeertubePluginOptions, VideoJSComponentInterface, videojsUntyped } from './peertube-videojs-typings'
|
import { PeertubePluginOptions, VideoJSCaption, VideoJSComponentInterface, videojsUntyped } from './peertube-videojs-typings'
|
||||||
import { isMobile, videoFileMaxByResolution, videoFileMinByResolution } from './utils'
|
import { isMobile, videoFileMaxByResolution, videoFileMinByResolution } from './utils'
|
||||||
import * as CacheChunkStore from 'cache-chunk-store'
|
import * as CacheChunkStore from 'cache-chunk-store'
|
||||||
import { PeertubeChunkStore } from './peertube-chunk-store'
|
import { PeertubeChunkStore } from './peertube-chunk-store'
|
||||||
|
@ -54,6 +54,7 @@ class PeerTubePlugin extends Plugin {
|
||||||
private player: any
|
private player: any
|
||||||
private currentVideoFile: VideoFile
|
private currentVideoFile: VideoFile
|
||||||
private torrent: WebTorrent.Torrent
|
private torrent: WebTorrent.Torrent
|
||||||
|
private videoCaptions: VideoJSCaption[]
|
||||||
private renderer
|
private renderer
|
||||||
private fakeRenderer
|
private fakeRenderer
|
||||||
private autoResolution = true
|
private autoResolution = true
|
||||||
|
@ -79,6 +80,7 @@ class PeerTubePlugin extends Plugin {
|
||||||
this.videoFiles = options.videoFiles
|
this.videoFiles = options.videoFiles
|
||||||
this.videoViewUrl = options.videoViewUrl
|
this.videoViewUrl = options.videoViewUrl
|
||||||
this.videoDuration = options.videoDuration
|
this.videoDuration = options.videoDuration
|
||||||
|
this.videoCaptions = options.videoCaptions
|
||||||
|
|
||||||
this.savePlayerSrcFunction = this.player.src
|
this.savePlayerSrcFunction = this.player.src
|
||||||
// Hack to "simulate" src link in video.js >= 6
|
// Hack to "simulate" src link in video.js >= 6
|
||||||
|
@ -421,6 +423,8 @@ class PeerTubePlugin extends Plugin {
|
||||||
|
|
||||||
this.initSmoothProgressBar()
|
this.initSmoothProgressBar()
|
||||||
|
|
||||||
|
this.initCaptions()
|
||||||
|
|
||||||
this.alterInactivity()
|
this.alterInactivity()
|
||||||
|
|
||||||
if (this.autoplay === true) {
|
if (this.autoplay === true) {
|
||||||
|
@ -581,7 +585,7 @@ class PeerTubePlugin extends Plugin {
|
||||||
this.player.options_.inactivityTimeout = 0
|
this.player.options_.inactivityTimeout = 0
|
||||||
}
|
}
|
||||||
const enableInactivity = () => {
|
const enableInactivity = () => {
|
||||||
this.player.options_.inactivityTimeout = saveInactivityTimeout
|
// this.player.options_.inactivityTimeout = saveInactivityTimeout
|
||||||
}
|
}
|
||||||
|
|
||||||
const settingsDialog = this.player.children_.find(c => c.name_ === 'SettingsDialog')
|
const settingsDialog = this.player.children_.find(c => c.name_ === 'SettingsDialog')
|
||||||
|
@ -611,6 +615,18 @@ class PeerTubePlugin extends Plugin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private initCaptions () {
|
||||||
|
for (const caption of this.videoCaptions) {
|
||||||
|
this.player.addRemoteTextTrack({
|
||||||
|
kind: 'captions',
|
||||||
|
label: caption.label,
|
||||||
|
language: caption.language,
|
||||||
|
id: caption.language,
|
||||||
|
src: caption.src
|
||||||
|
}, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Thanks: https://github.com/videojs/video.js/issues/4460#issuecomment-312861657
|
// Thanks: https://github.com/videojs/video.js/issues/4460#issuecomment-312861657
|
||||||
private initSmoothProgressBar () {
|
private initSmoothProgressBar () {
|
||||||
const SeekBar = videojsUntyped.getComponent('SeekBar')
|
const SeekBar = videojsUntyped.getComponent('SeekBar')
|
||||||
|
|
|
@ -16,13 +16,20 @@ interface VideoJSComponentInterface {
|
||||||
registerComponent (name: string, obj: any)
|
registerComponent (name: string, obj: any)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type VideoJSCaption = {
|
||||||
|
label: string
|
||||||
|
language: string
|
||||||
|
src: string
|
||||||
|
}
|
||||||
|
|
||||||
type PeertubePluginOptions = {
|
type PeertubePluginOptions = {
|
||||||
videoFiles: VideoFile[]
|
videoFiles: VideoFile[]
|
||||||
playerElement: HTMLVideoElement
|
playerElement: HTMLVideoElement
|
||||||
videoViewUrl: string
|
videoViewUrl: string
|
||||||
videoDuration: number
|
videoDuration: number
|
||||||
startTime: number
|
startTime: number
|
||||||
autoplay: boolean
|
autoplay: boolean,
|
||||||
|
videoCaptions: VideoJSCaption[]
|
||||||
}
|
}
|
||||||
|
|
||||||
// videojs typings don't have some method we need
|
// videojs typings don't have some method we need
|
||||||
|
@ -31,5 +38,6 @@ const videojsUntyped = videojs as any
|
||||||
export {
|
export {
|
||||||
VideoJSComponentInterface,
|
VideoJSComponentInterface,
|
||||||
PeertubePluginOptions,
|
PeertubePluginOptions,
|
||||||
videojsUntyped
|
videojsUntyped,
|
||||||
|
VideoJSCaption
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,8 @@ class SettingsMenuItem extends MenuItem {
|
||||||
throw new Error(`Component ${subMenuName} does not exist`)
|
throw new Error(`Component ${subMenuName} does not exist`)
|
||||||
}
|
}
|
||||||
this.subMenu = new SubMenuComponent(this.player(), options, menuButton, this)
|
this.subMenu = new SubMenuComponent(this.player(), options, menuButton, this)
|
||||||
|
const subMenuClass = this.subMenu.buildCSSClass().split(' ')[0]
|
||||||
|
this.settingsSubMenuEl_.className += ' ' + subMenuClass
|
||||||
|
|
||||||
this.eventHandlers()
|
this.eventHandlers()
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,7 @@ $setting-transition-easing: ease-out;
|
||||||
.vjs-settings-sub-menu-title {
|
.vjs-settings-sub-menu-title {
|
||||||
display: table-cell;
|
display: table-cell;
|
||||||
padding: 0 5px;
|
padding: 0 5px;
|
||||||
|
text-transform: capitalize;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vjs-settings-sub-menu-title {
|
.vjs-settings-sub-menu-title {
|
||||||
|
@ -141,15 +142,15 @@ $setting-transition-easing: ease-out;
|
||||||
.vjs-menu-item {
|
.vjs-menu-item {
|
||||||
outline: 0;
|
outline: 0;
|
||||||
font-weight: $font-semibold;
|
font-weight: $font-semibold;
|
||||||
|
|
||||||
padding: 5px 8px;
|
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
padding: 5px 8px;
|
||||||
|
|
||||||
&.vjs-back-button {
|
&.vjs-back-button {
|
||||||
background-color: inherit;
|
background-color: inherit;
|
||||||
padding: 8px 8px 13px 8px;
|
padding: 8px 8px 13px 12px;
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
border-bottom: 1px solid grey;
|
border-bottom: 1px solid grey;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
@include chevron-left(9px, 2px);
|
@include chevron-left(9px, 2px);
|
||||||
|
@ -174,6 +175,25 @@ $setting-transition-easing: ease-out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Special captions case
|
||||||
|
// Bigger caption button
|
||||||
|
&.vjs-captions-button {
|
||||||
|
width: 200px;
|
||||||
|
|
||||||
|
.vjs-menu-item {
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
.vjs-menu-item-text {
|
||||||
|
margin-left: 25px;
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vjs-menu {
|
||||||
|
width: inherit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,9 +20,11 @@ import 'whatwg-fetch'
|
||||||
import * as vjs from 'video.js'
|
import * as vjs from 'video.js'
|
||||||
import * as Channel from 'jschannel'
|
import * as Channel from 'jschannel'
|
||||||
|
|
||||||
import { VideoDetails } from '../../../../shared'
|
import { ResultList, VideoDetails } from '../../../../shared'
|
||||||
import { addContextMenu, getVideojsOptions, loadLocale } from '../../assets/player/peertube-player'
|
import { addContextMenu, getVideojsOptions, loadLocale } from '../../assets/player/peertube-player'
|
||||||
import { PeerTubeResolution } from '../player/definitions'
|
import { PeerTubeResolution } from '../player/definitions'
|
||||||
|
import { VideoJSCaption } from '../../assets/player/peertube-videojs-typings'
|
||||||
|
import { VideoCaption } from '../../../../shared/models/videos/video-caption.model'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Embed API exposes control of the embed player to the outside world via
|
* Embed API exposes control of the embed player to the outside world via
|
||||||
|
@ -178,6 +180,10 @@ class PeerTubeEmbed {
|
||||||
return fetch(this.getVideoUrl(videoId))
|
return fetch(this.getVideoUrl(videoId))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loadVideoCaptions (videoId: string): Promise<Response> {
|
||||||
|
return fetch(this.getVideoUrl(videoId) + '/captions')
|
||||||
|
}
|
||||||
|
|
||||||
removeElement (element: HTMLElement) {
|
removeElement (element: HTMLElement) {
|
||||||
element.parentElement.removeChild(element)
|
element.parentElement.removeChild(element)
|
||||||
}
|
}
|
||||||
|
@ -254,15 +260,27 @@ class PeerTubeEmbed {
|
||||||
const videoId = lastPart.indexOf('?') === -1 ? lastPart : lastPart.split('?')[ 0 ]
|
const videoId = lastPart.indexOf('?') === -1 ? lastPart : lastPart.split('?')[ 0 ]
|
||||||
|
|
||||||
await loadLocale(window.location.origin, vjs, navigator.language)
|
await loadLocale(window.location.origin, vjs, navigator.language)
|
||||||
let response = await this.loadVideoInfo(videoId)
|
const [ videoResponse, captionsResponse ] = await Promise.all([
|
||||||
|
this.loadVideoInfo(videoId),
|
||||||
|
this.loadVideoCaptions(videoId)
|
||||||
|
])
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!videoResponse.ok) {
|
||||||
if (response.status === 404) return this.videoNotFound(this.videoElement)
|
if (videoResponse.status === 404) return this.videoNotFound(this.videoElement)
|
||||||
|
|
||||||
return this.videoFetchError(this.videoElement)
|
return this.videoFetchError(this.videoElement)
|
||||||
}
|
}
|
||||||
|
|
||||||
const videoInfo: VideoDetails = await response.json()
|
const videoInfo: VideoDetails = await videoResponse.json()
|
||||||
|
let videoCaptions: VideoJSCaption[] = []
|
||||||
|
if (captionsResponse.ok) {
|
||||||
|
const { data } = (await captionsResponse.json()) as ResultList<VideoCaption>
|
||||||
|
videoCaptions = data.map(c => ({
|
||||||
|
label: c.language.label,
|
||||||
|
language: c.language.id,
|
||||||
|
src: window.location.origin + c.captionPath
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
this.loadParams()
|
this.loadParams()
|
||||||
|
|
||||||
|
@ -273,6 +291,7 @@ class PeerTubeEmbed {
|
||||||
loop: this.loop,
|
loop: this.loop,
|
||||||
startTime: this.startTime,
|
startTime: this.startTime,
|
||||||
|
|
||||||
|
videoCaptions,
|
||||||
inactivityTimeout: 1500,
|
inactivityTimeout: 1500,
|
||||||
videoViewUrl: this.getVideoUrl(videoId) + '/views',
|
videoViewUrl: this.getVideoUrl(videoId) + '/views',
|
||||||
playerElement: this.videoElement,
|
playerElement: this.videoElement,
|
||||||
|
@ -297,6 +316,7 @@ class PeerTubeEmbed {
|
||||||
}
|
}
|
||||||
|
|
||||||
addContextMenu(this.player, window.location.origin + videoInfo.embedPath)
|
addContextMenu(this.player, window.location.origin + videoInfo.embedPath)
|
||||||
|
|
||||||
this.initializeApi()
|
this.initializeApi()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ const playerKeys = {
|
||||||
'Quality': 'Quality',
|
'Quality': 'Quality',
|
||||||
'Auto': 'Auto',
|
'Auto': 'Auto',
|
||||||
'Speed': 'Speed',
|
'Speed': 'Speed',
|
||||||
|
'Subtitles/CC': 'Subtitles/CC',
|
||||||
'peers': 'peers',
|
'peers': 'peers',
|
||||||
'Go to the video page': 'Go to the video page',
|
'Go to the video page': 'Go to the video page',
|
||||||
'Settings': 'Settings',
|
'Settings': 'Settings',
|
||||||
|
|
Loading…
Reference in New Issue
Block a user