Skip to content

Instantly share code, notes, and snippets.

@stefansundin
Last active April 26, 2023 13:39
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save stefansundin/f9df6c5e0fd184c60709 to your computer and use it in GitHub Desktop.
Save stefansundin/f9df6c5e0fd184c60709 to your computer and use it in GitHub Desktop.
Userscript that shows you the download count for Launchpad packages. Be sure to go to the "View all builds" view. (Read the source code for notes!). Click the [Raw] button to install!
// ==UserScript==
// @name Launchpad Download Count
// @namespace https://gist.github.com/stefansundin/
// @homepage https://gist.github.com/stefansundin/f9df6c5e0fd184c60709
// @downloadURL https://gist.github.com/stefansundin/f9df6c5e0fd184c60709/raw/launchpad-download-count.user.js
// @version 0.3
// @author Stefan Sundin
// @description Gets the download count of your Launchpad packages.
// @icon https://launchpad.net/favicon.ico
// @match https://launchpad.net/~*/+archive/ubuntu/*
// @match https://launchpad.net/~*/+archive/ubuntu/*/+builds*
// @grant GM_xmlhttpRequest
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_deleteValue
// ==/UserScript==
// Note: Be patient, getting these results take a while, probably due to log analyzing on Launchpad's end.
// Sometimes you have to give it more than one try.
// NOTE: I have not yet tackled pagination, so you may have problems if you browse a big package!
// Patches and comments are welcome!
// To get more detailed stats, including very nice graphs, see this ppa:
// https://launchpad.net/~jfi/+archive/ubuntu/ppastats
// Configuration options:
var print_n_daily = GM_getValue('config_print_n_daily', 3);
// To change config, uncomment this:
//GM_setValue('config_print_n_daily', print_n_daily=15);
// This should preserve the value, even when the script updates.
// Go back to defaults with: (requires two refreshes)
//GM_deleteValue('config_print_n_daily');
var api = 'https://api.launchpad.net/1.0/'; // this api version has to be updated before April 2015, see https://launchpad.net/+apidoc/
var paths = window.location.pathname.split('/');
var user = paths[1];
var pkg = paths[4];
var total_count = 0;
var total_count_div = document.createElement('div');
var fetched = [];
var spinner = "data:image/gif;base64,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";
function add_to_total(n) {
total_count = total_count+n;
total_count_div.innerHTML = 'Total downloads: '+total_count;
}
function get_rows() {
return document.getElementsByClassName('listing')[0].getElementsByClassName('build-row');
}
// sorts result from getDailyDownloadTotals
function sort_dates(o) {
var a = [];
for (d in o) {
a.push(d);
}
return a.map(function(d) {
return { date: d, count: o[d] };
}).sort(function(a, b) {
return new Date(b.date) - new Date(a.date);
});
}
function getDownloadCount(pub_id, ver, arch, entry) {
return function(res) {
var key = [user, pkg, ver, arch].join('/');
var real_pub_id = GM_getValue(key, null);
if (fetched.indexOf(key) != -1) return;
var rows = get_rows();
var row = null;
var i;
for (i=0; i < rows.length; i++) {
var text = rows[i].getElementsByTagName('a')[0].textContent;
if (text.indexOf(arch) == -1 || text.indexOf(ver) == -1) continue;
row = rows[i];
break;
}
if (row == null) return;
var build_id = row.getElementsByTagName('a')[0].pathname.match(/\/(\d+)$/)[1];
var td = row.getElementsByTagName('td')[2];
if (res.status != 200) {
console.log(res);
if (real_pub_key == null) {
GM_deleteValue(key);
}
setTimeout(function() {
if (fetched.indexOf(key) != -1) return;
if (td.childNodes[0].tagName == "IMG") {
td.removeChild(td.firstChild);
}
if (td.childNodes.length > 0) {
td.appendChild(document.createElement('br'));
}
td.appendChild(document.createTextNode('Error ('+res.status+')'));
}, 5000);
return;
}
fetched.push(key);
//GM_setValue(build_id, pub_id);
GM_setValue(key, pub_id);
var n = parseInt(res.responseText, 10);
td.innerHTML = 'Downloads: '+n;
add_to_total(n);
console.log(pub_id, ver, arch, n);
if (n == 0 || print_n_daily == 0) return;
GM_xmlhttpRequest({
url: api+[user, '+archive', paths[3], pkg, '+binarypub', pub_id].join('/')+'?ws.op=getDailyDownloadTotals',
// e.g. https://api.launchpad.net/1.0/~stefansundin/+archive/ubuntu/truecrypt/+binarypub/50830085?ws.op=getDailyDownloadTotals
method: 'GET',
onload: function(res) {
var daily = sort_dates(JSON.parse(res.responseText));
console.log('daily', ver, arch, daily);
for (var i in daily.slice(0, print_n_daily)) {
td.appendChild(document.createElement('br'));
td.appendChild(document.createTextNode(daily[i].date+': '+daily[i].count));
}
}
});
};
}
function insertAfter(insert, after) {
after.parentNode.insertBefore(insert, after.nextSibling);
}
if (paths.length == 5) {
var links = document.getElementsByClassName('menu-link-packages');
console.log(links);
var a = document.createElement('a');
a.href = '+builds?build_state=built';
a.className = 'sprite info';
a.appendChild(document.createTextNode('View download counts'));
links[0].parentNode.appendChild(a.cloneNode(true));
var li = document.createElement('li');
li.appendChild(a.cloneNode(true));
insertAfter(li, links[1].parentNode);
}
else {
var rows = get_rows();
if (rows.length != 0) {
var i;
var gif = document.createElement('img');
gif.setAttribute('src', spinner);
for (i=0; i < rows.length; i++) {
var row = rows[i];
var td = document.createElement('td');
td.style.verticalAlign = 'middle';
if (row.getElementsByTagName('img')[0].alt == '[FULLYBUILT]') {
td.appendChild(gif.cloneNode());
}
row.appendChild(td);
//var build_id = row.getElementsByTagName('a')[0].pathname.match(/\/(\d+)$/)[1];
//var pub_id = GM_getValue(build_id, null);
//console.log(pub_id);
}
document.getElementById('maincontent').appendChild(total_count_div);
add_to_total(0);
GM_xmlhttpRequest({
url: api+[user, '+archive', pkg].join('/')+'?ws.op=getPublishedBinaries&ws.size=150',
// e.g. https://api.launchpad.net/1.0/~stefansundin/+archive/truecrypt?ws.op=getPublishedBinaries&ws.size=150
method: 'GET',
onload: function(res) {
var json = JSON.parse(res.responseText);
console.log(json);
json.entries.forEach(function(entry) {
var ver = entry.binary_package_version;
var arch = entry.distro_arch_series_link.match(/\/([a-z0-9]+)$/)[1];
var pub_id = entry.self_link.match(/\/(\d+)$/)[1];
var key = [user, pkg, ver, arch].join('/');
var real_pub_id = GM_getValue(key, null);
console.log(real_pub_id, pub_id, key, entry);
if (real_pub_id != null && pub_id != real_pub_id) return;
GM_xmlhttpRequest({
url: api+[user, '+archive', paths[3], pkg, '+binarypub', pub_id].join('/')+'?ws.op=getDownloadCount',
// e.g. https://api.launchpad.net/1.0/~stefansundin/+archive/ubuntu/truecrypt/+binarypub/50830085?ws.op=getDownloadCount
method: 'GET',
onload: getDownloadCount(pub_id, ver, arch, entry)
});
});
}
});
}
}
@LeoIannacone
Copy link

Awesome! thank you

@williamdes
Copy link

This awesome script still works !! 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment