A Custom Server for Shottr

I wrote a PHP script to act as custom cloud server for receiving and storing screenshots from shottr

Created: by Pradeep GowdaUpdated:Aug 11, 2024Tagged: php · utility · chatgpt · shottr .

Shottr is a screenshotter, annotation tool for MacOS. One of the features available for “Friends of the Proejct” (a paid tier) is the ability to upload the screenshot to their cloud. While handy, I want to own my data.

The developer of this application supports that option via setting a custom web server via command line.

To use a custom screenshot uploading endpoint

$ defaults write cc.ffitch.shottr uploadEndpoint 'https://mysite.com/endpoint'

I wrote a simple PHP web application to act as the server that will receive the upload and save it to a directory.

I have modified nginx so that the screenshot can be served from under the screenshots directory on this website.

The PHP code is given below as reference. I wrote most of this with the help of GPT.

Notes:

<?php

// Define the destination directory
$uploadDirectory = "/var/www/www.yoursite.com/screenshots/";
// Image Directory
$imageUrlBase = "https://www.yoursite.com/screenshots/";
// Log the information to a log file
$logFile = "/tmp/fileuploader.txt";

//timestamp
$timestamp = date("Y-m-d H:i:s");
$now = date("YmdHis");

// https://www.darklaunch.com/base58-encode-and-decode-using-php-with-example-base58-encode-base58-decode.html
function base58_encode($num)
{
    $alphabet = "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ";
    $base_count = strlen($alphabet);
    $encoded = "";

    while ($num >= $base_count) {
        $div = $num / $base_count;
        $mod = $num - $base_count * intval($div);
        $encoded = $alphabet[$mod] . $encoded;
        $num = intval($div);
    }

    if ($num) {
        $encoded = $alphabet[$num] . $encoded;
    }

    return $encoded;
}

function base58_decode($num)
{
    $alphabet = "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ";
    $len = strlen($num);
    $decoded = 0;
    $multi = 1;

    for ($i = $len - 1; $i >= 0; $i--) {
        $decoded += $multi * strpos($alphabet, $num[$i]);
        $multi = $multi * strlen($alphabet);
    }

    return $decoded;
}

function convertToWebP($inputFile, $outputFile, $quality = 80)
{
    $command = "cwebp -q $quality $inputFile -o $outputFile";
    exec($command, $output, $returnCode);

    if ($returnCode !== 0) {
        throw new Exception("Failed to convert image to WebP format.");
    }
}

// Function to log data to the file
function logData($data)
{
    global $logFile;
    global $timestamp;

    // Get the current timestamp
    $timestamp = date("Y-m-d H:i:s");

    // Format the data
    $logData = "[$timestamp] " . $data . PHP_EOL;

    // Append the data to the log file
    file_put_contents($logFile, $logData, FILE_APPEND);
}

// Check if a file was uploaded
if (!isset($_FILES["file"])) {
    echo "ERR: No file uploaded.";
    exit();
}

// Get the uploaded file details
$file = $_FILES["file"];
$fileName = $file["name"];
$tmpFilePath = $file["tmp_name"];
$fileContent = file_get_contents($tmpFilePath);
$newFileName = base58_encode($now) . ".webp";
$target = $uploadDirectory . $newFileName;

try {
    // Convert the uploaded image to WebP format
    convertToWebP($tmpFilePath, $target);
    unlink($tmpFilePath);
} catch (Exception $e) {
    $msg =
        "ERR: Failed to convert and save the uploaded file - " .
        $e->getMessage() .
        PHP_EOL;
    echo $msg;
    unlink($tmpFilePath);
    exit();
}

$logData =
    "Client IP=" .
    $_SERVER["REMOTE_ADDR"] .
    " Filename=" .
    $newFileName .
    PHP_EOL;
logData($logData);
$imageURL = $imageUrlBase . $newFileName;
echo "SUCCESS: " . $imageURL;
?>

The corresponding nginx config is:

    location /screenshots {
        alias /var/www/www.yoursite.com/screenshots;
    }