jebin2 commited on
Commit
a3c2acf
Β·
1 Parent(s): 9c32a2c

rm unwnated

Browse files
Files changed (3) hide show
  1. .gitignore +2 -1
  2. index.html +60 -58
  3. main.py +9 -14
.gitignore CHANGED
@@ -1,3 +1,4 @@
1
  venv
2
  .env
3
- image/__pycache__
 
 
1
  venv
2
  .env
3
+ image/__pycache__
4
+ extracted_images/
index.html CHANGED
@@ -1,5 +1,6 @@
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">
@@ -23,7 +24,7 @@
23
  margin: 0 auto;
24
  background: white;
25
  border-radius: 20px;
26
- box-shadow: 0 20px 40px rgba(0,0,0,0.1);
27
  overflow: hidden;
28
  }
29
 
@@ -67,7 +68,7 @@
67
  .search-box:focus {
68
  outline: none;
69
  background: white;
70
- box-shadow: 0 5px 20px rgba(0,0,0,0.1);
71
  }
72
 
73
  .search-icon {
@@ -100,7 +101,7 @@
100
  .category-dropdown:focus {
101
  outline: none;
102
  background: white;
103
- box-shadow: 0 5px 20px rgba(0,0,0,0.1);
104
  }
105
 
106
  .content {
@@ -142,7 +143,7 @@
142
 
143
  .feature-card:hover {
144
  transform: translateY(-5px);
145
- box-shadow: 0 15px 40px rgba(0,0,0,0.1);
146
  }
147
 
148
  .feature-card:hover::before {
@@ -213,9 +214,20 @@
213
  }
214
 
215
  @keyframes pulse {
216
- 0% { transform: scale(1); opacity: 1; }
217
- 50% { transform: scale(1.2); opacity: 0.7; }
218
- 100% { transform: scale(1); opacity: 1; }
 
 
 
 
 
 
 
 
 
 
 
219
  }
220
 
221
  .feature-tags {
@@ -280,29 +292,31 @@
280
  .header h1 {
281
  font-size: 2rem;
282
  }
283
-
284
  .header p {
285
  font-size: 1rem;
286
  }
287
-
288
  .content {
289
  padding: 20px;
290
  }
291
-
292
  .features-grid {
293
  grid-template-columns: 1fr;
294
  }
295
  }
296
  </style>
297
  </head>
 
298
  <body>
299
  <div class="container">
300
  <div class="header">
301
  <h1>πŸ› οΈ Tools Collection</h1>
302
  <p>Simple, fast, and reliable utility tools for your daily needs</p>
303
-
304
  <div class="search-container">
305
- <input type="text" id="search-box" class="search-box" placeholder="Search tools... (e.g., image, pdf, convert)">
 
306
  <span class="search-icon">πŸ”</span>
307
  </div>
308
 
@@ -317,7 +331,7 @@
317
  <div class="features-grid" id="features-grid">
318
  <!-- Features will be populated by JavaScript -->
319
  </div>
320
-
321
  <div class="no-results" id="no-results" style="display: none;">
322
  <div class="icon">πŸ”</div>
323
  <h3>No tools found</h3>
@@ -331,14 +345,14 @@
331
  let expandedFeatures = [];
332
  let currentCategory = '';
333
  let currentSearch = '';
334
-
335
  // Load features on page load
336
  document.addEventListener('DOMContentLoaded', async () => {
337
  await loadFeatures();
338
  setupSearch();
339
  setupCategoryFilter();
340
  });
341
-
342
  async function loadFeatures() {
343
  try {
344
  const response = await fetch('/api/features');
@@ -375,33 +389,24 @@
375
  "tags": ["pdf", "merge", "convert", "images", "document"],
376
  "coming_soon": true
377
  },
378
- "audio": {
379
- "name": "Audio Tools",
380
- "description": "Convert audio formats and compress audio files",
381
- "icon": "🎡",
382
- "features": ["convert_audio", "compress_audio"],
383
- "folder": "audio",
384
- "tags": ["audio", "music", "convert", "compress", "mp3", "wav"],
385
- "coming_soon": true
386
- },
387
  "video": {
388
  "name": "Video Tools",
389
- "description": "Basic video editing and format conversion",
390
  "icon": "🎬",
391
- "features": ["convert_video", "compress_video"],
392
  "folder": "video",
393
- "tags": ["video", "convert", "compress", "mp4", "avi", "editing"],
394
- "coming_soon": true
395
  }
396
  };
397
  expandFeatures();
398
  populateCategories();
399
  displayFeatures(expandedFeatures);
400
  }
401
-
402
  function expandFeatures() {
403
  expandedFeatures = [];
404
-
405
  Object.entries(allFeatures).forEach(([categoryKey, categoryData]) => {
406
  // Add the main category
407
  // expandedFeatures.push({
@@ -415,7 +420,7 @@
415
  // category: categoryKey,
416
  // type: 'category'
417
  // });
418
-
419
  // Add individual features
420
  if (categoryData.features && categoryData.features.length > 0) {
421
  categoryData.features.forEach(feature => {
@@ -434,13 +439,13 @@
434
  }
435
  });
436
  }
437
-
438
  function formatFeatureName(feature) {
439
  return feature.split('_')
440
  .map(word => word.charAt(0).toUpperCase() + word.slice(1))
441
  .join(' ');
442
  }
443
-
444
  function getFeatureDescription(category, feature) {
445
  const descriptions = {
446
  'image': {
@@ -450,23 +455,19 @@
450
  'pdf': {
451
  'images_to_pdf': 'Convert multiple images into a single PDF'
452
  },
453
- 'audio': {
454
- 'convert_audio': 'Convert between different audio formats',
455
- 'compress_audio': 'Reduce audio file size while maintaining quality'
456
- },
457
  'video': {
458
- 'convert_video': 'Convert between different video formats',
459
- 'compress_video': 'Reduce video file size while maintaining quality'
460
  }
461
  };
462
-
463
  return descriptions[category]?.[feature] || `${formatFeatureName(feature)} functionality`;
464
  }
465
-
466
  function populateCategories() {
467
  const dropdown = document.getElementById('category-dropdown');
468
  const categories = Object.keys(allFeatures);
469
-
470
  categories.forEach(category => {
471
  const option = document.createElement('option');
472
  option.value = category;
@@ -474,24 +475,24 @@
474
  dropdown.appendChild(option);
475
  });
476
  }
477
-
478
  function displayFeatures(features) {
479
  const grid = document.getElementById('features-grid');
480
  const noResults = document.getElementById('no-results');
481
-
482
  if (features.length === 0) {
483
  grid.style.display = 'none';
484
  noResults.style.display = 'block';
485
  return;
486
  }
487
-
488
  grid.style.display = 'grid';
489
  noResults.style.display = 'none';
490
-
491
  grid.innerHTML = features.map((feature) => {
492
  const isComingSoon = feature.coming_soon || false;
493
  const href = isComingSoon ? '#' : `/${feature.path}`;
494
-
495
  return `
496
  <a href="${href}" class="feature-card ${isComingSoon ? 'coming-soon' : ''}"
497
  ${isComingSoon ? 'onclick="return false;"' : ''}>
@@ -519,11 +520,11 @@
519
  `;
520
  }).join('');
521
  }
522
-
523
  function setupSearch() {
524
  const searchBox = document.getElementById('search-box');
525
  let debounceTimer;
526
-
527
  searchBox.addEventListener('input', (e) => {
528
  clearTimeout(debounceTimer);
529
  debounceTimer = setTimeout(() => {
@@ -532,38 +533,39 @@
532
  }, 300);
533
  });
534
  }
535
-
536
  function setupCategoryFilter() {
537
  const dropdown = document.getElementById('category-dropdown');
538
-
539
  dropdown.addEventListener('change', (e) => {
540
  currentCategory = e.target.value;
541
  filterFeatures();
542
  });
543
  }
544
-
545
  function filterFeatures() {
546
  let filteredFeatures = expandedFeatures;
547
-
548
  // Filter by category
549
  if (currentCategory) {
550
- filteredFeatures = filteredFeatures.filter(feature =>
551
  feature.category === currentCategory
552
  );
553
- }
554
-
555
  // Filter by search term
556
  if (currentSearch) {
557
- filteredFeatures = filteredFeatures.filter(feature =>
558
  feature.name.toLowerCase().includes(currentSearch) ||
559
  feature.description.toLowerCase().includes(currentSearch) ||
560
  feature.path.toLowerCase().includes(currentSearch) ||
561
  feature.tags.some(tag => tag.toLowerCase().includes(currentSearch))
562
  );
563
  }
564
-
565
  displayFeatures(filteredFeatures);
566
  }
567
  </script>
568
  </body>
 
569
  </html>
 
1
  <!DOCTYPE html>
2
  <html lang="en">
3
+
4
  <head>
5
  <meta charset="UTF-8">
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
 
24
  margin: 0 auto;
25
  background: white;
26
  border-radius: 20px;
27
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
28
  overflow: hidden;
29
  }
30
 
 
68
  .search-box:focus {
69
  outline: none;
70
  background: white;
71
+ box-shadow: 0 5px 20px rgba(0, 0, 0, 0.1);
72
  }
73
 
74
  .search-icon {
 
101
  .category-dropdown:focus {
102
  outline: none;
103
  background: white;
104
+ box-shadow: 0 5px 20px rgba(0, 0, 0, 0.1);
105
  }
106
 
107
  .content {
 
143
 
144
  .feature-card:hover {
145
  transform: translateY(-5px);
146
+ box-shadow: 0 15px 40px rgba(0, 0, 0, 0.1);
147
  }
148
 
149
  .feature-card:hover::before {
 
214
  }
215
 
216
  @keyframes pulse {
217
+ 0% {
218
+ transform: scale(1);
219
+ opacity: 1;
220
+ }
221
+
222
+ 50% {
223
+ transform: scale(1.2);
224
+ opacity: 0.7;
225
+ }
226
+
227
+ 100% {
228
+ transform: scale(1);
229
+ opacity: 1;
230
+ }
231
  }
232
 
233
  .feature-tags {
 
292
  .header h1 {
293
  font-size: 2rem;
294
  }
295
+
296
  .header p {
297
  font-size: 1rem;
298
  }
299
+
300
  .content {
301
  padding: 20px;
302
  }
303
+
304
  .features-grid {
305
  grid-template-columns: 1fr;
306
  }
307
  }
308
  </style>
309
  </head>
310
+
311
  <body>
312
  <div class="container">
313
  <div class="header">
314
  <h1>πŸ› οΈ Tools Collection</h1>
315
  <p>Simple, fast, and reliable utility tools for your daily needs</p>
316
+
317
  <div class="search-container">
318
+ <input type="text" id="search-box" class="search-box"
319
+ placeholder="Search tools... (e.g., image, pdf, convert)">
320
  <span class="search-icon">πŸ”</span>
321
  </div>
322
 
 
331
  <div class="features-grid" id="features-grid">
332
  <!-- Features will be populated by JavaScript -->
333
  </div>
334
+
335
  <div class="no-results" id="no-results" style="display: none;">
336
  <div class="icon">πŸ”</div>
337
  <h3>No tools found</h3>
 
345
  let expandedFeatures = [];
346
  let currentCategory = '';
347
  let currentSearch = '';
348
+
349
  // Load features on page load
350
  document.addEventListener('DOMContentLoaded', async () => {
351
  await loadFeatures();
352
  setupSearch();
353
  setupCategoryFilter();
354
  });
355
+
356
  async function loadFeatures() {
357
  try {
358
  const response = await fetch('/api/features');
 
389
  "tags": ["pdf", "merge", "convert", "images", "document"],
390
  "coming_soon": true
391
  },
392
+
 
 
 
 
 
 
 
 
393
  "video": {
394
  "name": "Video Tools",
395
+ "description": "Video player and media tools",
396
  "icon": "🎬",
397
+ "features": ["player"],
398
  "folder": "video",
399
+ "tags": ["video", "player", "media", "watch"]
 
400
  }
401
  };
402
  expandFeatures();
403
  populateCategories();
404
  displayFeatures(expandedFeatures);
405
  }
406
+
407
  function expandFeatures() {
408
  expandedFeatures = [];
409
+
410
  Object.entries(allFeatures).forEach(([categoryKey, categoryData]) => {
411
  // Add the main category
412
  // expandedFeatures.push({
 
420
  // category: categoryKey,
421
  // type: 'category'
422
  // });
423
+
424
  // Add individual features
425
  if (categoryData.features && categoryData.features.length > 0) {
426
  categoryData.features.forEach(feature => {
 
439
  }
440
  });
441
  }
442
+
443
  function formatFeatureName(feature) {
444
  return feature.split('_')
445
  .map(word => word.charAt(0).toUpperCase() + word.slice(1))
446
  .join(' ');
447
  }
448
+
449
  function getFeatureDescription(category, feature) {
450
  const descriptions = {
451
  'image': {
 
455
  'pdf': {
456
  'images_to_pdf': 'Convert multiple images into a single PDF'
457
  },
458
+
 
 
 
459
  'video': {
460
+ 'player': 'Video player for watching media content'
 
461
  }
462
  };
463
+
464
  return descriptions[category]?.[feature] || `${formatFeatureName(feature)} functionality`;
465
  }
466
+
467
  function populateCategories() {
468
  const dropdown = document.getElementById('category-dropdown');
469
  const categories = Object.keys(allFeatures);
470
+
471
  categories.forEach(category => {
472
  const option = document.createElement('option');
473
  option.value = category;
 
475
  dropdown.appendChild(option);
476
  });
477
  }
478
+
479
  function displayFeatures(features) {
480
  const grid = document.getElementById('features-grid');
481
  const noResults = document.getElementById('no-results');
482
+
483
  if (features.length === 0) {
484
  grid.style.display = 'none';
485
  noResults.style.display = 'block';
486
  return;
487
  }
488
+
489
  grid.style.display = 'grid';
490
  noResults.style.display = 'none';
491
+
492
  grid.innerHTML = features.map((feature) => {
493
  const isComingSoon = feature.coming_soon || false;
494
  const href = isComingSoon ? '#' : `/${feature.path}`;
495
+
496
  return `
497
  <a href="${href}" class="feature-card ${isComingSoon ? 'coming-soon' : ''}"
498
  ${isComingSoon ? 'onclick="return false;"' : ''}>
 
520
  `;
521
  }).join('');
522
  }
523
+
524
  function setupSearch() {
525
  const searchBox = document.getElementById('search-box');
526
  let debounceTimer;
527
+
528
  searchBox.addEventListener('input', (e) => {
529
  clearTimeout(debounceTimer);
530
  debounceTimer = setTimeout(() => {
 
533
  }, 300);
534
  });
535
  }
536
+
537
  function setupCategoryFilter() {
538
  const dropdown = document.getElementById('category-dropdown');
539
+
540
  dropdown.addEventListener('change', (e) => {
541
  currentCategory = e.target.value;
542
  filterFeatures();
543
  });
544
  }
545
+
546
  function filterFeatures() {
547
  let filteredFeatures = expandedFeatures;
548
+
549
  // Filter by category
550
  if (currentCategory) {
551
+ filteredFeatures = filteredFeatures.filter(feature =>
552
  feature.category === currentCategory
553
  );
554
+ }
555
+
556
  // Filter by search term
557
  if (currentSearch) {
558
+ filteredFeatures = filteredFeatures.filter(feature =>
559
  feature.name.toLowerCase().includes(currentSearch) ||
560
  feature.description.toLowerCase().includes(currentSearch) ||
561
  feature.path.toLowerCase().includes(currentSearch) ||
562
  feature.tags.some(tag => tag.toLowerCase().includes(currentSearch))
563
  );
564
  }
565
+
566
  displayFeatures(filteredFeatures);
567
  }
568
  </script>
569
  </body>
570
+
571
  </html>
main.py CHANGED
@@ -1,5 +1,5 @@
1
  from fastapi import FastAPI, Request, File, UploadFile, HTTPException, Form, Query
2
- from fastapi.responses import HTMLResponse, JSONResponse, FileResponse
3
  from fastapi.staticfiles import StaticFiles
4
  from jinja2 import Environment, FileSystemLoader, select_autoescape
5
  from custom_logger import logger_config
@@ -41,23 +41,14 @@ FEATURES = {
41
  "tags": ["pdf", "merge", "convert", "images", "document"],
42
  "coming_soon": True
43
  },
44
- "audio": {
45
- "name": "Audio Tools",
46
- "description": "Convert audio formats and compress audio files",
47
- "icon": "🎡",
48
- "features": ["convert_audio", "compress_audio"],
49
- "folder": "audio",
50
- "tags": ["audio", "music", "convert", "compress", "mp3", "wav"],
51
- "coming_soon": True
52
- },
53
  "video": {
54
  "name": "Video Tools",
55
- "description": "Basic video editing and format conversion",
56
  "icon": "🎬",
57
- "features": ["convert_video", "compress_video"],
58
  "folder": "video",
59
- "tags": ["video", "convert", "compress", "mp4", "avi", "editing"],
60
- "coming_soon": True
61
  }
62
  }
63
 
@@ -86,6 +77,10 @@ async def image_tools(request: Request):
86
  html_content = template.render(request=request)
87
  return HTMLResponse(content=html_content)
88
 
 
 
 
 
89
  @app.post("/image/upload")
90
  async def upload_image(
91
  id: str = Form(...),
 
1
  from fastapi import FastAPI, Request, File, UploadFile, HTTPException, Form, Query
2
+ from fastapi.responses import HTMLResponse, JSONResponse, FileResponse, RedirectResponse
3
  from fastapi.staticfiles import StaticFiles
4
  from jinja2 import Environment, FileSystemLoader, select_autoescape
5
  from custom_logger import logger_config
 
41
  "tags": ["pdf", "merge", "convert", "images", "document"],
42
  "coming_soon": True
43
  },
44
+
 
 
 
 
 
 
 
 
45
  "video": {
46
  "name": "Video Tools",
47
+ "description": "Video player and media tools",
48
  "icon": "🎬",
49
+ "features": ["player"],
50
  "folder": "video",
51
+ "tags": ["video", "player", "media", "watch"]
 
52
  }
53
  }
54
 
 
77
  html_content = template.render(request=request)
78
  return HTMLResponse(content=html_content)
79
 
80
+ @app.get("/video/player")
81
+ async def video_player():
82
+ return RedirectResponse(url="https://jebin2.github.io/JellyJump/player.html", status_code=302)
83
+
84
  @app.post("/image/upload")
85
  async def upload_image(
86
  id: str = Form(...),