karaoke-gen 0.99.3__py3-none-any.whl → 0.101.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- backend/api/routes/admin.py +512 -1
- backend/api/routes/audio_search.py +13 -2
- backend/api/routes/file_upload.py +42 -1
- backend/api/routes/internal.py +6 -0
- backend/api/routes/jobs.py +9 -1
- backend/api/routes/review.py +13 -6
- backend/api/routes/tenant.py +120 -0
- backend/api/routes/users.py +167 -245
- backend/main.py +6 -1
- backend/middleware/__init__.py +7 -1
- backend/middleware/tenant.py +192 -0
- backend/models/job.py +19 -3
- backend/models/tenant.py +208 -0
- backend/models/user.py +18 -0
- backend/services/email_service.py +253 -6
- backend/services/firestore_service.py +6 -0
- backend/services/job_manager.py +32 -1
- backend/services/stripe_service.py +61 -35
- backend/services/tenant_service.py +285 -0
- backend/services/user_service.py +85 -7
- backend/tests/emulator/test_made_for_you_integration.py +167 -0
- backend/tests/test_admin_job_files.py +337 -0
- backend/tests/test_admin_job_reset.py +384 -0
- backend/tests/test_admin_job_update.py +326 -0
- backend/tests/test_email_service.py +233 -0
- backend/tests/test_impersonation.py +223 -0
- backend/tests/test_job_creation_regression.py +4 -0
- backend/tests/test_job_manager.py +146 -1
- backend/tests/test_made_for_you.py +2086 -0
- backend/tests/test_models.py +139 -0
- backend/tests/test_tenant_api.py +350 -0
- backend/tests/test_tenant_middleware.py +345 -0
- backend/tests/test_tenant_models.py +406 -0
- backend/tests/test_tenant_service.py +418 -0
- backend/workers/video_worker.py +8 -3
- {karaoke_gen-0.99.3.dist-info → karaoke_gen-0.101.0.dist-info}/METADATA +1 -1
- {karaoke_gen-0.99.3.dist-info → karaoke_gen-0.101.0.dist-info}/RECORD +42 -28
- lyrics_transcriber/frontend/src/api.ts +13 -5
- lyrics_transcriber/frontend/src/components/PreviewVideoSection.tsx +90 -57
- {karaoke_gen-0.99.3.dist-info → karaoke_gen-0.101.0.dist-info}/WHEEL +0 -0
- {karaoke_gen-0.99.3.dist-info → karaoke_gen-0.101.0.dist-info}/entry_points.txt +0 -0
- {karaoke_gen-0.99.3.dist-info → karaoke_gen-0.101.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -6,7 +6,7 @@ export interface ApiClient {
|
|
|
6
6
|
getCorrectionData: () => Promise<CorrectionData>;
|
|
7
7
|
submitCorrections: (data: CorrectionData) => Promise<void>;
|
|
8
8
|
getAudioUrl: (audioHash: string) => string;
|
|
9
|
-
generatePreviewVideo: (data: CorrectionData) => Promise<PreviewVideoResponse>;
|
|
9
|
+
generatePreviewVideo: (data: CorrectionData, options?: PreviewOptions) => Promise<PreviewVideoResponse>;
|
|
10
10
|
getPreviewVideoUrl: (previewHash: string) => string;
|
|
11
11
|
updateHandlers: (enabledHandlers: string[]) => Promise<CorrectionData>;
|
|
12
12
|
isUpdatingHandlers?: boolean;
|
|
@@ -21,6 +21,11 @@ interface CorrectionUpdate {
|
|
|
21
21
|
corrected_segments: CorrectionData['corrected_segments'];
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
// Add interface for preview generation options
|
|
25
|
+
export interface PreviewOptions {
|
|
26
|
+
use_background_image?: boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
24
29
|
// Add new interface for preview response
|
|
25
30
|
interface PreviewVideoResponse {
|
|
26
31
|
status: "success" | "error";
|
|
@@ -96,11 +101,13 @@ export class LiveApiClient implements ApiClient {
|
|
|
96
101
|
return this.buildUrl(`/audio/${audioHash}`)
|
|
97
102
|
}
|
|
98
103
|
|
|
99
|
-
async generatePreviewVideo(data: CorrectionData): Promise<PreviewVideoResponse> {
|
|
104
|
+
async generatePreviewVideo(data: CorrectionData, options?: PreviewOptions): Promise<PreviewVideoResponse> {
|
|
100
105
|
// Extract only the needed fields, just like in submitCorrections
|
|
101
|
-
|
|
106
|
+
// Include use_background_image option (defaults to false for fast black background)
|
|
107
|
+
const updatePayload = {
|
|
102
108
|
corrections: data.corrections,
|
|
103
|
-
corrected_segments: data.corrected_segments
|
|
109
|
+
corrected_segments: data.corrected_segments,
|
|
110
|
+
use_background_image: options?.use_background_image ?? false,
|
|
104
111
|
};
|
|
105
112
|
|
|
106
113
|
const response = await fetch(this.buildUrl('/preview-video'), {
|
|
@@ -224,7 +231,8 @@ export class FileOnlyClient implements ApiClient {
|
|
|
224
231
|
throw new Error('Not supported in file-only mode');
|
|
225
232
|
}
|
|
226
233
|
|
|
227
|
-
|
|
234
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
235
|
+
async generatePreviewVideo(_data: CorrectionData, _options?: PreviewOptions): Promise<PreviewVideoResponse> {
|
|
228
236
|
throw new Error('Not supported in file-only mode');
|
|
229
237
|
}
|
|
230
238
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Box, Typography, CircularProgress, Alert, Button } from '@mui/material'
|
|
2
|
-
import { useState, useEffect } from 'react'
|
|
1
|
+
import { Box, Typography, CircularProgress, Alert, Button, FormControlLabel, Checkbox } from '@mui/material'
|
|
2
|
+
import { useState, useEffect, useCallback } from 'react'
|
|
3
3
|
import { ApiClient } from '../api'
|
|
4
4
|
import { CorrectionData } from '../types'
|
|
5
5
|
import { applyOffsetToCorrectionData } from './shared/utils/timingUtils'
|
|
@@ -25,75 +25,108 @@ export default function PreviewVideoSection({
|
|
|
25
25
|
error?: string;
|
|
26
26
|
}>({ status: 'loading' });
|
|
27
27
|
|
|
28
|
-
//
|
|
29
|
-
|
|
30
|
-
if (isModalOpen && apiClient) {
|
|
31
|
-
const generatePreview = async () => {
|
|
32
|
-
setPreviewState({ status: 'loading' });
|
|
33
|
-
try {
|
|
34
|
-
// Debug logging for timing offset
|
|
35
|
-
console.log(`[TIMING] PreviewVideoSection - Current timing offset: ${timingOffsetMs}ms`);
|
|
36
|
-
|
|
37
|
-
// Apply timing offset if needed
|
|
38
|
-
const dataToPreview = timingOffsetMs !== 0
|
|
39
|
-
? applyOffsetToCorrectionData(updatedData, timingOffsetMs)
|
|
40
|
-
: updatedData;
|
|
41
|
-
|
|
42
|
-
// Log some example timestamps after potential offset application
|
|
43
|
-
if (dataToPreview.corrected_segments.length > 0) {
|
|
44
|
-
const firstSegment = dataToPreview.corrected_segments[0];
|
|
45
|
-
console.log(`[TIMING] Preview - First segment id: ${firstSegment.id}`);
|
|
46
|
-
console.log(`[TIMING] - start_time: ${firstSegment.start_time}, end_time: ${firstSegment.end_time}`);
|
|
47
|
-
|
|
48
|
-
if (firstSegment.words.length > 0) {
|
|
49
|
-
const firstWord = firstSegment.words[0];
|
|
50
|
-
console.log(`[TIMING] - first word "${firstWord.text}" time: ${firstWord.start_time} -> ${firstWord.end_time}`);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const response = await apiClient.generatePreviewVideo(dataToPreview);
|
|
55
|
-
|
|
56
|
-
if (response.status === 'error') {
|
|
57
|
-
setPreviewState({
|
|
58
|
-
status: 'error',
|
|
59
|
-
error: response.message || 'Failed to generate preview video'
|
|
60
|
-
});
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
28
|
+
// Toggle for rendering with theme background image (slower) vs black background (faster)
|
|
29
|
+
const [useBackgroundImage, setUseBackgroundImage] = useState(false);
|
|
63
30
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
31
|
+
// Memoized function to generate preview
|
|
32
|
+
const generatePreview = useCallback(async () => {
|
|
33
|
+
if (!apiClient) return;
|
|
34
|
+
|
|
35
|
+
setPreviewState({ status: 'loading' });
|
|
36
|
+
try {
|
|
37
|
+
// Debug logging for timing offset
|
|
38
|
+
console.log(`[TIMING] PreviewVideoSection - Current timing offset: ${timingOffsetMs}ms`);
|
|
39
|
+
console.log(`[PREVIEW] Using background image: ${useBackgroundImage}`);
|
|
40
|
+
|
|
41
|
+
// Apply timing offset if needed
|
|
42
|
+
const dataToPreview = timingOffsetMs !== 0
|
|
43
|
+
? applyOffsetToCorrectionData(updatedData, timingOffsetMs)
|
|
44
|
+
: updatedData;
|
|
71
45
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
});
|
|
46
|
+
// Log some example timestamps after potential offset application
|
|
47
|
+
if (dataToPreview.corrected_segments.length > 0) {
|
|
48
|
+
const firstSegment = dataToPreview.corrected_segments[0];
|
|
49
|
+
console.log(`[TIMING] Preview - First segment id: ${firstSegment.id}`);
|
|
50
|
+
console.log(`[TIMING] - start_time: ${firstSegment.start_time}, end_time: ${firstSegment.end_time}`);
|
|
51
|
+
|
|
52
|
+
if (firstSegment.words.length > 0) {
|
|
53
|
+
const firstWord = firstSegment.words[0];
|
|
54
|
+
console.log(`[TIMING] - first word "${firstWord.text}" time: ${firstWord.start_time} -> ${firstWord.end_time}`);
|
|
82
55
|
}
|
|
83
|
-
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const response = await apiClient.generatePreviewVideo(dataToPreview, {
|
|
59
|
+
use_background_image: useBackgroundImage
|
|
60
|
+
});
|
|
84
61
|
|
|
62
|
+
if (response.status === 'error') {
|
|
63
|
+
setPreviewState({
|
|
64
|
+
status: 'error',
|
|
65
|
+
error: response.message || 'Failed to generate preview video'
|
|
66
|
+
});
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (!response.preview_hash) {
|
|
71
|
+
setPreviewState({
|
|
72
|
+
status: 'error',
|
|
73
|
+
error: 'No preview hash received from server'
|
|
74
|
+
});
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const videoUrl = apiClient.getPreviewVideoUrl(response.preview_hash);
|
|
79
|
+
setPreviewState({
|
|
80
|
+
status: 'ready',
|
|
81
|
+
videoUrl
|
|
82
|
+
});
|
|
83
|
+
} catch (error) {
|
|
84
|
+
setPreviewState({
|
|
85
|
+
status: 'error',
|
|
86
|
+
error: (error as Error).message || 'Failed to generate preview video'
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}, [apiClient, updatedData, timingOffsetMs, useBackgroundImage]);
|
|
90
|
+
|
|
91
|
+
// Generate preview when modal opens or when background toggle changes
|
|
92
|
+
useEffect(() => {
|
|
93
|
+
if (isModalOpen && apiClient) {
|
|
85
94
|
generatePreview();
|
|
86
95
|
}
|
|
87
|
-
}, [isModalOpen, apiClient,
|
|
96
|
+
}, [isModalOpen, apiClient, generatePreview]);
|
|
88
97
|
|
|
89
98
|
if (!apiClient) return null;
|
|
90
99
|
|
|
91
100
|
return (
|
|
92
101
|
<Box sx={{ mb: 2 }}>
|
|
102
|
+
{/* Background image toggle */}
|
|
103
|
+
<Box sx={{ px: 2, pt: 2, pb: 1 }}>
|
|
104
|
+
<FormControlLabel
|
|
105
|
+
control={
|
|
106
|
+
<Checkbox
|
|
107
|
+
checked={useBackgroundImage}
|
|
108
|
+
onChange={(e) => setUseBackgroundImage(e.target.checked)}
|
|
109
|
+
disabled={previewState.status === 'loading'}
|
|
110
|
+
size="small"
|
|
111
|
+
/>
|
|
112
|
+
}
|
|
113
|
+
label={
|
|
114
|
+
<Box>
|
|
115
|
+
<Typography variant="body2" component="span">
|
|
116
|
+
Render with theme background
|
|
117
|
+
</Typography>
|
|
118
|
+
<Typography variant="caption" color="text.secondary" display="block">
|
|
119
|
+
Preview uses black background for speed (~10s). Enable for theme background (~30-60s).
|
|
120
|
+
</Typography>
|
|
121
|
+
</Box>
|
|
122
|
+
}
|
|
123
|
+
/>
|
|
124
|
+
</Box>
|
|
125
|
+
|
|
93
126
|
{previewState.status === 'loading' && (
|
|
94
127
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2, p: 2 }}>
|
|
95
128
|
<CircularProgress size={24} />
|
|
96
|
-
<Typography>Generating preview video...</Typography>
|
|
129
|
+
<Typography>Generating preview video{useBackgroundImage ? ' with theme background' : ''}...</Typography>
|
|
97
130
|
</Box>
|
|
98
131
|
)}
|
|
99
132
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|