From ed005bae14704fe450b57244d3fd89945bda4981 Mon Sep 17 00:00:00 2001 From: Mischa Date: Sat, 20 Jun 2026 15:32:56 +0200 Subject: [PATCH] feat: add pixelfed-import script and make target Copies JSON export + script into Docker container and runs import via python3; installs python3 if absent. Idempotent (skips existing folders). Co-Authored-By: Claude Sonnet 4.6 Claude-Session: https://claude.ai/code/session_01Vgmzx8VTTTmCskSpQtsLTr --- Makefile | 6 +++ scripts/pixelfed-import.py | 107 +++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 scripts/pixelfed-import.py diff --git a/Makefile b/Makefile index 3079a27..5aeafe9 100644 --- a/Makefile +++ b/Makefile @@ -55,6 +55,12 @@ demo-load: demo-reset: docker exec intotheeast_grav bash -c "rm -rf /var/www/html/user/pages/01.trips/italy-2026-demo && cd /var/www/html && php bin/grav clearcache" +pixelfed-import: + docker exec intotheeast_grav bash -c "which python3 || apt-get install -y python3 --no-install-recommends -q" + docker cp /home/mischa/Nextcloud/Downloads/pixelfed/pixelfed-statuses.json intotheeast_grav:/tmp/pixelfed-statuses.json + docker cp scripts/pixelfed-import.py intotheeast_grav:/tmp/pixelfed-import.py + docker exec -w /var/www/html intotheeast_grav python3 /tmp/pixelfed-import.py + # ── Content sync (user repo ↔ Gitea) ────────────────────────────────────────── content-push: diff --git a/scripts/pixelfed-import.py b/scripts/pixelfed-import.py new file mode 100644 index 0000000..b03fd11 --- /dev/null +++ b/scripts/pixelfed-import.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python3 +"""One-time import of Pixelfed statuses into Grav entry pages.""" + +import json +import os +import urllib.request +from datetime import datetime, timezone + +INPUT_FILE = os.environ.get('PIXELFED_JSON', '/tmp/pixelfed-statuses.json') +USER_PAGES = 'user/pages/01.trips' + +TRIP_MAP = { + '2023': 'central-asia-2023', + '2024': 'us-canada-mex-2024', + '2025': 'italy-2025', +} + +EXT_MAP = { + 'image/jpeg': 'jpg', + 'image/png': 'png', + 'image/gif': 'gif', + 'image/webp': 'webp', +} + +ENTRY_TEMPLATE = """\ +--- +title: '{title}' +date: '{date}' +template: entry +published: true +hero_image: '{hero_image}' +lat: '' +lng: '' +location_city: '{location_city}' +location_country: '{location_country}' +weather_temp_c: '' +weather_desc: '' +--- + +{body} +""" + + +def download(url, dest): + try: + urllib.request.urlretrieve(url, dest) + return True + except Exception as exc: + print(f' Warning: download failed {url}: {exc}') + return False + + +def main(): + with open(INPUT_FILE) as f: + posts = json.load(f) + + counters = {} + + for post in posts: + year = post['created_at'][:4] + trip = TRIP_MAP.get(year) + if not trip: + print(f"Skip: no trip mapping for year {year} (post {post['id']})") + continue + + counters[trip] = counters.get(trip, 0) + 1 + n = counters[trip] + + date_str = post['created_at'][:10] # YYYY-MM-DD + folder = f'{date_str}-pixelfed-{n}.entry' + path = os.path.join(USER_PAGES, trip, '01.dailies', folder) + + if os.path.exists(path): + print(f'Skip: {folder} already exists') + continue + + os.makedirs(path) + print(f'Creating {trip}/{folder}') + + hero_image = '' + for i, att in enumerate(post.get('media_attachments', []), 1): + ext = EXT_MAP.get(att.get('mime', ''), 'jpg') + filename = f'photo-{i}.{ext}' + if download(att['url'], os.path.join(path, filename)) and i == 1: + hero_image = filename + + place = post.get('place') or {} + dt = datetime.fromisoformat(post['created_at'].replace('Z', '+00:00')) + date_fmt = dt.strftime('%Y-%m-%d %H:%M') + + entry_md = ENTRY_TEMPLATE.format( + title=f'Pixelfed Import {n}', + date=date_fmt, + hero_image=hero_image, + location_city=place.get('name', ''), + location_country=place.get('country', ''), + body=post.get('content_text', '').strip(), + ) + + with open(os.path.join(path, 'entry.md'), 'w') as f: + f.write(entry_md) + + print(f'\nDone. Posts per trip: {counters}') + + +if __name__ == '__main__': + main()