karaoke-gen 0.96.0__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 +696 -92
- backend/api/routes/audio_search.py +29 -8
- backend/api/routes/file_upload.py +99 -22
- backend/api/routes/health.py +65 -0
- backend/api/routes/internal.py +6 -0
- backend/api/routes/jobs.py +28 -1
- backend/api/routes/review.py +13 -6
- backend/api/routes/tenant.py +120 -0
- backend/api/routes/users.py +472 -51
- backend/main.py +31 -2
- 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/encoding_service.py +128 -31
- backend/services/firestore_service.py +6 -0
- backend/services/job_manager.py +44 -2
- backend/services/langfuse_preloader.py +98 -0
- backend/services/nltk_preloader.py +122 -0
- backend/services/spacy_preloader.py +65 -0
- backend/services/stripe_service.py +133 -11
- backend/services/tenant_service.py +285 -0
- backend/services/user_service.py +85 -7
- backend/tests/emulator/conftest.py +22 -1
- 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 +171 -9
- backend/tests/test_jobs_api.py +11 -1
- backend/tests/test_made_for_you.py +2086 -0
- backend/tests/test_models.py +139 -0
- backend/tests/test_spacy_preloader.py +119 -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/utils/test_data.py +27 -0
- backend/workers/screens_worker.py +16 -6
- backend/workers/video_worker.py +8 -3
- {karaoke_gen-0.96.0.dist-info → karaoke_gen-0.101.0.dist-info}/METADATA +1 -1
- {karaoke_gen-0.96.0.dist-info → karaoke_gen-0.101.0.dist-info}/RECORD +58 -39
- lyrics_transcriber/correction/agentic/agent.py +17 -6
- lyrics_transcriber/correction/agentic/providers/langchain_bridge.py +96 -43
- lyrics_transcriber/correction/agentic/providers/model_factory.py +27 -6
- lyrics_transcriber/correction/anchor_sequence.py +151 -37
- lyrics_transcriber/correction/handlers/syllables_match.py +44 -2
- lyrics_transcriber/correction/phrase_analyzer.py +18 -0
- lyrics_transcriber/frontend/src/api.ts +13 -5
- lyrics_transcriber/frontend/src/components/PreviewVideoSection.tsx +90 -57
- {karaoke_gen-0.96.0.dist-info → karaoke_gen-0.101.0.dist-info}/WHEEL +0 -0
- {karaoke_gen-0.96.0.dist-info → karaoke_gen-0.101.0.dist-info}/entry_points.txt +0 -0
- {karaoke_gen-0.96.0.dist-info → karaoke_gen-0.101.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tenant API routes for white-label portal configuration.
|
|
3
|
+
|
|
4
|
+
These endpoints are public (no auth required) since the frontend needs
|
|
5
|
+
to fetch tenant branding before the user logs in.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
from typing import Optional
|
|
10
|
+
|
|
11
|
+
from fastapi import APIRouter, Query, Request
|
|
12
|
+
|
|
13
|
+
from backend.models.tenant import TenantConfigResponse, TenantPublicConfig
|
|
14
|
+
from backend.services.tenant_service import get_tenant_service
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
router = APIRouter(prefix="/api/tenant", tags=["tenant"])
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@router.get("/config", response_model=TenantConfigResponse)
|
|
22
|
+
async def get_tenant_config(
|
|
23
|
+
request: Request,
|
|
24
|
+
tenant: Optional[str] = Query(
|
|
25
|
+
None,
|
|
26
|
+
description="Tenant ID override (for development). If not provided, detected from subdomain.",
|
|
27
|
+
),
|
|
28
|
+
):
|
|
29
|
+
"""
|
|
30
|
+
Get tenant configuration for the current portal.
|
|
31
|
+
|
|
32
|
+
This endpoint detects the tenant from:
|
|
33
|
+
1. Query parameter `tenant` (for development/testing)
|
|
34
|
+
2. X-Tenant-ID header (set by frontend)
|
|
35
|
+
3. Host header subdomain
|
|
36
|
+
|
|
37
|
+
Returns the public tenant configuration (branding, features, defaults)
|
|
38
|
+
or indicates this is the default Nomad Karaoke portal.
|
|
39
|
+
"""
|
|
40
|
+
tenant_service = get_tenant_service()
|
|
41
|
+
|
|
42
|
+
# Priority 1: Query parameter (for dev/testing)
|
|
43
|
+
tenant_id = tenant
|
|
44
|
+
|
|
45
|
+
# Priority 2: X-Tenant-ID header
|
|
46
|
+
if not tenant_id:
|
|
47
|
+
tenant_id = request.headers.get("X-Tenant-ID")
|
|
48
|
+
|
|
49
|
+
# Priority 3: Detect from Host header
|
|
50
|
+
if not tenant_id:
|
|
51
|
+
host = request.headers.get("Host", "")
|
|
52
|
+
# Check if this is a tenant subdomain
|
|
53
|
+
# Pattern: {tenant}.nomadkaraoke.com or {tenant}.gen.nomadkaraoke.com
|
|
54
|
+
if host and "nomadkaraoke.com" in host.lower():
|
|
55
|
+
parts = host.lower().split(".")
|
|
56
|
+
# Skip known non-tenant subdomains
|
|
57
|
+
if parts[0] not in ["gen", "api", "www", "buy", "admin"]:
|
|
58
|
+
potential_tenant = parts[0]
|
|
59
|
+
if tenant_service.tenant_exists(potential_tenant):
|
|
60
|
+
tenant_id = potential_tenant
|
|
61
|
+
|
|
62
|
+
# If no tenant detected, return default config
|
|
63
|
+
if not tenant_id:
|
|
64
|
+
logger.debug("No tenant detected, returning default config")
|
|
65
|
+
return TenantConfigResponse(tenant=None, is_default=True)
|
|
66
|
+
|
|
67
|
+
# Load tenant config
|
|
68
|
+
public_config = tenant_service.get_public_config(tenant_id)
|
|
69
|
+
|
|
70
|
+
if not public_config:
|
|
71
|
+
logger.warning(f"Tenant not found: {tenant_id}")
|
|
72
|
+
return TenantConfigResponse(tenant=None, is_default=True)
|
|
73
|
+
|
|
74
|
+
if not public_config.is_active:
|
|
75
|
+
logger.warning(f"Tenant is inactive: {tenant_id}")
|
|
76
|
+
return TenantConfigResponse(tenant=None, is_default=True)
|
|
77
|
+
|
|
78
|
+
logger.info(f"Returning config for tenant: {tenant_id}")
|
|
79
|
+
return TenantConfigResponse(tenant=public_config, is_default=False)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@router.get("/config/{tenant_id}", response_model=TenantConfigResponse)
|
|
83
|
+
async def get_tenant_config_by_id(tenant_id: str):
|
|
84
|
+
"""
|
|
85
|
+
Get tenant configuration by explicit tenant ID.
|
|
86
|
+
|
|
87
|
+
This is useful for admin tools or debugging.
|
|
88
|
+
"""
|
|
89
|
+
tenant_service = get_tenant_service()
|
|
90
|
+
public_config = tenant_service.get_public_config(tenant_id)
|
|
91
|
+
|
|
92
|
+
if not public_config:
|
|
93
|
+
return TenantConfigResponse(tenant=None, is_default=True)
|
|
94
|
+
|
|
95
|
+
# Check if tenant is active
|
|
96
|
+
if not public_config.is_active:
|
|
97
|
+
logger.warning(f"Tenant is inactive: {tenant_id}")
|
|
98
|
+
return TenantConfigResponse(tenant=None, is_default=True)
|
|
99
|
+
|
|
100
|
+
return TenantConfigResponse(tenant=public_config, is_default=False)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@router.get("/asset/{tenant_id}/{asset_name}")
|
|
104
|
+
async def get_tenant_asset(tenant_id: str, asset_name: str):
|
|
105
|
+
"""
|
|
106
|
+
Get a signed URL for a tenant asset (logo, favicon, etc.).
|
|
107
|
+
|
|
108
|
+
This redirects to the signed GCS URL for the asset.
|
|
109
|
+
"""
|
|
110
|
+
from fastapi.responses import RedirectResponse
|
|
111
|
+
|
|
112
|
+
tenant_service = get_tenant_service()
|
|
113
|
+
url = tenant_service.get_asset_url(tenant_id, asset_name)
|
|
114
|
+
|
|
115
|
+
if not url:
|
|
116
|
+
from fastapi import HTTPException
|
|
117
|
+
|
|
118
|
+
raise HTTPException(status_code=404, detail="Asset not found")
|
|
119
|
+
|
|
120
|
+
return RedirectResponse(url=url)
|