{"id":14209,"date":"2026-04-29T15:44:31","date_gmt":"2026-04-29T15:44:31","guid":{"rendered":"https:\/\/www.rjt.org.uk\/home\/?p=14209"},"modified":"2026-04-29T15:56:29","modified_gmt":"2026-04-29T15:56:29","slug":"publishing-lightroom-galleries-to-wordpress","status":"publish","type":"post","link":"https:\/\/www.rjt.org.uk\/home\/archives\/14209\/publishing-lightroom-galleries-to-wordpress\/","title":{"rendered":"Publishing Lightroom Galleries to WordPress"},"content":{"rendered":"\n<p>If you use Adobe Lightroom Classic and run a self-hosted WordPress site, you&#8217;ve probably looked at the various plugins that promise to wire the two together \u2014 only to find they all want a recurring subscription fee. For a personal blog where you just want to post the occasional gallery, that feels like overkill.<\/p>\n\n\n\n<p>This post walks through a free alternative I put together using WordPress&#8217;s built-in REST API and a small Python script. Once it&#8217;s set up, the workflow is:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Export from Lightroom as normal (2048px JPEG)<\/li>\n\n\n\n<li>Run one command pointing at the export folder<\/li>\n\n\n\n<li>Open the draft WordPress creates, add your text, publish<\/li>\n<\/ol>\n\n\n\n<p>No subscriptions. No third-party services. Just Python, which is already on your Mac.  It might work on a PC if you have Python, but I have not tried it.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">What You Need<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Adobe Lightroom Classic (any recent version)<\/li>\n\n\n\n<li>A self-hosted WordPress site (WordPress.org \u2014 this won&#8217;t work on WordPress.com free\/personal plans)<\/li>\n\n\n\n<li>Python 3 \u2014 already installed on macOS<\/li>\n\n\n\n<li>WordPress 5.6 or later (for Application Passwords support)<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Step 1 \u2014 Set Up the Python Environment<\/h2>\n\n\n\n<p>macOS protects its system Python from having packages installed into it directly. The fix is a virtual environment \u2014 a self-contained folder for your dependencies. Open Terminal and paste this single line:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>python3 -m venv ~\/wp-uploader-env &amp;&amp; source ~\/wp-uploader-env\/bin\/activate &amp;&amp; pip install requests<\/code><\/pre>\n\n\n\n<p>This creates the environment, activates it, and installs the one library the script needs (<code>requests<\/code>). You only ever need to run this once.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Step 2 \u2014 Create a WordPress Application Password<\/h2>\n\n\n\n<p>Application Passwords are built into WordPress and let external tools authenticate securely without using your main login password.<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Log into your WordPress admin dashboard<\/li>\n\n\n\n<li>Go to <strong>Users ? Profile<\/strong><\/li>\n\n\n\n<li>Scroll to the bottom \u2014 you&#8217;ll find the <strong>Application Passwords<\/strong> section<\/li>\n\n\n\n<li>Type a name (e.g. <code>Lightroom Upload<\/code>) and click <strong>Add New Application Password<\/strong><\/li>\n\n\n\n<li>Copy the password WordPress shows you \u2014 it only appears once<\/li>\n<\/ol>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>?? WordPress only shows this password once. Copy it immediately. If you lose it, delete it and generate a new one \u2014 no harm done.<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Step 3 \u2014 Download and Configure the Script<\/h2>\n\n\n\n<p>Download <code>wp_gallery_upload.py<\/code> and save it somewhere sensible \u2014 I use <code>~\/Documents\/coding\/wordpress\/<\/code>. Open it in any text editor and fill in the three lines at the top:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>WP_URL      = \"https:\/\/your-site.com\"          # Your WordPress site URL\nWP_USERNAME = \"your_username\"                   # Your WordPress username  \nWP_APP_PASS = \"xxxx xxxx xxxx xxxx xxxx xxxx\"  # Application Password from Step 2<\/code><\/pre>\n\n\n\n<p>That&#8217;s the only configuration needed. Make sure <code>WP_URL<\/code> has no trailing slash.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Step 4 \u2014 Export from Lightroom<\/h2>\n\n\n\n<p>Nothing special to install. Use your normal Lightroom export workflow (<strong>File ? Export<\/strong> or <strong>Shift+Cmd+E<\/strong>) with these settings:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Setting<\/th><th>Value<\/th><\/tr><\/thead><tbody><tr><td>Format<\/td><td>JPEG<\/td><\/tr><tr><td>Quality<\/td><td>85<\/td><\/tr><tr><td>Colour Space<\/td><td>sRGB<\/td><\/tr><tr><td>Resize to fit<\/td><td>Long Edge \u2014 2048 px<\/td><\/tr><tr><td>Resolution<\/td><td>72 ppi<\/td><\/tr><tr><td>Sharpen for<\/td><td>Screen, Standard<\/td><\/tr><tr><td>Export location<\/td><td>A dedicated folder per gallery<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Step 5 \u2014 Run the Script<\/h2>\n\n\n\n<p>Open Terminal, activate the environment, then point the script at your export folder:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>source ~\/wp-uploader-env\/bin\/activate\n\npython3 ~\/Documents\/coding\/wordpress\/wp_gallery_upload.py ~\/Pictures\/my-gallery --title \"My Post Title\"<\/code><\/pre>\n\n\n\n<p>You&#8217;ll see each image tick off as it uploads, then a confirmation with a direct link to the new draft post. Click it, add your text, and publish.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Optional \u2014 Raycast Integration<\/h2>\n\n\n\n<figure class=\"wp-block-image size-full is-resized\"><a href=\"https:\/\/www.rjt.org.uk\/home\/wp-content\/uploads\/2026\/04\/image.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1512\" height=\"438\" src=\"https:\/\/www.rjt.org.uk\/home\/wp-content\/uploads\/2026\/04\/image.png\" alt=\"\" class=\"wp-image-14216\" style=\"aspect-ratio:3.452249113420021;width:576px;height:auto\" srcset=\"https:\/\/www.rjt.org.uk\/home\/wp-content\/uploads\/2026\/04\/image.png 1512w, https:\/\/www.rjt.org.uk\/home\/wp-content\/uploads\/2026\/04\/image-300x87.png 300w, https:\/\/www.rjt.org.uk\/home\/wp-content\/uploads\/2026\/04\/image-1024x297.png 1024w, https:\/\/www.rjt.org.uk\/home\/wp-content\/uploads\/2026\/04\/image-150x43.png 150w, https:\/\/www.rjt.org.uk\/home\/wp-content\/uploads\/2026\/04\/image-768x222.png 768w\" sizes=\"auto, (max-width: 1512px) 100vw, 1512px\" \/><\/a><\/figure>\n\n\n\n<p>If you use <a href=\"https:\/\/www.raycast.com\">Raycast<\/a>, you can wrap this as a Script Command so you never touch Terminal. Download <code>wpgallery.sh<\/code>, set your base folder at the top of the file, drop it in your Raycast scripts folder, and reload Script Commands in Raycast settings.<\/p>\n\n\n\n<p>After that your workflow is: open Raycast, type <strong>WordPress Gallery Upload<\/strong>, enter the subfolder name, optionally add a title, press Enter. Raycast streams the upload progress in its output panel and prints the edit link when it&#8217;s done.<\/p>\n\n\n\n<p>The script is smart about paths \u2014 if you type just <code>MyUpload<\/code> it prepends your configured base folder automatically, but you can also type a full path for a one-off upload from anywhere.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Troubleshooting<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">401 Authentication error<\/h3>\n\n\n\n<p>Check your username and Application Password. Make sure you copied the full password from WordPress. If in doubt, delete it in WordPress and generate a fresh one.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Cannot connect to site<\/h3>\n\n\n\n<p>Confirm <code>WP_URL<\/code> uses <code>https:\/\/<\/code> and has no trailing slash. Check the site is reachable in a browser.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Timeout errors<\/h3>\n\n\n\n<p>Your server may have a short maximum execution time. Try uploading fewer images at once, or increase the <code>PAUSE_BETWEEN<\/code> value in the script. Security plugins like Wordfence can also block REST API requests \u2014 check your firewall rules if uploads are being rejected.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">No JPEG files found<\/h3>\n\n\n\n<p>The script looks for <code>.jpg<\/code> and <code>.jpeg<\/code> extensions. Make sure your Lightroom export format is set to JPEG (not TIFF or original).<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Scripts<\/h2>\n\n\n\n<div class=\"wp-block-buttons is-content-justification-center is-layout-flex wp-container-core-buttons-is-layout-16018d1d wp-block-buttons-is-layout-flex\">\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link wp-element-button\" href=\"https:\/\/rjt.org.uk\/home\/source\/wp_gallery_upload.py\">wp_gallery_upload.py<\/a><\/div>\n\n\n\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link wp-element-button\" href=\"https:\/\/rjt.org.uk\/home\/source\/wpgallery.sh\">wpgalley.sh<\/a><\/div>\n<\/div>\n\n\n\n<p>Both files are shown below so you can check them out before downloading.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>wp_gallery_upload.py<\/strong> \u2014 the main upload script<\/h2>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">#!\/usr\/bin\/env python3\n\"\"\"\nWordPress Gallery Uploader\n==========================\nUploads a folder of JPEG images to WordPress via the REST API\nand creates a draft blog post with a Gutenberg gallery block.\n\nSetup:\n  1. In WordPress admin, go to Users ? Profile ? scroll to the bottom\n  2. Under \"Application Passwords\", type a name (e.g. \"Lightroom Upload\")\n     and click \"Add New Application Password\"\n  3. Copy the password shown (with spaces is fine \u2014 paste it as-is below)\n  4. Fill in your details in the CONFIGURATION section below\n\nUsage:\n  python3 wp_gallery_upload.py \/path\/to\/your\/exported\/images\n  python3 wp_gallery_upload.py \/path\/to\/images --title \"My Gallery Post\"\n\nRequirements:\n  pip3 install requests Pillow\n\"\"\"\n\nimport argparse\nimport sys\n\nimport json\nimport os\nimport sys\nimport time\nfrom pathlib import Path\n\n# ?????????????????????????????????????????????\n#  CONFIGURATION \u2014 fill these in before running\n# ?????????????????????????????????????????????\n\nWP_URL      = \"https:\/\/your-site.com\"          # Your WordPress site URL (no trailing slash)\nWP_USERNAME = \"your_username\"                   # Your WordPress username\nWP_APP_PASS = \"xxxx xxxx xxxx xxxx xxxx xxxx\"  # Application Password from WordPress profile\n\n# ?????????????????????????????????????????????\n#  OPTIONS \u2014 adjust if needed\n# ?????????????????????????????????????????????\n\nPOST_STATUS    = \"draft\"           # \"draft\" or \"publish\"\nIMAGE_QUALITY  = 85                # JPEG quality for re-save check (0\u2013100)\nSUPPORTED_EXTS = {\".jpg\", \".jpeg\", \".JPG\", \".JPEG\"}\nPAUSE_BETWEEN  = 0.5               # Seconds to pause between uploads (be kind to your server)\n\n# ?????????????????????????????????????????????\n\n\ndef get_session(username: str, app_password: str):\n    \"\"\"Return a requests Session with Basic Auth configured.\"\"\"\n    import requests\n    from requests.auth import HTTPBasicAuth\n    session = requests.Session()\n    session.auth = HTTPBasicAuth(username, app_password.replace(\" \", \"\"))\n    session.headers.update({\"User-Agent\": \"WP-Gallery-Uploader\/1.0\"})\n    return session\n\n\ndef check_connection(session, wp_url: str) -> bool:\n    \"\"\"Verify we can reach the WordPress REST API and authenticate.\"\"\"\n    import requests\n    url = f\"{wp_url}\/wp-json\/wp\/v2\/users\/me\"\n    try:\n        r = session.get(url, timeout=15)\n        if r.status_code == 200:\n            name = r.json().get(\"name\", \"Unknown\")\n            print(f\"? Connected to WordPress as: {name}\", flush=True)\n            return True\n        elif r.status_code == 401:\n            print(\"? Authentication failed. Check your username and Application Password.\", flush=True)\n        else:\n            print(f\"? Unexpected response ({r.status_code}). Check your WP_URL.\", flush=True)\n        return False\n    except requests.exceptions.ConnectionError:\n        print(f\"? Could not connect to {wp_url}. Check the URL and your internet connection.\", flush=True)\n        return False\n\n\ndef get_image_files(folder: Path) -> list[Path]:\n    \"\"\"Return sorted list of JPEG files in the folder.\"\"\"\n    files = sorted([\n        f for f in folder.iterdir()\n        if f.is_file() and f.suffix in SUPPORTED_EXTS\n    ])\n    return files\n\n\ndef upload_image(session, wp_url: str, image_path: Path) -> dict | None:\n    \"\"\"Upload a single image to the WordPress Media Library.\"\"\"\n    import requests\n\n    url = f\"{wp_url}\/wp-json\/wp\/v2\/media\"\n    filename = image_path.name\n\n    with open(image_path, \"rb\") as f:\n        image_data = f.read()\n\n    headers = {\n        \"Content-Disposition\": f'attachment; filename=\"{filename}\"',\n        \"Content-Type\": \"image\/jpeg\",\n    }\n\n    try:\n        r = session.post(url, headers=headers, data=image_data, timeout=60)\n        if r.status_code == 201:\n            media = r.json()\n            print(f\"  ? Uploaded: {filename} (ID: {media['id']})\", flush=True)\n            return media\n        else:\n            print(f\"  ? Failed to upload {filename}: {r.status_code} \u2014 {r.text[:200]}\", flush=True)\n            return None\n    except requests.exceptions.Timeout:\n        print(f\"  ? Timeout uploading {filename}. Try a smaller image or check your server.\", flush=True)\n        return None\n\n\ndef build_gallery_block(media_items: list[dict]) -> str:\n    \"\"\"Build a Gutenberg gallery block from a list of uploaded media items.\"\"\"\n\n    # Build individual image blocks inside the gallery\n    image_blocks = []\n    for media in media_items:\n        media_id   = media[\"id\"]\n        media_url  = media[\"source_url\"]\n        media_link = media.get(\"link\", \"\")\n        alt_text   = media.get(\"alt_text\", \"\")\n        caption    = media.get(\"caption\", {}).get(\"raw\", \"\")\n\n        img_block = (\n            f'&lt;!-- wp:image {{\"id\":{media_id},'\n            f'\"sizeSlug\":\"large\",\"linkDestination\":\"media\"}} -->\\n'\n            f'&lt;figure class=\"wp-block-image size-large\">'\n            f'&lt;a href=\"{media_url}\">'\n            f'&lt;img src=\"{media_url}\" alt=\"{alt_text}\" class=\"wp-image-{media_id}\"\/>'\n            f'&lt;\/a>'\n            f'{\"&lt;figcaption class=\\'wp-element-caption\\'>\" + caption + \"&lt;\/figcaption>\" if caption else \"\"}'\n            f'&lt;\/figure>\\n'\n            f'&lt;!-- \/wp:image -->'\n        )\n        image_blocks.append(img_block)\n\n    ids_list = \",\".join(str(m[\"id\"]) for m in media_items)\n    images_joined = \"\\n\".join(image_blocks)\n\n    gallery_block = (\n        f'&lt;!-- wp:gallery {{\"ids\":[{ids_list}],'\n        f'\"columns\":3,\"imageCrop\":false,'\n        f'\"linkTo\":\"media\",\"sizeSlug\":\"large\"}} -->\\n'\n        f'&lt;figure class=\"wp-block-gallery has-nested-images '\n        f'columns-3 is-cropped\">\\n'\n        f'{images_joined}\\n'\n        f'&lt;\/figure>\\n'\n        f'&lt;!-- \/wp:gallery -->'\n    )\n\n    return gallery_block\n\n\ndef create_draft_post(session, wp_url: str, title: str,\n                      gallery_block: str, status: str) -> dict | None:\n    \"\"\"Create a WordPress post containing the gallery block.\"\"\"\n    import requests\n\n    url = f\"{wp_url}\/wp-json\/wp\/v2\/posts\"\n\n    # Add a placeholder paragraph before the gallery for your text\n    content = (\n        f'&lt;!-- wp:paragraph -->\\n'\n        f'&lt;p>Add your post text here...&lt;\/p>\\n'\n        f'&lt;!-- \/wp:paragraph -->\\n\\n'\n        f'{gallery_block}'\n    )\n\n    payload = {\n        \"title\":   title,\n        \"content\": content,\n        \"status\":  status,\n    }\n\n    try:\n        r = session.post(url, json=payload, timeout=30)\n        if r.status_code == 201:\n            return r.json()\n        else:\n            print(f\"? Failed to create post: {r.status_code} \u2014 {r.text[:300]}\", flush=True)\n            return None\n    except requests.exceptions.Timeout:\n        print(\"? Timeout creating post.\", flush=True)\n        return None\n\n\ndef main():\n    parser = argparse.ArgumentParser(\n        description=\"Upload a folder of images to WordPress and create a gallery post.\"\n    )\n    parser.add_argument(\n        \"folder\",\n        help=\"Path to the folder containing your exported JPEG images\"\n    )\n    parser.add_argument(\n        \"--title\",\n        default=\"\",\n        help='Title for the new blog post (default: folder name)'\n    )\n    args = parser.parse_args()\n\n    # ?? Validate folder ??????????????????????????????????????????????\n    folder = Path(args.folder).expanduser().resolve()\n    if not folder.exists() or not folder.is_dir():\n        print(f\"? Folder not found: {folder}\", flush=True)\n        sys.exit(1)\n\n    post_title = args.title or folder.name\n\n    # ?? Check config ?????????????????????????????????????????????????\n    if \"your-site.com\" in WP_URL or \"your_username\" in WP_USERNAME:\n        print(\"? Please edit the CONFIGURATION section at the top of this script\", flush=True)\n        print(\"  and fill in your WP_URL, WP_USERNAME, and WP_APP_PASS.\", flush=True)\n        sys.exit(1)\n\n    # ?? Check requests is installed ??????????????????????????????????\n    try:\n        import requests  # noqa: F401\n    except ImportError:\n        print(\"? The 'requests' library is not installed.\", flush=True)\n        print(\"  Run:  pip3 install requests\", flush=True)\n        sys.exit(1)\n\n    # ?? Find images ??????????????????????????????????????????????????\n    images = get_image_files(folder)\n    if not images:\n        print(f\"? No JPEG files found in: {folder}\", flush=True)\n        sys.exit(1)\n\n    print(f\"\\n{'?'*50}\", flush=True)\n    print(f\"  WordPress Gallery Uploader\", flush=True)\n    print(f\"{'?'*50}\", flush=True)\n    print(f\"  Folder : {folder}\", flush=True)\n    print(f\"  Images : {len(images)} found\", flush=True)\n    print(f'  Post   : \"{post_title}\" ({POST_STATUS}, flush=True)')\n    print(f\"  Site   : {WP_URL}\", flush=True)\n    print(f\"{'?'*50}\\n\", flush=True)\n\n    # ?? Connect ??????????????????????????????????????????????????????\n    session = get_session(WP_USERNAME, WP_APP_PASS)\n    if not check_connection(session, WP_URL):\n        sys.exit(1)\n\n    # ?? Upload images ????????????????????????????????????????????????\n    print(f\"\\nUploading {len(images, flush=True)} image(s)...\\n\")\n    uploaded_media = []\n\n    for i, image_path in enumerate(images, 1):\n        print(f\"  [{i}\/{len(images, flush=True)}] {image_path.name}\")\n        media = upload_image(session, WP_URL, image_path)\n        if media:\n            uploaded_media.append(media)\n        if i &lt; len(images):\n            time.sleep(PAUSE_BETWEEN)\n\n    if not uploaded_media:\n        print(\"\\n? No images were uploaded successfully. Aborting post creation.\", flush=True)\n        sys.exit(1)\n\n    print(f\"\\n? {len(uploaded_media, flush=True)}\/{len(images)} images uploaded successfully.\\n\")\n\n    # ?? Build gallery and create post ????????????????????????????????\n    print(\"Creating gallery post...\", flush=True)\n    gallery_block = build_gallery_block(uploaded_media)\n    post = create_draft_post(session, WP_URL, post_title, gallery_block, POST_STATUS)\n\n    if post:\n        post_id   = post[\"id\"]\n        edit_link = f\"{WP_URL}\/wp-admin\/post.php?post={post_id}&amp;action=edit\"\n        print(f\"\\n{'?'*50}\", flush=True)\n        print(f\"  ? Post created successfully!\", flush=True)\n        print(f\"  Title  : {post['title']['rendered']}\", flush=True)\n        print(f\"  Status : {post['status']}\", flush=True)\n        print(f\"  Edit   : {edit_link}\", flush=True)\n        print(f\"{'?'*50}\\n\", flush=True)\n        print(\"Next steps:\", flush=True)\n        print(\"  1. Open the edit link above in your browser\", flush=True)\n        print(\"  2. Replace the placeholder paragraph with your post text\", flush=True)\n        print(\"  3. Click Publish when you're ready\\n\", flush=True)\n    else:\n        print(\"\\n? Post creation failed. Your images were uploaded to the Media Library.\", flush=True)\n        print(f\"   You can create a post manually using them in WordPress admin.\\n\", flush=True)\n\n\nif __name__ == \"__main__\":\n    main()\n<\/pre>\n\n\n\n<p><em><strong>wpgallery.sh<\/strong> \u2014 the optional Raycast Script Command wrapper<\/em><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">#!\/bin\/bash\n\n# Required parameters:\n# @raycast.schemaVersion 1\n# @raycast.title WordPress Gallery Upload\n# @raycast.mode fullOutput\n\n# Optional parameters:\n# @raycast.icon ?\n# @raycast.packageName WordPress Tools\n# @raycast.description Upload a folder of images to WordPress and create a draft gallery post\n\n# Arguments:\n# @raycast.argument1 { \"type\": \"text\", \"placeholder\": \"Subfolder name (e.g. Tamron300samples)\" }\n# @raycast.argument2 { \"type\": \"text\", \"placeholder\": \"Post title (optional \u2014 uses folder name if blank)\", \"optional\": true }\n\n# ?? SET YOUR BASE FOLDER HERE ??????????????????????????????????????\nBASE_FOLDER=\"$HOME\/Pictures\"\n# ??????????????????????????????????????????????????????????????????\n\nSUBFOLDER=\"$1\"\nTITLE=\"$2\"\n\n# If the argument looks like a full path (starts with \/ or ~), use it directly.\n# Otherwise treat it as a subfolder name inside BASE_FOLDER.\n# This means you can type just \"Tamron300samples\" for ~\/Pictures\/the617\/Tamron300samples\n# or override with a full path like ~\/Pictures\/other-folder if needed.\nif [[ \"$SUBFOLDER\" == \/* ]] || [[ \"$SUBFOLDER\" == ~* ]]; then\n    FOLDER=\"${SUBFOLDER\/#\\~\/$HOME}\"\nelse\n    FOLDER=\"$BASE_FOLDER\/$SUBFOLDER\"\nfi\n\n# Activate the virtual environment\nsource ~\/wp-uploader-env\/bin\/activate\n\n# Run the Python script, passing title only if one was provided\nif [ -n \"$TITLE\" ]; then\n    python3 -u ~\/Documents\/coding\/wordpress\/wp_gallery_upload.py \"$FOLDER\" --title \"$TITLE\"\nelse\n    python3 -u ~\/Documents\/coding\/wordpress\/wp_gallery_upload.py \"$FOLDER\"\nfi\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>If you use Adobe Lightroom Classic and run a self-hosted WordPress site, you&#8217;ve probably looked at the various plugins that promise to wire the two together \u2014 only to find they all want a recurring subscription fee. For a personal blog where you just want to post the occasional gallery,<\/p>\n","protected":false},"author":2,"featured_media":14213,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[11,4],"tags":[],"class_list":["post-14209","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-lightroom-software","category-software","comments-off"],"_links":{"self":[{"href":"https:\/\/www.rjt.org.uk\/home\/wp-json\/wp\/v2\/posts\/14209","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.rjt.org.uk\/home\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.rjt.org.uk\/home\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.rjt.org.uk\/home\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.rjt.org.uk\/home\/wp-json\/wp\/v2\/comments?post=14209"}],"version-history":[{"count":6,"href":"https:\/\/www.rjt.org.uk\/home\/wp-json\/wp\/v2\/posts\/14209\/revisions"}],"predecessor-version":[{"id":14218,"href":"https:\/\/www.rjt.org.uk\/home\/wp-json\/wp\/v2\/posts\/14209\/revisions\/14218"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.rjt.org.uk\/home\/wp-json\/wp\/v2\/media\/14213"}],"wp:attachment":[{"href":"https:\/\/www.rjt.org.uk\/home\/wp-json\/wp\/v2\/media?parent=14209"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.rjt.org.uk\/home\/wp-json\/wp\/v2\/categories?post=14209"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.rjt.org.uk\/home\/wp-json\/wp\/v2\/tags?post=14209"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}