Skip to content
This repository was archived by the owner on Jan 30, 2026. It is now read-only.

Commit cb0107c

Browse files
committed
feat: convert video
1 parent b873c46 commit cb0107c

File tree

5 files changed

+567
-4
lines changed

5 files changed

+567
-4
lines changed

src/components/CreateContent/Section/_FooterArea.tsx

Lines changed: 147 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ export default function FooterArea({
7373
setFilesBeingCompressed
7474
// charCountArticle
7575
}: FooterAreaProps) {
76-
const { addAlert, removeAlert, updateAlert, updateAlertCancelState, removeAllCompressionAlerts } = useAlertContext();
76+
const { addAlert, removeAlert, updateAlert, updateAlertCancelState } = useAlertContext();
7777
const { openModal } = useModal();
7878
const [addTagInput, setAddTagInput] = useState<boolean>(false);
7979
const [tagInput, setTagInput] = useState('');
@@ -147,8 +147,152 @@ export default function FooterArea({
147147
isPDF;
148148

149149
if (!isValidType) {
150-
addAlert('File type not supported.', 'warning');
151-
continue;
150+
// Try to convert unsupported video types to MP4
151+
if (isVideo && !Utils.supportedVideoTypes.includes(file.type)) {
152+
let loadingAlertId: number | undefined;
153+
let abortController: AbortController | undefined;
154+
try {
155+
abortController = new AbortController();
156+
157+
loadingAlertId = addAlert(
158+
`Converting video ${i + 1}/${filesArray.length} to MP4... 0%`,
159+
'loading',
160+
() => {
161+
// Cancel conversion when cancel button is clicked
162+
abortController?.abort();
163+
},
164+
true // Start with cancel disabled
165+
);
166+
setIsCompressing(true);
167+
setFilesBeingCompressed && setFilesBeingCompressed((prev) => prev + 1);
168+
169+
const convertedFile = await Utils.convertVideo(
170+
file,
171+
(progress) => {
172+
// Update the alert with current progress
173+
if (loadingAlertId) {
174+
updateAlert(
175+
loadingAlertId,
176+
`Converting video ${i + 1}/${filesArray.length} to MP4... ${progress}%`
177+
);
178+
}
179+
180+
// Enable cancel button on first progress
181+
if (progress > 0) {
182+
updateAlertCancelState(loadingAlertId!, false);
183+
}
184+
},
185+
abortController.signal
186+
);
187+
188+
removeAlert(loadingAlertId);
189+
190+
// After conversion, check if the converted file needs compression
191+
if (convertedFile.size > maxOtherSizeInBytes) {
192+
// Check if video is too large for compression (100MB limit)
193+
if (convertedFile.size > maxVideoSizeForCompressionInBytes) {
194+
addAlert('The maximum allowed size for videos compression is 100 MB', 'warning');
195+
continue;
196+
}
197+
198+
// Video needs compression (between 20MB and 100MB)
199+
let compressionLoadingAlertId: number | undefined;
200+
let abortController: AbortController | undefined;
201+
try {
202+
abortController = new AbortController();
203+
204+
compressionLoadingAlertId = addAlert(
205+
`Compressing converted video ${i + 1}/${filesArray.length}...`,
206+
'loading',
207+
() => {
208+
// Cancel compression when cancel button is clicked
209+
abortController?.abort();
210+
},
211+
true // Start with cancel disabled
212+
);
213+
setIsCompressing(true);
214+
setFilesBeingCompressed && setFilesBeingCompressed((prev) => prev + 1);
215+
216+
const resizedFile = await Utils.resizeVideoFile(
217+
convertedFile,
218+
maxOtherSizeInBytes,
219+
(progress) => {
220+
// Update the alert with current progress
221+
updateAlert(
222+
compressionLoadingAlertId!,
223+
`Compressing converted video ${i + 1}/${filesArray.length}... ${progress}%`
224+
);
225+
226+
// Enable cancel button on first progress
227+
if (progress > 0) {
228+
updateAlertCancelState(compressionLoadingAlertId!, false);
229+
}
230+
},
231+
abortController.signal
232+
);
233+
234+
removeAlert(compressionLoadingAlertId);
235+
validFiles.push(resizedFile);
236+
} catch (compressionError) {
237+
// Remove the loading alert first
238+
if (compressionLoadingAlertId) {
239+
removeAlert(compressionLoadingAlertId);
240+
}
241+
242+
// Check if it was cancelled
243+
if (
244+
compressionError instanceof Error &&
245+
compressionError.message.includes('Compression cancelled')
246+
) {
247+
// Don't show error for cancellation
248+
continue;
249+
}
250+
251+
// Show the error message from compression
252+
const errorMessage =
253+
compressionError instanceof Error ? compressionError.message : String(compressionError);
254+
addAlert(errorMessage, 'warning');
255+
continue;
256+
} finally {
257+
setFilesBeingCompressed && setFilesBeingCompressed((prev) => Math.max(0, prev - 1));
258+
// Only set compressing to false when all files are processed
259+
if (validFiles.length === files.length) {
260+
setIsCompressing(false);
261+
}
262+
}
263+
} else {
264+
// No compression needed, add the converted file directly
265+
validFiles.push(convertedFile);
266+
}
267+
268+
continue;
269+
} catch (error) {
270+
// Remove the loading alert first
271+
if (loadingAlertId) {
272+
removeAlert(loadingAlertId);
273+
}
274+
275+
// Check if it was cancelled
276+
if (error instanceof Error && error.message.includes('Conversion cancelled')) {
277+
// Don't show error for cancellation
278+
continue;
279+
}
280+
281+
// Show error and continue with next file
282+
addAlert('Video conversion failed. File type not supported.', 'warning');
283+
continue;
284+
} finally {
285+
setFilesBeingCompressed && setFilesBeingCompressed((prev) => Math.max(0, prev - 1));
286+
if (validFiles.length === files.length) {
287+
setIsCompressing(false);
288+
}
289+
}
290+
}
291+
292+
if (!isVideo) {
293+
addAlert('File type not supported.', 'warning');
294+
continue;
295+
}
152296
}
153297

154298
if (isImage && file.size > maxImageSizeInBytes) {

src/components/CreateContent/Section/_InputArea.tsx

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,146 @@ export default function InputArea({
131131
isPDF;
132132

133133
if (!isValidType) {
134+
// Try to convert unsupported video types to MP4
135+
if (isVideo && !Utils.supportedVideoTypes.includes(file.type)) {
136+
let loadingAlertId: number | undefined;
137+
let abortController: AbortController | undefined;
138+
try {
139+
abortController = new AbortController();
140+
141+
loadingAlertId = addAlert(
142+
`Converting video ${i + 1}/${filesArray.length} to MP4... 0%`,
143+
'loading',
144+
() => {
145+
// Cancel conversion when cancel button is clicked
146+
abortController?.abort();
147+
},
148+
true // Start with cancel disabled
149+
);
150+
setIsCompressing(true);
151+
setFilesBeingCompressed && setFilesBeingCompressed((prev) => prev + 1);
152+
153+
const convertedFile = await Utils.convertVideo(
154+
file,
155+
(progress) => {
156+
// Update the alert with current progress
157+
if (loadingAlertId) {
158+
updateAlert(
159+
loadingAlertId,
160+
`Converting video ${i + 1}/${filesArray.length} to MP4... ${progress}%`
161+
);
162+
}
163+
164+
// Enable cancel button on first progress
165+
if (progress > 0) {
166+
updateAlertCancelState(loadingAlertId!, false);
167+
}
168+
},
169+
abortController.signal
170+
);
171+
172+
removeAlert(loadingAlertId);
173+
174+
// After conversion, check if the converted file needs compression
175+
if (convertedFile.size > maxOtherSizeInBytes) {
176+
// Check if video is too large for compression (100MB limit)
177+
if (convertedFile.size > maxVideoSizeForCompressionInBytes) {
178+
addAlert('The maximum allowed size for videos compression is 100 MB', 'warning');
179+
continue;
180+
}
181+
182+
// Video needs compression (between 20MB and 100MB)
183+
let compressionLoadingAlertId: number | undefined;
184+
let abortController: AbortController | undefined;
185+
try {
186+
abortController = new AbortController();
187+
188+
compressionLoadingAlertId = addAlert(
189+
`Compressing converted video ${i + 1}/${filesArray.length}...`,
190+
'loading',
191+
() => {
192+
// Cancel compression when cancel button is clicked
193+
abortController?.abort();
194+
},
195+
true // Start with cancel disabled
196+
);
197+
setIsCompressing(true);
198+
setFilesBeingCompressed && setFilesBeingCompressed((prev) => prev + 1);
199+
200+
const resizedFile = await Utils.resizeVideoFile(
201+
convertedFile,
202+
maxOtherSizeInBytes,
203+
(progress) => {
204+
// Update the alert with current progress
205+
updateAlert(
206+
compressionLoadingAlertId!,
207+
`Compressing converted video ${i + 1}/${filesArray.length}... ${progress}%`
208+
);
209+
210+
// Enable cancel button on first progress
211+
if (progress > 0) {
212+
updateAlertCancelState(compressionLoadingAlertId!, false);
213+
}
214+
},
215+
abortController.signal
216+
);
217+
218+
removeAlert(compressionLoadingAlertId);
219+
validFiles.push(resizedFile);
220+
} catch (compressionError) {
221+
// Remove the loading alert first
222+
if (compressionLoadingAlertId) {
223+
removeAlert(compressionLoadingAlertId);
224+
}
225+
226+
// Check if it was cancelled
227+
if (compressionError instanceof Error && compressionError.message.includes('Compression cancelled')) {
228+
// Don't show error for cancellation
229+
continue;
230+
}
231+
232+
// Show the error message from compression
233+
const errorMessage = compressionError instanceof Error ? compressionError.message : String(compressionError);
234+
addAlert(errorMessage, 'warning');
235+
continue;
236+
} finally {
237+
setFilesBeingCompressed && setFilesBeingCompressed((prev) => Math.max(0, prev - 1));
238+
// Only set compressing to false when all files are processed
239+
if (validFiles.length === files.length) {
240+
setIsCompressing(false);
241+
}
242+
}
243+
} else {
244+
// No compression needed, add the converted file directly
245+
validFiles.push(convertedFile);
246+
}
247+
248+
continue;
249+
} catch (error) {
250+
// Remove the loading alert first
251+
if (loadingAlertId) {
252+
removeAlert(loadingAlertId);
253+
}
254+
255+
// Check if it was cancelled
256+
if (error instanceof Error && error.message.includes('Conversion cancelled')) {
257+
// Don't show error for cancellation
258+
continue;
259+
}
260+
261+
// Show error and continue with next file
262+
addAlert('Video conversion failed. File type not supported.', 'warning');
263+
continue;
264+
} finally {
265+
setFilesBeingCompressed && setFilesBeingCompressed((prev) => Math.max(0, prev - 1));
266+
// Only set compressing to false when all files are processed
267+
if (validFiles.length === files.length) {
268+
setIsCompressing(false);
269+
}
270+
}
271+
}
272+
273+
// If not video, show error
134274
addAlert('File type not supported.', 'warning');
135275
continue;
136276
}

0 commit comments

Comments
 (0)