
diff --git a/frontend/src/pages/ClipEdit.tsx b/frontend/src/pages/ClipEdit.tsx
index 21bd7c8..3ce8c99 100644
--- a/frontend/src/pages/ClipEdit.tsx
+++ b/frontend/src/pages/ClipEdit.tsx
@@ -5,7 +5,7 @@ import PlaybackSlider from "./../components/video/PlaybackSlider";
import ClipRangeSlider from "./../components/video/ClipRangeSlider";
import ConfigBox from "../components/video/ConfigBox.tsx";
import ExportWidget from "../components/video/ExportWidget.tsx";
-import { editFile, processFile, getProgress, uploadFile } from "../utils/endpoints"
+import { compress, getJob } from "../utils/endpoints"
import type { VideoMetadata } from "../utils/types.ts";
import Box from "../components/Box.tsx";
import MetadataBox from "../components/video/MetadataBox.tsx";
@@ -61,33 +61,29 @@ const ClipEdit = () => {
const sendData = async () => {
if (!localFile) return;
- let fileId: string;
+ let jobId: string;
try {
- fileId = await uploadFile(localFile);
- setUploadedId(fileId);
+ jobId = await compress(localFile, outputMetadata);
+ setUploadedId(jobId);
} catch (err: unknown) {
- setError(`Failed to upload file: ${err instanceof Error ? err.message : 'Unknown error'}`);
+ setError(`Failed to start compression: ${err instanceof Error ? err.message : 'Unknown error'}`);
return;
}
- editFile(fileId, outputMetadata)
- .then(() => processFile(fileId)
- .catch((err: Error) => setError(`Failed to process file: ${err.message}`)))
- .catch((err: Error) => setError(`Failed to edit file: ${err.message}`));
-
- const interval = setInterval(async () => await pollProgress(fileId, interval), 500);
+ const interval = setInterval(async () => await pollProgress(jobId, interval), 500);
};
- const pollProgress = async (id: string, intervalId: number) => {
- getProgress(id)
- .then((progress) => {
- setProgress(progress.process.progress);
+ const pollProgress = async (jobId: string, intervalId: number) => {
+ getJob(jobId)
+ .then((job) => {
+ setProgress(job.progress);
- if (progress.process.complete) {
+ if (job.state === 'FAILED') {
+ setError(`Compression failed: ${job.errorOutput ?? 'Unknown error'}`);
+ clearInterval(intervalId);
+ } else if (job.isComplete) {
clearInterval(intervalId);
setDownloadable(true);
- } else {
- setDownloadable(false);
}
})
.catch((err: Error) => {
@@ -99,7 +95,7 @@ const ClipEdit = () => {
const handleDownload = async () => {
if (!uploadedId) return;
- const response = await fetch(API_URL + `/api/v1/download/output/${uploadedId}`);
+ const response = await fetch(API_URL + `/api/v1/jobs/${uploadedId}/download`, { credentials: 'include' });
if (!response.ok) {
console.error('Download failed');
diff --git a/frontend/src/utils/endpoints.ts b/frontend/src/utils/endpoints.ts
index 590a323..b711d07 100644
--- a/frontend/src/utils/endpoints.ts
+++ b/frontend/src/utils/endpoints.ts
@@ -1,4 +1,4 @@
-import type {VideoMetadata, APIResponse, User, Clip, ProgressResult } from "./types.ts";
+import type {VideoMetadata, APIResponse, User, Clip, JobResponse } from "./types.ts";
const API_URL = import.meta.env.VITE_API_URL;
@@ -12,7 +12,7 @@ export class AuthError extends Error {
* @return A promise that resolves to a JWT
*/
const login = async (GoogleToken: string): Promise
=> {
- const response = await fetch(API_URL + '/api/v1/auth/login', {
+ const response = await fetch(API_URL + '/api/v1/users/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
@@ -35,7 +35,7 @@ const login = async (GoogleToken: string): Promise => {
}
const logout = async () => {
- const response = await fetch(API_URL + '/api/v1/auth/logout', {
+ const response = await fetch(API_URL + '/api/v1/users/logout', {
method: 'POST',
credentials: 'include'
});
@@ -52,132 +52,57 @@ const logout = async () => {
}
/**
- * Uploads a file to the backend.
- * @param file - The file to upload.
+ * Uploads a file and starts compression in one step.
+ * @param file - The file to compress.
+ * @param options - The clip/compression options.
+ * @returns The job UUID to poll for progress.
*/
-const uploadFile = async (file: File): Promise => {
+const compress = async (file: File, options: VideoMetadata): Promise => {
const formData = new FormData();
formData.append('file', file);
+ formData.append('title', options.title);
+ if (options.description) formData.append('description', options.description);
+ formData.append('startPoint', options.startPoint.toString());
+ formData.append('duration', options.duration.toString());
+ if (options.fps) formData.append('fps', options.fps.toString());
+ if (options.width) formData.append('width', options.width.toString());
+ if (options.height) formData.append('height', options.height.toString());
+ if (options.fileSize) formData.append('fileSize', options.fileSize.toString());
- const response = await fetch(API_URL + '/api/v1/upload', {
+ const response = await fetch(API_URL + '/api/v1/media/compress', {
method: 'POST',
body: formData,
+ credentials: 'include',
});
+ if (!response.ok) {
+ throw new Error(`Compression failed: ${response.status}`);
+ }
+
const result: APIResponse = await response.json();
- if (result.status == "error") {
- throw new Error(`Failed to upload file: ${result.message}`);
+ if (result.status === 'error') {
+ throw new Error(`Compression failed: ${result.message}`);
}
return result.data.uuid;
};
/**
- * Submits metadata changes to the backend.
- * @param uuid - The UUID of the video file to edit.
- * @param videoMetadata - The metadata to update.
+ * Fetches the status of a background job.
+ * @param uuid - The job UUID.
*/
-const editFile = async (uuid: string, videoMetadata: VideoMetadata) => {
- const formData = new URLSearchParams();
-
- for (const [key, value] of Object.entries(videoMetadata)) {
- if (value !== undefined && value !== null) {
- formData.append(key, value.toString());
- }
- }
-
- const response = await fetch(API_URL + `/api/v1/edit/${uuid}`, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/x-www-form-urlencoded',
- },
- body: formData.toString(),
- });
+const getJob = async (uuid: string): Promise => {
+ const response = await fetch(API_URL + `/api/v1/jobs/${uuid}`, { credentials: 'include' });
if (!response.ok) {
- throw new Error(`Failed to edit file: ${response.status}`);
+ throw new Error(`Failed to fetch job: ${response.status}`);
}
const result: APIResponse = await response.json();
- if (result.status === "error") {
- throw new Error(`Failed to edit file: ${result.message}`);
- }
-
-};
-
-/**
- * Triggers file processing.
- * @param uuid - The UUID of the video file to process.
- */
-const processFile = async (uuid: string) => {
- const response = await fetch(API_URL + `/api/v1/process/${uuid}`, {credentials: "include"});
-
- if (!response.ok) {
- throw new Error(`Failed to process file: ${response.status}`);
- }
-
- const result: APIResponse = await response.json();
-
- if (result.status === "error") {
- throw new Error("Failed to process file: " + result.message);
- }
-};
-
-const convertFile = async (uuid: string) => {
- const response = await fetch(API_URL + `/api/v1/convert/${uuid}`);
-
- if (!response.ok) {
- throw new Error(`Failed to convert file: ${response.status}`);
- }
-
- const result: APIResponse = await response.json();
-
- if (result.status === "error") {
- throw new Error("Failed to convert file: " + result.message);
- }
-};
-
-/**
- * Fetches the processing progress percentage.
- * @param uuid - The UUID of the video file.
- */
-const getProgress = async (uuid: string): Promise => {
- const response = await fetch(API_URL + `/api/v1/progress/${uuid}`);
-
- if (!response.ok) {
- throw new Error(`Failed to fetch progress: ${response.status}`);
- }
-
- const result = await response.json();
-
- if (result.status === "error") {
- throw new Error(`Failed to fetch progress: ${result.message}`);
- }
-
- if (!result.data) {
- throw new Error('Invalid progress data received');
- }
-
- return result.data;
-};
-
-/**
- * Fetches original metadata from the backend.
- * @param uuid - The UUID of the video file.
- */
-const getMetadata = async (uuid: string): Promise => {
- const response = await fetch(API_URL + `/api/v1/metadata/original/${uuid}`, {credentials: "include"});
-
- if (!response.ok) {
- throw new Error(`Failed to fetch metadata: ${response.status}`);
- }
-
- const result = await response.json();
-
- if (result.status === "error") {
- throw new Error(`Failed to fetch metadata: ${result.message}`);
+ if (result.status === 'error') {
+ throw new Error(`Failed to fetch job: ${result.message}`);
}
return result.data;
@@ -187,7 +112,7 @@ const getMetadata = async (uuid: string): Promise => {
* Fetches the current user information. Returns null if not authenticated.
*/
const getUser = async (): Promise => {
- const response = await fetch(API_URL + '/api/v1/auth/user', {credentials: "include"});
+ const response = await fetch(API_URL + '/api/v1/users/me', {credentials: "include"});
if (!response.ok) {
return null;
@@ -245,7 +170,7 @@ const getClipById = async (id: string): Promise => {
};
const getVideoBlob = async(id: string): Promise => {
- const response = await fetch(API_URL + `/api/v1/download/clip/${id}`, {credentials: "include",});
+ const response = await fetch(API_URL + `/api/v1/clips/${id}/media`, {credentials: "include",});
if (!response.ok) {
if (response.status === 401 || response.status === 403) throw new AuthError();
@@ -303,22 +228,16 @@ const getStreamStatus = async (): Promise => {
};
const isThumbnailAvailable = async (id: number): Promise => {
- const response = await fetch(API_URL + `/api/v1/download/thumbnail/${id}`, {credentials: "include"});
+ const response = await fetch(API_URL + `/api/v1/clips/${id}/thumbnail`, {credentials: "include"});
return response.ok;
-
-
}
export {
login,
logout,
getStreamStatus,
- uploadFile,
- editFile,
- processFile,
- convertFile,
- getProgress,
- getMetadata,
+ compress,
+ getJob,
getUser,
getClips,
getClipById,
diff --git a/frontend/src/utils/types.ts b/frontend/src/utils/types.ts
index 04cc9f6..a75eede 100644
--- a/frontend/src/utils/types.ts
+++ b/frontend/src/utils/types.ts
@@ -33,15 +33,13 @@ type Clip = {
createdAt: string,
}
-type ProgressResult = {
- process: {
- progress: number,
- complete: boolean
- };
- conversion: {
- progress: number,
- complete: boolean
- };
+type JobResponse = {
+ uuid: string,
+ progress: number,
+ isComplete: boolean,
+ state: 'READY' | 'PROCESSING' | 'SUCCEEDED' | 'FAILED',
+ errorOutput: string | null,
+ createdAt: string,
};
export type {
@@ -49,5 +47,5 @@ export type {
VideoMetadata,
User,
Clip,
- ProgressResult
+ JobResponse
}
\ No newline at end of file