|
|
<!DOCTYPE html> |
|
|
<html lang="en"> |
|
|
<head> |
|
|
<meta charset="UTF-8"> |
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
|
<title>mp3_mp4 - Video Downloader</title> |
|
|
<script src="https://cdn.tailwindcss.com"></script> |
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
|
|
<style> |
|
|
|
|
|
@keyframes fadeIn { |
|
|
from { opacity: 0; } |
|
|
to { opacity: 1; } |
|
|
} |
|
|
|
|
|
@keyframes slideUp { |
|
|
from { transform: translateY(20px); opacity: 0; } |
|
|
to { transform: translateY(0); opacity: 1; } |
|
|
} |
|
|
|
|
|
.fade-in { |
|
|
animation: fadeIn 0.5s ease-out forwards; |
|
|
} |
|
|
|
|
|
.slide-up { |
|
|
animation: slideUp 0.4s ease-out forwards; |
|
|
} |
|
|
|
|
|
|
|
|
::-webkit-scrollbar { |
|
|
width: 6px; |
|
|
} |
|
|
|
|
|
::-webkit-scrollbar-track { |
|
|
background: rgba(0, 0, 0, 0.1); |
|
|
} |
|
|
|
|
|
::-webkit-scrollbar-thumb { |
|
|
background: #4f46e5; |
|
|
border-radius: 3px; |
|
|
} |
|
|
|
|
|
|
|
|
.dark-toggle { |
|
|
transition: all 0.3s ease; |
|
|
} |
|
|
</style> |
|
|
</head> |
|
|
<body class="bg-gray-50 dark:bg-gray-900 text-gray-800 dark:text-gray-200 min-h-screen transition-colors duration-300"> |
|
|
|
|
|
<div class="max-w-md mx-auto min-h-screen flex flex-col overflow-hidden relative" id="app"> |
|
|
|
|
|
<div id="welcome-screen" class="flex flex-col items-center justify-center flex-1 p-6 fade-in"> |
|
|
<div class="text-center slide-up"> |
|
|
<div class="w-24 h-24 bg-indigo-500 rounded-2xl flex items-center justify-center mx-auto mb-6 shadow-lg"> |
|
|
<i class="fas fa-music text-white text-4xl"></i> |
|
|
</div> |
|
|
<h1 class="text-4xl font-bold text-indigo-600 dark:text-indigo-400 mb-2">mp3_mp4</h1> |
|
|
<p class="text-lg text-gray-600 dark:text-gray-300 mb-8">Paste your link and download easily</p> |
|
|
<button onclick="showMainScreen()" class="bg-indigo-600 hover:bg-indigo-700 text-white font-medium py-3 px-8 rounded-full shadow-md transition-all transform hover:scale-105"> |
|
|
Start |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="main-screen" class="flex-1 flex flex-col p-6 hidden"> |
|
|
|
|
|
<div class="flex justify-between items-center mb-6"> |
|
|
<h2 class="text-2xl font-bold text-indigo-600 dark:text-indigo-400">Download Media</h2> |
|
|
<div class="flex items-center space-x-4"> |
|
|
<button onclick="toggleDarkMode()" class="dark-toggle p-2 rounded-full bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600"> |
|
|
<i class="fas fa-moon dark:hidden"></i> |
|
|
<i class="fas fa-sun hidden dark:block"></i> |
|
|
</button> |
|
|
<button onclick="showHistoryScreen()" class="p-2 rounded-full bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600"> |
|
|
<i class="fas fa-history"></i> |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="flex-1 flex flex-col"> |
|
|
<div class="mb-6"> |
|
|
<label for="video-url" class="block text-sm font-medium mb-2">Video URL</label> |
|
|
<div class="relative"> |
|
|
<input type="text" id="video-url" placeholder="Paste YouTube, TikTok, Instagram, etc. link here" |
|
|
class="w-full px-4 py-3 rounded-lg border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition-all"> |
|
|
<button onclick="pasteFromClipboard()" class="absolute right-2 top-1/2 transform -translate-y-1/2 bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 px-3 py-1 rounded-md text-sm transition-colors"> |
|
|
<i class="fas fa-paste mr-1"></i> Paste |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="mb-6"> |
|
|
<label class="block text-sm font-medium mb-2">Download Format</label> |
|
|
<div class="grid grid-cols-2 gap-4"> |
|
|
<button id="mp3-btn" onclick="selectFormat('mp3')" class="format-btn bg-indigo-100 dark:bg-indigo-900 border-2 border-indigo-300 dark:border-indigo-700 py-3 px-4 rounded-lg flex items-center justify-center transition-all hover:bg-indigo-200 dark:hover:bg-indigo-800"> |
|
|
<i class="fas fa-music mr-2"></i> MP3 (Audio) |
|
|
</button> |
|
|
<button id="mp4-btn" onclick="selectFormat('mp4')" class="format-btn bg-gray-100 dark:bg-gray-800 border-2 border-gray-300 dark:border-gray-700 py-3 px-4 rounded-lg flex items-center justify-center transition-all hover:bg-gray-200 dark:hover:bg-gray-700"> |
|
|
<i class="fas fa-video mr-2"></i> MP4 (Video) |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="mt-auto"> |
|
|
<button id="download-btn" onclick="startDownload()" disabled class="w-full bg-indigo-600 hover:bg-indigo-700 text-white font-medium py-3 px-4 rounded-lg shadow-md transition-all disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center"> |
|
|
<i class="fas fa-download mr-2"></i> Download |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="progress-screen" class="flex-1 flex flex-col items-center justify-center p-6 hidden"> |
|
|
<div class="text-center max-w-xs"> |
|
|
<div class="relative w-24 h-24 mb-6"> |
|
|
<div class="absolute inset-0 rounded-full border-4 border-indigo-200 dark:border-indigo-800"></div> |
|
|
<div id="progress-circle" class="absolute inset-0 rounded-full border-4 border-indigo-500 border-t-indigo-500 border-r-indigo-500 animate-spin" style="clip: rect(0, 24px, 24px, 12px);"></div> |
|
|
<div class="absolute inset-0 flex items-center justify-center"> |
|
|
<i id="progress-icon" class="fas fa-download text-indigo-500 text-2xl"></i> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<h3 id="progress-title" class="text-xl font-semibold mb-2">Preparing Download</h3> |
|
|
<p id="progress-message" class="text-gray-600 dark:text-gray-300 mb-6">Validating your link...</p> |
|
|
|
|
|
<div class="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2.5 mb-6"> |
|
|
<div id="progress-bar" class="bg-indigo-600 h-2.5 rounded-full" style="width: 0%"></div> |
|
|
</div> |
|
|
|
|
|
<button onclick="cancelDownload()" class="text-indigo-600 dark:text-indigo-400 font-medium hover:underline"> |
|
|
Cancel |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="history-screen" class="flex-1 flex flex-col p-6 hidden"> |
|
|
<div class="flex justify-between items-center mb-6"> |
|
|
<h2 class="text-2xl font-bold text-indigo-600 dark:text-indigo-400">Download History</h2> |
|
|
<button onclick="showMainScreen()" class="p-2 rounded-full bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600"> |
|
|
<i class="fas fa-arrow-left"></i> |
|
|
</button> |
|
|
</div> |
|
|
|
|
|
<div class="flex-1 overflow-y-auto"> |
|
|
<div id="history-list" class="space-y-3"> |
|
|
|
|
|
<div class="text-center py-10 text-gray-500" id="empty-history"> |
|
|
<i class="fas fa-history text-4xl mb-3 opacity-30"></i> |
|
|
<p>Your download history is empty</p> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="settings-menu" class="absolute inset-0 bg-white dark:bg-gray-800 shadow-lg z-10 hidden flex-col p-6"> |
|
|
<div class="flex justify-between items-center mb-8"> |
|
|
<h2 class="text-2xl font-bold text-indigo-600 dark:text-indigo-400">Settings</h2> |
|
|
<button onclick="hideSettingsMenu()" class="p-2 rounded-full bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600"> |
|
|
<i class="fas fa-times"></i> |
|
|
</button> |
|
|
</div> |
|
|
|
|
|
<div class="space-y-6"> |
|
|
<div> |
|
|
<h3 class="font-medium mb-3">Appearance</h3> |
|
|
<div class="flex items-center justify-between"> |
|
|
<span>Dark Mode</span> |
|
|
<label class="relative inline-flex items-center cursor-pointer"> |
|
|
<input type="checkbox" id="dark-mode-toggle" class="sr-only peer"> |
|
|
<div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-indigo-300 dark:peer-focus:ring-indigo-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-indigo-600"></div> |
|
|
</label> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div> |
|
|
<h3 class="font-medium mb-3">Download Options</h3> |
|
|
<div class="space-y-4"> |
|
|
<div class="flex items-center justify-between"> |
|
|
<span>Default Format</span> |
|
|
<select class="bg-gray-100 dark:bg-gray-700 border border-gray-300 dark:border-gray-600 text-gray-800 dark:text-gray-200 rounded-md px-3 py-1 text-sm"> |
|
|
<option>MP3</option> |
|
|
<option>MP4</option> |
|
|
</select> |
|
|
</div> |
|
|
<div class="flex items-center justify-between"> |
|
|
<span>Video Quality</span> |
|
|
<select class="bg-gray-100 dark:bg-gray-700 border border-gray-300 dark:border-gray-600 text-gray-800 dark:text-gray-200 rounded-md px-3 py-1 text-sm"> |
|
|
<option>720p (Recommended)</option> |
|
|
<option>480p</option> |
|
|
<option>360p</option> |
|
|
</select> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div> |
|
|
<h3 class="font-medium mb-3">About</h3> |
|
|
<p class="text-sm text-gray-600 dark:text-gray-300">App mp3_mp4 - Designed to simplify your downloads</p> |
|
|
<p class="text-sm text-gray-500 mt-2">Version 1.0.0</p> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="mt-auto pt-6"> |
|
|
<button class="w-full bg-indigo-600 hover:bg-indigo-700 text-white font-medium py-3 px-4 rounded-lg shadow-md transition-all"> |
|
|
Save Settings |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<script> |
|
|
|
|
|
let currentFormat = null; |
|
|
let downloadInProgress = false; |
|
|
let downloadHistory = JSON.parse(localStorage.getItem('downloadHistory')) || []; |
|
|
|
|
|
|
|
|
const welcomeScreen = document.getElementById('welcome-screen'); |
|
|
const mainScreen = document.getElementById('main-screen'); |
|
|
const progressScreen = document.getElementById('progress-screen'); |
|
|
const historyScreen = document.getElementById('history-screen'); |
|
|
const settingsMenu = document.getElementById('settings-menu'); |
|
|
const downloadBtn = document.getElementById('download-btn'); |
|
|
const videoUrlInput = document.getElementById('video-url'); |
|
|
const progressBar = document.getElementById('progress-bar'); |
|
|
const progressTitle = document.getElementById('progress-title'); |
|
|
const progressMessage = document.getElementById('progress-message'); |
|
|
const progressCircle = document.getElementById('progress-circle'); |
|
|
const progressIcon = document.getElementById('progress-icon'); |
|
|
const historyList = document.getElementById('history-list'); |
|
|
const emptyHistory = document.getElementById('empty-history'); |
|
|
const darkModeToggle = document.getElementById('dark-mode-toggle'); |
|
|
|
|
|
|
|
|
function initApp() { |
|
|
|
|
|
if (localStorage.getItem('darkMode') === 'true' || |
|
|
(!localStorage.getItem('darkMode') && window.matchMedia('(prefers-color-scheme: dark)').matches)) { |
|
|
document.documentElement.classList.add('dark'); |
|
|
darkModeToggle.checked = true; |
|
|
} |
|
|
|
|
|
|
|
|
renderHistory(); |
|
|
} |
|
|
|
|
|
|
|
|
function showMainScreen() { |
|
|
welcomeScreen.classList.add('hidden'); |
|
|
mainScreen.classList.remove('hidden'); |
|
|
progressScreen.classList.add('hidden'); |
|
|
historyScreen.classList.add('hidden'); |
|
|
settingsMenu.classList.add('hidden'); |
|
|
} |
|
|
|
|
|
function showProgressScreen() { |
|
|
mainScreen.classList.add('hidden'); |
|
|
progressScreen.classList.remove('hidden'); |
|
|
} |
|
|
|
|
|
function showHistoryScreen() { |
|
|
mainScreen.classList.add('hidden'); |
|
|
historyScreen.classList.remove('hidden'); |
|
|
} |
|
|
|
|
|
function showSettingsMenu() { |
|
|
settingsMenu.classList.remove('hidden'); |
|
|
} |
|
|
|
|
|
function hideSettingsMenu() { |
|
|
settingsMenu.classList.add('hidden'); |
|
|
} |
|
|
|
|
|
|
|
|
function selectFormat(format) { |
|
|
currentFormat = format; |
|
|
|
|
|
|
|
|
document.querySelectorAll('.format-btn').forEach(btn => { |
|
|
btn.classList.remove('border-indigo-300', 'dark:border-indigo-700', 'bg-indigo-100', 'dark:bg-indigo-900'); |
|
|
btn.classList.add('border-gray-300', 'dark:border-gray-700', 'bg-gray-100', 'dark:bg-gray-800'); |
|
|
}); |
|
|
|
|
|
const selectedBtn = document.getElementById(`${format}-btn`); |
|
|
selectedBtn.classList.remove('border-gray-300', 'dark:border-gray-700', 'bg-gray-100', 'dark:bg-gray-800'); |
|
|
selectedBtn.classList.add('border-indigo-300', 'dark:border-indigo-700', 'bg-indigo-100', 'dark:bg-indigo-900'); |
|
|
|
|
|
|
|
|
updateDownloadButtonState(); |
|
|
} |
|
|
|
|
|
|
|
|
function updateDownloadButtonState() { |
|
|
const url = videoUrlInput.value.trim(); |
|
|
downloadBtn.disabled = !(url && currentFormat); |
|
|
} |
|
|
|
|
|
|
|
|
function pasteFromClipboard() { |
|
|
navigator.clipboard.readText().then(text => { |
|
|
videoUrlInput.value = text; |
|
|
updateDownloadButtonState(); |
|
|
}).catch(err => { |
|
|
console.error('Failed to read clipboard:', err); |
|
|
|
|
|
videoUrlInput.value = ''; |
|
|
videoUrlInput.placeholder = "Paste not supported, type manually"; |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
function startDownload() { |
|
|
const url = videoUrlInput.value.trim(); |
|
|
if (!url || !currentFormat) return; |
|
|
|
|
|
showProgressScreen(); |
|
|
downloadInProgress = true; |
|
|
|
|
|
|
|
|
simulateDownloadProcess(url, currentFormat); |
|
|
} |
|
|
|
|
|
|
|
|
function cancelDownload() { |
|
|
downloadInProgress = false; |
|
|
progressTitle.textContent = 'Download Cancelled'; |
|
|
progressMessage.textContent = 'The download was cancelled by the user'; |
|
|
progressIcon.className = 'fas fa-times-circle text-red-500 text-2xl'; |
|
|
progressBar.style.width = '0%'; |
|
|
|
|
|
setTimeout(() => { |
|
|
showMainScreen(); |
|
|
}, 2000); |
|
|
} |
|
|
|
|
|
|
|
|
function simulateDownloadProcess(url, format) { |
|
|
|
|
|
progressTitle.textContent = 'Validating Link'; |
|
|
progressMessage.textContent = 'Checking if the URL is valid...'; |
|
|
progressBar.style.width = '10%'; |
|
|
|
|
|
setTimeout(() => { |
|
|
if (!isValidUrl(url)) { |
|
|
downloadInProgress = false; |
|
|
progressTitle.textContent = 'Invalid Link'; |
|
|
progressMessage.textContent = 'Please check the URL and try again'; |
|
|
progressIcon.className = 'fas fa-exclamation-circle text-red-500 text-2xl'; |
|
|
progressBar.style.width = '0%'; |
|
|
|
|
|
setTimeout(() => { |
|
|
showMainScreen(); |
|
|
}, 2000); |
|
|
return; |
|
|
} |
|
|
|
|
|
|
|
|
progressTitle.textContent = 'Downloading'; |
|
|
progressMessage.textContent = `Preparing ${format.toUpperCase()} file...`; |
|
|
progressBar.style.width = '30%'; |
|
|
|
|
|
setTimeout(() => { |
|
|
progressBar.style.width = '60%'; |
|
|
progressMessage.textContent = 'Processing media...'; |
|
|
|
|
|
setTimeout(() => { |
|
|
|
|
|
downloadInProgress = false; |
|
|
progressBar.style.width = '100%'; |
|
|
progressTitle.textContent = 'Download Complete'; |
|
|
progressMessage.textContent = `Your ${format.toUpperCase()} file is ready`; |
|
|
progressIcon.className = 'fas fa-check-circle text-green-500 text-2xl'; |
|
|
|
|
|
|
|
|
addToHistory(url, format); |
|
|
|
|
|
setTimeout(() => { |
|
|
showMainScreen(); |
|
|
progressBar.style.width = '0%'; |
|
|
}, 2000); |
|
|
}, 1500); |
|
|
}, 1500); |
|
|
}, 1500); |
|
|
} |
|
|
|
|
|
|
|
|
function isValidUrl(url) { |
|
|
try { |
|
|
new URL(url); |
|
|
return true; |
|
|
} catch (e) { |
|
|
return false; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function addToHistory(url, format) { |
|
|
const entry = { |
|
|
id: Date.now(), |
|
|
url, |
|
|
format, |
|
|
title: `Downloaded ${format.toUpperCase()} from ${extractDomain(url)}`, |
|
|
date: new Date().toLocaleString(), |
|
|
filename: `media_${Date.now()}.${format}` |
|
|
}; |
|
|
|
|
|
downloadHistory.unshift(entry); |
|
|
localStorage.setItem('downloadHistory', JSON.stringify(downloadHistory)); |
|
|
renderHistory(); |
|
|
} |
|
|
|
|
|
function renderHistory() { |
|
|
if (downloadHistory.length === 0) { |
|
|
emptyHistory.classList.remove('hidden'); |
|
|
return; |
|
|
} |
|
|
|
|
|
emptyHistory.classList.add('hidden'); |
|
|
historyList.innerHTML = ''; |
|
|
|
|
|
downloadHistory.forEach(entry => { |
|
|
const historyItem = document.createElement('div'); |
|
|
historyItem.className = 'bg-white dark:bg-gray-800 p-4 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700'; |
|
|
historyItem.innerHTML = ` |
|
|
<div class="flex justify-between items-start mb-2"> |
|
|
<h3 class="font-medium truncate">${entry.title}</h3> |
|
|
<span class="text-xs text-gray-500 ml-2 whitespace-nowrap">${entry.date}</span> |
|
|
</div> |
|
|
<div class="flex items-center justify-between text-sm"> |
|
|
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-indigo-100 dark:bg-indigo-900 text-indigo-800 dark:text-indigo-200"> |
|
|
${entry.format.toUpperCase()} |
|
|
</span> |
|
|
<div class="space-x-2"> |
|
|
<button onclick="playMedia('${entry.filename}')" class="text-indigo-600 dark:text-indigo-400 hover:text-indigo-800 dark:hover:text-indigo-300"> |
|
|
<i class="fas fa-play"></i> Play |
|
|
</button> |
|
|
<button onclick="showInFolder('${entry.filename}')" class="text-gray-600 dark:text-gray-400 hover:text-gray-800 dark:hover:text-gray-300"> |
|
|
<i class="fas fa-folder-open"></i> Folder |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
`; |
|
|
historyList.appendChild(historyItem); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
function playMedia(filename) { |
|
|
alert(`In a real app, this would play: ${filename}`); |
|
|
} |
|
|
|
|
|
function showInFolder(filename) { |
|
|
alert(`In a real app, this would open folder containing: ${filename}`); |
|
|
} |
|
|
|
|
|
|
|
|
function extractDomain(url) { |
|
|
try { |
|
|
const domain = new URL(url).hostname.replace('www.', ''); |
|
|
return domain.split('.')[0]; |
|
|
} catch (e) { |
|
|
return 'unknown'; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function toggleDarkMode() { |
|
|
const isDark = document.documentElement.classList.toggle('dark'); |
|
|
localStorage.setItem('darkMode', isDark); |
|
|
darkModeToggle.checked = isDark; |
|
|
} |
|
|
|
|
|
|
|
|
videoUrlInput.addEventListener('input', updateDownloadButtonState); |
|
|
darkModeToggle.addEventListener('change', toggleDarkMode); |
|
|
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', initApp); |
|
|
</script> |
|
|
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=AllBajo/mp3-mp4" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
|
|
</html> |