eubottura commited on
Commit
d1b95fe
·
verified ·
1 Parent(s): c4d3935

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +655 -19
index.html CHANGED
@@ -1,19 +1,655 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Douyin Video Download Link Processor</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ :root {
15
+ --primary: #FF0050;
16
+ --primary-dark: #CC0040;
17
+ --secondary: #161823;
18
+ --secondary-light: #2a2d3a;
19
+ --text-primary: #ffffff;
20
+ --text-secondary: #a0a0a0;
21
+ --bg-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
22
+ --card-bg: rgba(255, 255, 255, 0.05);
23
+ --border-color: rgba(255, 255, 255, 0.1);
24
+ --success: #4ade80;
25
+ --error: #f87171;
26
+ }
27
+
28
+ body {
29
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
30
+ background: var(--bg-gradient);
31
+ min-height: 100vh;
32
+ color: var(--text-primary);
33
+ position: relative;
34
+ overflow-x: hidden;
35
+ }
36
+
37
+ body::before {
38
+ content: '';
39
+ position: fixed;
40
+ top: 0;
41
+ left: 0;
42
+ right: 0;
43
+ bottom: 0;
44
+ background:
45
+ radial-gradient(circle at 20% 50%, rgba(102, 126, 234, 0.1) 0%, transparent 50%),
46
+ radial-gradient(circle at 80% 80%, rgba(118, 75, 162, 0.1) 0%, transparent 50%),
47
+ radial-gradient(circle at 40% 20%, rgba(255, 0, 80, 0.05) 0%, transparent 50%);
48
+ pointer-events: none;
49
+ z-index: 1;
50
+ }
51
+
52
+ .container {
53
+ max-width: 1200px;
54
+ margin: 0 auto;
55
+ padding: 2rem;
56
+ position: relative;
57
+ z-index: 2;
58
+ }
59
+
60
+ header {
61
+ text-align: center;
62
+ margin-bottom: 3rem;
63
+ animation: fadeInDown 0.8s ease;
64
+ }
65
+
66
+ h1 {
67
+ font-size: 2.5rem;
68
+ font-weight: 700;
69
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
70
+ -webkit-background-clip: text;
71
+ -webkit-text-fill-color: transparent;
72
+ background-clip: text;
73
+ margin-bottom: 0.5rem;
74
+ text-shadow: 0 0 40px rgba(102, 126, 234, 0.3);
75
+ }
76
+
77
+ .subtitle {
78
+ color: var(--text-secondary);
79
+ font-size: 1.1rem;
80
+ }
81
+
82
+ .credit {
83
+ margin-top: 1rem;
84
+ font-size: 0.9rem;
85
+ color: var(--text-secondary);
86
+ }
87
+
88
+ .credit a {
89
+ color: var(--primary);
90
+ text-decoration: none;
91
+ transition: color 0.3s ease;
92
+ }
93
+
94
+ .credit a:hover {
95
+ color: var(--primary-dark);
96
+ text-decoration: underline;
97
+ }
98
+
99
+ .main-card {
100
+ background: var(--card-bg);
101
+ backdrop-filter: blur(10px);
102
+ border-radius: 20px;
103
+ border: 1px solid var(--border-color);
104
+ padding: 2rem;
105
+ animation: fadeInUp 0.8s ease;
106
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2);
107
+ }
108
+
109
+ .input-section {
110
+ margin-bottom: 2rem;
111
+ }
112
+
113
+ label {
114
+ display: block;
115
+ margin-bottom: 0.5rem;
116
+ font-weight: 500;
117
+ color: var(--text-primary);
118
+ }
119
+
120
+ textarea {
121
+ width: 100%;
122
+ min-height: 150px;
123
+ padding: 1rem;
124
+ background: rgba(255, 255, 255, 0.05);
125
+ border: 2px solid var(--border-color);
126
+ border-radius: 12px;
127
+ color: var(--text-primary);
128
+ font-size: 1rem;
129
+ font-family: 'Courier New', monospace;
130
+ resize: vertical;
131
+ transition: all 0.3s ease;
132
+ }
133
+
134
+ textarea:focus {
135
+ outline: none;
136
+ border-color: var(--primary);
137
+ background: rgba(255, 255, 255, 0.08);
138
+ box-shadow: 0 0 20px rgba(255, 0, 80, 0.2);
139
+ }
140
+
141
+ textarea::placeholder {
142
+ color: var(--text-secondary);
143
+ }
144
+
145
+ .button-group {
146
+ display: flex;
147
+ gap: 1rem;
148
+ margin-top: 1.5rem;
149
+ flex-wrap: wrap;
150
+ }
151
+
152
+ button {
153
+ padding: 0.75rem 2rem;
154
+ background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
155
+ color: white;
156
+ border: none;
157
+ border-radius: 10px;
158
+ font-size: 1rem;
159
+ font-weight: 600;
160
+ cursor: pointer;
161
+ transition: all 0.3s ease;
162
+ position: relative;
163
+ overflow: hidden;
164
+ }
165
+
166
+ button::before {
167
+ content: '';
168
+ position: absolute;
169
+ top: 50%;
170
+ left: 50%;
171
+ width: 0;
172
+ height: 0;
173
+ border-radius: 50%;
174
+ background: rgba(255, 255, 255, 0.3);
175
+ transform: translate(-50%, -50%);
176
+ transition: width 0.6s, height 0.6s;
177
+ }
178
+
179
+ button:hover::before {
180
+ width: 300px;
181
+ height: 300px;
182
+ }
183
+
184
+ button:hover {
185
+ transform: translateY(-2px);
186
+ box-shadow: 0 10px 30px rgba(255, 0, 80, 0.4);
187
+ }
188
+
189
+ button:active {
190
+ transform: translateY(0);
191
+ }
192
+
193
+ button.secondary {
194
+ background: var(--secondary-light);
195
+ border: 1px solid var(--border-color);
196
+ }
197
+
198
+ .results-section {
199
+ margin-top: 2rem;
200
+ opacity: 0;
201
+ transform: translateY(20px);
202
+ transition: all 0.5s ease;
203
+ }
204
+
205
+ .results-section.show {
206
+ opacity: 1;
207
+ transform: translateY(0);
208
+ }
209
+
210
+ .results-header {
211
+ display: flex;
212
+ justify-content: space-between;
213
+ align-items: center;
214
+ margin-bottom: 1rem;
215
+ padding-bottom: 1rem;
216
+ border-bottom: 1px solid var(--border-color);
217
+ }
218
+
219
+ .results-title {
220
+ font-size: 1.3rem;
221
+ font-weight: 600;
222
+ }
223
+
224
+ .copy-all-btn {
225
+ padding: 0.5rem 1rem;
226
+ font-size: 0.9rem;
227
+ background: var(--secondary-light);
228
+ }
229
+
230
+ .url-list {
231
+ list-style: none;
232
+ counter-reset: url-counter;
233
+ }
234
+
235
+ .url-item {
236
+ counter-increment: url-counter;
237
+ padding: 1rem;
238
+ margin-bottom: 0.75rem;
239
+ background: rgba(255, 255, 255, 0.03);
240
+ border-radius: 10px;
241
+ border: 1px solid var(--border-color);
242
+ display: flex;
243
+ align-items: center;
244
+ gap: 1rem;
245
+ transition: all 0.3s ease;
246
+ animation: slideInLeft 0.5s ease forwards;
247
+ opacity: 0;
248
+ }
249
+
250
+ .url-item:hover {
251
+ background: rgba(255, 255, 255, 0.06);
252
+ border-color: var(--primary);
253
+ transform: translateX(5px);
254
+ }
255
+
256
+ .url-item::before {
257
+ content: counter(url-counter);
258
+ min-width: 30px;
259
+ height: 30px;
260
+ background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
261
+ border-radius: 50%;
262
+ display: flex;
263
+ align-items: center;
264
+ justify-content: center;
265
+ font-weight: bold;
266
+ font-size: 0.9rem;
267
+ }
268
+
269
+ .url-content {
270
+ flex: 1;
271
+ word-break: break-all;
272
+ font-family: 'Courier New', monospace;
273
+ font-size: 0.95rem;
274
+ color: var(--text-secondary);
275
+ }
276
+
277
+ .copy-btn {
278
+ padding: 0.4rem 0.8rem;
279
+ font-size: 0.85rem;
280
+ background: rgba(255, 255, 255, 0.1);
281
+ border: 1px solid var(--border-color);
282
+ min-width: auto;
283
+ }
284
+
285
+ .copy-btn.copied {
286
+ background: var(--success);
287
+ animation: pulse 0.5s ease;
288
+ }
289
+
290
+ .status-indicator {
291
+ display: inline-block;
292
+ width: 8px;
293
+ height: 8px;
294
+ border-radius: 50%;
295
+ margin-left: 0.5rem;
296
+ animation: blink 2s infinite;
297
+ }
298
+
299
+ .status-indicator.success {
300
+ background: var(--success);
301
+ }
302
+
303
+ .status-indicator.fallback {
304
+ background: var(--error);
305
+ }
306
+
307
+ @keyframes fadeInDown {
308
+ from {
309
+ opacity: 0;
310
+ transform: translateY(-30px);
311
+ }
312
+ to {
313
+ opacity: 1;
314
+ transform: translateY(0);
315
+ }
316
+ }
317
+
318
+ @keyframes fadeInUp {
319
+ from {
320
+ opacity: 0;
321
+ transform: translateY(30px);
322
+ }
323
+ to {
324
+ opacity: 1;
325
+ transform: translateY(0);
326
+ }
327
+ }
328
+
329
+ @keyframes slideInLeft {
330
+ from {
331
+ opacity: 0;
332
+ transform: translateX(-20px);
333
+ }
334
+ to {
335
+ opacity: 1;
336
+ transform: translateX(0);
337
+ }
338
+ }
339
+
340
+ @keyframes pulse {
341
+ 0%, 100% {
342
+ transform: scale(1);
343
+ }
344
+ 50% {
345
+ transform: scale(1.1);
346
+ }
347
+ }
348
+
349
+ @keyframes blink {
350
+ 0%, 100% {
351
+ opacity: 1;
352
+ }
353
+ 50% {
354
+ opacity: 0.5;
355
+ }
356
+ }
357
+
358
+ .loading {
359
+ display: none;
360
+ text-align: center;
361
+ padding: 2rem;
362
+ }
363
+
364
+ .loading.show {
365
+ display: block;
366
+ }
367
+
368
+ .spinner {
369
+ width: 50px;
370
+ height: 50px;
371
+ border: 3px solid var(--border-color);
372
+ border-top-color: var(--primary);
373
+ border-radius: 50%;
374
+ margin: 0 auto 1rem;
375
+ animation: spin 1s linear infinite;
376
+ }
377
+
378
+ @keyframes spin {
379
+ to {
380
+ transform: rotate(360deg);
381
+ }
382
+ }
383
+
384
+ @media (max-width: 768px) {
385
+ .container {
386
+ padding: 1rem;
387
+ }
388
+
389
+ h1 {
390
+ font-size: 2rem;
391
+ }
392
+
393
+ .main-card {
394
+ padding: 1.5rem;
395
+ }
396
+
397
+ .button-group {
398
+ flex-direction: column;
399
+ }
400
+
401
+ button {
402
+ width: 100%;
403
+ }
404
+
405
+ .results-header {
406
+ flex-direction: column;
407
+ gap: 1rem;
408
+ align-items: stretch;
409
+ }
410
+ }
411
+ </style>
412
+ </head>
413
+ <body>
414
+ <div class="container">
415
+ <header>
416
+ <h1>Douyin Video Download Link Processor</h1>
417
+ <p class="subtitle">Generate highest-bitrate direct download links</p>
418
+ <p class="credit">Built with <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank">anycoder</a></p>
419
+ </header>
420
+
421
+ <main class="main-card">
422
+ <section class="input-section">
423
+ <label for="urlInput">Enter Douyin Video URLs (one per line):</label>
424
+ <textarea
425
+ id="urlInput"
426
+ placeholder="https://www.douyin.com/video/1234567890123456789&#10;https://v.douyin.com/ABCDEFG/&#10;https://www.douyin.com/user/123456?modal_id=9876543210987654321"
427
+ ></textarea>
428
+ <div class="button-group">
429
+ <button onclick="processUrls()">Process URLs</button>
430
+ <button class="secondary" onclick="clearInput()">Clear</button>
431
+ <button class="secondary" onclick="loadSample()">Load Sample URLs</button>
432
+ </div>
433
+ </section>
434
+
435
+ <div class="loading" id="loading">
436
+ <div class="spinner"></div>
437
+ <p>Processing URLs...</p>
438
+ </div>
439
+
440
+ <section class="results-section" id="resultsSection">
441
+ <div class="results-header">
442
+ <h2 class="results-title">Download Links <span class="status-indicator success"></span></h2>
443
+ <button class="copy-all-btn" onclick="copyAllUrls()">Copy All</button>
444
+ </div>
445
+ <ul class="url-list" id="urlList"></ul>
446
+ </section>
447
+ </main>
448
+ </div>
449
+
450
+ <script>
451
+ // Base64 encoding function
452
+ function base64Encode(str) {
453
+ return btoa(unescape(encodeURIComponent(str)));
454
+ }
455
+
456
+ // URL encoding function
457
+ function urlEncode(str) {
458
+ return encodeURIComponent(str);
459
+ }
460
+
461
+ // Extract modal_id from URL
462
+ function extractModalId(url) {
463
+ const urlObj = new URL(url);
464
+ const modalId = urlObj.searchParams.get('modal_id');
465
+ return modalId;
466
+ }
467
+
468
+ // Normalize target URL
469
+ function normalizeTargetUrl(inputUrl) {
470
+ try {
471
+ const modalId = extractModalId(inputUrl);
472
+ if (modalId) {
473
+ return `https://www.douyin.com/video/${modalId}`;
474
+ }
475
+
476
+ // Extract base path before ? or #
477
+ const urlParts = inputUrl.split(/[?#]/);
478
+ return urlParts[0];
479
+ } catch (error) {
480
+ return inputUrl.split(/[?#]/)[0];
481
+ }
482
+ }
483
+
484
+ // Generate API hash
485
+ function generateApiHash(targetUrl) {
486
+ const base64Url = base64Encode(targetUrl);
487
+ const lengthPlus = targetUrl.length + 1000;
488
+ const base64Aio = base64Encode('aio-dl');
489
+ return base64Url + lengthPlus + base64Aio;
490
+ }
491
+
492
+ // Simulate API response (since we cannot make actual requests)
493
+ function simulateApiResponse(targetUrl) {
494
+ // Mock response with medias array
495
+ const mockResponse = {
496
+ medias: [
497
+ {
498
+ url: `https://mock-download.douyin.com/video/${Date.now()}_1080p.mp4`,
499
+ size: 50480000,
500
+ quality: '1080p'
501
+ },
502
+ {
503
+ url: `https://mock-download.douyin.com/video/${Date.now()}_720p.mp4`,
504
+ size: 25240000,
505
+ quality: '720p'
506
+ },
507
+ {
508
+ url: `https://mock-download.douyin.com/video/${Date.now()}_480p.mp4`,
509
+ size: 12620000,
510
+ quality: '480p'
511
+ }
512
+ ]
513
+ };
514
+ return mockResponse;
515
+ }
516
+
517
+ // Process a single URL
518
+ function processSingleUrl(inputUrl) {
519
+ try {
520
+ // Step a: Normalize target URL
521
+ const targetUrl = normalizeTargetUrl(inputUrl);
522
+
523
+ // Step b: Generate API hash
524
+ const apiHash = generateApiHash(targetUrl);
525
+
526
+ // Step c: Simulate API request (no actual request)
527
+ const requestBody = `url=${urlEncode(targetUrl)}&hash=${apiHash}`;
528
+
529
+ // Step d: Process simulated response
530
+ const simulatedResponse = simulateApiResponse(targetUrl);
531
+
532
+ if (simulatedResponse.medias && simulatedResponse.medias.length > 0) {
533
+ // Sort medias by size in descending order
534
+ const sortedMedias = [...simulatedResponse.medias].sort((a, b) => b.size - a.size);
535
+
536
+ // Step e: Extract primary download link
537
+ return sortedMedias[0].url;
538
+ } else {
539
+ // Step f: Handle errors with fallback
540
+ return `https://snapdouyin.app/#url=${urlEncode(targetUrl)}`;
541
+ }
542
+ } catch (error) {
543
+ // Fallback on any error
544
+ return `https://snapdouyin.app/#url=${urlEncode(inputUrl)}`;
545
+ }
546
+ }
547
+
548
+ // Process all URLs
549
+ function processUrls() {
550
+ const input = document.getElementById('urlInput').value.trim();
551
+ if (!input) {
552
+ alert('Please enter at least one Douyin URL');
553
+ return;
554
+ }
555
+
556
+ const urls = input.split('\n').filter(url => url.trim());
557
+ if (urls.length === 0) {
558
+ alert('Please enter valid URLs');
559
+ return;
560
+ }
561
+
562
+ // Show loading
563
+ document.getElementById('loading').classList.add('show');
564
+ document.getElementById('resultsSection').classList.remove('show');
565
+
566
+ // Simulate processing delay
567
+ setTimeout(() => {
568
+ const results = [];
569
+ urls.forEach(url => {
570
+ const result = processSingleUrl(url.trim());
571
+ results.push(result);
572
+ });
573
+
574
+ // Hide loading and show results
575
+ document.getElementById('loading').classList.remove('show');
576
+ displayResults(results);
577
+ }, 1500);
578
+ }
579
+
580
+ // Display results
581
+ function displayResults(results) {
582
+ const urlList = document.getElementById('urlList');
583
+ urlList.innerHTML = '';
584
+
585
+ results.forEach((url, index) => {
586
+ const li = document.createElement('li');
587
+ li.className = 'url-item';
588
+ li.style.animationDelay = `${index * 0.1}s`;
589
+
590
+ const isFallback = url.includes('snapdouyin.app/#url=');
591
+
592
+ li.innerHTML = `
593
+ <div class="url-content">${url}</div>
594
+ <button class="copy-btn" onclick="copyUrl('${url}', this)">Copy</button>
595
+ <span class="status-indicator ${isFallback ? 'fallback' : 'success'}"></span>
596
+ `;
597
+
598
+ urlList.appendChild(li);
599
+ });
600
+
601
+ document.getElementById('resultsSection').classList.add('show');
602
+ }
603
+
604
+ // Copy single URL
605
+ function copyUrl(url, button) {
606
+ navigator.clipboard.writeText(url).then(() => {
607
+ button.textContent = 'Copied!';
608
+ button.classList.add('copied');
609
+ setTimeout(() => {
610
+ button.textContent = 'Copy';
611
+ button.classList.remove('copied');
612
+ }, 2000);
613
+ });
614
+ }
615
+
616
+ // Copy all URLs
617
+ function copyAllUrls() {
618
+ const urls = Array.from(document.querySelectorAll('.url-content')).map(el => el.textContent);
619
+ const text = urls.map((url, index) => `${index + 1}. ${url}`).join('\n');
620
+
621
+ navigator.clipboard.writeText(text).then(() => {
622
+ const button = document.querySelector('.copy-all-btn');
623
+ const originalText = button.textContent;
624
+ button.textContent = 'Copied All!';
625
+ button.style.background = 'var(--success)';
626
+ setTimeout(() => {
627
+ button.textContent = originalText;
628
+ button.style.background = '';
629
+ }, 2000);
630
+ });
631
+ }
632
+
633
+ // Clear input
634
+ function clearInput() {
635
+ document.getElementById('urlInput').value = '';
636
+ document.getElementById('resultsSection').classList.remove('show');
637
+ }
638
+
639
+ // Load sample URLs
640
+ function loadSample() {
641
+ const sampleUrls = `https://www.douyin.com/video/7123456789012345678
642
+ https://v.douyin.com/ABCDEFG123456/
643
+ https://www.douyin.com/user/987654321?modal_id=7123456789012345678`;
644
+ document.getElementById('urlInput').value = sampleUrls;
645
+ }
646
+
647
+ // Add enter key support for processing
648
+ document.getElementById('urlInput').addEventListener('keydown', function(e) {
649
+ if (e.ctrlKey && e.key === 'Enter') {
650
+ processUrls();
651
+ }
652
+ });
653
+ </script>
654
+ </body>
655
+ </html>