import path from 'path' import rp from 'request-promise-native' import crypto from 'crypto' import { dbPromise } from './database' import { addListExternal } from './external' import { parseStringPromise } from 'xml2js' const values = require(path.join(process.cwd(), 'values.json')) function createHash (data) { return crypto .createHash('sha1') .update(data.artist + data.name) .digest('hex') .substr(0, 8) } async function search (track, limit = 30) { if (!values.lastfm) return [] let data try { data = await rp(`http://ws.audioscrobbler.com/2.0/?method=track.search&track=${track}&api_key=${values.lastfm.key}&format=json&limit=${limit}`) data = JSON.parse(data) if (!data.results || !data.results.trackmatches || !data.results.trackmatches.track) { throw new Error('No results') } } catch (e) { return [] } const final = [] for (const i in data.results.trackmatches.track) { const res = data.results.trackmatches.track[i] let clean = { id: createHash(res), artist: res.artist, title: res.name, external: true, mbid: res.mbid } clean = addListExternal(clean.id, clean) final.push(clean) } return final } // Authentication function getAPISig (params) { let allStrings = [] const qs = {} params.api_key = values.lastfm.key for (const key in params) { const val = params[key] if (val == null || val === '') continue allStrings.push(key + val) qs[key] = val } allStrings = allStrings.sort() allStrings.push(values.lastfm.secret) qs.api_sig = crypto.createHash('md5').update(allStrings.join('')).digest('hex') return qs } function getAuthURL () { return 'http://www.last.fm/api/auth/?api_key=' + values.lastfm.key + '&cb=' + values.lastfm.redirectUri } async function getSession (token) { const sessSig = getAPISig({ token, method: 'auth.getSession' }) const res = await rp('http://ws.audioscrobbler.com/2.0/', { qs: sessSig }) const rep = await parseStringPromise(res) const name = rep.lfm.session[0].name[0] const key = rep.lfm.session[0].key[0] return { name, key } } async function storeSession (userId, session) { if (!session.name || !session.key) throw new Error('Invalid session parameter.') const db = await dbPromise const existing = await db.get('SELECT * FROM LastFM WHERE userId = ?', userId) if (existing) { await db.run('UPDATE LastFM SET name = ?, key = ? WHERE userId = ?', session.name, session.key, userId) } else { await db.run('INSERT INTO LastFM (userId,name,key,created) VALUES (?,?,?,?)', userId, session.name, session.key, new Date()) } return true } async function disregardSession (userId) { const db = await dbPromise return db.run('DELETE FROM LastFM WHERE userId = ?', userId) } async function getSessionForUser (userId) { const db = await dbPromise return db.get('SELECT * FROM LastFM WHERE userId = ?', userId) } async function scrobbleTrack (userId, trackData) { const sess = await getSessionForUser(userId) if (!sess) throw new Error('User does not have a LastFM session.') const scrobbleSig = getAPISig({ sk: sess.key, method: 'track.scrobble', artist: trackData.artist || 'Unknown', title: trackData.title, album: trackData.album, duration: trackData.duration, mbid: trackData.mbid, timestamp: Math.floor(Date.now() / 1000) }) await rp.post('http://ws.audioscrobbler.com/2.0/', { form: scrobbleSig }) return true } export { search, getAPISig, getAuthURL, getSession, getSessionForUser, storeSession, disregardSession, scrobbleTrack }