triflix commited on
Commit
0ce04f3
·
verified ·
1 Parent(s): 80bc627

Create app/static/script.js

Browse files
Files changed (1) hide show
  1. app/static/script.js +183 -0
app/static/script.js ADDED
@@ -0,0 +1,183 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // app/static/script.js
2
+ document.addEventListener('DOMContentLoaded', () => {
3
+ // --- Constants and State ---
4
+ const CHUNK_SIZE = 4 * 1024 * 1024; // 4MB chunks
5
+ let currentFile = null;
6
+ let session = {}; // To hold session_id and token
7
+ let websocket = null;
8
+
9
+ // --- DOM Elements ---
10
+ const uploaderCard = document.getElementById('uploader-card');
11
+ const states = {
12
+ initial: document.getElementById('state-initial'),
13
+ uploading: document.getElementById('state-uploading'),
14
+ assembling: document.getElementById('state-assembling'),
15
+ complete: document.getElementById('state-complete'),
16
+ error: document.getElementById('state-error'),
17
+ };
18
+ const dropZone = document.getElementById('drop-zone');
19
+ const browseBtn = document.getElementById('browse-btn');
20
+ const fileInput = document.getElementById('file-input');
21
+ const fileNameEl = document.getElementById('file-name');
22
+ const fileSizeEl = document.getElementById('file-size');
23
+ const progressBar = document.getElementById('progress-bar');
24
+ const statusText = document.getElementById('status-text');
25
+ const downloadLink = document.getElementById('download-link');
26
+ const uploadAnotherBtn = document.getElementById('upload-another-btn');
27
+ const errorMessage = document.getElementById('error-message');
28
+ const retryBtn = document.getElementById('retry-btn');
29
+
30
+ // --- UI State Management ---
31
+ function switchState(state) {
32
+ Object.values(states).forEach(el => el.classList.remove('active'));
33
+ if (states[state]) {
34
+ states[state].classList.add('active');
35
+ }
36
+ }
37
+
38
+ // --- Event Listeners ---
39
+ browseBtn.addEventListener('click', () => fileInput.click());
40
+ fileInput.addEventListener('change', handleFileSelect);
41
+
42
+ // Drag and Drop
43
+ dropZone.addEventListener('dragover', (e) => {
44
+ e.preventDefault();
45
+ dropZone.classList.add('drag-over');
46
+ });
47
+ dropZone.addEventListener('dragleave', () => dropZone.classList.remove('drag-over'));
48
+ dropZone.addEventListener('drop', (e) => {
49
+ e.preventDefault();
50
+ dropZone.classList.remove('drag-over');
51
+ const files = e.dataTransfer.files;
52
+ if (files.length > 0) {
53
+ handleFileSelect({ target: { files: files } });
54
+ }
55
+ });
56
+
57
+ uploadAnotherBtn.addEventListener('click', resetUploader);
58
+ retryBtn.addEventListener('click', resetUploader);
59
+
60
+
61
+ // --- Core Functions ---
62
+ function handleFileSelect(event) {
63
+ const file = event.target.files[0];
64
+ if (!file) return;
65
+
66
+ currentFile = file;
67
+ fileNameEl.textContent = file.name;
68
+ fileSizeEl.textContent = formatBytes(file.size);
69
+
70
+ startUploadProcess(file);
71
+ }
72
+
73
+ async function startUploadProcess(file) {
74
+ switchState('uploading');
75
+ statusText.textContent = 'Creating secure session...';
76
+ progressBar.style.width = '0%';
77
+
78
+ try {
79
+ // 1. Create a new session
80
+ const response = await fetch('/sessions', { method: 'POST' });
81
+ if (!response.ok) throw new Error('Failed to create session.');
82
+ session = await response.json();
83
+
84
+ // 2. Setup WebSocket for progress
85
+ setupWebSocket();
86
+
87
+ // 3. Start uploading chunks
88
+ await uploadFileInChunks(file);
89
+
90
+ // 4. Finalize the upload
91
+ statusText.textContent = 'Finalizing upload...';
92
+ const completeResponse = await fetch(`/complete/${session.session_id}`, { method: 'POST' });
93
+ if (!completeResponse.ok) throw new Error('Failed to finalize upload.');
94
+
95
+ const result = await completeResponse.json();
96
+ downloadLink.href = result.download_url;
97
+
98
+ } catch (error) {
99
+ console.error('Upload process failed:', error);
100
+ showError(error.message);
101
+ }
102
+ }
103
+
104
+ function setupWebSocket() {
105
+ const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
106
+ const wsUrl = `${wsProtocol}//${window.location.host}/ws/${session.session_id}`;
107
+ websocket = new WebSocket(wsUrl);
108
+
109
+ websocket.onmessage = (event) => {
110
+ const data = JSON.parse(event.data);
111
+ if (data.type === 'progress') {
112
+ updateProgress(data.uploaded_bytes, data.total_bytes);
113
+ if(data.status === 'assembling') {
114
+ switchState('assembling');
115
+ } else if(data.status === 'completed') {
116
+ switchState('complete');
117
+ }
118
+ }
119
+ };
120
+ websocket.onerror = (error) => console.error('WebSocket Error:', error);
121
+ websocket.onclose = () => console.log('WebSocket closed.');
122
+ }
123
+
124
+ async function uploadFileInChunks(file) {
125
+ const totalChunks = Math.ceil(file.size / CHUNK_SIZE);
126
+ for (let i = 0; i < totalChunks; i++) {
127
+ const start = i * CHUNK_SIZE;
128
+ const end = Math.min(start + CHUNK_SIZE, file.size);
129
+ const chunk = file.slice(start, end);
130
+
131
+ const formData = new FormData();
132
+ formData.append('file', chunk, file.name);
133
+
134
+ const url = `/upload/${session.session_id}?chunk_index=${i}&total_size=${file.size}`;
135
+
136
+ statusText.textContent = `Uploading chunk ${i + 1} of ${totalChunks}...`;
137
+
138
+ const response = await fetch(url, {
139
+ method: 'PUT',
140
+ body: formData,
141
+ });
142
+
143
+ if (!response.ok) {
144
+ const errorData = await response.json();
145
+ throw new Error(errorData.detail || 'Chunk upload failed.');
146
+ }
147
+ }
148
+ }
149
+
150
+ function updateProgress(uploaded, total) {
151
+ if (total > 0) {
152
+ const percentage = (uploaded / total) * 100;
153
+ progressBar.style.width = `${percentage}%`;
154
+ statusText.textContent = `${formatBytes(uploaded)} / ${formatBytes(total)}`;
155
+ }
156
+ }
157
+
158
+ function showError(message) {
159
+ errorMessage.textContent = message;
160
+ switchState('error');
161
+ }
162
+
163
+ function resetUploader() {
164
+ currentFile = null;
165
+ session = {};
166
+ fileInput.value = ''; // Reset file input
167
+ if (websocket) websocket.close();
168
+ switchState('initial');
169
+ }
170
+
171
+ // --- Helper ---
172
+ function formatBytes(bytes, decimals = 2) {
173
+ if (bytes === 0) return '0 Bytes';
174
+ const k = 1024;
175
+ const dm = decimals < 0 ? 0 : decimals;
176
+ const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
177
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
178
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
179
+ }
180
+
181
+ // --- Initial State ---
182
+ switchState('initial');
183
+ });