Merge pull request #2621 from iwalton3/auto-set-tracks

Add subtitle/audio auto-set feature.
This commit is contained in:
Bill Thornton 2021-09-07 12:28:21 -04:00 committed by GitHub
commit ee20a51c48
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 142 additions and 6 deletions

View File

@ -2107,7 +2107,7 @@ class PlaybackManager {
}
}
function playInternal(item, playOptions, onPlaybackStartedFn) {
function playInternal(item, playOptions, onPlaybackStartedFn, prevSource) {
if (item.IsPlaceHolder) {
loading.hide();
showPlaybackInfoErrorMessage(self, 'PlaybackErrorPlaceHolder');
@ -2132,7 +2132,7 @@ class PlaybackManager {
const mediaType = item.MediaType;
const onBitrateDetectionFailure = function () {
return playAfterBitrateDetect(getSavedMaxStreamingBitrate(ServerConnections.getApiClient(item.ServerId), mediaType), item, playOptions, onPlaybackStartedFn);
return playAfterBitrateDetect(getSavedMaxStreamingBitrate(ServerConnections.getApiClient(item.ServerId), mediaType), item, playOptions, onPlaybackStartedFn, prevSource);
};
if (!isServerItem(item) || itemHelper.isLocalItem(item)) {
@ -2145,7 +2145,7 @@ class PlaybackManager {
return apiClient.detectBitrate().then(function (bitrate) {
appSettings.maxStreamingBitrate(endpointInfo.IsInNetwork, mediaType, bitrate);
return playAfterBitrateDetect(bitrate, item, playOptions, onPlaybackStartedFn);
return playAfterBitrateDetect(bitrate, item, playOptions, onPlaybackStartedFn, prevSource);
}, onBitrateDetectionFailure);
} else {
onBitrateDetectionFailure();
@ -2226,7 +2226,104 @@ class PlaybackManager {
});
}
function playAfterBitrateDetect(maxBitrate, item, playOptions, onPlaybackStartedFn) {
function rankStreamType(prevIndex, prevSource, mediaSource, streamType) {
if (prevIndex == -1) {
console.debug(`AutoSet ${streamType} - No Stream Set`);
if (streamType == 'Subtitle')
mediaSource.DefaultSubtitleStreamIndex = -1;
return;
}
if (!prevSource.MediaStreams || !mediaSource.MediaStreams) {
console.debug(`AutoSet ${streamType} - No MediaStreams`);
return;
}
let bestStreamIndex = null;
let bestStreamScore = 0;
const prevStream = prevSource.MediaStreams[prevIndex];
if (!prevStream) {
console.debug(`AutoSet ${streamType} - No prevStream`);
return;
}
console.debug(`AutoSet ${streamType} - Previous was ${prevStream.Index} - ${prevStream.DisplayTitle}`);
let prevRelIndex = 0;
for (const stream of prevSource.MediaStreams) {
if (stream.Type != streamType)
continue;
if (stream.Index == prevIndex)
break;
prevRelIndex += 1;
}
let newRelIndex = 0;
for (const stream of mediaSource.MediaStreams) {
if (stream.Type != streamType)
continue;
let score = 0;
if (prevStream.Codec == stream.Codec)
score += 1;
if (prevRelIndex == newRelIndex)
score += 1;
if (prevStream.Title && prevStream.Title == stream.Title)
score += 2;
if (prevStream.Language && prevStream.Language != 'und' && prevStream.Language == stream.Language)
score += 2;
console.debug(`AutoSet ${streamType} - Score ${score} for ${stream.Index} - ${stream.DisplayTitle}`);
if (score > bestStreamScore && score >= 3) {
bestStreamScore = score;
bestStreamIndex = stream.Index;
}
newRelIndex += 1;
}
if (bestStreamIndex != null) {
console.debug(`AutoSet ${streamType} - Using ${bestStreamIndex} score ${bestStreamScore}.`);
if (streamType == 'Subtitle')
mediaSource.DefaultSubtitleStreamIndex = bestStreamIndex;
if (streamType == 'Audio')
mediaSource.DefaultAudioStreamIndex = bestStreamIndex;
} else {
console.debug(`AutoSet ${streamType} - Threshold not met. Using default.`);
}
}
function autoSetNextTracks(prevSource, mediaSource) {
try {
if (!prevSource) return;
if (!mediaSource) {
console.warn('AutoSet - No mediaSource');
return;
}
if (typeof prevSource.DefaultAudioStreamIndex != 'number'
|| typeof prevSource.DefaultSubtitleStreamIndex != 'number')
return;
if (typeof mediaSource.DefaultAudioStreamIndex != 'number'
|| typeof mediaSource.DefaultSubtitleStreamIndex != 'number') {
console.warn('AutoSet - No stream indexes (but prevSource has them)');
return;
}
rankStreamType(prevSource.DefaultAudioStreamIndex, prevSource, mediaSource, 'Audio');
rankStreamType(prevSource.DefaultSubtitleStreamIndex, prevSource, mediaSource, 'Subtitle');
} catch (e) {
console.error(`AutoSet - Caught unexpected error: ${e}`);
}
}
function playAfterBitrateDetect(maxBitrate, item, playOptions, onPlaybackStartedFn, prevSource) {
const startPosition = playOptions.startPositionTicks;
const player = getPlayer(item, playOptions);
@ -2285,6 +2382,9 @@ class PlaybackManager {
playOptions.items = null;
return getPlaybackMediaSource(player, apiClient, deviceProfile, maxBitrate, item, startPosition, mediaSourceId, audioStreamIndex, subtitleStreamIndex).then(function (mediaSource) {
if (userSettings.enableSetUsingLastTracks())
autoSetNextTracks(prevSource, mediaSource);
const streamInfo = createStreamInfo(apiClient, item.MediaType, item, mediaSource, startPosition, player);
streamInfo.fullscreen = playOptions.fullscreen;
@ -2643,6 +2743,16 @@ class PlaybackManager {
return self.previousTrack(player);
};
function getPreviousSource(player) {
const prevSource = self.currentMediaSource(player);
const prevPlayerData = getPlayerData(player);
return {
...prevSource,
DefaultAudioStreamIndex: prevPlayerData.audioStreamIndex,
DefaultSubtitleStreamIndex: prevPlayerData.subtitleStreamIndex
};
}
self.nextTrack = function (player) {
player = player || self._currentPlayer;
if (player && !enableLocalPlaylistManagement(player)) {
@ -2658,7 +2768,7 @@ class PlaybackManager {
playInternal(newItemInfo.item, newItemPlayOptions, function () {
setPlaylistState(newItemInfo.item.PlaylistItemId, newItemInfo.index);
});
}, getPreviousSource(player));
}
};
@ -2679,7 +2789,7 @@ class PlaybackManager {
playInternal(newItem, newItemPlayOptions, function () {
setPlaylistState(newItem.PlaylistItemId, newIndex);
});
}, getPreviousSource(player));
}
}
};

View File

@ -195,6 +195,7 @@ import template from './playbackSettings.template.html';
context.querySelector('.chkPreferFmp4HlsContainer').checked = userSettings.preferFmp4HlsContainer();
context.querySelector('.chkEnableCinemaMode').checked = userSettings.enableCinemaMode();
context.querySelector('.chkEnableNextVideoOverlay').checked = userSettings.enableNextVideoInfoOverlay();
context.querySelector('.chkSetUsingLastTracks').checked = userSettings.enableSetUsingLastTracks();
context.querySelector('.chkExternalVideoPlayer').checked = appSettings.enableSystemExternalPlayers();
setMaxBitrateIntoField(context.querySelector('.selectVideoInNetworkQuality'), true, 'Video');
@ -236,6 +237,7 @@ import template from './playbackSettings.template.html';
userSettingsInstance.enableCinemaMode(context.querySelector('.chkEnableCinemaMode').checked);
userSettingsInstance.enableNextVideoInfoOverlay(context.querySelector('.chkEnableNextVideoOverlay').checked);
userSettingsInstance.enableSetUsingLastTracks(context.querySelector('.chkSetUsingLastTracks').checked);
userSettingsInstance.chromecastVersion(context.querySelector('.selectChromecastVersion').value);
userSettingsInstance.skipForwardLength(context.querySelector('.selectSkipForwardLength').value);
userSettingsInstance.skipBackLength(context.querySelector('.selectSkipBackLength').value);

View File

@ -82,6 +82,14 @@
</label>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input type="checkbox" is="emby-checkbox" class="chkSetUsingLastTracks" />
<span>${SetUsingLastTracks}</span>
</label>
<div class="fieldDescription checkboxFieldDescription">${SetUsingLastTracksHelp}</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription fldEnableNextVideoOverlay hide">
<label>
<input type="checkbox" is="emby-checkbox" class="chkEnableNextVideoOverlay" />

View File

@ -169,6 +169,19 @@ export class UserSettings {
return val !== 'false';
}
/**
* Get or set 'SetUsingLastTracks' state.
* @param {boolean|undefined} val - Flag to enable 'SetUsingLastTracks' or undefined.
* @return {boolean} 'SetUsingLastTracks' state.
*/
enableSetUsingLastTracks(val) {
if (val !== undefined) {
return this.set('enableSetUsingLastTracks', val.toString());
}
return this.get('enableSetUsingLastTracks', false) !== 'false';
}
/**
* Get or set 'Theme Songs' state.
* @param {boolean|undefined} val - Flag to enable 'Theme Songs' or undefined.
@ -528,6 +541,7 @@ export const allowedAudioChannels = currentSettings.allowedAudioChannels.bind(cu
export const preferFmp4HlsContainer = currentSettings.preferFmp4HlsContainer.bind(currentSettings);
export const enableCinemaMode = currentSettings.enableCinemaMode.bind(currentSettings);
export const enableNextVideoInfoOverlay = currentSettings.enableNextVideoInfoOverlay.bind(currentSettings);
export const enableSetUsingLastTracks = currentSettings.enableSetUsingLastTracks.bind(currentSettings);
export const enableThemeSongs = currentSettings.enableThemeSongs.bind(currentSettings);
export const enableThemeVideos = currentSettings.enableThemeVideos.bind(currentSettings);
export const enableFastFadein = currentSettings.enableFastFadein.bind(currentSettings);

View File

@ -1362,6 +1362,8 @@
"Settings": "Settings",
"SettingsSaved": "Settings saved.",
"SettingsWarning": "Changing these values may cause instability or connectivity failures. If you experience any problems, we recommend changing them back to default.",
"SetUsingLastTracks": "Set Subtitle/Audio Tracks with Previous Item",
"SetUsingLastTracksHelp": "Attempts to set the Subtitle/Audio track to the closest match to the last video.",
"Share": "Share",
"ShowAdvancedSettings": "Show advanced settings",
"ShowIndicatorsFor": "Show indicators for:",