xhs_ai_publisher: AI content creation and publishing for Xiaohongshu
AI-powered Xiaohongshu/Rednote content creation and publishing tool with PyQt desktop UI, FastAPI service, login-state reuse, preview publish, and automated browser workflows.
At a glance
- What is it?
- BetaStreetOmnis/xhs_ai_publisher pairs a PyQt desktop editor with a FastAPI service and Playwright automation so you can draft a Xiaohongshu post and push it live. The interesting part is the login-state reuse; the weak part is everything the README leaves unsaid about failure.
- Who is it for?
- Adopt xhs_ai_publisher if you already publish to Xiaohongshu by hand and want the drafting, cover generation and scheduled push in one place, and you accept that the first login has to happen in a visible browser. Skip it if you need a fully unattended pipeline on a server with no desktop: the README states plainly that headless mode will not pop a captcha window and instead asks you to log in again with a UI.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 80 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The gap xhs_ai_publisher tries to close
Publishing to Xiaohongshu is a manual loop: think of a topic, write a title and body, pick tags, build a cover, then upload each image and hit publish. xhs_ai_publisher targets people who run that loop often enough to resent it, and who are willing to keep a desktop program open to avoid it. The README frames the audience directly: content creators on the Xiaohongshu platform. The feature list splits into generation (titles, body text, tag suggestions, cover templates, hot-topic collection from Weibo, Baidu, Toutiao and Bilibili) and publishing (phone login, content preview, scheduled unattended publishing, saved login state). Multi-account support is there too, with login state and data isolated per user. That combination is narrower than a general social media scheduler and wider than a prompt wrapper, and the README does not claim otherwise.
How the pieces fit: PyQt, FastAPI, Playwright, and a local data directory
The repository layout tells most of the story. main.py is the desktop entry point, src/core holds the PyQt pages, services, processor and scheduler, and src/web holds a FastAPI application served by uvicorn as src.web.app:app. Playwright drives the actual browser. Runtime state lands in ~/.xhs_system/ by default, or in whatever XHS_DATA_DIR points at, and that directory holds cookies, storage_state, logs, the database and per-user Chrome profiles under users/<id>/chrome_user_data when XHS_USE_PERSISTENT_CONTEXT is true. Model API keys are encrypted at rest in ~/.xhs_system/keys.enc, which is a reasonable default for a tool that asks you to paste provider credentials. The two front ends share that data directory, which is why the documented Docker flow works at all: you log in once on a machine with a screen, then mount the resulting directory into the container at /data. The web service lazily initialises the browser runtime, so a container can start and answer health checks before any Playwright process exists. Set XHS_WEB_EAGER_BROWSER=true if you want the browser warm at boot instead.
Installing it and publishing a first post
The README recommends the one-shot installer. On macOS or Linux you make the scripts executable and run them; the launcher prefers venv/bin/python and falls back to the system interpreter if the virtual environment is broken, which is a small but honest piece of engineering.
chmod +x install.sh 启动程序.sh
./install.sh
./启动程序.shThe installer detects Playwright browsers and installs them if missing. Two flags are documented: --with-browser forces the install, --skip-browser skips it. Windows users run install.bat and 启动程序.bat instead. The README warns that Python 3.13 or 32-bit Python commonly breaks the PyQt5 install, so use 3.11 or 3.12 64-bit.
If you prefer the manual route, the README gives these steps. The fourth line installs Chromium into a project-local path rather than the default cache.
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
PLAYWRIGHT_BROWSERS_PATH="$HOME/.xhs_system/ms-playwright" python -m playwright install chromium
./启动程序.shBefore generating anything with a hosted model, copy .env.example and fill in the OpenAI-compatible block. The README notes that the UI configuration wins unless you set XHS_LLM_OVERRIDE=true.
XHS_LLM_BASE_URL=
XHS_LLM_MODEL=
XHS_LLM_API_KEY=
XHS_LLM_OVERRIDE=falseFor the headless service path, the login tool is the first real use. It opens a browser, reuses or saves storage_state plus cookies, and lets you finish a captcha or QR scan by hand if the platform challenges you.
python scripts/xhs_login_cli.py --phone 13800138000 --country-code +86After that, the Docker route is two commands. The compose file maps port 8000, sets XHS_HEADLESS to true, and mounts ./docker-data at /data. Health is at http://localhost:8000/healthz and readiness at http://localhost:8000/readyz.
docker compose build
docker compose up -dThe login-state design is the real product decision
Everything else in this project is replaceable. The login-state reuse is not, because it is the part that decides whether automation is viable at all. The README describes a deliberate sequence: complete a login once in a visible environment, save the state, then let the web or API service reuse it headlessly. When local storage_state or cookies expire, the login flow scans multiple system Chrome profiles to find a usable Xiaohongshu session and imports it; XHS_AUTO_IMPORT_SYSTEM_CHROME_STATE=false turns that off. The README also says the scan is skipped by default when Chrome is already running, to avoid spawning extra windows. Two other defaults are worth knowing. Stealth fingerprint overrides are not injected when a real persistent Chrome profile is used, unless XHS_ENABLE_STEALTH_SCRIPT=true. And forced DOM actions (JS-driven clicks, synthetic input and change events) are disabled for automatic publishing, only enabled in manual confirmation mode or with XHS_ENABLE_FORCE_DOM_ACTIONS=true. Those defaults read as someone who got burned by brittle selectors and decided that failing visibly beats clicking the wrong element.
Where it breaks: headless login, PyQt in containers, and a silent scheduler
The README is explicit that headless mode cannot solve an authentication challenge. If the login state goes stale, no captcha window appears; the service reports that you must log in again in a graphical environment. That is a hard boundary for anyone hoping to run this on a bare VPS indefinitely. The second limitation is architectural: the desktop build depends on PyQt5, and the README states that PyQt is not suitable for container GUI deployment, which is why the container runs FastAPI and Playwright only. You are effectively maintaining two deployment shapes with one data directory. Third, scheduled publishing requires the program to stay running and the account to stay logged in. That is a stated constraint, not an oversight, but it means a laptop that sleeps is a missed post. Finally, the README does not document rollback, retry semantics for a failed publish, or what happens to a queued task when the login state expires mid-run. Those gaps matter more than the feature list.
Compared with xiaohongshu-mcp and xhs-toolkit
The README itself points at xiaohongshu-mcp and xhs-toolkit as the reference for the login-then-service pattern, and says the current repository now supports the same shape. The difference is scope. Those projects are positioned as toolkits and MCP-style interfaces, meaning the content is expected to come from somewhere else, typically a model client driving the tool. xhs_ai_publisher bundles the content side: prompt templates under templates/prompts/*.json, cover and content-image generation, hot-topic collection, and a PyQt editor where a human reviews the draft before it goes out. If your workflow already lives in a chat client and you only need a publishing primitive, the bundled editor is weight you will not use. If you want the draft, the images and the push in one window, the toolkit approach leaves you assembling that yourself. The README does not publish a comparison table, so treat this as a scope difference rather than a quality ranking.
Licence, maintenance and the cost of upgrading
The project is Apache-2.0, which permits commercial use and modification and includes a patent grant; it also requires that you keep the licence and notice files and state significant changes. That is a summary of the licence text, not legal advice. On maintenance, the last push to main was on 2026-06-28 and release v1.2.0 is dated the same day. The repository is not archived, but a gap of roughly two and a half months between that push and now is not the same thing as steady activity, and the README's own version badges are inconsistent with the release tag, which is a small sign that documentation updates lag the code. Upgrade cost concentrates in two places: the Playwright and Chromium pairing, since a browser bump can change how the publishing automation behaves against the site, and the requirements split across requirements.txt, requirements-web.txt and requirements-build.txt, which means the desktop and container environments can drift apart. Pin what you install.
Editorial conclusion
Adopt xhs_ai_publisher if you already publish to Xiaohongshu by hand and want the drafting, cover generation and scheduled push in one place, and you accept that the first login has to happen in a visible browser. Skip it if you need a fully unattended pipeline on a server with no desktop: the README states plainly that headless mode will not pop a captcha window and instead asks you to log in again with a UI. Before committing, verify three things: that your Python is 3.11 or 3.12 64-bit on Windows, that a fresh login state written by scripts/xhs_login_cli.py survives a container restart against the mounted /data volume, and that the scheduler still fires when the desktop process stays open for a full day. The repository's last push was on 2026-06-28, so treat the current behaviour as a snapshot rather than a moving target.
Frequently asked questions
How do I install xhs_ai_publisher on Windows?
Run install.bat, then start the program with 启动程序.bat. The README advises Python 3.11 or 3.12 in 64-bit, because Python 3.13 or 32-bit Python commonly causes the PyQt5 installation to fail.
How does xhs_ai_publisher handle login?
You complete a login once in a visible environment, either through the desktop app or by running scripts/xhs_login_cli.py with a phone number and country code, and the login state is saved under ~/.xhs_system/. A web or Docker service can then reuse that state headlessly, but if it expires the README states that headless mode will not show a captcha window and you must log in again with a UI.
Can xhs_ai_publisher run in Docker without a desktop?
Yes, but only the FastAPI and Playwright service mode. The README states that the PyQt desktop build is not suitable for container GUI deployment, so the container runs src.web.app:app on port 8000 with XHS_HEADLESS set to true, and you mount the login-state directory at /data.
Community notes