Last active
November 17, 2023 14:09
-
-
Save nathanlesage/4033664c1e5cd2471e7b7d15a3bcf90d to your computer and use it in GitHub Desktop.
This PHP file simply requires an online web server and some variables by you and it will show you the overall statistics of your repository.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* | |
* GitHub Download Counter page | |
* Created by Hendrik Erz <[email protected]> | |
* Licensed via GNU GPL v3 | |
*/ | |
// CONFIG | |
$repo = 'Zettlr/Zettlr'; // The repository | |
$highlight = '#3e6dd6'; // The highlight color for the page. | |
// Calculate a few things | |
$is_light = hexdec(substr($highlight, 1)) > 0xffffff * 0.5; | |
$contrast_strong = ($is_light) ? '#000' : '#fff'; | |
$contrast_soft = ($is_light) ? '#444' : '#ddd'; | |
// HELPER FUNCTIONS | |
function cURL ($url) { | |
$ch = curl_init(); | |
curl_setopt($ch, CURLOPT_URL, $url); | |
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); | |
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT,1); | |
// GitHub requires a user agent header. So we'll just take something. | |
curl_setopt($ch, CURLOPT_HTTPHEADER, array("User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.89 Safari/537.36")); | |
$result = curl_exec($ch); | |
curl_close($ch); | |
return $result; | |
} | |
function retrieveReleases ($repo) { | |
$today = date('Y-m-d'); | |
$cache_file = "cache_$today.json"; | |
if (file_exists($cache_file)) { | |
$cache = file_get_contents($cache_file); | |
if ($cache !== false) { | |
// Return the cache | |
return json_decode($cache); | |
} | |
} else { | |
// We will create the cache later on -- remove other cache files | |
$files = scandir('.'); | |
if ($files !== false) { | |
foreach ($files as $file) { | |
if (preg_match('/cache_\d{4}-\d{2}-\d{2}\.json/', $file) === 1) { | |
unlink($file); | |
} | |
} | |
} | |
} | |
// First, let's grab some information on the repo! | |
$curl_result = cURL("https://api.github.com/repos/" . $repo); | |
if ($curl_result === false) { | |
return false; | |
} | |
$repo_info = json_decode($curl_result); | |
// Next on: The repo itself. NOTE: We will simply create a new array key for | |
// the releases. | |
// We have to account for the Github v3 pagination, which limits output to | |
// 30 entries. | |
$repo_info->all_releases = []; | |
$page = 0; | |
do { | |
$page++; | |
$releases = cURL("https://api.github.com/repos/" . $repo . "/releases?page=" . $page); | |
if ($releases === false) { | |
return false; | |
} | |
$releases = json_decode($releases); | |
$repo_info->all_releases = array_merge($repo_info->all_releases, $releases); | |
} while (sizeof($releases) > 0); | |
// Cache the result | |
$cache_contents = json_encode($repo_info); | |
file_put_contents($cache_file, $cache_contents); | |
// Return the cache | |
return $repo_info; | |
} | |
// Helper function for delimiting numbers over 1.000 in output | |
function localise_number ($number) { | |
if ($number < 1000) { | |
return $number; | |
} | |
$delim = ','; | |
$ret = (string) $number; | |
$cnt = 0; | |
for ($i = strlen($ret) - 1; $i > 0; $i--) { | |
$cnt++; | |
if ($cnt === 3) { | |
$ret = substr($ret, 0, $i) . $delim . substr($ret, $i); | |
$cnt = 0; | |
} | |
} | |
return $ret; | |
} | |
function isWindowsExe ($filename) { | |
return ( | |
str_ends_with($filename, '.exe') || | |
str_ends_with($filename, '.msi') | |
); | |
} | |
function isMacOSApp ($filename) { | |
return ( | |
str_ends_with($filename, '.dmg') || | |
str_ends_with($filename, '.pkg') | |
); | |
} | |
function isLinuxExec ($filename) { | |
return ( | |
str_ends_with($filename, '.deb') || | |
str_ends_with($filename, '.rpm') || | |
str_ends_with($filename, '.AppImage') | |
); | |
} | |
function totalDownloadsPerPlatform ($releases) { | |
$release_count = [ | |
'Windows' => 0, | |
'macOS' => 0, | |
'Linux' => 0, | |
'Other Files' => 0, | |
]; | |
foreach($releases as $release) { | |
foreach($release->assets as $asset) { | |
if (isWindowsExe($asset->name)) { | |
$release_count['Windows'] += $asset->download_count; | |
} else if (isMacOSApp($asset->name)) { | |
$release_count['macOS'] += $asset->download_count; | |
} else if (isLinuxExec($asset->name)) { | |
$release_count['Linux'] += $asset->download_count; | |
} else { | |
$release_count['Other Files'] += $asset->download_count; | |
} | |
} | |
} | |
return $release_count; | |
} | |
function readableKB ($kb) { | |
$ret_val = $kb; | |
$unit = 'KB'; | |
if ($ret_val > 1000) { | |
$ret_val /= 1000; | |
$unit = 'MB'; | |
} | |
if ($ret_val > 1000) { | |
$ret_val /= 1000; | |
$unit = 'GB'; | |
} | |
if ($ret_val > 1000) { | |
$ret_val /= 1000; | |
$unit = 'TB'; | |
} | |
$ret_val = round($ret_val, 2); | |
return "$ret_val $unit"; | |
} | |
// Now, actually retrieve the releases | |
$repo_info = retrieveReleases($repo); | |
$downloads = totalDownloadsPerPlatform($repo_info->all_releases); | |
?> | |
<!DOCTYPE html> | |
<html lang="en" dir="ltr"> | |
<head> | |
<meta charset="utf-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1"> | |
<title><?php echo $repo_info->name; ?> Download Count</title> | |
<style type="text/css"> | |
* { | |
box-sizing:border-box; | |
} | |
html, body { | |
margin:0; | |
padding:0; | |
} | |
body { | |
font-family: sans-serif; | |
cursor:default; | |
} | |
h1, p { | |
margin:0.5em; | |
line-height: 120%; | |
} | |
a, a:visited, a:focus { | |
color: <?php echo $highlight; ?>; | |
text-decoration: none; | |
transition:0.5s color ease; | |
} | |
a:hover { | |
color: #999; | |
transition:0.5s color ease; | |
} | |
footer { | |
position: fixed; | |
width: 100%; | |
bottom: 0; | |
background-color: <?php echo $highlight; ?>; | |
color: <?php echo $contrast_strong; ?>; | |
display: block; | |
line-height: 200%; | |
text-align: center; | |
} | |
footer a { | |
color: <?php echo $contrast_soft; ?> !important; | |
} | |
div.container { | |
width:100%; | |
} | |
@media (min-width:600px) { div.container { width: 80%; margin:0 auto; } } | |
@media (min-width:800px) { div.container { width: 60%; margin:0 auto; } } | |
@media (min-width:1000px) { div.container { width: 50%; margin:0 auto; } } | |
@media (min-width:1200px) { div.container { width: 40%; margin:0 auto; } } | |
table { | |
border-collapse: collapse; | |
width: 100%; | |
} | |
th, td { | |
padding: 4px 20px; | |
border-bottom: 1px solid #333; | |
} | |
td.number { | |
text-align: right; | |
padding-right: 20px; | |
} | |
td.filename { | |
font-size: 80%; | |
color: <?php echo $highlight; ?>; | |
} | |
td.divider { | |
background-color: <?php echo $highlight; ?>; | |
opacity: 0.2; | |
border: none; | |
border-bottom: 1px solid white; | |
border-bottom-left-radius: 10px; | |
border-bottom-right-radius: 10px; | |
} | |
tr { | |
border-bottom: 1px solid #333; | |
} | |
tr:hover { | |
background-color: #ddd; | |
} | |
span.date { | |
font-style: italic; | |
font-weight: normal; | |
font-size: 75%; | |
} | |
a.topic-button { | |
display: inline-block; | |
margin: 5px; | |
padding: 5px 10px; | |
border-radius: 4px; | |
background-color: <?php echo $highlight; ?>; | |
color: <?php echo $contrast_strong; ?>; | |
opacity: 0.8; | |
} | |
a.topic-button:hover { | |
opacity: 1; | |
} | |
div#card-container { | |
display: flex; | |
} | |
div.card { | |
flex: 1 1 0px; | |
padding: 10px; | |
margin: 0 10px; | |
border-radius: 10px; | |
background-color: <?php echo $highlight; ?>; | |
color: <?php echo $contrast_strong; ?>; | |
text-align: center; | |
font-weight: bold; | |
} | |
hr { | |
margin: 20px 0px; | |
} | |
</style> | |
</head> | |
<body> | |
<div class="container"> | |
<h1><?php echo $repo_info->name; ?> Download Count</h1> | |
<p><em><?php echo $repo_info->description; ?></em> | <a href="<?php echo $repo_info->html_url; ?>" target="_blank">GitHub</a></p> | |
<p>This page shows overall download counts for all versions of <?php echo $repo_info->name; ?>.</p> | |
<div id="card-container"> | |
<div class="card"> | |
<p>Stargazers</p> | |
<p><?php echo localise_number($repo_info->stargazers_count); ?></p> | |
</div> | |
<div class="card"> | |
<p>Forks</p> | |
<p><?php echo localise_number($repo_info->forks_count); ?></p> | |
</div> | |
<div class="card"> | |
<p>Issues</p> | |
<p><?php echo localise_number($repo_info->open_issues_count); ?></p> | |
</div> | |
<div class="card"> | |
<p>Repository Size</p> | |
<p><?php echo readableKB($repo_info->size); ?></p> | |
</div> | |
</div> | |
<hr> | |
<p> | |
<?php | |
foreach ($repo_info->topics as $topic) { | |
echo "<a href=\"https://github.com/topics/$topic\" class=\"topic-button\" target=\"_blank\">$topic</a>"; | |
} | |
?> | |
</p> | |
<h2>At a Glance</h2> | |
<p> | |
<table> | |
<thead> | |
<tr> | |
<th style="text-align: left;">Platform</th> | |
<th style="text-align: right;">Total Download Count</th> | |
</tr> | |
</thead> | |
<tbody> | |
<?php | |
$total = 0; | |
foreach ($downloads as $platform => $count) { | |
$total += $count; | |
echo "<tr><td>$platform</td><td class=\"number\">" . localise_number($count) . "</td></tr>"; | |
} | |
echo "<tr><th style=\"text-align: left;\">Total downloads:</th><th style=\"text-align: right;\">" . localise_number($total) . "</th></tr>"; | |
?> | |
</tbody> | |
</table> | |
</p> | |
<p> </p> | |
<?php | |
// Additional info: How many days since the first release? | |
$firstRelease = array_reverse($repo_info->all_releases)[0]; // Last index = first release | |
// Get Unix seconds | |
$firstRelease = strtotime($firstRelease->published_at); | |
$daysSinceFirstRelease = round((time() - $firstRelease) / 60 / 60 / 24); | |
$yearsSinceFirstRelease = floor($daysSinceFirstRelease / 365); | |
$downloadsPerDay = round($total / $daysSinceFirstRelease); | |
echo "<p>All <strong>" . localise_number(sizeof($repo_info->all_releases)) . "</strong> releases of $repo_info->name have been downloaded <strong>" . localise_number($total) . "</strong> times.</p>\n"; | |
echo "<p>The first release was made <strong>"; | |
if ($yearsSinceFirstRelease > 0) { | |
echo "$yearsSinceFirstRelease years and " . ($daysSinceFirstRelease % 365) . " days"; | |
} else { | |
echo $daysSinceFirstRelease . " days"; | |
} | |
echo "</strong> ago. On average, $repo_info->name is downloaded <strong>$downloadsPerDay times each day</strong>.</p>"; | |
?> | |
<p> </p> | |
<h2>In Detail</h2> | |
<?php | |
// Now generate. | |
echo "<table>\n<tr><th>Release</th><th style=\"text-align: left;\">Platform</th><th style=\"text-align: right;\">Downloads</th></tr>\n"; | |
// First: Loop through all releases | |
foreach($repo_info->all_releases as $release) { | |
echo "<tr><th rowspan=\"" . (count($release->assets)+1) . "\">$release->tag_name<br><span class=\"date\">" . date("M d, Y, H:i", strtotime($release->published_at)) ."</span></th></tr>\n"; | |
// Sort the assets | |
usort($release->assets, fn($a, $b) => $b->download_count - $a->download_count); | |
// Then loop through all assets ... | |
foreach($release->assets as $asset) { | |
echo "<tr><td title=\"$asset->name\" class=\"filename\">$asset->name</td><td class=\"number\">" . localise_number($asset->download_count) . "</td></tr>\n"; | |
} | |
// Everytime add a spacer row | |
echo "<tr><td class=\"divider\" colspan=\"3\"></td></tr>"; | |
} | |
echo "</table>\n"; | |
?> | |
<div style="margin-bottom: 20%;"> </div> | |
</div> | |
<footer> | |
GitHub Download Counter | Copyright © 2018—<?php echo date("Y") ?> Hendrik Erz | License: <a href="https://www.gnu.org/licenses/gpl-3.0.en.html" target="_blank">GNU GPL v3</a> | <a href="https://gist.github.com/nathanlesage" target="_blank">Download at GitHub Gist</a> | |
</footer> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment