refactor: remove dead class members flagged by fallow

Fallow dead-code analysis flagged 13 issues. Removed the 10 that are
genuinely unreferenced:
- LLMFactory.getBestProvider, LLMFactory.updateSettings
- GeminiClient.isAvailable, OllamaClient.isAvailable
- ProcessingSpinner.setLabel, plus the now-dead callCount field and its
  '#N' suffix in the spinner text (setLabel was callCount's only writer,
  so the counter had been stuck at 0 already)
- YouTubeTranscriptExtractor.fetchTranscriptSegments, combineTranscript,
  clearConfigCache, parseJsonCaptions
- dropped the unused 'export' on the CaptionTrack interface (still used
  locally in the file, just never imported elsewhere)

Kept OllamaClient.validateConnection and createChatCompletion: fallow
flagged them, but transcript-summarizer.ts calls them via a loosely
typed 'client' variable that fallow cannot statically resolve.

Build and lint pass clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Richard McCorkle 2026-05-17 20:20:37 +02:00
parent 65a17f83d5
commit 800bbee770
5 changed files with 3 additions and 131 deletions

View file

@ -32,14 +32,6 @@ export class GeminiClient {
logger.debug('Creating Gemini client');
}
/**
* Check if the client can be used on the current platform
*/
isAvailable(): boolean {
// Gemini should work on all platforms through our fetch shim
return true;
}
/**
* Generate content using a Gemini model
*

View file

@ -25,10 +25,6 @@ export class LLMFactory {
logger.debug('Created LLM Factory');
}
getBestProvider(): string {
return this.settings.selectedLLM;
}
getOllamaClient(): OllamaClient {
if (!this.ollamaClient) {
const baseUrl = this.settings.apiKeys['ollama'] || 'http://localhost:11434';
@ -36,10 +32,4 @@ export class LLMFactory {
}
return this.ollamaClient;
}
updateSettings(settings: LLMSettings): void {
this.settings = settings;
this.ollamaClient = null;
logger.debug('LLM Factory settings updated');
}
}

View file

@ -41,16 +41,6 @@ export class OllamaClient {
logger.debug('Ollama client created with base URL:', baseUrl);
}
/**
* Check if Ollama can be used on the current platform
*/
isAvailable(): boolean {
// Ollama should work on all platforms with the unified fetch shim
// However, users need to ensure their Ollama server is accessible
// from their device (typically via localhost on same network)
return true;
}
/**
* Validate that the Ollama server is accessible
*/

View file

@ -13,14 +13,12 @@ const SPINNER_INTERVAL_MS = 100;
* Mobile: Obsidian has no status bar, so it renders a text element inside
* the supplied modal content element instead. Same Braille frames either way.
*
* Lifecycle: `start()` mounts and animates; `setLabel(text)` updates the
* per-step label and bumps the call counter; `stop()` removes the spinner
* Lifecycle: `start()` mounts and animates; `stop()` removes the spinner
* and clears the interval. Safe to call `stop()` more than once.
*/
export class ProcessingSpinner {
private statusBarItem: HTMLElement | null = null;
private modalSpinnerEl: HTMLElement | null = null;
private callCount = 0;
private currentLabel: string;
private spinnerFrame = 0;
private spinnerHandle: number | null = null;
@ -48,13 +46,6 @@ export class ProcessingSpinner {
}, SPINNER_INTERVAL_MS);
}
/** Update the per-step label and bump the call counter. */
setLabel(label: string): void {
if (label) this.currentLabel = label;
this.callCount += 1;
this.render();
}
/** Removes the spinner and stops the animation. Safe to call more than once. */
stop(): void {
if (this.spinnerHandle !== null) {
@ -73,7 +64,7 @@ export class ProcessingSpinner {
private render(): void {
const spinner = SPINNER_FRAMES[this.spinnerFrame];
const text = `${spinner} ${this.prefix} · ${this.currentLabel} · #${this.callCount}`;
const text = `${spinner} ${this.prefix} · ${this.currentLabel}`;
if (this.statusBarItem) this.statusBarItem.setText(text);
if (this.modalSpinnerEl) this.modalSpinnerEl.setText(text);
}

View file

@ -22,7 +22,7 @@ const isString = (value: unknown): value is string => typeof value === 'string';
/**
* Interface for YouTube caption tracks
*/
export interface CaptionTrack {
interface CaptionTrack {
languageCode: string;
kind?: string; // "asr" for auto-generated
baseUrl?: string;
@ -240,18 +240,6 @@ export class YouTubeTranscriptExtractor {
}
}
/**
* Extracts only transcript segments from a YouTube video (backward compatibility)
* @param videoId YouTube video ID
* @param options Optional language and country settings
* @returns Promise with transcript segments only
*/
static async fetchTranscriptSegments(videoId: string, options: TranscriptOptions = {}): Promise<TranscriptSegment[]> {
const result = await this.fetchTranscript(videoId, options);
return result.segments;
}
/**
* Get video metadata from the player response
* @param videoId YouTube video ID
@ -292,15 +280,6 @@ export class YouTubeTranscriptExtractor {
}
}
/**
* Combines transcript segments into a single text
* @param segments Array of transcript segments
* @returns Combined transcript text
*/
static combineTranscript(segments: TranscriptSegment[]): string {
return segments.map(segment => segment.text).join(' ');
}
/**
* Extracts video ID from a YouTube URL
* @param url YouTube video URL
@ -614,15 +593,6 @@ export class YouTubeTranscriptExtractor {
return response.text();
}
/**
* Clear the cached YouTube config (useful for testing or after errors)
*/
static clearConfigCache(): void {
this.cachedConfig = null;
this.cachedVideoId = null;
transcriptLogger.debug('YouTube config cache cleared');
}
/**
* Select the best caption track for a requested language.
* Priority: manual+exact manual+base auto+exact auto+base
@ -1545,65 +1515,4 @@ export class YouTubeTranscriptExtractor {
}
}
/**
* Parses YouTube captions in JSON format
* @param jsonText The JSON content of captions
* @param videoId Video ID for reference
* @returns Parsed transcript segments
*/
static parseJsonCaptions(jsonText: string, videoId: string): TranscriptSegment[] {
transcriptLogger.debug(`Parsing JSON captions for video ${videoId}`);
try {
const transcriptJson = JSON.parse(jsonText) as { events?: unknown[] };
const events = Array.isArray(transcriptJson.events) ? transcriptJson.events : [];
// Convert events to our TranscriptSegment format
const segments: TranscriptSegment[] = [];
events
.filter((event): event is { segs?: Array<{ utf8?: string }>; tStartMs?: string; dDurationMs?: string } => {
return isRecord(event) && Array.isArray(event.segs);
})
.forEach((event) => {
const startMs = event.tStartMs ? parseInt(event.tStartMs) : 0;
const durationMs = event.dDurationMs ? parseInt(event.dDurationMs) : 0;
// Combine all segments in this event
const text = (event.segs ?? [])
.map((seg) => seg.utf8 || '')
.join('')
.replace(/[\u200B-\u200D\uFEFF]/g, ''); // Remove special chars
const trimmedText = text.trim();
if (trimmedText) {
segments.push({
text: trimmedText,
start: startMs / 1000, // Convert to seconds
duration: durationMs / 1000 // Convert to seconds
});
}
});
if (segments.length === 0) {
throw new Error(`No transcript segments found in JSON data. Video ID: ${videoId}`);
}
return segments;
} catch (e) {
const errorMessage = getSafeErrorMessage(e);
transcriptLogger.error("Error parsing JSON captions:", errorMessage);
// Return a basic error segment
return [
{
text: `Failed to parse YouTube JSON captions for video ${videoId}. Error: ${errorMessage}`,
start: 0,
duration: 0
}
];
}
}
}