A robust solution for automatically processing downloaded media files from Transmission, organizing them with FileBot, and triggering Plex library updates. This project evolved from a simple cleanup script into a comprehensive media management solution.
- macOS (tested on Monterey and later)
- Plex Media Server (running locally)
- FileBot (licensed version)
- Transmission (for downloading)
- Homebrew (for installing dependencies)
- Command line tools:
- yq (
brew install yq) - xmlstarlet (
brew install xmlstarlet) - curl (usually pre-installed on macOS)
- yq (
Note: The installation wizard (
install.sh) can automatically install missing dependencies with your permission.
-
Clone this repository or download the scripts to your preferred location:
git clone <repository-url> cd transmission-filebot
-
Run the installation wizard:
./install.sh
The wizard will:
- Check for required dependencies and offer to install them
- Validate your Plex server is reachable
- Guide you through obtaining a Plex authentication token
- Display your Plex library sections to help choose the media path
- Generate a complete configuration file at
~/.config/transmission-done/config.yml - Create a symlink at
~/.local/bin/transmission-done - Back up any existing configuration with a timestamp
That's it! The script is now installed and ready to use.
The setup wizard creates ~/.config/transmission-done/config.yml automatically. If you need to modify it later:
paths:
default_home: /Users/yourusername
plex:
server: http://localhost:32400
token: your_plex_token_here
media_path: /path/to/your/plex/media
logging:
file: .filebot/logs/transmission-processing.log
max_size: 10485760 # 10MB in bytesConfigure Transmission to run the script automatically:
- Open Transmission
- Go to Preferences (⌘,)
- Navigate to the "Downloading" tab
- Check "Run script when download completes"
- Enter:
~/.local/bin/transmission-done(or the full path shown during installation)
When Transmission completes a download:
- The script validates the environment and configuration
- Cleans up unwanted files (NFO, TXT, EXE)
- Uses FileBot to:
- Identify the media (TV show or movie)
- Rename files according to Plex conventions
- Move files to the appropriate Plex library folder
- Download artwork and metadata
- Import subtitles
- Triggers a Plex library scan
- Logs all actions for troubleshooting
The script supports both automated (Transmission) and manual invocation modes.
Double-click process-media.command or run from terminal:
./process-media.commandThe script will:
- Present a native macOS folder picker dialog
- Process all media files in the selected folder
- Show preview and ask for confirmation
- Display macOS notifications on completion
Create MediaProcessor.app using Automator (see CREATE_AUTOMATOR_APP.md):
- Drag a folder containing media files onto the app
- The app processes the files automatically
- Notifications indicate success or failure
- Safety Checks: Verifies files are complete (not being downloaded)
- Preview: Shows what changes will be made before processing
- Confirmation: Asks for approval before moving files
- Notifications: macOS notifications with success/failure sounds
- No Transmission Required: Works independently of Transmission
If your Plex media library lives on a NAS (e.g., Synology) mounted via NFS, the NFS export settings must be configured correctly for FileBot to move files.
In Control Panel → Shared Folder → [your share] → NFS Permissions, create or edit the NFS rule:
| Setting | Value | Notes |
|---|---|---|
| Hostname or IP | Your client's IP or subnet (e.g., 10.0.12.0/22) |
Restrict to your LAN |
| Privilege | Read/Write | Required for FileBot to move files |
| Squash | Map all users to admin | See explanation below |
| Security | sys | Standard UNIX authentication |
| Enable asynchronous | Yes | Better performance |
| Allow non-privileged ports | Yes | Required for macOS clients |
| Allow subfolder access | Yes | Media lives in subdirectories |
NFS permission checks happen on the NAS, not on your Mac. The macOS noowners mount option only affects how files appear locally — the NAS still enforces UNIX permissions based on the user ID (UID) it receives with each request.
The problem: macOS user UIDs (501, 502, etc.) don't match Synology user UIDs (admin = 1024). With "No mapping", the NAS receives your macOS UID and can't match it to any NAS user — so writes are denied even though local permissions look fine.
"Map all users to admin" solves this by remapping all NFS requests to the Synology admin user, which has full access to the shared folder.
With "Map all users to admin", directories should be rwxrwxrwx (777). This is safe for a media server on a firewalled LAN.
If directories lose write permissions (FileBot will report Access Denied), fix from the NAS:
# SSH into your NAS (requires sudo even for admin):
ssh admin@your-nas
sudo find /volume1/YourShare/Media/ -type d -exec chmod 777 {} +Important:
chmodfrom the macOS NFS client will fail with "Operation not permitted". Always fix permissions directly on the NAS via SSH.
Mount the NFS share with noowners:
sudo mount -t nfs -o noowners your-nas.local:/volume1/YourShare /path/to/mountAfter changing permissions on the NAS, you may need to remount to clear the NFS attribute cache:
sudo diskutil unmount force /path/to/mount
sudo mount -t nfs -o noowners your-nas.local:/volume1/YourShare /path/to/mountIf Transmission runs in a container (e.g., Docker/podman with the haugene image), it cannot directly invoke macOS scripts. The transmission-trigger-watcher.sh daemon bridges this:
- The container writes a trigger file when a download completes
- The watcher daemon (running on the host) polls for triggers every 60 seconds
- It maps container paths to macOS NFS mount paths and invokes
transmission-done.sh - Failed triggers are retried up to 5 times, then moved to
.deadfor manual inspection
When Transmission runs the script, it does so with a limited environment that differs from your regular shell environment. The script receives:
-
From Transmission:
TR_TORRENT_DIR: Directory containing the downloaded filesTR_TORRENT_NAME: Name of the downloaded torrentTR_APP_VERSION: Transmission versionTR_TIME_LOCALTIME: Local time of completionTR_TORRENT_HASH: Torrent hashTR_TORRENT_ID: Internal Transmission torrent ID
-
System Environment:
- Limited
PATH(usually just/usr/bin:/bin:/usr/sbin:/sbin) - No user environment variables (like those set in
.zshrcor.bash_profile) - No
HOMEvariable by default
- Limited
This is why the script:
- Explicitly sets
PATHto include/usr/local/bin - Uses a configurable
default_homein config.yml - Sources all paths from the config file rather than environment variables
If you need to debug environment issues:
# Add this near the start of the script
env > /tmp/transmission-env.log-
Script not running after download
- Check Transmission's script path setting
- Verify script permissions (
chmod +x) - Check the log file for errors
-
FileBot errors
- Ensure FileBot is properly licensed
- Verify FileBot is in your PATH
- Check if the target media directory exists and is writable
-
FileBot reports "Access Denied" or files not moving
- This is almost always a NAS/NFS permission issue, not a FileBot problem
- Check that the target directory is writable (see NAS Storage Setup)
- FileBot logs both successful and failed moves with
[MOVE]— check the log forfailed due to I/O error
-
Plex not updating
- Verify your Plex token is correct
- Ensure Plex server is running
- Check server URL in config.yml
- Look for connection errors in the logs
-
Check the logs:
tail -f ~/.filebot/logs/transmission-processing.log -
Run the test suite:
./run_tests.sh
-
Verify Plex connectivity:
curl -H "X-Plex-Token: your_token" http://localhost:32400/identity
This project uses BATS (Bash Automated Testing System) for comprehensive testing.
Install BATS:
brew install bats-coreRun all tests:
./run_tests.shRun only unit tests:
bats test/unit/*.batsRun only integration tests:
bats test/integration/*.batsRun a specific test file:
bats test/unit/test_mode_detection.batsRun tests with verbose output:
bats -t test/unit/test_mode_detection.batsTest individual functions in isolation:
test_mode_detection.bats- Invocation mode detection (automated vs manual)test_type_detection.bats- Media type heuristics (TV vs movie)test_plex_api.bats- Plex API functions and library scanstest_filebot.bats- FileBot processing and fallback chainstest_error_logging.bats- Error analysis and reportingtest_file_safety.bats- File readiness and safety checks
Test complete end-to-end workflows:
test_tv_workflow.bats- Complete TV show processing workflowtest_movie_workflow.bats- Complete movie processing workflowtest_manual_mode.bats- Manual invocation mode (user-initiated)
- 114 comprehensive tests covering all major functionality
- 84 unit tests verify individual components in isolation
- 30 integration tests verify complete end-to-end workflows
- All tests run in TEST_MODE to avoid side effects
- Comprehensive coverage of error conditions and edge cases
- Mock implementations for FileBot and Plex API calls
- Test helpers for common assertions and setup
Tests follow BATS conventions:
@test "description of what is being tested" {
# Setup
export TEST_MODE=true
local test_dir="${TEST_TEMP_DIR}/test"
mkdir -p "${test_dir}"
# Execute
run function_to_test "${test_dir}"
# Assert
assert_success
assert_equal "expected" "${output}"
}See existing tests in test/unit/ and test/integration/ for more examples.
This project began as a simple "done-cleanup" script that removed unwanted files after Transmission downloads completed. Key evolution points:
- Initial Version: Basic cleanup of NFO, TXT, and EXE files
- FileBot Integration: Added automated media organization and renaming
- Plex Integration: Implemented automatic library updates
- Robust Error Handling: Added retry logic and comprehensive logging
- Configuration Management: Moved from hardcoded values to YAML configuration
- Test Suite (2026-01): Comprehensive BATS test infrastructure with 114 tests
- 84 unit tests for individual functions
- 30 integration tests for complete workflows
- Continuous integration with GitHub Actions
- Unified Installer (2026-01): One-command installation with
install.sh- Automatic dependency installation with user consent
- Plex server validation and token configuration
- Complete config file generation
- Library introspection with proper XML parsing
- Automatic symlink creation to
~/.local/bin
The current version is a comprehensive media automation solution with proper error handling, logging, configuration management, and extensive testing capabilities.
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
Please ensure tests pass before submitting: ./run_tests.sh
MIT License - See LICENSE file for details