File: src/services/ocr.ts
Compresses an image for optimal OCR processing.
- Parameters:
file- Original image file - Returns: Compressed image blob (max 1920px, JPEG 85% quality)
- Usage:
const compressed = await compressImage(imageFile);
Sends image to OCR edge function for text extraction.
- Parameters:
imageUrl- Supabase Storage URL - Returns:
OcrResultwith lines, text, and confidence - Throws: Error if OCR fails
Uploads image to Supabase Storage.
- Parameters:
file- Image file to uploaduserId- User's UUID for folder path
- Returns: Public URL of uploaded image
File: src/services/spotify.ts
Starts Spotify OAuth flow with PKCE.
- Side Effects:
- Generates code verifier and challenge
- Stores verifier in localStorage
- Redirects to Spotify authorization
Exchanges authorization code for access token.
- Parameters:
code- Authorization code from callback - Returns:
trueif successful - Side Effects: Stores tokens in localStorage
Searches Spotify catalog for tracks.
- Parameters:
query- Search string (title + artist) - Returns: Array of matching tracks (max 5)
Creates a new playlist with specified tracks.
- Parameters:
name- Playlist nametrackIds- Array of Spotify track IDs
- Returns: Created playlist with ID and URL
Checks if user has valid Spotify tokens.
Clears stored Spotify tokens.
Returns current access token if valid.
File: src/services/songMatcher.ts
Matches parsed songs to Spotify tracks.
- Parameters:
songs- Array of parsed song data - Returns: Array with Spotify matches and confidence scores
- Algorithm:
- Search Spotify for each song
- Score results using Levenshtein distance
- Consider title similarity, artist similarity, era
- Return best match above threshold
interface MatchScore {
titleScore: number; // 0-1, weighted 0.5
artistScore: number; // 0-1, weighted 0.4
eraScore: number; // 0-1, weighted 0.1
total: number; // Combined score
}File: src/services/textParser.ts
Main entry point for parsing OCR output.
- Parameters:
lines- OCR line data with bounding boxesdefaultSide- Default tape side (default: 'A')
- Returns: Array of parsed songs
| Function | Purpose |
|---|---|
detectColumns() |
Identifies left/right columns for A/B sides |
splitWideSpanningLine() |
Splits lines spanning both columns |
parseSongAndArtist() |
Extracts title and artist from text |
stripSongDuration() |
Removes duration timestamps |
isLikelySongEntry() |
Filters non-song text |
combineConsecutiveLines() |
Merges split lines |
Endpoint: {SUPABASE_URL}/functions/v1/ocr-scan
Processes an image URL through Google Cloud Vision OCR.
Request:
{
imageUrl: string; // URL of image to process
}Response:
{
lines: Array<{
text: string;
confidence: number;
boundingBox: {
x: number;
y: number;
width: number;
height: number;
};
}>;
fullText: string;
confidence: number;
}Headers Required:
Authorization: Bearer {SUPABASE_ANON_KEY}Content-Type: application/json
Error Responses:
400- Missing imageUrl500- OCR processing failed
interface OcrLine {
text: string;
confidence: number;
boundingBox: {
x: number;
y: number;
width: number;
height: number;
};
}interface ParsedSong {
id: string;
side: 'A' | 'B';
trackNumber: number;
extractedText: string;
title: string;
artist: string;
duration?: string;
confidence: number;
}interface MatchedSong extends ParsedSong {
spotifyTrack?: SpotifyTrack;
matchConfidence: number;
manuallyMatched: boolean;
}interface SpotifyTrack {
id: string;
name: string;
artists: Array<{ name: string }>;
album: {
name: string;
images: Array<{ url: string }>;
release_date: string;
};
duration_ms: number;
external_urls: {
spotify: string;
};
}// Sign up
const { data, error } = await supabase.auth.signUp({
email: 'user@example.com',
password: 'password123'
});
// Sign in
const { data, error } = await supabase.auth.signInWithPassword({
email: 'user@example.com',
password: 'password123'
});
// Sign out
await supabase.auth.signOut();
// Get session
const { data: { session } } = await supabase.auth.getSession();// 1. Generate verifier and challenge
const verifier = generateCodeVerifier();
const challenge = await generateCodeChallenge(verifier);
// 2. Redirect to Spotify
window.location.href = `https://accounts.spotify.com/authorize?
client_id=${CLIENT_ID}&
response_type=code&
redirect_uri=${REDIRECT_URI}&
code_challenge=${challenge}&
code_challenge_method=S256&
scope=${SCOPES}`;
// 3. Exchange code for token (in callback)
const response = await fetch('https://accounts.spotify.com/api/token', {
method: 'POST',
body: new URLSearchParams({
grant_type: 'authorization_code',
code: authCode,
redirect_uri: REDIRECT_URI,
client_id: CLIENT_ID,
code_verifier: verifier
})
});