import { type Accessor, createEffect, type Setter, on, createSignal, Switch, Match, For } from "solid-js"; import { FileDisplay, type AppOptions } from "../types"; import "./OptionsModal.css"; import "./Dialog.css"; import "toastify-js/src/toastify.css"; import { toast } from "../utils"; import { createStore } from "solid-js/store"; import { MediaContainer, VideoCodec, AudioCodec, VideoEncodingSetting } from "music-server-shared/types"; interface OptionsModalProps { showOptions: Accessor; setShowOptions: Setter; options: AppOptions; setOptions: (options: AppOptions) => void; } function supportedVideoCodecs(container: MediaContainer) { let codecs: VideoCodec[] = []; switch (container) { case MediaContainer.mp3: case MediaContainer.webm: case MediaContainer.ogg: break; case MediaContainer.mp4: codecs = [VideoCodec.av1, VideoCodec.h264, VideoCodec.h265, VideoCodec.vp9]; break; default: { throw new Error(`Unsupported container: ${container}`); } } return codecs.concat([VideoCodec.none]); } function supportedAudioCodecs(container: MediaContainer, forVideo?: boolean) { let codecs: AudioCodec[] = []; switch (container) { case MediaContainer.mp3: codecs = [AudioCodec.mp3]; break; case MediaContainer.webm: codecs = [AudioCodec.opus, AudioCodec.vorbis]; break; case MediaContainer.ogg: codecs = [AudioCodec.opus, AudioCodec.vorbis]; break; case MediaContainer.mp4: codecs = [AudioCodec.mp3]; if (forVideo) codecs.push(AudioCodec.opus, AudioCodec.aac); //ffmpeg generates garbage if opus or aac is used in mp4 without video break; default: { throw new Error(`Unsupported container: ${container}`); } } return codecs; } export default function OptionsModal(props: OptionsModalProps) { //clone, otherwise it shares the reference const [newOptions, setNewOptions] = createStore({ ...props.options }); const [optionsPage, setOptionsPage] = createSignal(1); let dialogElement!: HTMLDialogElement; let formElement!: HTMLFormElement; //update to valid codecs on video container change createEffect(() => { const suportedVideo = supportedVideoCodecs(newOptions.videoSettings.container); if (!suportedVideo.includes(newOptions.videoSettings.videoCodec)) { setNewOptions("videoSettings", "videoCodec", suportedVideo[0]); } const suportedAudio = supportedAudioCodecs( newOptions.videoSettings.container, newOptions.videoSettings.videoCodec !== VideoCodec.none, ); if (!suportedAudio.includes(newOptions.videoSettings.audioCodec)) { setNewOptions("videoSettings", "audioCodec", suportedAudio[0]); } }); //update to valid codecs on audio container change createEffect(() => { const suportedAudio = supportedAudioCodecs(newOptions.audioSettings.container, false); if (!suportedAudio.includes(newOptions.audioSettings.audioCodec)) { setNewOptions("audioSettings", "audioCodec", suportedAudio[0]); } }); createEffect( on(props.showOptions, () => { if (props.showOptions()) { dialogElement.showModal(); setNewOptions(props.options); setOptionsPage(1); } }), ); function handleSave(e: Event) { e.preventDefault(); if (!formElement.reportValidity()) return; toast("Saved changes", "success"); props.setOptions(newOptions); handleClose(); } function handleClose() { dialogElement.close(); props.setShowOptions(false); } async function deleteServerCache() { const response = await fetch("/reset-cache", { method: "POST", credentials: "same-origin" }).catch(() => {}); if (!response || response.status !== 200) { toast("Clearing server cache failed", "error"); } else { toast("Server cache cleared", "info"); } } return ( e.target.nodeName === "DIALOG" && handleClose()}>
Player Options

{newOptions.enableTranscoding && ( )} {newOptions.enableTranscoding && ( )}
setNewOptions("enableTranscoding", e.target.checked)} />
setNewOptions("languages", e.target.value)} disabled={!newOptions.enableTranscoding} />
setNewOptions("disableChunkedTranscoding", e.target.checked)} disabled={!newOptions.enableTranscoding} />
setNewOptions("maxTranscodes", e.target.valueAsNumber)} />
setNewOptions("maxDownloads", e.target.valueAsNumber)} />
setNewOptions("hideVolume", e.target.checked)} />
setNewOptions("hideCover", e.target.checked)} />
setNewOptions("hidePlaylistCover", e.target.checked)} />
setNewOptions("progressBarHeight", e.target.valueAsNumber)} />
setNewOptions("useMSE", e.target.checked)} />
setNewOptions("rememberDir", e.target.checked)} />
setNewOptions("duplicateWarning", e.target.checked)} />
setNewOptions("transcodeCovers", e.target.checked)} />
Transcoding settings for videos
Transcoding settings for music
); }