-
Notifications
You must be signed in to change notification settings - Fork 0
Feature/gateway unified analysis #8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| // Unified analysis gateway (mock/preview). Real integrations (yt-dlp/model) should be wired to the providers. | ||
| const { promises: fs } = require('fs') | ||
|
|
||
| exports.handler = async (event) => { | ||
| if (event.httpMethod !== 'POST') { | ||
| return { statusCode: 405, body: 'Method Not Allowed' } | ||
| } | ||
|
|
||
| // NOTE: Netlify Functions cannot parse multipart by default; here we return a fixed preview response. | ||
| // In real usage, move this to a full server or add multipart parsing + external service calls. | ||
| const now = Date.now() | ||
| const seed = (now % 1000) / 1000 | ||
| const isAIGenerated = seed > 0.5 | ||
| const confidence = Number((0.55 + seed * 0.35).toFixed(3)) | ||
|
|
||
| const result = { | ||
| isAIGenerated, | ||
| confidence, | ||
| processingTime: 0.4, | ||
| modelVersion: 'gateway-preview-v1', | ||
| decisionSource: 'preview', | ||
| source: { | ||
| kind: 'file', | ||
| fileName: 'preview', | ||
| fileSizeBytes: 0, | ||
| mimeType: 'application/octet-stream' | ||
| }, | ||
| features: { | ||
| spectralRegularity: Number(((seed + 0.17) % 1).toFixed(3)), | ||
| temporalPatterns: Number(((seed + 0.43) % 1).toFixed(3)), | ||
| harmonicStructure: Number(((seed + 0.71) % 1).toFixed(3)), | ||
| artificialIndicators: [ | ||
| 'Preview-only decision based on fingerprint.', | ||
| 'No model inference was available at request time.' | ||
| ] | ||
|
Comment on lines
+32
to
+35
|
||
| }, | ||
| audioInfo: { | ||
| duration: 0, | ||
| sampleRate: 44100, | ||
| bitrate: 192, | ||
| format: 'PREVIEW' | ||
| } | ||
| } | ||
|
|
||
| return { | ||
| statusCode: 200, | ||
| body: JSON.stringify({ result, warnings: ['gateway_mock_preview'] }) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,59 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import type { AnalysisResult, AnalysisErrorCode } from '@/hooks/analysisTypes' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export type SourceType = 'youtube' | 'file' | 'spotify' | 'apple' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export interface AnalyzePayload { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sourceType: SourceType | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| url?: string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| file?: File | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export interface AnalyzeResponse { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result?: AnalysisResult | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| warnings?: string[] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| errors?: string[] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const MAX_BYTES = 30 * 1024 * 1024 // 30MB | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const MAX_BYTES = 30 * 1024 * 1024 // 30MB | |
| const MAX_BYTES = 100 * 1024 * 1024 // 100MB |
Copilot
AI
Dec 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The fetch call does not handle network errors or exceptions. If the fetch fails due to network issues (e.g., no internet connection, DNS failure), this will throw an unhandled exception. Consider wrapping the fetch in a try-catch block to properly handle these errors and return an appropriate error code like 'backend_unreachable'.
| const response = await fetch(`${apiBaseUrl}/api/analyze`, { | |
| method: 'POST', | |
| body: formData | |
| }) | |
| let response: Response | |
| try { | |
| response = await fetch(`${apiBaseUrl}/api/analyze`, { | |
| method: 'POST', | |
| body: formData | |
| }) | |
| } catch { | |
| return { result: null, error: 'backend_unreachable' as AnalysisErrorCode } | |
| } |
Copilot
AI
Dec 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error handling for non-OK HTTP responses is too broad. A non-OK response could be a 400 (bad request), 500 (server error), or network timeout, but all are mapped to 'backend_unreachable'. Consider checking response.status to provide more specific error codes. For example, 4xx errors could map to different error codes than 5xx errors, improving the user experience with more accurate error messages.
| const response = await fetch(`${apiBaseUrl}/api/analyze`, { | |
| method: 'POST', | |
| body: formData | |
| }) | |
| if (!response.ok) { | |
| return { result: null, error: 'backend_unreachable' as AnalysisErrorCode } | |
| let response: Response | |
| try { | |
| response = await fetch(`${apiBaseUrl}/api/analyze`, { | |
| method: 'POST', | |
| body: formData | |
| }) | |
| } catch { | |
| // Network-level error (timeout, DNS failure, etc.) | |
| return { result: null, error: 'backend_unreachable' as AnalysisErrorCode } | |
| } | |
| if (!response.ok) { | |
| // Distinguish between client and server errors for better error reporting | |
| if (response.status >= 400 && response.status < 500) { | |
| return { result: null, error: 'backend_unexpected_response' as AnalysisErrorCode } | |
| } | |
| if (response.status >= 500 && response.status < 600) { | |
| return { result: null, error: 'backend_unreachable' as AnalysisErrorCode } | |
| } | |
| return { result: null, error: 'backend_unexpected_response' as AnalysisErrorCode } |
Copilot
AI
Dec 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The JSON parsing on line 50 could throw an exception if the response body is not valid JSON. Consider wrapping this in a try-catch block or handling the case where the response is malformed, returning 'backend_unexpected_response' error in such cases.
| const data = await response.json() as AnalyzeResponse | |
| let data: AnalyzeResponse | |
| try { | |
| data = (await response.json()) as AnalyzeResponse | |
| } catch { | |
| return { result: null, error: 'backend_unexpected_response' as AnalysisErrorCode } | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The imported
fsmodule is never used in this function. Consider removing this unused import to keep the code clean.