diff options
author | Florrie <towerofni@gmail.com> | 2017-08-03 03:20:23 +0400 |
---|---|---|
committer | Florrie <towerofni@gmail.com> | 2017-08-03 03:20:23 +0400 |
commit | 63ee18f61991cfcd10e42871d57eccd53fa51828 (patch) | |
tree | b07bbe3aa8b4358a77758783638b447465a850fe /src | |
parent | b94845e008931bfaee56b91db1f892af7a6d339f (diff) | |
parent | 70462b74635da8eb92c10d51d1a8a1fe06ceb9f1 (diff) |
Merge branch 'master' of https://github.com/liam4/http-music
Diffstat (limited to 'src')
-rwxr-xr-x | src/cli.js | 29 | ||||
-rwxr-xr-x | src/crawl-http.js | 2 | ||||
-rwxr-xr-x | src/crawl-itunes.js | 32 | ||||
-rwxr-xr-x | src/crawl-local.js | 2 | ||||
-rw-r--r-- | src/crawl-youtube.js | 2 | ||||
-rw-r--r-- | src/crawlers.js | 13 | ||||
-rw-r--r-- | src/downloaders.js | 10 | ||||
-rw-r--r-- | src/loop-play.js | 22 | ||||
-rw-r--r-- | src/playlist-utils.js | 10 | ||||
-rw-r--r-- | src/smart-playlist.js | 49 |
10 files changed, 142 insertions, 29 deletions
diff --git a/src/cli.js b/src/cli.js index 4bc64ab..9b21395 100755 --- a/src/cli.js +++ b/src/cli.js @@ -4,6 +4,8 @@ // maxlistenersexceededwarning. process.on('warning', e => console.warn(e.stack)) +const { getCrawlerByName } = require('./crawlers') + async function main(args) { let script @@ -13,18 +15,21 @@ async function main(args) { return } - switch (args[0]) { - case 'play': script = require('./play'); break - case 'crawl-http': script = require('./crawl-http'); break - case 'crawl-local': script = require('./crawl-local'); break - case 'crawl-itunes': script = require('./crawl-itunes'); break - case 'crawl-youtube': script = require('./crawl-youtube'); break - case 'download-playlist': script = require('./download-playlist'); break - - default: - console.error(`Invalid command "${args[0]}" provided.`) - console.error("Try 'man http-music'?") - return + const module = getCrawlerByName(args[0]) + + if (module) { + script = module.main + } else { + switch (args[0]) { + case 'play': script = require('./play'); break + case 'download-playlist': script = require('./download-playlist'); break + case 'smart-playlist': script = require('./smart-playlist'); break + + default: + console.error(`Invalid command "${args[0]}" provided.`) + console.error("Try 'man http-music'?") + return + } } await script(args.slice(1)) diff --git a/src/crawl-http.js b/src/crawl-http.js index 7308f3f..76f3941 100755 --- a/src/crawl-http.js +++ b/src/crawl-http.js @@ -195,7 +195,7 @@ async function main(args) { console.log(JSON.stringify(downloadedPlaylist, null, 2)) } -module.exports = main +module.exports = {main, crawl} if (require.main === module) { main(process.argv.slice(2)) diff --git a/src/crawl-itunes.js b/src/crawl-itunes.js index 6060ffa..e6b63b3 100755 --- a/src/crawl-itunes.js +++ b/src/crawl-itunes.js @@ -30,7 +30,23 @@ function findChild(grouplike, name) { return grouplike.items.find(x => x.name === name) } -async function crawl(libraryXML) { +let NO_LIBRARY_SYMBOL = Symbol('No library') + +async function crawl( + libraryPath = `${process.env.HOME}/Music/iTunes/iTunes Music Library.xml` +) { + let libraryXML + + try { + libraryXML = await readFile(libraryPath) + } catch (err) { + if (err.code === 'ENOENT') { + throw NO_LIBRARY_SYMBOL + } else { + throw err + } + } + const document = new xmldoc.XmlDocument(libraryXML) const libraryDict = document.children.find(child => child.name === 'dict') @@ -94,16 +110,12 @@ async function crawl(libraryXML) { } async function main(args) { - const libraryPath = args[0] || ( - `${process.env.HOME}/Music/iTunes/iTunes Music Library.xml` - ) - - let library + let playlist try { - library = await readFile(libraryPath) + playlist = await crawl(args[0]) } catch(err) { - if (err.code === 'ENOENT') { + if (err === NO_LIBRARY_SYMBOL) { console.error( "It looks like you aren't sharing the iTunes Library XML file." ) @@ -125,12 +137,10 @@ async function main(args) { } } - const playlist = await crawl(library) - console.log(JSON.stringify(playlist, null, 2)) } -module.exports = main +module.exports = {main, crawl} if (require.main === module) { main(process.argv.slice(2)) diff --git a/src/crawl-local.js b/src/crawl-local.js index 629e015..d4176ed 100755 --- a/src/crawl-local.js +++ b/src/crawl-local.js @@ -41,7 +41,7 @@ async function main(args) { } } -module.exports = main +module.exports = {main, crawl} if (require.main === module) { main(process.argv.slice(2)) diff --git a/src/crawl-youtube.js b/src/crawl-youtube.js index 823fef7..4b4c66c 100644 --- a/src/crawl-youtube.js +++ b/src/crawl-youtube.js @@ -41,7 +41,7 @@ async function main(args) { } } -module.exports = main +module.exports = {main, crawl} if (require.main === module) { main(process.argv.slice(2)) diff --git a/src/crawlers.js b/src/crawlers.js new file mode 100644 index 0000000..5ad7fb4 --- /dev/null +++ b/src/crawlers.js @@ -0,0 +1,13 @@ +'use strict' + +module.exports = { + getCrawlerByName: function(name) { + switch (name) { + case 'crawl-http': return require('./crawl-http') + case 'crawl-local': return require('./crawl-local') + case 'crawl-itunes': return require('./crawl-itunes') + case 'crawl-youtube': return require('./crawl-youtube') + default: return null + } + } +} diff --git a/src/downloaders.js b/src/downloaders.js index c3dc43d..b9cc33d 100644 --- a/src/downloaders.js +++ b/src/downloaders.js @@ -107,7 +107,7 @@ function makeConverterDownloader(downloader, type) { const inFile = await downloader(arg) const base = path.basename(inFile, path.extname(inFile)) const tempDir = tempy.directory() - const outFile = tempDir + base + '.' + type + const outFile = `${tempDir}/${base}.${type}` await promisifyProcess(spawn('avconv', ['-i', inFile, outFile]), false) @@ -122,6 +122,14 @@ module.exports = { makePowerfulDownloader, makeConverterDownloader, + byName: { + 'http': makeHTTPDownloader, + 'local': makeLocalDownloader, + 'file': makeLocalDownloader, + 'youtube': makeYouTubeDownloader, + 'youtube-dl': makeYouTubeDownloader + }, + getDownloaderFor(arg) { if (arg.startsWith('http://') || arg.startsWith('https://')) { if (arg.includes('youtube.com')) { diff --git a/src/loop-play.js b/src/loop-play.js index b0bb4dd..9328073 100644 --- a/src/loop-play.js +++ b/src/loop-play.js @@ -5,7 +5,10 @@ const { spawn } = require('child_process') const FIFO = require('fifo-js') const EventEmitter = require('events') -const { getDownloaderFor, makeConverterDownloader } = require('./downloaders') +const { + getDownloaderFor, makeConverterDownloader, + byName: downloadersByName +} = require('./downloaders') const { getItemPathString } = require('./playlist-utils') const promisifyProcess = require('./promisify-process') @@ -114,7 +117,22 @@ class PlayController { if (picked === null) { return null } else { - let downloader = getDownloaderFor(picked.downloaderArg) + let downloader + + if (picked.downloader) { + downloader = downloadersByName[picked.downloader]() + + if (!downloader) { + console.error( + `Invalid downloader for track ${picked.name}:`, downloader + ) + + return false + } + } else { + downloader = getDownloaderFor(picked.downloaderArg) + } + downloader = makeConverterDownloader(downloader, 'wav') this.downloadController.download(downloader, picked.downloaderArg) return picked diff --git a/src/playlist-utils.js b/src/playlist-utils.js index 55638ed..5efd478 100644 --- a/src/playlist-utils.js +++ b/src/playlist-utils.js @@ -56,6 +56,16 @@ function updateGroupFormat(group) { // isn't a string.. if (typeof item[1] === 'string' || item.downloaderArg) { item = updateTrackFormat(item) + + // TODO: Should this also apply to groups? Is recursion good? Probably + // not! + // + // TODO: How should saving/serializing handle this? For now it just saves + // the result, after applying. (I.e., "apply": {"foo": "baz"} will save + // child tracks with {"foo": "baz"}.) + if (groupObj.apply) { + Object.assign(item, groupObj.apply) + } } else { item = updateGroupFormat(item) } diff --git a/src/smart-playlist.js b/src/smart-playlist.js new file mode 100644 index 0000000..e65ff1f --- /dev/null +++ b/src/smart-playlist.js @@ -0,0 +1,49 @@ +'use strict' + +const fs = require('fs') +const { getCrawlerByName } = require('./crawlers') + +const { promisify } = require('util') +const readFile = promisify(fs.readFile) + +async function processItem(item) { + // Object.assign is used so that we keep original properties, e.g. "name" + // or "apply". + + if ('items' in item) { + return Object.assign(item, { + items: await Promise.all(item.items.map(processItem)) + }) + } else if ('source' in item) { + const [ name, ...args ] = item.source + + const crawlModule = getCrawlerByName(name) + + if (crawlModule === null) { + console.error(`No crawler by name ${name} - skipped item:`, item) + return Object.assign(item, {failed: true}) + } + + const { crawl } = crawlModule + + return Object.assign(item, await crawl(...args)) + } else { + return item + } +} + +async function main(opts) { + // TODO: Error when no file is given + + if (opts.length === 0) { + console.log("Usage: smart-playlist /path/to/playlist") + } else { + const playlist = JSON.parse(await readFile(opts[0])) + console.log(JSON.stringify(await processItem(playlist), null, 2)) + } +} + +if (require.main === module) { + main(process.argv.slice(2)) + .catch(err => console.error(err)) +} |