DuckDuckGo's HTML search endpoint now requires vqd session tokens,
making server-side search-based URL resolution unreliable. Simplified
the thumbnail pipeline:
- OG scrape only for non-aggregator URLs (direct publisher pages)
- AI image generation via OpenRouter for aggregator articles (Google News)
- Added Image Model settings field in admin UI
- Added thumbnail_local column for AI-generated images
- Updated public/admin display to check thumbnail_local as fallback
- Removed all DuckDuckGo search code (find_article_url_via_search,
extract_ddg_url, resolve_article_for_scraping)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Google News RSS stores encrypted article URLs that cannot be resolved
server-side (JS SPA, no HTTP redirects, encrypted base64 IDs). Replace
the failed decode/parse strategies with DuckDuckGo search-based lookup
using article title + publisher name to find actual article pages, then
scrape OG images from those pages.
- Add find_article_url_via_search() using DuckDuckGo HTML endpoint
- Add resolve_article_for_scraping() combining redirect + search fallback
- Mark failed lookups with '_no_image' sentinel to avoid repeated retries
- Backfill button resets sentinels to allow manual re-scraping
- Clean up verbose debug instrumentation from prior attempts
- Update frontend to handle '_no_image' sentinel gracefully
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace batchexecute API with direct fetch + HTML parsing strategies:
data-n-au attributes, anchor href extraction, meta refresh, JS redirect.
Add verbose debug output to diagnose server-side response from Google News.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace broken base64 decoding with Google's internal batchexecute API
endpoint to resolve encrypted article IDs to actual publisher URLs.
This is the approach used by known Google News decoder libraries.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Temporary debug version that returns query counts, URL resolution results,
and OG scraping outcomes to identify why thumbnail scraping returns 0.
Also adds Google News URL decoder (base64/protobuf approach).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The OG image scraper was using Google News redirect URLs (source_url) directly,
which don't serve og:image tags. Added resolve_scrape_url() to follow redirects
(including meta-refresh and JS redirects) to reach the actual publisher page
before scraping. Also added a backfill AJAX endpoint and admin button to
re-scrape thumbnails for existing articles.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Google News RSS feeds report every article as coming from
"news.google.com" because the source was extracted from the feed URL
rather than the individual article. This adds a multi-strategy
resolver that extracts the real publisher name via:
1. SimplePie <source> element (Google News provides this)
2. Title suffix parsing ("Headline - Source" format)
3. HTTP HEAD redirect resolution as a fallback
Also adds a one-click "Fix Article Sources" backfill button on the
settings page to correct existing articles in the database.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Addresses QA Agent 2 findings:
- Add :focus outline on title and thumbnail links for keyboard navigation
- Wrap excerpt paragraph in conditional to avoid empty <p> when both
ai_summary and excerpt are missing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rewrites public CSS to match actual .pn-* class names from the PHP
template and changes the default shortcode layout from card/grid to a
horizontal list: fixed-width thumbnail on the left, title + summary on
the right. Includes compact variant for widgets and responsive
stacking on small screens.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Peptide_News_Loader class was referenced by class-peptide-news.php
but never committed, causing a fatal error on plugin activation.
Also adds public/css/public-style.css which is enqueued by the public
class.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Hostinger's git pull fails with divergent branches when force-pushing
orphan commits. Switch to a linear commit strategy: checkout the
existing deploy branch, sync plugin files from main, and push normally.
This keeps Hostinger's git pull fast-forward compatible.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace broken SCP/SSH deploy (blocked by Hostinger firewall) with a
deploy-branch strategy. After CI passes, GitHub Actions pushes only
plugin files to the 'deploy' branch. Hostinger's built-in GIT
integration watches this branch via webhook and auto-pulls.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
FTP deploy timed out due to Hostinger blocking FTPS from external CI
servers. Switched to appleboy/scp-action using SSH on port 65002,
which is more reliable for CI/CD pipelines.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The deploy workflow references CI via `uses: ./.github/workflows/ci.yml`
which requires the CI workflow to have an `on: workflow_call` trigger.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>