diff --git a/backend/apps/generation/views.py b/backend/apps/generation/views.py index 3b5e05e..9f38b33 100644 --- a/backend/apps/generation/views.py +++ b/backend/apps/generation/views.py @@ -27,8 +27,10 @@ logger = logging.getLogger(__name__) # File validation constants ALLOWED_IMAGE_EXTS = {'jpeg', 'jpg', 'png', 'webp', 'bmp', 'tiff', 'gif'} ALLOWED_VIDEO_EXTS = {'mp4', 'mov'} +ALLOWED_AUDIO_EXTS = {'mp3', 'wav'} MAX_IMAGE_SIZE = 30 * 1024 * 1024 # 30MB MAX_VIDEO_SIZE = 50 * 1024 * 1024 # 50MB +MAX_AUDIO_SIZE = 15 * 1024 * 1024 # 15MB # Columns added in migration 0003; may not exist in production DB yet. _M0003_COLS = ('ark_task_id', 'result_url', 'error_message', 'reference_urls') @@ -82,6 +84,9 @@ def upload_media_view(request): elif ext in ALLOWED_VIDEO_EXTS: media_type = 'video' max_size = MAX_VIDEO_SIZE + elif ext in ALLOWED_AUDIO_EXTS: + media_type = 'audio' + max_size = MAX_AUDIO_SIZE else: return Response( {'error': f'不支持的文件格式: {ext}'}, @@ -276,10 +281,11 @@ def video_tasks_list_view(request): return Response({'results': results}) -@api_view(['GET']) +@api_view(['GET', 'DELETE']) @permission_classes([IsAuthenticated]) def video_task_detail_view(request, task_id): - """GET /api/v1/video/tasks/ — Get task status, poll Seedance if active.""" + """GET /api/v1/video/tasks/ — Get task status, poll Seedance if active. + DELETE /api/v1/video/tasks/ — Delete task record.""" try: record = _eval_qs( GenerationRecord.objects.filter(user=request.user), @@ -288,6 +294,10 @@ def video_task_detail_view(request, task_id): except GenerationRecord.DoesNotExist: return Response({'error': '任务不存在'}, status=status.HTTP_404_NOT_FOUND) + if request.method == 'DELETE': + record.delete() + return Response(status=status.HTTP_204_NO_CONTENT) + # If task is still active, poll Seedance API for latest status ark_task_id = record.__dict__.get('ark_task_id', '') if record.status in ('queued', 'processing') and ark_task_id: diff --git a/backend/config/asgi.py b/backend/config/asgi.py index 64c5b3c..88fe41a 100644 --- a/backend/config/asgi.py +++ b/backend/config/asgi.py @@ -1,4 +1,4 @@ -"""ASGI config for Jimeng Clone backend.""" +"""ASGI config for AirDrama backend.""" import os from django.core.asgi import get_asgi_application diff --git a/backend/config/settings.py b/backend/config/settings.py index aa1e6d2..2863a60 100644 --- a/backend/config/settings.py +++ b/backend/config/settings.py @@ -1,4 +1,4 @@ -"""Django settings for Jimeng Clone backend.""" +"""Django settings for AirDrama backend.""" import os from pathlib import Path @@ -8,7 +8,7 @@ BASE_DIR = Path(__file__).resolve().parent.parent SECRET_KEY = os.environ.get( 'DJANGO_SECRET_KEY', - 'django-insecure-dev-key-change-in-production-jimeng-clone-2026' + 'django-insecure-dev-key-change-in-production-airdrama-2026' ) DEBUG = os.environ.get('DJANGO_DEBUG', 'True').lower() in ('true', '1', 'yes') diff --git a/backend/config/wsgi.py b/backend/config/wsgi.py index f02075b..3fbb634 100644 --- a/backend/config/wsgi.py +++ b/backend/config/wsgi.py @@ -1,4 +1,4 @@ -"""WSGI config for Jimeng Clone backend.""" +"""WSGI config for AirDrama backend.""" import os from django.core.wsgi import get_wsgi_application diff --git a/web/index.html b/web/index.html index 0f3e3de..9c7a3d0 100644 --- a/web/index.html +++ b/web/index.html @@ -4,7 +4,7 @@ - 即梦 — AI 视频生成 + AirDrama — AI 视频生成
diff --git a/web/package-lock.json b/web/package-lock.json index 0d64662..80dec7f 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -170,6 +170,7 @@ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -531,6 +532,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=20.19.0" }, @@ -571,6 +573,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=20.19.0" } @@ -1560,8 +1563,7 @@ "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@types/babel__core": { "version": "7.20.5", @@ -1653,6 +1655,7 @@ "integrity": "sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" @@ -1664,6 +1667,7 @@ "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", "dev": true, "license": "MIT", + "peer": true, "peerDependencies": { "@types/react": "^18.0.0" } @@ -1839,7 +1843,6 @@ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -1850,7 +1853,6 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -1950,6 +1952,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -2209,8 +2212,7 @@ "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/dom-helpers": { "version": "5.2.1", @@ -2680,6 +2682,7 @@ "integrity": "sha512-0+MoQNYyr2rBHqO1xilltfDjV9G7ymYGlAUazgcDLQaUf8JDHbuGwsxN6U9qWaElZ4w1B2r7yEGIL3GdeW3Rug==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@acemir/cssom": "^0.9.31", "@asamuzakjp/dom-selector": "^6.8.1", @@ -2775,7 +2778,6 @@ "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", "dev": true, "license": "MIT", - "peer": true, "bin": { "lz-string": "bin/bin.js" } @@ -2929,6 +2931,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -3018,7 +3021,6 @@ "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", @@ -3033,8 +3035,7 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/prop-types": { "version": "15.8.1", @@ -3074,6 +3075,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -3098,6 +3100,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -3593,6 +3596,7 @@ "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", diff --git a/web/public/demo/demo-16-9.mp4 b/web/public/demo/demo-16-9.mp4 new file mode 100644 index 0000000..cda0cb0 Binary files /dev/null and b/web/public/demo/demo-16-9.mp4 differ diff --git a/web/public/demo/demo-21-9.mp4 b/web/public/demo/demo-21-9.mp4 new file mode 100644 index 0000000..8271a87 Binary files /dev/null and b/web/public/demo/demo-21-9.mp4 differ diff --git a/web/public/demo/demo-9-16.mp4 b/web/public/demo/demo-9-16.mp4 new file mode 100644 index 0000000..a0cce4d Binary files /dev/null and b/web/public/demo/demo-9-16.mp4 differ diff --git a/web/src/App.tsx b/web/src/App.tsx index 37b2ee6..af5f725 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -1,5 +1,6 @@ import { useEffect } from 'react'; import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'; +import { AmbientBackground } from './components/AmbientBackground'; import { VideoGenerationPage } from './components/VideoGenerationPage'; import { ProtectedRoute } from './components/ProtectedRoute'; import { LoginPage } from './pages/LoginPage'; @@ -10,6 +11,7 @@ import { UsersPage } from './pages/UsersPage'; import { RecordsPage } from './pages/RecordsPage'; import { SettingsPage } from './pages/SettingsPage'; import { ProfilePage } from './pages/ProfilePage'; +import { AssetsPage } from './pages/AssetsPage'; import { useAuthStore } from './store/auth'; export default function App() { @@ -21,6 +23,7 @@ export default function App() { return ( + } /> } /> + + + + } + /> (null); + + useEffect(() => { + const el = glowRef.current; + if (!el) return; + + let rafId: number; + let targetX = 50; + let targetY = 50; + let currentX = 50; + let currentY = 50; + + const handleMouseMove = (e: MouseEvent) => { + targetX = (e.clientX / window.innerWidth) * 100; + targetY = (e.clientY / window.innerHeight) * 100; + }; + + const animate = () => { + currentX += (targetX - currentX) * 0.08; + currentY += (targetY - currentY) * 0.08; + el.style.setProperty('--mouse-x', `${currentX}%`); + el.style.setProperty('--mouse-y', `${currentY}%`); + rafId = requestAnimationFrame(animate); + }; + + window.addEventListener('mousemove', handleMouseMove, { passive: true }); + rafId = requestAnimationFrame(animate); + + return () => { + window.removeEventListener('mousemove', handleMouseMove); + cancelAnimationFrame(rafId); + }; + }, []); + + return ( + <> +