commit da1ba11d3c19c24a459062f8a23a8da7f6aaa4e9
parent bdb750fc3e9c37e0805c2b2b87a706c2bd3fa0f8
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Sat, 16 May 2026 15:10:43 -0400
untranscribable tweaks
Diffstat:
4 files changed, 65 insertions(+), 8 deletions(-)
diff --git a/editor/app/channels/[slug]/components/stages/DiagnosticsStage.tsx b/editor/app/channels/[slug]/components/stages/DiagnosticsStage.tsx
@@ -57,13 +57,6 @@ export function DiagnosticsStage({
}: Props) {
const buckets: DiagnosticBucket[] = [
{
- ids: untranscribableIds,
- label: "Marked untranscribable",
- description:
- "transcript.json present but contains an empty transcription array.",
- ariaLabel: "marked untranscribable",
- },
- {
ids: noMetadataIds,
label: "Missing metadata.info.json",
description:
@@ -178,6 +171,31 @@ export function DiagnosticsStage({
</div>
)}
</div>
+ {untranscribableIds.length > 0 && (
+ <div
+ aria-label="marked untranscribable"
+ className="flex flex-col gap-2 rounded border border-zinc-200 dark:border-zinc-800 p-3"
+ >
+ <div>
+ <h4 className="text-sm font-semibold">
+ Marked untranscribable ({untranscribableIds.length})
+ </h4>
+ <p className="text-xs text-zinc-500">
+ Videos explicitly marked as untranscribable (transcript.json with
+ an empty transcription array). Informational only — no action
+ needed.
+ </p>
+ </div>
+ <VideoIdList
+ slug={slug}
+ ids={untranscribableIds}
+ ariaLabel="marked untranscribable list"
+ emptyAriaLabel="marked untranscribable empty"
+ emptyMessage="None"
+ itemAriaLabel={(id) => `marked untranscribable ${id}`}
+ />
+ </div>
+ )}
</div>
);
}
diff --git a/editor/app/channels/[slug]/components/stages/TranscodeStage.tsx b/editor/app/channels/[slug]/components/stages/TranscodeStage.tsx
@@ -9,6 +9,7 @@ import {
import { QueueControl } from "../../../../components/QueueControl";
import { cancelJobAction } from "../../../../jobs/actions";
import {
+ clearFailedTranscodingsAction,
removeFailedTranscodingsAction,
transcodeFailuresAction,
} from "../../whisperActions";
@@ -153,6 +154,17 @@ function FailedTranscodingsSection({
/>
{ids.length > 0 ? (
<div className="flex flex-col gap-2">
+ <StreamActionLog
+ trigger={() => clearFailedTranscodingsAction(slug, removeQueue)}
+ cancelAction={cancelJobAction}
+ buttonLabel={`Clear failed-transcodings list (${ids.length})`}
+ runningLabel="Clearing…"
+ label="Clear failed-transcodings list"
+ />
+ <p className="text-xs text-zinc-500">
+ Clears the list only — video directories stay on disk. Use this when
+ ffmpeg has been fixed or the listing is stale.
+ </p>
<label className="flex items-center gap-2 text-xs text-zinc-600 dark:text-zinc-400">
Type
<span className="font-mono">remove</span>
diff --git a/editor/app/channels/[slug]/lib/stageStatus.ts b/editor/app/channels/[slug]/lib/stageStatus.ts
@@ -52,6 +52,7 @@ const JOB_KIND_TO_STAGE: Record<string, StageId> = {
"download-missing": "download",
"transcode-failures": "transcode",
"remove-failed-transcodings": "transcode",
+ "clear-failed-transcodings": "transcode",
"transcode-audio": "transcode",
"whisper-all": "transcribe",
"whisper-retry": "transcribe",
@@ -111,8 +112,9 @@ export function computeStageStatuses(
const transcribePending = buckets.downloadedNoTranscript.length;
const transcribeFailed = failedVideoIds.length;
const cleanupPending = buckets.multipleAudioFormats.length;
+ // `untranscribable` is deliberately excluded: those videos are an intentional
+ // user decision ("mark untranscribable"), not an anomaly with an action.
const diagnosticsPending =
- buckets.untranscribable.length +
buckets.noMetadata.length +
buckets.missingFromArchive.length +
buckets.duplicateDirs.length;
diff --git a/editor/app/channels/[slug]/whisperActions.ts b/editor/app/channels/[slug]/whisperActions.ts
@@ -10,6 +10,10 @@ import { TRANSCRIPTION_QUEUE } from "yt-dlp-transcript-common/lib/platform";
import { runWhisperBatch } from "yt-dlp-transcript-common/controller/whisperBatch";
import { runTranscodeFailures } from "yt-dlp-transcript-common/controller/transcodeFailures";
import { removeFailedTranscodings } from "yt-dlp-transcript-common/controller/removeFailedTranscodings";
+import {
+ clearFailedTranscodings,
+ loadFailedTranscodings,
+} from "yt-dlp-transcript-common/controller/failedTranscodings";
import { clearFailedTranscriptions } from "yt-dlp-transcript-common/controller/failedTranscriptions";
import { cleanAudioFromTranscribed } from "yt-dlp-transcript-common/controller/cleanAudioFromTranscribed";
import { cleanExtraAudioFormats } from "yt-dlp-transcript-common/controller/cleanExtraAudioFormats";
@@ -148,6 +152,27 @@ export async function removeFailedTranscodingsAction(
});
}
+export async function clearFailedTranscodingsAction(
+ slug: string,
+ queueKey?: string,
+): Promise<StreamActionResult> {
+ const paths = getPaths();
+ return runManagedFunction({
+ kind: "clear-failed-transcodings",
+ queueKey: queueKey === undefined ? defaultQueueKey(slug) : queueKey.trim(),
+ paths,
+ channelSlug: slug,
+ fn: async (onLog) => {
+ const ids = await loadFailedTranscodings(paths, slug);
+ await clearFailedTranscodings(paths, slug);
+ onLog(
+ `Cleared ${ids.length} entr${ids.length === 1 ? "y" : "ies"} from failed-transcodings; video dirs left intact.`,
+ );
+ revalidatePath(`/channels/${slug}`);
+ },
+ });
+}
+
export async function cleanExtraAudioFormatsAction(
slug: string,
queueKey?: string,