Backup FilmAffinity data to CSV and upload ratings to IMDb
Project description
FilmAffinity Backup & IMDb Uploader
Backup your FilmAffinity ratings and lists to CSV, then upload them to IMDb or Letterboxd.
Table of Contents
- Features
- Installation
- Docker
- Quick Start
- FilmAffinity Backup
- IMDb Uploader
- Requirements
- Usage
- Configuration File
- Session Persistence
- Troubleshooting
- License
- Acknowledgments
Note: Forked from Ignacio Heredia/filmaffinity-backup with IMDb upload support and other improvements.
⚠️ Disclaimer: This tool uses web scraping and browser automation techniques. Please use it responsibly:
- Respect the Terms of Service of FilmAffinity and IMDb
- Use reasonable delays between requests (built-in by default)
- Only use this tool for personal, non-commercial purposes
- Excessive or automated access may result in IP blocking or account suspension
- The authors are not responsible for any misuse or consequences arising from using this tool
Features
- Backup FilmAffinity data - Export watched movies and custom lists to CSV
- Export to Letterboxd - Generate Letterboxd-compatible CSV for easy import
- Upload to IMDb - Transfer your ratings to IMDb using Selenium automation
- English title support - Use English version of FilmAffinity for better IMDb matching
- Resume support - Continue interrupted sessions
- Rate limiting - Automatic retry with exponential backoff
Installation
From PyPI
# Basic installation (FilmAffinity backup only)
pip install filmaffinity-backup
# Full installation (includes IMDb uploader and optional extras)
pip install "filmaffinity-backup[all]"
From Conda
conda install -c oyale filmaffinity-backup
From GitHub
# Basic installation (FilmAffinity backup only)
pip install git+https://github.com/oyale/filmaffinity-backup.git
# Full installation (includes IMDb uploader)
pip install "filmaffinity-backup[all] @ git+https://github.com/oyale/filmaffinity-backup.git"
For Development
git clone https://github.com/oyale/filmaffinity-backup.git
cd filmaffinity-backup
# Editable install with all dependencies
pip install -e ".[all]"
Manual Install
pip install -r requirements.txt
Docker
A Dockerfile is provided for containerized usage.
Pull from GitHub Container Registry
Pre-built images are available on GitHub Container Registry:
docker pull ghcr.io/oyale/filmaffinity-backup:latest
Build the Image Locally
If you prefer to build the image yourself:
docker build -t filmaffinity-backup .
Run the Container
Create a local data directory to store CSV files:
mkdir -p data
Run the container using the backup or upload commands:
# Backup FilmAffinity data
# Usage: docker run ... [image_name] backup [arguments]
docker run -it --rm -v "$(pwd)/data":/app/data ghcr.io/oyale/filmaffinity-backup backup $YOUR_USER_ID
# Upload to IMDb
# Usage: docker run ... [image_name] upload [arguments]
docker run -it --rm -v "$(pwd)/data":/app/data ghcr.io/oyale/filmaffinity-backup upload --csv /app/data/$YOUR_USER_ID/watched.csv --auto-rate
Note:
- The
-v "$(pwd)/data":/app/dataflag mounts your localdatafolder to the container.- Important: When referencing files in the upload command, use the container path (
/app/data/...), not your local path.
Project Structure
filmaffinity-backup/
├── filmaffinity/ # FilmAffinity scraper package
│ ├── scraper.py # Web scraping functions
│ └── cli.py # Command-line interface
├── imdb_uploader/ # IMDb uploader package
│ ├── uploader.py # Main upload orchestration
│ ├── browser_automation.py # Selenium WebDriver operations
│ ├── data_processing.py # CSV reading & IMDb matching
│ ├── reporting.py # Output formatting & statistics
│ ├── config.py # Configuration & session management
│ ├── constants.py # Constants and type definitions
│ ├── prompts.py # User interaction prompts
│ ├── csv_validator.py # CSV format validation
│ └── cli.py # Command-line interface
├── tests/ # Unit tests
├── data/ # Downloaded CSV files (per user)
└── pyproject.toml # Python packaging configuration
Quick Start
# Step 1: Backup your FilmAffinity ratings
fa-backup YOUR_USER_ID
# Step 2: Upload to IMDb
fa-upload --csv data/YOUR_USER_ID/watched.csv --auto-rate
Tip: If running without installing, use
python -m filmaffinity.cliandpython -m imdb_uploader.cliinstead.
Part 1: FilmAffinity Backup
Backup your FilmAffinity watched movies and custom lists to CSV files.
Information Saved
- list movies: For each movie in the list, it saves:
movie titleoriginal title(fetched from the movie detail page)movie yearmovie countrymovie directorsuser scoreFilmaffinity scoreFilmaffinity movie id
- watched movies: same as list movies, plus the
movie genre
Usage
To find your user_id, go to your ratings page and copy the ID from the URL:
https://www.filmaffinity.com/en/userratings.php?user_id={YOUR_ID}
# Basic backup (lists + watched)
fa-backup YOUR_USER_ID
# Only backup watched films (skip lists)
fa-backup YOUR_USER_ID --skip-lists
# Use Spanish titles instead of English
fa-backup YOUR_USER_ID --lang es
# Resume an interrupted session
fa-backup YOUR_USER_ID --resume
Your data will be saved to the ./data/{user_id}/ folder.
Command Line Options (fa-backup)
| Option | Description |
|---|---|
--skip-lists |
Skip downloading user lists, only get watched films |
--resume |
Resume an interrupted session, skip already downloaded lists/watched |
--lang |
Language for FilmAffinity (es or en). Default: en |
--data-dir |
Directory to save CSV files (default: ./data) |
--format |
Export format: csv (default), letterboxd, or json |
Letterboxd Export
Export your FilmAffinity ratings to Letterboxd-compatible CSV format:
# Backup with Letterboxd export
fa-backup YOUR_USER_ID --format letterboxd
This creates additional *_letterboxd.csv files alongside the standard CSV files. These can be directly imported into Letterboxd at https://letterboxd.com/import/.
The Letterboxd CSV includes:
- Title - Original title (English) when available, otherwise local title
- Year - Release year
- Rating10 - Your rating on 1-10 scale
- WatchedDate - Left empty (FilmAffinity doesn't track this)
JSON Export
Export your FilmAffinity data to structured JSON format:
# Backup with JSON export
fa-backup YOUR_USER_ID --format json
This creates *.json files with structured data containing all film information as an array of objects. Each film object includes all available metadata (title, year, rating, director, etc.).
Example JSON structure:
[
{
"title": "The Shawshank Redemption",
"original_title": "The Shawshank Redemption",
"year": "1994",
"score": "9.3",
"director": "Frank Darabont"
}
]
Language Option (--lang)
By default, the script scrapes FilmAffinity's English version (/en/). Using --lang es switches to the Spanish version. English provides:
- English/International titles that match better with IMDb
- Fewer HTTP requests - no need to fetch original titles separately
- Faster execution - skips the per-movie detail page requests
Note: English is the default since it provides better IMDb matching. Spanish mode (--lang es) requires extra requests to fetch original titles, which is slower and more likely to trigger rate limiting.
Rate Limiting
The script intentionally waits 5s between each parsing request to avoid getting the IP blocked by the FilmAffinity server. If a 429 (Too Many Requests) error is encountered, the script will automatically retry with exponential backoff (30s → 60s → 120s).
Part 2: IMDb Uploader
Upload your FilmAffinity ratings to IMDb using Selenium automation. Supports dry-run mode for verifying mappings before making any changes.
Recommended Workflow
# Step 1: Backup FilmAffinity ratings
fa-backup YOUR_USER_ID --skip-lists
# Step 2: Dry-run to verify IMDb mappings
fa-upload --csv data/YOUR_USER_ID/watched.csv --dry-run
# Step 3: Upload ratings
export IMDB_USERNAME="your_email"
export IMDB_PASSWORD="your_password"
fa-upload --csv data/YOUR_USER_ID/watched.csv --auto-login --auto-rate
Uploader Features
- Dry-run mode: Maps FilmAffinity titles to IMDb IDs without making any changes on IMDb.
- Automated rating: Uses Selenium to log in to IMDb and rate movies based on the CSV data.
- Fuzzy matching: Matches titles using fuzzy logic, with boosts for matching years and directors.
- English title support: Works best with CSVs generated using
--lang en(titles already in English). - Original title fallback: When using Spanish CSVs, can use the
original titlecolumn for better IMDb matching. - Existing rating detection: Detects if a movie is already rated on IMDb and prompts to skip or overwrite.
- Same rating skip: Automatically skips movies that are already rated with the same score on IMDb.
- Manual IMDb ID entry: When no match is found, manually enter an IMDb ID or URL.
- Unattended mode: Batch processing without user interaction.
- Skipped movies export: Saves skipped movies to separate CSV files by category for selective re-processing.
- Retry by category: Re-run only specific categories of skipped movies (ambiguous, not found, already rated, etc.).
- CAPTCHA detection: Detects CAPTCHA challenges during login and prompts user to solve them.
- Rate limiting: Automatic retry with exponential backoff on HTTP errors.
- Config file support: Save your settings to a JSON config file for easy reuse.
- Session persistence: Resume interrupted uploads from where you left off.
Requirements
- Python 3.9+
- Selenium and webdriver-manager
- Cinemagoer (IMDbPY fork)
Install with:
pip install -e ".[imdb]"
Or manually:
pip install selenium webdriver-manager cinemagoer
Uploader Usage
Dry-run Mode
Verify mappings between FilmAffinity titles and IMDb IDs:
fa-upload --csv data/YOUR_USER_ID/watched.csv --dry-run --dry-run-output imdb_matches.csv
This generates a CSV file (imdb_matches.csv) with the following columns:
local_title: Title from the FilmAffinity CSVlocal_year: Year from the FilmAffinity CSVlocal_director: Director(s) from the FilmAffinity CSVimdb_id: Matched IMDb IDimdb_title: Matched IMDb titleimdb_year: Matched IMDb yearscore: Confidence score of the matchquery: Search query usedresult_count: Number of results returned by IMDb
Automated Rating
To automatically rate movies on IMDb:
-
Set your IMDb credentials as environment variables:
export IMDB_USERNAME="your_username" export IMDB_PASSWORD="your_password"
-
Run the script with the
--auto-rateflag:fa-upload --csv data/YOUR_USER_ID/watched.csv --auto-login --auto-rate
Unattended Mode (Batch Processing)
For fully automated batch processing without any user prompts:
fa-upload --csv data/YOUR_USER_ID/watched.csv --auto-login --auto-rate --unattended
In unattended mode:
- Ambiguous matches (title/year mismatch) are automatically skipped
- Already-rated movies are automatically skipped
- Failed auto-ratings are skipped (no manual fallback)
- All skipped items are saved to a CSV file for later review
No-Overwrite Mode
To only add new ratings without overwriting existing ones, but still handle ambiguous matches interactively:
fa-upload --csv data/YOUR_USER_ID/watched.csv --auto-login --auto-rate --no-overwrite
This is useful when you want to:
- Skip movies you've already rated on IMDb
- But still manually resolve title/year mismatches when the match is ambiguous
# Retry already-rated movies (to update ratings)
fa-upload --retry already_rated --auto-login --auto-rate
Handling Ambiguous Matches
When a movie match is ambiguous (title or year doesn't match exactly), the script shows a selection dialog:
======================================================================
🔍 AMBIGUOUS MATCH - Please select the correct movie
======================================================================
CSV Data (from FilmAffinity):
Title: El secreto de sus ojos
Year: 2009
Director: Juan José Campanella
----------------------------------------------------------------------
IMDb Candidates:
----------------------------------------------------------------------
[1] The Secret in Their Eyes (2009) - Juan José Campanella
IMDb ID: tt1305806 | Confidence: 85.3%
[2] Secret in Their Eyes (2015) - Billy Ray
IMDb ID: tt1741273 | Confidence: 72.1%
----------------------------------------------------------------------
Enter 1-2 to select, [M]anual IMDb ID, [S]kip, or [Q]uit
Re-processing Skipped Movies
After a run, skipped movies are saved to separate CSV files in the skipped/ directory, organized by category:
skipped_ambiguous.csv- Movies with ambiguous IMDb matchesskipped_not_found.csv- Movies not found on IMDbskipped_already_rated.csv- Movies already rated on IMDb (different score)skipped_same_rating.csv- Movies already rated with the same score (no action needed)skipped_auto_rate_failed.csv- Movies where auto-rating failedskipped_user_choice.csv- Movies manually skipped by userskipped_all.csv- Combined file with all skipped movies
Use the --retry option to re-process specific categories:
# Retry all skipped movies
fa-upload --retry all --auto-login --auto-rate
# Retry only ambiguous matches (with manual selection)
fa-upload --retry ambiguous --auto-login --auto-rate
# Retry movies that weren't found (maybe IMDb added them since)
fa-upload --retry not_found --auto-login --auto-rate
# Retry already-rated movies (to update ratings)
fa-upload --retry already_rated --auto-login --auto-rate
# Use a custom skipped directory
fa-upload --retry all --skipped-dir my_skipped/ --auto-login --auto-rate
Command Line Options (fa-upload)
| Option | Description |
|---|---|
--csv |
Path to the FilmAffinity CSV file (required unless using --retry or --resume) |
--dry-run |
Only map titles to IMDb IDs, don't rate anything |
--dry-run-output |
Output path for dry-run CSV (default: imdb_matches.csv) |
--auto-login |
Try automated login using IMDB_USERNAME/IMDB_PASSWORD env vars |
--auto-rate |
Automatically click rating stars (best-effort) |
--headless |
Run browser in headless mode (no UI) |
--no-overwrite |
Never overwrite existing IMDb ratings (auto-skip already rated) |
--unattended |
Run without user interaction, skip ambiguous matches and existing ratings |
--skipped-dir |
Output directory for skipped CSV files by category (default: skipped/) |
--retry |
Re-run using skipped movies: all, ambiguous, not_found, already_rated, auto_rate_failed, user_skipped |
--start |
Start processing from a specific index in the CSV |
--limit |
Limit the number of items processed |
--confirm-threshold |
Confidence threshold for low-confidence warnings (default: 0.75) |
--no-confirm |
Skip all confirmation prompts (use with caution) |
--debug |
Enable debug output for troubleshooting |
--config |
Path to JSON config file (searches upload_imdb.json, ~/.config/upload_imdb/config.json by default) |
--save-config PATH |
Save current options to a config file and exit |
--show-config |
Show current configuration and exit |
--resume |
Resume previous interrupted session |
--clear-session |
Clear saved session and start fresh |
--session-file |
Path to session file (default: .upload_imdb_session.json) |
Configuration File
You can save your frequently used options to a JSON config file:
# Save current options to config file
fa-upload --csv data/YOUR_USER_ID/watched.csv --auto-login --auto-rate --save-config upload_imdb.json
# Show current configuration
fa-upload --show-config
# Use a specific config file
fa-upload --csv data/YOUR_USER_ID/watched.csv --config my_config.json
Example config file (upload_imdb.json):
{
"headless": false,
"auto_login": true,
"auto_rate": true,
"confirm_threshold": 0.75,
"no_overwrite": false,
"skipped_dir": "skipped",
"max_retries": 3,
"page_load_wait": 2.0,
"element_wait": 0.5,
"login_wait": 2.0,
"captcha_wait": 5.0,
"rating_wait": 1.0
}
The script searches for config files in this order:
- Path specified with
--config upload_imdb.json(current directory)~/.config/upload_imdb/config.json~/.upload_imdb.json
Environment Variables
Timing settings can also be configured using environment variables, which take precedence over config file settings:
export PAGE_LOAD_WAIT=3.0
export ELEMENT_WAIT=1.0
export LOGIN_WAIT=5.0
export CAPTCHA_WAIT=10.0
export RATING_WAIT=2.0
export SEARCH_WAIT=1.0
Environment variables are useful for:
- CI/CD pipelines
- Docker containers
- Temporary overrides without modifying config files
Session Persistence
The script automatically saves progress, allowing you to resume interrupted sessions:
# Start a new session
fa-upload --csv data/YOUR_USER_ID/watched.csv --auto-login --auto-rate
# If interrupted, resume from where you left off
fa-upload --resume
# Clear session and start fresh
fa-upload --csv data/YOUR_USER_ID/watched.csv --clear-session
Session state includes:
- Current position in the CSV
- Statistics (applied, skipped counts)
- List of processed movies
Notes
- Dry-run recommended: Always run the script in dry-run mode first to verify mappings.
- IMDb login: The script supports both manual and automated login. Automated login may fail if IMDb changes its login flow.
- Terms of Service: Automated interactions with IMDb may violate their terms of service. Use responsibly.
Troubleshooting
-
Cinemagoer not installed: If
cinemagoer(IMDbPY fork) is not installed, the script will skip lookups in dry-run mode. Install it using:pip install cinemagoer
-
WebDriver issues: Ensure your browser and WebDriver versions are compatible. The script uses webdriver-manager to auto-download the correct driver.
License
This project is licensed under the AGPL-3.0-or-later License. See the LICENSE file for details.
Acknowledgments
- Original FilmAffinity backup tool by Ignacio Heredia
- Cinemagoer (IMDbPY fork) for IMDb data access
- Selenium for browser automation
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file filmaffinity_backup-1.2.1.tar.gz.
File metadata
- Download URL: filmaffinity_backup-1.2.1.tar.gz
- Upload date:
- Size: 97.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
88ad6b631f80eba62ba46486628df40500710b00dd4d3fea0d74ecf3441271f5
|
|
| MD5 |
9a8c851088fa0b176ebde1c19c963dae
|
|
| BLAKE2b-256 |
636cd87ab8e49d112ecbd8801bca4fca3b782b3288e9191947e91c88d588d4b1
|
Provenance
The following attestation bundles were made for filmaffinity_backup-1.2.1.tar.gz:
Publisher:
publish.yml on oyale/filmaffinity-backup
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
filmaffinity_backup-1.2.1.tar.gz -
Subject digest:
88ad6b631f80eba62ba46486628df40500710b00dd4d3fea0d74ecf3441271f5 - Sigstore transparency entry: 764143319
- Sigstore integration time:
-
Permalink:
oyale/filmaffinity-backup@e156cec99bb7159e74e881d4135e2045606531cc -
Branch / Tag:
refs/tags/v1.2.1 - Owner: https://github.com/oyale
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@e156cec99bb7159e74e881d4135e2045606531cc -
Trigger Event:
release
-
Statement type:
File details
Details for the file filmaffinity_backup-1.2.1-py3-none-any.whl.
File metadata
- Download URL: filmaffinity_backup-1.2.1-py3-none-any.whl
- Upload date:
- Size: 68.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ea336f97e47cc3ceb4c90490f862da6c6383a8c825129b202bdab2f3a0748b62
|
|
| MD5 |
197306d27e23bcb8cd5595a2f604b2d6
|
|
| BLAKE2b-256 |
39cc3f6f57a6ab8a8dad1c230f8dda9e2a8580c741149180864d9bd16322ac05
|
Provenance
The following attestation bundles were made for filmaffinity_backup-1.2.1-py3-none-any.whl:
Publisher:
publish.yml on oyale/filmaffinity-backup
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
filmaffinity_backup-1.2.1-py3-none-any.whl -
Subject digest:
ea336f97e47cc3ceb4c90490f862da6c6383a8c825129b202bdab2f3a0748b62 - Sigstore transparency entry: 764143321
- Sigstore integration time:
-
Permalink:
oyale/filmaffinity-backup@e156cec99bb7159e74e881d4135e2045606531cc -
Branch / Tag:
refs/tags/v1.2.1 - Owner: https://github.com/oyale
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@e156cec99bb7159e74e881d4135e2045606531cc -
Trigger Event:
release
-
Statement type: