|
@@ -0,0 +1,417 @@
|
|
|
+#!/usr/bin/php
|
|
|
+<?php
|
|
|
+
|
|
|
+/*
|
|
|
+ This program is free software: you can redistribute it and/or modify
|
|
|
+ it under the terms of the GNU General Public License as published by
|
|
|
+ the Free Software Foundation, either version 3 of the License, or
|
|
|
+ (at your option) any later version.
|
|
|
+
|
|
|
+ This program is distributed in the hope that it will be useful,
|
|
|
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
+ GNU General Public License for more details.
|
|
|
+
|
|
|
+ You should have received a copy of the GNU General Public License
|
|
|
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
+*/
|
|
|
+
|
|
|
+$SCRIPTNAME='pfaltgall';
|
|
|
+$SCRIPTVERSION='0.1';
|
|
|
+
|
|
|
+require 'lib/ckratelimit.php';
|
|
|
+require 'lib/httpjson.php';
|
|
|
+
|
|
|
+$configfp=null;
|
|
|
+
|
|
|
+$conf=[
|
|
|
+ 'host'=>null,
|
|
|
+ 'token'=>null,
|
|
|
+ 'accid'=>null
|
|
|
+];
|
|
|
+
|
|
|
+$help=
|
|
|
+"[[[ SYNOPSIS ]]]
|
|
|
+
|
|
|
+ {$SCRIPTNAME} [options] <configuration file path>
|
|
|
+
|
|
|
+[[[ DESCRIPTION ]]]
|
|
|
+
|
|
|
+ This is {$SCRIPTNAME} v{$SCRIPTVERSION}, a CLI PHP script that can generate an html file with
|
|
|
+a gallery from your Pixelfed profile.
|
|
|
+ In order to create it, you just need to login to your Pixelfed account and
|
|
|
+get an app token (Settings -> Applications -> Create new token), then create
|
|
|
+a configuration file for {$SCRIPTNAME} like this (don’t write the «---» lines):
|
|
|
+
|
|
|
+---
|
|
|
+host=your_instance_host
|
|
|
+token=your_token
|
|
|
+---
|
|
|
+
|
|
|
+ For example:
|
|
|
+
|
|
|
+---
|
|
|
+host=pixelfed.social
|
|
|
+token=as7f8a7s0d89f7as97df09a8s7d90f81jkl2h34lkj12h3jkl4
|
|
|
+---
|
|
|
+
|
|
|
+ Then run {$SCRIPTNAME} with the path of the configuration file you have
|
|
|
+created. This will create an «index.html» file that will be ready to be put
|
|
|
+where you want (you can also see it locally, obviously).
|
|
|
+
|
|
|
+[[[ OPTIONS ]]]
|
|
|
+
|
|
|
+ -h, --help
|
|
|
+ Show this help text and exit.
|
|
|
+
|
|
|
+[[[ DISCLAIMER AND LICENSE ]]]
|
|
|
+
|
|
|
+ This program comes with ABSOLUTELY NO WARRANTY; for details see the source.
|
|
|
+ This is free software, and you are welcome to redistribute it under certain
|
|
|
+conditions; see <http://www.gnu.org/licenses/> for details.\n";
|
|
|
+
|
|
|
+for ($i=1; $i<$argc; $i++) {
|
|
|
+ if ($argv[$i]=='-h' || $argv[$i]=='--help') {
|
|
|
+ echo $help;
|
|
|
+ exit(0);
|
|
|
+ } elseif ($argv[$i]=='--make-readme') {
|
|
|
+ file_put_contents(__DIR__.'/README.md',"```text\n{$help}\n```\n");
|
|
|
+ exit(0);
|
|
|
+ } elseif (is_null($configfp)) {
|
|
|
+ $configfp=$argv[$i];
|
|
|
+ } else {
|
|
|
+ eecho("Error: «{$argv[$i]}» is not a valid option and the configuration file has already been set to «{$configfp}» (use «-h» or «--help» to read help).\n");
|
|
|
+ exit(1);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+if (is_null($configfp)) {
|
|
|
+ eecho("Error: you have not specified a config file (use «-h» or «--help» to read help).\n");
|
|
|
+ exit(1);
|
|
|
+}
|
|
|
+
|
|
|
+$fconf=@parse_ini_file($configfp);
|
|
|
+if ($fconf===false) {
|
|
|
+ eecho("Error: {$SCRIPTNAME} could not open configuration file «{$configfp}».\n");
|
|
|
+ exit(1);
|
|
|
+}
|
|
|
+
|
|
|
+$errors=[];
|
|
|
+if (!array_key_exists('host',$fconf))
|
|
|
+ $errors[]="no «host» defined";
|
|
|
+if (!array_key_exists('token',$fconf))
|
|
|
+ $errors[]="no «token» defined";
|
|
|
+if (count($errors)>0) {
|
|
|
+ eecho("Error: {$SCRIPTNAME} has found errors in «{$configfp}» configuration file:\n");
|
|
|
+ foreach ($errors as $val)
|
|
|
+ eecho(" - {$val}\n");
|
|
|
+ eecho("Use «-h» or «--help» to read help.\n");
|
|
|
+ exit(1);
|
|
|
+}
|
|
|
+foreach ($conf as $key=>$val)
|
|
|
+ if (array_key_exists($key,$fconf))
|
|
|
+ $conf[$key]=$fconf[$key];
|
|
|
+//print_r($conf);
|
|
|
+
|
|
|
+$acc=httpjson("https://{$conf['host']}/api/v1/accounts/verify_credentials",null,null,null,null,$conf['token']);
|
|
|
+//print_r($res);
|
|
|
+if (!$acc['ok']) {
|
|
|
+ eecho("Error: {$SCRIPTNAME} could not retrieve the account id associated with the given token ({$acc['errors']}).\n");
|
|
|
+ exit(2);
|
|
|
+}
|
|
|
+ckrl($acc['headers']);
|
|
|
+$acc=$acc['content'];
|
|
|
+
|
|
|
+$urls=[];
|
|
|
+$imgs='';
|
|
|
+$i=0;
|
|
|
+$ic=0;
|
|
|
+do {
|
|
|
+ $i++;
|
|
|
+ echo "\rRetrieving chunk {$i}";
|
|
|
+ $endpoint="https://{$conf['host']}/api/v1/accounts/{$acc['id']}/statuses?limit=40&only_media=1&exclude_replies=1&exclude_reblogs=1";
|
|
|
+ if (isset($max_id)) $endpoint.="&max_id={$max_id}";
|
|
|
+ $res=httpjson($endpoint,null,null,null,null,$conf['token']);
|
|
|
+ //print_r($res);
|
|
|
+ if (!$res['ok']) {
|
|
|
+ eecho("\rError: {$SCRIPTNAME} could not retrieve chunk {$i} of statuses ({$res['errors']}).\n");
|
|
|
+ exit(2);
|
|
|
+ }
|
|
|
+ $count=count($res['content']);
|
|
|
+ if ($count>0) {
|
|
|
+ foreach ($res['content'] as $status) {
|
|
|
+ if (isset($status['created_at']) && preg_match('#^\s+$#',$status['created_at'])!==1) {
|
|
|
+ $date=strtotime($status['created_at']);
|
|
|
+ $date=' ['.date('Y/m/d',$date).']';
|
|
|
+ } else {
|
|
|
+ $date='';
|
|
|
+ }
|
|
|
+ if (isset($status['content']) && preg_match('#^\s+$#',$status['content'])!==1) {
|
|
|
+ $desc=strip_tags($status['content']);
|
|
|
+ $desc=preg_replace('/#\w+/','',$desc);
|
|
|
+ $desc=trim($desc);
|
|
|
+ } else {
|
|
|
+ $desc='';
|
|
|
+ }
|
|
|
+ if (isset($status['media_attachments']) && is_array($status['media_attachments'])) {
|
|
|
+ $ca=count($status['media_attachments']);
|
|
|
+ $ia=0;
|
|
|
+ foreach ($status['media_attachments'] as $attachment) {
|
|
|
+ if (isset($attachment['url'])) {
|
|
|
+ $url=$attachment['url'];
|
|
|
+ $ia++;
|
|
|
+ if (isset($attachment['description']) && preg_match('#^\s+$#',$attachment['description'])!==1)
|
|
|
+ $altdesc=' alt="'.htmlspecialchars(trim($attachment['description']),ENT_QUOTES|ENT_HTML5).'"';
|
|
|
+ else
|
|
|
+ $altdesc='';
|
|
|
+ if ($ca>1)
|
|
|
+ $icnt=" ({$ia}/{$ca})";
|
|
|
+ else
|
|
|
+ $icnt='';
|
|
|
+ $imgs.="<div class=\"page\"><table class=\"imgtab\"><tr><td class=\"imgcel\"><a href=\"{$url}\" name=\"img{$ic}\"><img class=\"img\" src=\"{$url}\"{$altdesc}></a></td></tr><caption class=\"imgcaptcel\">{$desc}{$icnt}{$date}</caption></table></div>\n";
|
|
|
+ $ic++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ $max_id=$res['content'][$count-1]['id'];
|
|
|
+ //echo "count: {$count}; max_id: {$max_id}\n";
|
|
|
+ if ($count<40)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ ckrl($res['headers']);
|
|
|
+} while ($count>0);
|
|
|
+echo "\n";
|
|
|
+
|
|
|
+$title="{$acc['username']}@{$conf['host']}";
|
|
|
+if ($acc['display_name']!='') $title=htmlspecialchars($acc['display_name'],ENT_QUOTES|ENT_HTML5)." ({$title})";
|
|
|
+
|
|
|
+$html='<!DOCTYPE HTML>
|
|
|
+<html lang="en">
|
|
|
+<head>
|
|
|
+<title>'.$title.'</title>
|
|
|
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
|
|
+<meta name="description" content="Album">
|
|
|
+<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1.0, minimum-scale=1.0, user-scalable=yes">
|
|
|
+<meta property="og:image" content="'.$acc['avatar_static'].'">
|
|
|
+<link rel="icon" type="image/png" href="'.$acc['avatar_static'].'">
|
|
|
+<!--
|
|
|
+<meta property="og:image" content="imgs/ogimage.jpg">
|
|
|
+<link rel="icon" type="image/png" href="imgs/icon-16.png" sizes="16x16">
|
|
|
+<link rel="icon" type="image/png" href="imgs/icon-24.png" sizes="24x24">
|
|
|
+<link rel="icon" type="image/png" href="imgs/icon-32.png" sizes="32x32">
|
|
|
+<link rel="icon" type="image/png" href="imgs/icon-64.png" sizes="64x64">
|
|
|
+<link rel="icon" type="image/png" href="imgs/icon-128.png" sizes="128x128">
|
|
|
+<link rel="apple-touch-icon-precomposed" href="imgs/icon-180.png">
|
|
|
+<link rel="icon" type="image/png" href="imgs/icon-192.png" sizes="192x192">
|
|
|
+<link rel="icon" type="image/png" href="imgs/icon-256.png" sizes="256x256">
|
|
|
+<link rel="icon" type="image/png" href="imgs/icon-512.png" sizes="512x512">
|
|
|
+-->
|
|
|
+<!-- <link rel="stylesheet" type="text/css" href="gallery.css"> -->
|
|
|
+<style>
|
|
|
+* {
|
|
|
+ box-sizing: border-box;
|
|
|
+}
|
|
|
+
|
|
|
+html {
|
|
|
+ scroll-behavior: smooth;
|
|
|
+ height: 100vh;
|
|
|
+}
|
|
|
+
|
|
|
+body {
|
|
|
+ font-family: "sans";
|
|
|
+ font-size: 12pt;
|
|
|
+ background-color: black;
|
|
|
+ color: white;
|
|
|
+ margin: 0;
|
|
|
+ height: 100vh;
|
|
|
+}
|
|
|
+
|
|
|
+a {
|
|
|
+ color: #87decd;
|
|
|
+}
|
|
|
+
|
|
|
+a:focus {
|
|
|
+ outline: none;
|
|
|
+}
|
|
|
+
|
|
|
+p {
|
|
|
+ margin: 0;
|
|
|
+ padding: 0;
|
|
|
+ text-indent: 3mm;
|
|
|
+ line-height: 1.3em;
|
|
|
+}
|
|
|
+
|
|
|
+p.firstp, p.center {
|
|
|
+ text-indent: 0;
|
|
|
+}
|
|
|
+
|
|
|
+p.center {
|
|
|
+ text-align: center;
|
|
|
+ text-wrap: balance;
|
|
|
+ padding: 1em 0 1em 0;
|
|
|
+}
|
|
|
+
|
|
|
+.profile {
|
|
|
+ width: 640px;
|
|
|
+}
|
|
|
+
|
|
|
+.avatar {
|
|
|
+ border-radius: 12px;
|
|
|
+}
|
|
|
+
|
|
|
+hr {
|
|
|
+ display: block;
|
|
|
+ border: none;
|
|
|
+ height: 1px;
|
|
|
+ background-color: #666666;
|
|
|
+ margin: 3mm 0 3mm 0;
|
|
|
+}
|
|
|
+
|
|
|
+#maindiv {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ overflow: auto;
|
|
|
+ scroll-snap-type: y mandatory;
|
|
|
+ scroll-padding: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.page {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ scroll-snap-stop: always;
|
|
|
+ scroll-snap-align: start;
|
|
|
+ scroll-margin: 0;
|
|
|
+ /*border: 1px solid yellow;*/
|
|
|
+}
|
|
|
+
|
|
|
+.imgtab, .imgtab tr, .imgtab td {
|
|
|
+ margin: 0;
|
|
|
+ padding: 0;
|
|
|
+ border: none;
|
|
|
+ border-collapse: collapse;
|
|
|
+ table-layout: fixed;
|
|
|
+ scroll-snap-align: none;
|
|
|
+}
|
|
|
+
|
|
|
+.img {
|
|
|
+ display: block;
|
|
|
+ position: relative;
|
|
|
+ max-width: 92vw;
|
|
|
+ max-height: 92vh;
|
|
|
+ border: 8px solid white;
|
|
|
+ border-bottom: none;
|
|
|
+ scroll-snap-align: none;
|
|
|
+}
|
|
|
+
|
|
|
+.imgcaptcel {
|
|
|
+ background-color: white;
|
|
|
+ color: black;
|
|
|
+ padding: 4px 8px 4px 8px;
|
|
|
+ font-size: 10pt;
|
|
|
+ caption-side: bottom;
|
|
|
+ text-align: left;
|
|
|
+ line-height: 1.2;
|
|
|
+ scroll-snap-align: none;
|
|
|
+}
|
|
|
+
|
|
|
+.textdiv {
|
|
|
+ width: 20cm;
|
|
|
+ max-width: 92%;
|
|
|
+ background-color: #333333;
|
|
|
+ border-radius: 8px;
|
|
|
+ padding: 3mm;
|
|
|
+ scroll-snap-align: none;
|
|
|
+}
|
|
|
+
|
|
|
+#notif {
|
|
|
+ display: none;
|
|
|
+ position: fixed;
|
|
|
+ top: 50%;
|
|
|
+ left: 50%;
|
|
|
+ transform: translate(-50%, -50%);
|
|
|
+ background-color: rgb(51 51 51 / .85);
|
|
|
+ padding: 4px 6px 4px 6px;
|
|
|
+ color: white;
|
|
|
+ border-radius: 6px;
|
|
|
+ font-size: 10pt;
|
|
|
+ z-index: 1;
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
+
|
|
|
+.link {
|
|
|
+ margin-top: 2px;
|
|
|
+ float: right;
|
|
|
+ color: #333333;
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
+
|
|
|
+#tools {
|
|
|
+ /*display: none;*/
|
|
|
+ width: 100%;
|
|
|
+ position: fixed;
|
|
|
+ left: 0;
|
|
|
+ bottom: 0;
|
|
|
+ background-color: rgb(51 51 51 / .85);;
|
|
|
+ padding: 4px 6px 4px 6px;
|
|
|
+ color: white;
|
|
|
+ font-size: 10pt;
|
|
|
+ z-index: 1;
|
|
|
+}
|
|
|
+
|
|
|
+@media only screen and (max-width:15cm) {
|
|
|
+ .img {
|
|
|
+ max-width: 100vw;
|
|
|
+ max-height: 98vh;
|
|
|
+ border: 2px solid white;
|
|
|
+ }
|
|
|
+ .imgcaptcel {
|
|
|
+ padding: 1px 2px 1px 2px;
|
|
|
+ font-size: 8pt;
|
|
|
+ }
|
|
|
+ .textdiv {
|
|
|
+ font-size: 9pt;
|
|
|
+ }
|
|
|
+ .link {
|
|
|
+ margin-top: 0;
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|
|
|
+</head>
|
|
|
+<body>
|
|
|
+<div id="maindiv">
|
|
|
+<div class="page">
|
|
|
+<div class="profile">
|
|
|
+<p class="center"><img src="'.$acc['avatar_static'].'" class="avatar"></p>
|
|
|
+<p class="center"><a href="'.$acc['url'].'">'.$title.'</a></p>
|
|
|
+<p class="center">'.nl2br($acc['note']).'</p>
|
|
|
+</div>
|
|
|
+</div>
|
|
|
+'.$imgs.'
|
|
|
+</div>
|
|
|
+</body>
|
|
|
+</html>
|
|
|
+';
|
|
|
+
|
|
|
+file_put_contents('index.html',$html);
|
|
|
+
|
|
|
+exit(0);
|
|
|
+
|
|
|
+
|
|
|
+function eecho($text) {
|
|
|
+ fwrite(STDERR,$text);
|
|
|
+}
|
|
|
+
|
|
|
+function ckrl($headers) {
|
|
|
+ $rl=ckratelimit($headers);
|
|
|
+ if ($rl['ok'] && $rl['sleep']>0) {
|
|
|
+ echo "\rInfo: reached rate limit, sleeping for {$rl['sleep']} second(s)... ";
|
|
|
+ sleep($rl['sleep']);
|
|
|
+ echo "\n";
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+?>
|