PTtool hardlinks large files so a media server can rename them without copying
自动化硬链接工具,在最大可能情况下节约空间,并保持做种。 小于1M的文件直接复制,刮削修改nfo等小文件不影响做种。 大于1M的文件硬链接到目的目录,可以随意修改文件名。
At a glance
- What is it?
- PTtool is a shell toolset for private tracker users who want their download folder and their media library to share storage without doubling it. Files below a size threshold are copied so scraping tools can edit them, larger files are hardlinked so they can be renamed and moved freely, and the marker-file design makes the linking resumable.
- Who is it for?
- Adopt PTtool if you run a private tracker seeding setup alongside a media server and want the library folder to share storage with the download folder, since the hardlink approach gives you free renames without a second copy of every video. Skip it if the two folders sit on different filesystems, because hardlinks cannot cross a partition boundary, as the project's own notes state.
- Can I use it commercially?
- Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
- Is it still maintained?
- Yes. The repository last received commits 125 days ago.
- What is it written in?
- Mainly Shell, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem is that scraping edits files you are seeding
PTtool is described as an automated hardlink tool that saves space in the largest possible way while keeping the seeding intact, and the reasoning is explained in the design section.
Files smaller than 1MB are copied directly, so tools like Emby and tmm can scrape and modify the nfo and other small files without affecting the seeding. Files larger than 1MB are hardlinked into the destination directory, and you can rename those freely but not change their content.
That is the whole idea, and it comes from a specific conflict. When tmm or Emby scrapes, it will definitely modify the nfo files, and the downloaded cover images differ from site to site, so small files are copied and are safe to modify. The large files, which are the actual video, are hardlinked, so they occupy only one copy of the storage.
The result is described as both seeding and Emby working at once, with two file entries pointing at one piece of storage.
The canonical layout is a download folder and a destination folder side by side. The source directory is where the downloaded PT files are saved, and the destination is where your processed video files live, with the Emby and tmm directories configured under the destination. The source holds the files qBittorrent downloads into subdirectories such as tv, anime, movie, 4k and soft, and the destination is where the scraping tools point.
The project is Shell as its primary language, GPL-3.0 licensed, and the last push was on 2026-06-20 with no GitHub releases.
The hardlink rules, including the one that bites
The notes section after the design goals is where the constraints are, and they are the part to read before touching anything.
The first is a filesystem limit. The source and destination directories have to be on the same disk partition, because hardlinks cannot cross partitions. That is not a limitation of the tool but of the operating system, and it is the constraint that decides whether the whole approach works for you. A download folder on one array and a library folder on another cannot be hardlinked.
The second is about how you move files afterwards. A hardlinked file can be moved with `mv` to change its storage location without breaking the hardlink effect, but `cp` will add another copy of the storage. So for files that have been hardlinked, use `mv` and not `cp`.
The third is the semantic of hardlinks themselves, and it is worth understanding rather than memorising. A hardlinked file exists in several places at once, but they all point at the same storage. Only when all of the hardlinks have been deleted is the file actually removed by the system. And all the hardlinked files share data, so modifying one modifies every hardlink pointing at that location.
That last sentence is the practical risk, and it is why the size threshold exists. A video file hardlinked into your library and your seed folder is one file on disk, and if your media server or a script modifies it in place, both copies change. For nfo files and cover images, that would break your seeding and your scrape, so those are copied instead.
So the design is a deliberate division: things that get edited get copied, things that only get renamed get linked.
Three scripts for three different jobs
The toolset is three scripts, and they differ in how much they check and how often they run.
`mklink.sh` is the simplest. You modify the source and destination directories in the script parameters, replace them with your own, and run it, and it hardlinks all the files over 1MB from the source into the destination while copying the ones under 1MB. The invocation form is given as a comment and a call:
# mklink.sh sourcedir dstdir
mklink.sh /share/Download/tmp/src /share/Download/tmp/dstThe important caveat about mklink.sh is that it operates directly on the two folders and performs no idempotency check. It suits directories that are brand new and have never been hardlinked before.
`dirlink.sh` is the resumable one, and the design principle is stated precisely. For each subdirectory under the source, it checks whether an `islinked.lk` marker file exists. If it does, that subdirectory is skipped, and if it does not, the subdirectory is hardlinked into the destination and a marker file is created.
The same size rule applies, files under 1MB are copied and files over 1MB are hardlinked. You can modify the directories inside the script or pass them as the first and second command line arguments.
# dirlink.sh sourcedir dstdir
dirlink.sh /share/Download/tmp/src /share/Download/tmp/dst`autolink.sh` is the third, and the name plus the marker mechanism suggests it is the continuous or scheduled variant, running the same linking logic without you invoking it by hand.
The marker file is what makes dirlink.sh safe to run repeatedly against a live download folder, since a directory that has already been linked is simply left alone rather than being linked a second time.
The layout has to be right for the marker files to work
There is a structural requirement for dirlink.sh that is easy to miss and produces confusing failures.
The note says the files under the source directory need to be placed into individual subdirectories, for example `src/anime/anime1` and `src/tv/tv2`, in order for `islinked.lk` to work correctly.
So the hierarchy is not just organisational, it is functional. The script iterates over the immediate children of the source and treats each as a unit, so if you dump files directly into the source root the script has no directory to attach a marker to and the intended unit of work does not exist.
That aligns with how qBittorrent is configured, and the README walks through it. Download into a resource directory, with qBittorrent's categories pointing at subdirectories such as tv, anime, movie, 4k and soft. In the qBittorrent web interface, move the torrent save location by right-clicking a torrent and choosing the save location from the menu, and set the category directory by right-clicking, choosing category, then new category and filling in the name and path. For multi-file torrents, choose automatic management when adding, and for single-file torrents either add a subfolder yourself or force the client to create one.
That last instruction is the reason the earlier note about subdirectories exists. A single-file torrent saved flat into the source root would break the dirlink unit, so the client is told to wrap it in a folder instead.
After that, the sequence is to run the hardlink script to link the files into the destination, and point tmm and Emby at the destination for scraping.
The README also mentions a rebuild or recreation step under a heading about rebuilding, but the explanation is incomplete, so it is worth checking the documentation for the exact command if you need to start the linking over.
Script output language and the wider toolset
Two smaller things round out the project, and one of them tells you something about the audience.
The first is script output language. The output language of the scripts is controlled by the environment variable `SCRIPT_LANG`, which by default follows the system `LANG`:
# 英文输出
SCRIPT_LANG=en_US ./dirlink.sh /src /dst
# 中文输出(默认)
./dirlink.sh /src /dstThe rename_episodes.py script supports the same environment variable, so the Python helper and the shell scripts speak the same language setting.
The second is the size of the surrounding ecosystem, and this is where the project is really a directory rather than a single tool. The README lists around thirty related projects, and the ones closest to this tool are the seeding and cross-seeding helpers, with IYUUAutoReseed, PT-Plugin-Plus and pt_helper named as the PT three musketeers, one of which is described as not open source.
There are also download and flow tools, with a Flexget plugin for NexusPHP, a browser-based cross-seeding assistant, a one-click re-upload script, an automatic sign-in project, a torrent poster wall generator, and qBittorrent RSS rule management. Then the media server and arrangement side, with Emby external URL playback, subtitle subsetting for MKV files, subtitle translation, and a batch of Chinese-language automatic anime following solutions built on the Mikan Project.
And a client for the tracker sites themselves, pt_mate, based on Flutter with Material Design 3, supporting M-Team and NexusPHP.
The README is bilingual by default and ships English, Japanese, Korean, Spanish, German, French and Traditional Chinese versions alongside the Simplified Chinese original.
The project is being rebuilt by an agent
The most unusual thing about this repository is stated at the top, and it is worth reading carefully because it changes how you should judge the code.
The project was fully reconstructed with opencode, implementing an AI Agent driven development and testing loop where the requirements lead to architecture, then to coding, then to Docker testing, then to experience being written back into the repository, with all of it done by the agent autonomously. The AGENTS.md file and the docs/ directory are where the detail lives.
So the project has been through a rewrite in which an AI agent did the requirements, the architecture, the implementation, the Docker-based testing and the capture of what was learned. That is a genuinely different provenance from a hand-written shell script collection, and for a reader it has two implications.
The first is that the AGENTS.md and docs/ files are the primary documentation, more so than the README, because they are where the agent recorded its process. The README points you at them for the detail.
The second is that the script behaviour described in the README is the specification the agent worked to, and the CI badge shows there is a test workflow that runs the scripts. For a tool that hardlinks files, a test harness matters more than usual, because the failure mode is data loss or a broken seeding ratio rather than a crash.
The rest of the repository is conventional for a shell toolset. There are the scripts themselves, the Python helper rename_episodes.py, a tests/ directory, a .github/ directory with the CI workflow, a renovate.json for dependency updates, a CHANGELOG.md, an AGENTS.md and the README in seven languages.
The practical caveat is the one that applies to any tool in this corner of the disk-hacking world. It operates on your actual media and download folders, it creates hardlinks whose contents are shared, and it assumes you understand the difference between a copy and a link. Read the notes section first, and run it on a directory you can afford to be wrong about.
Editorial conclusion
Adopt PTtool if you run a private tracker seeding setup alongside a media server and want the library folder to share storage with the download folder, since the hardlink approach gives you free renames without a second copy of every video. Skip it if the two folders sit on different filesystems, because hardlinks cannot cross a partition boundary, as the project's own notes state. Verify first that your source and destination are on the same partition, that you understand the editing rule, since a hardlinked file shares its content and an edit to one changes the other, and which of the three scripts fits your workflow, since mklink.sh does no idempotency check while dirlink.sh is resumable and autolink.sh is the watcher.
Frequently asked questions
How do I use PTtool to link my download folder to my media library?
Set the source and destination directories in the script, for example `SRC="/share/Download/src"` and `DST="/share/Download/dst"`, then run the script so that files over 1MB are hardlinked into the destination and files under 1MB are copied. `mklink.sh` takes the two directories as arguments and performs no idempotency check, while `dirlink.sh` checks each subdirectory for an `islinked.lk` marker file and skips the ones already linked.
What are the limits of hardlinking in PTtool?
The source and destination must be on the same disk partition, because hardlinks cannot cross partitions. Hardlinked files share their content, so modifying one changes all of them, which is why files over 1MB are linked and must not be edited in place, while small files such as nfo are copied. For moving a hardlinked file use `mv`, not `cp`, since `cp` adds another copy of the storage.
Why does dirlink.sh need files in subdirectories?
The script iterates over the subdirectories of the source and treats each one as the unit of work, attaching an `islinked.lk` marker file to it to record that it has been linked. Files need to sit in their own subdirectories, such as `src/anime/anime1`, for the marker to work correctly. That is also why qBittorrent is configured to create a subfolder for single-file torrents.
Official sources
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.
[](https://hysenlabs.com/projects/appotry-pttool)Was this useful?