Merge pull request #7141 from thornbill/subtitle-styling
Extract native/custom subtitle element logic to separate typescript file
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Options specifying if the player's native subtitle (cue) element should be used, a custom element (div), or allow
|
||||
* Jellyfin to choose automatically based on known browser support. Some browsers do not properly apply CSS styling to
|
||||
* the native subtitle element.
|
||||
*/
|
||||
export const SubtitleStylingOption = {
|
||||
Auto: 'Auto',
|
||||
Custom: 'Custom',
|
||||
Native: 'Native'
|
||||
} as const;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-redeclare
|
||||
export type SubtitleStylingOption = typeof SubtitleStylingOption[keyof typeof SubtitleStylingOption];
|
||||
43
src/apps/stable/features/playback/utils/subtitleStyles.ts
Normal file
43
src/apps/stable/features/playback/utils/subtitleStyles.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { SubtitleStylingOption } from 'apps/stable/features/playback/constants/subtitleStylingOption';
|
||||
import browser from 'scripts/browser';
|
||||
import type { UserSettings } from 'scripts/settings/userSettings';
|
||||
|
||||
// TODO: This type override should be removed when userSettings are properly typed
|
||||
interface SubtitleAppearanceSettings {
|
||||
subtitleStyling: SubtitleStylingOption
|
||||
}
|
||||
|
||||
export function useCustomSubtitles(userSettings: UserSettings) {
|
||||
const subtitleAppearance = userSettings.getSubtitleAppearanceSettings() as SubtitleAppearanceSettings;
|
||||
switch (subtitleAppearance.subtitleStyling) {
|
||||
case SubtitleStylingOption.Native:
|
||||
return false;
|
||||
case SubtitleStylingOption.Custom:
|
||||
return true;
|
||||
default:
|
||||
// after a system update, ps4 isn't showing anything when creating a track element dynamically
|
||||
// going to have to do it ourselves
|
||||
if (browser.ps4) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Tizen 5 doesn't support displaying secondary subtitles
|
||||
if ((browser.tizenVersion && browser.tizenVersion >= 5) || browser.web0s) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (browser.edge) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// font-size styling does not seem to work natively in firefox. Switching to custom subtitles element for firefox.
|
||||
if (browser.firefox) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// iOS/macOS global caption settings are causing huge font-size and margins
|
||||
if (browser.safari) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -178,7 +178,6 @@ const Scroller: FC<PropsWithChildren<ScrollerProps>> = ({
|
||||
allowNativeScroll: !enableScrollButtons,
|
||||
forceHideScrollbars: enableScrollButtons,
|
||||
// In edge, with the native scroll, the content jumps around when hovering over the buttons
|
||||
// @ts-expect-error browser doesn't explicitly declare browser.edge, so fails type checking
|
||||
requireAnimation: enableScrollButtons && browser.edge
|
||||
};
|
||||
|
||||
|
||||
@@ -2,8 +2,11 @@ import DOMPurify from 'dompurify';
|
||||
import debounce from 'lodash-es/debounce';
|
||||
import Screenfull from 'screenfull';
|
||||
|
||||
import { useCustomSubtitles } from 'apps/stable/features/playback/utils/subtitleStyles';
|
||||
import subtitleAppearanceHelper from 'components/subtitlesettings/subtitleappearancehelper';
|
||||
import { AppFeature } from 'constants/appFeature';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import { currentSettings as userSettings } from 'scripts/settings/userSettings';
|
||||
import { MediaError } from 'types/mediaError';
|
||||
|
||||
import browser from '../../scripts/browser';
|
||||
@@ -330,8 +333,8 @@ export class HtmlVideoPlayer {
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
* @private
|
||||
*/
|
||||
incrementFetchQueue() {
|
||||
if (this.#fetchQueue <= 0) {
|
||||
this.isFetching = true;
|
||||
@@ -342,8 +345,8 @@ export class HtmlVideoPlayer {
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
* @private
|
||||
*/
|
||||
decrementFetchQueue() {
|
||||
this.#fetchQueue--;
|
||||
|
||||
@@ -354,8 +357,8 @@ export class HtmlVideoPlayer {
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
* @private
|
||||
*/
|
||||
updateVideoUrl(streamInfo) {
|
||||
const mediaSource = streamInfo.mediaSource;
|
||||
const item = streamInfo.item;
|
||||
@@ -406,8 +409,8 @@ export class HtmlVideoPlayer {
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
* @private
|
||||
*/
|
||||
setSrcWithFlvJs(elem, options, url) {
|
||||
return import('flv.js').then(({ default: flvjs }) => {
|
||||
const flvPlayer = flvjs.createPlayer({
|
||||
@@ -432,8 +435,8 @@ export class HtmlVideoPlayer {
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
* @private
|
||||
*/
|
||||
setSrcWithHlsJs(elem, options, url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
requireHlsPlayer(async () => {
|
||||
@@ -473,8 +476,8 @@ export class HtmlVideoPlayer {
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
* @private
|
||||
*/
|
||||
async setCurrentSrc(elem, options) {
|
||||
elem.removeEventListener('error', this.onError);
|
||||
|
||||
@@ -577,8 +580,8 @@ export class HtmlVideoPlayer {
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
* @private
|
||||
*/
|
||||
getTextTracks() {
|
||||
const videoElement = this.#mediaElement;
|
||||
if (videoElement) {
|
||||
@@ -595,8 +598,8 @@ export class HtmlVideoPlayer {
|
||||
setSubtitleOffset = debounce(this._setSubtitleOffset, 100);
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
* @private
|
||||
*/
|
||||
_setSubtitleOffset(offset) {
|
||||
const offsetValue = parseFloat(offset);
|
||||
|
||||
@@ -624,8 +627,8 @@ export class HtmlVideoPlayer {
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
* @private
|
||||
*/
|
||||
updateCurrentTrackOffset(offsetValue, currentTrackIndex = PRIMARY_TEXT_TRACK_INDEX) {
|
||||
let offsetToCompare = this.#currentTrackOffset;
|
||||
if (this.isSecondaryTrack(currentTrackIndex)) {
|
||||
@@ -650,19 +653,19 @@ export class HtmlVideoPlayer {
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* These browsers will not clear the existing active cue when setting an offset
|
||||
* for native TextTracks.
|
||||
* Any previous text tracks that are on the screen when the offset changes will remain next
|
||||
* to the new tracks until they reach the end time of the new offset's instance of the track.
|
||||
*/
|
||||
* @private
|
||||
* These browsers will not clear the existing active cue when setting an offset
|
||||
* for native TextTracks.
|
||||
* Any previous text tracks that are on the screen when the offset changes will remain next
|
||||
* to the new tracks until they reach the end time of the new offset's instance of the track.
|
||||
*/
|
||||
requiresHidingActiveCuesOnOffsetChange() {
|
||||
return !!browser.firefox;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
* @private
|
||||
*/
|
||||
hideTextTrackWithActiveCues(currentTrack) {
|
||||
if (currentTrack.activeCues) {
|
||||
currentTrack.mode = 'hidden';
|
||||
@@ -670,11 +673,11 @@ export class HtmlVideoPlayer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces the active cue to clear by disabling then re-enabling the track.
|
||||
* The track mode is reverted inside of a 0ms timeout to free up the track
|
||||
* and allow it to disable and clear the active cue.
|
||||
* @private
|
||||
*/
|
||||
* Forces the active cue to clear by disabling then re-enabling the track.
|
||||
* The track mode is reverted inside of a 0ms timeout to free up the track
|
||||
* and allow it to disable and clear the active cue.
|
||||
* @private
|
||||
*/
|
||||
forceClearTextTrackActiveCues(currentTrack) {
|
||||
if (currentTrack.activeCues) {
|
||||
currentTrack.mode = 'disabled';
|
||||
@@ -685,8 +688,8 @@ export class HtmlVideoPlayer {
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
* @private
|
||||
*/
|
||||
setTextTrackSubtitleOffset(currentTrack, offsetValue, currentTrackIndex) {
|
||||
if (currentTrack.cues) {
|
||||
offsetValue = this.updateCurrentTrackOffset(offsetValue, currentTrackIndex);
|
||||
@@ -712,8 +715,8 @@ export class HtmlVideoPlayer {
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
* @private
|
||||
*/
|
||||
setTrackEventsSubtitleOffset(trackEvents, offsetValue, currentTrackIndex) {
|
||||
if (Array.isArray(trackEvents)) {
|
||||
offsetValue = this.updateCurrentTrackOffset(offsetValue, currentTrackIndex) * 1e7; // ticks
|
||||
@@ -740,8 +743,8 @@ export class HtmlVideoPlayer {
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
* @private
|
||||
*/
|
||||
isAudioStreamSupported(stream, deviceProfile, container) {
|
||||
const codec = (stream.Codec || '').toLowerCase();
|
||||
|
||||
@@ -764,8 +767,8 @@ export class HtmlVideoPlayer {
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
* @private
|
||||
*/
|
||||
getSupportedAudioStreams() {
|
||||
const profile = this.#lastProfile;
|
||||
|
||||
@@ -807,8 +810,8 @@ export class HtmlVideoPlayer {
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/audioTracks
|
||||
|
||||
/**
|
||||
* @type {ArrayLike<any>|any[]}
|
||||
*/
|
||||
* @type {ArrayLike<any>|any[]}
|
||||
*/
|
||||
const elemAudioTracks = elem.audioTracks || [];
|
||||
console.debug(`found ${elemAudioTracks.length} audio tracks`);
|
||||
|
||||
@@ -890,26 +893,26 @@ export class HtmlVideoPlayer {
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param e {Event} The event received from the `<video>` element
|
||||
*/
|
||||
* @private
|
||||
* @param e {Event} The event received from the `<video>` element
|
||||
*/
|
||||
onEnded = (e) => {
|
||||
/**
|
||||
* @type {HTMLMediaElement}
|
||||
*/
|
||||
* @type {HTMLMediaElement}
|
||||
*/
|
||||
const elem = e.target;
|
||||
this.destroyCustomTrack(elem);
|
||||
onEndedInternal(this, elem, this.onError);
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param e {Event} The event received from the `<video>` element
|
||||
*/
|
||||
* @private
|
||||
* @param e {Event} The event received from the `<video>` element
|
||||
*/
|
||||
onTimeUpdate = (e) => {
|
||||
/**
|
||||
* @type {HTMLMediaElement}
|
||||
*/
|
||||
* @type {HTMLMediaElement}
|
||||
*/
|
||||
const elem = e.target;
|
||||
// get the player position and the transcoding offset
|
||||
const time = elem.currentTime;
|
||||
@@ -933,21 +936,21 @@ export class HtmlVideoPlayer {
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param e {Event} The event received from the `<video>` element
|
||||
*/
|
||||
* @private
|
||||
* @param e {Event} The event received from the `<video>` element
|
||||
*/
|
||||
onVolumeChange = (e) => {
|
||||
/**
|
||||
* @type {HTMLMediaElement}
|
||||
*/
|
||||
* @type {HTMLMediaElement}
|
||||
*/
|
||||
const elem = e.target;
|
||||
saveVolume(elem.volume);
|
||||
Events.trigger(this, 'volumechange');
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
* @private
|
||||
*/
|
||||
onNavigatedToOsd = () => {
|
||||
const dlg = this.#videoDialog;
|
||||
if (dlg) {
|
||||
@@ -958,8 +961,8 @@ export class HtmlVideoPlayer {
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
* @private
|
||||
*/
|
||||
onStartedAndNavigatedToOsd() {
|
||||
// If this causes a failure during navigation we end up in an awkward UI state
|
||||
this.setCurrentTrackElement(this.#subtitleTrackIndexToSetOnPlaying);
|
||||
@@ -970,23 +973,23 @@ export class HtmlVideoPlayer {
|
||||
|
||||
if (this.#secondarySubtitleTrackIndexToSetOnPlaying != null && this.#secondarySubtitleTrackIndexToSetOnPlaying >= 0) {
|
||||
/**
|
||||
* Using a 0ms timeout to set the secondary subtitles because of some weird race condition when
|
||||
* setting both primary and secondary tracks at the same time.
|
||||
* The `TextTrack` content and cues will somehow get mixed up and each track will play a mix of both languages.
|
||||
* Putting this in a timeout fixes it completely.
|
||||
*/
|
||||
* Using a 0ms timeout to set the secondary subtitles because of some weird race condition when
|
||||
* setting both primary and secondary tracks at the same time.
|
||||
* The `TextTrack` content and cues will somehow get mixed up and each track will play a mix of both languages.
|
||||
* Putting this in a timeout fixes it completely.
|
||||
*/
|
||||
setTimeout(() => this.setSecondarySubtitleStreamIndex(this.#secondarySubtitleTrackIndexToSetOnPlaying), 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param e {Event} The event received from the `<video>` element
|
||||
*/
|
||||
* @private
|
||||
* @param e {Event} The event received from the `<video>` element
|
||||
*/
|
||||
onPlaying = (e) => {
|
||||
/**
|
||||
* @type {HTMLMediaElement}
|
||||
*/
|
||||
* @type {HTMLMediaElement}
|
||||
*/
|
||||
const elem = e.target;
|
||||
if (!this.#started) {
|
||||
this.#started = true;
|
||||
@@ -1015,15 +1018,15 @@ export class HtmlVideoPlayer {
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
* @private
|
||||
*/
|
||||
onPlay = () => {
|
||||
Events.trigger(this, 'unpause');
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
* @private
|
||||
*/
|
||||
ensureValidVideo(elem) {
|
||||
if (elem !== this.#mediaElement) {
|
||||
return;
|
||||
@@ -1041,22 +1044,22 @@ export class HtmlVideoPlayer {
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
* @private
|
||||
*/
|
||||
onClick = () => {
|
||||
Events.trigger(this, 'click');
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
* @private
|
||||
*/
|
||||
onDblClick = () => {
|
||||
Events.trigger(this, 'dblclick');
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
* @private
|
||||
*/
|
||||
onPause = () => {
|
||||
Events.trigger(this, 'pause');
|
||||
};
|
||||
@@ -1066,13 +1069,13 @@ export class HtmlVideoPlayer {
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param e {Event} The event received from the `<video>` element
|
||||
*/
|
||||
* @private
|
||||
* @param e {Event} The event received from the `<video>` element
|
||||
*/
|
||||
onError = (e) => {
|
||||
/**
|
||||
* @type {HTMLMediaElement}
|
||||
*/
|
||||
* @type {HTMLMediaElement}
|
||||
*/
|
||||
const elem = e.target;
|
||||
const errorCode = elem.error ? (elem.error.code || 0) : 0;
|
||||
const errorMessage = elem.error ? (elem.error.message || '') : '';
|
||||
@@ -1112,8 +1115,8 @@ export class HtmlVideoPlayer {
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
* @private
|
||||
*/
|
||||
destroyCustomRenderedTrackElements(targetTrackIndex) {
|
||||
if (this.isPrimaryTrack(targetTrackIndex)) {
|
||||
if (this.#videoSubtitlesElem) {
|
||||
@@ -1137,8 +1140,8 @@ export class HtmlVideoPlayer {
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
* @private
|
||||
*/
|
||||
destroyNativeTracks(videoElement, targetTrackIndex) {
|
||||
if (videoElement) {
|
||||
const destroySingleTrack = typeof targetTrackIndex === 'number';
|
||||
@@ -1157,8 +1160,8 @@ export class HtmlVideoPlayer {
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
* @private
|
||||
*/
|
||||
destroyStoredTrackInfo(targetTrackIndex) {
|
||||
if (this.isPrimaryTrack(targetTrackIndex)) {
|
||||
this.#customTrackIndex = -1;
|
||||
@@ -1175,8 +1178,8 @@ export class HtmlVideoPlayer {
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
* @private
|
||||
*/
|
||||
destroyCustomTrack(videoElement, targetTrackIndex) {
|
||||
this.destroyCustomRenderedTrackElements(targetTrackIndex);
|
||||
this.destroyNativeTracks(videoElement, targetTrackIndex);
|
||||
@@ -1196,8 +1199,8 @@ export class HtmlVideoPlayer {
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
* @private
|
||||
*/
|
||||
fetchSubtitlesUwp(track) {
|
||||
return Windows.Storage.StorageFile.getFileFromPathAsync(track.Path).then(function (storageFile) {
|
||||
return Windows.Storage.FileIO.readTextAsync(storageFile);
|
||||
@@ -1207,8 +1210,8 @@ export class HtmlVideoPlayer {
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
* @private
|
||||
*/
|
||||
async fetchSubtitles(track, item) {
|
||||
if (window.Windows && itemHelper.isLocalItem(item)) {
|
||||
return this.fetchSubtitlesUwp(track, item);
|
||||
@@ -1229,8 +1232,8 @@ export class HtmlVideoPlayer {
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
* @private
|
||||
*/
|
||||
setTrackForDisplay(videoElement, track, targetTextTrackIndex = PRIMARY_TEXT_TRACK_INDEX) {
|
||||
if (!track) {
|
||||
// Destroy all tracks by passing undefined if there is no valid primary track
|
||||
@@ -1262,8 +1265,8 @@ export class HtmlVideoPlayer {
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
* @private
|
||||
*/
|
||||
renderSsaAss(videoElement, track, item) {
|
||||
const supportedFonts = ['application/vnd.ms-opentype', 'application/x-truetype-font', 'font/otf', 'font/ttf', 'font/woff', 'font/woff2'];
|
||||
const availableFonts = [];
|
||||
@@ -1358,54 +1361,10 @@ export class HtmlVideoPlayer {
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
requiresCustomSubtitlesElement(userSettings) {
|
||||
const subtitleAppearance = userSettings.getSubtitleAppearanceSettings();
|
||||
switch (subtitleAppearance.subtitleStyling) {
|
||||
case 'Native':
|
||||
return false;
|
||||
case 'Custom':
|
||||
return true;
|
||||
default:
|
||||
// after a system update, ps4 isn't showing anything when creating a track element dynamically
|
||||
// going to have to do it ourselves
|
||||
if (browser.ps4) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Tizen 5 doesn't support displaying secondary subtitles
|
||||
if (browser.tizenVersion >= 5 || browser.web0s) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (browser.edge) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// font-size styling does not seem to work natively in firefox. Switching to custom subtitles element for firefox.
|
||||
if (browser.firefox) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (browser.iOS) {
|
||||
const userAgent = navigator.userAgent.toLowerCase();
|
||||
// works in the browser but not the native app
|
||||
if ((userAgent.includes('os 9') || userAgent.includes('os 8')) && !userAgent.includes('safari')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
* @private
|
||||
*/
|
||||
renderSubtitlesWithCustomElement(videoElement, track, item, targetTextTrackIndex) {
|
||||
Promise.all([import('../../scripts/settings/userSettings'), this.fetchSubtitles(track, item)]).then((results) => {
|
||||
const [userSettings, subtitleData] = results;
|
||||
this.fetchSubtitles(track, item).then((subtitleData) => {
|
||||
const subtitleAppearance = userSettings.getSubtitleAppearanceSettings();
|
||||
const subtitleVerticalPosition = parseInt(subtitleAppearance.verticalPosition, 10);
|
||||
|
||||
@@ -1441,20 +1400,18 @@ export class HtmlVideoPlayer {
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
* @private
|
||||
*/
|
||||
setSubtitleAppearance(elem, innerElem) {
|
||||
Promise.all([import('../../scripts/settings/userSettings'), import('../../components/subtitlesettings/subtitleappearancehelper')]).then(([userSettings, subtitleAppearanceHelper]) => {
|
||||
subtitleAppearanceHelper.applyStyles({
|
||||
text: innerElem,
|
||||
window: elem
|
||||
}, userSettings.getSubtitleAppearanceSettings());
|
||||
});
|
||||
subtitleAppearanceHelper.applyStyles({
|
||||
text: innerElem,
|
||||
window: elem
|
||||
}, userSettings.getSubtitleAppearanceSettings());
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
* @private
|
||||
*/
|
||||
getCueCss(appearance, selector) {
|
||||
return `${selector}::cue {
|
||||
${appearance.text.map((s) => s.value !== undefined && s.value !== '' ? `${s.name}:${s.value}!important;` : '').join('')}
|
||||
@@ -1462,29 +1419,25 @@ export class HtmlVideoPlayer {
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
* @private
|
||||
*/
|
||||
setCueAppearance() {
|
||||
Promise.all([import('../../scripts/settings/userSettings'), import('../../components/subtitlesettings/subtitleappearancehelper')]).then(([userSettings, subtitleAppearanceHelper]) => {
|
||||
const elementId = `${this.id}-cuestyle`;
|
||||
const elementId = `${this.id}-cuestyle`;
|
||||
|
||||
let styleElem = document.querySelector(`#${elementId}`);
|
||||
if (!styleElem) {
|
||||
styleElem = document.createElement('style');
|
||||
styleElem.id = elementId;
|
||||
document.getElementsByTagName('head')[0].appendChild(styleElem);
|
||||
}
|
||||
let styleElem = document.querySelector(`#${elementId}`);
|
||||
if (!styleElem) {
|
||||
styleElem = document.createElement('style');
|
||||
styleElem.id = elementId;
|
||||
document.getElementsByTagName('head')[0].appendChild(styleElem);
|
||||
}
|
||||
|
||||
styleElem.innerHTML = this.getCueCss(subtitleAppearanceHelper.getStyles(userSettings.getSubtitleAppearanceSettings()), '.htmlvideoplayer');
|
||||
});
|
||||
styleElem.innerHTML = this.getCueCss(subtitleAppearanceHelper.getStyles(userSettings.getSubtitleAppearanceSettings()), '.htmlvideoplayer');
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
* @private
|
||||
*/
|
||||
async renderTracksEvents(videoElement, track, item, targetTextTrackIndex = PRIMARY_TEXT_TRACK_INDEX) {
|
||||
const { currentSettings: userSettings } = await import('../../scripts/settings/userSettings');
|
||||
|
||||
if (!itemHelper.isLocalItem(item) || track.IsExternal) {
|
||||
const format = (track.Codec || '').toLowerCase();
|
||||
if (format === 'ssa' || format === 'ass') {
|
||||
@@ -1496,7 +1449,7 @@ export class HtmlVideoPlayer {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.requiresCustomSubtitlesElement(userSettings)) {
|
||||
if (useCustomSubtitles(userSettings)) {
|
||||
this.renderSubtitlesWithCustomElement(videoElement, track, item, targetTextTrackIndex);
|
||||
return;
|
||||
}
|
||||
@@ -1555,8 +1508,8 @@ export class HtmlVideoPlayer {
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
* @private
|
||||
*/
|
||||
updateSubtitleText(timeMs) {
|
||||
const allTrackEvents = [this.#currentTrackEvents, this.#currentSecondaryTrackEvents];
|
||||
const subtitleTextElements = [this.#videoSubtitlesElem, this.#videoSecondarySubtitlesElem];
|
||||
@@ -1587,8 +1540,8 @@ export class HtmlVideoPlayer {
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
* @private
|
||||
*/
|
||||
setCurrentTrackElement(streamIndex, targetTextTrackIndex) {
|
||||
console.debug(`setting new text track index to: ${streamIndex}`);
|
||||
|
||||
@@ -1648,8 +1601,8 @@ export class HtmlVideoPlayer {
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
* @private
|
||||
*/
|
||||
createMediaElement(options) {
|
||||
const dlg = document.querySelector('.videoPlayerContainer');
|
||||
|
||||
|
||||
45
src/scripts/browser.d.ts
vendored
Normal file
45
src/scripts/browser.d.ts
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
export default browser;
|
||||
/**
|
||||
* Browser detection utility.
|
||||
*/
|
||||
declare namespace browser {
|
||||
import version = version;
|
||||
export { version };
|
||||
import versionMajor = versionMajor;
|
||||
export { versionMajor };
|
||||
export let edge: boolean;
|
||||
export let edgeChromium: boolean;
|
||||
export let firefox: boolean;
|
||||
export let safari: boolean;
|
||||
export let osx: boolean;
|
||||
export let ipad: boolean;
|
||||
export let ps4: boolean;
|
||||
export let tv: boolean;
|
||||
export let mobile: boolean;
|
||||
export let xboxOne: boolean;
|
||||
export let animate: boolean;
|
||||
export let hisense: boolean;
|
||||
export let tizen: boolean;
|
||||
export let vidaa: boolean;
|
||||
export let web0s: boolean;
|
||||
export let edgeUwp: boolean;
|
||||
export let web0sVersion: number | undefined;
|
||||
export let tizenVersion: number | undefined;
|
||||
export let orsay: boolean;
|
||||
export let operaTv: boolean;
|
||||
export let slow: boolean;
|
||||
export let touch: boolean;
|
||||
export let keyboard: boolean;
|
||||
export { supportsCssAnimation };
|
||||
export let iOS: boolean;
|
||||
export let iOSVersion: number | undefined;
|
||||
}
|
||||
|
||||
declare namespace matched {
|
||||
export { browser };
|
||||
export { version };
|
||||
export let platform: string;
|
||||
export { versionMajor };
|
||||
}
|
||||
|
||||
declare function supportsCssAnimation(allowPrefix: any): any;
|
||||
@@ -600,7 +600,7 @@ export class UserSettings {
|
||||
|
||||
/**
|
||||
* Get subtitle appearance settings.
|
||||
* @param {string|undefined} key - Settings key.
|
||||
* @param {string|undefined} [key] - Settings key.
|
||||
* @return {Object} Subtitle appearance settings.
|
||||
*/
|
||||
getSubtitleAppearanceSettings(key) {
|
||||
|
||||
Reference in New Issue
Block a user