Self-hosted service
ihmily/doubao-nomark avatar
ihmily/doubao-nomark

Doubao-nomark: four ways to pull clean media out of a chat link

一键下载无水印豆包AI图片/视频API | 浏览器扩展| 微软Edge插件 | 谷歌Chrome插件 | 油猴脚本

1,029 stars186 forksJavaScriptMIT

At a glance

What is it?
A FastAPI service and three browser extensions that turn a shared Doubao, Dola or Qianwen conversation URL into direct image and video links, with two documentation inconsistencies worth knowing about.
Who is it for?
This is a small, single purpose service that solves one problem and shows its work in the changelog. The parse endpoints are symmetric, the response shapes are documented with examples, and the extension paths cover both the no server case and the library case.
Can I use it commercially?
Yes. MIT 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 27 days ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 20, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What the service actually does

The pitch is one sentence: from a Doubao conversation link, extract watermark free image and video resources, delivered as an API service or a browser extension. The supported sources widened over time, and the changelog records the sequence rather than a vague feature list. v1.0.4 added Qianwen chat page image extraction to both the API and the extensions. v1.0.5 fixed watermark free video extraction on Doubao and added video support to the browser extension. v1.0.7 fixed video extraction and reworked the extension UI. v1.0.8 added support for the international Doubao site at www.dola.com and added a Chrome extension.

That last entry explains the topic tags, which include doubao, dola-ai, doubao-api, doubao-extension, doubao-seedance and qianwen-extension. The seedance tag points at ByteDance's video generation model, which is what produces the clips the tool exists to retrieve.

The repository is a Python service with a JavaScript extension surface, and the language metadata reflects the extension side rather than the server. The tree has `app.py` at the root, a `doubao_parser/` package holding the parsing logic, an `extension/` directory with the Chrome extension, `docs/` with screenshots, and packaging files including `pyproject.toml`, `requirements.txt`, `uv.lock`, `uv.toml` and a `.ruff.toml`.

Four documented ways to run it

Getting started is documented four ways, which is generous for a service this focused, and the recommended path uses uv. The README marks the local uv route as the recommended one, and the steps are clone, sync, activate and serve.

bash
# 1. 克隆项目
git clone https://github.com/ihmily/doubao-nomark.git
cd doubao-nomark

# 2.使用 uv 创建虚拟环境并安装依赖
uv sync

# 3. 激活虚拟环境
source .venv/bin/activate  # Linux/Mac

The second route is plain pip with `pip install -r requirements.txt` followed by the same uvicorn command. The third is Docker, split into two sub routes: pulling the published image `ihmily/doubao-nomark` from Docker Hub and running it detached with port 8000 mapped, or building locally with `docker build -t doubao-nomark .`. The README also gives the stop and remove commands, so the full container lifecycle is covered.

The fourth route is the one that matters for integration, because it installs the project as a library in editable mode with `pip install -e .` and imports from `doubao_parser` directly. The build backend is hatchling and the wheel target packages `doubao_parser`, so the parser is cleanly separated from the web layer. The runtime dependencies are a compact list: cryptography, FastAPI, uvicorn with the standard extra, httpx, Pydantic and pydantic-settings. Python 3.10 or later is required, while the Dockerfile pins a 3.11 slim base, exposes port 8000 and runs the same uvicorn command as the local instructions.

Library use is the cleanest surface

The library example is short enough to read at a glance, which is a good sign for an integration surface. Two async functions are exposed, one per media type, each taking a URL and a `return_raw` flag.

python
from doubao_parser.image import doubao_image_parse

# 异步调用
result = await doubao_image_parse(
    url="https://www.doubao.com/thread/xxxxxx",
    return_raw=False,  # False: 返回简化格式, True: 返回原始数据
)

The `return_raw` flag is the interesting design decision. A simplified response is what a caller almost always wants, since it flattens the upstream payload into a predictable shape, and the raw form is preserved for anyone who needs a field the author did not think to expose. Since both functions are async, the HTTP client underneath is httpx, and the cryptography dependency is a strong hint about what the parsing actually does: the identifiers in these URLs are not plain IDs, and something in the chain is encrypted or signed.

The video counterpart takes the same arguments and is imported from `doubao_parser.video`. The README then points readers at the package directory for details rather than documenting the rest of the API surface, which means the library is documented by example plus source. That is a reasonable trade for something this narrow, and it is also the place to look if you need a field neither example exposes.

The HTTP endpoints and their response shapes

Two endpoints cover everything: `POST /parse` for images and `POST /parse-video` for video, and each one also accepts a GET with the URL as a query parameter, which makes both of them trivially testable from a browser or from curl. Interactive documentation is served by FastAPI at `/docs`, and the README tells you to visit `http://localhost:8000/docs` to see it.

The image response is a small object with a success flag, a count and a list, documented with a filled in example.

json
{
  "success": true,
  "image_count": 3,
  "images": [
    {
      "url": "https://...",
      "width": 1024,
      "height": 768
    }
  ]
}

The video response keeps the same success flag but returns a single nested object rather than a list, carrying the source URL, the dimensions, a definition string and a poster image.

json
{
  "success": true,
  "video": {
    "url": "https://...",
    "width": 1920,
    "height": 1080,
    "definition": "1080p",
    "poster_url": "https://..."
  }
}

That asymmetry is worth noting if you write a client. Images come back as an array with a separate count, video comes back as a single object, so a caller that assumes a uniform list will break on the video path. Both request bodies take the same two fields, a URL and `return_raw`, which keeps the client side simple even though the responses differ.

Three extension flavours, one of them server free

The extensions exist so the tool can be used without running a server, and there are three of them targeting different installation habits. The Tampermonkey script is the lightest: install Tampermonkey or a similar userscript manager, then install the script from its Greasy Fork page.

The Edge extension is published in the Microsoft Edge add-ons store. On a Doubao chat page it shows a camera button in the lower right corner or a download button in the top bar, and clicking it opens a material download panel. The README also covers the case where the button does not appear, which is fixed by exiting or refreshing the browser and reopening the chat page. That is a useful detail to have written down, since extension injection timing is a common source of confusion.

The Chrome extension is a standard Chromium Manifest V3 extension kept in `extension/chrome`, and it is installed locally rather than from a store: open `chrome://extensions/`, enable developer mode, load the unpacked extension from that directory, then open or refresh a Doubao, Dola or Qianwen chat page to get the same lower right entry point.

Getting a share link is documented identically for both media types: long press a message, select the image or video, then use the share action and copy the link address. The README explicitly notes that the method for video has caught up with the one for images.

Two places where the repository disagrees with itself

The first contradiction is licensing, and it is the one to resolve before building anything on this. The licence field records MIT, and the README displays an MIT badge linking to opensource.org. The licence section at the bottom of the README says something different, stating that the project is only for study and exchange. Those are not the same grant. MIT permits commercial use, modification and redistribution with attribution, while a study and exchange notice restricts use without defining exactly how. Anyone planning to deploy this inside a product needs to treat that section as a real constraint rather than boilerplate, and the honest reading is that the metadata and the prose have drifted apart.

The second contradiction is versioning. The changelog's most recent entry is v1.0.8, the release that added dola.com support and the Chrome extension, while `pyproject.toml` declares `version = "1.0.4"`. The package name in that same file is `doubao_parser` rather than the repository name, which suggests the library's own version was never bumped past 1.0.4 while the service and extensions moved on. Install it with uv or pip and inspect the distribution metadata, and you will see 1.0.4 regardless of which release notes you read.

Neither issue is a dealbreaker, and both are the sort of thing a maintainer fixes in a minute. They matter because they tell you the packaging metadata is not the source of truth here. The changelog and the source are.

Editorial conclusion

This is a small, single purpose service that solves one problem and shows its work in the changelog. The parse endpoints are symmetric, the response shapes are documented with examples, and the extension paths cover both the no server case and the library case. Two things deserve a second look before depending on it. The licence story is contradictory, since the metadata and the badge say MIT while the licence section says the project is for study and exchange only, and those are very different terms. The version story is contradictory too, since the changelog reaches v1.0.8 while `pyproject.toml` still declares 1.0.4. Neither is fatal, but both mean the packaging metadata lags the code. If you integrate it, pin the commit you tested and take a position on the licence yourself.

Frequently asked questions

How do I run the doubao-nomark service locally?

The README recommends the uv route: clone the repository, run `uv sync` to create the environment and install dependencies, activate `.venv` with `source .venv/bin/activate` on Linux or macOS, then serve with `uvicorn app:app --host 0.0.0.0 --port 8000`. Interactive API documentation is then at `http://localhost:8000/docs`. A plain pip route using `pip install -r requirements.txt` is documented as the alternative.

Can I use doubao-nomark as a Python library?

Yes. The README documents installing it in editable mode with `pip install -e .` and importing two async functions: `doubao_image_parse` from `doubao_parser.image` and `doubao_video_parse` from `doubao_parser.video`. Both take a URL and a `return_raw` flag, where false returns the simplified shape and true returns the upstream data. The build backend is hatchling and the wheel packages `doubao_parser`.

What are the API endpoints?

Two: `POST /parse` for images and `POST /parse-video` for video, and each also accepts a GET with the URL as a query parameter. The image response has a success flag, an image count and an images array with url, width and height per entry. The video response returns a single nested video object with url, width, height, definition and poster_url, so the two shapes are not symmetrical.

How do I get a share link for an image or video?

The method is the same for both media types, which the README calls out explicitly. In the chat page, long press the message to select the image or video, use the share option and copy the link address. That URL is what you pass to the API or to the extension.

What licence is doubao-nomark under?

The sources disagree. The licence field says MIT and the README shows an MIT badge, but the licence section of the README states that the project is only for study and exchange. The prose is the more restrictive statement and should be treated as the binding one until the maintainer reconciles them. The README also asks users to respect the platform's terms of service and applicable law.

Which browser extensions are available?

Three. A Tampermonkey userscript installable from Greasy Fork, a published Edge add-on that injects a camera button into the chat page, and a standard Chromium Manifest V3 extension in the `extension/chrome` directory that you load unpacked with developer mode enabled. All three work without running the server.

Official sources

  1. ihmily/doubao-nomark on GitHub
  2. Issues
  3. License: MIT
  4. Project website
  5. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/ihmily-doubao-nomark.svg)](https://hysenlabs.com/projects/ihmily-doubao-nomark)