updated api_routed and db_queries for audio files

This commit is contained in:
Gaumit Kauts
2026-02-15 00:01:05 -07:00
parent 2f6e776818
commit c5e3004f02
4 changed files with 85 additions and 1 deletions

View File

@@ -22,6 +22,7 @@ from db_queries import (
create_audio_post,
create_user,
get_archive_metadata,
get_original_audio_url,
get_archive_rights,
get_audio_post_by_id,
get_post_bundle,
@@ -438,6 +439,34 @@ def api_post_bundle(post_id: int):
return jsonify(bundle)
@api.get("/posts/<int:post_id>/audio-url")
def api_post_audio_url(post_id: int):
"""
Get signed URL for original audio/video so users can play it.
Private posts require owner user_id in query params.
"""
row = get_audio_post_by_id(post_id)
if not row:
return _error("Post not found.", 404)
visibility = row.get("visibility")
owner_id = row.get("user_id")
requester_id = request.args.get("user_id", type=int)
expires_in = request.args.get("expires_in", default=3600, type=int)
expires_in = min(max(60, expires_in), 86400)
if visibility == "private" and requester_id != owner_id:
return _error("Not authorized to access this private audio.", 403)
try:
result = get_original_audio_url(post_id=post_id, expires_in=expires_in)
return jsonify(result)
except ValueError as e:
return _error(str(e), 404)
except Exception as e:
return _error(str(e), 500)
@api.post("/posts/<int:post_id>/files")
def api_add_file(post_id: int):
payload = request.get_json(force=True, silent=False) or {}

View File

@@ -3,7 +3,7 @@ Supabase data layer aligned with TitanForge/schema.sql.
"""
import os
from typing import Any, Dict, List, Optional
from typing import Any, Dict, List, Optional, Tuple
from dotenv import load_dotenv
from supabase import Client, create_client
@@ -36,6 +36,17 @@ def _paginate(page: int, limit: int) -> tuple[int, int]:
return start, end
def _parse_bucket_path(stored_path: str) -> Tuple[str, str]:
"""
Convert stored path like 'archives/user/uuid/original/file.mp4'
into ('archives', 'user/uuid/original/file.mp4').
"""
parts = (stored_path or "").split("/", 1)
if len(parts) != 2 or not parts[0] or not parts[1]:
raise ValueError(f"Invalid storage path format: {stored_path}")
return parts[0], parts[1]
def upload_storage_object(
bucket: str,
object_path: str,
@@ -56,6 +67,50 @@ def upload_storage_object(
)
def get_original_audio_url(post_id: int, expires_in: int = 3600) -> Dict[str, Any]:
"""
Return a signed URL for the original audio/video archive file.
"""
response = (
supabase.table("archive_files")
.select("path, content_type")
.eq("post_id", post_id)
.eq("role", "original_audio")
.limit(1)
.execute()
)
row = _first(response)
if not row:
raise ValueError("Original audio file not found for this post.")
bucket, object_path = _parse_bucket_path(row["path"])
signed = supabase.storage.from_(bucket).create_signed_url(object_path, expires_in)
# Supabase python client can return dict or object with .get depending on version.
if isinstance(signed, dict):
signed_url = (
signed.get("signedURL")
or signed.get("signedUrl")
or signed.get("data", {}).get("signedUrl")
or signed.get("data", {}).get("signedURL")
)
else:
signed_url = None
if not signed_url:
raise RuntimeError("Failed to create signed URL for original audio.")
return {
"post_id": post_id,
"bucket": bucket,
"object_path": object_path,
"content_type": row.get("content_type"),
"signed_url": signed_url,
"expires_in": expires_in,
}
# ==================== Users ====================
def create_user(payload: Dict[str, Any]) -> Dict[str, Any]: