0.13.1-5 Adjust Detection

This commit is contained in:
2026-04-14 05:54:48 +00:00
parent 3feb780e16
commit cac6442b5b
14 changed files with 937 additions and 156 deletions
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "ripster-frontend",
"version": "0.13.1-4",
"version": "0.13.1-5",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "ripster-frontend",
"version": "0.13.1-4",
"version": "0.13.1-5",
"dependencies": {
"primeicons": "^7.0.0",
"primereact": "^10.9.2",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "ripster-frontend",
"version": "0.13.1-4",
"version": "0.13.1-5",
"private": true,
"type": "module",
"scripts": {
@@ -4,6 +4,7 @@ import { Button } from 'primereact/button';
import { DataTable } from 'primereact/datatable';
import { Column } from 'primereact/column';
import { InputText } from 'primereact/inputtext';
import { Dropdown } from 'primereact/dropdown';
export default function MetadataSelectionDialog({
visible,
@@ -36,12 +37,20 @@ export default function MetadataSelectionDialog({
const [submitError, setSubmitError] = useState('');
const [seasonFilter, setSeasonFilter] = useState('');
const [discValidationError, setDiscValidationError] = useState('');
const metadataProvider = String(context?.metadataProvider || 'omdb').trim().toLowerCase() || 'omdb';
const contextMetadataProvider = String(context?.metadataProvider || 'omdb').trim().toLowerCase() || 'omdb';
const [selectedMetadataProvider, setSelectedMetadataProvider] = useState(contextMetadataProvider);
const mediaProfile = String(
context?.mediaProfile
|| context?.selectedMetadata?.mediaProfile
|| ''
).trim().toLowerCase();
const metadataProvider = selectedMetadataProvider === 'tmdb' ? 'tmdb' : 'omdb';
const providerOptions = mediaProfile === 'dvd'
? [
{ label: 'Film (OMDb)', value: 'omdb' },
{ label: 'Serie (TMDb)', value: 'tmdb' }
]
: [{ label: contextMetadataProvider === 'tmdb' ? 'TMDb' : 'OMDb', value: contextMetadataProvider }];
const providerLabel = metadataProvider === 'tmdb' ? 'TMDb' : 'OMDb';
const supportsExternalIdInput = metadataProvider === 'omdb';
const requiresDiscNumber = metadataProvider === 'tmdb' && (mediaProfile === 'dvd' || !mediaProfile);
@@ -70,6 +79,7 @@ export default function MetadataSelectionDialog({
setSubmitError('');
setSeasonFilter('');
setDiscValidationError('');
setSelectedMetadataProvider(contextMetadataProvider);
}, [visible, context]);
useEffect(() => {
@@ -85,7 +95,7 @@ export default function MetadataSelectionDialog({
if (submitError) {
setSubmitError('');
}
}, [manualDiscNumber, seasonFilter, selected]);
}, [manualDiscNumber, seasonFilter, selected, metadataProvider]);
const rows = useMemo(() => {
const base = Array.isArray(context?.metadataCandidates)
@@ -240,6 +250,25 @@ export default function MetadataSelectionDialog({
modal
>
<div className="search-row">
{providerOptions.length > 1 ? (
<Dropdown
value={metadataProvider}
options={providerOptions}
optionLabel="label"
optionValue="value"
onChange={(event) => {
const nextProvider = String(event?.value || 'omdb').trim().toLowerCase() || 'omdb';
setSelectedMetadataProvider(nextProvider);
setSelected(null);
setExtraResults([]);
setSearchError('');
setSubmitError('');
setDiscValidationError('');
}}
disabled={searchBusy || busy}
style={{ minWidth: '12.5rem' }}
/>
) : null}
<InputText
value={query}
onChange={(event) => setQuery(event.target.value)}
+17 -5
View File
@@ -985,10 +985,10 @@ function buildDvdSeriesEpisodeOutputPathPreview(settings, metadata, title, assig
).trim();
const multiTemplateRaw = String(
settings?.output_template_dvd_series_multi_episode
|| '{seriesTitle}/Staffel {seasonNr}/S{seasonNr}E{episodeRange}'
|| '{seriesTitle}/Staffel {seasonNr}/S{0:seasonNr}E{episodeRange} - {episodeTitle} (Teil {parts})'
).trim();
const singleTemplate = singleTemplateRaw || '{seriesTitle}/Staffel {seasonNr}/S{seasonNr}E{episodeNr} - {episodeTitle}';
const multiTemplate = multiTemplateRaw || '{seriesTitle}/Staffel {seasonNr}/S{seasonNr}E{episodeRange}';
const multiTemplate = multiTemplateRaw || '{seriesTitle}/Staffel {seasonNr}/S{0:seasonNr}E{episodeRange} - {episodeTitle} (Teil {parts})';
const template = episodeRange.isMulti ? multiTemplate : singleTemplate;
const rendered = renderTemplate(template, {
seriesTitle,
@@ -1936,6 +1936,13 @@ export default function PipelineStatusCard({
const handBrakeTitleDecisionRequired = state === 'WAITING_FOR_USER_DECISION'
&& Boolean(pipeline?.context?.handBrakeTitleDecisionRequired);
const handBrakeDecisionMode = String(pipeline?.context?.handBrakeDecisionMode || '').trim().toLowerCase();
const isSeriesPlayAllDecisionMode = handBrakeDecisionMode === 'series_playall_or_double';
const handBrakeDecisionSummary = pipeline?.context?.handBrakeDecisionSummary
&& typeof pipeline.context.handBrakeDecisionSummary === 'object'
? pipeline.context.handBrakeDecisionSummary
: null;
const ambiguousLongestTitleId = normalizeTitleId(handBrakeDecisionSummary?.longestCandidateTitleId);
const waitingHandBrakeTitleRows = useMemo(() => {
const raw = Array.isArray(pipeline?.context?.handBrakeTitleCandidates)
@@ -2849,7 +2856,7 @@ export default function PipelineStatusCard({
{handBrakeTitleDecisionRequired && retryJobId && (
<Button
label="DVD Titel bestätigen"
label={isSeriesPlayAllDecisionMode ? 'PlayAll/Doppelfolge bestätigen' : 'DVD Titel bestätigen'}
icon="pi pi-check"
severity="warning"
outlined
@@ -3077,9 +3084,11 @@ export default function PipelineStatusCard({
{handBrakeTitleDecisionRequired && !queueLocked ? (
<div className="playlist-decision-block">
<h3>DVD Titel-Auswahl erforderlich</h3>
<h3>{isSeriesPlayAllDecisionMode ? 'Serien-Grenzfall: PlayAll oder Doppelfolge?' : 'DVD Titel-Auswahl erforderlich'}</h3>
<small>
Mehrere DVD-Titel gefunden, die die Mindestlänge erfüllen. Bitte einen oder mehrere Episoden-Titel auswählen.
{isSeriesPlayAllDecisionMode
? 'Grenzfall erkannt: Der längste Titel passt zeitlich zur Summe der kürzeren Episoden. Bitte entscheiden, ob er als PlayAll ausgeschlossen oder als Doppelfolge mit encodiert werden soll.'
: 'Mehrere DVD-Titel gefunden, die die Mindestlänge erfüllen. Bitte einen oder mehrere Episoden-Titel auswählen.'}
</small>
{waitingHandBrakeTitleRows.length > 0 ? (
<div className="playlist-decision-list">
@@ -3102,6 +3111,9 @@ export default function PipelineStatusCard({
/>
<span>
{`Titel -t ${row.handBrakeTitleId}`}
{isSeriesPlayAllDecisionMode && ambiguousLongestTitleId && row.handBrakeTitleId === ambiguousLongestTitleId
? ' | Längster Titel (PlayAll-/Doppelfolge-Kandidat)'
: ''}
{row.durationLabel ? ` | ${row.durationLabel}` : ''}
{row.audioTrackCount > 0 ? ` | Audio: ${row.audioTrackCount}` : ''}
{row.subtitleTrackCount > 0 ? ` | Untertitel: ${row.subtitleTrackCount}` : ''}
+12 -2
View File
@@ -5,6 +5,7 @@ import { Column } from 'primereact/column';
import { Tag } from 'primereact/tag';
import { Button } from 'primereact/button';
import { Toast } from 'primereact/toast';
import { useNavigate } from 'react-router-dom';
import { api } from '../api/client';
import { confirmModal } from '../utils/confirmModal';
@@ -26,6 +27,7 @@ function formatDetectedMediaType(value) {
}
export default function DatabasePage() {
const navigate = useNavigate();
const [orphanRows, setOrphanRows] = useState([]);
const [orphanLoading, setOrphanLoading] = useState(false);
const [orphanImportBusyPath, setOrphanImportBusyPath] = useState(null);
@@ -63,13 +65,21 @@ export default function DatabasePage() {
try {
const response = await api.importOrphanRawFolder(row.rawPath);
const newJobId = response?.job?.id;
const activationStarted = Boolean(response?.activation?.started);
toastRef.current?.show({
severity: 'success',
summary: 'Job angelegt',
detail: newJobId ? `Historieneintrag #${newJobId} erstellt.` : 'Historieneintrag wurde erstellt.',
summary: activationStarted ? 'Analyse gestartet' : 'Job angelegt',
detail: activationStarted
? (newJobId
? `Job #${newJobId} läuft jetzt im Ripper-View.`
: 'Job läuft jetzt im Ripper-View.')
: (newJobId ? `Historieneintrag #${newJobId} erstellt.` : 'Historieneintrag wurde erstellt.'),
life: 3500
});
await loadOrphans();
if (activationStarted) {
navigate('/ripper');
}
} catch (error) {
toastRef.current?.show({
severity: 'error',