mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
WIP: set theme music volume level via user setting
This commit is contained in:
parent
ccd1070417
commit
56f8ec5947
10 changed files with 99 additions and 1 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -21,3 +21,7 @@ config.json
|
||||||
|
|
||||||
# environment related
|
# environment related
|
||||||
.envrc
|
.envrc
|
||||||
|
|
||||||
|
# other
|
||||||
|
repomix.config.json
|
||||||
|
repomix-output.txt
|
|
@ -1,4 +1,5 @@
|
||||||
import Checkbox from '@mui/material/Checkbox';
|
import Checkbox from '@mui/material/Checkbox';
|
||||||
|
import Slider from '@mui/material/Slider';
|
||||||
import FormControl from '@mui/material/FormControl';
|
import FormControl from '@mui/material/FormControl';
|
||||||
import FormControlLabel from '@mui/material/FormControlLabel';
|
import FormControlLabel from '@mui/material/FormControlLabel';
|
||||||
import FormHelperText from '@mui/material/FormHelperText';
|
import FormHelperText from '@mui/material/FormHelperText';
|
||||||
|
@ -13,10 +14,11 @@ import type { DisplaySettingsValues } from '../types/displaySettingsValues';
|
||||||
|
|
||||||
interface LibraryPreferencesProps {
|
interface LibraryPreferencesProps {
|
||||||
onChange: (event: React.SyntheticEvent) => void;
|
onChange: (event: React.SyntheticEvent) => void;
|
||||||
|
onSliderChange: () => (event: Event, value: number | number[]) => void;
|
||||||
values: DisplaySettingsValues;
|
values: DisplaySettingsValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function LibraryPreferences({ onChange, values }: Readonly<LibraryPreferencesProps>) {
|
export function LibraryPreferences({ onChange, onSliderChange, values }: Readonly<LibraryPreferencesProps>) {
|
||||||
return (
|
return (
|
||||||
<Stack spacing={3}>
|
<Stack spacing={3}>
|
||||||
<Typography variant='h2'>{globalize.translate('HeaderLibraries')}</Typography>
|
<Typography variant='h2'>{globalize.translate('HeaderLibraries')}</Typography>
|
||||||
|
@ -79,6 +81,30 @@ export function LibraryPreferences({ onChange, values }: Readonly<LibraryPrefere
|
||||||
</FormHelperText>
|
</FormHelperText>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
|
<FormControl fullWidth>
|
||||||
|
<FormControlLabel
|
||||||
|
aria-describedby='display-settings-lib-theme-song-volume-level-description'
|
||||||
|
control={
|
||||||
|
<Slider
|
||||||
|
disabled={!values.enableLibraryThemeSongs}
|
||||||
|
valueLabelDisplay='auto'
|
||||||
|
value={values.libraryThemeSongsVolumeLevel}
|
||||||
|
marks
|
||||||
|
defaultValue={50}
|
||||||
|
min={0}
|
||||||
|
max={100}
|
||||||
|
step={5}
|
||||||
|
onChange={onSliderChange}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label={globalize.translate('LabelThemeSongVolume')}
|
||||||
|
name='libraryThemeSongsVolumeLevel'
|
||||||
|
/>
|
||||||
|
<FormHelperText id='display-settings-lib-theme-song-volume-level-description'>
|
||||||
|
{globalize.translate('ThemeSongVolumeHelp')}
|
||||||
|
</FormHelperText>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
<FormControl fullWidth>
|
<FormControl fullWidth>
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
aria-describedby='display-settings-lib-theme-videos-description'
|
aria-describedby='display-settings-lib-theme-videos-description'
|
||||||
|
|
|
@ -87,6 +87,7 @@ async function loadDisplaySettings({
|
||||||
enableItemDetailsBanner: Boolean(settings.detailsBanner()),
|
enableItemDetailsBanner: Boolean(settings.detailsBanner()),
|
||||||
enableLibraryBackdrops: Boolean(settings.enableBackdrops()),
|
enableLibraryBackdrops: Boolean(settings.enableBackdrops()),
|
||||||
enableLibraryThemeSongs: Boolean(settings.enableThemeSongs()),
|
enableLibraryThemeSongs: Boolean(settings.enableThemeSongs()),
|
||||||
|
libraryThemeSongsVolumeLevel: settings.themeSongsVolumeLevel(),
|
||||||
enableLibraryThemeVideos: Boolean(settings.enableThemeVideos()),
|
enableLibraryThemeVideos: Boolean(settings.enableThemeVideos()),
|
||||||
enableRewatchingInNextUp: Boolean(settings.enableRewatchingInNextUp()),
|
enableRewatchingInNextUp: Boolean(settings.enableRewatchingInNextUp()),
|
||||||
episodeImagesInNextUp: Boolean(settings.useEpisodeImagesInNextUpAndResume()),
|
episodeImagesInNextUp: Boolean(settings.useEpisodeImagesInNextUpAndResume()),
|
||||||
|
@ -132,6 +133,7 @@ async function saveDisplaySettings({
|
||||||
userSettings.detailsBanner(newDisplaySettings.enableItemDetailsBanner);
|
userSettings.detailsBanner(newDisplaySettings.enableItemDetailsBanner);
|
||||||
userSettings.enableBackdrops(newDisplaySettings.enableLibraryBackdrops);
|
userSettings.enableBackdrops(newDisplaySettings.enableLibraryBackdrops);
|
||||||
userSettings.enableThemeSongs(newDisplaySettings.enableLibraryThemeSongs);
|
userSettings.enableThemeSongs(newDisplaySettings.enableLibraryThemeSongs);
|
||||||
|
userSettings.themeSongsVolumeLevel(newDisplaySettings.libraryThemeSongsVolumeLevel);
|
||||||
userSettings.enableThemeVideos(newDisplaySettings.enableLibraryThemeVideos);
|
userSettings.enableThemeVideos(newDisplaySettings.enableLibraryThemeVideos);
|
||||||
userSettings.enableRewatchingInNextUp(newDisplaySettings.enableRewatchingInNextUp);
|
userSettings.enableRewatchingInNextUp(newDisplaySettings.enableRewatchingInNextUp);
|
||||||
userSettings.useEpisodeImagesInNextUpAndResume(newDisplaySettings.episodeImagesInNextUp);
|
userSettings.useEpisodeImagesInNextUpAndResume(newDisplaySettings.episodeImagesInNextUp);
|
||||||
|
|
|
@ -9,6 +9,7 @@ export interface DisplaySettingsValues {
|
||||||
enableItemDetailsBanner: boolean;
|
enableItemDetailsBanner: boolean;
|
||||||
enableLibraryBackdrops: boolean;
|
enableLibraryBackdrops: boolean;
|
||||||
enableLibraryThemeSongs: boolean;
|
enableLibraryThemeSongs: boolean;
|
||||||
|
libraryThemeSongsVolumeLevel: number;
|
||||||
enableLibraryThemeVideos: boolean;
|
enableLibraryThemeVideos: boolean;
|
||||||
enableRewatchingInNextUp: boolean;
|
enableRewatchingInNextUp: boolean;
|
||||||
episodeImagesInNextUp: boolean;
|
episodeImagesInNextUp: boolean;
|
||||||
|
|
|
@ -40,6 +40,19 @@ export default function UserDisplayPreferences() {
|
||||||
}
|
}
|
||||||
}, [updateField, values]);
|
}, [updateField, values]);
|
||||||
|
|
||||||
|
const handleSliderChange = useCallback(() => (e: Event, newValue: number | number[]) => {
|
||||||
|
const target = e.target as HTMLInputElement;
|
||||||
|
const fieldName = target.name as keyof DisplaySettingsValues;
|
||||||
|
const value = Array.isArray(newValue) ? newValue[0] : newValue;
|
||||||
|
|
||||||
|
if (values?.[fieldName] !== value) {
|
||||||
|
updateField({
|
||||||
|
name: fieldName,
|
||||||
|
value: value.toString()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [updateField, values]);
|
||||||
|
|
||||||
if (loading || !values) {
|
if (loading || !values) {
|
||||||
return <LoadingComponent />;
|
return <LoadingComponent />;
|
||||||
}
|
}
|
||||||
|
@ -66,6 +79,7 @@ export default function UserDisplayPreferences() {
|
||||||
/>
|
/>
|
||||||
<LibraryPreferences
|
<LibraryPreferences
|
||||||
onChange={handleFieldChange}
|
onChange={handleFieldChange}
|
||||||
|
onSliderChange={handleSliderChange}
|
||||||
values={values}
|
values={values}
|
||||||
/>
|
/>
|
||||||
<NextUpPreferences
|
<NextUpPreferences
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import escapeHtml from 'escape-html';
|
import escapeHtml from 'escape-html';
|
||||||
|
import dom from '../../scripts/dom';
|
||||||
import browser from '../../scripts/browser';
|
import browser from '../../scripts/browser';
|
||||||
import layoutManager from '../layoutManager';
|
import layoutManager from '../layoutManager';
|
||||||
import { pluginManager } from '../pluginManager';
|
import { pluginManager } from '../pluginManager';
|
||||||
|
@ -67,6 +68,19 @@ function showOrHideMissingEpisodesField(context) {
|
||||||
context.querySelector('.fldDisplayMissingEpisodes').classList.remove('hide');
|
context.querySelector('.fldDisplayMissingEpisodes').classList.remove('hide');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateThemeSongVolumeDisabledState(context) {
|
||||||
|
const isEnabled = context.querySelector('#chkThemeSong').checked;
|
||||||
|
const sliderElement = context.querySelector('#sliderThemeSongVolume');
|
||||||
|
sliderElement.disabled = !isEnabled;
|
||||||
|
|
||||||
|
sliderElement.classList.toggle('disabled', !isEnabled);
|
||||||
|
// Find the slider container and toggle the disabled class on it
|
||||||
|
const sliderContainer = dom.parentWithClass(sliderElement, 'sliderContainer-settings');
|
||||||
|
if (sliderContainer) {
|
||||||
|
sliderContainer.classList.toggle('disabled', !isEnabled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function loadForm(context, user, userSettings) {
|
function loadForm(context, user, userSettings) {
|
||||||
if (appHost.supports('displaylanguage')) {
|
if (appHost.supports('displaylanguage')) {
|
||||||
context.querySelector('.languageSection').classList.remove('hide');
|
context.querySelector('.languageSection').classList.remove('hide');
|
||||||
|
@ -115,6 +129,11 @@ function loadForm(context, user, userSettings) {
|
||||||
context.querySelector('.chkDisplayMissingEpisodes').checked = user.Configuration.DisplayMissingEpisodes || false;
|
context.querySelector('.chkDisplayMissingEpisodes').checked = user.Configuration.DisplayMissingEpisodes || false;
|
||||||
|
|
||||||
context.querySelector('#chkThemeSong').checked = userSettings.enableThemeSongs();
|
context.querySelector('#chkThemeSong').checked = userSettings.enableThemeSongs();
|
||||||
|
context.querySelector('#chkThemeSong').addEventListener('change', function() {
|
||||||
|
updateThemeSongVolumeDisabledState(context);
|
||||||
|
});
|
||||||
|
context.querySelector('#sliderThemeSongVolume').value = userSettings.themeSongsVolumeLevel();
|
||||||
|
updateThemeSongVolumeDisabledState(context);
|
||||||
context.querySelector('#chkThemeVideo').checked = userSettings.enableThemeVideos();
|
context.querySelector('#chkThemeVideo').checked = userSettings.enableThemeVideos();
|
||||||
context.querySelector('#chkFadein').checked = userSettings.enableFastFadein();
|
context.querySelector('#chkFadein').checked = userSettings.enableFastFadein();
|
||||||
context.querySelector('#chkBlurhash').checked = userSettings.enableBlurhash();
|
context.querySelector('#chkBlurhash').checked = userSettings.enableBlurhash();
|
||||||
|
@ -150,6 +169,7 @@ function saveUser(context, user, userSettingsInstance, apiClient) {
|
||||||
userSettingsInstance.dateTimeLocale(context.querySelector('.selectDateTimeLocale').value);
|
userSettingsInstance.dateTimeLocale(context.querySelector('.selectDateTimeLocale').value);
|
||||||
|
|
||||||
userSettingsInstance.enableThemeSongs(context.querySelector('#chkThemeSong').checked);
|
userSettingsInstance.enableThemeSongs(context.querySelector('#chkThemeSong').checked);
|
||||||
|
userSettingsInstance.themeSongsVolumeLevel(context.querySelector('#sliderThemeSongVolume').value);
|
||||||
userSettingsInstance.enableThemeVideos(context.querySelector('#chkThemeVideo').checked);
|
userSettingsInstance.enableThemeVideos(context.querySelector('#chkThemeVideo').checked);
|
||||||
userSettingsInstance.theme(context.querySelector('#selectTheme').value);
|
userSettingsInstance.theme(context.querySelector('#selectTheme').value);
|
||||||
userSettingsInstance.dashboardTheme(context.querySelector('#selectDashboardTheme').value);
|
userSettingsInstance.dashboardTheme(context.querySelector('#selectDashboardTheme').value);
|
||||||
|
|
|
@ -255,6 +255,13 @@
|
||||||
<div class="fieldDescription checkboxFieldDescription">${EnableThemeSongsHelp}</div>
|
<div class="fieldDescription checkboxFieldDescription">${EnableThemeSongsHelp}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="sliderContainer-settings">
|
||||||
|
<div class="sliderContainer">
|
||||||
|
<input is="emby-slider" id="sliderThemeSongVolume" label="${LabelThemeSongVolume}" type="range" min="0" max="100" step="1" value="50" />
|
||||||
|
</div>
|
||||||
|
<div class="fieldDescription">${ThemeSongVolumeHelp}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="checkboxContainer checkboxContainer-withDescription fldThemeVideo">
|
<div class="checkboxContainer checkboxContainer-withDescription fldThemeVideo">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" is="emby-checkbox" id="chkThemeVideo" />
|
<input type="checkbox" is="emby-checkbox" id="chkThemeVideo" />
|
||||||
|
|
|
@ -248,6 +248,14 @@
|
||||||
height: 2.83em; /* similar to emby-input with its 110% font-size */
|
height: 2.83em; /* similar to emby-input with its 110% font-size */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sliderContainer-settings.disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sliderContainer-settings.disabled .sliderBubble {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.sliderLabel {
|
.sliderLabel {
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: 0.25em;
|
margin-bottom: 0.25em;
|
||||||
|
|
|
@ -230,6 +230,19 @@ export class UserSettings {
|
||||||
return toBoolean(this.get('enableThemeSongs', false), false);
|
return toBoolean(this.get('enableThemeSongs', false), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get or set 'Theme Songs' volume level.
|
||||||
|
* @param {number|undefined} [val] - Volume level to set.
|
||||||
|
* @return {number} Current volume level.
|
||||||
|
*/
|
||||||
|
themeSongsVolumeLevel(val) {
|
||||||
|
if (val !== undefined) {
|
||||||
|
return this.set('themeSongsVolumeLevel', val.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseInt(this.get('themeSongsVolumeLevel') || '100', 10);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get or set 'Theme Videos' state.
|
* Get or set 'Theme Videos' state.
|
||||||
* @param {boolean|undefined} [val] - Flag to enable 'Theme Videos' or undefined.
|
* @param {boolean|undefined} [val] - Flag to enable 'Theme Videos' or undefined.
|
||||||
|
@ -676,6 +689,7 @@ export const selectAudioNormalization = currentSettings.selectAudioNormalization
|
||||||
export const enableNextVideoInfoOverlay = currentSettings.enableNextVideoInfoOverlay.bind(currentSettings);
|
export const enableNextVideoInfoOverlay = currentSettings.enableNextVideoInfoOverlay.bind(currentSettings);
|
||||||
export const enableVideoRemainingTime = currentSettings.enableVideoRemainingTime.bind(currentSettings);
|
export const enableVideoRemainingTime = currentSettings.enableVideoRemainingTime.bind(currentSettings);
|
||||||
export const enableThemeSongs = currentSettings.enableThemeSongs.bind(currentSettings);
|
export const enableThemeSongs = currentSettings.enableThemeSongs.bind(currentSettings);
|
||||||
|
export const themeSongsVolumeLevel = currentSettings.themeSongsVolumeLevel.bind(currentSettings);
|
||||||
export const enableThemeVideos = currentSettings.enableThemeVideos.bind(currentSettings);
|
export const enableThemeVideos = currentSettings.enableThemeVideos.bind(currentSettings);
|
||||||
export const enableFastFadein = currentSettings.enableFastFadein.bind(currentSettings);
|
export const enableFastFadein = currentSettings.enableFastFadein.bind(currentSettings);
|
||||||
export const enableBlurhash = currentSettings.enableBlurhash.bind(currentSettings);
|
export const enableBlurhash = currentSettings.enableBlurhash.bind(currentSettings);
|
||||||
|
|
|
@ -960,6 +960,7 @@
|
||||||
"LabelTextWeight": "Text weight",
|
"LabelTextWeight": "Text weight",
|
||||||
"Bold": "Bold",
|
"Bold": "Bold",
|
||||||
"LabelTheme": "Theme",
|
"LabelTheme": "Theme",
|
||||||
|
"LabelThemeSongVolume": "Theme song volume",
|
||||||
"LabelTime": "Time",
|
"LabelTime": "Time",
|
||||||
"LabelTimeLimitHours": "Time limit (hours)",
|
"LabelTimeLimitHours": "Time limit (hours)",
|
||||||
"LabelTitle": "Title",
|
"LabelTitle": "Title",
|
||||||
|
@ -1578,6 +1579,7 @@
|
||||||
"TellUsAboutYourself": "Tell us about yourself",
|
"TellUsAboutYourself": "Tell us about yourself",
|
||||||
"TextSent": "Text sent.",
|
"TextSent": "Text sent.",
|
||||||
"ThemeSongs": "Theme songs",
|
"ThemeSongs": "Theme songs",
|
||||||
|
"ThemeSongVolumeHelp": "Adjust the volume of theme songs played while browsing the library.",
|
||||||
"ThemeVideos": "Theme videos",
|
"ThemeVideos": "Theme videos",
|
||||||
"TheseSettingsAffectSubtitlesOnThisDevice": "These settings affect subtitles on this device",
|
"TheseSettingsAffectSubtitlesOnThisDevice": "These settings affect subtitles on this device",
|
||||||
"ThisWizardWillGuideYou": "This wizard will help guide you through the setup process. To begin, please select your preferred language.",
|
"ThisWizardWillGuideYou": "This wizard will help guide you through the setup process. To begin, please select your preferred language.",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue