Move inactive projects out of active workspace: - bauntown (community website) - maerchenzauber (AI story generation) - memoro (voice memo app) - news (news aggregation) - nutriphi (nutrition tracking) - reader (reading app) - uload (URL shortener) - wisekeep (AI wisdom extraction) Update CLAUDE.md documentation: - Add presi to active projects - Document archived projects section - Update workspace configuration Archived apps can be re-activated by moving back to apps/ 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
15 KiB
Audio-Archiv: Upload- und Verarbeitungsstatus-Tracking
Analyse der aktuellen Situation
Aktuelle Datenstruktur
AudioFile (Lokales Archiv)
interface AudioFile {
id: string; // Lokale ID
uri: string; // Lokaler Dateipfad
filename: string; // z.B. "recording-1234567890.m4a"
duration: number; // Dauer in Sekunden
createdAt: Date; // Erstellungsdatum
size?: number; // Dateigröße in Bytes
}
Status: Keine Tracking-Informationen für Upload oder Verarbeitung vorhanden.
Memo (Nach Verarbeitung)
interface Memo {
id: string;
title: string;
source?: MemoSource;
metadata?: MemoMetadata;
transcript?: string;
// ... weitere Felder
}
interface MemoSource {
type?: 'audio' | 'text' | 'upload' | 'photo';
audio_path?: string; // z.B. "user-123/recording-1234567890.m4a"
transcript?: string;
duration?: number;
additional_recordings?: AdditionalRecording[];
// ... weitere Felder
}
interface MemoMetadata {
processing?: ProcessingMetadata;
transcriptionStatus?: string;
recordingStatus?: string;
// ... weitere Felder
}
Aktueller Upload-Flow
-
Lokale Aufnahme →
fileStorageService.saveRecording()- Speichert Audio lokal als
AudioFile - Keine Verknüpfung zu Memo
- Speichert Audio lokal als
-
Upload aus Archiv →
handleReupload()inRecordingsList.tsxcloudStorageService.uploadAudioForProcessing()→ Cloud StoragetriggerTranscription()→ memoro-service API- Problem: Keine Statusverfolgung nach Upload
-
Memo-Erstellung
- Backend erstellt Memo nach erfolgreicher Transkription
- Problem: Keine Verbindung zwischen lokalem
AudioFileund erstelltemMemo
Identifizierte Probleme
- Fehlende Verbindung: Kein Link zwischen lokalem
AudioFileund hochgeladenem/verarbeitetemMemo - Kein Status-Tracking: User sieht nicht, ob Upload erfolgreich war
- Doppelter Upload möglich: Keine Markierung verhindert erneutes Hochladen
- Keine Memo-Verlinkung: User kann nicht direkt zum fertigen Memo navigieren
Lösungsvorschläge
Option 1: Minimale Lösung (Schnell & Einfach)
Ansatz: Erweitere AudioFile um Upload-Status-Felder
Änderungen
// features/storage/storage.types.ts
interface AudioFile {
id: string;
uri: string;
filename: string;
duration: number;
createdAt: Date;
size?: number;
// NEU: Upload & Verarbeitung
uploadStatus?: 'pending' | 'uploading' | 'uploaded' | 'failed';
uploadedAt?: Date;
uploadError?: string;
processingStatus?: 'pending' | 'processing' | 'completed' | 'failed';
// NEU: Verbindung zum Memo
memoId?: string;
cloudPath?: string; // z.B. "user-123/recording-1234567890.m4a"
}
Implementierung
-
Upload-Tracking in
RecordingsList.tsxconst handleReupload = async (recording: AudioFile) => { // Setze Status auf "uploading" await fileStorageService.updateRecordingStatus(recording.id, { uploadStatus: 'uploading' }); try { // Upload... const uploadResult = await cloudStorageService.uploadAudioForProcessing(...); if (uploadResult.success) { await fileStorageService.updateRecordingStatus(recording.id, { uploadStatus: 'uploaded', uploadedAt: new Date(), cloudPath: uploadResult.filePath, processingStatus: 'processing' }); // Trigger transcription... const transcriptionResult = await triggerTranscription(...); // Status bleibt 'processing' bis Memo erstellt wurde } } catch (error) { await fileStorageService.updateRecordingStatus(recording.id, { uploadStatus: 'failed', uploadError: error.message }); } }; -
Memo-Verbindung via Realtime
// Wenn neues Memo erstellt wird, prüfe ob es eine lokale Aufnahme gibt useEffect(() => { const handleNewMemo = async (memo: Memo) => { if (memo.source?.audio_path) { const filename = memo.source.audio_path.split('/').pop(); const localRecording = await fileStorageService.findRecordingByFilename(filename); if (localRecording) { await fileStorageService.updateRecordingStatus(localRecording.id, { memoId: memo.id, processingStatus: 'completed' }); } } }; }, []); -
UI-Updates in
AudioPlayeroderRecordingsList- Badge/Icon für Upload-Status
- Button "Zum Memo" wenn
memoIdvorhanden - Status-Indikatoren
Vorteile
- ✅ Schnell implementierbar
- ✅ Keine Backend-Änderungen nötig
- ✅ Funktioniert mit bestehender Infrastruktur
Nachteile
- ⚠️ Status geht verloren bei App-Neuinstallation
- ⚠️ Matching über Dateiname kann fehlschlagen
- ⚠️ Keine Synchronisation über Geräte hinweg
Option 2: Backend-Integration (Robust & Skalierbar)
Ansatz: Backend verfolgt Upload-Status und benachrichtigt Frontend
Änderungen
-
Neue Datenbank-Tabelle:
audio_uploadsCREATE TABLE audio_uploads ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), user_id UUID REFERENCES auth.users(id), filename TEXT NOT NULL, cloud_path TEXT NOT NULL, local_id TEXT, -- Optional: Lokale AudioFile ID upload_status TEXT NOT NULL DEFAULT 'pending', processing_status TEXT NOT NULL DEFAULT 'pending', memo_id UUID REFERENCES memos(id), uploaded_at TIMESTAMPTZ, processing_started_at TIMESTAMPTZ, completed_at TIMESTAMPTZ, error_message TEXT, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() ); -
Upload-Flow mit Tracking
const handleReupload = async (recording: AudioFile) => { // 1. Erstelle Upload-Record const uploadRecord = await supabase .from('audio_uploads') .insert({ filename: recording.filename, local_id: recording.id, upload_status: 'pending' }) .select() .single(); // 2. Upload & Update Status const uploadResult = await cloudStorageService.uploadAudioForProcessing(...); await supabase .from('audio_uploads') .update({ cloud_path: uploadResult.filePath, upload_status: 'uploaded', uploaded_at: new Date().toISOString(), processing_status: 'processing' }) .eq('id', uploadRecord.id); // 3. Trigger Transcription mit Upload-ID await triggerTranscription({ ..., uploadRecordId: uploadRecord.id }); }; -
Backend aktualisiert Status
- memoro-service aktualisiert
audio_uploadswährend Verarbeitung - Setzt
memo_idwenn Memo erstellt wurde - Setzt
processing_status: 'completed'
- memoro-service aktualisiert
-
Frontend Realtime Subscription
useEffect(() => { const subscription = supabase .channel('audio-uploads') .on('postgres_changes', { event: 'UPDATE', schema: 'public', table: 'audio_uploads' }, (payload) => { // Update lokalen AudioFile Status updateLocalRecording(payload.new); }) .subscribe(); }, []);
Vorteile
- ✅ Robust & Zuverlässig
- ✅ Geräteübergreifende Synchronisation
- ✅ Präzises Tracking über gesamten Lifecycle
- ✅ Fehlerbehandlung & Retry-Logik möglich
Nachteile
- ⚠️ Backend-Änderungen erforderlich
- ⚠️ Komplexere Implementierung
- ⚠️ Zusätzliche Datenbank-Tabelle
Option 3: Hybrid-Ansatz (Empfohlen)
Kombination: Lokales Tracking (Option 1) + optionale Backend-Verifizierung
Implementierung
-
Phase 1: Lokales Tracking (wie Option 1)
- Erweitere
AudioFileum Status-Felder - Tracking in
fileStorageService - UI zeigt Status an
- Erweitere
-
Phase 2: Backend-Verifizierung (später)
- Füge
audio_uploadsTabelle hinzu - Synchronisiere bei App-Start
- Korrigiere falsche lokale Stati
- Füge
-
Matching-Strategie
// Beim Upload: Speichere eindeutigen Identifier const uploadMetadata = { localId: recording.id, deviceId: await getDeviceId(), uploadTimestamp: Date.now() }; // Backend speichert Metadata in memo.metadata.uploadInfo // Frontend kann später matchen
Vorteile
- ✅ Schnelle initiale Implementierung
- ✅ Schrittweise Verbesserung möglich
- ✅ Guter Kompromiss zwischen Aufwand und Nutzen
Empfehlung & Nächste Schritte
Empfohlener Ansatz: Option 3 (Hybrid)
Begründung:
- Schneller Mehrwert für User (Phase 1)
- Technische Schuld bleibt überschaubar
- Einfach auf robuste Lösung erweiterbar
Implementierungs-Roadmap
Phase 1: Lokales Tracking (1-2 Tage)
-
Datenmodell erweitern
AudioFileInterface erweitern- Migration für bestehende Daten
-
Storage-Service anpassen
updateRecordingStatus()implementierenfindRecordingByFilename()implementieren- SQLite Queries anpassen
-
Upload-Flow aktualisieren
- Status-Tracking in
handleReupload() - Error-Handling verbessern
- Doppel-Upload verhindern
- Status-Tracking in
-
UI-Komponenten
- Status-Badge/Icon in
AudioPlayer - "Zum Memo"-Button hinzufügen
- Upload-Status-Indikator
- Translations für Status-Texte
- Status-Badge/Icon in
-
Memo-Verbindung
- Realtime-Listener für neue Memos
- Matching-Logik über Dateiname
- Status-Update auf
completed
Phase 2: Backend-Integration (optional, später)
-
Datenbank
audio_uploadsTabelle erstellen- RLS Policies definieren
- Indexes anlegen
-
Backend-Service
- Upload-Record Erstellung
- Status-Updates während Verarbeitung
- Memo-Verlinkung
-
Frontend-Sync
- Realtime Subscription
- Sync bei App-Start
- Conflict-Resolution
UI/UX Konzept
Status-Anzeige im Audio-Archiv
┌─────────────────────────────────────────┐
│ 🎤 Aufnahme │
│ 25.09.2024, 14:30 Uhr • 2:34 │
│ │
│ ⏸ ▶ ──────●────── [Waveform] │
│ │
│ ┌─────────────────────────────────┐ │
│ │ ✓ Hochgeladen & Verarbeitet │ │ <- Status-Badge
│ │ 📝 Memo ansehen → │ │ <- Action-Button
│ └─────────────────────────────────┘ │
└─────────────────────────────────────────┘
Status-Varianten
-
Nicht hochgeladen
- Button: "Hochladen" (Primary)
- Badge: Keine
-
Lädt hoch...
- Button: "Lädt hoch..." (Disabled, Loading)
- Badge: "⏳ Wird hochgeladen..."
-
Wird verarbeitet...
- Button: Disabled
- Badge: "🔄 Wird verarbeitet..."
-
Erfolgreich verarbeitet
- Button: "Zum Memo →" (Link zum Memo)
- Badge: "✓ Fertig"
- Optional: Memo-Titel anzeigen
-
Fehler
- Button: "Erneut versuchen"
- Badge: "❌ Upload fehlgeschlagen"
- Fehlerdetails in Tooltip/Modal
Technische Details
SQLite Schema-Erweiterung
-- Migration: Add upload tracking to recordings
ALTER TABLE recordings ADD COLUMN upload_status TEXT;
ALTER TABLE recordings ADD COLUMN uploaded_at INTEGER;
ALTER TABLE recordings ADD COLUMN upload_error TEXT;
ALTER TABLE recordings ADD COLUMN processing_status TEXT;
ALTER TABLE recordings ADD COLUMN memo_id TEXT;
ALTER TABLE recordings ADD COLUMN cloud_path TEXT;
-- Index für schnelles Lookup
CREATE INDEX idx_recordings_filename ON recordings(filename);
CREATE INDEX idx_recordings_memo_id ON recordings(memo_id);
Service-Methoden
// features/storage/fileStorage.service.ts
interface RecordingStatusUpdate {
uploadStatus?: 'pending' | 'uploading' | 'uploaded' | 'failed';
uploadedAt?: Date;
uploadError?: string;
processingStatus?: 'pending' | 'processing' | 'completed' | 'failed';
memoId?: string;
cloudPath?: string;
}
class FileStorageService {
async updateRecordingStatus(
recordingId: string,
update: RecordingStatusUpdate
): Promise<void> {
// SQLite Update...
}
async findRecordingByFilename(filename: string): Promise<AudioFile | null> {
// SQLite Query...
}
async findRecordingByMemoId(memoId: string): Promise<AudioFile | null> {
// SQLite Query...
}
async getRecordingsWithStatus(
status: 'uploaded' | 'processing' | 'completed'
): Promise<AudioFile[]> {
// SQLite Query...
}
}
Testing-Plan
Unit Tests
updateRecordingStatus()- Status-UpdatesfindRecordingByFilename()- Filename-Matching- Memo-Verbindungs-Logik
Integration Tests
- Upload → Status-Update → Memo-Verlinkung
- Fehlerbehandlung bei Upload-Fehler
- Doppel-Upload Prevention
E2E Tests
- Aufnahme → Upload → Verarbeitung → Memo anzeigen
- Offline-Upload → Online → Status-Sync
Offene Fragen
-
Dateinamen-Kollisionen: Was passiert wenn zwei Geräte dieselbe Dateiname verwenden?
- Lösung: Device-ID in Dateinamen einbauen oder eindeutige Upload-ID verwenden
-
Aufnahmen löschen: Soll Memo-Verbindung erhalten bleiben wenn lokale Aufnahme gelöscht wird?
- Empfehlung: Ja, nur
uploadStatusbehalten für Referenz
- Empfehlung: Ja, nur
-
Alte Aufnahmen: Wie mit bestehenden Aufnahmen ohne Status umgehen?
- Empfehlung: Initial alle auf
uploadStatus: nullsetzen, User kann manuell hochladen
- Empfehlung: Initial alle auf
-
Sync über Geräte: Sollen Upload-Stati zwischen Geräten synchronisiert werden?
- Phase 1: Nein (nur lokal)
- Phase 2: Ja (via Backend)
Zusammenfassung
Ja, es ist machbar!
Die vorgeschlagene Lösung ermöglicht:
- ✅ Status-Anzeige: Upload & Verarbeitungsstatus im Archiv sichtbar
- ✅ Memo-Verlinkung: Direkte Navigation zum fertigen Memo
- ✅ Fehlerbehandlung: Klare Fehlermeldungen und Retry-Möglichkeit
- ✅ Doppel-Upload Prevention: Bereits hochgeladene Aufnahmen markiert
- ✅ Progressive Enhancement: Schnelle Basis-Implementierung, später erweiterbar
Geschätzter Aufwand (Phase 1)
- Datenmodell & Migration: 2-3h
- Service-Implementierung: 3-4h
- Upload-Flow-Integration: 2-3h
- UI-Komponenten: 3-4h
- Testing & Bugfixes: 2-3h
Gesamt: ~12-17 Stunden (1.5-2 Arbeitstage)