13 KiB
AGENTS.md
Note: Keep AGENTS.md updated as project structure, key services, or workflows change.
This repo is OF DL (also known as OF-DL), a C# app suite (console + Avalonia desktop GUI) that downloads media from a user's OnlyFans account(s). This document is for AI agents helping developers modify the project. It focuses on architecture, data flow, and the most important change points.
Quick Flow
Program.Mainis the app entrypoint: default launch is GUI;--cliswitches to CLI mode.StartupService.CheckVersionAsyncchecks the latest release tag (OFDLV*) fromgit.ofdl.toolswhen not in DEBUG.StartupService.ValidateEnvironmentAsyncvalidates OS, FFmpeg,rules.json, and Widevine device files.AuthServiceloadsauth.jsonor opens a browser login (PuppeteerSharp) and persists auth data.ApiServicesigns every API request with dynamic rules and the current auth.DownloadOrchestrationServiceselects creators, prepares folders/DBs, and callsDownloadServiceper media type.DownloadServicedownloads media, handles DRM, and records metadata in SQLite.OF DL.Guistarts with config validation, then auth validation/browser login, then loads users/lists and provides multi-select download controls.
Project Layout
OF DL.Cli/CLI/contains Spectre.Console UI helpers and progress reporting (CLI-only).OF DL.Gui/contains the Avalonia desktop UI (App,MainWindow,AboutWindow,FaqWindow, MVVM view models, and GUI event handlers).OF DL.Gui/Helpers/contains GUI-specific utility helpers (for example, Docker-aware web-link behavior shared across windows).OF DL.Core/Services/contains application services (API, auth, download, config, DB, startup, logging, filenames).OF DL.Core/Models/holds configuration, auth, API request/response models, downloads/startup results, DTOs, entities, and mapping helpers.OF DL.Core/Widevine/implements Widevine CDM handling and key derivation.OF DL.Core/Helpers/,OF DL.Core/Utils/,OF DL.Core/Crypto/,OF DL.Core/Enumerations/contain shared core logic.OF DL.Core/Helpers/EnvironmentHelper.cscentralizes environment checks (Docker and Windows).docs/andmkdocs.ymldefine the documentation site.site/is generated MkDocs output and should not be edited by hand.docker/contains container entrypoint and supervisor configuration.
Key Services
ApiService(OF DL.Core/Services/ApiService.cs) builds signed headers, performs HTTP requests, and maps DTOs to entities. It also handles DRM-related calls like MPD/PSSH extraction and license requests.AuthService(OF DL.Core/Services/AuthService.cs) loadsauth.jsonor performs browser-based login with PuppeteerSharp, then persists auth. It also normalizes cookies.ConfigService(OF DL.Core/Services/ConfigService.cs) loadsconfig.conf(HOCON), migrates legacyconfig.json, and updates global settings (logging, text sanitization).DownloadService(OF DL.Core/Services/DownloadService.cs) downloads all media (images, video, audio) and handles DRM video decryption and FFmpeg execution.DownloadOrchestrationService(OF DL.Core/Services/DownloadOrchestrationService.cs) coordinates user selection, subscription lists, per-user folder prep, and per-media-type download execution.DBService(OF DL.Core/Services/DBService.cs) manages SQLite metadata DBs for downloaded media and ausers.dbindex.StartupService(OF DL.Core/Services/StartupService.cs) validates FFmpeg, rules.json, Widevine device files, and performs release version checks.LoggingService(OF DL.Core/Services/LoggingService.cs) writes logs tologs/OFDL.txtand updates log level based on config.FileNameService(OF DL.Core/Services/FileNameService.cs) formats filenames using the custom format rules from config.
Models
- DTOs live under
OF DL.Core/Models/Dtos/and mirror API response JSON. - Entities live under
OF DL.Core/Models/Entities/and represent the internal domain used by download logic. - Mappers in
OF DL.Core/Models/Mappers/convert DTOs into entities to isolate API changes from downstream logic. - Non-DTO/Entity models are grouped by concern under
OF DL.Core/Models/OfdlApi/,Auth/,Config/,Downloads/, andStartup/. - Classes in
OF DL.Core/Models/OfdlApi/mirror request and response JOSN OF DL APIs (custom and gitea) - Classes in
OF DL.Core/Models/Config/are used for reading and storing application configuration - Classes in
OF DL.Core/Models/Downloads/contain counts and application state for downloads
Configuration
- Primary config file is
config.conf(HOCON).ConfigServicemigrates legacyconfig.jsonif found and creates a defaultconfig.confif missing. Configlives inOF DL.Core/Models/Config/Config.csand is populated byConfigService.LoadConfigFromFileAsync.ConfigService.UpdateConfigis the central place where runtime config changes are applied (logging level and text sanitization).- CLI flag
--non-interactiveforces non-interactive mode;ConfigService.IsCliNonInteractiveandConfig.NonInteractiveModeboth gate prompts. - FFmpeg path is read from
config.conf,auth.json, or auto-detected from PATH/current directory.
Runtime Files (relative to the working directory)
config.conf,auth.json, andrules.jsonare loaded from the current working directory.cdm/(Widevine device files),chrome-data/(Puppeteer profile), andlogs/are created under the working directory.users.dbis stored at the working directory root.
Execution and Testing
- .NET SDK: 10.x (
net10.0for all projects). - Build from the repo root:
dotnet build OF DL.sln
- Run from source (GUI mode, default):
dotnet run --project "OF DL/OF DL.Cli.csproj"
- Run CLI mode:
dotnet run --project "OF DL/OF DL.Cli.csproj" -- --cli
- If you want a local
rules.jsonfallback, run fromOF DL.Cli/or copyOF DL.Cli/rules.jsoninto your working directory. - Run tests:
dotnet test "OF DL.Tests/OF DL.Tests.csproj"
- Optional coverage (coverlet collector):
dotnet test "OF DL.Tests/OF DL.Tests.csproj" --collect:"XPlat Code Coverage"
Authentication Flow
- Auth data is stored in
auth.jsonusing theAuthmodel inOF DL.Core/Models/Auth/Auth.cs. AuthService.LoadFromBrowserAsynclaunches Chrome via PuppeteerSharp, waits for login, then extractsauth_idandsesscookies,bcTokenShafrom localStorage (used asX_BC), andUSER_AGENTfrom the browser.AuthService.ValidateCookieStringrewrites the cookie string so it contains onlyauth_idandsessand ensures a trailing;.AuthServiceuseschrome-data/as its user data directory;Logoutdeleteschrome-dataandauth.json.
Environment variables used by auth:
OFDL_DOCKER=truetoggles Docker-specific instructions and browser flags.OFDL_PUPPETEER_EXECUTABLE_PATHoverrides the Chromium path for PuppeteerSharp.
Dynamic Rules and Signature Headers
- All OnlyFans API requests use dynamic headers from
ApiService.GetDynamicHeaders. - Dynamic rules are fetched from
https://git.ofdl.tools/sim0n00ps/dynamic-rules/raw/branch/main/rules.jsonwith fallback to localrules.jsonin the current working directory. The repo shipsOF DL/rules.jsonas the default rules file. - Cache durations: 15 minutes for remote rules, 5 minutes for local rules.
DynamicRulesshape is defined inOF DL.Core/Models/OfdlApi/DynamicRules.csand includesapp-token,static_param,prefix,suffix,checksum_constant, andchecksum_indexes.
Signature algorithm in GetDynamicHeaders:
input = "{static_param}\n{timestamp_ms}\n{path+query}\n{user_id}"hash = SHA1(input)lower-case hex stringchecksum = sum(hashString[index] char values) + checksum_constantsign = "{prefix}:{hash}:{checksum_hex}:{suffix}"
Headers included in signed requests:
app-token,sign,time,user-id,user-agent,x-bc,cookie.
Widevine CDM and DRM Decryption
- Runtime Widevine device files are expected at
cdm/devices/chrome_1610/device_client_id_blobandcdm/devices/chrome_1610/device_private_key(relative to the working directory). Paths are defined inOF DL.Core/Widevine/Constants.csand validated inStartupService.
DRM flow is primarily in DownloadService.GetDecryptionInfo and ApiService DRM helpers:
ApiService.GetDRMMPDPSSHdownloads the MPD manifest and extracts thecenc:psshvalue.ApiService.GetDRMMPDLastModifieduses CloudFront signed cookies and returns MPDLast-Modified.DownloadService.GetDecryptionInfobuilds DRM headers (viaGetDynamicHeaders) and hits the license endpoint.
Two decryption paths exist:
- If CDM device files exist,
ApiService.GetDecryptionKeyCDMusesWidevine/CDMApi. - If missing,
ApiService.GetDecryptionKeyOFDLcallshttps://ofdl.tools/WVas a fallback.
DownloadService.DownloadDrmMedia runs FFmpeg with -cenc_decryption_key, CloudFront cookies, and auth
cookies/user-agent. Output is written to {filename}_source.mp4, then moved and recorded in SQLite.
Download Paths, Data, and Logs
- Default download root is
__user_data__/sites/OnlyFans/{username}whenDownloadPathis blank. This is computed inDownloadOrchestrationService.ResolveDownloadPath. - Each creator folder gets a
Metadata/user_data.db(SQLite) managed byDBService. - A global
users.dbin the working directory tracks subscribed creators and user IDs. - Logs are written to
logs/OFDL.txt(rolling daily); FFmpeg report files are also written underlogs/when debug logging is enabled.
Docs (MkDocs)
- Docs source lives under
docs/and configuration is inmkdocs.yml. - Build the site with
mkdocs build --clean(outputs tosite/). - Preview locally with
mkdocs serve.
CI/CD (Gitea Workflows)
/.gitea/workflows/publish-docs.ymlbuilds and deploys docs on tag pushes matchingOFDLV*and on manual dispatch./.gitea/workflows/publish-docker.ymlbuilds multi-arch Docker images onOFDLV*tags and pushes to the Gitea registry./.gitea/workflows/publish-release.ymlpublishes Windows and Linux builds onOFDLV*tags and creates a draft release.
Docker Image
- Built via
/.gitea/workflows/publish-docker.ymlon tag pushesOFDLV*. - Image tags:
git.ofdl.tools/sim0n00ps/of-dl:latestandgit.ofdl.tools/sim0n00ps/of-dl:{version}. - Build args include
VERSION(tag name withOFDLVstripped). - Platforms:
linux/amd64andlinux/arm64. - Runtime uses
/configas the working directory and/datafor downloads;docker/entrypoint.shseeds/config/config.confand/config/rules.jsonfrom/default-config.
Release Checklist
- Update docs under
docs/and verify locally withmkdocs build --cleanormkdocs serve. - Tag the release as
OFDLV{version}and push the tag. - Verify the draft release artifact and publish the release in Gitea.
Coding Style (from .editorconfig)
- Indentation: 4 spaces by default, 2 spaces for XML/YAML/project files. No tabs.
- Line endings: LF for
*.sh, CRLF for*.cmd/*.bat. - C# braces on new lines (
csharp_new_line_before_open_brace = all). - Prefer predefined types (
int,string) and avoidvarexcept when type is apparent. usingdirectives go outside the namespace andSystemnamespaces are sorted first.- Private/internal fields use
_camelCase; private/internal static fields uses_prefix. constfields should be PascalCase.- Prefer braces for control blocks and expression-bodied members (silent preferences).
Where to Look First
OF DL/Program.csfor the execution path and menu flow.OF DL.Gui/ViewModels/MainWindowViewModel.csfor GUI startup flow (config -> auth -> users/lists -> selection).OF DL.Gui/Views/MainWindow.axamlfor GUI layout and interaction points.OF DL.Gui/Views/AboutWindow.axamlandOF DL.Gui/Views/FaqWindow.axamlfor Help menu windows.OF DL.Core/Services/ApiService.csfor OF API calls and header signing.OF DL.Core/Services/DownloadService.csfor downloads and DRM handling.OF DL.Core/Services/DownloadOrchestrationService.csfor creator selection and flow control.OF DL.Core/Widevine/for CDM key generation and license parsing.OF DL.Core/Models/Config/Config.csandOF DL.Core/Services/ConfigService.csfor config shape and parsing.OF DL.Core/Services/AuthService.csfor user-facing authentication behavior and browser login flow.OF DL.Core/Helpers/EnvironmentHelper.csfor shared Docker/Windows runtime checks.docs/for public documentation; update docs whenever user-facing behavior or configuration changes.
Documentation updates for common changes:
- Config option added/removed/changed in
Configorconfig.conf: updatedocs/config/all-configuration-options.md( full spec),docs/config/configuration.md(organized list), anddocs/config/custom-filename-formats.mdif filename tokens or formats are affected. - Authentication flow changes (browser login, legacy methods, required fields): update
docs/config/auth.md. - CLI/menu flow or download workflow changes: update
docs/running-the-program.md. - Docker runtime or container flags/paths change: update
docs/installation/docker.md.