Add per-task-type submission configuration with client-side movie spec checks

Coordinators can configure, per task type, accepted file extensions, a
naming convention, and (for video files) required resolution/format/
codec/frame rate. All checking happens entirely client-side in the
browser before upload - the backend only stores and validates the
configuration itself, never inspects submitted files.

- backend: submission_config_by_task_type JSON column on Project,
  schemas/submission_config.py for validation, GET/PUT
  /projects/{id}/submission-config endpoints. Added .mxf/.ma/.usd(a/c)
  to the supported file formats.
- frontend: new Submissions tab in Project Settings
  (SubmissionConfigManager.vue) to configure rules per task type.
  TaskSubmissions.vue enforces them before upload: extension/naming
  checks via regex, and real video resolution/codec/frame rate
  detection via mediainfo.js (parses container metadata directly,
  so it works for formats browsers can't play natively like MXF,
  ProRes, or DNxHD).
This commit is contained in:
2026-07-19 10:38:29 +08:00
parent 762bd34f74
commit 23740af816
13 changed files with 962 additions and 13 deletions
+26
View File
@@ -95,6 +95,22 @@ export interface ProjectSettings {
enabled_shot_tasks?: string[]
}
export interface SubmissionTypeConfig {
allowed_extensions: string[]
naming_pattern?: string | null
check_naming: boolean
required: boolean
check_movie_spec: boolean
movie_resolution?: string | null
movie_format?: string | null
movie_codec?: string | null
movie_frame_rate?: number | null
}
export interface ProjectSubmissionConfig {
config_by_task_type: Record<string, SubmissionTypeConfig>
}
export const projectService = {
async getUserProjects(): Promise<Project[]> {
const response = await apiClient.get('/projects/')
@@ -182,6 +198,16 @@ export const projectService = {
return response.data
},
async getProjectSubmissionConfig(projectId: number): Promise<ProjectSubmissionConfig> {
const response = await apiClient.get(`/projects/${projectId}/submission-config`)
return response.data
},
async updateProjectSubmissionConfig(projectId: number, config: ProjectSubmissionConfig): Promise<ProjectSubmissionConfig> {
const response = await apiClient.put(`/projects/${projectId}/submission-config`, config)
return response.data
},
async uploadThumbnail(projectId: number, file: File): Promise<{ message: string; thumbnail_url: string }> {
const formData = new FormData()
formData.append('file', file)