update
This commit is contained in:
+48
-7
@@ -52,6 +52,11 @@ def init_db() -> None:
|
|||||||
segments_count INTEGER NOT NULL,
|
segments_count INTEGER NOT NULL,
|
||||||
intro_seconds REAL NOT NULL,
|
intro_seconds REAL NOT NULL,
|
||||||
outro_seconds REAL NOT NULL,
|
outro_seconds REAL NOT NULL,
|
||||||
|
reencode_enabled INTEGER NOT NULL DEFAULT 0,
|
||||||
|
encoding_passes INTEGER NOT NULL DEFAULT 1,
|
||||||
|
target_os TEXT NOT NULL DEFAULT 'windows',
|
||||||
|
ffmpeg_pass1_template TEXT,
|
||||||
|
ffmpeg_pass2_template TEXT,
|
||||||
created_at TEXT NOT NULL,
|
created_at TEXT NOT NULL,
|
||||||
updated_at TEXT NOT NULL
|
updated_at TEXT NOT NULL
|
||||||
);
|
);
|
||||||
@@ -65,6 +70,7 @@ def init_db() -> None:
|
|||||||
filename TEXT NOT NULL,
|
filename TEXT NOT NULL,
|
||||||
file_path TEXT NOT NULL,
|
file_path TEXT NOT NULL,
|
||||||
duration_seconds REAL NOT NULL,
|
duration_seconds REAL NOT NULL,
|
||||||
|
is_exported INTEGER NOT NULL DEFAULT 0,
|
||||||
created_at TEXT NOT NULL
|
created_at TEXT NOT NULL
|
||||||
);
|
);
|
||||||
CREATE TABLE IF NOT EXISTS markers (
|
CREATE TABLE IF NOT EXISTS markers (
|
||||||
@@ -80,6 +86,7 @@ def init_db() -> None:
|
|||||||
segment_key TEXT NOT NULL,
|
segment_key TEXT NOT NULL,
|
||||||
start_seconds REAL NOT NULL,
|
start_seconds REAL NOT NULL,
|
||||||
end_seconds REAL NOT NULL,
|
end_seconds REAL NOT NULL,
|
||||||
|
color TEXT,
|
||||||
modified_at TEXT NOT NULL
|
modified_at TEXT NOT NULL
|
||||||
);
|
);
|
||||||
CREATE INDEX IF NOT EXISTS idx_markers_video_id ON markers(video_id);
|
CREATE INDEX IF NOT EXISTS idx_markers_video_id ON markers(video_id);
|
||||||
@@ -90,6 +97,10 @@ def init_db() -> None:
|
|||||||
columns = {row[1] for row in cur.fetchall()}
|
columns = {row[1] for row in cur.fetchall()}
|
||||||
if "reencode_enabled" not in columns:
|
if "reencode_enabled" not in columns:
|
||||||
cur.execute("ALTER TABLE projects ADD COLUMN reencode_enabled INTEGER NOT NULL DEFAULT 0")
|
cur.execute("ALTER TABLE projects ADD COLUMN reencode_enabled INTEGER NOT NULL DEFAULT 0")
|
||||||
|
if "encoding_passes" not in columns:
|
||||||
|
cur.execute("ALTER TABLE projects ADD COLUMN encoding_passes INTEGER NOT NULL DEFAULT 1")
|
||||||
|
if "target_os" not in columns:
|
||||||
|
cur.execute("ALTER TABLE projects ADD COLUMN target_os TEXT NOT NULL DEFAULT 'windows'")
|
||||||
if "ffmpeg_pass1_template" not in columns:
|
if "ffmpeg_pass1_template" not in columns:
|
||||||
cur.execute("ALTER TABLE projects ADD COLUMN ffmpeg_pass1_template TEXT")
|
cur.execute("ALTER TABLE projects ADD COLUMN ffmpeg_pass1_template TEXT")
|
||||||
if "ffmpeg_pass2_template" not in columns:
|
if "ffmpeg_pass2_template" not in columns:
|
||||||
@@ -99,6 +110,13 @@ def init_db() -> None:
|
|||||||
if "modified_at" not in segment_edit_columns:
|
if "modified_at" not in segment_edit_columns:
|
||||||
cur.execute("ALTER TABLE segment_edits ADD COLUMN modified_at TEXT")
|
cur.execute("ALTER TABLE segment_edits ADD COLUMN modified_at TEXT")
|
||||||
cur.execute("UPDATE segment_edits SET modified_at = ?", (_now(),))
|
cur.execute("UPDATE segment_edits SET modified_at = ?", (_now(),))
|
||||||
|
if "color" not in segment_edit_columns:
|
||||||
|
cur.execute("ALTER TABLE segment_edits ADD COLUMN color TEXT")
|
||||||
|
|
||||||
|
cur.execute("PRAGMA table_info(videos)")
|
||||||
|
video_columns = {row[1] for row in cur.fetchall()}
|
||||||
|
if "is_exported" not in video_columns:
|
||||||
|
cur.execute("ALTER TABLE videos ADD COLUMN is_exported INTEGER NOT NULL DEFAULT 0")
|
||||||
cur.execute(
|
cur.execute(
|
||||||
"UPDATE projects SET ffmpeg_pass1_template = ? WHERE ffmpeg_pass1_template IS NULL",
|
"UPDATE projects SET ffmpeg_pass1_template = ? WHERE ffmpeg_pass1_template IS NULL",
|
||||||
(DEFAULT_FFMPEG_PASS1_TEMPLATE,),
|
(DEFAULT_FFMPEG_PASS1_TEMPLATE,),
|
||||||
@@ -111,10 +129,19 @@ def init_db() -> None:
|
|||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
|
|
||||||
|
def mark_video_exported(video_id: str) -> None:
|
||||||
|
with _DB_LOCK:
|
||||||
|
conn = get_conn()
|
||||||
|
cur = conn.cursor()
|
||||||
|
cur.execute("UPDATE videos SET is_exported = 1 WHERE id = ?", (video_id,))
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
def _normalize_project(project: dict | None) -> dict | None:
|
def _normalize_project(project: dict | None) -> dict | None:
|
||||||
if not project:
|
if not project:
|
||||||
return None
|
return None
|
||||||
project["reencode_enabled"] = bool(project.get("reencode_enabled", 0))
|
project["reencode_enabled"] = bool(project.get("reencode_enabled", 0))
|
||||||
|
project["encoding_passes"] = int(project.get("encoding_passes", 1))
|
||||||
|
project["target_os"] = project.get("target_os", "windows")
|
||||||
project["ffmpeg_pass1_template"] = project.get("ffmpeg_pass1_template") or DEFAULT_FFMPEG_PASS1_TEMPLATE
|
project["ffmpeg_pass1_template"] = project.get("ffmpeg_pass1_template") or DEFAULT_FFMPEG_PASS1_TEMPLATE
|
||||||
project["ffmpeg_pass2_template"] = project.get("ffmpeg_pass2_template") or DEFAULT_FFMPEG_PASS2_TEMPLATE
|
project["ffmpeg_pass2_template"] = project.get("ffmpeg_pass2_template") or DEFAULT_FFMPEG_PASS2_TEMPLATE
|
||||||
return project
|
return project
|
||||||
@@ -126,6 +153,8 @@ def create_project(
|
|||||||
intro_seconds: float,
|
intro_seconds: float,
|
||||||
outro_seconds: float,
|
outro_seconds: float,
|
||||||
reencode_enabled: bool = False,
|
reencode_enabled: bool = False,
|
||||||
|
encoding_passes: int = 1,
|
||||||
|
target_os: str = "windows",
|
||||||
ffmpeg_pass1_template: str | None = None,
|
ffmpeg_pass1_template: str | None = None,
|
||||||
ffmpeg_pass2_template: str | None = None,
|
ffmpeg_pass2_template: str | None = None,
|
||||||
) -> dict:
|
) -> dict:
|
||||||
@@ -138,10 +167,11 @@ def create_project(
|
|||||||
"""
|
"""
|
||||||
INSERT INTO projects (
|
INSERT INTO projects (
|
||||||
id, name, segments_count, intro_seconds, outro_seconds,
|
id, name, segments_count, intro_seconds, outro_seconds,
|
||||||
reencode_enabled, ffmpeg_pass1_template, ffmpeg_pass2_template,
|
reencode_enabled, encoding_passes, target_os,
|
||||||
|
ffmpeg_pass1_template, ffmpeg_pass2_template,
|
||||||
created_at, updated_at
|
created_at, updated_at
|
||||||
)
|
)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
""",
|
""",
|
||||||
(
|
(
|
||||||
project_id,
|
project_id,
|
||||||
@@ -150,6 +180,8 @@ def create_project(
|
|||||||
intro_seconds,
|
intro_seconds,
|
||||||
outro_seconds,
|
outro_seconds,
|
||||||
1 if reencode_enabled else 0,
|
1 if reencode_enabled else 0,
|
||||||
|
encoding_passes,
|
||||||
|
target_os,
|
||||||
ffmpeg_pass1_template or DEFAULT_FFMPEG_PASS1_TEMPLATE,
|
ffmpeg_pass1_template or DEFAULT_FFMPEG_PASS1_TEMPLATE,
|
||||||
ffmpeg_pass2_template or DEFAULT_FFMPEG_PASS2_TEMPLATE,
|
ffmpeg_pass2_template or DEFAULT_FFMPEG_PASS2_TEMPLATE,
|
||||||
now,
|
now,
|
||||||
@@ -199,6 +231,8 @@ def update_current_project(
|
|||||||
intro_seconds: float | None = None,
|
intro_seconds: float | None = None,
|
||||||
outro_seconds: float | None = None,
|
outro_seconds: float | None = None,
|
||||||
reencode_enabled: bool | None = None,
|
reencode_enabled: bool | None = None,
|
||||||
|
encoding_passes: int | None = None,
|
||||||
|
target_os: str | None = None,
|
||||||
ffmpeg_pass1_template: str | None = None,
|
ffmpeg_pass1_template: str | None = None,
|
||||||
ffmpeg_pass2_template: str | None = None,
|
ffmpeg_pass2_template: str | None = None,
|
||||||
) -> dict | None:
|
) -> dict | None:
|
||||||
@@ -222,6 +256,12 @@ def update_current_project(
|
|||||||
if reencode_enabled is not None:
|
if reencode_enabled is not None:
|
||||||
fields.append("reencode_enabled = ?")
|
fields.append("reencode_enabled = ?")
|
||||||
params.append(1 if reencode_enabled else 0)
|
params.append(1 if reencode_enabled else 0)
|
||||||
|
if encoding_passes is not None:
|
||||||
|
fields.append("encoding_passes = ?")
|
||||||
|
params.append(encoding_passes)
|
||||||
|
if target_os is not None:
|
||||||
|
fields.append("target_os = ?")
|
||||||
|
params.append(target_os)
|
||||||
if ffmpeg_pass1_template is not None:
|
if ffmpeg_pass1_template is not None:
|
||||||
fields.append("ffmpeg_pass1_template = ?")
|
fields.append("ffmpeg_pass1_template = ?")
|
||||||
params.append(ffmpeg_pass1_template or DEFAULT_FFMPEG_PASS1_TEMPLATE)
|
params.append(ffmpeg_pass1_template or DEFAULT_FFMPEG_PASS1_TEMPLATE)
|
||||||
@@ -252,8 +292,8 @@ def create_video(project_id: str, filename: str, file_path: str, duration_second
|
|||||||
cur = conn.cursor()
|
cur = conn.cursor()
|
||||||
cur.execute(
|
cur.execute(
|
||||||
"""
|
"""
|
||||||
INSERT INTO videos (id, project_id, filename, file_path, duration_seconds, created_at)
|
INSERT INTO videos (id, project_id, filename, file_path, duration_seconds, is_exported, created_at)
|
||||||
VALUES (?, ?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, 0, ?)
|
||||||
""",
|
""",
|
||||||
(video_id, project_id, filename, file_path, duration_seconds, now),
|
(video_id, project_id, filename, file_path, duration_seconds, now),
|
||||||
)
|
)
|
||||||
@@ -342,9 +382,9 @@ def replace_segment_edits(video_id: str, segments: list[dict]) -> list[dict]:
|
|||||||
cur.execute(
|
cur.execute(
|
||||||
"""
|
"""
|
||||||
INSERT INTO segment_edits (
|
INSERT INTO segment_edits (
|
||||||
id, video_id, segment_key, start_seconds, end_seconds, modified_at
|
id, video_id, segment_key, start_seconds, end_seconds, color, modified_at
|
||||||
)
|
)
|
||||||
VALUES (?, ?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||||
""",
|
""",
|
||||||
(
|
(
|
||||||
uuid.uuid4().hex,
|
uuid.uuid4().hex,
|
||||||
@@ -352,13 +392,14 @@ def replace_segment_edits(video_id: str, segments: list[dict]) -> list[dict]:
|
|||||||
segment["segment_key"],
|
segment["segment_key"],
|
||||||
float(segment["start_seconds"]),
|
float(segment["start_seconds"]),
|
||||||
float(segment["end_seconds"]),
|
float(segment["end_seconds"]),
|
||||||
|
segment.get("color"),
|
||||||
now,
|
now,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
conn.commit()
|
conn.commit()
|
||||||
cur.execute(
|
cur.execute(
|
||||||
"""
|
"""
|
||||||
SELECT segment_key, start_seconds, end_seconds, modified_at
|
SELECT segment_key, start_seconds, end_seconds, color, modified_at
|
||||||
FROM segment_edits
|
FROM segment_edits
|
||||||
WHERE video_id = ?
|
WHERE video_id = ?
|
||||||
ORDER BY rowid
|
ORDER BY rowid
|
||||||
|
|||||||
+21
-4
@@ -8,7 +8,7 @@ from pathlib import Path
|
|||||||
|
|
||||||
from fastapi import Body, FastAPI, File, HTTPException, Query, UploadFile
|
from fastapi import Body, FastAPI, File, HTTPException, Query, UploadFile
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
from fastapi.responses import StreamingResponse
|
from fastapi.responses import FileResponse, StreamingResponse
|
||||||
|
|
||||||
from . import db, jobs, media, mkv, schemas
|
from . import db, jobs, media, mkv, schemas
|
||||||
from .settings import APP_NAME, JOBS_DIR, PROJECTS_DIR, UPLOADS_DIR, BLACKDETECT_WINDOW
|
from .settings import APP_NAME, JOBS_DIR, PROJECTS_DIR, UPLOADS_DIR, BLACKDETECT_WINDOW
|
||||||
@@ -340,7 +340,8 @@ def split_video(
|
|||||||
log_cb=lambda message: job_manager.log(job_id, message),
|
log_cb=lambda message: job_manager.log(job_id, message),
|
||||||
custom_segments=custom_segments,
|
custom_segments=custom_segments,
|
||||||
)
|
)
|
||||||
return {"outputs": outputs, "output_dir": str(output_dir)}
|
db.mark_video_exported(video_id)
|
||||||
|
return {"outputs": [Path(o).name for o in outputs], "output_dir": str(output_dir)}
|
||||||
|
|
||||||
job_manager.run_in_thread(job_id, run_job)
|
job_manager.run_in_thread(job_id, run_job)
|
||||||
return job_manager.get_job(job_id)
|
return job_manager.get_job(job_id)
|
||||||
@@ -386,7 +387,7 @@ def split_all_videos(payload: schemas.SplitAllRequest | None = Body(default=None
|
|||||||
project_dir = _project_dir(project["id"])
|
project_dir = _project_dir(project["id"])
|
||||||
all_outputs: list[str] = []
|
all_outputs: list[str] = []
|
||||||
total_videos = len(ready)
|
total_videos = len(ready)
|
||||||
for video_index, (video, markers) in enumerate(ready, start=1):
|
for video_index, (video, markers, custom_segments) in enumerate(ready, start=1):
|
||||||
label = f"{video_index}/{total_videos} {Path(video['filename']).stem}"
|
label = f"{video_index}/{total_videos} {Path(video['filename']).stem}"
|
||||||
output_dir = project_dir / "outputs" / video["id"]
|
output_dir = project_dir / "outputs" / video["id"]
|
||||||
temp_dir = JOBS_DIR / job_id / video["id"]
|
temp_dir = JOBS_DIR / job_id / video["id"]
|
||||||
@@ -412,8 +413,10 @@ def split_all_videos(payload: schemas.SplitAllRequest | None = Body(default=None
|
|||||||
span=1.0 / float(total_videos),
|
span=1.0 / float(total_videos),
|
||||||
),
|
),
|
||||||
log_cb=lambda message: job_manager.log(job_id, message),
|
log_cb=lambda message: job_manager.log(job_id, message),
|
||||||
|
custom_segments=custom_segments,
|
||||||
)
|
)
|
||||||
all_outputs.extend(outputs)
|
db.mark_video_exported(video["id"])
|
||||||
|
all_outputs.extend([Path(o).name for o in outputs])
|
||||||
|
|
||||||
return {"outputs": all_outputs, "skipped": skipped}
|
return {"outputs": all_outputs, "skipped": skipped}
|
||||||
|
|
||||||
@@ -456,3 +459,17 @@ def get_frame(
|
|||||||
media_type="image/jpeg",
|
media_type="image/jpeg",
|
||||||
headers={"Cache-Control": "public, max-age=86400"},
|
headers={"Cache-Control": "public, max-age=86400"},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/api/videos/{video_id}/outputs/{filename}")
|
||||||
|
def download_output(video_id: str, filename: str):
|
||||||
|
project = _require_project()
|
||||||
|
project_dir = _project_dir(project["id"])
|
||||||
|
file_path = project_dir / "outputs" / video_id / filename
|
||||||
|
if not file_path.exists():
|
||||||
|
raise HTTPException(status_code=404, detail="Output file not found")
|
||||||
|
return FileResponse(
|
||||||
|
path=file_path,
|
||||||
|
filename=filename,
|
||||||
|
media_type="video/x-matroska",
|
||||||
|
)
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ class ProjectCreate(BaseModel):
|
|||||||
intro_seconds: float = Field(ge=0)
|
intro_seconds: float = Field(ge=0)
|
||||||
outro_seconds: float = Field(ge=0)
|
outro_seconds: float = Field(ge=0)
|
||||||
reencode_enabled: bool = False
|
reencode_enabled: bool = False
|
||||||
|
encoding_passes: int = Field(default=1, ge=1, le=2)
|
||||||
|
target_os: str = Field(default="windows")
|
||||||
ffmpeg_pass1_template: str | None = None
|
ffmpeg_pass1_template: str | None = None
|
||||||
ffmpeg_pass2_template: str | None = None
|
ffmpeg_pass2_template: str | None = None
|
||||||
|
|
||||||
@@ -17,6 +19,8 @@ class ProjectUpdate(BaseModel):
|
|||||||
intro_seconds: float | None = Field(default=None, ge=0)
|
intro_seconds: float | None = Field(default=None, ge=0)
|
||||||
outro_seconds: float | None = Field(default=None, ge=0)
|
outro_seconds: float | None = Field(default=None, ge=0)
|
||||||
reencode_enabled: bool | None = None
|
reencode_enabled: bool | None = None
|
||||||
|
encoding_passes: int | None = Field(default=None, ge=1, le=2)
|
||||||
|
target_os: str | None = None
|
||||||
ffmpeg_pass1_template: str | None = None
|
ffmpeg_pass1_template: str | None = None
|
||||||
ffmpeg_pass2_template: str | None = None
|
ffmpeg_pass2_template: str | None = None
|
||||||
|
|
||||||
@@ -28,6 +32,8 @@ class ProjectOut(BaseModel):
|
|||||||
intro_seconds: float
|
intro_seconds: float
|
||||||
outro_seconds: float
|
outro_seconds: float
|
||||||
reencode_enabled: bool
|
reencode_enabled: bool
|
||||||
|
encoding_passes: int
|
||||||
|
target_os: str
|
||||||
ffmpeg_pass1_template: str | None = None
|
ffmpeg_pass1_template: str | None = None
|
||||||
ffmpeg_pass2_template: str | None = None
|
ffmpeg_pass2_template: str | None = None
|
||||||
created_at: str
|
created_at: str
|
||||||
@@ -40,6 +46,7 @@ class VideoOut(BaseModel):
|
|||||||
filename: str
|
filename: str
|
||||||
file_path: str
|
file_path: str
|
||||||
duration_seconds: float
|
duration_seconds: float
|
||||||
|
is_exported: bool = False
|
||||||
created_at: str
|
created_at: str
|
||||||
|
|
||||||
|
|
||||||
@@ -51,6 +58,7 @@ class SegmentEdit(BaseModel):
|
|||||||
segment_key: str
|
segment_key: str
|
||||||
start_seconds: float = Field(ge=0)
|
start_seconds: float = Field(ge=0)
|
||||||
end_seconds: float = Field(ge=0)
|
end_seconds: float = Field(ge=0)
|
||||||
|
color: str | None = None
|
||||||
|
|
||||||
|
|
||||||
class SegmentEditsUpdate(BaseModel):
|
class SegmentEditsUpdate(BaseModel):
|
||||||
|
|||||||
+401
-1462
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+5
-1
@@ -55,6 +55,8 @@ export async function createProject(payload: {
|
|||||||
intro_seconds: number;
|
intro_seconds: number;
|
||||||
outro_seconds: number;
|
outro_seconds: number;
|
||||||
reencode_enabled?: boolean;
|
reencode_enabled?: boolean;
|
||||||
|
encoding_passes?: number;
|
||||||
|
target_os?: string;
|
||||||
ffmpeg_pass1_template?: string | null;
|
ffmpeg_pass1_template?: string | null;
|
||||||
ffmpeg_pass2_template?: string | null;
|
ffmpeg_pass2_template?: string | null;
|
||||||
}): Promise<Project> {
|
}): Promise<Project> {
|
||||||
@@ -70,6 +72,8 @@ export async function updateProject(payload: {
|
|||||||
intro_seconds?: number;
|
intro_seconds?: number;
|
||||||
outro_seconds?: number;
|
outro_seconds?: number;
|
||||||
reencode_enabled?: boolean;
|
reencode_enabled?: boolean;
|
||||||
|
encoding_passes?: number;
|
||||||
|
target_os?: string;
|
||||||
ffmpeg_pass1_template?: string | null;
|
ffmpeg_pass1_template?: string | null;
|
||||||
ffmpeg_pass2_template?: string | null;
|
ffmpeg_pass2_template?: string | null;
|
||||||
}): Promise<Project> {
|
}): Promise<Project> {
|
||||||
@@ -174,7 +178,7 @@ export async function listSegmentEdits(videoId: string): Promise<{ segments: Seg
|
|||||||
|
|
||||||
export async function replaceSegmentEdits(
|
export async function replaceSegmentEdits(
|
||||||
videoId: string,
|
videoId: string,
|
||||||
segments: { segment_key: string; start_seconds: number; end_seconds: number }[]
|
segments: { segment_key: string; start_seconds: number; end_seconds: number; color?: string | null }[]
|
||||||
): Promise<{ segments: SegmentEdit[] }> {
|
): Promise<{ segments: SegmentEdit[] }> {
|
||||||
return request(`/api/videos/${videoId}/segment-edits`, {
|
return request(`/api/videos/${videoId}/segment-edits`, {
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
|
|||||||
+37
-4
@@ -1,5 +1,17 @@
|
|||||||
import { createTheme } from "@mui/material/styles";
|
import { createTheme } from "@mui/material/styles";
|
||||||
|
|
||||||
|
declare module "@mui/material/Button" {
|
||||||
|
interface ButtonPropsVariantOverrides {
|
||||||
|
tonal: true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module "@mui/material/Chip" {
|
||||||
|
interface ChipPropsVariantOverrides {
|
||||||
|
tonal: true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export type ColorMode = "light" | "dark";
|
export type ColorMode = "light" | "dark";
|
||||||
|
|
||||||
export function buildTheme(mode: ColorMode) {
|
export function buildTheme(mode: ColorMode) {
|
||||||
@@ -30,7 +42,7 @@ export function buildTheme(mode: ColorMode) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
shape: {
|
shape: {
|
||||||
borderRadius: 16, // Softer, more fluid shapes
|
borderRadius: 12, // More balanced Material Design 3 curve
|
||||||
},
|
},
|
||||||
typography: {
|
typography: {
|
||||||
fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif',
|
fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif',
|
||||||
@@ -49,6 +61,18 @@ export function buildTheme(mode: ColorMode) {
|
|||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
MuiButton: {
|
MuiButton: {
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
props: { variant: "tonal" },
|
||||||
|
style: {
|
||||||
|
backgroundColor: mode === "dark" ? "#4A4458" : "#E8DEF8",
|
||||||
|
color: mode === "dark" ? "#E8DEF8" : "#1D192B",
|
||||||
|
"&:hover": {
|
||||||
|
backgroundColor: mode === "dark" ? "#625B71" : "#D0BCFF",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
] as any,
|
||||||
styleOverrides: {
|
styleOverrides: {
|
||||||
root: {
|
root: {
|
||||||
borderRadius: 100, // Full pill shape typical of M3 Expressive
|
borderRadius: 100, // Full pill shape typical of M3 Expressive
|
||||||
@@ -69,7 +93,7 @@ export function buildTheme(mode: ColorMode) {
|
|||||||
MuiCard: {
|
MuiCard: {
|
||||||
styleOverrides: {
|
styleOverrides: {
|
||||||
root: {
|
root: {
|
||||||
borderRadius: 24, // Playful, edge-hugging containers
|
borderRadius: 16, // Refined container rounding
|
||||||
boxShadow: "none", // Flatter hierarchy using surface tones
|
boxShadow: "none", // Flatter hierarchy using surface tones
|
||||||
backgroundColor: mode === "dark" ? "#2B2930" : "#F3EDF7",
|
backgroundColor: mode === "dark" ? "#2B2930" : "#F3EDF7",
|
||||||
backgroundImage: "none",
|
backgroundImage: "none",
|
||||||
@@ -92,7 +116,7 @@ export function buildTheme(mode: ColorMode) {
|
|||||||
MuiPaper: {
|
MuiPaper: {
|
||||||
styleOverrides: {
|
styleOverrides: {
|
||||||
rounded: {
|
rounded: {
|
||||||
borderRadius: 24,
|
borderRadius: 16,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -100,12 +124,21 @@ export function buildTheme(mode: ColorMode) {
|
|||||||
styleOverrides: {
|
styleOverrides: {
|
||||||
root: {
|
root: {
|
||||||
"& .MuiOutlinedInput-root": {
|
"& .MuiOutlinedInput-root": {
|
||||||
borderRadius: 12, // Softer input fields
|
borderRadius: 8, // More compact input fields
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
MuiChip: {
|
MuiChip: {
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
props: { variant: "tonal" },
|
||||||
|
style: {
|
||||||
|
backgroundColor: mode === "dark" ? "#4A4458" : "#E8DEF8",
|
||||||
|
color: mode === "dark" ? "#E8DEF8" : "#1D192B",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
] as any,
|
||||||
styleOverrides: {
|
styleOverrides: {
|
||||||
root: {
|
root: {
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ export type Project = {
|
|||||||
intro_seconds: number;
|
intro_seconds: number;
|
||||||
outro_seconds: number;
|
outro_seconds: number;
|
||||||
reencode_enabled: boolean;
|
reencode_enabled: boolean;
|
||||||
|
encoding_passes: number;
|
||||||
|
target_os: "windows" | "linux";
|
||||||
ffmpeg_pass1_template: string | null;
|
ffmpeg_pass1_template: string | null;
|
||||||
ffmpeg_pass2_template: string | null;
|
ffmpeg_pass2_template: string | null;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
@@ -17,6 +19,7 @@ export type Video = {
|
|||||||
filename: string;
|
filename: string;
|
||||||
file_path: string;
|
file_path: string;
|
||||||
duration_seconds: number;
|
duration_seconds: number;
|
||||||
|
is_exported: boolean;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -43,5 +46,6 @@ export type SegmentEdit = {
|
|||||||
segment_key: string;
|
segment_key: string;
|
||||||
start_seconds: number;
|
start_seconds: number;
|
||||||
end_seconds: number;
|
end_seconds: number;
|
||||||
|
color: string | null;
|
||||||
modified_at: string;
|
modified_at: string;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user