Monday, October 12, 2026

upgrading Moodle 4.5 on Ubuntu 24.04 to Moodle 5.3 on Ubuntu 26.04

Initial backup snapshot

https://allinonelms.com/blog/moodle-5-3

says we have one year time before moving from 4.5 to 5.3, so we can wait for Dec, Moodle 5.3.1, if plugins break. But we went ahead, and plugins broke. 

Our Ubuntu install also needed to be upgraded from 24.04 to 26.04, since MySQL 8.4 is required by Moodle 5.3.

The idea is to use sudo do-release-upgrade
(if no upgrade is available, it will tell us)
after taking a backup snapshot.

Duck.ai gave the detailed steps for 

  1. create a snapshot of the original OS disk,
  2. generate a new managed disk from that snapshot,
  3. then deploy a new virtual machine using the new managed disk. 
I did only 1. and 2., did not need to do 3. It just took a few seconds to create snapshot and generate a new managed disk from the snapshot, for our 45 GB OS disk. Just to be safe, I stopped the services, then stopped the VM, then took the snapshot.

sudo service apache2 stop
sudo service postgresql stop
sudo service mysql stop


Gemini, on prompting, listed the disadvantages of upgrading vs creating a new VM from scratch, like the need to choose old vs new config files, the need to have downtime, etc. (private link to conversation is here for my reference, https://aistudio.google.com/prompts/1X_RhD-CQTQS1DdVHShfBhK67VxMp6uYk )

Since the CMS PHP code would also be migrated to PHP 8.5 with this upgrade, tested for breaks using Antigravity - just fed in the code, and asked for what to patch. Only the deprecated FILTER_SANITIZE_STRING in login-action.php and a suggested composer update for dependencies

sudo -u www-data composer update --no-dev --optimize-autoloader

(If done with sudo, would need 
sudo chown -R www-data:www-data /var/www/html/vendor
sudo chown www-data:www-data /var/www/html/composer.lock
)

To have a temporary 'Site under Maintenance' message, a cloudflare redirect to a page hosted on github pages, Rules > Overview > Redirect rules - struggled a bit to find the location of the redirect rules page, private link to conversation is at https://aistudio.google.com/prompts/1iFwSbjXsmI1TsYEIIlQcxvDM0VfolK2I 


Errors during the do-release-upgrade

While doing the do-release-upgrade after saving the snapshot, these were the problems observed.

rclone warning - not from ubuntu repos.
will need to reinstall, maybe.

login.defs - chose to upgrade

Postgres said,
Success. Please check that the upgraded cluster works. If it does,
you can remove the old cluster with
    pg_dropcluster 16 main

certbot not upgraded, dependency problems.

Package apache2 is not configured yet. Will defer actions by package libapache2-mod-php8.5.
Processing triggers for dbus (1.16.2-2ubuntu4) ...
Errors were encountered while processing:
 apache2
 python3-certbot-apache
needrestart is being skipped since dpkg has failed
packages have been installed but needrestart is suspended

etc.

dpkg: error processing package python3-certbot-apache (--configure):
 dependency problems - leaving unconfigured
Errors were encountered while processing:
 apache2
 python3-certbot-apache

Upgrade complete

The upgrade has completed but there were errors during the upgrade
process.

To continue please press [ENTER]
=== Command detached from window (Sun Oct 11 04:49:30 2026) ===

Gemini, in this conversation, https://aistudio.google.com/prompts/1ySB2bg3r8w23ODeVyiyw-hyOXi-u8gmI said that we can safely reboot, the issue is with apache (and since apache is required for certbot, that fails too). Apache would be loading the old PHP versions, we have to disable PHP 8.3 and enable PHP 8.5. But

sudo a2dismod php8.3
ERROR: /etc/apache2/mods-enabled/php8.3.load is not a symbolic link, not deleting
ERROR: /etc/apache2/mods-enabled/php8.3.conf is not a symbolic link, not deleting
Module php8.3 disabled.

So somewhere the mods-enabled got turned into real files. Had to find all real files (and not links) with

sudo find /etc/apache2/*-enabled/ -type f

and delete all of them. 

sudo bash -c '
# Fix conf-enabled
for file in /etc/apache2/conf-enabled/*; do
    if [ -f "$file" ] && [ ! -L "$file" ]; then
        name=$(basename "$file")
        if [ -f "/etc/apache2/conf-available/$name" ]; then
            rm -f "$file"
        else
            mv "$file" "/etc/apache2/conf-available/$name"
        fi
        ln -s "../conf-available/$name" "$file"
    fi
done

# Fix mods-enabled
for file in /etc/apache2/mods-enabled/*; do
    if [ -f "$file" ] && [ ! -L "$file" ]; then
        name=$(basename "$file")
        if [ -f "/etc/apache2/mods-available/$name" ]; then
            rm -f "$file"
        else
            mv "$file" "/etc/apache2/mods-available/$name"
        fi
        ln -s "../mods-available/$name" "$file"
    fi
done
'

Now 
sudo apache2ctl configtest
was OK, and restarting apache followed by dpkg 

sudo systemctl restart apache2
sudo dpkg --configure -a

fixed certbot also. Then, since postgres 18 was running fine,

sudo pg_dropcluster 16 main

sudo apt purge "postgresql-16*"
sudo apt autoremove


Clear browser host cache for testing, git upgrade

Flush DNS and test - Edge browser on Linux, method suggested by ChatGPT 

https://chatgpt.com/c/6acb1b41-4d04-83e8-a31f-f6958c4a2bbb
has many more testing and dns tips.

edge://net-internals/#dns
and choose Clear host cache.
and choose Flush socket pools.

Then the actual Moodle upgrade using git,
https://chatgpt.com/c/6acb1c57-0e94-83e8-aa2a-31ae3e8f9ef0
git branch -r
to list the remote branches, and then, following 
or

git branch --track MOODLE_503_STABLE origin/MOODLE_503_STABLE
git checkout  MOODLE_503_STABLE

sudo chown -R azureuser:www-data .
sudo chmod -R 775 .

sudo -u www-data /usr/bin/php admin/cli/upgrade.php 

At this point, after the upgrade completed, we needed to install all the PHP modules needed by Moodle 5.3.


Post-install fixes

Took the help of chatgpt to generate the commands needed to install all the required PHP modules,
sudo apt update

sudo apt install \
  php8.5-cli \
  php8.5-common \
  php8.5-curl \
  php8.5-gd \
  php8.5-intl \
  php8.5-mbstring \
  php8.5-mysql \
  php8.5-xml \
  php8.5-zip

/usr/bin/php --ini # to find the ini file path
Loaded Configuration File: /etc/php/8.5/cli/php.ini


Edit both the cli as well as the apache php.ini files to bump
max_input_vars = 5000

upload_max_filesize = 100M
post_max_size = 110M
memory_limit = 256M
max_execution_time = 300
max_input_time = 300


Needed to change the sites' document root in the sites-available of apache to append /public to the existing DocumentRoot value. Copied the "missing from disk" plugins to the public directory by asking chatgpt to write the commands as below,

cd /var/www/theMoodlegit

for plugin in \
    mod/quiz/accessrule/proctoring \
    admin/tool/tcpdffonts \
    availability/condition/othercompleted \
    availability/condition/stash \
    course/format/onetopic \
    course/format/remuiformat \
    course/format/tiles
do
    destination="public/$plugin"

    if [ -d "$plugin" ] && [ ! -e "$destination" ]; then
        sudo mv "$plugin" "$destination"
        echo "Moved $plugin"
    else
        echo "SKIPPED $plugin — check source and destination"
    fi
done

Had to install composer and run it, since Moodle 5.1 onwards use composer for dependencies. Without doing this, some plugins were complaining of Mustache errors, file not found etc. https://claude.ai/chat/427274e2-d874-4b60-b744-2989d51b6b72

sudo apt install composer

cd /var/www/theMoodlegit
composer install --no-dev --classmap-authoritative # (for each instance.)

Router not configured message still shows up, we need to follow the guide to set it up, would be mandatory for future versions of Moodle.

Lots of warnings like

PHP Deprecated:  Function xml_parser_free() is deprecated since 8.5, as it has no effect since PHP 8.0 in /var/www/theMoodlegit/public/lib/classes/xml_parser.php on line 190

ChatGPT recommends ignoring it till the Moodle team removes it, since Moodle 5.3 officially does not support PHP 8.5.

The installation and fixes so far took from 9 am to 2 pm with an hour's lunch break. The next day, the following were also fixed.

Theme edits and unsupported plugins 

Fix for duplicate logo appearing, by adding these lines to the Raw SCSS of Moove theme,

/* Dark mode is disabled, so hide the duplicate dark logo */
nav.navbar .navbar-brand img.logo.dark { display: none !important; }

Site Administration > Appearance > Themes > (gear icon of Moove theme) > Advanced tab > Raw SCSS textbox

(This duplication was a side-effect of us removing the Connectime branding logo advertisement of the Moove theme in SCSS.) 

Later, users found problems with courses using formattype onetopic (which has not been updated to support Moodle 5.3) - https://claude.ai/chat/5c5a6dd0-e0b9-4bfb-a41f-4b8062fabef6 - changed to topics instead in the database to fix,
UPDATE mdl_course SET format = 'topics' WHERE format = 'onetopic';
(after first trying it for a single course and testing.)

Similar fix for some other courses which were using formattype tiles - in that case, only the edit mode was affected, so the teacher admins could themselves change the course formattype to Multitopic (which is topics in the db) in the Course Settings in the UI.


Thursday, October 08, 2026

interesting results with 60 Moodle users logged in

One of our Ubuntu servers hosting Moodle instances had been bumped from D2asv5 (8 GB RAM) to D32asv5 (128 GB RAM) anticipating 100+ users for a training event. Interestingly, even with 60+ users logged on simultaneously, RAM usage did not cross 8 GB. So, tried using D4asv5 (16 GB RAM) for this standalone server, and then tried even not bumping it up from D2asv5. But with D2 (2 cpu cores), the CPU was continuously pegged at 100%, with load average going up to 15. Probably better to bump up to D4. Logging was via https://github.com/hn-88/log-memory-cpu-usage as mentioned in the previous post, and emails being sent to me similar to this - https://hnsws.blogspot.com/2021/02/scripts-to-alert-admin-when-hard-disk.html

(for my reference, conversation with Gemini to find the number of logged in users - and what they were doing - is at https://aistudio.google.com/prompts/1rp43v9LvY7d-b6pk82fgF0dDVQPWmJh8 )

Suggested database queries were - 

SELECT COUNT(DISTINCT l.userid) AS unique_users_active
FROM mdl_logstore_standard_log l
WHERE l.timecreated BETWEEN [START_UNIX_TIMESTAMP] AND [END_UNIX_TIMESTAMP]; 

to find the number of logged in users, and to see what they were doing, 

SELECT 
    u.username, 
    u.firstname, 
    u.lastname, 
    FROM_UNIXTIME(l.timecreated) AS time_of_action,
    l.eventname, 
    l.component, 
    l.target
FROM mdl_logstore_standard_log l
JOIN mdl_user u ON u.id = l.userid
WHERE l.timecreated BETWEEN [START_UNIX_TIMESTAMP] AND [END_UNIX_TIMESTAMP]
ORDER BY l.timecreated ASC;

Edit: Digging deeper, the limited RAM use is probably due to the MaxRequestWorkers setting in Apache settings as per https://docs.moodle.org/503/en/Performance_recommendations#Apache_Performance

Wednesday, October 07, 2026

convert UTC to IST in Google Sheets

 Running this simple memory and cpu logger on one of our servers, the timezone on that machine is UTC, to view in IST, we just have to add 5 hours and 30 minutes, like

=A2 + TIME(5, 30, 0)

Monday, October 05, 2026

getting started with Antigravity

Got access to Google Antigravity (via a telecom provider free offer for subscribers on 5G in India). This is google's agentic coding platform, where you just tell the agent to do specific tasks, and it will do the rest, like in their intro video. I'm exploring making antigravity write code to make OpenSpace run natively on Mac using OpenGL to Metal translation via MGL - where in the best case scenario, we might be able to see native performance similar to what we found with VMWare's driver.

Thursday, October 01, 2026

increasing per user quota in Google WorkSpace

There was a request from a user to increase the Google WorkSpace storage quota from 15 GB to 100 GB for two accounts. 

I asked Gemini via aistudio.google.com how I can increase the quota of a single user, (without changing their Organizational Unit quota) and it showed me this method. Accordingly, have set 100 GB for the group called higher storage users - the two user accounts have been added as members. Later, I got a confirmation from them that the quota increase was successful.
Here is how the admin can do it:

  1. Create a Group: In the Google Admin console, go to Directory > Groups and create a new group (e.g., "Higher Storage Users"). Add the single user to this group[3].
  2. Navigate to Storage: Go to Menu > Storage[4].
  3. Manage Limits: Under the Storage settings section, click Manage[4].
  4. Select the Group: On the left-hand panel, search for and select the configuration group you just created[3][4].
  5. Set the New Quota: Turn on the User storage limit, enter the new, higher storage amount for that user, and click Save[2][3].
Note: Group limits override Organizational Unit (OU) baseline limits, making this the best way to handle one-off storage increases.[3]

And the references cited were:

Sunday, September 27, 2026

Raspberry Pi reinstall - went back to cloning instead

One of the raspberry pis in our waiting area was not booting. Some files appeared to have been corrupted - the Disks utility in Linux Mint found many errors. 

Thought I would reinstall the OS with the latest version.  Via duck search ai, identified the board - "The Raspberry Pi Model B (2011-era board) has a yellow RCA composite video connector and a full-size SD card slot." 

After reinstalling, "To set your Raspberry Pi Model B to use composite video output by default, you need to edit the /boot/config.txt file and add the line hdmi_ignore_hotplug=1 to disable HDMI output. Additionally, set the video mode by adding sdtv_mode=2 for PAL or sdtv_mode=0 for NTSC, then reboot the device. This will ensure that the composite output is used as the default display."

Still didn't work.

Tried with HDMI, then came to configuration screen after 2-3 minutes.

But saving the locale took more than 5 minutes?

So, trying sdcard integrity with f3


f3write: Can't create file ./1.h2w: Permission denied

so

sudo f3write /media/sssvv/rootfs/

1 hour 35 minutes or so shown to write to the ~52 GB free.

Creating file 16.h2w ... OK! Avg: 9.20 MB/s            
Min: 4.85 MB/s, Max: 12.88 MB/s, 100 samples
Creating file 17.h2w ... OK! Avg: 9.30 MB/s            
Min: 4.89 MB/s, Max: 13.01 MB/s, 100 samples
Creating file 18.h2w ... 33.08% -- 9.04 MB/s -- 1:04:43
...
Creating file 53.h2w ... OK! Avg: 8.32 MB/s            
Min: 4.24 MB/s, Max: 12.81 MB/s, 35 samples
Free space: 16.00 MB
Average sequential write speed: 9.19 MB/s (109773304 sectors / 1:37:13)


The files are seen in the root directory of /media/sssvv/rootfs/ and could be deleted after the test. 

But after reinstall with latest rpi legacy image of 2025, various issues like taking a long time to boot, video out not working even after sudo raspi-config set to video out enabled, etc (using HDMI output)

Instead, just cloning from the working sdcard on the other raspberry pi.


Instead of exactly the same commands, increased block size (and used pigz, which might be irrelevant) to make it faster.

sudo dd if=/dev/sdc bs=16M status=progress | pigz -c > waitingvideos.image.gz
31914983424 bytes (32 GB, 30 GiB) copied, 1914 s, 16.7 MB/s
1902+1 records in
1902+1 records out
31914983424 bytes (32 GB, 30 GiB) copied, 1913.54 s, 16.7 MB/s

# gunzip -c waitingvideos.image.gz | dd of=/dev/sdc status=progress
31911858688 bytes (32 GB, 30 GiB) copied, 6398 s, 5.0 MB/s
62333952+0 records in
62333952+0 records out
31914983424 bytes (32 GB, 30 GiB) copied, 6398.28 s, 5.0 MB/s

107 minutes, or 1:47 hours.



Tuesday, September 22, 2026

email problem - SPF and DKIM records

One of our admins reported a problem with replying to mails - they use Zoho - and copy-pasted Claude's solution - 

1. Modify the SPF record to include the zoho domain also - 
- Current value: v=spf1 include:zeptomail.net ~all
- New value: v=spf1 include:zoho.com include:zeptomail.net ~all

2. Publish the DKIM key. In Zoho Mail Admin Console > Domains > ourdomain.in > Email Configuration > DKIM > add selector. Zoho shows you a long key, which is to be added as a DNS TXT record.

The first time we did it, the DKIM record was sent to me by WhatsApp, and perhaps there was some non-printing character or line-break in the middle of the key, because of which the validator tool at https://mxtoolbox.com/SuperTool.aspx complained of invalid syntax for the DKIM key. Then I was sent the record by email as requested, and this time, copy-pasting it into Cloudflare's dashboard worked fine and the validator passed it. Currently no DMARC records are added, we can add those later if need be. And the problem seems to be solved.

Wednesday, September 16, 2026

updating to MacOS 27

The M4 Mac mini asked to download 13+ GB and update MacOS to version 27, Golden Gate - upgrading from MacOS Tahoe 26.6. Since the office fiber internet was down, I hesitated to start the process using Airtel 5G - but since 5G is supposed to be unlimited on my plan, went ahead.

It took nearly 90 minutes to complete the whole update, probably because while downloading, the 5G network was dropping down to 4G and getting throttled due to exhausting today's quota. On 5G, the download was happening at 30-45 Mbps. 

Interestingly, even after the "Downloading" phase completed and the "Preparing" phase started, there was still plenty of downloading activity as seen in Activity Monitor app. "Preparing" took half an hour. Then 8 minutes on the Apple logo screen while restarting. 

traceroutes just for general interest

I had asked an LLM about the network backhaul used by our ISPs like Airtel, BSNL etc, and was told that while there was no publicly available information about the actual specifications of their backhaul, a traceroute might give more information. Some traceroutes to google.com and nebula.tv from BSNLindicated that they terminated at their Points of Presence - POPs in Chennai - nebula.tv via Cloudflare. With Airtel, google.com seems to go to a Mumbai POP - bom.

traceroute to google.com (142.250.183.174), 64 hops max, 40 byte packets
 1  10.219.86.239 (10.219.86.239)  3.068 ms  3.368 ms  3.003 ms
 2  * * *
 3  192.168.116.2 (192.168.116.2)  64.774 ms  35.220 ms  29.967 ms
 4  * * *
 5  * * *
 6  125.21.99.61 (125.21.99.61)  36.796 ms
    dsl-ncr-dynamic-013.32.23.125.airtelbroadband.in (125.23.32.13)  28.051 ms  44.851 ms
 7  182.79.177.69 (182.79.177.69)  40.855 ms
    116.119.68.247 (116.119.68.247)  47.689 ms
    116.119.161.183 (116.119.161.183)  40.998 ms
 8  * 142.250.169.206 (142.250.169.206)  58.361 ms *
 9  142.250.230.197 (142.250.230.197)  158.451 ms *
    64.233.174.17 (64.233.174.17)  289.370 ms
10  142.251.55.67 (142.251.55.67)  38.116 ms
    142.251.55.230 (142.251.55.230)  233.997 ms
    142.251.55.42 (142.251.55.42)  37.590 ms
11  142.251.55.65 (142.251.55.65)  38.276 ms
    142.251.55.67 (142.251.55.67)  41.114 ms
    172.253.71.132 (172.253.71.132)  37.117 ms
12  bom07s32-in-f14.1e100.net (142.250.183.174)  58.148 ms  35.764 ms
    142.250.62.67 (142.250.62.67)  42.145 ms

Then, a couple of traceroutes to a US server, from BSNL fiber to home network done from a Windows machine

tracert ourdomain.org

Tracing route to ourdomain [redacted]
over a maximum of 30 hops:

  1     1 ms    <1 ms    <1 ms  192.168.1.1
  2     2 ms     4 ms     2 ms  117.192.80.1
  3     4 ms     3 ms     3 ms  10.219.29.194
  4     *        *        *     Request timed out.
  5     *        *        *     Request timed out.
  6    13 ms    13 ms    13 ms  10.200.120.225
  7     *        *        *     Request timed out.
  8    15 ms    14 ms    15 ms  115.110.161.189.static.vsnl.net.in [115.110.161.189]
  9    37 ms    50 ms    37 ms  172.25.199.34
 10    37 ms    36 ms    38 ms  172.28.124.134
 11    39 ms    36 ms    75 ms  172.25.138.206
 12     *        *        *     Request timed out.
 13     *        *        *     Request timed out.
 14     *        *        *     Request timed out.
 15     *      278 ms     *     if-bundle-12-2.qcore2.pvu-paris.as6453.net [80.231.245.13]
 16   272 ms   271 ms     *     ae0.cr2.dfw7.us.zip.zayo.com [64.125.20.63]
 17   272 ms   272 ms   293 ms  ae1.cr2.chd4.us.zip.zayo.com [64.125.25.76]
 18   257 ms     *      353 ms  ae9.cr1.phx4.us.zip.zayo.com [64.125.27.186]
 19     *        *        *     Request timed out.
 20   274 ms   272 ms   273 ms  64.125.199.194.IDIA-409280-003-ZYO.zip.zayo.com [64.125.199.194]
 21   241 ms   246 ms   241 ms  core1-edge2-lag1.phx1.codero.com [216.55.160.6]
 22   254 ms   251 ms   251 ms  216-55-160-81.dedicated.codero.net [216.55.160.81]
(and the next hop was our server in Phoenix.)

So apparently BSNL routed this request via Tata Communications (vsnl), via Paris interconnect to Zayo, and there to Phoenix. Of course, routes would vary from time to time, too.

Trying from a Mac via Airtel 5G network,

traceroute ourdomain.org
traceroute to ourdomain.org (redacted), 64 hops max, 40 byte packets
 1  10.219.86.239 (10.219.86.239)  3.615 ms  2.808 ms  2.575 ms
 2  * * *
 3  192.168.116.2 (192.168.116.2)  28.903 ms  32.648 ms  70.657 ms
 4  * 192.168.116.49 (192.168.116.49)  42.654 ms  33.930 ms
 5  * * *
 6  dsl-ncr-dynamic-013.32.23.125.airtelbroadband.in (125.23.32.13)  49.859 ms
    125.21.99.61 (125.21.99.61)  60.570 ms
    dsl-ncr-dynamic-013.32.23.125.airtelbroadband.in (125.23.32.13)  27.138 ms
 7  116.119.167.20 (116.119.167.20)  249.939 ms
    116.119.167.119 (116.119.167.119)  339.057 ms
    116.119.167.20 (116.119.167.20)  251.139 ms
 8  * * *
 9  be3271.ccr41.lax01.atlas.cogentco.com (154.54.42.101)  348.294 ms  258.764 ms  332.531 ms
10  be2932.ccr32.phx01.atlas.cogentco.com (154.54.45.161)  284.207 ms
    be2931.ccr31.phx01.atlas.cogentco.com (154.54.44.85)  256.272 ms  418.788 ms
11  be3452.rcr21.phx02.atlas.cogentco.com (66.28.4.242)  308.459 ms
    be3451.rcr21.phx02.atlas.cogentco.com (66.28.4.26)  432.845 ms  248.367 ms
12  38.122.88.107 (38.122.88.107)  253.042 ms  252.078 ms  246.671 ms
13  core1-edge1-lag1.phx1.codero.com (216.55.160.4)  273.957 ms  253.373 ms  259.721 ms
14  59-160-55-216.dedicated.codero.net (216.55.160.59)  303.754 ms  307.035 ms
    216-55-160-71.dedicated.codero.net (216.55.160.71)  259.048 ms
15  ourdomain.org (redacted)  444.128 ms  405.611 ms  409.528 ms

In this case, the route seemed to be within Airtel (Bharti Telecom) till the 8th hop, and then directly LA and then Phoenix.

 

Tuesday, September 15, 2026

unable to update plugins after a Moodle update

After upgrading our 4 Moodle instances to the latest minor version, 

Site Administration > Plugins > Plugins overview > Check for available updates button returns "Unable to fetch available updates data - unexpected HTTP response code."

Probably the cause is Moodle's Cloudflare ray rejecting the request from our server with a 403 (I verified that it was sending a 403 with curl) as mentioned in this thread, https://moodle.org/mod/forum/discuss.php?d=429373

So I'll try again later.

Monday, September 14, 2026

exporting all LLM conversations to a private repo

Started work on exporting all conversations from Gemini (via aistudio.google.com), Claude, ChatGPT and Github Copilot to a private repo. 

Claude was the simplest - clicking on my initials (bottom left) → Settings → Privacy → Export data
Got an email with a download link (valid ~24 hours) containing a ZIP of your full conversation history as JSON files within a minute. The download link actually downloaded a json file, which had links to the actual conversations and metadata zip file downloads. Then, parsed the conversations using https://github.com/tyashin/Claude-export-data-parser and a full indexed repo of markdown files was ready for me to sync to my private repo.

OpenAI says it will take a few days for them to prepare and send me the export - had to do the export from the Android app, the web version just went into a loop with Confirm export > ask for one-time password sent to email > Confirm export > ask for one-time password sent to email ... The export initiated from the Android app successfully sent an email to me with "ChatGPT - Your data export has started" and the next day, I got an email with "ChatGPT - Your data export is ready" and a download link. The data export included json files as well as a large chat.html file. Unfortunately, the chat.html had only the text of the  conversations, but no date info. So, following the path of creating a json parser for this data too, as mentioned below.

AIStudio did not seem to have any export ability, even with google takeout (please see the next paragraph). Third-party tools can scrape and export as json, what worked for me was saveai.net AI Chat Exporter. Now I've to parse the json and create an index like Claude-export-data-parser does for Claude. 

Correction to the above paragraph - AIStudio seemed to be storing all conversations in Google Drive, and downloading the directory "Google AI Studio" as a zip file seemed to get us all the conversations.

Made this public repo - https://github.com/hn-88/ai-json-parsers - with the json-to-markdown conversion python scripts generated by Gemini and Claude, which would parse the json files exported by each service and create an indexed set of markdown files. When I host this on github, (in a private repo, since some info does not need to be public) I can search for phrases in all the conversations as well as have a detailed reference of all the LLM replies (some of which are wrong, some of which are useful.)

Sunday, September 13, 2026

resizing Azure VM - moved to D2asv5

There was a request to bump up the capacity of a Moodle instance for an in-person workshop on 12th and 13th. 

Used Chrome on Android to log in to the Azure portal, shut down the instance and size it higher (D2asv4 8 GB to D4asv5 16 GB).

When down-sizing it back, found that D2asv5 (<$41 per month) was cheaper than the original D2asv4 (<$46 per month). So, used D2asv5 8 GB instead.

Saturday, September 12, 2026

app for automating sending of SMS / WhatsApp messages

This is what it does:
1. take the phone number from the clipboard
2. check if that number has an associated whatsapp account
3. If whatsapp account exists, create a preformatted whatsapp message to that whatsapp account - i.e. opens Whatsapp with the draft message, which the user can then send
4. creates an SMS draft too, in the default SMS app, which we can send if required.

Created with Claude in just one prompt - what I wanted initially, and the sequence of prompts, is at
and the one issue with the generated code and fix are at

Friday, September 11, 2026

repeated Cloudflare "human check"

One of our content admins wrote,

For the past week (or so), each time we go to our website, and even click on a sub-page, etc, Cloudflare makes us check that we are human (not a bot). See screenshot attached. 
Do you experience this as well, or is it just me?

It is a bit annoying to do it for EVERY page. Is there a way to do it randomly, or at least once (so it knows this IP address is fine), etc.?

Another admin mentioned that 

I remember (another) website team making some changes to the Cloudflare settings following the phishing attacks and bot injection issues we faced on the (other) website. I believe the human verification check you are seeing could be related to those security measures.  

And I added,

I think one way to avoid getting the bot-check "every time" would be to keep a tab open with a page from our domain - then the session would remain open for around 10 minutes, and any other page in our domain you open within those 10 minutes will not ask you for a bot-check. If you are doing some content editing work which takes more than 10 minutes, if you navigate to another page in the open tab within 10 minutes or refresh that page within 10 minutes, you can avoid the bot-checks.


logo update for sign-in with Google screen

One of our CMS admins mentioned that the 'Sign in with Google' screen still displays the old logo, and that it needed to be replace with the new logo of the organization.

The process according to Claude was,

  1. Go to Google Cloud Console → select your project.

  2. Navigate to APIs & Services → OAuth consent screen (in newer UI it may be under APIs & Services → Branding).

  3. Under the App information section, you'll see an App logo field — upload your logo there (square image, recommended 120x120px, max 1MB, JPG/PNG/BMP).

  4. Save changes. 
I had to find the account under which the project had been created from our old emails - there is no way to easily find the email id under which the project was created from the project id which is present in the php code. But Claude says that the Super Admin for Google Workspace can give themselves the permissions in order to get this info, but I did not try it. Private URL for my reference, https://claude.ai/chat/5e521ed1-ec1a-4301-8c89-f7a21939b6e1

Wednesday, September 09, 2026

Claude's review of swayam plus integration local plugin for Moodle

Created a wiki page at https://github.com/hn-88/swayamplus-sso-api-integration/wiki
and populated it with Claude's comments during its review of code for this plugin which had been generated by Gemini in a single prompt.

Tuesday, September 08, 2026

Google Maps Street View video

Private URL for my reference - https://claude.ai/chat/859a7468-53e7-4e90-96ce-deca9ab6f363

Claude was happy to write code for me when I prompted, "If it is feasible, please write a google apps project which will work with google street view, capturing street view images between two points which the user chooses, and exporting the images as a video journey between the two places on the map similar to what is seen by a dashcam on a car. If not feasible, please suggest alternatives."

But the code requires a project with billing enabled, apparently it does not work with the demo api key provided at https://developers.google.com/maps/demo-key

So, pivoted to making screenshots of the google.com/maps page open in street-view instead - tested using SikuliX IDE 2.0.5 from https://github.com/oculix-org/SikuliX1/releases/download/v2.0.5/sikulixide-2.0.5-linux.jar

Points to note - the delay needs to be more than 5 seconds for me, the images load quite slowly. Alt+Shift+C to abort the SikuliX task.

ffmpeg -framerate 1 -i frame_%04d.png -vf "scale=trunc(iw/2)*2:trunc(ih/2)*2,minterpolate=fps=10:mi_mode=blend" -c:v libx264 -pix_fmt yuv420p journey.mp4

for frame blending, but a timelapse style with more frames per second would probably be needed for longer journeys. The scaling is because the libx264 codec needs even numbered pixel counts for width and height, which may not be guaranteed when choosing a rectangle onscreen.

Monday, September 07, 2026

debugging and testing local Moodle plugin with Claude

Private URL for my reference - https://claude.ai/chat/12096022-e224-4e6b-9e01-40149a93cef7

Used Claude to debug (add missing version.php, and many other omissions) the local plugin for swayamplus api integration generated by Gemini - github repo at https://github.com/hn-88/swayamplus-sso-api-integration. Also added some features like retry logic and test url generation.

The readme file has some info about how ngrok was used for testing with a mock python server - https://github.com/hn-88/swayamplus-sso-api-integration/tree/main/test-python-api-server

Some points to note:

  1. The plugin uses the "Manual enrolment" enrolment method, which is enabled by default on all courses - we can see the enabled methods by going to the relevant course > Participants > Enrolled users (dropdown) > Enrolment methods. 

  2. Guest access should probably be removed from the same place as above, in case enabled earlier.

  3. Currently the classes/privacy/provider.php is not implemented, so the mdl_local_swayamplus_enrol table is not updated by deleting rows if / when users are deleted.

  4. If the user is already present on our system, they are directly enrolled in the course.

  5. If the user is new to our system, the user is dropped into the profile completion and agreement to terms and conditions workflows. Following that, the user is actually enrolled in the course automatically, but dropped into their Profile page. According to Claude, we can add a large link to "My Courses" in the Profile page as follows: 

    1. Go to any page where you can add a block sitewide — the Front Page is the standard choice. Turn editing on.
    2. Add a block → HTML.
    3. Give it a title (e.g. "Continue learning") and content — something like a large, bold link:
      <p style="text-align:center;">  
      <a href="https://ourdomain.org/my/courses.php"     
      style="font-size:1.4em; font-weight:bold; 
      display:inline-block; padding:10px 20px;">
      → Go to My Courses  </a></p>
      
    4. On that block's own settings (gear icon → Configure), under "Where this block appears" → Page contexts, set it to "Display throughout the entire site." Save.
    5. Now navigate to any user's profile page — your own is fine. The block should appear there too (since you just made it sitewide). Open that same block's settings again, and this time change "Display on page types" to "Only user profile pages." Save.


network problems were due to overheating SFP module

On two consecutive days, internet at the Planetarium went down at around 9 am, working fine before that. The network folks tried sending light down the fiber, it was fine. But the SFP module in the media converter at our end was very hot. They swapped out the media converter, and the network started working again. 

Saturday, September 05, 2026

avidemux on Mac - default settings are fine

Verified that with the default settings on Avidemux 2.8.2 compiled for ARM on Mac,

MPEG4 ASP (xvid4) video and AC3 lav audio work for playout on the DVD player, 1920x1080.

For 4K - 3840x2160 - the Videotoolbox HEVC default settings are 2000 bitrate and 4000 max bitrate, which can be increased to 5000 bitrate and 8000 max bitrate to be on par with the settings in python-ffmpeg-warp.

Friday, September 04, 2026

investigating lack of referrals

The Moodle plugin in this previous post was not reporting any referrals for the month of August. Investigating, found that
  1. In the new site (swayamplus.education.gov.in redirected from swayam-plus.swayam2.ac.in) login is via email sent to our email id, and my previous email registration doesn't seem to work. Maybe because I had unsubscribed from course alerts?

  2. After logging in with a fresh email, found that swayamplus is no longer adding a referer id to the course URL. There is no &ref=ABC123 part in the URL.
Since they are not sending referral IDs, we cannot report what they don't send!

Moodle server error alert - due to .jpeg extension

One of our Moodle admins reported that
trying to add a content update. I keep getting this error: "servererror,moodle". What could be the problem?

I asked for the URL, and what he had been trying to do. Apparently, the issue was with adding image files with .jpeg extension in a rich-text box. When I renamed the files to .jpg and added them to the box, there was no error. 

Thursday, September 03, 2026

creating m3u playlists with Claude

Tried out creating new playlists for playout in Kodi atop LibreElec on Raspberry Pi 4, using Claude instead of manually copy-pasting filenames. Perhaps a time saving of half-an-hour or so? Private URL for my reference at https://claude.ai/chat/cf6e6881-2424-4a24-a1a7-ed38bc9b4942

Uploaded a playlist and prompted

Just like this playlist, please create a playlist called U055newname.m3u where the items #EXTINF:0,1.mp4
 /var/media/1.mp4
 #EXTINF:0,2.mp4
 /var/media/2.mp4
 #EXTINF:0,3.mp4
 /var/media/3.mp4
 are replaced by the files

/var/media/4.mp4
 /var/media/5.mp4
 /var/media/6.mp4
 /var/media/7.mp4

and the appropriate EXTINF lines.

Claude did catch some typos I had made, and double-checked some files, like

Note the last item keeps its .mp4 extension (matching what you specified: only.mp4), unlike the ENG version which was .avi. Let me know if that should also be .avi.

Also asked Claude to create a python script with the prompt

Now, please write a python script to check each m3u file in the current directory if it has all these items as the first few items of the playlist,

(lines of playlist pasted here)

(for playlists which have TEL in the name)

and if any items are missing, to add them in this order.

Similarly, for m3u files with 3lang or ENG in the name, 

(lines of playlist pasted here)

and for playlists with HIN in the name, 

(lines of playlist pasted here)

which worked well - I have uploaded to the private repo upstairs-Kodi-playlists

Wednesday, September 02, 2026

dotnet api timeouts and action taken

The developers on one of our apps noted that 
The API is intermittently failing, and we are seeing a CORS error in the browser Network tab.

Claude noted that the CORS error is misleading, and it is probably because of a backend API timeout. 

this is almost never an actual CORS policy misconfiguration — it's the browser's error message for "the actual response didn't carry an Access-Control-Allow-Origin header," which happens whenever the request fails or errors out before your CORS middleware gets a chance to run. Since it's intermittent, that's your biggest clue.

Since a lot of bot traffic was seen looking for Wordpress files, they requested blocking those bots which look for non-existent Wordpress files.

Taking Gemini's advice, 
1. Disabled php on the api server, since it's not required by our services - 
sudo a2dismod php8.3
sudo systemctl restart apache2

2. Added one more firewall rule to Cloudflare, the URL being like

ourdomain.org/security/security-rules

(http.host contains "ourdomainname" and http.request.uri.path contains "wp-") or (http.host contains "ourdomainname" and http.request.uri.path contains ".php")
then Block

Friday, August 28, 2026

All of your Google Play apps have been successfully registered

There was an email from Google saying that Android apps in the Play store (and outside) need to be registered, and we need to register keys if we are signing outside the Play Store.

One of our apps was being signed outside the Play Store, so in the URL
play.google.com/console/u/6/developers/android-developer-verification
clicking the arrow against that app, it asks us to add the public key.
To find the public key,
where I'm using this method in a github action,

      - name: Extract SHA-256 fingerprint only
        run: |
          BUILD_TOOLS="$ANDROID_HOME/build-tools/34.0.0"
          "$BUILD_TOOLS/apksigner" verify --print-certs app-release.apk \
            | grep -i "SHA-256"

I clicked on Add Key and added the digest, at first it showed Pending verification, in 10 minutes it showed verified.

Here is part of Gemini's reply on why this key is needed - 

To ensure your app functions flawlessly whether it is downloaded from the Play Store, sideloaded by a QA tester, or downloaded directly from your website, your backend services (Firebase, Google Cloud, etc.) should always have both fingerprints registered:
  1. The Google Play App Signing SHA-256 (found in Play Console -> Release > Setup > App Integrity).
  2. Your Local Keystore SHA-256 (the one generated from yourkey.keystore).

major changes needed for Moodle app

I asked Gemini 3.1 Pro Preview via aistudio.google.com -
Does the Moodle Android App at https://github.com/moodlehq/moodleapp support optimization using R8?

Gemini said, in short, "The short answer is no, not out of the box. The Moodle App is optimized using Angular/JS minification tools (Terser/Webpack) rather than Android's native R8. You should not enable R8 in the Moodle App's Android build configuration, as it will likely break the bridge between the app's JavaScript and native Android plugins."

Then, I asked, 
Then how can this app (and other similar cordova apps) meet the upcoming quality requirements in https://android-developers.googleblog.com/2026/08/app-quality-memory-optimization-secure-onboarding.html ? The play store quality check tool seems to mandate R8 optimization.

Gemini's answer is pasted below.

The recent Google Play announcement from August 2026 (enforcing new memory and
code optimization standards by February 2027) definitely changes the landscape
for hybrid frameworks like Cordova, Capacitor, and Ionic, which power apps like
Moodle.

According to the new Google Play requirements, apps must have a minimum of 25%
coverage across optimization, shrinking, and obfuscation for their native DEX
code using R8 (or a similar tool), and they must stay below strict memory/bitmap
thresholds.

Here is how the Moodle app, and Cordova apps in general, can meet these upcoming
requirements without breaking their architecture:

1. Hitting the 25% R8 DEX Coverage Rule

While blindly enabling R8 (minifyEnabled true) breaks the Cordova JS-to-Native
bridge, it can be enabled safely if properly configured. To meet the 25%
threshold, Cordova apps do not need to obfuscate their entire codebase; they
just need to shrink enough of it.

  - Targeting Heavy Dependencies: In a standard Cordova app, the actual Cordova
    wrapper code and plugins make up a very small percentage of the compiled
    .dex (native Android) file. The vast majority of the native code comes from
    heavy dependencies like AndroidX libraries, Google Play Services, Firebase,
    and other third-party native SDKs.
  - Targeted ProGuard/R8 Rules: Developers will need to enable minifyEnabled
    true but use a highly specific proguard-rules.pro file. They will use
    aggressive -keep rules to protect the Cordova framework (-keep class
    org.apache.cordova.** { *; }) and plugin reflection methods so the app
    doesn't crash.
  - By allowing R8 to shrink and dead-code-eliminate the massive AndroidX and
    Google Play Services libraries while leaving the Cordova bridge alone,
    hybrid apps can easily surpass the 25% optimization threshold required by
    Google Play.

2. Meeting the Dynamic Memory & Bitmap Thresholds

The new requirements aren't just about R8; they strictly monitor dynamic memory
and background bitmap usage. This is actually the bigger challenge for Cordova
apps because they run inside an Android WebView, which is notoriously
memory-heavy.

To prevent the Play Store from penalizing or down-ranking the app, hybrid apps
will have to aggressively optimize their web layers:

  - Background DOM Management: The Moodle Angular app will need to actively
    destroy heavy DOM elements (like large course lists or forums) when the app
    goes into the background, replacing them with placeholders to free up RAM.
  - Bitmap/Image Eviction: The WebView caches images heavily. When the user
    navigates away from an image-heavy course, the app must ensure the WebView
    releases those bitmaps from memory rather than keeping them cached in
    non-visible states.
  - Garbage Collection: Relying more heavily on WeakReferences in JavaScript and
    ensuring Angular observables are cleanly unsubscribed to prevent memory
    leaks in the Node/V8 engine powering the WebView.

3. Implementing the "Zero-Tap Sign-In" (Device Migration)

The blog post also mandates the adoption of the Android Restore Credentials API
for seamless device migration. For the Moodle app, this means updating the
native Android authentication plugins to hook into Android's Credential Manager
API. Instead of relying solely on standard web cookies or isolated local
storage, the app's native layer will need to back up the user's secure token so
that if a student buys a new phone, Moodle logs them in automatically.

The Path Forward for Moodle (and Cordova)

Because the enforcement deadline is February 2027, framework maintainers have
several months to react. You can expect the following to happen across the
ecosystem:

1.  Upstream Updates: The Apache Cordova and Ionic Capacitor teams will likely
    push updates to their Android build templates that include safe, boilerplate
    proguard-rules.pro files out of the box.
2.  Plugin Consumer Rules: Native plugin authors will increasingly embed
    consumerProguardFiles in their Gradle setups. This tells R8 exactly what to
    preserve automatically, taking the burden off the app developer.
3.  Moodle HQ Patches: For the Moodle app specifically, Moodle HQ will update
    their build pipeline before the 2027 deadline. If you are compiling your own
    custom version of the Moodle app, you should wait for Moodle HQ to release
    their compliant ProGuard configurations rather than trying to force R8 on
    your own today.



Thursday, August 27, 2026

recovering h5p activites after mod_hvp bug on Moodle

This was noticed on July 24th - lots of pages on our Moodle instances were showing "The version of the H5P library H5P.Column used in this content is not valid. Content contains H5P.Column 1.18, but it should be H5P.Column 1.22."


At that time, I replied that, according to Gemini, this is a known H5P bug - https://github.com/h5p/moodle-mod_hvp/issues/632 - and the H5P core development team is actively working on a fix (Pull Request #633) - https://github.com/h5p/moodle-mod_hvp/pull/633
Gemini private URL for my reference - https://aistudio.google.com/prompts/1Ddl2aYqln8Y8wTIPtlLXG_8Fipeay_5F

Unfortunately, the next version of mod_hvp Moodle plugin was released, but it only had code to prevent such errors in future, but it did not automatically fix the existing problems.

So, took the help of Gemini again, and took steps to fix our Moodle instances. Private URL for my reference - https://aistudio.google.com/prompts/1LJYJaO3R8mjgpsd3w67TDlACd3Ni8JhL

Workflow was:

0. First download a "good copy" of the database backup - Since the buggy mod_hvp was released after Feb 2026, my choice was the backup of 22 Feb 2026. Unfortunately, this backup was 500+ MB as a .sql.gz file, where the logstore_standard_log table had not yet been cleaned up. Setting up this database on my local machine took 4+ hours for the database import. Private URL for my reference - https://chatgpt.com/c/6a8d13c5-1988-83ee-816b-082af1f2a874

sudo apt install mysql-client (was already installed)
sudo apt install mysql-server

systemctl status mysql
sudo mysql
CREATE DATABASE restore_test CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
quit;
zcat your_backup.sql.gz | sudo restore_test

(this took 4+ hours - should probably have done the gunzip separately? Also, 11+ GB database at /var/ from a 9+ GB sql file - sudo du -sb /var/lib/mysql/restore_test)

sudo mysql
CREATE USER 'myuser'@'localhost' IDENTIFIED BY 'your_password_here';
GRANT ALL PRIVILEGES ON restore_test.* TO 'myuser'@'localhost';
FLUSH PRIVILEGES;

Then to fix "Public Key Retrieval is not allowed" error in DBeaver, right-click to edit the connection, 

Go to Driver Properties.
Modify the properties,
allowPublicKeyRetrieval to true
useSSL to false

Then, by using a different port in a tunnel to the remote database, we can copy and paste data with DBeaver.


1. Fix the main library reference (e.g., Column or Interactive Book containers):

UPDATE vv_hvp h
JOIN vv_hvp_libraries old_lib ON old_lib.id = h.main_library_id
JOIN vv_hvp_libraries new_lib ON new_lib.machine_name = old_lib.machine_name
  AND new_lib.major_version = 1 AND new_lib.minor_version = 22
SET h.main_library_id = new_lib.id, h.filtered = NULL
WHERE old_lib.machine_name = 'H5P.Column'
  AND old_lib.major_version = 1 AND old_lib.minor_version = 18;

Updated rows 0

2.  Fix nested library references

UPDATE vv_hvp 
SET json_content = REPLACE(json_content, 'H5P.Column 1.18', 'H5P.Column 1.22'),
    filtered = NULL
WHERE json_content LIKE '%H5P.Column 1.18%';

UPDATE vv_hvp 
SET json_content = REPLACE(json_content, 'H5P.InteractiveVideo 1.27', 'H5P.InteractiveVideo 1.28'),
    filtered = NULL
WHERE json_content LIKE '%H5P.InteractiveVideo 1.27%';

Updated rows 7

3. Recovering "Wiped" Activities (Data Loss)

SELECT id, name, course FROM vv_hvp WHERE json_content LIKE '%"content":{"params":{}}}%';

37 rows on our instance.

4. Fixing one of the data-missing json-content fields - 
SELECT json_content FROM vv_hvp WHERE id = 30851 (from the backup)

to find the current libraries, look for H5P string in the json, 
and add to the ones below.

SELECT machine_name, major_version, minor_version
FROM vv_hvp_libraries
WHERE machine_name IN (
    'H5P.Video', 
    'H5P.MultiChoice', 
    'H5P.AdvancedText',
    'H5P.InteractiveVideo',
'H5P.Column',
'H5P.Image',
'H5P.CoursePresentation'
)
ORDER BY machine_name, major_version DESC, minor_version DESC;

Only had to find/replace CoursePresentation version, others had current versions. Then pasted the "corrected" json content in the same field of the live database.

5. Run a find/replace on the entire live database - starting first with one course,

UPDATE vv_hvp 
SET json_content = REPLACE(json_content, 'H5P.Column 1.18', 'H5P.Column 1.22'),
    filtered = NULL
WHERE course = 606 
  AND json_content LIKE '%H5P.Column 1.18%';

Updated rows 42.

Update main library id just in case.

UPDATE vv_hvp h
JOIN vv_hvp_libraries old_lib ON old_lib.id = h.main_library_id
JOIN vv_hvp_libraries new_lib ON new_lib.machine_name = old_lib.machine_name
SET h.main_library_id = new_lib.id, 
    h.filtered = NULL
WHERE h.course = 606
  AND old_lib.machine_name = 'H5P.Column'
  AND old_lib.major_version = 1 
  AND old_lib.minor_version = 18
  AND new_lib.major_version = 1 
  AND new_lib.minor_version = 22;

Updated rows 0.

Then, did for all courses,
UPDATE vv_hvp 
SET json_content = REPLACE(json_content, 'H5P.Column 1.18', 'H5P.Column 1.22'),
    filtered = NULL
WHERE json_content LIKE '%H5P.Column 1.18%';

Updated 221.


Then asked Gemini if this can be automated for data recovery for the 30+ instances of data loss - private URL for my reference -
 https://aistudio.google.com/prompts/14Bv6L5AfaOglFuUY_VJZ6F7A1cchR-FQ

That resulted in the python scripts at https://github.com/hn-88/fix-hvp/
which seem to have fixed all the data-loss in this instance.

Then, a new problem was reported on another Moodle instance - youtube videos were playing with audio only, and black screen. Putting that in a separate post here.

Moodle H5P Interactive video playing only audio with black screen

It was reported that all the H5P Interactive video activities on one of our Moodle instances were only playing audio, no video visible, black screen instead. My initial reaction was that this seemed to be another symptom of the mod_hvp bug which had prevented activities from being updated as per the previous post. But the fix in this case was slightly different.

Private URL of chat with Gemini for my reference - https://aistudio.google.com/prompts/1sowy1kk_uVUetNXh2Og3WBgVcfGQ-lQy

The query
SELECT machine_name, major_version, minor_version
FROM vv_hvp_libraries
WHERE machine_name IN (
'H5P.Video',
'H5P.MultiChoice',
'H5P.AdvancedText',
'H5P.InteractiveVideo',
'H5P.Column',
'H5P.Image',
'H5P.CoursePresentation',
'H5P.Summary'
)
 had this response.

H5P.AdvancedText 1 1
H5P.Column 1 13
H5P.Column 1 17
H5P.Column 1 18
H5P.CoursePresentation 1 22
H5P.CoursePresentation 1 24
H5P.CoursePresentation 1 25
H5P.CoursePresentation 1 26
H5P.Image 1 1
H5P.InteractiveVideo 1 22
H5P.InteractiveVideo 1 24
H5P.InteractiveVideo 1 26
H5P.InteractiveVideo 1 27
H5P.MultiChoice 1 14
H5P.MultiChoice 1 16
H5P.Summary 1 10
H5P.Video 1 5
H5P.Video 1 6

Gemini said, If you replace 'H5P.Column 1.18' with 'H5P.Column 1.22', Moodle's H5P renderer will look for H5P.Column 1.22 in vv_hvp_libraries, fail to find it, and throw a fatal error. H5P.Column 1.22 is not installed on this instance. 

Primary cause - Outdated H5P.Video Library vs. YouTube Player Changes. Check with

SELECT id, machine_name, major_version, minor_version, patch_version 
FROM vv_hvp_libraries 
WHERE machine_name IN ('H5P.Video', 'H5P.InteractiveVideo')
ORDER BY machine_name, major_version, minor_version;

If H5P.Video 1.6 is below 1.6.67, updating the library will resolve the issue - and gave this thread as the reference,
which was discussing the exact same issue.

After some trial and error, the correct solution was:
1. In the Moodle server's config.php, add this line near the bottom before require_once(...)
$CFG->mod_hvp_dev = 1;
2. Download link: https://hub-api.h5p.org/v1/content-types/H5P.InteractiveVideo (save the file as InteractiveVideo.h5p)
3. Go to https://your-moodle-domain/mod/hvp/library_list.php
(Or navigate via UI: Site Administration > Plugins > Activity modules > H5P > H5P Libraries)
and upload the saved InteractiveVideo.h5p in the Upload Libraries box.
4. Verify that the new version is installed with the query 
SELECT id, machine_name, major_version, minor_version, patch_version 
FROM vv_hvp_libraries 
WHERE machine_name = 'H5P.Video';
5. Now in mod/hvp/library_list.php we see Interactive Video (1.27.9) listed, and also a green button at the right to "Upgrade library content". Click on the upgrade library content button to automatically fix all the interactive videos on the site. The page goes to 100% with AJAX updating in less than 30 seconds.
6. Purge caches (sudo php admin/cli/purge_caches.php on CLI ) and test one of the videos to see if it is OK
7. Restore the config.php by commenting out the mod_hvp_dev line we added.