From 166426a54a135f558f9665188cee2c50ea8a3f7f Mon Sep 17 00:00:00 2001 From: Joshua Pohl Date: Sat, 25 Apr 2020 14:16:55 -0500 Subject: [PATCH] feat: add initial download script --- bin/bin.js | 136 +++++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 8 +-- 2 files changed, 140 insertions(+), 4 deletions(-) create mode 100644 bin/bin.js diff --git a/bin/bin.js b/bin/bin.js new file mode 100644 index 0000000..d2605ef --- /dev/null +++ b/bin/bin.js @@ -0,0 +1,136 @@ +#!/usr/bin/env node + +let _url = require("url"); +let path = require("path"); +let fs = require("fs"); +let commander = require("commander"); +let rssParser = require("rss-parser"); +let got = require("got"); +let filenamify = require("filenamify"); +let dayjs = require("dayjs"); +let { version } = require("../package.json"); + +let parser = new rssParser(); + +commander + .version(version) + .option("--url ", "url to podcast rss feed") + .option("--info", "print retrieved podcast info instead of downloading") + .option("--out-dir ", "specify output directory", "./") + .parse(process.argv); + +let BYTES_IN_MB = 1000000; +let currentProgressLine = ""; +let printProgress = ({ percent, total }) => { + let percentRounded = (percent * 100).toFixed(0); + let line = `downloading... ${percentRounded}%`; + + if (total) { + let totalMBs = total / BYTES_IN_MB; + let roundedTotalMbs = totalMBs.toFixed(2); + line += ` of ${roundedTotalMbs} MB`; + } + + if (line !== currentProgressLine) { + process.stdout.clearLine(); + process.stdout.cursorTo(0); + process.stdout.write(line); + currentProgressLine = line; + } +}; + +let endPrintProgress = () => { + process.stdout.write("\n\n"); +}; + +let downloadPodcast = async ({ url, outputPath }) => { + return new Promise((resolve) => { + got + .stream(url) + .on("downloadProgress", (progress) => { + printProgress(progress); + }) + .on("end", () => { + endPrintProgress(); + resolve(); + }) + .pipe(fs.createWriteStream(outputPath)); + }); +}; + +let getUrlExt = (url) => { + let pathname = _url.parse(url).pathname; + let ext = path.extname(pathname); + return ext; +}; + +let VALID_AUDIO_TYPES = [".mp3", ".aac", ".m4a", ".wav", ".ogg", ".flac"]; +let isAudioUrl = (url) => { + let ext = getUrlExt(url); + return VALID_AUDIO_TYPES.includes(ext); +}; + +let getDownloadUrl = ({ enclosure, link }) => { + if (link && isAudioUrl(link)) { + return link; + } + + if (enclosure && isAudioUrl(enclosure.url)) { + return enclosure.url; + } + + return null; +}; + +let main = async () => { + let feed = await parser.parseURL(commander.url); + + if (commander.info) { + console.log(`Title: ${feed.title}`); + console.log(`Description: ${feed.description}`); + console.log(`Total Episodes: ${feed.items.length}`); + return; + } + + if (feed.items.length === 0) { + console.log("No episodes found to download"); + return; + } + + console.log(`Starting download of ${feed.items.length} items`); + let counter = 1; + for (let item of feed.items) { + let { enclosure, link, title, pubDate } = item; + + let url = getDownloadUrl({ enclosure, link }); + + if (!url) { + console.error("Unable to find donwload URL. Skipping"); + break; + } + + let fileName = pubDate + ? `${dayjs(new Date(pubDate)).format("YYYYMMDD")}-${title}` + : title; + let safeFilename = filenamify(fileName, { replacement: "_" }); + let fileExt = getUrlExt(url); + let outputPath = path.resolve( + process.cwd(), + commander.outDir, + `${safeFilename}${fileExt}` + ); + + console.log(`Title: ${title}`); + console.log(`URL: ${url}`); + console.log(`${counter} of ${feed.items.length}`); + + await downloadPodcast({ + outputPath, + url, + }); + + counter += 1; + } +}; + +main(); diff --git a/package.json b/package.json index 9099086..660e33b 100644 --- a/package.json +++ b/package.json @@ -4,13 +4,12 @@ "description": "A CLI for downloading podcasts.", "main": "./bin/bin.js", "scripts": { - "lint": "eslint --ext .js", + "lint": "eslint ./bin", "relase": "standard-version" }, "lint-staged": { "*.{js,json,md}": [ - "prettier --write", - "git add" + "prettier --write" ] }, "husky": { @@ -43,6 +42,7 @@ "commander": "^5.1.0", "dayjs": "^1.8.25", "filenamify": "^4.1.0", - "got": "^11.0.2" + "got": "^11.0.2", + "rss-parser": "^3.7.6" } }