Home Page
Home
Search:

Welcome to the digital realm of Jeffrey Riaboy. Here, you’ll find a curated collection of my endeavors, musings, updates, and assorted reflections. As a self-taught programmer and avid computer enthusiast, I work in C++ as a favorite choice, though the realities of our digital world have led me to become proficient in dozens of other languages.

Programming has been a lifelong journey for me which I have been hacking on since before I can rememember. It is my passion, profession, first love, and my constant challenge, offering both fulfillment and frustration in equal measure.

This space is dedicated to sharing insights, innovations, and inspirations I’ve gathered along the way. My aim is for you to discover something here that sparks your interest or serves your needs, as that is the driving force behind my commitment to compile and share this content. Dive in and explore. Your presence is highly appreciated.

Dakusan~
The original (well... last) intro page to my website before this became the home. It is a flash portal to my personal sites of the past.
Intro
[1999-2001?] My ancient NES emulator made in Visual Basic (which was made to prove the power and flexibility [not speed] of the language).
HyNes
[2002] A chronicle of my experiences and tinkering from early ’02 to early ’04 on an addictive yet horribly crappy MMORPG. Site also has some nice “hacking”/reverse engineering tutorials.
Ragnarok Hacking
I’ve temporarily set this to link to the Projects section of this website until I’m ready to announce the new website this will link to.
Projects
Updates Archive
Updated:04/15/24
The nulltypes system has been overhauled so all null types are under a generic type named NullType in the top level package. For example, instead of using nulltypes.NullUint8 you would now use NullType[uint8]. This also really helped clean up the null types code. This is a version breaking change, hence the minor version number update.

Other minor changes:
  • Readme file and package information has been updated with the following changes:
    • The type support section has been redone for clarity
    • The structs in the code examples have had the members labeled to explain their used supported type
  • Marshled JSON strings are now properly json escaped
  • Added bypass for my RawBytes bug fix, now that it has been fixed in go v1.23
  • Removed test case that is no longer compatible with go 1.21+
Download Content
Section: Misc > Resume
Updated:03/23/24

I made a pretty massive overhaul of my resume. Both the PDF and the copy on this site’s resume section have been updated.

Download Content
Updated:03/16/24

GoFasterSQL is a tool designed to enhance the efficiency and simplicity of scanning SQL rows into structures in Go[lang].

While this project was first released in December of 2023 on github and has already had 7 releases there, I’ve finally gotten around to adding it here. I don’t anticipate there being many more releases any time soon as the project is feature complete.

Download Content
Post Archive
RSS Feed
Checking permissions before updating Flatpaks
AI IS FUCKING CRAZY

I wanted to make a script to check if flatpak update app permissions have changed before updating them. I decided to use ChatGPTs newest model, 01-preview for this task and holy crap am I impressed. I had to give it 14 commands to get to the final product and it seems to work great. That number could have been reduced quite a bit had I done things a bit differently.

I still had to find the problems, look up reference docs to correct it, and even debug in an IDE a little. But just telling it where the problems were it got there in the end, and its user interaction output is way better than what i was planning on doing.

Anywho, here are the commands I gave it, followed by the code it gave me in the end. I have not throughly checked and tested the code, but it seems to work from a few quick trials. For demonstration purposes, I have left all of my original typos uncorrected below.


Write me a program in php that checks if there are any updates for my installed flatpaks. and then for each flatpak check and see if the permissions have changed between my current version and the new version. report to me the changes. then ask me if i want to continue with the updates.
your code doesnt work.
first $command = 'flatpak list --app --updates --columns=application,app,branch,origin,installation'; should be flatpak remote-ls --updates --columns=application,app,branch,origin (installation is an invalid column)
and then flatpak info --show-permissions doesnt give the original permissions. you have to get the original non-user-modified permissions from /var/lib/flatpak
the line if (empty($output)) { is incorrect, it should be if (count($output)) { as $output is an array, not a string
$columns = preg_split('/\s{2,}/', trim($line)); is incorrect. its tab delimited, so it just needs to be explode("\t", trim($line));
you also have a problem with the $metadataPath. its not always in /var/lib/flatpak/app/ . It may be in /var/lib/flatpak/runtime/. You can confirm which directory it should be in with the options column having "runtime" as an option.
in the --columns for your first command, "application" should be "name"
actually, you cant get the options from flatpak remote-ls --updates . you have to get the options from flatpak list
flatpak remote-info does not have an option --show-permissions
in your flatpak remote-info command, there is no --branch option.
there is no --assumeno option
there is no --dry-run permission. perhaps use --no-deploy?
you are incorrect. flatpak remote-info does not have an option --show-permissions . we already went over that. perhaps flatpak remote-info --show-metadata to get the permissions
flatpak remote-info does not have --branch
when checking the metadata, do not compare the info under [Application]
also, move the "Get architecture" line above its for loop
you have a problem with exec($remoteInfoCommand, $remoteOutput); you need to always empty $remoteOutput before running the exec. Otherwise, it appends the data to what is already there.


<?php
/**
 * This script checks for Flatpak updates and reports permission changes.
 */

// Function to parse permissions from Flatpak metadata
function parsePermissions($content) {
    $permissions = [];
    $lines = is_array($content) ? $content : explode("\n", $content);
    $currentSection = '';
    $skipSections = ['Application']; // Sections to skip

    foreach ($lines as $line) {
        $line = trim($line);

        if (empty($line)) {
            continue;
        }

        // Check for section headers
        if (preg_match('/^\[(.*)\]$/', $line, $matches)) {
            $currentSection = $matches[1];

            // Skip the [Application] section
            if (in_array($currentSection, $skipSections)) {
                $currentSection = '';
                continue;
            }

            $permissions[$currentSection] = [];
        } elseif ($currentSection !== '') {
            // Only process lines within non-skipped sections
            $parts = explode('=', $line, 2);
            if (count($parts) == 2) {
                $key = $parts[0];
                $values = explode(';', trim($parts[1], ';'));
                $permissions[$currentSection][$key] = $values;
            } else {
                // Handle keys without '=' (e.g., single permissions)
                $permissions[$currentSection][$line] = [];
            }
        }
    }

    return $permissions;
}

// Function to compare permissions
function comparePermissions($current, $new) {
    $differences = [];

    // Get all sections
    $sections = array_unique(array_merge(array_keys($current), array_keys($new)));

    foreach ($sections as $section) {
        $currentSection = isset($current[$section]) ? $current[$section] : [];
        $newSection = isset($new[$section]) ? $new[$section] : [];

        // Get all keys in this section
        $keys = array_unique(array_merge(array_keys($currentSection), array_keys($newSection)));

        foreach ($keys as $key) {
            $currentValues = isset($currentSection[$key]) ? $currentSection[$key] : [];
            $newValues = isset($newSection[$key]) ? $newSection[$key] : [];

            // Compare values
            $added = array_diff($newValues, $currentValues);
            $removed = array_diff($currentValues, $newValues);

            if (!empty($added) || !empty($removed)) {
                $differences[$section][$key] = [
                    'added' => $added,
                    'removed' => $removed,
                ];
            }
        }
    }

    return $differences;
}

// Function to display permission differences
function displayDifferences($appId, $differences) {
    echo "Permission changes for $appId:\n";
    foreach ($differences as $section => $keys) {
        echo "  [$section]\n";
        foreach ($keys as $key => $changes) {
            if (!empty($changes['added'])) {
                echo "    + $key = " . implode(';', $changes['added']) . "\n";
            }
            if (!empty($changes['removed'])) {
                echo "    - $key = " . implode(';', $changes['removed']) . "\n";
            }
        }
    }
    echo "\n";
}

// Get architecture (moved above the loop)
$archCommand = 'flatpak --default-arch';
exec($archCommand, $archOutput);
$arch = trim(implode('', $archOutput));

// Step 1: Get the list of installed Flatpaks
$installedCommand = 'flatpak list --columns=application,branch,origin,options';
exec($installedCommand, $installedOutput);

// Remove header line if present
if (!empty($installedOutput) && strpos($installedOutput[0], 'Application ID') !== false) {
    array_shift($installedOutput);
}

// Build an associative array of installed applications
$installedApps = [];

foreach ($installedOutput as $line) {
    // The output is tab-delimited
    $columns = explode("\t", trim($line));
    if (count($columns) >= 4) {
        $appId = $columns[0];
        $branch = $columns[1];
        $origin = $columns[2];
        $options = $columns[3];

        $installedApps[$appId] = [
            'appId' => $appId,
            'branch' => $branch,
            'origin' => $origin,
            'options' => $options,
        ];
    }
}

// Get the list of available updates
$updatesCommand = 'flatpak remote-ls --updates --columns=application';
exec($updatesCommand, $updatesOutput);

// Remove header line if present
if (!empty($updatesOutput) && strpos($updatesOutput[0], 'Application ID') !== false) {
    array_shift($updatesOutput);
}

// Build a list of applications that have updates
$updatesAvailable = array_map('trim', $updatesOutput);

if (empty($updatesAvailable)) {
    echo "No updates available for installed Flatpaks.\n";
    exit(0);
}

$permissionChanges = [];

foreach ($updatesAvailable as $appId) {
    if (!isset($installedApps[$appId])) {
        // The updated app is not in the installed apps list, skip it
        continue;
    }

    $app = $installedApps[$appId];
    $branch = $app['branch'];
    $origin = $app['origin'];
    $options = $app['options'];

    // Determine if it's an app or runtime
    $isRuntime = strpos($options, 'runtime') !== false;

    // Paths to the metadata files
    if ($isRuntime) {
        $metadataPath = "/var/lib/flatpak/runtime/$appId/$arch/$branch/active/metadata";
    } else {
        $metadataPath = "/var/lib/flatpak/app/$appId/$arch/$branch/active/metadata";
    }

    // Check if the metadata file exists
    if (!file_exists($metadataPath)) {
        echo "Metadata file not found for $appId. Skipping.\n";
        continue;
    }

    // Read current permissions from the metadata file
    $metadataContent = file_get_contents($metadataPath);
    $currentPermissions = parsePermissions($metadataContent);

    // Get new metadata from remote
    $ref = $appId . '/' . $arch . '/' . $branch;
    $remoteInfoCommand = 'flatpak remote-info --show-metadata ' . escapeshellarg($origin) . ' ' . escapeshellarg($ref);

    // Clear $remoteOutput before exec()
    $remoteOutput = [];
    exec($remoteInfoCommand, $remoteOutput);

    if (empty($remoteOutput)) {
        echo "Failed to retrieve remote metadata for $appId. Skipping.\n";
        continue;
    }

    // Parse new permissions from the remote metadata
    $newPermissions = parsePermissions($remoteOutput);

    // Compare permissions
    $differences = comparePermissions($currentPermissions, $newPermissions);

    if (!empty($differences)) {
        $permissionChanges[$appId] = $differences;
        displayDifferences($appId, $differences);
    }
}

// If there are no permission changes, inform the user
if (empty($permissionChanges)) {
    echo "No permission changes detected in the available updates.\n";
}

// Ask user if they want to proceed
echo "Do you want to proceed with the updates? (y/N): ";
$handle = fopen("php://stdin", "r");
$line = fgets($handle);
$answer = trim(strtolower($line));

if ($answer == 'y' || $answer == 'yes') {
    // Proceed with updates
    echo "Updating Flatpaks...\n";
    passthru('flatpak update -y');
} else {
    echo "Updates canceled.\n";
}

fclose($handle);
?>
Plex recover posters & art from previous backup

My Plex server decided to go through my database and screw with all the posters and art, so I made the below script which can be used to recover them. You should only use this if doing a full revert to backup is not a viable option. Do also note that if you have refreshed the metadata on any tv series since the backup, it could cause issues with the episode posters.


#Run the following commands as root sudo su #User variables SERVICE_NAME='plexmediaserver' BACKUP_LOCATION='/ROOT_BACKUP_LOCATION/' PLEX_USER='plex' #Derived variables (Slashes at end of directory paths are required) SQLITE3_PATH="/usr/lib/$SERVICE_NAME/Plex SQLite" DB_PATH="/var/lib/$SERVICE_NAME/Library/Application Support/Plex Media Server/Plug-in Support/Databases/com.plexapp.plugins.library.db" IMG_PATH="/var/lib/$SERVICE_NAME/Library/Application Support/Plex Media Server/Metadata/" CACHE_PATH="/var/lib/$SERVICE_NAME/Library/Application Support/Plex Media Server/Cache/PhotoTranscoder/" #Stop the plex service until we have updated everything echo "Stopping Plex" systemctl stop "$SERVICE_NAME" #Set the (user_thumb_url, user_art_url, and updated_at) from the old database in the new database. #The updated_at is required to match the cached images echo "Updating database" sudo -u "$PLEX_USER" "$SQLITE3_PATH" "$DB_PATH" -cmd "ATTACH DATABASE '$BACKUP_LOCATION$DB_PATH' as OLDDB" "
UPDATE metadata_items AS NMDI SET user_thumb_url=OMDI.user_thumb_url, user_art_url=OMDI.user_art_url, updated_at=OMDI.updated_at FROM OLDDB.metadata_items AS OMDI WHERE NMDI.id=OMDI.id
"
#Copy the posters and art from the old directory to the new directory echo "Copying posters" cd "$BACKUP_LOCATION$IMG_PATH" find -type d \( -name "art" -o -name "posters" \) | xargs -n100 bash -c 'rsync -a --relative "$@" "'"$IMG_PATH"'"' _ #Copy the cached images over echo "Copying cache" rsync -a "$BACKUP_LOCATION$CACHE_PATH" "$CACHE_PATH" #Restart plex echo "Starting Plex" systemctl start "$SERVICE_NAME"
Some useful Plex commands

The below commands are tailored for Linux, as file paths are different for Windows. They need to be ran with the sudo -u plex prefix for proper user access permissions.

Get list of libraries
'/usr/lib/plexmediaserver/Plex Media Scanner' --list

Force library intro dection
'/usr/lib/plexmediaserver/Plex Media Scanner' --analyze --manual --server-action intros --section LIBRARY_ID

Force library credit dection
'/usr/lib/plexmediaserver/Plex Media Scanner' --analyze --manual --server-action credits --section LIBRARY_ID

Export media list
sqlite3 '/var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Plug-in Support/Databases/com.plexapp.plugins.library.db' '
SELECT MDI.id, file, title, MDI."index", hints, GROUP_CONCAT(T.text) AS taggings
FROM media_parts AS MP
INNER JOIN media_items AS MI ON MI.id=MP.media_item_id
INNER JOIN metadata_items AS MDI ON MDI.id=MI.metadata_item_id
LEFT JOIN taggings as T ON T.metadata_item_id=MDI.id AND T.text IN ("credits", "intro")
GROUP BY MDI.id'
This outputs the following (column separated by pipe “|”) for each individual media item:
  1. Metadata item ID
  2. File path
  3. Title
  4. Episode Number
  5. Hints (e.x. =&episode=1&episodic=1&season=1&show=Ninja%20Turtles&show_name=Ninja%20Turtles&year=2002)
  6. “credits” and “intros” tags (if the credits and intros timestamps have been calculated)