Add legacy auth method to GUI, remove unnecessary activity log entries, and avoid deleting the auth.json file when auth fails

This commit is contained in:
whimsical-c4lic0 2026-02-20 00:35:19 -06:00
parent f536a34772
commit 0a709f97ea
6 changed files with 195 additions and 114 deletions

View File

@ -209,14 +209,6 @@ public class Program(IServiceProvider serviceProvider)
Log.Error("Auth failed"); Log.Error("Auth failed");
authService.CurrentAuth = null; authService.CurrentAuth = null;
if (!configService.CurrentConfig.DisableBrowserAuth)
{
if (File.Exists("auth.json"))
{
File.Delete("auth.json");
}
}
if (!configService.CurrentConfig.NonInteractiveMode && if (!configService.CurrentConfig.NonInteractiveMode &&
!configService.CurrentConfig.DisableBrowserAuth) !configService.CurrentConfig.DisableBrowserAuth)
{ {
@ -747,11 +739,6 @@ public class Program(IServiceProvider serviceProvider)
else if (File.Exists("auth.json")) else if (File.Exists("auth.json"))
{ {
Log.Information("Auth file found but could not be deserialized"); Log.Information("Auth file found but could not be deserialized");
if (!configService.CurrentConfig.DisableBrowserAuth)
{
Log.Debug("Deleting auth.json");
File.Delete("auth.json");
}
if (configService.CurrentConfig.NonInteractiveMode) if (configService.CurrentConfig.NonInteractiveMode)
{ {

View File

@ -6,6 +6,10 @@ public static class Constants
public const string DocumentationUrl = "https://docs.ofdl.tools/"; public const string DocumentationUrl = "https://docs.ofdl.tools/";
public const string AuthHelperExtensionUrl = "https://github.com/whimsical-c4lic0/OF-DL-Auth-Helper/";
public const string LegacyAuthDocumentationUrl = "https://docs.ofdl.tools/config/auth/#legacy-methods";
public const string ApiUrl = "https://onlyfans.com/api2/v2"; public const string ApiUrl = "https://onlyfans.com/api2/v2";
public const int ApiPageSize = 50; public const int ApiPageSize = 50;

View File

@ -5,6 +5,7 @@ public enum AppScreen
Loading, Loading,
Config, Config,
Auth, Auth,
ManualAuth,
UserSelection, UserSelection,
Error Error
} }

View File

@ -12,6 +12,7 @@ using Avalonia.Threading;
using Newtonsoft.Json; using Newtonsoft.Json;
using OF_DL.Enumerations; using OF_DL.Enumerations;
using OF_DL.Gui.Services; using OF_DL.Gui.Services;
using OF_DL.Helpers;
using OF_DL.Models; using OF_DL.Models;
using OF_DL.Models.Config; using OF_DL.Models.Config;
using OF_DL.Models.Downloads; using OF_DL.Models.Downloads;
@ -258,7 +259,18 @@ public partial class MainWindowViewModel(
[ObservableProperty] private string _authScreenMessage = string.Empty; [ObservableProperty] private string _authScreenMessage = string.Empty;
[ObservableProperty] [NotifyCanExecuteChangedFor(nameof(StartBrowserLoginCommand))] [ObservableProperty] private string _manualAuthScreenMessage = string.Empty;
[ObservableProperty] private string _manualAuthInstructionsText = string.Empty;
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(GoBackToAuthScreenCommand))]
[NotifyCanExecuteChangedFor(nameof(ContinueWithManualAuthCommand))]
private bool _isManualAuthValidationInProgress;
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(StartBrowserLoginCommand))]
[NotifyCanExecuteChangedFor(nameof(OpenManualAuthScreenCommand))]
private bool _isBrowserLoginInProgress; private bool _isBrowserLoginInProgress;
[ObservableProperty] private string _errorMessage = string.Empty; [ObservableProperty] private string _errorMessage = string.Empty;
@ -354,6 +366,8 @@ public partial class MainWindowViewModel(
public bool IsAuthScreen => CurrentScreen == AppScreen.Auth; public bool IsAuthScreen => CurrentScreen == AppScreen.Auth;
public bool IsManualAuthScreen => CurrentScreen == AppScreen.ManualAuth;
public bool IsUserSelectionScreen => CurrentScreen == AppScreen.UserSelection; public bool IsUserSelectionScreen => CurrentScreen == AppScreen.UserSelection;
public bool IsErrorScreen => CurrentScreen == AppScreen.Error; public bool IsErrorScreen => CurrentScreen == AppScreen.Error;
@ -545,10 +559,12 @@ public partial class MainWindowViewModel(
AvailableUsers.Clear(); AvailableUsers.Clear();
UserLists.Clear(); UserLists.Clear();
SelectedListName = null; SelectedListName = null;
IsManualAuthValidationInProgress = false;
ManualAuthScreenMessage = string.Empty;
AuthenticatedUserDisplay = "Not authenticated."; AuthenticatedUserDisplay = "Not authenticated.";
AuthScreenMessage = "You have been logged out. Click 'Login with Browser' to authenticate."; AuthScreenMessage =
StatusMessage = "Logged out."; "You have been logged out. OF DL needs access to your OnlyFans account.\nAn included web browser can be used to sign-in, or you can create an \"auth.json\" file manually.";
CurrentScreen = AppScreen.Auth; CurrentScreen = AppScreen.Auth;
OnPropertyChanged(nameof(SelectedUsersSummary)); OnPropertyChanged(nameof(SelectedUsersSummary));
DownloadSelectedCommand.NotifyCanExecuteChanged(); DownloadSelectedCommand.NotifyCanExecuteChanged();
@ -570,7 +586,6 @@ public partial class MainWindowViewModel(
EnforceGuiOnlyConfigValues(configService.CurrentConfig); EnforceGuiOnlyConfigValues(configService.CurrentConfig);
BuildConfigFields(configService.CurrentConfig); BuildConfigFields(configService.CurrentConfig);
ConfigScreenMessage = "Edit configuration values and save to apply changes."; ConfigScreenMessage = "Edit configuration values and save to apply changes.";
StatusMessage = "Editing configuration.";
CurrentScreen = AppScreen.Config; CurrentScreen = AppScreen.Config;
} }
@ -584,7 +599,6 @@ public partial class MainWindowViewModel(
if (!loaded) if (!loaded)
{ {
ConfigScreenMessage = "Configuration is still invalid."; ConfigScreenMessage = "Configuration is still invalid.";
StatusMessage = ConfigScreenMessage;
CurrentScreen = AppScreen.Config; CurrentScreen = AppScreen.Config;
return; return;
} }
@ -592,7 +606,6 @@ public partial class MainWindowViewModel(
if (_configReturnScreen == AppScreen.UserSelection && _allUsers.Count > 0) if (_configReturnScreen == AppScreen.UserSelection && _allUsers.Count > 0)
{ {
CurrentScreen = AppScreen.UserSelection; CurrentScreen = AppScreen.UserSelection;
StatusMessage = "Configuration changes canceled.";
return; return;
} }
@ -611,7 +624,6 @@ public partial class MainWindowViewModel(
} }
ConfigScreenMessage = "Refreshing user lists..."; ConfigScreenMessage = "Refreshing user lists...";
StatusMessage = ConfigScreenMessage;
try try
{ {
@ -624,14 +636,11 @@ public partial class MainWindowViewModel(
UpdateIgnoredUsersListFieldOptions(); UpdateIgnoredUsersListFieldOptions();
ConfigScreenMessage = $"User lists refreshed ({_allLists.Count})."; ConfigScreenMessage = $"User lists refreshed ({_allLists.Count}).";
StatusMessage = ConfigScreenMessage;
AppendLog(ConfigScreenMessage); AppendLog(ConfigScreenMessage);
} }
catch (Exception ex) catch (Exception ex)
{ {
ConfigScreenMessage = $"Could not refresh user lists: {ex.Message}"; AppendLog($"Could not refresh user lists: {ex.Message}");
StatusMessage = ConfigScreenMessage;
AppendLog(ConfigScreenMessage);
} }
} }
@ -641,7 +650,6 @@ public partial class MainWindowViewModel(
if (!TryBuildConfig(out Config newConfig)) if (!TryBuildConfig(out Config newConfig))
{ {
ConfigScreenMessage = "Fix configuration validation errors and save again."; ConfigScreenMessage = "Fix configuration validation errors and save again.";
StatusMessage = ConfigScreenMessage;
return; return;
} }
@ -655,14 +663,12 @@ public partial class MainWindowViewModel(
{ {
ConfigScreenMessage = "config.conf could not be loaded after saving. Please review your values."; ConfigScreenMessage = "config.conf could not be loaded after saving. Please review your values.";
CurrentScreen = AppScreen.Config; CurrentScreen = AppScreen.Config;
StatusMessage = ConfigScreenMessage;
BuildConfigFields(configService.CurrentConfig); BuildConfigFields(configService.CurrentConfig);
return; return;
} }
BuildConfigFields(configService.CurrentConfig); BuildConfigFields(configService.CurrentConfig);
ConfigScreenMessage = "Configuration saved."; ConfigScreenMessage = "Configuration saved.";
StatusMessage = "Configuration saved.";
if (!await ValidateEnvironmentAsync(false)) if (!await ValidateEnvironmentAsync(false))
{ {
@ -682,16 +688,8 @@ public partial class MainWindowViewModel(
[RelayCommand(CanExecute = nameof(CanStartBrowserLogin))] [RelayCommand(CanExecute = nameof(CanStartBrowserLogin))]
private async Task StartBrowserLoginAsync() private async Task StartBrowserLoginAsync()
{ {
if (configService.CurrentConfig.DisableBrowserAuth)
{
AuthScreenMessage = "Browser authentication is disabled in config.";
return;
}
IsBrowserLoginInProgress = true; IsBrowserLoginInProgress = true;
AppendLog("Starting browser authentication flow.");
StartBrowserLoginCommand.NotifyCanExecuteChanged();
bool success; bool success;
try try
{ {
@ -702,8 +700,7 @@ public partial class MainWindowViewModel(
return; return;
} }
AuthScreenMessage = message; Dispatcher.UIThread.Post(() => SetLoading(message));
AppendLog(message);
}); });
} }
finally finally
@ -714,27 +711,86 @@ public partial class MainWindowViewModel(
if (!success || authService.CurrentAuth == null) if (!success || authService.CurrentAuth == null)
{ {
AuthScreenMessage = AuthScreenMessage =
"Authentication failed. Log in using the opened browser window and retry."; "Authentication failed. Retry the browser based login, or use\n'Manual Authentication' to continue.";
CurrentScreen = AppScreen.Auth; CurrentScreen = AppScreen.Auth;
StatusMessage = "Authentication failed.";
AppendLog("Browser authentication failed.");
return; return;
} }
await authService.SaveToFileAsync(); await authService.SaveToFileAsync();
bool isAuthValid = await ValidateCurrentAuthAsync(true); bool isAuthValid = await ValidateCurrentAuthAsync();
if (!isAuthValid) if (!isAuthValid)
{ {
AuthScreenMessage = "Authentication is still invalid after login. Please retry."; AuthScreenMessage =
"Authentication is still invalid after login.\nPlease retry, or use 'Manual Authentication' to continue.";
CurrentScreen = AppScreen.Auth; CurrentScreen = AppScreen.Auth;
StatusMessage = "Authentication failed.";
return; return;
} }
await LoadUsersAndListsAsync(); await LoadUsersAndListsAsync();
} }
[RelayCommand(CanExecute = nameof(CanOpenManualAuthScreen))]
private void OpenManualAuthScreen()
{
IsManualAuthValidationInProgress = false;
ManualAuthScreenMessage = string.Empty;
string configFolder = EnvironmentHelper.IsRunningInDocker()
? "your OF DL config folder"
: "the same folder as OF DL";
ManualAuthInstructionsText =
$"An \"auth.json\" file is required to use OF DL. See the documentation for creating this file manually." +
$"\nOnce you've created the \"auth.json\", save it to {configFolder}.";
CurrentScreen = AppScreen.ManualAuth;
}
[RelayCommand(CanExecute = nameof(CanGoBackToAuthScreen))]
private void GoBackToAuthScreen()
{
IsManualAuthValidationInProgress = false;
ManualAuthScreenMessage = string.Empty;
AuthScreenMessage =
"OF DL needs access to your OnlyFans account.\nAn included web browser can be used to sign-in, or you can create an \"auth.json\" file manually.";
CurrentScreen = AppScreen.Auth;
}
[RelayCommand(CanExecute = nameof(CanContinueWithManualAuth))]
private async Task ContinueWithManualAuthAsync()
{
IsManualAuthValidationInProgress = true;
ManualAuthScreenMessage = "Validating auth.json...";
try
{
bool loadedFromFile = await authService.LoadFromFileAsync();
if (!loadedFromFile)
{
authService.CurrentAuth = null;
IsAuthenticated = false;
AuthenticatedUserDisplay = "Not authenticated.";
ManualAuthScreenMessage =
"auth.json was not found or could not be read. Update auth.json and try again.";
return;
}
bool isValid = await ValidateCurrentAuthAsync();
if (!isValid)
{
ManualAuthScreenMessage =
"auth.json failed validation. Update the file and try again.";
return;
}
await LoadUsersAndListsAsync();
}
finally
{
IsManualAuthValidationInProgress = false;
}
}
[RelayCommand(CanExecute = nameof(CanApplySelectedList))] [RelayCommand(CanExecute = nameof(CanApplySelectedList))]
private async Task SelectUsersFromListAsync() private async Task SelectUsersFromListAsync()
{ {
@ -763,7 +819,6 @@ public partial class MainWindowViewModel(
user.IsSelected = selectedUsernames.Contains(user.Username); user.IsSelected = selectedUsernames.Contains(user.Username);
} }
StatusMessage = $"Selected {selectedUsernames.Count} users from list '{SelectedListName}'.";
OnPropertyChanged(nameof(SelectedUsersSummary)); OnPropertyChanged(nameof(SelectedUsersSummary));
OnPropertyChanged(nameof(AllUsersSelected)); OnPropertyChanged(nameof(AllUsersSelected));
DownloadSelectedCommand.NotifyCanExecuteChanged(); DownloadSelectedCommand.NotifyCanExecuteChanged();
@ -831,7 +886,6 @@ public partial class MainWindowViewModel(
if (_workCancellationSource is { IsCancellationRequested: false }) if (_workCancellationSource is { IsCancellationRequested: false })
{ {
_workCancellationSource.Cancel(); _workCancellationSource.Cancel();
StatusMessage = "Stop requested. Waiting for current operation to cancel...";
UpdateProgressStatus("Stopping..."); UpdateProgressStatus("Stopping...");
AppendLog("Stop requested."); AppendLog("Stop requested.");
} }
@ -842,13 +896,11 @@ public partial class MainWindowViewModel(
List<SelectableUserViewModel> selectedUsers = AvailableUsers.Where(user => user.IsSelected).ToList(); List<SelectableUserViewModel> selectedUsers = AvailableUsers.Where(user => user.IsSelected).ToList();
if (!downloadPurchasedTabOnly && selectedUsers.Count == 0) if (!downloadPurchasedTabOnly && selectedUsers.Count == 0)
{ {
StatusMessage = "Select at least one user before downloading.";
return; return;
} }
if (downloadPurchasedTabOnly && _allUsers.Count == 0) if (downloadPurchasedTabOnly && _allUsers.Count == 0)
{ {
StatusMessage = "No users are loaded. Refresh users and retry.";
return; return;
} }
@ -868,9 +920,6 @@ public partial class MainWindowViewModel(
AppendLog(downloadPurchasedTabOnly AppendLog(downloadPurchasedTabOnly
? "Starting Purchased Tab download." ? "Starting Purchased Tab download."
: $"Starting download for {selectedUsers.Count} users."); : $"Starting download for {selectedUsers.Count} users.");
StatusMessage = downloadPurchasedTabOnly
? "Starting Purchased Tab download..."
: $"Starting download for {selectedUsers.Count} users...";
// Show progress bar immediately with indeterminate state // Show progress bar immediately with indeterminate state
StartDownloadProgress( StartDownloadProgress(
@ -915,19 +964,14 @@ public partial class MainWindowViewModel(
ThrowIfStopRequested(); ThrowIfStopRequested();
eventHandler.OnScrapeComplete(DateTime.Now - start); eventHandler.OnScrapeComplete(DateTime.Now - start);
StatusMessage = downloadPurchasedTabOnly
? "Purchased Tab download completed."
: "Download run completed.";
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
StatusMessage = "Operation canceled.";
AppendLog("Operation canceled."); AppendLog("Operation canceled.");
} }
catch (Exception ex) catch (Exception ex)
{ {
AppendLog($"Download failed: {ex.Message}"); AppendLog($"Download failed: {ex.Message}");
StatusMessage = "Download failed. Check logs.";
} }
finally finally
{ {
@ -990,11 +1034,27 @@ public partial class MainWindowViewModel(
!IsDownloading && !IsDownloading &&
!IsBrowserLoginInProgress; !IsBrowserLoginInProgress;
private bool CanOpenManualAuthScreen() =>
CurrentScreen == AppScreen.Auth &&
!IsDownloading &&
!IsBrowserLoginInProgress;
private bool CanGoBackToAuthScreen() =>
CurrentScreen == AppScreen.ManualAuth &&
!IsDownloading &&
!IsManualAuthValidationInProgress;
private bool CanContinueWithManualAuth() =>
CurrentScreen == AppScreen.ManualAuth &&
!IsDownloading &&
!IsManualAuthValidationInProgress;
partial void OnCurrentScreenChanged(AppScreen value) partial void OnCurrentScreenChanged(AppScreen value)
{ {
OnPropertyChanged(nameof(IsLoadingScreen)); OnPropertyChanged(nameof(IsLoadingScreen));
OnPropertyChanged(nameof(IsConfigScreen)); OnPropertyChanged(nameof(IsConfigScreen));
OnPropertyChanged(nameof(IsAuthScreen)); OnPropertyChanged(nameof(IsAuthScreen));
OnPropertyChanged(nameof(IsManualAuthScreen));
OnPropertyChanged(nameof(IsUserSelectionScreen)); OnPropertyChanged(nameof(IsUserSelectionScreen));
OnPropertyChanged(nameof(IsErrorScreen)); OnPropertyChanged(nameof(IsErrorScreen));
OnPropertyChanged(nameof(SelectedUsersSummary)); OnPropertyChanged(nameof(SelectedUsersSummary));
@ -1007,6 +1067,9 @@ public partial class MainWindowViewModel(
LogoutCommand.NotifyCanExecuteChanged(); LogoutCommand.NotifyCanExecuteChanged();
EditConfigCommand.NotifyCanExecuteChanged(); EditConfigCommand.NotifyCanExecuteChanged();
StartBrowserLoginCommand.NotifyCanExecuteChanged(); StartBrowserLoginCommand.NotifyCanExecuteChanged();
OpenManualAuthScreenCommand.NotifyCanExecuteChanged();
GoBackToAuthScreenCommand.NotifyCanExecuteChanged();
ContinueWithManualAuthCommand.NotifyCanExecuteChanged();
OpenSinglePostOrMessageModalCommand.NotifyCanExecuteChanged(); OpenSinglePostOrMessageModalCommand.NotifyCanExecuteChanged();
SubmitSinglePostOrMessageCommand.NotifyCanExecuteChanged(); SubmitSinglePostOrMessageCommand.NotifyCanExecuteChanged();
} }
@ -1021,6 +1084,9 @@ public partial class MainWindowViewModel(
RefreshIgnoredUsersListsCommand.NotifyCanExecuteChanged(); RefreshIgnoredUsersListsCommand.NotifyCanExecuteChanged();
LogoutCommand.NotifyCanExecuteChanged(); LogoutCommand.NotifyCanExecuteChanged();
StartBrowserLoginCommand.NotifyCanExecuteChanged(); StartBrowserLoginCommand.NotifyCanExecuteChanged();
OpenManualAuthScreenCommand.NotifyCanExecuteChanged();
GoBackToAuthScreenCommand.NotifyCanExecuteChanged();
ContinueWithManualAuthCommand.NotifyCanExecuteChanged();
OpenSinglePostOrMessageModalCommand.NotifyCanExecuteChanged(); OpenSinglePostOrMessageModalCommand.NotifyCanExecuteChanged();
SubmitSinglePostOrMessageCommand.NotifyCanExecuteChanged(); SubmitSinglePostOrMessageCommand.NotifyCanExecuteChanged();
} }
@ -1093,7 +1159,6 @@ public partial class MainWindowViewModel(
ConfigScreenMessage = ConfigScreenMessage =
"config.conf is invalid. Update all fields below and save to continue."; "config.conf is invalid. Update all fields below and save to continue.";
CurrentScreen = AppScreen.Config; CurrentScreen = AppScreen.Config;
StatusMessage = ConfigScreenMessage;
return; return;
} }
@ -1114,7 +1179,6 @@ public partial class MainWindowViewModel(
{ {
if (_allUsers.Count == 0) if (_allUsers.Count == 0)
{ {
StatusMessage = "No users are loaded. Refresh users and retry.";
return; return;
} }
@ -1135,7 +1199,6 @@ public partial class MainWindowViewModel(
DateTime start = DateTime.Now; DateTime start = DateTime.Now;
string label = request.Type == SingleDownloadType.Post ? "single post" : "single paid message"; string label = request.Type == SingleDownloadType.Post ? "single post" : "single paid message";
AppendLog($"Starting {label} download from URL."); AppendLog($"Starting {label} download from URL.");
StatusMessage = $"Starting {label} download...";
StartDownloadProgress($"Initializing {label} download...", 0, false); StartDownloadProgress($"Initializing {label} download...", 0, false);
@ -1155,9 +1218,8 @@ public partial class MainWindowViewModel(
{ {
if (!_allUsers.TryGetValue(request.Username, out long userId)) if (!_allUsers.TryGetValue(request.Username, out long userId))
{ {
StatusMessage = AppendLog(
$"Creator '{request.Username}' is not in loaded users. Refresh users and ensure you are subscribed."; $"Creator '{request.Username}' is not in loaded users. Refresh users and ensure you are subscribed.");
AppendLog(StatusMessage);
return; return;
} }
@ -1179,8 +1241,7 @@ public partial class MainWindowViewModel(
string? resolvedUsername = await downloadOrchestrationService.ResolveUsernameAsync(userId); string? resolvedUsername = await downloadOrchestrationService.ResolveUsernameAsync(userId);
if (string.IsNullOrWhiteSpace(resolvedUsername)) if (string.IsNullOrWhiteSpace(resolvedUsername))
{ {
StatusMessage = $"Could not resolve username for user ID {userId}."; AppendLog($"Could not resolve username for user ID {userId}.");
AppendLog(StatusMessage);
return; return;
} }
@ -1205,19 +1266,14 @@ public partial class MainWindowViewModel(
ThrowIfStopRequested(); ThrowIfStopRequested();
eventHandler.OnScrapeComplete(DateTime.Now - start); eventHandler.OnScrapeComplete(DateTime.Now - start);
StatusMessage = request.Type == SingleDownloadType.Post
? "Single post download completed."
: "Single paid message download completed.";
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
StatusMessage = "Operation canceled.";
AppendLog("Operation canceled."); AppendLog("Operation canceled.");
} }
catch (Exception ex) catch (Exception ex)
{ {
AppendLog($"Single item download failed: {ex.Message}"); AppendLog($"Single item download failed: {ex.Message}");
StatusMessage = "Single item download failed. Check logs.";
} }
finally finally
{ {
@ -1275,7 +1331,6 @@ public partial class MainWindowViewModel(
if (!confirmed) if (!confirmed)
{ {
StatusMessage = "Download canceled.";
AppendLog("Download canceled after missing CDM keys warning."); AppendLog("Download canceled after missing CDM keys warning.");
} }
@ -1354,17 +1409,11 @@ public partial class MainWindowViewModel(
bool hasValidAuth = await TryLoadAndValidateExistingAuthAsync(logAuthenticationMessage); bool hasValidAuth = await TryLoadAndValidateExistingAuthAsync(logAuthenticationMessage);
if (!hasValidAuth) if (!hasValidAuth)
{ {
if (configService.CurrentConfig.DisableBrowserAuth) IsManualAuthValidationInProgress = false;
{ ManualAuthScreenMessage = string.Empty;
ShowError(
"Authentication is missing or invalid and browser auth is disabled. Enable browser auth in config or provide a valid auth.json.");
return;
}
AuthScreenMessage = AuthScreenMessage =
"Authentication is required. Click 'Login with Browser' and complete the OnlyFans login flow."; "OF DL needs access to your OnlyFans account.\nAn included web browser can be used to sign-in, or you can create an \"auth.json\" file manually.";
CurrentScreen = AppScreen.Auth; CurrentScreen = AppScreen.Auth;
StatusMessage = "Authentication required.";
return; return;
} }
@ -1401,7 +1450,6 @@ public partial class MainWindowViewModel(
configService.CurrentConfig.FFmpegPath, configService.CurrentConfig.FFmpegPath,
"FFmpeg"); "FFmpeg");
CurrentScreen = AppScreen.Config; CurrentScreen = AppScreen.Config;
StatusMessage = ConfigScreenMessage;
return false; return false;
} }
@ -1418,7 +1466,6 @@ public partial class MainWindowViewModel(
configService.CurrentConfig.FFprobePath, configService.CurrentConfig.FFprobePath,
"FFprobe"); "FFprobe");
CurrentScreen = AppScreen.Config; CurrentScreen = AppScreen.Config;
StatusMessage = ConfigScreenMessage;
return false; return false;
} }
@ -1442,14 +1489,13 @@ public partial class MainWindowViewModel(
private async Task<bool> TryLoadAndValidateExistingAuthAsync(bool logAuthenticationMessage) private async Task<bool> TryLoadAndValidateExistingAuthAsync(bool logAuthenticationMessage)
{ {
bool loadedFromFile = await authService.LoadFromFileAsync(); bool loadedFromFile = await authService.LoadFromFileAsync();
if (!loadedFromFile) if (loadedFromFile)
{ {
IsAuthenticated = false; return await ValidateCurrentAuthAsync();
AppendLog("No valid auth.json found.");
return false;
} }
return await ValidateCurrentAuthAsync(logAuthenticationMessage); IsAuthenticated = false;
return false;
} }
private bool ValidateConfiguredToolPathsOnStartup() private bool ValidateConfiguredToolPathsOnStartup()
@ -1479,11 +1525,10 @@ public partial class MainWindowViewModel(
ConfigScreenMessage = "Configuration has invalid FFmpeg/FFprobe path values. Fix and save to continue."; ConfigScreenMessage = "Configuration has invalid FFmpeg/FFprobe path values. Fix and save to continue.";
CurrentScreen = AppScreen.Config; CurrentScreen = AppScreen.Config;
StatusMessage = ConfigScreenMessage;
return false; return false;
} }
private async Task<bool> ValidateCurrentAuthAsync(bool logAuthenticationMessage) private async Task<bool> ValidateCurrentAuthAsync()
{ {
authService.ValidateCookieString(); authService.ValidateCookieString();
UserEntities.User? user = await authService.ValidateAuthAsync(); UserEntities.User? user = await authService.ValidateAuthAsync();
@ -1491,35 +1536,13 @@ public partial class MainWindowViewModel(
{ {
authService.CurrentAuth = null; authService.CurrentAuth = null;
IsAuthenticated = false; IsAuthenticated = false;
if (File.Exists("auth.json") && !configService.CurrentConfig.DisableBrowserAuth)
{
File.Delete("auth.json");
}
AppendLog("Auth validation failed.");
return false; return false;
} }
string displayName = !string.IsNullOrWhiteSpace(user.Name) ? user.Name : "Unknown Name"; string displayName = !string.IsNullOrWhiteSpace(user.Name) ? user.Name : "Unknown Name";
string displayUsername = !string.IsNullOrWhiteSpace(user.Username) ? user.Username : "Unknown Username"; string displayUsername = !string.IsNullOrWhiteSpace(user.Username) ? user.Username : "Unknown Username";
if (HidePrivateInfo) AuthenticatedUserDisplay = HidePrivateInfo ? "[Hidden for Privacy]" : $"{displayName} ({displayUsername})";
{
AuthenticatedUserDisplay = "[Hidden for Privacy]";
if (logAuthenticationMessage)
{
AppendLog("Authenticated as [Hidden for Privacy].");
}
}
else
{
AuthenticatedUserDisplay = $"{displayName} ({displayUsername})";
if (logAuthenticationMessage)
{
AppendLog($"Authenticated as {AuthenticatedUserDisplay}.");
}
}
IsAuthenticated = true; IsAuthenticated = true;
return true; return true;
} }
@ -1564,8 +1587,7 @@ public partial class MainWindowViewModel(
} }
CurrentScreen = AppScreen.UserSelection; CurrentScreen = AppScreen.UserSelection;
StatusMessage = $"Loaded {_allUsers.Count} users and {_allLists.Count} lists."; AppendLog($"Loaded {_allUsers.Count} users and {_allLists.Count} lists.");
AppendLog(StatusMessage);
DownloadSelectedCommand.NotifyCanExecuteChanged(); DownloadSelectedCommand.NotifyCanExecuteChanged();
DownloadPurchasedTabCommand.NotifyCanExecuteChanged(); DownloadPurchasedTabCommand.NotifyCanExecuteChanged();
SelectUsersFromListCommand.NotifyCanExecuteChanged(); SelectUsersFromListCommand.NotifyCanExecuteChanged();
@ -2032,14 +2054,12 @@ public partial class MainWindowViewModel(
private void SetLoading(string message) private void SetLoading(string message)
{ {
LoadingMessage = message; LoadingMessage = message;
StatusMessage = message;
CurrentScreen = AppScreen.Loading; CurrentScreen = AppScreen.Loading;
} }
private void ShowError(string message) private void ShowError(string message)
{ {
ErrorMessage = message; ErrorMessage = message;
StatusMessage = message;
CurrentScreen = AppScreen.Error; CurrentScreen = AppScreen.Error;
AppendLog(message); AppendLog(message);
} }

View File

@ -71,6 +71,20 @@
<Setter Property="RenderTransform" Value="scale(0.98)" /> <Setter Property="RenderTransform" Value="scale(0.98)" />
</Style> </Style>
<Style Selector="Button.link">
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Padding" Value="0" />
<Setter Property="Foreground" Value="{DynamicResource PrimaryButtonBackgroundBrush}" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Cursor" Value="Hand" />
<Setter Property="HorizontalAlignment" Value="Left" />
</Style>
<Style Selector="Button.link:pointerover">
<Setter Property="Foreground" Value="{DynamicResource PrimaryButtonBackgroundHoverBrush}" />
</Style>
<Style Selector="TextBox.fileNameOverlayInput:not(:empty)"> <Style Selector="TextBox.fileNameOverlayInput:not(:empty)">
<Setter Property="Foreground" Value="Transparent" /> <Setter Property="Foreground" Value="Transparent" />
</Style> </Style>
@ -1011,6 +1025,48 @@
Classes="primary" Classes="primary"
IsEnabled="{Binding !IsBrowserLoginInProgress}" IsEnabled="{Binding !IsBrowserLoginInProgress}"
Command="{Binding StartBrowserLoginCommand}" /> Command="{Binding StartBrowserLoginCommand}" />
<Button Content="Manual Authentication"
Classes="secondary"
IsEnabled="{Binding !IsBrowserLoginInProgress}"
Command="{Binding OpenManualAuthScreenCommand}" />
</StackPanel>
</StackPanel>
<StackPanel IsVisible="{Binding IsManualAuthScreen}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Width="760"
Spacing="14">
<TextBlock HorizontalAlignment="Center"
FontSize="24"
FontWeight="SemiBold"
Text="Manual Authentication" />
<StackPanel HorizontalAlignment="Center" Spacing="8">
<TextBlock TextWrapping="Wrap"
TextAlignment="Center"
HorizontalAlignment="Center"
Foreground="{DynamicResource TextSecondaryBrush}"
Text="{Binding ManualAuthInstructionsText}" />
<Button Classes="link"
HorizontalAlignment="Center"
Content="https://docs.ofdl.tools/config/auth/#legacy-methods"
Click="OnAuthLegacyMethodsClick" />
</StackPanel>
<TextBlock TextWrapping="Wrap"
TextAlignment="Center"
HorizontalAlignment="Center"
Text="{Binding ManualAuthScreenMessage}" />
<StackPanel Orientation="Horizontal" Spacing="10" HorizontalAlignment="Center">
<Button Content="Go Back"
Classes="secondary"
Command="{Binding GoBackToAuthScreenCommand}" />
<Button Content="Continue"
Classes="primary"
Command="{Binding ContinueWithManualAuthCommand}" />
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>

View File

@ -196,6 +196,19 @@ public partial class MainWindow : Window
} }
} }
private async void OnAuthLegacyMethodsClick(object? sender, RoutedEventArgs e)
{
try
{
await WebLinkHelper.OpenOrCopyAsync(this, Constants.LegacyAuthDocumentationUrl,
dockerFeedbackAsync: ShowCopyToastAsync);
}
catch (Exception exception)
{
Log.Error(exception, "Failed to handle link click event. {ErrorMessage}", exception.Message);
}
}
private void OnFaqClick(object? sender, RoutedEventArgs e) private void OnFaqClick(object? sender, RoutedEventArgs e)
{ {
FaqWindow faqWindow = new() { WindowStartupLocation = WindowStartupLocation.CenterOwner }; FaqWindow faqWindow = new() { WindowStartupLocation = WindowStartupLocation.CenterOwner };