|
2 | 2 | * 영상 다운로드 관련 유틸리티 함수들 |
3 | 3 | */ |
4 | 4 |
|
5 | | -interface DownloadProgress { |
6 | | - loaded: number; |
7 | | - total: number; |
8 | | - percentage: number; |
9 | | -} |
10 | | - |
11 | 5 | interface DownloadOptions { |
12 | 6 | filename?: string; |
13 | | - onProgress?: (progress: DownloadProgress) => void; |
14 | 7 | onError?: (error: Error) => void; |
15 | 8 | onComplete?: () => void; |
16 | 9 | } |
17 | 10 |
|
18 | 11 | /** |
19 | 12 | * 영상 URL로부터 파일을 다운로드하는 함수 |
20 | 13 | */ |
21 | | -export async function downloadVideo( |
| 14 | +export function downloadVideo( |
22 | 15 | videoUrl: string, |
23 | 16 | options: DownloadOptions = {} |
24 | | -): Promise<void> { |
25 | | - const { |
26 | | - filename = `video_${Date.now()}.mp4`, |
27 | | - onProgress, |
28 | | - onError, |
29 | | - onComplete, |
30 | | - } = options; |
| 17 | +): void { |
| 18 | + const { filename = `video_${Date.now()}.mp4`, onError, onComplete } = options; |
31 | 19 |
|
32 | 20 | try { |
33 | | - // AbortController로 다운로드 취소 가능하게 구현 |
34 | | - const controller = new AbortController(); |
35 | | - |
36 | | - const response = await fetch(videoUrl, { |
37 | | - signal: controller.signal, |
38 | | - }); |
39 | | - |
40 | | - if (!response.ok) { |
41 | | - throw new Error(`HTTP error! status: ${response.status}`); |
42 | | - } |
43 | | - |
44 | | - const contentLength = response.headers.get("content-length"); |
45 | | - const total = contentLength ? parseInt(contentLength, 10) : 0; |
46 | | - |
47 | | - if (!response.body) { |
48 | | - throw new Error("Response body is null"); |
49 | | - } |
50 | | - |
51 | | - const reader = response.body.getReader(); |
52 | | - const chunks: Uint8Array[] = []; |
53 | | - let loaded = 0; |
54 | | - |
55 | | - while (true) { |
56 | | - const { done, value } = await reader.read(); |
57 | | - |
58 | | - if (done) break; |
59 | | - |
60 | | - chunks.push(value); |
61 | | - loaded += value.length; |
62 | | - |
63 | | - // 진행률 콜백 호출 |
64 | | - if (onProgress && total > 0) { |
65 | | - const percentage = Math.round((loaded / total) * 100); |
66 | | - onProgress({ loaded, total, percentage }); |
67 | | - } |
68 | | - } |
69 | | - |
70 | | - // Blob 생성 |
71 | | - const blob = new Blob(chunks, { type: "video/mp4" }); |
72 | | - |
73 | 21 | // 다운로드 링크 생성 및 클릭 |
74 | | - const url = URL.createObjectURL(blob); |
75 | 22 | const link = document.createElement("a"); |
76 | | - link.href = url; |
| 23 | + link.href = videoUrl; |
77 | 24 | link.download = filename; |
| 25 | + link.target = "_blank"; // 새 탭에서 열기 |
78 | 26 |
|
79 | 27 | // 링크를 DOM에 임시로 추가하고 클릭 |
80 | 28 | document.body.appendChild(link); |
81 | 29 | link.click(); |
82 | 30 |
|
83 | 31 | // 정리 |
84 | 32 | document.body.removeChild(link); |
85 | | - URL.revokeObjectURL(url); |
86 | 33 |
|
87 | 34 | onComplete?.(); |
88 | 35 | } catch (error) { |
@@ -115,4 +62,4 @@ export function generateSafeFilename( |
115 | 62 | /** |
116 | 63 | * 다운로드 상태를 관리하는 커스텀 훅에서 사용할 수 있는 타입들 |
117 | 64 | */ |
118 | | -export type { DownloadProgress, DownloadOptions }; |
| 65 | +export type { DownloadOptions }; |
0 commit comments