This is a tool used to log in to private torrent websites and take a screenshot of the user's profile page. This can be used to showcase stats on your current tracker as part of an application to another site. It can also be used as a historical record of your stats on a tracker in case it goes down or becomes otherwise unavailable.
- Opens the selected trackers and logs in, navigating to the user's profile page
- Requests user input for trackers with manual inputs (like Captcha, 2FA, etc.)
- Redacts the user's sensitive information
- There are several types of redaction:
- Blur (adds a Gaussian blur)
- Box (draws a solid box)
- Removal (the text is completely removed)
- Text (Replaces the text)
- None (no redaction)
- There following elements are currently redacted
- Email address
- IP address (including ISP)
- Passkey (for torrents or for IRC)
- There are several types of redaction:
- Takes a full-page screenshot of the redacted user profile
Below are examples of the different types of redaction from the MooKo tracker.
There are currently 160 supported trackers listed below. The available trackers come in the following types:
- Headless: Can run with the browser in headless mode, meaning no UI browser is needed
- Manual: There is some user interaction needed (a Captcha or 2FA to log in, etc.), requiring a UI browser
The implementation for these trackers can be found in the handler package.
The following trackers do not require a UI (unless FORCE_UI_BROWSER has been set to true), and can be run in the background:
If the following trackers are enabled (either uncommented in TRACKER_INPUT_FILE_PATH, or MANUAL is included in TRACKER_EXECUTION_ORDER), then
a UI must be enabled. Instructions for this in Docker can be seen below.
|
First, copy the trackers_example.csv file. This file needs to be updated with your user's login information for each tracker.
Any unwanted trackers can be deleted, or prefixed by the CSV_COMMENT_SYMBOL environment variable so they are excluded. The tracker names are
case-insensitive.
The file can be saved anywhere but needs to be mounted into the Docker container, and it will be referenced by the TRACKER_INPUT_FILE_PATH
environment variable when running the application, so remember where it is saved and what it is named.
The application is run using Docker, and below are the commands to run the latest docker image.
Docker Commands
docker run \
--env DISPLAY="${DISPLAY}" \
--env BROWSER_HEIGHT=1050 \
--env BROWSER_WIDTH=1680 \
--env CSV_COMMENT_SYMBOL='#' \
--env ENABLE_ADULT_TRACKERS=true \
--env ENABLE_TRANSLATION_TO_ENGLISH=true \
--env FAIL_ON_UNSUPPORTED_TRACKER=true \
--env FORCE_UI_BROWSER=false \
--env INPUT_TIMEOUT_ENABLED=false \
--env INPUT_TIMEOUT_SECONDS=300 \
--env JAVA_XMS=128m \
--env JAVA_XMX=512m \
--env LOG_LEVEL=INFO \
--env LOG_TRACKER_NAME=true \
--env NUMBER_OF_PARALLEL_THREADS=5 \
--env NUMBER_OF_SCREENSHOT_ATTEMPTS=1 \
--env OUTPUT_DIRECTORY_NAME_FORMAT=yyyy-MM-dd \
--env OUTPUT_DIRECTORY_PARENT_PATH=/app/screenshots \
--env PROGRESS_BAR_COMPLETE_CHARACTER='█' \
--env PROGRESS_BAR_ENABLED=true \
--env PROGRESS_BAR_FORMAT=":bar :percent% | :progress/:total | [:elapsed]" \
--env PROGRESS_BAR_INCOMPLETE_CHARACTER='░' \
--env PROGRESS_BAR_LENGTH=35 \
--env REDACTION_TEXT=---- \
--env REDACTION_TYPE=BOX \
--env SCREENSHOT_EXISTS_ACTION=CREATE_ANOTHER \
--env TAKE_SCREENSHOT_ON_ERROR=false \
--env TIMEZONE=UTC \
--env TRACKER_EXECUTION_ORDER=HEADLESS,MANUAL \
--env TRACKER_INPUT_FILE_PATH=/app/screenshots/trackers.csv \
-v /tmp/.X11-unix:/tmp/.X11-unix \
-v /tmp/screenshots:/app/screenshots \
-v tracker-chrome-cache:/tmp/chrome-home \
--name tracker-profiles \
--rm zodac/tracker-profiles:latest |
MSYS_NO_PATHCONV=1 docker run \
--env DISPLAY=host.docker.internal:0 \
--env BROWSER_HEIGHT=1050 \
--env BROWSER_WIDTH=1680 \
--env CSV_COMMENT_SYMBOL='#' \
--env ENABLE_ADULT_TRACKERS=true \
--env ENABLE_TRANSLATION_TO_ENGLISH=true \
--env FAIL_ON_UNSUPPORTED_TRACKER=true \
--env FORCE_UI_BROWSER=false \
--env INPUT_TIMEOUT_ENABLED=false \
--env INPUT_TIMEOUT_SECONDS=300 \
--env JAVA_XMS=128m \
--env JAVA_XMX=512m \
--env LOG_LEVEL=INFO \
--env LOG_TRACKER_NAME=true \
--env NUMBER_OF_PARALLEL_THREADS=5 \
--env NUMBER_OF_SCREENSHOT_ATTEMPTS=1 \
--env OUTPUT_DIRECTORY_NAME_FORMAT=yyyy-MM-dd \
--env OUTPUT_DIRECTORY_PARENT_PATH=/app/screenshots \
--env PROGRESS_BAR_COMPLETE_CHARACTER='█' \
--env PROGRESS_BAR_ENABLED=true \
--env PROGRESS_BAR_FORMAT=":bar :percent% | :progress/:total | [:elapsed]" \
--env PROGRESS_BAR_INCOMPLETE_CHARACTER='░' \
--env PROGRESS_BAR_LENGTH=35 \
--env REDACTION_TEXT=---- \
--env REDACTION_TYPE=BOX \
--env SCREENSHOT_EXISTS_ACTION=CREATE_ANOTHER \
--env TAKE_SCREENSHOT_ON_ERROR=false \
--env TIMEZONE=UTC \
--env TRACKER_EXECUTION_ORDER=HEADLESS,MANUAL \
--env TRACKER_INPUT_FILE_PATH=/app/screenshots/trackers.csv \
-v /c/tmp/screenshots:/app/screenshots \
-v tracker-chrome-cache:/tmp/chrome-home \
--name tracker-profiles \
--rm zodac/tracker-profiles:latest |
There are two ways to execute the application - with a UI browser and without. The default commands will execute trackers that require a UI, so the UI will need to be configured to run through Docker. A UI browser is needed for trackers that require some user input during login, like a Captcha, Cloudflare verification check, 2FA, etc.
Below will define how to do this for your host system.
To run through Docker with a UI, local connections to the host display might need to be enabled:
# This seems to be reset upon reboot and may need to be reapplied
xhost +local:I use VcXsrv as the X server for UI. When configuring VcXsrv, make sure to set the following in the configuration:
- Multiple windows
- Display number -1 or 0
- Disable access control
To disable the UI and run the browser in headless mode only, ensure FORCE_UI_BROWSER and ENABLE_TRANSLATION_TO_ENGLISH are set to false, and
exclude MANUAL from TRACKER_EXECUTION_ORDER.
The following are all possible configuration options, defined as environment variables for the docker image:
| Environment Variable | Description | Default Value |
|---|---|---|
| BROWSER_HEIGHT | The height (in pixels) of the web browser used to take screenshots | 1050 |
| BROWSER_WIDTH | The width (in pixels) of the web browser used to take screenshots | 1680 |
| CSV_COMMENT_SYMBOL | If this character is the first in a CSV row, the CSV row is considered a comment and not processed | # |
| DISPLAY | The X11 display used to render browser screenshots (see Browser UI) | None (required) |
| ENABLE_ADULT_TRACKERS | Whether to take screenshots of trackers that primarily host adult content | true |
| ENABLE_TRANSLATION_TO_ENGLISH | Whether to translate non-English trackers to English | true |
| FAIL_ON_UNSUPPORTED_TRACKER | Whether to fail if a tracker in the CSV file has no matching handler implementation | true |
| FORCE_UI_BROWSER | Forces a browser with UI for each tracker, even for headless trackers (this will disable parallel execution) | false |
| INPUT_TIMEOUT_ENABLED | Whether to add a timeout for when a user-input is required, otherwise waits | false |
| INPUT_TIMEOUT_SECONDS | If INPUT_TIMEOUT_ENABLED is enabled, how long to wait for a user-input in [seconds] | 300 |
| JAVA_XMS | The initial heap size for the Java process | 128m |
| JAVA_XMX | The maximum heap size for the Java process | 512m |
| LOG_LEVEL | The logging level for console output [TRACE, DEBUG, INFO, WARN, ERROR] | INFO |
| LOG_TRACKER_NAME | Whether to prefix each log message with the name of the tracker being screenshot | true |
| NUMBER_OF_PARALLEL_THREADS | The number of parallel browser threads to use for Headless trackers [min: 1, max: 32] | 5 |
| NUMBER_OF_SCREENSHOT_ATTEMPTS | The number of times to attempt to screenshot a tracker before marking it as a fail [min: 1, max: 5] | 1 |
| OUTPUT_DIRECTORY_NAME_FORMAT | The name of the output directory to be created for the of the screenshots (see Patterns for Formatting and Parsing) | yyyy-MM-dd |
| OUTPUT_DIRECTORY_PARENT_PATH | The output location of the new directory created for the screenshots, relative to the project root | /tmp/screenshots |
| PROGRESS_BAR_COMPLETE_CHARACTER | The character used to render the completed portion of the progress bar (must differ from PROGRESS_BAR_INCOMPLETE_CHARACTER | █ |
| PROGRESS_BAR_ENABLED | Whether to render a progress bar at the bottom of the console output | true |
| PROGRESS_BAR_FORMAT | The format string for the progress bar (must not be blank) | :bar :percent% | [:elapsed] |
| PROGRESS_BAR_INCOMPLETE_CHARACTER | The character used to render the incomplete portion of the progress bar (must differ from PROGRESS_BAR_COMPLETE_CHARACTER | ░ |
| PROGRESS_BAR_LENGTH | The length (in characters) of the progress bar [min: 10, max: 80] | 35 |
| REDACTION_TEXT | The placeholder text used to replace sensitive information (only when using TEXT redaction, will be truncated if longer than the sensitive information) | ---- |
| REDACTION_TYPE | Comma-separated list of redaction types to apply (if more than one is selected then multiple screenshots will be taken) [NONE, BLUR, BOX, REMOVE, TEXT] | BOX |
| SCREENSHOT_EXISTS_ACTION | What to do when a screenshot for the tracker for the given date already exists [CREATE_ANOTHER, OVERWRITE, SKIP] | CREATE_ANOTHER |
| TAKE_SCREENSHOT_ON_ERROR | Whether to take a screenshot of the current tracker page if any failure occurs (in a subdirectory called errors) |
false |
| TIMEZONE | The local timezone, used to retrieve the current date to name the output directory | UTC |
| TRACKER_EXECUTION_ORDER | The order in which different tracker types should be executed, at least one must be selected (case-insensitive) | HEADLESS,MANUAL |
| TRACKER_INPUT_FILE_PATH | The path to the input tracker definition CSV file (inside the docker container) | /tmp/screenshots/trackers.csv |
The progress bar is rendered at the bottom of the console output during execution, showing overall screenshot progress. It is powered by the Clique library.
The bar advances in fine-grained steps (login, profile page, each screenshot, logout) so the fill and percentage move smoothly. A tracker counter (
X/Y) is always appended to the right of the rendered format string, showing how many trackers have fully completed regardless of how the format
string is configured.
The format string supports the following tokens, as defined in the Clique source:
| Token | Description |
|---|---|
:bar |
The rendered progress bar itself |
:elapsed |
Time elapsed since the bar was started |
:percent |
Completion percentage (0–100), based on workflow steps not tracker count |
:progress |
Number of completed internal ticks (workflow steps, not trackers) |
:total |
Total number of internal ticks (workflow steps, not trackers) |
Note:
:progressand:totalreflect the internal tick count used to drive the bar fill (workflow steps), not the number of trackers. For a tracker counter, use theX/Ysuffix that is always appended to the bar.
Note: When the JVM's native encoding is not UTF-8, multibyte characters passed via environment variable (such as
█and░) may arrive as individual bytes rather than a single character. I try to convert these appropriately, but it doesn't always work. If you're having issues, try to use plain ASCII characters, or use the defaults. Feel free to raise an issue and I can look into it.
This project follows Semantic Versioning (MAJOR.MINOR.PATCH):
- MAJOR: incompatible changes to the public API (e.g. removed or renamed configuration options, changed CSV format)
- MINOR: backwards-compatible new functionality added to the public API (e.g. new configuration options or trackers)
- PATCH: backwards-compatible bug fixes or code changes
Changes to the trackers_example.csv file are considered MINOR (new trackers) or MAJOR (updates to the tracker
name). However, removing a tracker due to the site no longer being available is not considered a MAJOR change. Trackers are external to the
application and not part of its public API, as their availability depends on third-party sites that can change or disappear at any time. Tracker
removals will be released as PATCH versions.
I use AI agents to perform the "first-pass" implementation for a new tracker site. The AI will investigate and evaluate the generate structure of the site and build an initial handler. Based off of this, I'll refine and test until I'm happy that it matches the same standards as the existing implementations.
Since there were over 100 implementations prior to using AI, there are established patterns and styles to follow, hopefully guiding the AI to follow the same standards.
Claude Code employs a multi-agent pipeline to implement each new tracker handler:
| Agent | Responsibility |
|---|---|
| Orchestrator | Coordinates the full workflow, delegating to specialised sub-agents in sequence |
| Login Agent | Inspects the tracker's login page to determine the correct selectors for the username field, password field, login button, and post-login confirmation element |
| Profile Agent | Navigates the user's profile page to identify the navigation selector that reaches it, the content selector that confirms it has loaded, and the logout button selector |
| Page Structure Agent | Invoked when the profile agent flags structural elements and detects cookie/consent banners, fixed headers, and fixed sidebars that must be handled before screenshotting |
| Redactor Agent | Reviews the profile page for sensitive fields and determines the appropriate redaction selectors |
Every implementation relies on:
- Existing handler code: The agents study the handlers already in
the handler/ package to understand project conventions, selector
construction patterns, and platform-specific base classes (
Unit3dHandler,GazelleHandler, etc.) - CLAUDE.md: This defines project-level standards covering structure, selector conventions, linting rules, and the mandatory post-implementation checklist
- Agent definitions: Each agent has a Markdown file defining its scope, and how to parse the tracker to build the handler, which is refined over time
All AI-generated code is reviewed and approved before being merged, including manual testing of the new handler. Since each handler needs to be tested against a real site, there's no value to automating much testing, so each implementation will be verified manually.
The JavaScript redaction logic used in BlurRedactor and BoxRedactor is mostly AI created with little input from me. I don't know JavaScript well enough to perform this overlay-based redaction, but I verify against all tracker handlers when updates are made, to cover as many cases as possible.
The ProgressBarPrintStream contains some code for handling print streams to override the default console printing of the Clique progress bar. I'm not super well-versed in handling streams, so I use AI to make updates and then verify manually afterwards.
Run the following command to run git hooks for the project:
bash ./ci/hooks/setup-hooks.shBelow is the command to build and run the development docker image with everything enabled (requires the UI to be defined):
docker build -f ./docker/Dockerfile -t tracker-profiles-dev . &&
docker run \
--env DISPLAY="${DISPLAY}" \
--env BROWSER_HEIGHT=1050 \
--env BROWSER_WIDTH=1680 \
--env CSV_COMMENT_SYMBOL='#' \
--env ENABLE_ADULT_TRACKERS=true \
--env ENABLE_TRANSLATION_TO_ENGLISH=true \
--env FAIL_ON_UNSUPPORTED_TRACKER=false \
--env FORCE_UI_BROWSER=true \
--env INPUT_TIMEOUT_ENABLED=true \
--env INPUT_TIMEOUT_SECONDS=300 \
--env JAVA_XMS=128m \
--env JAVA_XMX=512m \
--env LOG_LEVEL=TRACE \
--env LOG_TRACKER_NAME=true \
--env NUMBER_OF_PARALLEL_THREADS=5 \
--env NUMBER_OF_SCREENSHOT_ATTEMPTS=5 \
--env OUTPUT_DIRECTORY_NAME_FORMAT=yyyy-MM-dd \
--env OUTPUT_DIRECTORY_PARENT_PATH=/app/screenshots \
--env PROGRESS_BAR_COMPLETE_CHARACTER='█' \
--env PROGRESS_BAR_ENABLED=true \
--env PROGRESS_BAR_FORMAT=":bar :percent% | :progress/:total | [:elapsed]" \
--env PROGRESS_BAR_INCOMPLETE_CHARACTER='░' \
--env PROGRESS_BAR_LENGTH=35 \
--env REDACTION_TEXT=---- \
--env REDACTION_TYPE=NONE,BLUR,BOX,REMOVE,TEXT \
--env SCREENSHOT_EXISTS_ACTION=CREATE_ANOTHER \
--env TAKE_SCREENSHOT_ON_ERROR=true \
--env TIMEZONE=UTC \
--env TRACKER_EXECUTION_ORDER=HEADLESS,MANUAL \
--env TRACKER_INPUT_FILE_PATH=/app/screenshots/trackers.csv \
-v /tmp/.X11-unix:/tmp/.X11-unix \
-v /tmp/screenshots:/app/screenshots \
-v tracker-chrome-cache:/tmp/chrome-home \
--name tracker-profiles-dev \
--rm tracker-profiles-devAll supported private trackers have an implementation found in the handler package. To add a new one, extend AbstractTrackerHandler.java, following the convention from an existing implementation like AbTorrents.java.
Ensure the TrackerType is set correctly for your tracker.
Rather than using debugging tools, I generally add log statements (and then tune the log level once finished debugging). But if needed, you can also
use IDE debugging tools in IntelliJ as follows. Click on Run> Edit Configurations and add the environment variables for the application.
Once done, open the ApplicationLauncher.java and run the
main method from the IDE.
The AbstractTrackerHandler.java implementation
for each tracker is retrieved by the trackerName field within the CSV file.
Selenium WebDriver is used to leverage the Chromium web browser to
take screenshots. While the application usually runs in headless mode, this can be changed by updating the
FORCE_UI_BROWSER value in the configuration. This will cause a new browser instance to
launch when taking a screenshot, and can be used for debugging a new implementation.
Some of the Manual trackers listed in Trackers> Manual Interaction contain a Cloudflare
verification check. This check cannot be passed simply by opening the browser UI like other manual trackers. Instead,
undetected-chromedriver is used to patch the Google
Chrome binary, making it possibly to successfully check the Cloudflare box (by a user's manual input), bypassing
Cloudflare detection.