diff --git a/config/defaults.js b/config/defaults.js
index 2d811c60..118e4a4c 100644
--- a/config/defaults.js
+++ b/config/defaults.js
@@ -44,7 +44,7 @@ const defaults = {
default_sync_mode: 'cleanseek',
slplayer_plex_timeline_update_interval: 10000,
- slplayer_controls_visible_checker_interval: 500,
+ slplayer_controls_visible_checker_interval: 250,
// Controlls the max time difference cutoff for syncing by changing the playback speed
slplayer_speed_sync_max_diff: 10000,
diff --git a/src/player/index.js b/src/player/index.js
index 22e2e4d9..595a1094 100644
--- a/src/player/index.js
+++ b/src/player/index.js
@@ -25,11 +25,6 @@ export const setVolume = (volume) => {
export const play = () => getPlayer().getMediaElement().play();
export const pause = () => getPlayer().getMediaElement().pause();
-// eslint-disable-next-line no-underscore-dangle
-export const areControlsShown = () => getOverlay().getControls().enabled_
- && (getOverlay().getControls().getControlsContainer().getAttribute('shown') != null
- || getOverlay().getControls().getControlsContainer().getAttribute('casting') != null);
-
export const isTimeInBufferedRange = (timeMs) => {
const bufferedTimeRange = getPlayer().getMediaElement().buffered;
diff --git a/src/player/state.js b/src/player/state.js
index 8ad531b6..feaeab3f 100644
--- a/src/player/state.js
+++ b/src/player/state.js
@@ -20,6 +20,15 @@ export const setOverlay = (newOverlay) => {
overlay = newOverlay;
};
+// eslint-disable-next-line no-underscore-dangle
+export const areControlsShown = () => !getOverlay() || (getOverlay()?.getControls().enabled_
+ && (getOverlay()?.getControls().getControlsContainer().getAttribute('shown') != null
+ || getOverlay()?.getControls().getControlsContainer().getAttribute('casting') != null));
+
+export const getControlsOffset = (fallbackHeight) => (areControlsShown()
+ ? (getPlayer()?.getMediaElement().offsetHeight || fallbackHeight) * 0.025 + 48 || 0
+ : 0);
+
/**
* Resize the subtitles to the dimensions of the video element.
*
@@ -31,15 +40,18 @@ export const resizeSubtitleContainer = () => {
return;
}
+ const bottomOffset = getControlsOffset();
+ console.debug('resizeSubtitleContainer', bottomOffset);
+
const {
videoWidth, videoHeight, offsetWidth, offsetHeight,
} = getPlayer().getMediaElement();
- const ratio = Math.min(offsetWidth / videoWidth, offsetHeight / videoHeight);
+ const ratio = Math.min(offsetWidth / videoWidth, (offsetHeight - bottomOffset) / videoHeight);
const subsWrapperWidth = videoWidth * ratio;
const subsWrapperHeight = videoHeight * ratio;
const subsWrapperLeft = (offsetWidth - subsWrapperWidth) / 2;
- const subsWrapperTop = (offsetHeight - subsWrapperHeight) / 2;
+ const subsWrapperTop = ((offsetHeight - bottomOffset) - subsWrapperHeight) / 2;
subtitleRenderer.resize(subsWrapperWidth, subsWrapperHeight, subsWrapperLeft, subsWrapperTop);
};
@@ -94,6 +106,18 @@ const initRenderer = async (ass) => {
resizeSubtitleContainer();
};
+const handleStreamError = async (assPromise) => {
+ try {
+ await assPromise;
+ } catch (e) {
+ if (assAbortController) {
+ // If there is no abort controller, we have just aborted
+ // If there is one, then something went wrong
+ throw e;
+ }
+ }
+};
+
const makeAss = async (url) => {
console.debug('makeAss');
const libjass = await import('libjass');
@@ -101,6 +125,9 @@ const makeAss = async (url) => {
const stream = resiliantStreamFactory(url, assAbortController.signal);
const parser = new libjass.parser.StreamParser(stream);
+ // Purposefully not awaited because we never get the full file at once
+ // We still need to catch abort errors to clean up console
+ handleStreamError(parser.ass);
return parser.minimalASS;
};
@@ -130,8 +157,9 @@ export const setSubtitleUrl = async (url) => {
const ass = await makeAss(url);
if (subtitleRenderer) {
- // eslint-disable-next-line no-underscore-dangle
+ // eslint-disable-next-line no-underscore-dangle
subtitleRenderer._ass = ass;
+ resizeSubtitleContainer();
} else {
await initRenderer(ass);
}
diff --git a/src/store/modules/plexclients/getters.js b/src/store/modules/plexclients/getters.js
index 93e25529..f90daf3e 100644
--- a/src/store/modules/plexclients/getters.js
+++ b/src/store/modules/plexclients/getters.js
@@ -79,4 +79,10 @@ export default {
: false),
GET_LAST_PLAY_MEDIA_COMMAND_ID: (state) => state.lastPlayMediaCommandId,
+
+ GET_ACTIVE_MEDIA_METADATA_MARKERS: (state, getters) => getters
+ .GET_ACTIVE_MEDIA_METADATA?.Marker || [],
+
+ GET_ACTIVE_MEDIA_METADATA_INTRO_MARKER: (state, getters) => getters
+ .GET_ACTIVE_MEDIA_METADATA_MARKERS.find((marker) => marker.type === 'intro'),
};
diff --git a/src/store/modules/plexservers/actions.js b/src/store/modules/plexservers/actions.js
index 168d4d3c..945044fe 100644
--- a/src/store/modules/plexservers/actions.js
+++ b/src/store/modules/plexservers/actions.js
@@ -89,7 +89,10 @@ export default {
includeStations: 1,
includeExternalMedia: 1,
asyncAugmentMetadata: 1,
+ asyncRefreshLocalMediaAgent: 1,
+ asyncRefreshAnalysis: 1,
checkFiles: 1,
+ includeMarkers: 1,
},
signal,
});
diff --git a/src/store/modules/slplayer/actions.js b/src/store/modules/slplayer/actions.js
index 041d3dea..7c7ca8a1 100644
--- a/src/store/modules/slplayer/actions.js
+++ b/src/store/modules/slplayer/actions.js
@@ -3,13 +3,16 @@ import CAF from 'caf';
import guid from '@/utils/guid';
import { fetchJson, queryFetch, makeUrl } from '@/utils/fetchutils';
import {
- play, pause, getDurationMs, areControlsShown, getCurrentTimeMs, isTimeInBufferedRange,
+ play, pause, getDurationMs, getCurrentTimeMs, isTimeInBufferedRange,
isMediaElementAttached, isPlaying, isPresentationPaused, isBuffering, getVolume, isPaused,
waitForMediaElementEvent, destroy, cancelTrickPlay, load, setPlaybackRate, getPlaybackRate,
setCurrentTimeMs, setVolume, addEventListener, removeEventListener,
- getSmallPlayButton, getBigPlayButton, unload,
+ getSmallPlayButton, getBigPlayButton, unload, addMediaElementEventListener,
+ removeMediaElementEventListener,
} from '@/player';
-import { destroySubtitles, setSubtitleUrl, destroyAss } from '@/player/state';
+import {
+ destroySubtitles, setSubtitleUrl, destroyAss, areControlsShown, resizeSubtitleContainer,
+} from '@/player/state';
import Deferred from '@/utils/deferredpromise';
export default {
@@ -350,6 +353,7 @@ export default {
dispatch('START_PERIODIC_PLEX_TIMELINE_UPDATE');
} catch (e) {
if (getters.GET_PLAYER_INITIALIZED_DEFERRED_PROMISE) {
+ // TODO: potentially close player
getters.GET_PLAYER_INITIALIZED_DEFERRED_PROMISE.reject(e);
commit('SET_PLAYER_INITIALIZED_DEFERRED_PROMISE', null);
}
@@ -373,6 +377,7 @@ export default {
DESTROY_PLAYER_STATE: async ({ commit, dispatch }) => {
console.debug('DESTROY_PLAYER_STATE');
commit('STOP_UPDATE_PLAYER_CONTROLS_SHOWN_INTERVAL');
+ commit('UPDATE_PLAYER_CONTROLS_SHOWN', false);
await dispatch('UNREGISTER_PLAYER_EVENTS');
await dispatch('CANCEL_PERIODIC_PLEX_TIMELINE_UPDATE');
@@ -462,4 +467,8 @@ export default {
await dispatch('plexclients/UPDATE_ACTIVE_PLAY_QUEUE', null, { root: true });
},
+
+ SKIP_INTRO: () => {
+ console.debug('SKIP_INTRO');
+ },
};
diff --git a/src/store/modules/slplayer/state.js b/src/store/modules/slplayer/state.js
index cab04ec2..8484d94c 100644
--- a/src/store/modules/slplayer/state.js
+++ b/src/store/modules/slplayer/state.js
@@ -7,7 +7,7 @@ const state = () => ({
mediaIndex: 0,
offsetMs: 0,
playerState: 'stopped',
- playerControlsShown: true,
+ playerControlsShown: false,
playerControlsShownInterval: null,
bufferingEventListener: null,
clickEventListener: null,
diff --git a/src/views/slplayer.vue b/src/views/slplayer.vue
index 391927b6..697f036b 100644
--- a/src/views/slplayer.vue
+++ b/src/views/slplayer.vue
@@ -25,6 +25,19 @@
@enterpictureinpicture="HANDLE_PICTURE_IN_PICTURE_CHANGE"
@leavepictureinpicture="HANDLE_PICTURE_IN_PICTURE_CHANGE"
/>
+
+
+ Skip Intro
+