jerrrycans commited on
Commit
d57a882
·
verified ·
1 Parent(s): 0952d8b

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +1102 -0
app.py ADDED
@@ -0,0 +1,1102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, File, UploadFile, Request
2
+ from fastapi.responses import HTMLResponse, JSONResponse, StreamingResponse, FileResponse
3
+ import requests
4
+ import time
5
+ import asyncio
6
+ from typing import Dict
7
+ import os
8
+ import mimetypes
9
+
10
+ app = FastAPI()
11
+
12
+ HTML_CONTENT = """
13
+ <!DOCTYPE html>
14
+ <html lang="en">
15
+ <head>
16
+ <meta charset="UTF-8">
17
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
18
+ <title>Radd PRO Uploader</title>
19
+ <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap" rel="stylesheet">
20
+ <style>
21
+ body {
22
+ font-family: 'Poppins', sans-serif;
23
+ background-color: #121212;
24
+ color: #e0e0e0;
25
+ margin: 0;
26
+ min-height: 100vh;
27
+ display: flex;
28
+ justify-content: center;
29
+ align-items: center;
30
+ padding: 20px;
31
+ box-sizing: border-box;
32
+ }
33
+
34
+ body::before {
35
+ content: "";
36
+ position: fixed;
37
+ top: 0;
38
+ left: 0;
39
+ width: 100%;
40
+ height: 100%;
41
+ background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAQAAACM/9unAAAACXBIWXMAAAsTAAALEwEAmpwYAAABF0lEQVR4nO3YMRLCQBRE0clsYgxZwwq7oLGxAgp2AtbsqQZsYgEG0wEWc/LSZnb9HcMjR8fHvEKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgsK8tJOq9fzs7vlkNlWvf5ELi+20t5k2VSL7p/iHJ5Rz5ynOhU7rQ4pUi+6b4hyeUc+cpzoVO60OkV7rQ4pUi+6b4hxenu+en4rQAOkaks/eJegGaUfOHI+nRvQCND1w2J9yf8C2KZwNjkeUsAKRa7Nz50tQAokVbO/U6AaiWjl5wd6kQAOke8Xpx3JgTQKVq7Heh0ADpFvF6cd0kE0Claux3odAA6RfxenHc+BNCpWrsd6HQAOkX8Xpx3PgTQqVq7Heh0ADpF/F6cdz4E0KlarjdI/cR1FBQUGBgYGBgYGBgYG/AT4AIHph6aQGU7oAAAAASUVORK5CYII=') repeat;
42
+ animation: grain 8s steps(10) infinite;
43
+ opacity: 0.2;
44
+ pointer-events: none;
45
+ }
46
+
47
+ @keyframes grain {
48
+ 0% { transform: translate(0, 0); }
49
+ 10% { transform: translate(-5%, -5%); }
50
+ 20% { transform: translate(-10%, 5%); }
51
+ 30% { transform: translate(5%, -10%); }
52
+ 40% { transform: translate(-5%, 15%); }
53
+ 50% { transform: translate(-10%, 5%); }
54
+ 60% { transform: translate(15%, 0); }
55
+ 70% { transform: translate(0, 10%); }
56
+ 80% { transform: translate(-15%, 0); }
57
+ 90% { transform: translate(10%, 5%); }
58
+ 100% { transform: translate(5%, 0); }
59
+ }
60
+
61
+ .container {
62
+ position: relative;
63
+ width: 100%;
64
+ max-width: 450px;
65
+ margin: 0 auto;
66
+ padding: 2rem;
67
+ background: rgba(18, 18, 18, 0.9);
68
+ backdrop-filter: blur(10px);
69
+ border-radius: 15px;
70
+ z-index: 1;
71
+ text-align: center;
72
+ box-shadow: 0 0 20px rgba(0, 0, 0, 0.8);
73
+ }
74
+
75
+ h1 {
76
+ margin-bottom: 1.5rem;
77
+ font-size: 1.8rem;
78
+ color: #ffffff;
79
+ text-shadow: 0 0 5px rgba(255, 255, 255, 0.2);
80
+ }
81
+
82
+ .btn {
83
+ display: inline-block;
84
+ position: relative;
85
+ padding: 12px 24px;
86
+ margin: 0.5rem;
87
+ font-size: 1rem;
88
+ font-weight: 600;
89
+ color: #ffffff;
90
+ background-color: #2a2a2a;
91
+ border: none;
92
+ border-radius: 5px;
93
+ cursor: pointer;
94
+ overflow: hidden;
95
+ z-index: 1;
96
+ transition: color 0.3s ease, box-shadow 0.3s ease;
97
+ }
98
+
99
+ .btn:hover {
100
+ color: #ffffff;
101
+ box-shadow: 0 0 15px rgba(200, 200, 200, 0.5);
102
+ }
103
+
104
+ .btn:hover::before {
105
+ content: '';
106
+ position: absolute;
107
+ inset: -10px;
108
+ background: radial-gradient(circle at center, rgba(200,200,200,0.2), transparent);
109
+ filter: blur(20px);
110
+ animation: glowAnimation 2s infinite;
111
+ z-index: -1;
112
+ pointer-events: none;
113
+ }
114
+
115
+ @keyframes glowAnimation {
116
+ 0% { transform: scale(0.8); }
117
+ 50% { transform: scale(1.2); }
118
+ 100% { transform: scale(0.8); }
119
+ }
120
+
121
+ .btn:active {
122
+ transform: scale(0.98);
123
+ }
124
+
125
+ .small-btn {
126
+ padding: 6px 12px;
127
+ font-size: 0.8rem;
128
+ font-weight: 500;
129
+ background-color: #2a2a2a;
130
+ color: #ffffff;
131
+ border: none;
132
+ border-radius: 5px;
133
+ cursor: pointer;
134
+ transition: color 0.3s ease, box-shadow 0.3s ease;
135
+ position: relative;
136
+ overflow: hidden;
137
+ z-index: 1;
138
+ margin: 0.25rem;
139
+ }
140
+
141
+ .small-btn:hover {
142
+ color: #ffffff;
143
+ box-shadow: 0 0 10px rgba(200, 200, 200, 0.5);
144
+ }
145
+
146
+ .small-btn:hover::before {
147
+ content: '';
148
+ position: absolute;
149
+ inset: -10px;
150
+ background: radial-gradient(circle at center, rgba(200,200,200,0.2), transparent);
151
+ filter: blur(15px);
152
+ animation: glowAnimation 2s infinite;
153
+ z-index: -1;
154
+ pointer-events: none;
155
+ }
156
+
157
+ .small-btn:active {
158
+ transform: scale(0.98);
159
+ }
160
+
161
+ .drop-zone {
162
+ position: relative;
163
+ padding: 20px;
164
+ margin-bottom: 1rem;
165
+ border: 2px dashed #aaa;
166
+ border-radius: 10px;
167
+ cursor: pointer;
168
+ transition: all 0.3s ease;
169
+ background: rgba(255, 255, 255, 0.05);
170
+ overflow: hidden;
171
+ }
172
+
173
+ .drop-zone:hover, .drop-zone.drag-over {
174
+ border-color: #ffffff;
175
+ background: rgba(255, 255, 255, 0.1);
176
+ position: relative;
177
+ }
178
+
179
+ .drop-zone:hover::before, .drop-zone.drag-over::before {
180
+ content: '';
181
+ position: absolute;
182
+ inset: -10px;
183
+ background: radial-gradient(circle at center, rgba(200,200,200,0.2), transparent);
184
+ filter: blur(30px);
185
+ animation: grainGlow 5s infinite;
186
+ z-index: -1;
187
+ pointer-events: none;
188
+ }
189
+
190
+ @keyframes grainGlow {
191
+ 0% { opacity: 0.2; }
192
+ 50% { opacity: 0.5; }
193
+ 100% { opacity: 0.2; }
194
+ }
195
+
196
+ .file-input {
197
+ display: none;
198
+ }
199
+
200
+ .file-name {
201
+ margin-top: 1rem;
202
+ font-size: 0.9rem;
203
+ color: #aaa;
204
+ word-break: break-all;
205
+ }
206
+
207
+ .progress-container {
208
+ display: none;
209
+ margin-top: 1.5rem;
210
+ }
211
+
212
+ .progress-bar {
213
+ width: 100%;
214
+ height: 10px;
215
+ background-color: #333;
216
+ border-radius: 5px;
217
+ overflow: hidden;
218
+ margin-bottom: 10px;
219
+ }
220
+
221
+ .progress {
222
+ width: 0%;
223
+ height: 100%;
224
+ background-color: #ffffff;
225
+ transition: width 0.3s ease;
226
+ }
227
+
228
+ .loading-spinner {
229
+ display: none;
230
+ width: 40px;
231
+ height: 40px;
232
+ border: 4px solid #333;
233
+ border-top: 4px solid #ffffff;
234
+ border-radius: 50%;
235
+ animation: spin 1s linear infinite;
236
+ margin: 20px auto;
237
+ }
238
+
239
+ @keyframes spin {
240
+ 0% { transform: rotate(0deg); }
241
+ 100% { transform: rotate(360deg); }
242
+ }
243
+
244
+ .result-container {
245
+ display: none;
246
+ margin-top: 1.5rem;
247
+ }
248
+
249
+ .result-link {
250
+ color: #ffffff;
251
+ text-decoration: none;
252
+ font-weight: 600;
253
+ transition: all 0.3s ease;
254
+ margin-right: 10px;
255
+ word-break: break-all;
256
+ }
257
+
258
+ .result-link:hover {
259
+ text-decoration: underline;
260
+ }
261
+
262
+ .link-buttons {
263
+ display: flex;
264
+ justify-content: center;
265
+ flex-wrap: wrap;
266
+ margin-top: 10px;
267
+ }
268
+
269
+ .file-types {
270
+ margin-top: 2rem;
271
+ font-size: 0.8rem;
272
+ color: #aaa;
273
+ }
274
+
275
+ .modal {
276
+ display: none;
277
+ position: fixed;
278
+ z-index: 2;
279
+ left: 0;
280
+ top: 0;
281
+ width: 100%;
282
+ height: 100%;
283
+ overflow: auto;
284
+ background-color: rgba(0,0,0,0.8);
285
+ }
286
+
287
+ .modal-content {
288
+ background-color: #1e1e1e;
289
+ margin: 15% auto;
290
+ padding: 20px;
291
+ border: 1px solid #333;
292
+ width: 90%;
293
+ max-width: 600px;
294
+ border-radius: 10px;
295
+ color: #e0e0e0;
296
+ animation: modalFadeIn 0.3s;
297
+ position: relative;
298
+ }
299
+
300
+ @keyframes modalFadeIn {
301
+ from {opacity: 0; transform: scale(0.8);}
302
+ to {opacity: 1; transform: scale(1);}
303
+ }
304
+
305
+ .close {
306
+ color: #aaa;
307
+ position: absolute;
308
+ top: 10px;
309
+ right: 15px;
310
+ font-size: 28px;
311
+ font-weight: bold;
312
+ transition: color 0.3s ease;
313
+ }
314
+
315
+ .close:hover,
316
+ .close:focus {
317
+ color: #fff;
318
+ text-decoration: none;
319
+ cursor: pointer;
320
+ }
321
+
322
+ .embed-container {
323
+ display: flex;
324
+ flex-direction: column;
325
+ align-items: stretch;
326
+ margin-top: 15px;
327
+ }
328
+
329
+ #embedLink {
330
+ width: 100%;
331
+ padding: 10px;
332
+ background-color: #333;
333
+ border: 1px solid #555;
334
+ color: #e0e0e0;
335
+ border-radius: 5px;
336
+ margin-bottom: 10px;
337
+ font-size: 0.9rem;
338
+ }
339
+
340
+ .history-btn-container {
341
+ display: flex;
342
+ justify-content: center;
343
+ gap: 10px;
344
+ margin-top: 1rem;
345
+ }
346
+
347
+ .history-btn, .clear-history-btn {
348
+ display: inline-block;
349
+ padding: 8px 16px;
350
+ font-size: 0.9rem;
351
+ font-weight: 500;
352
+ color: #ffffff;
353
+ border: none;
354
+ border-radius: 3px;
355
+ cursor: pointer;
356
+ transition: background-color 0.3s ease, box-shadow 0.3s ease;
357
+ }
358
+
359
+ .history-btn {
360
+ background-color: #2a2a2a;
361
+ }
362
+
363
+ .history-btn:hover {
364
+ background-color: #3a3a3a;
365
+ box-shadow: 0 0 10px rgba(200, 200, 200, 0.3);
366
+ }
367
+
368
+ .clear-history-btn {
369
+ background-color: #1a0505;
370
+ }
371
+
372
+ .clear-history-btn:hover {
373
+ background-color: #2a0a0a;
374
+ box-shadow: 0 0 10px rgba(255, 0, 0, 0.3);
375
+ }
376
+
377
+ .history-modal {
378
+ display: none;
379
+ position: fixed;
380
+ z-index: 3;
381
+ left: 0;
382
+ top: 0;
383
+ width: 100%;
384
+ height: 100%;
385
+ overflow: auto;
386
+ background-color: rgba(0,0,0,0.8);
387
+ }
388
+
389
+ .history-modal-content {
390
+ background-color: #1e1e1e;
391
+ margin: 10% auto;
392
+ padding: 20px;
393
+ border: 1px solid #333;
394
+ width: 90%;
395
+ max-width: 600px;
396
+ border-radius: 10px;
397
+ color: #e0e0e0;
398
+ animation: modalFadeIn 0.3s;
399
+ position: relative;
400
+ max-height: 80vh;
401
+ overflow-y: auto;
402
+ }
403
+
404
+ .history-item {
405
+ display: flex;
406
+ justify-content: space-between;
407
+ align-items: center;
408
+ padding: 10px;
409
+ border-bottom: 1px solid #333;
410
+ transition: background-color 0.3s ease;
411
+ }
412
+
413
+ .history-item:hover {
414
+ background-color: #2a2a2a;
415
+ }
416
+
417
+ .history-item-name {
418
+ flex-grow: 1;
419
+ margin-right: 10px;
420
+ word-break: break-all;
421
+ }
422
+
423
+ .history-item-actions {
424
+ display: flex;
425
+ gap: 5px;
426
+ }
427
+
428
+ .quick-open-modal {
429
+ display: none;
430
+ position: fixed;
431
+ z-index: 4;
432
+ left: 0;
433
+ top: 0;
434
+ width: 100%;
435
+ height: 100%;
436
+ background-color: rgba(0,0,0,0.9);
437
+ overflow: auto;
438
+ }
439
+
440
+ .quick-open-content {
441
+ margin: 5% auto;
442
+ padding: 20px;
443
+ width: 90%;
444
+ max-width: 800px;
445
+ text-align: center;
446
+ }
447
+
448
+ .quick-open-content img,
449
+ .quick-open-content video,
450
+ .quick-open-content audio {
451
+ max-width: 100%;
452
+ max-height: 70vh;
453
+ margin-bottom: 20px;
454
+ }
455
+
456
+ .quick-open-content iframe {
457
+ width: 100%;
458
+ height: 70vh;
459
+ border: none;
460
+ }
461
+
462
+ @media (max-width: 480px) {
463
+ .container {
464
+ padding: 1.5rem;
465
+ }
466
+
467
+ h1 {
468
+ font-size: 1.5rem;
469
+ }
470
+
471
+ .btn, .small-btn {
472
+ font-size: 0.9rem;
473
+ padding: 10px 20px;
474
+ margin: 0.25rem;
475
+ }
476
+
477
+ .file-types {
478
+ font-size: 0.7rem;
479
+ }
480
+
481
+ .modal-content, .history-modal-content {
482
+ width: 95%;
483
+ margin: 5% auto;
484
+ padding: 15px;
485
+ }
486
+
487
+ .history-item {
488
+ flex-direction: column;
489
+ align-items: flex-start;
490
+ }
491
+
492
+ .history-item-actions {
493
+ margin-top: 10px;
494
+ flex-wrap: wrap;
495
+ }
496
+
497
+ .history-item-name {
498
+ font-size: 0.9rem;
499
+ }
500
+
501
+ .drop-zone {
502
+ padding: 15px;
503
+ }
504
+
505
+ .drop-zone p {
506
+ font-size: 0.9rem;
507
+ }
508
+
509
+ .result-link {
510
+ font-size: 0.9rem;
511
+ }
512
+
513
+ .link-buttons {
514
+ flex-direction: column;
515
+ align-items: stretch;
516
+ }
517
+
518
+ .link-buttons .small-btn {
519
+ margin: 0.25rem 0;
520
+ }
521
+
522
+ .close {
523
+ top: 5px;
524
+ right: 10px;
525
+ font-size: 24px;
526
+ }
527
+
528
+ .quick-open-content {
529
+ margin: 10% auto;
530
+ padding: 10px;
531
+ }
532
+ }
533
+ </style>
534
+ </head>
535
+ <body>
536
+ <div class="container">
537
+ <h1>Radd PRO Uploader</h1>
538
+ <form id="uploadForm">
539
+ <div id="dropZone" class="drop-zone">
540
+ <input type="file" name="file" id="file" class="file-input" accept="*" required>
541
+ <label for="file" class="btn">Choose File</label>
542
+ <p>or drag and drop file here/paste image</p>
543
+ </div>
544
+ <div class="file-name" id="fileName"></div>
545
+ <button type="submit" id="uploadBtn" class="btn" style="display: none; margin-top: 1rem;">Upload File</button>
546
+ <div class="progress-container" id="progressContainer"></div>
547
+ <div class="loading-spinner" id="loadingSpinner"></div>
548
+ </form>
549
+ <div class="result-container" id="resultContainer"></div>
550
+ <div class="file-types">
551
+ All file types are supported
552
+ </div>
553
+ <div class="history-btn-container">
554
+ <button id="historyBtn" class="history-btn">View Upload History</button>
555
+ <button id="clearHistoryBtn" class="clear-history-btn">Clear History</button>
556
+ </div>
557
+ </div>
558
+
559
+ <div id="embedModal" class="modal">
560
+ <div class="modal-content">
561
+ <span class="close">&times;</span>
562
+ <h2>Embed Video Link</h2>
563
+ <p>copy the link to embed it on discord:</p>
564
+ <div class="embed-container">
565
+ <input type="text" id="embedLink" readonly>
566
+ <button onclick="copyEmbedLink()" class="small-btn copy-embed-btn">Copy</button>
567
+ </div>
568
+ </div>
569
+ </div>
570
+
571
+ <div id="historyModal" class="history-modal">
572
+ <div class="history-modal-content">
573
+ <span class="close">&times;</span>
574
+ <h2>Upload History</h2>
575
+ <div id="historyList"></div>
576
+ </div>
577
+ </div>
578
+
579
+ <div id="quickOpenModal" class="quick-open-modal">
580
+ <div class="quick-open-content">
581
+ <span class="close">&times;</span>
582
+ <div id="quickOpenContent"></div>
583
+ </div>
584
+ </div>
585
+
586
+ <script>
587
+ const fileInput = document.getElementById('file');
588
+ const fileName = document.getElementById('fileName');
589
+ const uploadForm = document.getElementById('uploadForm');
590
+ const progressContainer = document.getElementById('progressContainer');
591
+ const loadingSpinner = document.getElementById('loadingSpinner');
592
+ const resultContainer = document.getElementById('resultContainer');
593
+ const dropZone = document.getElementById('dropZone');
594
+ const modal = document.getElementById('embedModal');
595
+ const historyModal = document.getElementById('historyModal');
596
+ const quickOpenModal = document.getElementById('quickOpenModal');
597
+ const span = document.getElementsByClassName("close");
598
+ const embedLinkInput = document.getElementById('embedLink');
599
+ const uploadBtn = document.getElementById('uploadBtn');
600
+ const historyBtn = document.getElementById('historyBtn');
601
+ const clearHistoryBtn = document.getElementById('clearHistoryBtn');
602
+ const historyList = document.getElementById('historyList');
603
+ const quickOpenContent = document.getElementById('quickOpenContent');
604
+
605
+ fileInput.addEventListener('change', handleFileSelect);
606
+
607
+ uploadForm.addEventListener('submit', (e) => {
608
+ e.preventDefault();
609
+ if (fileInput.files.length > 0) {
610
+ uploadFile(fileInput.files[0]);
611
+ }
612
+ });
613
+
614
+ dropZone.addEventListener('dragover', (e) => {
615
+ e.preventDefault();
616
+ dropZone.classList.add('drag-over');
617
+ });
618
+
619
+ dropZone.addEventListener('dragleave', () => {
620
+ dropZone.classList.remove('drag-over');
621
+ });
622
+
623
+ dropZone.addEventListener('drop', (e) => {
624
+ e.preventDefault();
625
+ dropZone.classList.remove('drag-over');
626
+ handleFileSelect({ target: { files: e.dataTransfer.files } });
627
+ });
628
+
629
+ document.addEventListener('paste', (e) => {
630
+ const items = e.clipboardData.items;
631
+ for (let i = 0; i < items.length; i++) {
632
+ if (items[i].kind === 'file') {
633
+ const file = items[i].getAsFile();
634
+ handleFileSelect({ target: { files: [file] } });
635
+ break;
636
+ }
637
+ }
638
+ });
639
+
640
+ span[0].onclick = function() {
641
+ modal.style.display = "none";
642
+ }
643
+
644
+ span[1].onclick = function() {
645
+ historyModal.style.display = "none";
646
+ }
647
+
648
+ span[2].onclick = function() {
649
+ quickOpenModal.style.display = "none";
650
+ }
651
+
652
+ window.onclick = function(event) {
653
+ if (event.target == modal) {
654
+ modal.style.display = "none";
655
+ }
656
+ if (event.target == historyModal) {
657
+ historyModal.style.display = "none";
658
+ }
659
+ if (event.target == quickOpenModal) {
660
+ quickOpenModal.style.display = "none";
661
+ }
662
+ }
663
+
664
+ historyBtn.onclick = function() {
665
+ showHistory();
666
+ }
667
+
668
+ clearHistoryBtn.onclick = function() {
669
+ if (confirm('Are you sure you want to clear your upload history?')) {
670
+ localStorage.removeItem('uploadHistory');
671
+ alert('Upload history has been cleared.');
672
+ }
673
+ }
674
+
675
+ function handleFileSelect(e) {
676
+ if (e.target.files && e.target.files.length > 0) {
677
+ const file = e.target.files[0];
678
+ fileName.textContent = file.name;
679
+ uploadBtn.style.display = 'inline-block';
680
+
681
+ const dataTransfer = new DataTransfer();
682
+ dataTransfer.items.add(file);
683
+ fileInput.files = dataTransfer.files;
684
+ }
685
+ }
686
+
687
+ async function uploadFile(file) {
688
+ progressContainer.innerHTML = '';
689
+ progressContainer.style.display = 'block';
690
+ loadingSpinner.style.display = 'block';
691
+ uploadBtn.disabled = true;
692
+ resultContainer.innerHTML = '';
693
+ resultContainer.style.display = 'none';
694
+
695
+ const progressBar = createProgressBar(file.name);
696
+ progressContainer.appendChild(progressBar);
697
+
698
+ const formData = new FormData();
699
+ formData.append('file', file);
700
+
701
+ while (true) {
702
+ try {
703
+ const xhr = new XMLHttpRequest();
704
+ xhr.open('POST', '/upload', true);
705
+ xhr.upload.onprogress = (event) => updateProgress(event, progressBar.querySelector('.progress'));
706
+
707
+ xhr.onload = function() {
708
+ if (xhr.status === 200) {
709
+ const response = JSON.parse(xhr.responseText);
710
+ if (response.url) {
711
+ addResultLink(response.url, file.name, response.originalExtension);
712
+ saveToHistory(file.name, response.url, response.originalExtension);
713
+ resetUploadState();
714
+ return;
715
+ } else {
716
+ throw new Error('Upload failed: ' + response.error);
717
+ }
718
+ } else {
719
+ throw new Error(`HTTP error! status: ${xhr.status}`);
720
+ }
721
+ };
722
+
723
+ xhr.onerror = function() {
724
+ throw new Error('Network error occurred');
725
+ };
726
+
727
+ xhr.send(formData);
728
+
729
+ await new Promise((resolve, reject) => {
730
+ xhr.onloadend = resolve;
731
+ xhr.onerror = reject;
732
+ });
733
+
734
+ break;
735
+ } catch (error) {
736
+ console.error('Upload error:', error);
737
+ await new Promise(resolve => setTimeout(resolve, 1000));
738
+ }
739
+ }
740
+ }
741
+
742
+ function createProgressBar(fileName) {
743
+ const progressBarContainer = document.createElement('div');
744
+ progressBarContainer.className = 'progress-bar';
745
+ const progress = document.createElement('div');
746
+ progress.className = 'progress';
747
+ progressBarContainer.appendChild(progress);
748
+ const label = document.createElement('div');
749
+ label.textContent = fileName;
750
+ label.style.fontSize = '0.8rem';
751
+ label.style.marginBottom = '5px';
752
+ const container = document.createElement('div');
753
+ container.appendChild(label);
754
+ container.appendChild(progressBarContainer);
755
+ return container;
756
+ }
757
+
758
+ function updateProgress(event, progressBar) {
759
+ if (event.lengthComputable) {
760
+ const percentComplete = (event.loaded / event.total) * 100;
761
+ progressBar.style.width = percentComplete + '%';
762
+ }
763
+ }
764
+
765
+ function resetUploadState() {
766
+ fileInput.value = '';
767
+ fileName.textContent = '';
768
+ uploadBtn.style.display = 'none';
769
+ uploadBtn.disabled = false;
770
+ loadingSpinner.style.display = 'none';
771
+ }
772
+
773
+ function addResultLink(url, fileName, originalExtension) {
774
+ const linkContainer = document.createElement('div');
775
+ linkContainer.style.marginBottom = '10px';
776
+
777
+ const link = document.createElement('a');
778
+ link.href = url;
779
+ link.textContent = `View ${fileName}`;
780
+ link.className = 'result-link';
781
+ link.target = '_blank';
782
+ link.download = fileName;
783
+
784
+ linkContainer.appendChild(link);
785
+
786
+ const buttonsContainer = document.createElement('div');
787
+ buttonsContainer.className = 'link-buttons';
788
+
789
+ const copyBtn = document.createElement('button');
790
+ copyBtn.textContent = 'Copy Link';
791
+ copyBtn.className = 'small-btn copy-btn';
792
+ copyBtn.onclick = () => {
793
+ navigator.clipboard.writeText(window.location.origin + url).then(() => {
794
+ alert('Link copied to clipboard!');
795
+ });
796
+ };
797
+ buttonsContainer.appendChild(copyBtn);
798
+
799
+ const openBtn = document.createElement('button');
800
+ openBtn.textContent = 'Open';
801
+ openBtn.className = 'small-btn';
802
+ openBtn.onclick = () => {
803
+ quickOpen(url, fileName, originalExtension);
804
+ };
805
+ buttonsContainer.appendChild(openBtn);
806
+
807
+ if (originalExtension.toLowerCase() === 'mp4') {
808
+ const embedBtn = document.createElement('button');
809
+ embedBtn.textContent = 'Embed Video for Discord';
810
+ embedBtn.className = 'small-btn embed-btn';
811
+ embedBtn.onclick = () => {
812
+ showEmbedModal(url);
813
+ };
814
+ buttonsContainer.appendChild(embedBtn);
815
+ }
816
+
817
+ linkContainer.appendChild(buttonsContainer);
818
+
819
+ resultContainer.appendChild(linkContainer);
820
+ resultContainer.style.display = 'block';
821
+ }
822
+
823
+ function showEmbedModal(url) {
824
+ const embedUrl = `https://x266.mov/e/${encodeURIComponent(window.location.origin + url)}`;
825
+ embedLinkInput.value = embedUrl;
826
+ modal.style.display = "block";
827
+ }
828
+
829
+ function copyEmbedLink() {
830
+ embedLinkInput.select();
831
+ document.execCommand('copy');
832
+ alert('Embed link copied to clipboard!');
833
+ }
834
+
835
+ function saveToHistory(fileName, url, originalExtension) {
836
+ let history = JSON.parse(localStorage.getItem('uploadHistory')) || [];
837
+ history.unshift({ fileName, url, originalExtension, timestamp: new Date().toISOString() });
838
+ if (history.length > 500) history = history.slice(0, 500);
839
+ localStorage.setItem('uploadHistory', JSON.stringify(history));
840
+ }
841
+
842
+ function showHistory() {
843
+ const history = JSON.parse(localStorage.getItem('uploadHistory')) || [];
844
+ historyList.innerHTML = '';
845
+ history.forEach(item => {
846
+ const historyItem = document.createElement('div');
847
+ historyItem.className = 'history-item';
848
+
849
+ const itemName = document.createElement('span');
850
+ itemName.className = 'history-item-name';
851
+ itemName.textContent = item.fileName;
852
+ historyItem.appendChild(itemName);
853
+
854
+ const actionsContainer = document.createElement('div');
855
+ actionsContainer.className = 'history-item-actions';
856
+
857
+ const copyBtn = document.createElement('button');
858
+ copyBtn.textContent = 'Copy Link';
859
+ copyBtn.className = 'small-btn';
860
+ copyBtn.onclick = () => {
861
+ navigator.clipboard.writeText(window.location.origin + item.url).then(() => {
862
+ alert('Link copied to clipboard!');
863
+ });
864
+ };
865
+ actionsContainer.appendChild(copyBtn);
866
+
867
+ const openBtn = document.createElement('button');
868
+ openBtn.textContent = 'Open';
869
+ openBtn.className = 'small-btn';
870
+ openBtn.onclick = () => {
871
+ quickOpen(item.url, item.fileName, item.originalExtension);
872
+ };
873
+ actionsContainer.appendChild(openBtn);
874
+
875
+ if (item.originalExtension.toLowerCase() === 'mp4') {
876
+ const embedBtn = document.createElement('button');
877
+ embedBtn.textContent = 'Embed';
878
+ embedBtn.className = 'small-btn';
879
+ embedBtn.onclick = () => {
880
+ showEmbedModal(item.url);
881
+ historyModal.style.display = "none";
882
+ };
883
+ actionsContainer.appendChild(embedBtn);
884
+ }
885
+
886
+ historyItem.appendChild(actionsContainer);
887
+ historyList.appendChild(historyItem);
888
+ });
889
+ historyModal.style.display = "block";
890
+ }
891
+
892
+ function quickOpen(url, fileName, originalExtension) {
893
+ quickOpenContent.innerHTML = '';
894
+ const fullUrl = window.location.origin + url;
895
+
896
+ if (['jpeg', 'jpg', 'gif', 'png'].includes(originalExtension.toLowerCase())) {
897
+ const img = document.createElement('img');
898
+ img.src = fullUrl;
899
+ img.alt = fileName;
900
+ quickOpenContent.appendChild(img);
901
+ } else if (originalExtension.toLowerCase() === 'mp4') {
902
+ const video = document.createElement('video');
903
+ video.src = fullUrl;
904
+ video.controls = true;
905
+ quickOpenContent.appendChild(video);
906
+ } else if (originalExtension.toLowerCase() === 'mp3') {
907
+ const audio = document.createElement('audio');
908
+ audio.src = fullUrl;
909
+ audio.controls = true;
910
+ quickOpenContent.appendChild(audio);
911
+ } else if (originalExtension.toLowerCase() === 'pdf') {
912
+ const iframe = document.createElement('iframe');
913
+ iframe.src = fullUrl;
914
+ quickOpenContent.appendChild(iframe);
915
+ } else if (originalExtension.toLowerCase() === 'txt') {
916
+ fetch(fullUrl)
917
+ .then(response => response.text())
918
+ .then(text => {
919
+ const pre = document.createElement('pre');
920
+ pre.textContent = text;
921
+ quickOpenContent.appendChild(pre);
922
+ });
923
+ } else {
924
+ const link = document.createElement('a');
925
+ link.href = fullUrl;
926
+ link.textContent = 'Download ' + fileName;
927
+ link.target = '_blank';
928
+ link.download = fileName;
929
+ quickOpenContent.appendChild(link);
930
+ }
931
+
932
+ quickOpenModal.style.display = "block";
933
+ }
934
+ </script>
935
+ </body>
936
+ </html>
937
+ """
938
+
939
+ @app.get("/", response_class=HTMLResponse)
940
+ async def index():
941
+ return HTML_CONTENT
942
+
943
+ @app.post("/upload")
944
+ async def handle_upload(file: UploadFile = File(...)):
945
+ if not file.filename:
946
+ return JSONResponse(content={"error": "No file selected."}, status_code=400)
947
+
948
+ cookies = await get_cookies()
949
+ if 'csrftoken' not in cookies or 'sessionid' not in cookies:
950
+ return JSONResponse(content={"error": "Failed"}, status_code=500)
951
+
952
+ original_extension = os.path.splitext(file.filename)[1][1:]
953
+ supported_types = ['mp4', 'png', 'jpg', 'jpeg', 'gif', 'mp3', 'pdf', 'txt']
954
+
955
+ if original_extension.lower() in supported_types:
956
+ temp_filename = file.filename
957
+ content_type = file.content_type
958
+ else:
959
+ temp_filename = f"{file.filename}.png"
960
+ content_type = "image/png"
961
+
962
+ upload_result = await initiate_upload(cookies, temp_filename, content_type)
963
+ if not upload_result or 'upload_url' not in upload_result:
964
+ return JSONResponse(content={"error": "Failed to upload"}, status_code=500)
965
+
966
+ file_content = await file.read()
967
+ upload_success = await retry_upload(upload_result['upload_url'], file_content, content_type)
968
+ if not upload_success:
969
+ return JSONResponse(content={"error": "FAILED GOD MAN AFTER alot of attempts"}, status_code=500)
970
+
971
+ original_url = upload_result['serving_url']
972
+ mirrored_url = f"/rbxg/{original_url.split('/pbxt/')[1]}"
973
+
974
+ if original_extension.lower() not in supported_types:
975
+ mirrored_url = mirrored_url.replace('.png', '')
976
+
977
+ return JSONResponse(content={"url": mirrored_url, "originalExtension": original_extension})
978
+
979
+ @app.get("/rbxg/{path:path}")
980
+ async def handle_file_stream(path: str, request: Request):
981
+ original_url = f'https://replicate.delivery/pbxt/{path}'
982
+
983
+ if not path.lower().endswith(('.png', '.jpg', '.jpeg', '.gif', '.mp4', '.mp3', '.pdf', '.txt')):
984
+ original_url += '.png'
985
+
986
+ range_header = request.headers.get('Range')
987
+ headers = {'Range': range_header} if range_header else {}
988
+ response = requests.get(original_url, headers=headers, stream=True)
989
+
990
+ def generate():
991
+ for chunk in response.iter_content(chunk_size=8192):
992
+ yield chunk
993
+
994
+ headers = dict(response.headers)
995
+ headers['Access-Control-Allow-Origin'] = '*'
996
+ headers['Content-Disposition'] = 'inline'
997
+
998
+ if response.status_code == 206:
999
+ headers['Content-Range'] = response.headers.get('Content-Range')
1000
+
1001
+ original_extension = os.path.splitext(path)[1][1:]
1002
+ content_type, _ = mimetypes.guess_type(f"file.{original_extension}")
1003
+ if content_type:
1004
+ headers['Content-Type'] = content_type
1005
+
1006
+ return StreamingResponse(generate(), status_code=response.status_code, headers=headers)
1007
+
1008
+ @app.get("/embed")
1009
+ async def embed_video(url: str, thumbnail: str):
1010
+ html = f'''
1011
+ <html>
1012
+ <head>
1013
+ <meta property="og:type" content="video.other">
1014
+ <meta property="og:video" content="{url}">
1015
+ <meta property="og:video:url" content="{url}">
1016
+ <meta property="og:video:secure_url" content="{url}">
1017
+ <meta property="og:video:type" content="video/mp4">
1018
+ <meta property="og:video:width" content="1280">
1019
+ <meta property="og:video:height" content="720">
1020
+ <meta property="og:image" content="{thumbnail}">
1021
+ <meta property="og:image:secure_url" content="{thumbnail}">
1022
+ <meta property="og:image:width" content="1280">
1023
+ <meta property="og:image:height" content="720">
1024
+ <meta property="og:image:type" content="image/png">
1025
+ <style>
1026
+ body, html {{ margin: 0; padding: 0; height: 100%; background: #000; }}
1027
+ #thumbnail {{ width: 100%; height: 100%; object-fit: contain; cursor: pointer; }}
1028
+ #video {{ display: none; width: 100%; height: 100%; object-fit: contain; }}
1029
+ </style>
1030
+ </head>
1031
+ <body>
1032
+ <img id="thumbnail" src="{thumbnail}" onclick="playVideo()">
1033
+ <video id="video" controls autoplay>
1034
+ <source src="{url}" type="video/mp4">
1035
+ Your browser does not support the video tag.
1036
+ </video>
1037
+ <script>
1038
+ function playVideo() {{
1039
+ document.getElementById('thumbnail').style.display = 'none';
1040
+ document.getElementById('video').style.display = 'block';
1041
+ document.getElementById('video').play();
1042
+ }}
1043
+ </script>
1044
+ </body>
1045
+ </html>
1046
+ '''
1047
+ return HTMLResponse(content=html)
1048
+
1049
+ async def get_cookies() -> Dict[str, str]:
1050
+ try:
1051
+ response = requests.get('https://replicate.com/levelsio/neon-tokyo', headers={
1052
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36'
1053
+ })
1054
+ return dict(response.cookies)
1055
+ except Exception as e:
1056
+ print(f'Error fetching the page: {e}')
1057
+ return {}
1058
+
1059
+ async def initiate_upload(cookies: Dict[str, str], filename: str, content_type: str) -> Dict:
1060
+ url = f'https://replicate.com/api/upload/{filename}?content_type={content_type}'
1061
+ try:
1062
+ response = requests.post(url, cookies=cookies, headers={
1063
+ 'X-CSRFToken': cookies.get('csrftoken'),
1064
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36',
1065
+ 'Referer': 'https://replicate.com/levelsio/neon-tokyo',
1066
+ 'Origin': 'https://replicate.com',
1067
+ 'Accept': '*/*',
1068
+ 'Accept-Language': 'en-US,en;q=0.5',
1069
+ 'Accept-Encoding': 'identity',
1070
+ 'Sec-Fetch-Dest': 'empty',
1071
+ 'Sec-Fetch-Mode': 'cors',
1072
+ 'Sec-Fetch-Site': 'same-origin',
1073
+ 'Sec-GPC': '1',
1074
+ 'Priority': 'u=1, i'
1075
+ })
1076
+ return response.json()
1077
+ except Exception as e:
1078
+ print(f'Error initiating upload: {e}')
1079
+ raise
1080
+
1081
+ async def upload_file(upload_url: str, file_content: bytes, content_type: str) -> bool:
1082
+ try:
1083
+ response = requests.put(upload_url, data=file_content, headers={'Content-Type': content_type})
1084
+ return response.status_code == 200
1085
+ except Exception as e:
1086
+ print(f'Error uploading file: {e}')
1087
+ return False
1088
+
1089
+ async def retry_upload(upload_url: str, file_content: bytes, content_type: str, max_retries: int = 5, delay: int = 1) -> bool:
1090
+ while True:
1091
+ try:
1092
+ success = await upload_file(upload_url, file_content, content_type)
1093
+ if success:
1094
+ return True
1095
+ print("Upload failed. Retrying...")
1096
+ except Exception as e:
1097
+ print(f"Error during upload: {e}")
1098
+
1099
+ await asyncio.sleep(delay)
1100
+ delay = min(delay * 2, 60)
1101
+
1102
+ return False