The Problem Nobody Thinks About Until It's Too Late
I've got 75+ game reviews on backloggd.com. I like the social element, the community, leaving snarky comments. But one thing nags at me: I don't own any of it.
Backloggd is run by a single developer. No API. If he gets hit by a bus, or pivots to monetization, or just burns out, my entire review archive evaporates.
All those hours documenting what I actually thought about these games, gone, because a service I have zero control over decided it was done.
I used to want to build a TUI for backlog tracking. Backloggd solved that. But it created a new problem: total dependency.
Caveat: I'm a Backloggd backer and I genuinely like the service. This isn't about distrust. It's about agency. I want my data on my terms.
So I decided to grab it before it disappears.
The Asterisk: Historical Data
There's a problem I'm not solving today: all my reviews from before today are still trapped on Backloggd.com's servers. The RSS feed only shows the ~25 most recent reviews. Everything before July 2025? Gone from the feed, still on the site. That is an entirely different beast.
For now, I'm solving the forward problem: from today onward, every review gets captured. The historical stuff? I'll circle back to that when I figure out the scraping approach. This post is just the foundation: the infrastructure that makes the future easier.
So this article covers future-proofing my reviews starting now, not rescuing the past. That's coming next. Probably. Hopefully.
The Trap (You Know This Part)
I started simple. "Just backup the RSS feed. Extract the data. Done."
Then my brain went:
- "What if I parse it into JSON?"
- "What if I build a dashboard showing my gaming patterns over time?"
- "What if I create an AI insights engine analyzing my review sentiment?"
- "Wait, this could be a case study. Personal branding moment. Blog post material."
By the time I got to "case study," I was building a 40-hour project in my head while staring at a 1-hour problem.
This is the pattern. It never changes. The fun part (pathfinding, solving, optimizing) is maybe 5% of the work. The boring part (polish, iteration, maintenance) is 95%. And I have the discipline of a goldfish with ADHD when it comes to the boring part.
The Decision (Fast)
Remove the constraint.
The constraint was: "Will this impress client prospects?" The answer is nobody fucking cares about my gaming review backup strategy. This is my blog. I put whatever I want on it. If someone reads it and thinks "oh that's useful," great. If not, irrelevant.
So: minimal bash script, honest article about why I didn't over-engineer it, publish, done.
Here's What I Actually Built
The logic is stupid simple: poll the Backloggd RSS feed, extract review IDs, check if I've already got them, append the new ones, send a webhook notification if anything breaks.
That's it.
The Script
I created a directory in my cloud-synced folder (I use mega.io for this):
mkdir -p ~/MY_SYNC_FOLDER/scripts/backup/backloggd-archive
Then create the script:
#!/bin/bash
FEED_URL="https://backloggd.com/u/YOUR_USERNAME/reviews/rss/"
ARCHIVE_DIR="$(dirname "$0")"
ITEMS_FILE="$ARCHIVE_DIR/items.xml"
ERROR_LOG="$ARCHIVE_DIR/error.log"
WEBHOOK_URL="https://ntfy.sh/YOUR_TOPIC_NAME"
mkdir -p "$ARCHIVE_DIR"
# Fetch RSS
FEED=$(curl -s --max-time 10 "$FEED_URL" 2>/dev/null) || {
curl -d "Backloggd backup failed: Fetch error at $(date '+%Y-%m-%d %H:%M:%S')" "$WEBHOOK_URL"
echo "$(date '+%Y-%m-%d %H:%M:%S') - FETCH FAILED" >> "$ERROR_LOG"
exit 1
}
# Extract all items
ITEMS=$(echo "$FEED" | sed -n '/<item>/,/<\/item>/p' 2>/dev/null) || {
curl -d "Backloggd backup failed: Parse error at $(date '+%Y-%m-%d %H:%M:%S')" "$WEBHOOK_URL"
echo "$(date '+%Y-%m-%d %H:%M:%S') - PARSE FAILED" >> "$ERROR_LOG"
exit 1
}
# Process each item, skip if ID already exists
ADDED=0
while IFS= read -r line; do
if [[ $line == *"<item>"* ]]; then
ITEM="$line"
while IFS= read -r next_line; do
ITEM="$ITEM"$'\n'"$next_line"
if [[ $next_line == *"</item>"* ]]; then
break
fi
done
ID=$(echo "$ITEM" | grep -oP 'review/\K[0-9]+' | head -1)
if [ -z "$ID" ] || grep -q "review/$ID/" "$ITEMS_FILE" 2>/dev/null; then
continue
fi
echo "$ITEM" >> "$ITEMS_FILE"
((ADDED++))
fi
done <<< "$ITEMS"
echo "$(date '+%Y-%m-%d %H:%M:%S') - Added $ADDED new items" >> "$ERROR_LOG"
Then just make the file executable:
chmod +x ~/YOUR_SYNC_FOLDER/backup/backloggd-archive/backloggd-backup.sh
What to replace:
YOUR_SYNC_FOLDER: Whatever you use (mine is/home/balint/Sync)YOUR_USERNAME: Your Backloggd usernameYOUR_TOPIC_NAME: Pick any name
How It Works
- Fetches the RSS feed - Gets your latest reviews from Backloggd
- Extracts review blocks - Uses
sedto pull complete<item>elements (preserves all metadata, no parsing overhead) - Deduplicates by review ID - Extracts the ID from the review URL, checks if it's already in our file, skips if yes
- Appends new reviews - Only the new ones get added
- Logs everything - Success count, timestamps, all errors
- Notifies on failure - If fetch or parse breaks, sends a webhook notification (more on that in a second)
Why raw XML and no parsing layer? Because I don't know what I'll want to query later. Raw XML preserves options. If I decide in six months I want to export to JSON or filter by rating or whatever, the raw data is still there, unchanged.
A good lesson here was while thinking this through: do not transform the data at extraction time. Transform when actually needed. Leaves format wide open and does not lock me into anything.
Granted: this is technically NOT a valid RSS, BUT it's structured. Easy to query later. It's a future me problem that let me make a decision fast and move fast.
Setup: Notifications
The script uses ntfy.sh , a dead simple webhook notifications. No account needed, no complex SMTP config.
- Install the ntfy app on your phone (Android/iOS) or use the web version
- Open the app, tap the "+" button, enter a topic name (can be anything, make sure it's not guessable, so strangers don't get funny ideas).
- Hit subscribe
- Done. Any message to that URL now shows up on your phone
In the script, both error cases (fetch fail, parse fail) send a webhook:
curl -d "Backloggd backup failed: Fetch error at $(date '+%Y-%m-%d %H:%M:%S')" "$WEBHOOK_URL"
If things work? Silent. If things break? Notification.
Automate It
I've added this to my crontab (I use Arch, BTW):
crontab -e
Add this line (runs daily at 9 PM):
0 21 * * * ~/MY_SYNC_FOLDER/scripts/backup/backloggd-archive/backloggd-backup.sh
Save and exit. Done. Every day at 9 PM, if I wrote a new review, it gets backed up (appended to existing file to avoid complications).
Storage
The whole backloggd-archive folder lives in my Mega.io encrypted sync folder. Not GitHub, not some public repo. Encrypted at rest, synced automatically. If my computer dies, I reinstall, mount Mega, add the cron line back, and it's running again.
That's the actual portability I need, not "how do I set up CI/CD," but "if this machine explodes, how fast can I resurrect this?"
Why This Actually Works
The lesson I reluctantly learn time and again: Simple systems survive. Complex systems get abandoned. I tended to create complex systems (whenever I did not abandon them first = 90% of the time). So here I'm writing this article 2 hours after I thought of this project.
My reviews are safe. I own the data. The script runs in the background. I don't think about it again.
That was the entire point. Now I can go off and kill monsters in Resistance 3.
Thank you for coming to my TED talk.
