package/package.json000644 001750 001750 0000000657 13012203641013017 0ustar00000000 000000 { "name": "node-tunein", "version": "1.0.1", "description": "a nodejs wrapper for tunein web radios", "main": "tunein.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { "type": "git", "url": "git@iohub:node-tunein" }, "keywords": [ "tunein", "webradios" ], "author": "Julien Ledun ", "license": "MIT", "dependencies": {} } package/.npmignore000644 001750 001750 0000000642 13011323412012520 0ustar00000000 000000 # See http://help.github.com/ignore-files/ for more about ignoring files. # compiled output /dist /tmp # dependencies /node_modules /bower_components # IDEs and editors /.idea /.vscode .project .classpath *.launch .settings/ *~ .*~ .*.swp # misc /.sass-cache /connect.lock /coverage/* /libpeerconnection.log npm-debug.log testem.log /typings # e2e /e2e/*.js /e2e/*.map #System Files .DS_Store Thumbs.db test.js package/README.md000644 001750 001750 0000004653 13012203460012007 0ustar00000000 000000 # tunein.js A wrapper to search and browse TuneIn web radios library. Based on what I've read in the Python [mopidy TuneIn module](https://github.com/kingosticks/mopidy-tunein.git) and a basic request to [radiotime.com](http://opml.radiotime.com). I've Googled and DuckDucked a lot but couldn't find any docs about opml.radiotime.com specifications so the functions provided by this module are quite simple. This project needs a lot of improvements and completed with tests, doc, ... # Install ``` npm install --save node-tunein ``` This projects is developped and tested with nodejs-v7.1.0 # Usage ```javascript "use strict"; let TuneIn = require('node-tunein'); let tunein = new TuneIn(); tunein.browse() .then( (data) => console.log(data) ) .catch( (err) => console.log(err) ); ``` # What does this module provide ? The module provides ability to a client to : * browse radiotime.com library * search in radiotime.com library All returned URLs are parsed with url and querystring native modules of NodeJS. Returned objects are list of categories and/or webradios, client needs to parse objects to know what to do with :( Returned objects can have children (an array of library elements) # Future step Change browse entry parameters to an object with : * guide_id, * genre_id, * filter, * c, * id, * whatever filter field provided by opml.radiotime.com # Licence The MIT License (MIT) Copyright (c) 2016 Julien Ledun Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. package/browseHistory.js000644 001750 001750 0000003164 13012037174013754 0ustar00000000 000000 "use strict"; const querystring = require('querystring'); const crypto = require('crypto'); module.exports = class BrowseHistory { constructor() { this.reset(); } reset() { // init this.histo = []; this.index = -1; } indexOf( hash ) { if ( this.histo.length == 0 ) return -1; let i; let retval = -1; for ( i = 0; i < this.histo.length; i++ ) { if ( this.histo[i].hash === hash ) { retval = i; break; } } return retval; } add( url ) { // make sure that added url is not already the last element let hash = crypto.createHash('md5').update(querystring.stringify( url.search )).digest('hex'); // check already existing element in browse history // then set index to found item let tmp = this.indexOf( hash ); if ( tmp > -1 ) { this.index = tmp; return; } // make sure that we always push at last index (after a previous call, for example) while( this.index > -1 && this.index < this.histo.length - 1 ) this.histo.pop(); this.histo.push({ hash: hash, url: url, timestamp: Date.now() }); this.index++; } previous() { // get previous value in browse history if ( this.index > 0 ) this.index--; return this.getCurrent(); } next() { // get next value in browse history if ( this.index < this.histo.length -1 ) this.index++; return this.getCurrent(); } getCurrent() { if ( this.index < 0 ) return {}; console.log(this.histo); console.log(this.index); return this.histo[ this.index ].url; } content() { return this.histo; } }; package/tunein.js000644 001750 001750 0000006242 13012026352012367 0ustar00000000 000000 "use strict"; const http = require('http'); const url = require('url'); const querystring = require('querystring'); const BrowseHistory = new require('./browseHistory.js'); module.exports = class tunein { constructor() { this.reset(); } reset() { // init browse history if ( this.histo ) { this.histo.reset(); }else{ this.histo = new BrowseHistory(); } this.url = {}; this.keys = []; this.url.filter = {}; this.browseHistory = []; this.url.protocol = "http"; this.url.host = "opml.radiotime.com"; this.initSearch(); } get() { return new Promise( (resolve, reject) => { // copy url of current element in browse history let tmpurl = JSON.parse( JSON.stringify( this.histo.getCurrent() ) ); // stringify url tmpurl.search = querystring.stringify(tmpurl.search); // add filter if exists if ( this.filter ) tmpurl.search += `&${querystring.stringify(this.url.filter)}`; // get data from radiotime.com let req = http.get( url.format(tmpurl), (res) => { let tuneinRes = ""; res.on('data', (chunk) => tuneinRes += chunk); res.on('end', () => { let data = this.parseResult( JSON.parse( tuneinRes ) ); return resolve( ( data ) ); }); }); req.on('error', (err) => { return reject( err ); }); }); } parseURL( strUrl ) { let parsedurl = url.parse( strUrl ); parsedurl.query = querystring.parse( parsedurl.query ); return parsedurl; } parseResult( data ) { if ( data.head.status != 200 ) return new Error(`TuneIn Request error : ${data.head.fault}`); data.body.forEach( (elm) => { if ( typeof elm.URL != "undefined") { elm.URL = this.parseURL( elm.URL ); // store main categories if ( typeof elm.URL.query != "undefined" && typeof elm.URL.query.c != undefined ) if ( this.keys.indexOf( elm.key ) === -1 ) this.keys.push( elm.key ); } if ( typeof elm.children != "undefined") { elm.children.forEach( (child) => { child.URL = this.parseURL( child.URL ); }); } }); return data; } getKeys() { // return stored main categories return this.keys; } getBrowseHistory() { // return browse history return this.histo.content(); } initSearch() { // set default to json instead of opml this.url.search = {render: "json"}; } browsePrevious() { this.histo.previous(); return this.get(); } browseNext() { this.histo.next(); return this.get(); } browse( category, filter ) { // browse commands let cat = category || ''; let fil = filter || ''; this.url.pathname = "Browse.ashx"; this.initSearch(); if ( cat ) { if ( this.keys.indexOf( cat ) !== -1 ) { this.url.search.c = cat; }else{ this.url.search.id = cat; } } if ( filter ) { this.url.filter = fil; } this.histo.add( this.url ); return this.get(); } search( chunk ) { // search in ratiotime repository this.url.pathname = "Search.ashx"; this.initSearch(); this.url.search.query = chunk; return this.get(); } } package/contributors000644 001750 001750 0000000044 13011314320013173 0ustar00000000 000000 Julien Ledun package/licence000644 001750 001750 0000002115 13011314451012046 0ustar00000000 000000 The MIT License (MIT) Copyright (c) 2016 Julien Ledun Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.