// Music data: plays, history and stats, favorites, smart playlist rules, and Family playlists shared by everyone with Music. const http = require('http'), assert = require('assert'), fs = require('fs'), os = require('os'), path = require('path'); process.env.DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), 'nehub-md-')); const users = require('../users'), md = require('../musicdata'); const people = { a: users.create({ name: 'Ann', role: 'admin', handle: 'YQ' }), b: users.create({ name: 'Bob', role: 'user', tabs: ['music'], handle: 'Yg' }), c: users.create({ name: 'Cy', role: 'user', tabs: ['music'], handle: 'Yw' }) }; const hub = http.createServer((req, res) => users.runAs(people[req.headers['x-user'] || 'a'], () => md.handle(req, res))).listen(0, async () => { const B = 'http://127.0.0.1:' + hub.address().port + '/api/music', call = (p, o = {}, who) => fetch(B + p, { ...o, headers: { ...(o.headers || {}), ...(who ? { 'x-user': who } : {}) } }), post = (p, b, who, m = 'POST') => call(p, { method: m, body: JSON.stringify(b) }, who), j = async r => r.json(); const s = (n, o = {}) => ({ key: String(100 + n), title: 'Song ' + n, artist: 'Artist ' + (n % 2), album: 'Album ' + n, thumb: '/library/metadata/1/thumb/1', artistKey: String(10 + n % 2), albumKey: String(50 + n), dur: 180000, ...o }); try { // plays assert.equal((await post('/played', { key: 'abc', title: 'x' })).status, 400, 'only Plex songs'); for (const n of [1, 2, 1, 3, 1]) assert.equal((await post('/played', s(n))).status, 200); const h = await j(await call('/history?limit=3')); assert.equal(h.length, 3); assert.equal(h[0].title, 'Song 1', 'newest first'); assert.equal((await j(await post('/played', s(1)))).duplicate, true, 'the same song twice in a moment counts once'); assert.equal((await j(await call('/history'))).length, 5); const st = await j(await call('/stats?range=week')); assert.ok(st.plays >= 4 && st.minutes >= 9); assert.equal(st.songs[0].title, 'Song 1'); assert.equal(st.songs[0].plays, 3, 'the most played song is first'); assert.equal(st.artists[0].name, 'Artist 1'); assert.equal(st.series.length, 7, 'a week has seven days'); assert.equal(st.series.at(-1).minutes, st.minutes, 'all of it was today'); assert.equal(st.streak, 1); assert.equal((await j(await call('/stats?range=year'))).series.length, 1, 'a year is shown per month'); assert.deepEqual((await j(await call('/stats', {}, 'b'))).plays, 0, "each person's own history"); assert.equal((await j(await call('/top?days=30&limit=2'))).length, 2); // favorites await post('/favs', s(7)); await post('/favs', s(8)); await post('/favs', s(7)); let f = await j(await call('/favs')); assert.equal(f.length, 2, 'saving twice keeps one'); assert.equal(f[0].key, '107', 'the one just saved again is newest'); await call('/favs/107', { method: 'DELETE' }); assert.deepEqual((await j(await call('/favs'))).map(x => x.key), ['108']); assert.deepEqual(await j(await call('/favs', {}, 'b')), []); // smart playlist rules are cleaned let sp = await j(await post('/smart', { name: 'Old gospel', rules: { genres: ['Gospel', '