From 6f4ce94467db4e95cab117007e3724695b7d9533 Mon Sep 17 00:00:00 2001 From: liam4 Date: Tue, 1 Aug 2017 11:33:19 -0300 Subject: Smart playlists --- src/cli.js | 29 +++++++++++++++++------------ src/crawl-http.js | 2 +- src/crawl-itunes.js | 32 +++++++++++++++++++++----------- src/crawl-local.js | 2 +- src/crawl-youtube.js | 2 +- src/crawlers.js | 13 +++++++++++++ src/smart-playlist.js | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 103 insertions(+), 26 deletions(-) create mode 100644 src/crawlers.js create mode 100644 src/smart-playlist.js (limited to 'src') 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 e776b9c..7553e85 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/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)) +} -- cgit 1.3.0-6-gf8a5