Repository navigation
Releases
3.17.1
What's Changed
- build(python): drop the upper bound on requires-python by @cloakedkeys in #533
- fix(session): start the rate-limit backoff when a minimum gap is set by @cloakedkeys in #535
- fix(fansly): fetch a new link when the CDN rejects a signed one by @cloakedkeys in #537
Full Changelog: 3.17.0...3.17.1
3.17.0
A lot of bug fixes went into this release. The codebase was refactored to make it easier to maintain, and the check table got some quality-of-life features.
Check table
- Size limits are a number plus a unit (Bytes / KB / MB / GB). #514, #522, #527
- Right-click copies a username or post id; right-click the Media ID or Post ID box to paste. #529
- The username filter matches part of a name. #526
- Clear Filters empties the username box. #525
- The length boxes show which is hours, minutes and seconds. #518, #528
- Enter on the Number cell adds or removes the row. #515
- A table with several creators no longer crashes. #519
- A deleted or renamed creator is skipped instead of stopping the check. #520
Creator picker and prompts
- Search matches the typed text in the username only. #531
- Ctrl+A selects everything shown. #530
- Ctrl+D clears the selection; an empty search no longer crashes on Down. #513
- "Yes Update Selection" opens the picker with nothing ticked. #511
- Esc keeps the creators you ticked. #524
- Help notes that Ctrl+V pastes in Windows Terminal; use Alt+V there. #512
- Clearer full-rescan hint. #517
Config
- The config menu takes sizes and download speed as a number plus a unit. #516
- Unusual
config.jsoncontents no longer crash or lose saved values. #503, #508
Scans, labels, database
- A scan that lost a page is rescanned next run instead of skipping posts. #501
- Label names are decoded (
&becomes&);{label}folders use the decoded name. #523 - Only busy-database errors are retried. #506
Packaging
ofscraperon PyPI is this code again. #493, #494- Docker images are tagged with the release number. #495
Maintenance
- Unused dependencies removed. #496, #500
- Ruff lint in CI. #497
- Refactors and test fixes. #498, #499, #502, #504, #505, #507, #509, #510
Upgrade: uv tool upgrade ofscraper-community, pipx upgrade ofscraper-community or pip install -U ofscraper-community.
Full list: 3.16.13...3.17.0
3.16.13
Maintenance
- Dependencies: python-dateutil 4.0.0 (the pin now allows 4.x;
sixis replaced bytzdata), the astral-sh/uv 0.13.8 Docker base image, and random-unicode-emoji 3.0.1 in the test extras. #491
Upgrade: uv tool upgrade ofscraper-community, pipx upgrade ofscraper-community or pip install -U ofscraper-community.
Full list: 3.16.12...3.16.13
3.16.12
Downloads
- Stories, highlights and DRM videos keep going after an IP change too: 3.16.11 fetched a new link when a signed download link was rejected with a 403, but only for the normal downloader. Stories and highlights are now fetched again by id, using a 5-minute per-model cache so a batch of rejected files doesn't send one request each. DRM videos get a new CloudFront signature, used only if the post still points at the same
.mpd. The download then resumes. #490
Upgrade: uv tool upgrade ofscraper-community, pipx upgrade ofscraper-community or pip install -U ofscraper-community.
Full list: 3.16.11...3.16.12
3.16.11
Downloads
- Downloads keep going after an IP change: OnlyFans download links are signed for the IP that fetched them, so after a reconnect or VPN switch every link fetched before it answered 403 and every remaining download in the run failed. When a link is rejected that way, the normal downloader now fetches the post again, takes the new link for the same file, and tries once more, resuming the
.part. This covers timeline, pinned, archived, streams, messages and purchases. Stories, highlights and DRM videos fail as before. #489, ported from datawhores/OF-Scraper#653 by @danijel1124
Upgrade: uv tool upgrade ofscraper-community, pipx upgrade ofscraper-community or pip install -U ofscraper-community.
Full list: 3.16.10...3.16.11
3.16.10
Fansly streams
- Stream segments download in parallel: Fansly HLS stream videos are fetched several segments at a time through the normal download session, then joined by ffmpeg without re-encoding. It picks the same quality as before.
FANSLY_STREAM_SEGMENTS(envOFSC_FANSLY_STREAM_SEGMENTS, default 8) sets how many at once;0turns it off. #295, fixes #292 - A stopped stream download resumes: segments that were already fetched are kept in a hidden
.<part name>.segmentsfolder, and the next run fetches only the missing ones. If the stream or variant changed, it starts over. With--no-auto-resumeorauto_resume: false, nothing is kept. #483, fixes #443 - More stream types resume: AES-128 encrypted HLS, HLS byte ranges and unprotected static DASH now use the resumable downloader as well. Live HLS, other encryption methods, and protected, live or multi-period DASH still go to ffmpeg alone. #484, fixes #444
User scripts
- Scripts get UTF-8 text on Windows: naming, skip-download, after-download, after-like and final scripts run in Python's UTF-8 mode, and their output is read as UTF-8. Non-English text in names no longer comes back garbled on Python 3.11–3.14. #481, fixes #480
Check table
;filter shortcuts fixed:;on a Username cell fills the Username filter, and;on a Streams cell selects Streams in the response filter. #482, fixes #419- One-bound length filters include the bound: a length filter with only a start, or only an end, now includes an item exactly at that bound. #482
Subscriptions
- Empty-page limit lowered to 5: subscription paging now stops after 5 empty pages in a row instead of 20, which still allows 50 hidden subscriptions in a row. #479
Maintenance
- Docs: the
FANSLY_STREAM_SEGMENTSreference covers the resumable stream types. #488 - Dependency and CI updates: #485, #426, #409, #410, #411, #487
Upgrade: uv tool upgrade ofscraper-community, pipx upgrade ofscraper-community or pip install -U ofscraper-community.
Full list: 3.16.9...3.16.10
3.16.9
Prompts
- The error line stays above the help text after a resize: the error line's position was worked out once, when the prompt opened, so after a resize it could cover the re-wrapped keybinding help again. It is now placed from the help's drawn height on every redraw, which also handles word wrapping and wide characters. #477, fixes #473
Check table
- A cart retry fetches the paid list once: when files from several paid posts failed, the retry downloaded the creator's whole paid list once per failed post. It now fetches the list once and takes every failed paid post from it. #476, fixes #474
Subscriptions
- Subscription paging has a limit on empty pages: paging continues past empty pages while OnlyFans reports more (#465), so a server that kept doing so would page forever. Paging now stops after 20 empty pages in a row and logs where it stopped. #478, fixes #475
Upgrade: uv tool upgrade ofscraper-community, pipx upgrade ofscraper-community or pip install -U ofscraper-community.
Full list: 3.16.8...3.16.9
3.16.8
Stop without quitting
- Downloads can be stopped without quitting: Ctrl+C used to be the only way to stop downloads, and it quit the program. The check table has a Stop Downloads button (Ctrl+X) that empties the queue and stops the files in progress while the table stays open. Elsewhere the first Ctrl+C stops the downloads and a second one quits. Files that didn't finish keep their part files and resume on the next run. #451, fixes #440
- Fetching posts can be stopped too: the first Ctrl+C while posts, messages, stories, highlights, labels, streams, pinned, archived or paid content is being fetched stops the fetch. From the menu it goes back to the menu, a command-line run ends with exit code 130, and
--daemonshuts down. The same applies to the check table's fetch before the table opens. #453, fixes #445 - Fansly livestream recordings stop cleanly: stopping a recording killed ffmpeg outright, so whatever it hadn't written yet was lost. ffmpeg is now told to finish and close the file, and is killed only if it hasn't exited after 15 seconds. This applies to the stop event, Ctrl+C with
--liveand daemon shutdown. #452, fixes #442
Check table
- Open the check table from the Action Menu: a new choice, "Browse and pick media (check table)", asks the usual creator questions, then which table to open (Posts, Messages, Paid or Stories). When the table closes, downloads sent from it finish and you're back at the menu. Settings the table changes don't carry over to the next run. #436, fixes #425
- The cart downloads a creator's batch in one go: the cart was downloaded one file at a time and logged "Downloading up to N files at a time" for every file. A creator's cart is now one batch, and each row is still marked with its own result. #450, fixes #439
- A failed cart download refreshes only its own posts: the retry re-fetched the creator's whole area (the full timeline from 2000) to get fresh links. It now fetches just the failed timeline, archived, pinned, streams, paid or message posts, and falls back to the full refresh only when it has to. #463, fixes #458
- Open the download folder from the table:
oopens the folder of the highlighted row's file and selects it (Windows and macOS), or the creator's download folder if the file isn't on disk. An "Open Download Folder" button opens the creator's folder. #434, fixes #424 - Fewer columns by default: Post Media Count, Responsetype, Price, Post ID and Media ID are hidden.
hshows or hides them in the main and cart tables, and the choice is remembered. Their filters still work while hidden. #470, closes #468 - Progress shows while the table fetches: the fetch before the table opens showed nothing, so a long fetch looked stuck. It now shows the same page progress as a normal scrape, and the activity line goes back to what it said when the fetch ends, instead of staying on screen next to the download's own line. #448, fixes #438, fixes #467
- Paste into the ID filters: pasting into Media ID or Post ID did nothing. Pasted numbers now go in, and pasting a temp file name such as
tempvid_<media id>_<post id>.partfills both filters. #460, fixes #455 - Ctrl+D works with a sidebar box focused: a focused text box took Ctrl+D, and hiding a sidebar left the focus on a hidden box, so typing and the cart keys went nowhere. The table now gets the focus back. #428, fixes #422
- Sidebar layout and a Clear Filters button: the size and length boxes no longer overlap, the size boxes are labelled in bytes, and a Clear Filters button sits next to Filter. #431, fixes #423, fixes #427
Prompts and menus
- Esc goes back from any prompt: once inside a menu step there was no way back. Esc now cancels the current prompt and returns to the menu it came from, changing nothing. On the main menu it does nothing. #449, fixes #437
- A redownload Yes applies to that run only: answering Yes to "redownload all files" kept scanning from 2000 and re-downloading everything for the rest of the session. #435, fixes #433
- Clean redraw after a terminal resize: resizing the terminal left copies of the prompt's question on screen, and left old frames of the download display behind, clipped. Both now clear the screen and redraw. #469, fixes #466; #461, fixes #456
- The live display comes back after a prompt: after a prompt the download display didn't restart, and an empty "Activity Progress" box was printed before every later prompt. #429, fixes #420, fixes #421
- The error line no longer covers the help text: "Invalid input" and "You must select at least one" were drawn over the keybinding help. They now sit above it. #459, fixes #454
- The model picker's columns line up: each detail column (subscribed, end or renewed date, last seen, price) is padded to its widest value, wide characters included. #447, fixes #441
- The rescan hint shows only when it helps: once per run, and only when an area scanned from the last scan and the run downloads. Scan range lines end in "now" when no
--beforewas given. #432, fixes #430
Downloads and the API
- An expired signed link fails at once: a 403 from an expired signed CDN link was retried for over a minute although a retry can't succeed, and it raised the shared 403 backoff, which slowed the API calls fetching a fresh link. It now fails straight away without the backoff. #462, fixes #457
- Subscriptions are fetched without repeats: the next page started at the number of subscriptions returned, but OnlyFans counts hidden ones too, so pages overlapped and were fetched again. Paging now follows the requested page size and continues past a page that comes back empty. On a test account it took 13 requests instead of 19. #465, fixes #464
Dependencies and CI
- Dependencies upgraded: six unused packages removed;
aiohttpandclickdeclared directly; upgrades includerich15,textual8,cloup4 withclick8.5,tenacity9,psutil7,xxhash4 andaiofiles25. Stored media IDs are unchanged. #405, closes #404 - Locked dependencies are audited: a
pip-auditjob checksuv.lockfor known vulnerabilities on Linux, Windows and macOS, on Python 3.11 and 3.15. #407 - Dependabot version updates are on: for uv, GitHub Actions and Docker. #408
Other
- A glossary and the first decision record for the project's own terms. #446
- A flaky Esc test is fixed: the test now allows for slow CI runners. #472, fixes #471
Upgrade: uv tool upgrade ofscraper-community, pipx upgrade ofscraper-community or pip install -U ofscraper-community.
Full list: 3.16.7...3.16.8
3.16.7
Security
- Cache entries are read as plain data only (CVE-2025-69872): the API cache could run code from an entry planted in the cache folder, in both cache modes. Cached entries are now read with no classes or functions allowed, so existing caches still work and a planted entry is ignored and logged. #399, #401
- Packages with known vulnerabilities updated: urllib3, soupsieve, anyio, certifi, h2, hpack, idna, pygments and setuptools are on fixed versions. #400
Scripts
- Scripts get their whole input: the task that sends a skip-download, naming or after-download script its data could be cleaned up before it finished, and the script then waited until the timeout. #385, fixes #381
- A script's answer is used as soon as it exits: if a script started a program that keeps running in the background, ofscraper waited for that program and on Python 3.11 reported a timeout even though the script worked. It now waits a moment for the rest of the output, uses the script's answer and logs a warning. Same on every Python version. #393, fixes #391
- Stopping ofscraper stops its scripts: a running script, or ffmpeg, kept running when ofscraper was stopped or the call was cancelled. It's now killed, along with programs it started, including when you press Ctrl+C just as a script starts. #388 #394 #395, fixes #387
- A killed script no longer holds things up: after a timeout or cancel, ofscraper no longer waits for programs the script started in the background. #392, fixes #389
Check commands
- A failed download refreshes only its own creator: in
post_check,msg_check,paid_checkandstory_check, one failed download made the retry re-scrape every creator in the check. #383, fixes #382
Logs
- More auth values are masked: session cookies OnlyFans sends back,
x-bcvalues not fromauth.json,auth_id,user-idand the cookies whose names hold your user ID are now masked by name. The start of the TLS fingerprint seed is no longer logged. #397 - The home folder is masked only as a whole name: with a home folder called
dan, "danger" no longer turns into{home_directory}ger. #397
Other
--before/--afteraccept "1 hour ago", as they already accepted "2 hours ago". #398- The status line says "Couldn't check" when a network problem stops the check, instead of showing DOWN as if the auth were bad. #398
Upgrade: uv tool upgrade ofscraper-community, pipx upgrade ofscraper-community or pip install -U ofscraper-community.
Full list: 3.16.6...3.16.7
3.16.6
Size limits
- DRM videos below
-smare skipped, not failed: the size limits were checked on the audio and video tracks one by one, so with a minimum size the small audio track was "under size min" for almost every video, and the video then failed after all its retries. The limits now see the whole video (audio + video), and a video outside them is skipped straight away with no retries. #321, fixes #316 - Size-skipped files are no longer saved as downloaded: a file skipped by
-sm/-sxwas recorded as done, so it was never tried again when you changed the limit. It is now counted as skipped and tried again next run. #326, fixes #319 - Old rows from that bug are repaired: on the first start after upgrading, files the bug saved as downloaded with no file on disk are set back to not downloaded, so they're tried again. A backup is made first. Files that really were downloaded, and permanent skips, are left alone. #359, fixes #352
- Files sent without a size now get the size limits and the skip script too. #342, fixes #336
- A size skip stays a skip when a later error hits the same attempt, and the
.partof a resumed file that's then skipped is removed. #347, #349, fixes #345 #348 - DRM: when the audio track is skipped, the video track isn't downloaded either. #344, fixes #343
- After-download script: size-skipped items reach your script as
skippedwith their real media type, notForced_skipped. #337, fixes #332 #341
skip_download_script
- A skip is asked again next run: printing
false(or nothing) skips the file for this run only. It's counted as skipped, not saved as downloaded, and the script is asked again next time. Each file is asked once per run. #357, fixes #350 #351 - New: permanent skips: print
skip-permanentto skip a file for good. It's saved as done with no file and not asked again. #357 - New: undo permanent skips:
--retry-permanent-skipsasks the script again about permanently skipped files on a normal run, andofscraper db --unmark-permanent-skipsun-marks them by creator,--post-idor--media-id. Only permanent skips are touched. #362, fixes #354 - DRM: when the script skips one track of a DRM video, the whole video is skipped. #331, fixes #325
Check commands
- Items skipped from the check table aren't marked downloaded, and the Downloaded column updates straight away. Skipped items can be added again. #355, fixes #353 #358
post_checklists streams posts, and appliestimeline_strictto timeline posts. #327 #333, fixes #324 #329msg_checkskips inputs that don't match a creator, and reads usernames that contain digits. #338, fixes #334- 24-hour times in the check table and the scrape summary. #339, fixes #335
--metadatano longer writes cached check data over newer rows. #346, fixes #340- The check table no longer crashes on posts with no date; they sort last. #370
ofscraper db
- Reading changes nothing:
ofscraper dbused to switch a database to WAL mode just by reading it, failed on read-only files, and created an empty file and folder for creators with no database. It now opens databases read-only, works on read-only files and network shares (\\server\share), and skips a creator with no database with a short message. A database it can't read is named and skipped, and the other creators still show. #374, fixes #373 - Older databases work: databases missing newer columns no longer fail with "no such column", over all areas or picked areas. #365, fixes #364
- Rows with no date show an empty date instead of crashing, and
--exportworks with a custom date format. The table's columns stay in the same order every run. #367, fixes #366 #368 - Shared databases: over all areas, a database that holds several creators now shows only the chosen creator's media. Per-area output gains the
skip_permanentcolumn. #372, fixes #371 --post-idand--media-idwork: they never matched anything before. They take one id, or several separated by commas or spaces. #362, fixes #363
Merge DB's
- Downloaded state and post links are kept when merging databases. #297 #299, fixes #296 #298
- A failed database no longer crashes the end of the merge, and failed databases are listed in the summary. #301 #308, fixes #300 #305
- The merge screen works as asked: "Do another merge" Yes starts another merge, and No on the backup question or Back to Main Menu closes it. #303 #306, fixes #302 #304
- The source folder is checked before the target is created, and no backup is made for a database created just now. #311 #313, fixes #310 #312
Scanning
- Posts and messages with no date no longer stop a scan without a word or crash the media filters. They're kept, downloaded with their media's date (or 1970-01-01), and logged with a warning, once per file. This covers OnlyFans and Fansly. #370 #377, fixes #369 #375
- The last page of a timeline, archived or streams scan is kept. #330, fixes #322
- Deleted posts are skipped in archived and streams lookups, and posts and messages that come back keep a cleared deleted flag. #320 #328, fixes #318 #323
Errors you can see
- A failed creator is shown: when something fails for one creator, a warning names the creator and the error at normal output, and the run moves on. Before, it only reached the traceback log. The same goes for a failed area of a scrape (timeline, messages, paid and so on), a failed daemon pass and the scheduler. A repeating scheduler error now waits its normal interval instead of looping. #380, fixes #379
metadatano longer crashes after locking stray items. #378, fixes #376- Main-menu actions that fail say so on screen. #309, fixes #307
- The download count says how many files download at once. #293, fixes #291
Fansly
Stash
- New: Stash sync copies the OnlyFans and Fansly details of your downloads into Stash. See STASH.md for setup. #140
- Purchase type, Fansly conversation id and mentions are now saved, and Stash sync uses them to link bought OnlyFans posts and Fansly messages to their pages. A Fansly message links to its conversation. #317 #356 #361, fixes #315 #360
Upgrade: uv tool upgrade ofscraper-community, pipx upgrade ofscraper-community or pip install -U ofscraper-community.
Full list: 3.16.5...3.16.6