Skip to content

Commit 37329b9

Browse files
Merge pull request #3 from ytube-downloader/dev
Update fixing mp3
2 parents 848c302 + de9f45a commit 37329b9

File tree

6 files changed

+610
-32
lines changed

6 files changed

+610
-32
lines changed

.env.example

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,4 +68,17 @@ VITE_APP_NAME="${APP_NAME}"
6868
VIDEO_DOWNLOAD_API_KEY=your_api_key_here
6969
VIDEO_DOWNLOAD_API_TIMEOUT=120
7070
VIDEO_DOWNLOAD_API_RETRY=3
71+
VIDEO_DOWNLOAD_API_BASE_URL=https://p.savenow.to/ajax/download.php
7172
VIDEO_DOWNLOAD_API_RATE_LIMIT=100
73+
74+
# Cache settings for video info
75+
CACHE_VIDEO_INFO_TTL=3600
76+
77+
# File upload settings
78+
MAX_AUDIO_DURATION=7200
79+
MAX_VIDEO_SIZE=2048
80+
81+
# Rate limiting
82+
RATE_LIMIT_DOWNLOADS=30
83+
RATE_LIMIT_INFO=120
84+
RATE_LIMIT_BATCH=10

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Laravel 12 Video Downloader Template
22

3-
A modern, responsive video downloader web application built with Laravel 12, based on the original React video downloader template. This application provides a clean, professional interface for downloading videos from popular platforms in various qualities and formats.
3+
A modern, responsive video downloader web application built with Laravel 12. This application provides a clean, professional interface for downloading videos from popular platforms in various qualities and formats.
44

55
## 🎯 Features
66

app/Http/Controllers/DownloaderController.php

Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,4 +341,274 @@ public function downloadClip(Request $request): JsonResponse
341341
], 500);
342342
}
343343
}
344+
345+
346+
347+
/**
348+
* Get video info specifically for audio extraction
349+
*/
350+
public function getAudioInfo(Request $request): JsonResponse
351+
{
352+
$request->validate([
353+
'url' => 'required|url'
354+
]);
355+
356+
try {
357+
$videoInfo = $this->downloadService->getVideoInfo($request->input('url'));
358+
359+
if ($videoInfo['success']) {
360+
// Add audio-specific information
361+
$audioInfo = $videoInfo['video_info'];
362+
$audioInfo['available_audio_formats'] = ['MP3', 'WAV', 'M4A', 'AAC', 'FLAC', 'OGG'];
363+
$audioInfo['audio_qualities'] = [
364+
'96' => '96 kbps (Mobile)',
365+
'128' => '128 kbps (Standard)',
366+
'192' => '192 kbps (High)',
367+
'256' => '256 kbps (Premium)',
368+
'320' => '320 kbps (Maximum)'
369+
];
370+
$audioInfo['estimated_audio_sizes'] = $this->calculateAudioSizes($audioInfo['duration'] ?? '0:00');
371+
372+
return response()->json([
373+
'success' => true,
374+
'audio_info' => $audioInfo
375+
]);
376+
}
377+
378+
return response()->json($videoInfo, 400);
379+
380+
} catch (\Exception $e) {
381+
return response()->json([
382+
'success' => false,
383+
'error' => 'Failed to get audio info: ' . $e->getMessage()
384+
], 400);
385+
}
386+
}
387+
388+
/**
389+
* Download audio in MP3 format
390+
*/
391+
public function downloadMp3(Request $request): JsonResponse
392+
{
393+
$request->validate([
394+
'url' => 'required|url',
395+
'bitrate' => 'required|integer|in:96,128,192,256,320',
396+
'sample_rate' => 'sometimes|integer|in:44100,48000',
397+
'normalize' => 'sometimes|boolean',
398+
'remove_noise' => 'sometimes|boolean'
399+
]);
400+
401+
try {
402+
$url = $request->input('url');
403+
$bitrate = $request->input('bitrate', 192);
404+
$sampleRate = $request->input('sample_rate', 44100);
405+
$normalize = $request->input('normalize', false);
406+
$removeNoise = $request->input('remove_noise', false);
407+
408+
$options = [
409+
'sample_rate' => $sampleRate,
410+
'normalize_audio' => $normalize,
411+
'noise_reduction' => $removeNoise
412+
];
413+
414+
$result = $this->downloadService->extractAudio($url, 'mp3', $bitrate, $options);
415+
416+
if ($result['success']) {
417+
// Create local download record
418+
$download = $this->downloadService->createDownload([
419+
'url' => $url,
420+
'quality' => $bitrate . 'kbps',
421+
'format' => 'mp3'
422+
], $request->ip());
423+
424+
return response()->json([
425+
'success' => true,
426+
'message' => 'MP3 extraction started successfully',
427+
'download_id' => $result['download_id'],
428+
'local_id' => $download->download_id,
429+
'format_name' => 'MP3 Audio',
430+
'quality' => $bitrate . ' kbps',
431+
'info' => $result['info'] ?? null
432+
]);
433+
}
434+
435+
return response()->json($result, 400);
436+
437+
} catch (\Exception $e) {
438+
return response()->json([
439+
'success' => false,
440+
'error' => 'MP3 download failed: ' . $e->getMessage()
441+
], 500);
442+
}
443+
}
444+
445+
/**
446+
* Download audio in various formats
447+
*/
448+
public function downloadAudio(Request $request): JsonResponse
449+
{
450+
$request->validate([
451+
'url' => 'required|url',
452+
'format' => 'required|string|in:mp3,wav,m4a,aac,flac,ogg',
453+
'bitrate' => 'sometimes|integer|in:96,128,192,256,320',
454+
'sample_rate' => 'sometimes|integer|in:44100,48000,96000',
455+
'bit_depth' => 'sometimes|integer|in:16,24,32',
456+
'normalize' => 'sometimes|boolean',
457+
'remove_noise' => 'sometimes|boolean'
458+
]);
459+
460+
try {
461+
$url = $request->input('url');
462+
$format = $request->input('format');
463+
$bitrate = $request->input('bitrate', 192);
464+
$sampleRate = $request->input('sample_rate', 44100);
465+
$bitDepth = $request->input('bit_depth', 16);
466+
$normalize = $request->input('normalize', false);
467+
$removeNoise = $request->input('remove_noise', false);
468+
469+
$options = [
470+
'sample_rate' => $sampleRate,
471+
'bit_depth' => $bitDepth,
472+
'normalize_audio' => $normalize,
473+
'noise_reduction' => $removeNoise
474+
];
475+
476+
// Use appropriate service method based on format
477+
if ($format === 'wav') {
478+
$result = $this->downloadService->extractWAV($url, $bitrate);
479+
} elseif ($format === 'flac') {
480+
$result = $this->downloadService->extractFLAC($url);
481+
} else {
482+
$result = $this->downloadService->extractAudio($url, $format, $bitrate, $options);
483+
}
484+
485+
if ($result['success']) {
486+
// Create local download record
487+
$download = $this->downloadService->createDownload([
488+
'url' => $url,
489+
'quality' => $format === 'flac' ? 'lossless' : $bitrate . 'kbps',
490+
'format' => $format
491+
], $request->ip());
492+
493+
return response()->json([
494+
'success' => true,
495+
'message' => ucfirst($format) . ' extraction started successfully',
496+
'download_id' => $result['download_id'],
497+
'local_id' => $download->download_id,
498+
'format_name' => $this->getFormatDisplayName($format),
499+
'quality' => $format === 'flac' ? 'Lossless' : $bitrate . ' kbps',
500+
'info' => $result['info'] ?? null
501+
]);
502+
}
503+
504+
return response()->json($result, 400);
505+
506+
} catch (\Exception $e) {
507+
return response()->json([
508+
'success' => false,
509+
'error' => 'Audio download failed: ' . $e->getMessage()
510+
], 500);
511+
}
512+
}
513+
514+
/**
515+
* Get detailed audio download progress
516+
*/
517+
public function getAudioDownloadStatus(Request $request): JsonResponse
518+
{
519+
$request->validate([
520+
'download_id' => 'required|string'
521+
]);
522+
523+
try {
524+
$downloadId = $request->input('download_id');
525+
526+
// Get status from external API
527+
$result = $this->downloadService->getDownloadStatus($downloadId);
528+
529+
if ($result['success']) {
530+
$data = $result['data'];
531+
532+
return response()->json([
533+
'success' => true,
534+
'status' => $data['status'] ?? 'processing',
535+
'progress' => $data['progress'] ?? 0,
536+
'download_url' => $data['download_url'] ?? null,
537+
'file_size' => $data['file_size'] ?? null,
538+
'estimated_time_remaining' => $data['eta'] ?? null,
539+
'error' => $data['error'] ?? null
540+
]);
541+
}
542+
543+
return response()->json($result, 400);
544+
545+
} catch (\Exception $e) {
546+
return response()->json([
547+
'success' => false,
548+
'error' => 'Failed to get download status: ' . $e->getMessage()
549+
], 400);
550+
}
551+
}
552+
553+
/**
554+
* Calculate estimated file sizes for different audio formats
555+
*/
556+
private function calculateAudioSizes(string $duration): array
557+
{
558+
// Parse duration (format: "mm:ss" or "h:mm:ss")
559+
$parts = explode(':', $duration);
560+
$seconds = 0;
561+
562+
if (count($parts) == 2) {
563+
$seconds = ($parts[0] * 60) + $parts[1];
564+
} elseif (count($parts) == 3) {
565+
$seconds = ($parts[0] * 3600) + ($parts[1] * 60) + $parts[2];
566+
}
567+
568+
if ($seconds == 0) return [];
569+
570+
$minutes = $seconds / 60;
571+
572+
// Approximate sizes in MB per minute for different audio formats
573+
$sizesPerMinute = [
574+
'mp3_96' => 0.7,
575+
'mp3_128' => 1.0,
576+
'mp3_192' => 1.4,
577+
'mp3_256' => 1.9,
578+
'mp3_320' => 2.4,
579+
'wav' => 10.0,
580+
'flac' => 5.0,
581+
'm4a' => 0.8,
582+
'aac' => 0.9,
583+
'ogg' => 1.1
584+
];
585+
586+
$sizes = [];
587+
foreach ($sizesPerMinute as $format => $sizePerMin) {
588+
$totalMB = $minutes * $sizePerMin;
589+
$sizes[$format] = [
590+
'mb' => round($totalMB, 1),
591+
'formatted' => $totalMB > 1024 ? round($totalMB / 1024, 2) . ' GB' : round($totalMB, 1) . ' MB'
592+
];
593+
}
594+
595+
return $sizes;
596+
}
597+
598+
/**
599+
* Get display name for audio format
600+
*/
601+
private function getFormatDisplayName(string $format): string
602+
{
603+
$displayNames = [
604+
'mp3' => 'MP3 Audio',
605+
'wav' => 'WAV Audio (Uncompressed)',
606+
'm4a' => 'M4A Audio',
607+
'aac' => 'AAC Audio',
608+
'flac' => 'FLAC Audio (Lossless)',
609+
'ogg' => 'OGG Audio'
610+
];
611+
612+
return $displayNames[$format] ?? strtoupper($format) . ' Audio';
613+
}
344614
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
3+
namespace App\Http\Middleware;
4+
5+
use Closure;
6+
use Illuminate\Http\Request;
7+
use Symfony\Component\HttpFoundation\Response;
8+
9+
class ValidateVideoUrl
10+
{
11+
private array $supportedDomains = [
12+
'youtube.com',
13+
'youtu.be',
14+
'vimeo.com',
15+
'dailymotion.com',
16+
'facebook.com',
17+
'instagram.com',
18+
'tiktok.com',
19+
'twitter.com',
20+
'twitch.tv'
21+
];
22+
23+
public function handle(Request $request, Closure $next): Response
24+
{
25+
if ($request->has('url')) {
26+
$url = $request->input('url');
27+
28+
// Basic URL validation
29+
if (!filter_var($url, FILTER_VALIDATE_URL)) {
30+
return response()->json([
31+
'success' => false,
32+
'error' => 'Invalid URL format'
33+
], 400);
34+
}
35+
36+
// Check if domain is supported
37+
$isSupported = false;
38+
foreach ($this->supportedDomains as $domain) {
39+
if (strpos($url, $domain) !== false) {
40+
$isSupported = true;
41+
break;
42+
}
43+
}
44+
45+
if (!$isSupported) {
46+
return response()->json([
47+
'success' => false,
48+
'error' => 'Unsupported platform. Please use a supported video platform.',
49+
'supported_platforms' => $this->supportedDomains
50+
], 400);
51+
}
52+
}
53+
54+
return $next($request);
55+
}
56+
}

0 commit comments

Comments
 (0)