0.13.0 DVD Series
This commit is contained in:
1 parent
43702b0138
commit
974b7bfad3
36 files changed
+9666
-739
No files matched your search
@@ -293,9 +293,10 @@ function ExternalStoragePathsEditor({ value, onChange, settingKey }) {
|
||||
);
|
||||
}
|
||||
|
||||
function DetectedDrivesInfo() {
|
||||
function DetectedDrivesInfo({ expertModeEnabled = false, onNotify = null }) {
|
||||
const [drives, setDrives] = useState(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [unlocking, setUnlocking] = useState(false);
|
||||
|
||||
const load = () => {
|
||||
setLoading(true);
|
||||
@@ -307,21 +308,70 @@ function DetectedDrivesInfo() {
|
||||
|
||||
useEffect(() => { load(); }, []);
|
||||
|
||||
const notify = (severity, summary, detail) => {
|
||||
if (typeof onNotify === 'function') {
|
||||
onNotify({ severity, summary, detail });
|
||||
}
|
||||
};
|
||||
|
||||
const handleUnlockOne = async (devicePath) => {
|
||||
setUnlocking(true);
|
||||
try {
|
||||
await api.forceUnlockDrive({ devicePath });
|
||||
notify('success', 'Laufwerk freigegeben', `${devicePath} wurde entsperrt.`);
|
||||
load();
|
||||
} catch (error) {
|
||||
notify('error', 'Freigabe fehlgeschlagen', error?.message || 'Unbekannter Fehler');
|
||||
} finally {
|
||||
setUnlocking(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleUnlockAll = async () => {
|
||||
setUnlocking(true);
|
||||
try {
|
||||
await api.forceUnlockDrive({ all: true });
|
||||
notify('success', 'Laufwerke freigegeben', 'Alle erkannten Laufwerke wurden entsperrt.');
|
||||
load();
|
||||
} catch (error) {
|
||||
notify('error', 'Freigabe fehlgeschlagen', error?.message || 'Unbekannter Fehler');
|
||||
} finally {
|
||||
setUnlocking(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="detected-drives-info">
|
||||
<div className="detected-drives-header">
|
||||
<span className="detected-drives-label">Erkannte optische Laufwerke</span>
|
||||
<Button
|
||||
icon="pi pi-refresh"
|
||||
text
|
||||
rounded
|
||||
size="small"
|
||||
loading={loading}
|
||||
onClick={load}
|
||||
type="button"
|
||||
aria-label="Laufwerke neu einlesen"
|
||||
title="Laufwerke neu einlesen"
|
||||
/>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '0.3rem' }}>
|
||||
{expertModeEnabled ? (
|
||||
<Button
|
||||
icon="pi pi-unlock"
|
||||
text
|
||||
rounded
|
||||
size="small"
|
||||
loading={unlocking}
|
||||
disabled={loading}
|
||||
onClick={handleUnlockAll}
|
||||
type="button"
|
||||
aria-label="Alle Laufwerke freigeben"
|
||||
title="Alle Laufwerke freigeben"
|
||||
/>
|
||||
) : null}
|
||||
<Button
|
||||
icon="pi pi-refresh"
|
||||
text
|
||||
rounded
|
||||
size="small"
|
||||
loading={loading}
|
||||
disabled={unlocking}
|
||||
onClick={load}
|
||||
type="button"
|
||||
aria-label="Laufwerke neu einlesen"
|
||||
title="Laufwerke neu einlesen"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{drives === null || loading
|
||||
? <span className="detected-drives-empty">Wird geladen…</span>
|
||||
@@ -334,6 +384,7 @@ function DetectedDrivesInfo() {
|
||||
<th>Gerät</th>
|
||||
<th>Modell</th>
|
||||
<th>MakeMKV Index</th>
|
||||
{expertModeEnabled ? <th>Freigabe</th> : null}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -342,6 +393,19 @@ function DetectedDrivesInfo() {
|
||||
<td><code>{d.path}</code></td>
|
||||
<td>{d.model || '—'}</td>
|
||||
<td><Tag value={d.discArg ?? '—'} severity="info" /></td>
|
||||
{expertModeEnabled ? (
|
||||
<td>
|
||||
<Button
|
||||
label="Freigeben"
|
||||
icon="pi pi-unlock"
|
||||
size="small"
|
||||
text
|
||||
onClick={() => handleUnlockOne(d.path)}
|
||||
loading={unlocking}
|
||||
disabled={loading}
|
||||
/>
|
||||
</td>
|
||||
) : null}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
@@ -438,7 +502,15 @@ function buildToolSections(settings) {
|
||||
|
||||
// Path keys per medium — _owner keys are rendered inline
|
||||
const BLURAY_PATH_KEYS = ['raw_dir_bluray', 'movie_dir_bluray', 'output_template_bluray'];
|
||||
const DVD_PATH_KEYS = ['raw_dir_dvd', 'movie_dir_dvd', 'output_template_dvd'];
|
||||
const DVD_PATH_KEYS = [
|
||||
'raw_dir_dvd',
|
||||
'raw_dir_dvd_series',
|
||||
'movie_dir_dvd',
|
||||
'series_dir_dvd',
|
||||
'output_template_dvd',
|
||||
'output_template_dvd_series_episode',
|
||||
'output_template_dvd_series_multi_episode'
|
||||
];
|
||||
const CD_PATH_KEYS = ['raw_dir_cd', 'movie_dir_cd', 'cd_output_template'];
|
||||
const AUDIOBOOK_PATH_KEYS = ['raw_dir_audiobook', 'movie_dir_audiobook', 'output_template_audiobook', 'output_chapter_template_audiobook', 'audiobook_raw_template'];
|
||||
const DOWNLOAD_PATH_KEYS = ['download_dir'];
|
||||
@@ -732,6 +804,7 @@ function PathCategoryTab({ settings, values, errors, dirtyKeys, onChange, effect
|
||||
|
||||
const defaultRaw = effectivePaths?.defaults?.raw || 'data/output/raw';
|
||||
const defaultMovies = effectivePaths?.defaults?.movies || 'data/output/movies';
|
||||
const defaultSeries = effectivePaths?.defaults?.series || 'data/output/series';
|
||||
const defaultCd = effectivePaths?.defaults?.cd || 'data/output/cd';
|
||||
const defaultAudiobookRaw = effectivePaths?.defaults?.audiobookRaw || 'data/output/audiobook-raw';
|
||||
const defaultAudiobookMovies = effectivePaths?.defaults?.audiobookMovies || 'data/output/audiobooks';
|
||||
@@ -745,7 +818,9 @@ function PathCategoryTab({ settings, values, errors, dirtyKeys, onChange, effect
|
||||
const blurayRaw = ep.bluray?.raw || defaultRaw;
|
||||
const blurayMovies = ep.bluray?.movies || defaultMovies;
|
||||
const dvdRaw = ep.dvd?.raw || defaultRaw;
|
||||
const dvdSeriesRaw = ep.dvd?.seriesRaw || dvdRaw;
|
||||
const dvdMovies = ep.dvd?.movies || defaultMovies;
|
||||
const dvdSeries = ep.dvd?.series || defaultSeries;
|
||||
const cdRaw = ep.cd?.raw || defaultCd;
|
||||
const cdMovies = ep.cd?.movies || cdRaw;
|
||||
const audiobookRaw = ep.audiobook?.raw || defaultAudiobookRaw;
|
||||
@@ -796,6 +871,17 @@ function PathCategoryTab({ settings, values, errors, dirtyKeys, onChange, effect
|
||||
{isDefault(dvdMovies, defaultMovies) && <span className="path-default-badge">Standard</span>}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>DVD Serie</strong></td>
|
||||
<td>
|
||||
<code>{dvdSeriesRaw}</code>
|
||||
{isDefault(dvdSeriesRaw, dvdRaw) && <span className="path-default-badge">wie DVD RAW</span>}
|
||||
</td>
|
||||
<td>
|
||||
<code>{dvdSeries}</code>
|
||||
{isDefault(dvdSeries, defaultSeries) && <span className="path-default-badge">Standard</span>}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>CD / Audio</strong></td>
|
||||
<td>
|
||||
@@ -915,7 +1001,8 @@ export default function DynamicSettingsForm({
|
||||
errors,
|
||||
dirtyKeys,
|
||||
onChange,
|
||||
effectivePaths
|
||||
effectivePaths,
|
||||
onNotify
|
||||
}) {
|
||||
const safeCategories = Array.isArray(categories) ? categories : [];
|
||||
const expertModeEnabled = toBoolean(values?.[EXPERT_MODE_SETTING_KEY]);
|
||||
@@ -1009,7 +1096,7 @@ export default function DynamicSettingsForm({
|
||||
const isDriveCategory = normalizeText(category?.category) === 'laufwerk';
|
||||
return (
|
||||
<div className="settings-sections">
|
||||
{isDriveCategory && <DetectedDrivesInfo />}
|
||||
{isDriveCategory && <DetectedDrivesInfo expertModeEnabled={expertModeEnabled} onNotify={onNotify} />}
|
||||
{sections.map((section) => (
|
||||
<section
|
||||
key={`${category?.category || 'category'}-${section.id}`}
|
||||
|
||||
Reference in new issue
Block a user