Library / SDK
TradingAi666/TzFilm-Douyin-Tool avatar
TradingAi666/TzFilm-Douyin-Tool

TzFilm-Douyin-Tool scrapes your own Douyin dashboard by stealing Chrome's focus

自动从抖音创作者中心导出视频数据,支持 macOS 后台定时运行

406 stars90 forksJavaScriptMIT

At a glance

What is it?
TzFilm-Douyin-Tool exports your own video statistics from the Douyin creator backend on a schedule, with no API key, by driving Chrome with AppleScript and parsing the downloaded Excel into SQLite. The value is the write-up of five macOS automation traps it had to work around. The cost, stated plainly in the README, is that a run takes Chrome's focus for about thirty seconds.
Who is it for?
TzFilm-Douyin-Tool fits one creator on a Mac who wants an hourly history of their own Douyin numbers without an API and who is willing to give up the foreground for half a minute at a time. It does not fit a team, a Linux box, or anyone who needs the data faster than hourly, since the backend caps its export at roughly the latest 100 videos and the README warns that tighter intervals can trip risk controls.
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 133 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 October 7, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The manual flow is four clicks, and the tool exists because they repeat hourly

The origin story is short and specific. The author runs an AI and technology channel on Douyin and wanted video data every day, which meant opening `creator.douyin.com`, switching to the submission list, clicking export data, downloading an Excel file, and then analysing it by hand. The script automates exactly that sequence and nothing more.

The pipeline is worth reading as a diagram because each stage hands off to the next in a way that constrains the ones after it. The creator backend is driven by AppleScript controlling Chrome. The script switches to the submission list tab, clicks the export button, and the browser downloads a work-list spreadsheet. openpyxl parses it, the rows go into SQLite, and a Telegram bot push is optional at the end.

What lands in the database per video is the set a creator actually watches: plays, likes, comments, shares, favourites, completion rate, and click-through rate. Because every run writes a snapshot rather than overwriting a row, the history is a growth curve, which is the thing the creator dashboard itself will not give you.

The claim of purity is worth stating precisely rather than as marketing. There is no API key and no third-party service in the path. The data is your own, read out of your own logged-in browser session, and it lands in a local database. That is the whole appeal for a creator who does not want to hand a vendor's backend a token.

launchd cannot read ~/Downloads, so the file moves through osascript instead

The first of five documented traps is the macOS TCC sandbox. A process started by launchd cannot read `~/Downloads`, and the frustrating part is that granting Full Disk Access does not fix it. The system prompt will grant the permission and the process still gets nothing, because the restriction here is not the one that setting controls.

The documented workaround is a chain of process hand-offs: launchd starts Python, Python calls osascript, osascript runs a shell script, and the shell script performs the move. The reason it works is that osascript inherits the TCC context of the GUI session it is invoked from, so the operation appears to come from a process that already has the access.

This is the kind of fix that works and is also a warning. It is not a documented API path, it depends on a specific chain of process relationships, and it will change if any link is rewritten. Anyone reading this file before running it should understand that the file move is being smuggled across a permission boundary rather than granted one.

It is also the trap most likely to be hit first by a new user, because the obvious thing to try, adding Full Disk Access, is exactly the thing that does not work.

A click injected by AppleScript is not a user gesture, so the download is blocked

The second trap is the one that shapes the whole architecture. Chrome silently swallows downloads triggered from a background tab, because a `click()` injected through AppleScript does not count as a user gesture, and Chrome's download policy treats a non-gesture download from a background tab as something to suppress rather than something to perform.

Nothing errors. That is what makes it hard. The script believes it clicked the button, the page believes it was clicked, and the file simply never arrives.

The fix is three lines of AppleScript and it is blunt: activate the application, set the active tab index, and set the index of the window to 1, which forces Chrome to the foreground so the click becomes a real user gesture. The README names the consequence directly: a scrape takes Chrome's focus for roughly thirty seconds, and calls it a hard flaw with no fix short of moving to the Chrome DevTools Protocol.

So the design has a visible cost, stated rather than hidden. Every hour, on a schedule you chose, the browser you were using comes to the front and stays there for half a minute. That is fine on a spare machine and unacceptable on the one you are working on, and it is worth deciding which of those you have before you install the launchd job.

Garfish isolates the DOM, so body.innerText returns 460 characters of navigation

The third trap is the one that will break first in practice, and the README flags it again in its notes section. The Douyin creator backend is built on Garfish, a micro-frontend framework, and Garfish gives each micro-application its own DOM scope. The visible result for a scraper is that reading the whole page with `body.innerText` yields about 460 characters, which is the navigation bar and nothing else.

This is not a bug in the tool and it is not fixable by waiting. The content exists in the document, it is just not reachable from the top-level body node, so the natural approach silently returns the wrong thing. A script that checks only whether the text came back non-empty will report success.

The fourth trap is in the same family and even quieter. Unicode escapes written into AppleScript fail without an error, so matching a Chinese button label with an escape sequence such as `\u5bfc\u51fa` produces a comparison that is always false. The documented fix is to give up on escapes entirely and hard-match on character code, comparing the first character code of the export label against a number, for example 23548.

The fifth trap is quieter still and is the reason the launchd chain above has the shape it does: launchd does not set HOME, so `os.path.expanduser("~")` returns a wrong path in a background process. Every path in the project has to be built without relying on the home directory resolving itself.

The prediction is current plays times a median of your own past ratios

The tracker answers a question the dashboard does not: where will this video land. It samples a new video every thirty minutes and predicts a final play count from three pieces of your own history plus three quality corrections.

The formula is written out in the README. The predicted final equals the current plays, multiplied by the median same-period ratio, then by a CTR correction, an interaction correction, and an average-duration correction. The same-period ratio is the median multiplier from your historical videos at the same elapsed time since publication, so a video at 2.3 hours is compared against your other videos at 2.3 hours rather than against your finished ones.

The three corrections are percentile comparisons against those same-period videos: click-through rate, interaction rate, and average watch duration. Being above the median earns a multiplier, being below costs one.

Confidence is stated as a function of tracking time, from 25% in the first two hours to 90% at twenty-four hours. That progression is the honest part of the design, and it is also the reason the workflow generates forty-eight scheduled runs for a single video: twenty-four hours at one every thirty minutes. The query script exposes the result, with commands for the latest state, the full growth curve, the twenty-four-hour-to-final ratios used for calibration, and the complete record for a named video.

Two modules share one database. `video_stats` holds the hourly raw snapshots and doubles as the historical input to the prediction, and `video_tracking` plus a metadata table hold the thirty-minute checkpoints and the baseline state.

Comment replies are drafted by you and sent 30 at a time by Playwright

The newest component automates comment replies, and its shape is a deliberate split between generation and sending. The tool exports comments, you produce the reply text with whatever assistant you prefer, and only then does the tool send.

Setup is one command that installs the Node dependencies and the Playwright browser, then stops for you to log in by hand:

bash
python3 auto_reply.py setup

Exporting scans a video's full comment list and writes the result to a JSON file of unreplied comments. You then edit a second JSON file into a plan, and the format is a selected work with its title and publish text plus a list of comment objects, each carrying the username, the original comment text, and your reply message.

Sending has a dry run:

bash
python3 auto_reply.py reply "视频标题关键词" --dry-run

Without the flag it sends for real, in batches of thirty. The browser automation matches each comment, clicks reply, types the text, and sends, one at a time rather than through a bulk endpoint.

The design choice worth naming is that this path is Playwright and Node, not AppleScript, and the README says it supports macOS, Windows, and Linux because of it. That also means it does not steal focus the way the hourly export does, since it is not injecting clicks into a foreground tab. The two halves of the project use different automation approaches for the same reason: one is bound to a logged-in Chrome session, the other runs its own browser.

Three operational limits, and the newest file in the repository is not in the manifest

The notes section lists what to expect. The scrape takes Chrome's focus for about thirty seconds. The creator dashboard's DOM changes occasionally, and a failed run is often a Garfish container identifier that moved, with a debugging method in `SKILL.md`. Run frequency should be an hour or longer because going faster can trigger risk controls on the account.

There is a fourth limit that shapes the whole database. The backend exports at most the latest hundred or so videos, so anything older has to be recovered from the snapshots you already took. That means the tool is only useful if it has been running since you wanted the history to start, and a fresh install has no backfill.

The file manifest lists the core script as roughly five hundred lines of Python with embedded AppleScript, the tracker, the query tool, the reply wrapper, the Playwright directory, the schema, the skill manual, and the launchd template. Setting up the hourly job means editing the plist with your username, copying it into your LaunchAgents directory, and loading it with launchctl.

One detail does not appear in that list. The repository root also holds a `feishu_sync.py`, a script for syncing to Feishu, which the manifest does not mention and the README does not describe. Treat undocumented files in a repository of scraped-credentials-adjacent scripts as something to read before running.

The licence is MIT, there are no GitHub releases, and the last push was on 2026-05-27.

Editorial conclusion

TzFilm-Douyin-Tool fits one creator on a Mac who wants an hourly history of their own Douyin numbers without an API and who is willing to give up the foreground for half a minute at a time. It does not fit a team, a Linux box, or anyone who needs the data faster than hourly, since the backend caps its export at roughly the latest 100 videos and the README warns that tighter intervals can trip risk controls. Before you install anything, read `SKILL.md`, because that file holds the debugging method for the failure you will hit first: a Garfish container identifier in the creator dashboard changing and the scrape silently returning nothing.

Frequently asked questions

Does TzFilm-Douyin-Tool need a Douyin API key?

No. It drives Chrome with AppleScript against a session you are already logged into, and the project describes itself as having zero API keys and no third-party services in the path. Data is parsed from the Excel file the dashboard exports.

What does TzFilm-Douyin-Tool scrape?

Your own video statistics from the Douyin creator backend: plays, likes, comments, shares, favourites, completion rate, and click-through rate. Each run writes a snapshot to SQLite so growth is tracked over time, and an optional Telegram bot can push the results.

Can TzFilm-Douyin-Tool run on Windows or Linux?

The hourly export path is macOS only, because it uses AppleScript against Chrome and a launchd job. The comment auto-reply component is built on Playwright and Node instead, and that part is documented as working on macOS, Windows, and Linux.

How does TzFilm-Douyin-Tool predict a video's final play count?

The v3 model multiplies current plays by the median same-period ratio from your own past videos, then applies CTR, interaction, and average-duration corrections computed as percentiles against videos at the same elapsed time. Confidence rises from 25% before two hours to 90% at twenty-four hours.

Why does the TzFilm-Douyin-Tool scrape sometimes return nothing?

The creator dashboard is built on the Garfish micro-frontend framework, which isolates each micro-application's DOM, so reading the page body returns only about 460 characters of navigation. A failed run is often a Garfish container identifier that changed, and SKILL.md carries the debugging method.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. TradingAi666/TzFilm-Douyin-Tool on GitHub
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/tradingai666-tzfilm-douyin-tool.svg)](https://hysenlabs.com/projects/tradingai666-tzfilm-douyin-tool)