Commit 6051397b authored by Marco Flowers's avatar Marco Flowers

Added the stats module, condensed /by_xx to /search

Added the stats module which aggregates overall stream
statistics. Removed /by_genre and /by_format and replaced
them with /search.
parent e20ffb39
......@@ -16,14 +16,13 @@ var app = express();
var config = conf.all().config;
/* Controllers */
var stats = require('./controllers/stats.js')(query, cache);
var streamsFindBy = require('./controllers/stream-api.js')(query, cache);
var streamFindById = require('./controllers/stream-by-id.js')(query, cache);
var index = require('./controllers/index.js')(query, cache, streamsFindBy);
var genres = require('./controllers/genres.js')(query, cache, streamsFindBy);
var formats = require('./controllers/formats.js')(query, cache, streamsFindBy);
var index = require('./controllers/index.js')(query, cache, streamsFindBy, stats);
var yp_cgi = require('./controllers/yp-cgi.js')(query, qs, validator, config);
var listen = require('./controllers/listen.js')(query, qs, xmlbuilder, streamFindById);
var search = require('./controllers/search.js')(query, cache, streamsFindBy, stats);
/*
To rerun api docs after modifying the apidoc comments use the command
......@@ -47,10 +46,17 @@ app.set('views', __dirname + '/views');
/* Website Routes */
app.get('/', index);
app.post('/cgi-bin/yp-cgi', yp_cgi);
app.get('/by_genre/:genre', genres);
app.get('/by_format/:format', formats);
// backwards compatibility for bookmarks, redirect to /search
app.get('/by_genre/:genre', function(req, res) {
res.redirect('/search?genre='+req.param("genre"));
});
app.get('/by_format/:format', function(req, res) {
res.redirect('/search?format='+req.param("format"));
});
app.get('/search/', search);
app.get('/listen/:streamId/:filename',listen);
// allows updated ban lists to be reloaded
app.get('/reloadconfig/:password',function(req, res) {
// so not everyone can reload the config file
......
var query, cache, streamsFindBy;
function init(q, c, s) {
query = q;
cache = c;
streamsFindBy = s;
return byFormat;
}
function byFormat(req, res) {
var params = req.params;
var genre = undefined;
var q = undefined;
var order = 0; //descending
var limit = 2;
var json = 0;
var format = req.param("format");
var starting_after = req.param("starting_after");
var ending_before = req.param("ending_before");
streamsFindBy(format, genre, q, order, limit, starting_after, ending_before, json, function(err, rows) {
if (err) {
res.send(503);
} else {
var error;
if (rows.length === 0) {
error = "No streams for this format.";
} else {
error = false;
}
var next_url, prev_url, home_url;
var result = rows;
// if the full results don't appear show a start button
if((starting_after || ending_before) && result.length < limit) {
// clear pagination params
delete params.ending_before;
delete params.starting_after;
qstring = querystring.stringify(params);
home_url = req.path+'?'+qstring;
}
// on the fist page and any page with max results show the next button
if(!(starting_after || ending_before) || (result.length == limit)) {
var last_id = rows[result.length-1].id;
delete params.ending_before;
params.starting_after = last_id;
qstring = querystring.stringify(params);
next_url = req.path+'?'+qstring;
}
// any page with max results and any page with results with starting_after
// show the previous button
if((result.length == limit) || (result.length > 0 && starting_after)) {
var prev_id = rows[0].id;
delete params.starting_after;
params.ending_before = prev_id;
qstring = querystring.stringify(params);
prev_url = req.path+'?'+qstring;
}
res.render("by_xx", {
title: format,
servers: rows,
error: error,
next: next_url,
prev: prev_url,
home: home_url
});
}
});
}
module.exports = init;
var query, cache, streamsFindBy;
function init(q, c, s) {
query = q;
cache = c;
streamsFindBy = s;
return byGenre;
}
function byGenre(req, res) {
var params = req.params;
var format = undefined;
var q = undefined;
var order = 0; //descending
var limit = 2;
var json = 0;
var genre = req.param("genre");
var starting_after = req.param("starting_after");
var ending_before = req.param("ending_before");
streamsFindBy(format, genre, q, order, limit, starting_after, ending_before, json, function(err, rows) {
if (err) {
res.send(503);
} else {
var error;
if (rows.length === 0) {
error = "No streams for this genre.";
} else {
error = false;
}
var next_url, prev_url, home_url;
var result = rows;
// if the full results don't appear show a start button
if((starting_after || ending_before) && result.length < limit) {
// clear pagination params
delete params.ending_before;
delete params.starting_after;
qstring = querystring.stringify(params);
home_url = req.path+'?'+qstring;
}
// on the fist page and any page with max results show the next button
if(!(starting_after || ending_before) || (result.length == limit)) {
var last_id = rows[result.length-1].id;
delete params.ending_before;
params.starting_after = last_id;
qstring = querystring.stringify(params);
next_url = req.path+'?'+qstring;
}
// any page with max results and any page with results with starting_after
// show the previous button
if((result.length == limit) || (result.length > 0 && starting_after)) {
var prev_id = rows[0].id;
delete params.starting_after;
params.ending_before = prev_id;
qstring = querystring.stringify(params);
prev_url = req.path+'?'+qstring;
}
res.render("by_xx", {
title: genre,
servers: rows,
error: error,
next: next_url,
prev: prev_url,
home: home_url
});
}
});
}
module.exports = init;
var query, cache, streamsFindBy;
var query, cache, streamsFindBy, stats;
function init(q, c, s) {
function init(q, c, s, st) {
query = q;
cache = c;
streamsFindBy = s;
stats = st;
return index;
}
......@@ -26,11 +27,16 @@ function index(req, res) {
} else {
error = false;
}
stats(function(errorStats, stats) {
res.render("index", {
title: '',
servers: rows,
error: error
error: error,
stats: stats
});
});
}
});
}
......
var query, cache, streamsFindBy, stats;
function init(q, c, s, st) {
query = q;
cache = c;
streamsFindBy = s;
stats = st;
return bySearch;
}
function bySearch(req, res) {
var params = req.query;
var format = req.param("format");
var genre = req.param("genre");
var q = req.param("q");
var order = 0; //descending
var limit = 2;
var json = 0;
var starting_after = req.param("starting_after");
var ending_before = req.param("ending_before");
streamsFindBy(format, genre, q, order, limit, starting_after, ending_before, json, function(err, rows) {
if (err) {
res.send(503);
} else {
var error;
if (rows.length === 0) {
error = "No streams for this search.";
} else {
error = false;
var next_url, prev_url, home_url;
var result = rows;
// if the full results don't appear show a start button
if((starting_after || ending_before) && result.length < limit) {
// clear pagination params
delete params.ending_before;
delete params.starting_after;
qstring = querystring.stringify(params);
home_url = req.path+'?'+qstring;
}
// on the fist page and any page with max results show the next button
if(!(starting_after || ending_before) || (result.length == limit)) {
var last_id = rows[result.length-1].id;
delete params.ending_before;
params.starting_after = last_id;
qstring = querystring.stringify(params);
next_url = req.path+'?'+qstring;
}
// any page with max results and any page with results with starting_after
// show the previous button
if((result.length == limit) || (result.length > 0 && starting_after)) {
var prev_id = rows[0].id;
delete params.starting_after;
params.ending_before = prev_id;
qstring = querystring.stringify(params);
prev_url = req.path+'?'+qstring;
}
}
stats(function(errorStats, stats) {
res.render("by_xx", {
title: "Search",
genre:genre,
format:format,
q:q,
servers: rows,
error: error,
next: next_url,
prev: prev_url,
home: home_url,
stats:stats
});
});
}
});
}
module.exports = init;
var query, cache;
var async = require('async');
function init(q, c) {
query = q;
cache = c;
return getCachedStats;
}
function getCachedStats(cb) {
cache.wrap("stats", function (_cb) {
getStats(_cb);
}, 5, cb);
}
function getStats(resultCallback)
{
console.log("go")
var genresq = 'SELECT DISTINCT val FROM (SELECT unnest(genres) as val FROM streams) s;';
var formats = 'SELECT DISTINCT val FROM (SELECT unnest(codec_sub_types) as val FROM streams) s;';
var baseStatsQuery ='SELECT COUNT(*) AS total FROM streams AS s '+
'INNER JOIN server_mounts AS sm ON s.id = sm.stream_id ';
var wheremodifier = 'WHERE $1 = ANY(s.codec_sub_types)'
var stats = {}
stats.statistics = {}
async.waterfall([
function getallGenres(cb) {
query(genresq, cb);
},
function(rows, result, cb) {
stats["genres"] = rows
query(formats, cb);
},
function(rows, result, cb) {
stats["formats"] = rows
query(baseStatsQuery, cb)
},
function(rows, result, cb) {
stats.statistics["Total Streams"] = rows[0].total
query(baseStatsQuery+wheremodifier,['Vorbis'], cb)
},
function(rows, result, cb) {
stats.statistics["Ogg Vorbis"] = rows[0].total
query(baseStatsQuery+wheremodifier,['Opus'], cb)
},
function(rows, result, cb) {
stats.statistics["Opus"] = rows[0].total
query(baseStatsQuery+wheremodifier,['Theora'], cb)
},
function(rows, result, cb) {
stats.statistics["Theora"] = rows[0].total
query(baseStatsQuery+wheremodifier,['MP3'], cb)
},
function(rows, result, cb) {
stats.statistics["MP3"] = rows[0].total
query(baseStatsQuery+wheremodifier,['WebM'], cb)
},
function(rows, result, cb) {
stats.statistics["Webm"] = rows[0].total
query(baseStatsQuery+wheremodifier,['AAC'], cb)
},
function(rows, result, cb) {
stats.statistics["AAC"] = rows[0].total
query(baseStatsQuery+wheremodifier,['AAC+'], cb)
},
function(rows, result, cb) {
stats.statistics["AAC+"] = rows[0].total
query(baseStatsQuery+wheremodifier,['NSV'], cb)
},
function(rows, result, cb) {
stats.statistics["NSV"] = rows[0].total
resultCallback(null, stats)
}
],function (err, result) {
if(err) {
resultCallback(err, null)
}
});
}
module.exports = init;
......@@ -5,7 +5,7 @@
{% endblock %}
{% block content %}
<h2>Streams <small>{{ title }}</small></h2>
<h2>Streams <small>{% if genre %} Genre:{{genre}}{% endif %}{% if format %} Format:{{format}}{% endif %}{% if q %} Search:{{q}}{% endif %}</small></h2>
<div id="servers-list">
{% if error %}
<p class="error">{{ error }}</p>
......
......@@ -48,7 +48,7 @@
<div class="navbar-right">
<form class="navbar-form" role="search" method="get" action="/search">
<div class="input-group">
<input type="text" class="form-control" placeholder="Search" name="search">
<input type="text" class="form-control" placeholder="Search" name="q">
<div class="input-group-btn">
<button type="submit" class="btn btn-default" aria-label="Go"><i class="glyphicon glyphicon-search"></i></button>
</div>
......@@ -66,7 +66,10 @@
<h3 class="panel-title">Statistics</h3>
</div>
<div class="panel-body">
Panel content
{% for name, number in stats.statistics %}
{{name}} - {{number}}
<br>
{% endfor %}
</div>
</div>
<div class="panel panel-default">
......@@ -74,7 +77,10 @@
<h3 class="panel-title">Popular genres</h3>
</div>
<div class="panel-body">
Panel content
{% for genre in stats.genres %}
<a href='/search/?genre={{genre.val}}'>{{genre.val}}</a>
<br>
{% endfor %}
</div>
</div>
<div class="panel panel-default">
......@@ -82,7 +88,10 @@
<h3 class="panel-title">Formats</h3>
</div>
<div class="panel-body">
Panel content
{% for format in stats.formats %}
<a href='/search/?format={{format.val}}'>{{format.val}}</a>
<br>
{% endfor %}
</div>
</div>
</div>
......
......@@ -24,13 +24,13 @@
<p class="songname"><b>On Air:</b> <i>{{ server.songname }}</i></p>
<p class="genres"><b>Genres:</b>
{% for genre in server.genres %}
<a href="/by_genre/{{ genre }}" class="label label-default">{{ genre }}</a>
<a href="/search/?genre={{ genre }}" class="label label-default">{{ genre }}</a>
{% endfor %}
</p>
<p class="tunein"><b>Tune in:</b> <a href="/listen/{{ server.id }}/listen.m3u">M3U</a> | <a href="/listen/{{ server.id }}/listen.xspf">XSPF</a></p>
{% for format in server.codec_sub_types %}
{% if loop.first %}
<p class="codec"><a href="/by_format/{{ format }}">{{ format }}</a>
<p class="codec"><a href="/search/?format={{ format }}">{{ format }}</a>
{% endif %}
{% endfor %}
<!--p class="format">{{ server.stream_type }}</p-->
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment