feat(core/backend): pipeline continuity + threaded ffmpeg burn-in export + upload/save-timeline

Video pipeline (script→assets→storyboard→video→stitch):
- robust split_script_into_segments (4 non-empty scenes), scene-aware storyboard/video prompts
- link VideoSegment→ScriptSegment + storyboard-frame reference image (graceful text fallback)
- idempotent poll_video_segment (no double-charge on repeated polling)
- threaded export (no Celery worker needed) + poll-export endpoint
- run_export_job rewritten to filter_complex: per-clip trim, xfade transitions,
  subtitle burn-in (Pillow PNG overlay; this ffmpeg lacks libass), BGM mix
- upload-video-segment / upload-bgm / save-timeline endpoints
- serializers embed asset preview URLs (beat assets pagination); Pillow added to requirements

Also includes prior uncommitted backend work: account preferences/sessions,
billing trend, product/asset endpoints, accounts 0002 migration.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
zyc
2026-06-09 14:46:16 +08:00
co-authored by Claude Opus 4.8
parent 8959946241
commit 92826dec14
13 changed files with 1229 additions and 97 deletions
@@ -0,0 +1,94 @@
# Generated by Django 5.1.15 on 2026-06-08 09:48
import apps.accounts.models
import django.db.models.deletion
import uuid
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("accounts", "0001_initial"),
]
operations = [
migrations.CreateModel(
name="LoginSession",
fields=[
(
"id",
models.UUIDField(
default=uuid.uuid4,
editable=False,
primary_key=True,
serialize=False,
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
("user_agent", models.CharField(blank=True, max_length=400)),
("ip_address", models.GenericIPAddressField(blank=True, null=True)),
("last_seen_at", models.DateTimeField(auto_now=True)),
("revoked_at", models.DateTimeField(blank=True, null=True)),
(
"user",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="login_sessions",
to=settings.AUTH_USER_MODEL,
),
),
],
options={
"ordering": ["-last_seen_at"],
},
),
migrations.CreateModel(
name="UserPreference",
fields=[
(
"id",
models.UUIDField(
default=uuid.uuid4,
editable=False,
primary_key=True,
serialize=False,
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
(
"notify",
models.JSONField(
blank=True, default=apps.accounts.models._default_notify
),
),
("two_factor_enabled", models.BooleanField(default=False)),
(
"creation_defaults",
models.JSONField(
blank=True, default=apps.accounts.models._default_creation
),
),
(
"display",
models.JSONField(
blank=True, default=apps.accounts.models._default_display
),
),
(
"user",
models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
related_name="preference",
to=settings.AUTH_USER_MODEL,
),
),
],
options={
"abstract": False,
},
),
]