How FlClash's Profile Auto-Update System Handles Subscription Imports
FlClash automatically refreshes subscription profiles by comparing stored timestamps against configurable intervals, fetching remote configuration files, validating them through the Clash core, and persisting updated metadata including bandwidth usage statistics parsed from HTTP headers.
FlClash implements a robust interval-driven mechanism to keep subscription profiles synchronized with remote sources. The chen08209/FlClash repository orchestrates this behavior through Dart model classes and provider actions that manage periodic updates while ensuring configuration integrity via the underlying Clash engine.
Profile Structure and Auto-Update Configuration
URL-Based Profile Attributes
In lib/models/profile.dart, the Profile class defines the data structure for subscription imports. Profiles imported from remote sources use ProfileType.url and store several critical fields that govern the auto-update behavior:
url— The remote subscription address to fetch.autoUpdate— Boolean flag defaulting to true that controls whether the profile should refresh automatically.autoUpdateDuration— TheDurationinterval (defaulting todefaultUpdateDuration) specifying how long to wait between updates.lastUpdateDate— Timestamp recording the last successful import.subscriptionInfo— Parsed data from thesubscription-userinfoHTTP header containing upload, download, total bandwidth, and expiration date.
These fields persist in the local database defined in lib/database/profiles.dart, which stores auto_update, auto_update_duration_millis, and last_update_date columns.
The Auto-Update Orchestration Logic
Entry Point: autoUpdateProfiles()
The central coordination happens in lib/providers/action.dart within the ProfilesAction class. The autoUpdateProfiles() method (around line 60) serves as the entry point invoked during app startup or periodic background tasks:
The method iterates through every stored profile and applies three filtering criteria to determine eligibility:
- Skip manual-update profiles — Checks
if (!profile.autoUpdate) continue;to respect user preferences. - Exclude file-based profiles — Skips local configurations with
if (profile.type == ProfileType.file) continue;since only remote URLs require refreshing. - Time-based validation — Determines if an update is required by evaluating whether the stored
lastUpdateDateplus the configuredautoUpdateDurationfalls before the current time:
final isNotNeedUpdate = profile.lastUpdateDate
?.add(profile.autoUpdateDuration)
.isBeforeNow;
if (isNotNeedUpdate == false) continue;
Individual Profile Updates
When a profile passes the time check, the system invokes updateProfile() (around line 90 in lib/providers/action.dart). This method wraps the core update logic and handles UI state notifications while delegating the actual import work to the Profile model.
Subscription Import and Validation Process
HTTP Fetch and Header Parsing
The update() method in lib/models/profile.dart (around line 200) handles the actual subscription import workflow:
First, it fetches the remote content using request.getFileResponseForUrl(url). During this phase, the system extracts metadata from HTTP response headers:
content-disposition— Used to generate default profile labels from the filename.subscription-userinfo— Parsed viaSubscriptionInfo.formHStringto extract upload bytes, download bytes, total bandwidth quota, and expiration timestamps.
Core Validation and Persistence
Before activating the new configuration, FlClash validates the downloaded file:
- Temporary storage — The subscription content saves to a temporary path.
- Configuration validation — The file passes to
coreController.validateConfig(path), which verifies the configuration against the Clash core rules. Any validation message aborts the import. - Permanent storage — Upon successful validation, the system copies the temporary file to the profile's permanent location using
await tempFile.copy(mFile.path). - Metadata refresh — The method returns an updated
Profileinstance withlastUpdateDateset toDateTime.now()and the refreshedsubscriptionInfo.
If an error occurs during any step, the exception is caught and logged, but the auto-update loop continues processing remaining profiles rather than failing entirely.
Working with the Auto-Update API
You can interact with the auto-update system programmatically using the following patterns:
Trigger a manual refresh for a single subscription profile:
await ref.read(profilesActionProvider.notifier).updateProfile(myProfile);
Enable or modify auto-update settings for a specific profile:
await ref.read(profilesProvider.notifier).put(
myProfile.copyWith(
autoUpdate: true,
autoUpdateDuration: Duration(hours: 12)
),
);
Execute the periodic auto-update check (typically called at app launch or via background tasks):
await ref.read(profilesActionProvider.notifier).autoUpdateProfiles();
Summary
- FlClash distinguishes between
ProfileType.urlandProfileType.file, applying auto-updates only to remote subscriptions. - The
autoUpdateProfiles()method inlib/providers/action.dartorchestrates refreshes by checking thelastUpdateDateagainstautoUpdateDurationfor each profile. - The
Profile.update()method inlib/models/profile.darthandles HTTP fetching,subscription-userinfoheader parsing, and Clash core validation before persisting files. - Errors during individual profile updates are isolated, ensuring one failed subscription does not block updates for others.
- Database persistence in
lib/database/profiles.dartstores auto-update preferences and timestamps for offline scheduling.
Frequently Asked Questions
How does FlClash determine when to update a subscription profile?
FlClash calculates the next update time by adding the profile's autoUpdateDuration to its lastUpdateDate. When the app runs autoUpdateProfiles() in lib/providers/action.dart, it skips profiles where this calculated time is still in the future, refreshing only those whose intervals have elapsed.
What happens if a subscription URL returns invalid configuration data?
During the update process in lib/models/profile.dart, FlClash passes the downloaded file to coreController.validateConfig(path). If the Clash core returns validation errors, the import aborts before replacing the existing profile, preserving the last known good configuration while logging the error for diagnostics.
Where does FlClash store auto-update settings and timestamps?
The application persists these values in a local Drift database defined in lib/database/profiles.dart, specifically within fields mapping to auto_update, auto_update_duration_millis, and last_update_date. This allows the auto-update scheduler to function correctly even after app restarts.
Can users disable auto-updates for specific subscriptions while keeping them for others?
Yes. Each Profile instance maintains its own autoUpdate boolean flag defaulting to true. Users can disable automatic refreshing for individual profiles by setting autoUpdate: false via the settings API, which autoUpdateProfiles() checks via if (!profile.autoUpdate) continue; before attempting any network requests.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →