forked from daniel-j/wonder-radio
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.js
More file actions
executable file
·269 lines (240 loc) · 7.91 KB
/
Copy pathserver.js
File metadata and controls
executable file
·269 lines (240 loc) · 7.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
#!/usr/bin/env node
'use strict';
var http = require('http');
var net = require('net');
var child_process = require('child_process');
var mysql = require('mysql');
var hiddenInfo = require('./hidden.json');
var mysqlUser = hiddenInfo.mysqlUsername;
var mysqlPassword = hiddenInfo.mysqlPassword;
var mysqlDatabase = hiddenInfo.mysqlDatabase;
var mpdPassword = hiddenInfo.mpdPassword;
var icecastMount = "/stream";
var icecastPassword = hiddenInfo.icecastPassword;
var ipinfoApiKey = hiddenInfo.ipinfoApiKey;
var trackWait = 8*60; // In minutes
var minimumRating = -10;
var db = mysql.createConnection({
user : mysqlUser,
password : mysqlPassword,
socketPath: '/var/run/mysqld/mysqld.sock',
database: mysqlDatabase
});
db.connect();
console.log("Connected to database");
var icecastOptions = {
port: 8000,
auth: 'admin:'+icecastPassword,
path: "/admin/listclients.xsl?mount="+icecastMount
};
var ipcache = {};
var addresses = [];
var clientinfo = [];
function createGeoList() {
var list = [];
for (var i = 0; i < clientinfo.length; i++) {
if (clientinfo[i].ipinfo) {
list.push([clientinfo[i].ipinfo.x, clientinfo[i].ipinfo.y]);
}
}
return list;
}
function updateIpInfo(ip) {
if (ipcache[ip]) {
var pos = addresses.indexOf(ip);
if (pos !== -1) {
clientinfo[pos].ipinfo = ipcache[ip];
}
return;
}
http.get("http://api.ipinfodb.com/v3/ip-city/?key="+ipinfoApiKey+"&ip="+ip+"&format=json×tamp="+Date.now(), function (res) {
var buffer = "";
res.on('data', function (chunk) {
buffer += chunk;
});
res.on('end', function () {
var pos = addresses.indexOf(ip);
var fullinfo = JSON.parse(buffer);
console.log(fullinfo);
if (fullinfo.latitude == 0 && fullinfo.longitude == 0) {
console.log("Unknown location for ip "+ip);
ipcache[ip] = null;
if (pos !== -1) {
clientinfo[pos].ipinfo = null;
}
return;
}
var ipinfo = {y: Math.round( 244/2-(fullinfo.latitude)*1.22 + 35), x: Math.round(454/2 + fullinfo.longitude*1.06 - 15)};
ipcache[ip] = ipinfo;
if (pos !== -1) {
clientinfo[pos].ipinfo = ipinfo;
}
});
}).on('error', function () {
console.log("Error getting ip info");
var pos = addresses.indexOf(ip);
if (pos !== -1) {
addresses.splice(pos, 1);
clientinfo.splice(pos, 1);
}
});
}
function updateListeners() {
var req = http.request(icecastOptions, function (res) {
if (res.statusCode !== 200) return;
res.setEncoding('utf8');
var buffer = "";
res.on('data', function (chunk) {
buffer += chunk;
});
res.on('end', function () {
var mounts = JSON.parse(buffer);
delete mounts[""];
for (var i in mounts) {
mounts[i].length--;
}
if (mounts[icecastMount]) {
var clients = mounts[icecastMount];
var newaddresses = [];
for (var i = 0; i < clients.length; i++) {
var ip = clients[i].ip;
newaddresses.push(ip);
var pos = addresses.indexOf(ip);
if (pos === -1) {
console.log(ip);
addresses.push(ip);
clientinfo.push({});
updateIpInfo(ip);
pos = addresses.length-1;
}
clientinfo[pos].time = clients[i].time;
}
for (var i = 0; i < addresses.length; i++) {
if (newaddresses.indexOf(addresses[i]) === -1) {
addresses.splice(i, 1);
clientinfo.splice(i, 1);
i--;
}
}
}
});
});
req.on('error', function(e) {
console.log('problem with request: ' + e.message);
});
req.end();
}
var randomTimes = Math.floor(Math.random()*1000); // 100 different combinations...
for (var i = 0; i < randomTimes; i++) {
db.query('SELECT RAND()');
}
console.log("Initialized");
var lastPlayedFile = "";
var oldQueueId = 0;
var currentTrackId = 0;
function mpdUpdate(cb) {
var child = child_process.spawn("./mpd-update.sh", [mpdPassword]);
queueNextSong();
child.stdout.on('data', function (data) {
cb(data.toString().trim().split("@@"));
});
}
function queueTrack(file, cb) {
child_process.execFile('./mpd-queue.sh', [mpdPassword, file], function (err, stdout, stderr) {
console.log("Queued "+file);
if (cb) cb();
});
}
function queueRandom(cb) {
// Only tracks played more than trackWait minutes ago and got a rating above minimumRating
db.query('SELECT file, IFNULL((SELECT SUM(vote) FROM votes WHERE tracks.id=votes.trackId), 0) as rating, lastplayed FROM tracks WHERE (SELECT id FROM queue WHERE tracks.id=queue.trackId) IS NULL AND (lastplayed < date_sub(now(), interval '+trackWait+' minute) OR lastplayed IS NULL) HAVING rating > '+minimumRating+' ORDER BY rating*RAND()*2-plays*RAND()+RAND()*10 DESC LIMIT 1', function(err, rows, fields) {
if (err) console.log(err);
console.log(rows[0]);
if (rows && rows[0]) {
var entry = rows[0];
console.log("Next song hasn't been played since "+(entry.lastplayed||'NEVER'), entry.file);
queueTrack(entry.file, function () {
if (cb) cb(entry);
});
} else {
// Only play those with rating above minimumRating, no matter when they were last played
db.query('SELECT file, IFNULL((SELECT SUM(vote) FROM votes WHERE tracks.id=votes.trackId), 0) as rating, lastplayed FROM tracks WHERE (SELECT id FROM queue WHERE tracks.id=queue.trackId) IS NULL HAVING rating > '+minimumRating+' ORDER BY rating*RAND()*2-plays*RAND()+RAND()*10 DESC LIMIT 1', function(err, rows, fields) {
if (err) console.log(err);
console.log(rows[0]);
if (rows && rows[0]) {
var entry = rows[0];
console.log("Next song has the rating "+entry.rating+" and was played pretty recently: "+entry.lastplayed, entry.file);
queueTrack(entry.file, function () {
if (cb) cb(entry);
});
} else {
// I give up, play a random song
db.query('SELECT file, IFNULL((SELECT SUM(vote) FROM votes WHERE tracks.id=votes.trackId), 0) as rating, lastplayed FROM tracks ORDER BY rating*RAND()*2-plays*RAND()+RAND()*10 DESC LIMIT 1', function(err, rows, fields) {
if (err) console.log(err);
console.log(rows[0]);
if (rows && rows.length > 0) {
var entry = rows[0];
console.log("I give up. Next song is random!", entry.file);
queueTrack(entry.file, function () {
if (cb) cb(entry);
});
} else {
console.log("Unable to queue music.. Database empty?");
}
});
}
});
}
});
}
function queueNextSong() {
db.query('SELECT queue.id, trackId, file, RAND() as randomNr FROM queue LEFT JOIN tracks ON tracks.id=queue.trackId WHERE (NOW()-queue.added)/60 > 30 ORDER BY queue.added DESC LIMIT 1', function (err, rows, fields) {
if (err) console.log(err);
console.log("\nqueueNextSong()", rows);
var random = Math.random();
if ((!rows || rows.length === 0) && random > 0.40) {
console.log("Random track...", random);
//console.log(rows);
queueRandom();
} else if (rows.length > 0) {
console.log("Will play an old queued track");
playFromQueue(rows[0]);
} else {
db.query('SELECT queue.id, trackId, file FROM queue LEFT JOIN tracks ON tracks.id=queue.trackId ORDER BY queue.added ASC LIMIT 1', function (err, rows, fields) {
if(err) console.error(err);
if (!rows || rows.length === 0) {
console.log("Queue is empty...");
queueRandom();
} else {
console.log("Will play a queued track", rows[0].file);
playFromQueue(rows[0]);
}
});
}
});
}
function playFromQueue(item) {
oldQueueId = item.id;
queueTrack(item.file, function () {
});
}
mpdUpdate(function (track) {
var file = track[0];
var title = track[1];
var artist = track[2];
lastPlayedFile = file;
console.log(">> Now playing "+(title || file));
process.title = (title || file).replace(/\ /g, '_');
db.query("DELETE FROM queue WHERE trackId IN (SELECT id FROM tracks WHERE file = ?)", [file]);
db.query("UPDATE tracks SET plays = plays+1, lastplayed = NOW() WHERE file = "+mysql.escape(file), function () {
queueNextSong();
});
});
setInterval(updateListeners, 2*1000);
var geoserver = net.createServer(function (c) {
c.on('error', function () {
});
var list = createGeoList();
c.end(JSON.stringify(list));
});
geoserver.listen(8001);