Created
October 7, 2025 08:03
-
-
Save Amr1977/6bb006e2adfbb96b0315cf35342feb22 to your computer and use it in GitHub Desktop.
Downloads all MP3 files from a specific tab (if any) in a reciter page. * Usage: node download_mp3s.js "https://ourquraan.com/shyookh/<reciter>#<tab-id>"
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * Script: download_mp3s.js | |
| * Description: Downloads all MP3 files from a specific tab (if any) in a reciter page. | |
| * Usage: node download_mp3s.js "https://ourquraan.com/shyookh/<reciter>#<tab-id>" | |
| */ | |
| import fs from 'fs'; | |
| import path from 'path'; | |
| import axios from 'axios'; | |
| import * as cheerio from 'cheerio'; | |
| import cliProgress from 'cli-progress'; | |
| const targetUrl = process.argv[2]; | |
| if (!targetUrl) { | |
| console.error("β Usage: node download_mp3s.js <url>"); | |
| process.exit(1); | |
| } | |
| function extractReciterName(url) { | |
| try { | |
| const u = new URL(url); | |
| const decoded = decodeURIComponent(u.pathname); | |
| const parts = decoded.split('/').filter(Boolean); | |
| return parts.pop() || 'Unknown_Reciter'; | |
| } catch { | |
| return 'Unknown_Reciter'; | |
| } | |
| } | |
| function extractTabId(url) { | |
| try { | |
| const u = new URL(url); | |
| return u.hash.replace('#', '').trim() || null; | |
| } catch { | |
| return null; | |
| } | |
| } | |
| const reciterName = extractReciterName(targetUrl); | |
| const tabId = extractTabId(targetUrl); | |
| const baseFolder = path.resolve('./downloads', reciterName); | |
| const downloadDir = tabId | |
| ? path.join(baseFolder, tabId) | |
| : baseFolder; | |
| if (!fs.existsSync(downloadDir)) { | |
| fs.mkdirSync(downloadDir, { recursive: true }); | |
| console.log(`π Created folder: ${downloadDir}`); | |
| } | |
| async function getMp3Links(url) { | |
| console.log(`π Fetching: ${url}`); | |
| const { data } = await axios.get(url, { headers: { 'User-Agent': 'Mozilla/5.0' } }); | |
| const $ = cheerio.load(data); | |
| let links = []; | |
| if (tabId) { | |
| console.log(`π― Looking inside tab: #${tabId}`); | |
| const tabSection = $(`#${tabId}`); | |
| if (tabSection.length === 0) { | |
| console.log(`β οΈ Tab with id "${tabId}" not found.`); | |
| return []; | |
| } | |
| tabSection.find('a[href$=".mp3"]').each((_, el) => { | |
| const href = $(el).attr('href'); | |
| if (href) { | |
| const absoluteUrl = new URL(href, url).href; | |
| if (!links.includes(absoluteUrl)) links.push(absoluteUrl); | |
| } | |
| }); | |
| } else { | |
| $('a[href$=".mp3"]').each((_, el) => { | |
| const href = $(el).attr('href'); | |
| if (href) { | |
| const absoluteUrl = new URL(href, url).href; | |
| if (!links.includes(absoluteUrl)) links.push(absoluteUrl); | |
| } | |
| }); | |
| } | |
| console.log(`π§ Found ${links.length} MP3 links${tabId ? ` in tab #${tabId}` : ''}.\n`); | |
| return links; | |
| } | |
| async function downloadFile(fileUrl) { | |
| const fileName = decodeURIComponent(path.basename(new URL(fileUrl).pathname)); | |
| const filePath = path.join(downloadDir, fileName); | |
| if (fs.existsSync(filePath)) { | |
| console.log(`β© Skipping (already exists): ${fileName}`); | |
| return; | |
| } | |
| const response = await axios.get(fileUrl, { responseType: 'stream' }); | |
| const totalLength = parseInt(response.headers['content-length'] || '0', 10); | |
| console.log(`β¬οΈ Downloading: ${fileName}`); | |
| const progressBar = new cliProgress.SingleBar({ | |
| format: ' {bar} {percentage}% | {value}/{total} KB ', | |
| hideCursor: true, | |
| barCompleteChar: 'β', | |
| barIncompleteChar: 'β' | |
| }, cliProgress.Presets.shades_classic); | |
| if (totalLength > 0) progressBar.start(Math.ceil(totalLength / 1024), 0); | |
| const writer = fs.createWriteStream(filePath); | |
| let downloaded = 0; | |
| response.data.on('data', (chunk) => { | |
| downloaded += chunk.length; | |
| if (totalLength > 0) progressBar.update(Math.ceil(downloaded / 1024)); | |
| }); | |
| response.data.pipe(writer); | |
| return new Promise((resolve, reject) => { | |
| writer.on('finish', () => { | |
| if (totalLength > 0) progressBar.stop(); | |
| console.log(`β Completed: ${fileName}\n`); | |
| resolve(fileName); | |
| }); | |
| writer.on('error', (err) => { | |
| if (totalLength > 0) progressBar.stop(); | |
| reject(err); | |
| }); | |
| }); | |
| } | |
| async function main() { | |
| try { | |
| const mp3Links = await getMp3Links(targetUrl); | |
| if (mp3Links.length === 0) { | |
| console.log("β οΈ No MP3 links found."); | |
| return; | |
| } | |
| for (const link of mp3Links) { | |
| try { | |
| await downloadFile(link); | |
| } catch (err) { | |
| console.error(`β Failed to download: ${link}`, err.message); | |
| } | |
| } | |
| console.log(`\nβ All downloads complete! Saved in: ${downloadDir}`); | |
| } catch (err) { | |
| console.error("β Error:", err.message); | |
| } | |
| } | |
| main(); |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Install dependencies:
npm install axios cheerio cli-progressRun:
where URL is any reciter page/tab in https://ourquraan.com