forked from sim0n00ps/OF-DL
Update web link handling to copy links to clipboard when click inside docker
This commit is contained in:
parent
4d3ae0e19a
commit
2dcb9a3753
@ -24,6 +24,7 @@ most important change points.
|
|||||||
- `OF DL/Program.cs` is the single entrypoint and routes between GUI (default) and CLI (`--cli`).
|
- `OF DL/Program.cs` is the single entrypoint and routes between GUI (default) and CLI (`--cli`).
|
||||||
- `OF DL/CLI/` contains Spectre.Console UI helpers and progress reporting (CLI-only).
|
- `OF DL/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/` 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/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,
|
- `OF DL.Core/Models/` holds configuration, auth, API request/response models, downloads/startup results, DTOs,
|
||||||
entities, and mapping helpers.
|
entities, and mapping helpers.
|
||||||
|
|||||||
@ -2,6 +2,10 @@ namespace OF_DL.Helpers;
|
|||||||
|
|
||||||
public static class Constants
|
public static class Constants
|
||||||
{
|
{
|
||||||
|
public const string DiscordInviteUrl = "https://discord.com/invite/6bUW8EJ53j";
|
||||||
|
|
||||||
|
public const string DocumentationUrl = "https://docs.ofdl.tools/";
|
||||||
|
|
||||||
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;
|
||||||
|
|||||||
92
OF DL.Gui/Helpers/WebLinkHelper.cs
Normal file
92
OF DL.Gui/Helpers/WebLinkHelper.cs
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using OF_DL.Helpers;
|
||||||
|
using Serilog;
|
||||||
|
|
||||||
|
namespace OF_DL.Gui.Helpers;
|
||||||
|
|
||||||
|
public static class WebLinkHelper
|
||||||
|
{
|
||||||
|
private const string CopiedToClipboardMessage = "Copied to clipboard";
|
||||||
|
|
||||||
|
public static async Task OpenOrCopyAsync(
|
||||||
|
Window owner,
|
||||||
|
string url,
|
||||||
|
Control? toolTipTarget = null,
|
||||||
|
Func<string, Task>? dockerFeedbackAsync = null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (EnvironmentHelper.IsRunningInDocker())
|
||||||
|
{
|
||||||
|
TopLevel? topLevel = TopLevel.GetTopLevel(owner);
|
||||||
|
if (topLevel?.Clipboard != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await topLevel.Clipboard.SetTextAsync(url);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dockerFeedbackAsync != null)
|
||||||
|
{
|
||||||
|
await dockerFeedbackAsync(CopiedToClipboardMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toolTipTarget != null)
|
||||||
|
{
|
||||||
|
await ShowTemporaryTooltipAsync(toolTipTarget, CopiedToClipboardMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error("Failed to copy URL to clipboard. {ErrorMessage}", e.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
OpenExternalUrl(url);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error(e, "Failed to open external URL. {ErrorMessage}", e.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task ShowTemporaryTooltipAsync(
|
||||||
|
Control target,
|
||||||
|
string message,
|
||||||
|
int durationMilliseconds = 1500)
|
||||||
|
{
|
||||||
|
object? originalTip = ToolTip.GetTip(target);
|
||||||
|
|
||||||
|
ToolTip.SetTip(target, message);
|
||||||
|
ToolTip.SetIsOpen(target, true);
|
||||||
|
|
||||||
|
await Task.Delay(durationMilliseconds);
|
||||||
|
|
||||||
|
ToolTip.SetIsOpen(target, false);
|
||||||
|
ToolTip.SetTip(target, originalTip);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void OpenExternalUrl(string url)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ProcessStartInfo processStartInfo = new(url) { UseShellExecute = true };
|
||||||
|
Process.Start(processStartInfo);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Ignore browser launch failures to preserve prior behavior.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,7 +1,8 @@
|
|||||||
using System.Diagnostics;
|
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using Avalonia.Platform;
|
using Avalonia.Platform;
|
||||||
|
using OF_DL.Gui.Helpers;
|
||||||
|
using Serilog;
|
||||||
|
|
||||||
namespace OF_DL.Gui.Views;
|
namespace OF_DL.Gui.Views;
|
||||||
|
|
||||||
@ -37,26 +38,39 @@ public partial class AboutWindow : Window
|
|||||||
DataContext = this;
|
DataContext = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void OnOpenSourceCodeClick(object? sender, RoutedEventArgs e) =>
|
private async void OnOpenSourceCodeClick(object? sender, RoutedEventArgs e)
|
||||||
await OpenExternalUrlAsync(SourceCodeUrl);
|
|
||||||
|
|
||||||
private async void OnOpenFfmpegLicenseClick(object? sender, RoutedEventArgs e) =>
|
|
||||||
await OpenExternalUrlAsync(FfmpegLicenseUrl);
|
|
||||||
|
|
||||||
private async void OnOpenFfprobeLicenseClick(object? sender, RoutedEventArgs e) =>
|
|
||||||
await OpenExternalUrlAsync(FfprobeLicenseUrl);
|
|
||||||
|
|
||||||
private async Task OpenExternalUrlAsync(string url)
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ProcessStartInfo processStartInfo = new(url) { UseShellExecute = true };
|
await WebLinkHelper.OpenOrCopyAsync(this, SourceCodeUrl, sender as Control);
|
||||||
|
|
||||||
Process.Start(processStartInfo);
|
|
||||||
}
|
}
|
||||||
catch
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
await Task.CompletedTask;
|
Log.Error(exception, "Failed to handle link click event. {ErrorMessage}", exception.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void OnOpenFfmpegLicenseClick(object? sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await WebLinkHelper.OpenOrCopyAsync(this, FfmpegLicenseUrl, sender as Control);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Failed to handle link click event. {ErrorMessage}", exception.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void OnOpenFfprobeLicenseClick(object? sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await WebLinkHelper.OpenOrCopyAsync(this, FfprobeLicenseUrl, sender as Control);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Failed to handle link click event. {ErrorMessage}", exception.Message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,23 +1,18 @@
|
|||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Diagnostics;
|
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using Avalonia.Platform;
|
using Avalonia.Platform;
|
||||||
|
using OF_DL.Gui.Helpers;
|
||||||
using OF_DL.Helpers;
|
using OF_DL.Helpers;
|
||||||
|
using Serilog;
|
||||||
|
|
||||||
namespace OF_DL.Gui.Views;
|
namespace OF_DL.Gui.Views;
|
||||||
|
|
||||||
public class FaqLink
|
public class FaqLink(string label, string url)
|
||||||
{
|
{
|
||||||
public FaqLink(string label, string url)
|
public string Label { get; } = label;
|
||||||
{
|
|
||||||
Label = label;
|
|
||||||
Url = url;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Label { get; }
|
public string Url { get; } = url;
|
||||||
|
|
||||||
public string Url { get; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class FaqEntry
|
public class FaqEntry
|
||||||
@ -136,26 +131,19 @@ public partial class FaqWindow : Window
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async void OnLinkClick(object? sender, RoutedEventArgs e)
|
private async void OnLinkClick(object? sender, RoutedEventArgs e)
|
||||||
{
|
|
||||||
if (sender is not Button { CommandParameter: string url } || string.IsNullOrWhiteSpace(url))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await OpenExternalUrlAsync(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task OpenExternalUrlAsync(string url)
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ProcessStartInfo processStartInfo = new(url) { UseShellExecute = true };
|
if (sender is not Button { CommandParameter: string url } || string.IsNullOrWhiteSpace(url))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Process.Start(processStartInfo);
|
await WebLinkHelper.OpenOrCopyAsync(this, url, sender as Control);
|
||||||
}
|
}
|
||||||
catch
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
await Task.CompletedTask;
|
Log.Error(exception, "Failed to handle link click event. {ErrorMessage}", exception.Message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -144,6 +144,21 @@
|
|||||||
<Setter Property="CornerRadius" Value="8" />
|
<Setter Property="CornerRadius" Value="8" />
|
||||||
<Setter Property="BorderThickness" Value="1" />
|
<Setter Property="BorderThickness" Value="1" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="Border.copyToast">
|
||||||
|
<Setter Property="Background" Value="{DynamicResource PrimaryButtonBackgroundBrush}" />
|
||||||
|
<Setter Property="BorderBrush" Value="{DynamicResource SurfaceBorderBrush}" />
|
||||||
|
<Setter Property="BorderThickness" Value="1" />
|
||||||
|
<Setter Property="CornerRadius" Value="10" />
|
||||||
|
<Setter Property="Padding" Value="12,8" />
|
||||||
|
<Setter Property="Opacity" Value="0" />
|
||||||
|
<Setter Property="BoxShadow" Value="0 6 12 -4 #33000000, 0 2 4 -2 #26000000" />
|
||||||
|
<Setter Property="Transitions">
|
||||||
|
<Transitions>
|
||||||
|
<DoubleTransition Property="Opacity" Duration="0:0:0.18" />
|
||||||
|
</Transitions>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
</Window.Styles>
|
</Window.Styles>
|
||||||
|
|
||||||
<Grid RowDefinitions="Auto,Auto,*">
|
<Grid RowDefinitions="Auto,Auto,*">
|
||||||
@ -1491,5 +1506,20 @@
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
<Border x:Name="CopyToastBorder"
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.RowSpan="3"
|
||||||
|
Classes="copyToast"
|
||||||
|
IsVisible="False"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
VerticalAlignment="Bottom"
|
||||||
|
Margin="0,0,20,20"
|
||||||
|
ZIndex="2000">
|
||||||
|
<TextBlock x:Name="CopyToastTextBlock"
|
||||||
|
Foreground="{DynamicResource PrimaryButtonForegroundBrush}"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
Text="Copied to clipboard" />
|
||||||
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Window>
|
</Window>
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
using System.Diagnostics;
|
|
||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
@ -8,19 +7,20 @@ using Avalonia.Platform.Storage;
|
|||||||
using Avalonia.VisualTree;
|
using Avalonia.VisualTree;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
using OF_DL.Helpers;
|
using OF_DL.Helpers;
|
||||||
|
using OF_DL.Gui.Helpers;
|
||||||
using OF_DL.Gui.ViewModels;
|
using OF_DL.Gui.ViewModels;
|
||||||
|
using Serilog;
|
||||||
|
|
||||||
namespace OF_DL.Gui.Views;
|
namespace OF_DL.Gui.Views;
|
||||||
|
|
||||||
public partial class MainWindow : Window
|
public partial class MainWindow : Window
|
||||||
{
|
{
|
||||||
private const string DiscordInviteUrl = "https://discord.com/invite/6bUW8EJ53j";
|
|
||||||
private const string DocumentationUrl = "https://docs.ofdl.tools/";
|
|
||||||
private bool _hasInitialized;
|
private bool _hasInitialized;
|
||||||
private bool _activityLogAutoScroll = true;
|
private bool _activityLogAutoScroll = true;
|
||||||
private bool _activityLogUserInteracted;
|
private bool _activityLogUserInteracted;
|
||||||
private bool _isActivityLogProgrammaticScroll;
|
private bool _isActivityLogProgrammaticScroll;
|
||||||
private ScrollViewer? _activityLogScrollViewer;
|
private ScrollViewer? _activityLogScrollViewer;
|
||||||
|
private CancellationTokenSource? _copyToastCancellationTokenSource;
|
||||||
public MainWindowViewModel? ViewModel => DataContext as MainWindowViewModel;
|
public MainWindowViewModel? ViewModel => DataContext as MainWindowViewModel;
|
||||||
|
|
||||||
public MainWindow()
|
public MainWindow()
|
||||||
@ -68,6 +68,10 @@ public partial class MainWindow : Window
|
|||||||
|
|
||||||
ActivityLogListBox.PointerWheelChanged -= OnActivityLogPointerInteracted;
|
ActivityLogListBox.PointerWheelChanged -= OnActivityLogPointerInteracted;
|
||||||
ActivityLogListBox.PointerPressed -= OnActivityLogPointerInteracted;
|
ActivityLogListBox.PointerPressed -= OnActivityLogPointerInteracted;
|
||||||
|
|
||||||
|
_copyToastCancellationTokenSource?.Cancel();
|
||||||
|
_copyToastCancellationTokenSource?.Dispose();
|
||||||
|
_copyToastCancellationTokenSource = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void OnBrowseFfmpegPathClick(object? sender, RoutedEventArgs e)
|
private async void OnBrowseFfmpegPathClick(object? sender, RoutedEventArgs e)
|
||||||
@ -166,11 +170,31 @@ public partial class MainWindow : Window
|
|||||||
vm.SetDownloadPath(selectedFolder.Name);
|
vm.SetDownloadPath(selectedFolder.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void OnJoinDiscordClick(object? sender, RoutedEventArgs e) =>
|
private async void OnJoinDiscordClick(object? sender, RoutedEventArgs e)
|
||||||
await OpenExternalUrlAsync(DiscordInviteUrl);
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await WebLinkHelper.OpenOrCopyAsync(this, Constants.DiscordInviteUrl,
|
||||||
|
dockerFeedbackAsync: ShowCopyToastAsync);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Failed to handle link click event. {ErrorMessage}", exception.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async void OnDocumentationClick(object? sender, RoutedEventArgs e) =>
|
private async void OnDocumentationClick(object? sender, RoutedEventArgs e)
|
||||||
await OpenExternalUrlAsync(DocumentationUrl);
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await WebLinkHelper.OpenOrCopyAsync(this, Constants.DocumentationUrl,
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
@ -192,17 +216,45 @@ public partial class MainWindow : Window
|
|||||||
aboutWindow.Show(this);
|
aboutWindow.Show(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task OpenExternalUrlAsync(string url)
|
private async Task ShowCopyToastAsync(string message)
|
||||||
{
|
{
|
||||||
|
if (_copyToastCancellationTokenSource != null)
|
||||||
|
{
|
||||||
|
await _copyToastCancellationTokenSource.CancelAsync();
|
||||||
|
_copyToastCancellationTokenSource.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
CancellationTokenSource cancellationTokenSource = new();
|
||||||
|
_copyToastCancellationTokenSource = cancellationTokenSource;
|
||||||
|
|
||||||
|
CopyToastTextBlock.Text = message;
|
||||||
|
CopyToastBorder.Opacity = 0;
|
||||||
|
CopyToastBorder.IsVisible = true;
|
||||||
|
CopyToastBorder.Opacity = 1;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ProcessStartInfo processStartInfo = new(url) { UseShellExecute = true };
|
await Task.Delay(TimeSpan.FromSeconds(2), cancellationTokenSource.Token);
|
||||||
|
|
||||||
Process.Start(processStartInfo);
|
|
||||||
}
|
}
|
||||||
catch
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
await Task.CompletedTask;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CopyToastBorder.Opacity = 0;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await Task.Delay(TimeSpan.FromMilliseconds(180), cancellationTokenSource.Token);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cancellationTokenSource.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
CopyToastBorder.IsVisible = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -255,7 +307,8 @@ public partial class MainWindow : Window
|
|||||||
Dispatcher.UIThread.Post(ScrollActivityLogToBottom);
|
Dispatcher.UIThread.Post(ScrollActivityLogToBottom);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnActivityLogPointerInteracted(object? sender, PointerEventArgs e) => _activityLogUserInteracted = true;
|
private void OnActivityLogPointerInteracted(object? sender, PointerEventArgs e) =>
|
||||||
|
_activityLogUserInteracted = true;
|
||||||
|
|
||||||
private void OnActivityLogScrollChanged(object? sender, ScrollChangedEventArgs e)
|
private void OnActivityLogScrollChanged(object? sender, ScrollChangedEventArgs e)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,23 +1,27 @@
|
|||||||
# Running the Program
|
# Running the Program
|
||||||
|
|
||||||
Once you are happy you have filled everything in [auth.json](/config/auth) correctly, you can double click OF-DL.exe and you should see a command prompt window appear, it should look something like this:
|
Once you are happy you have filled everything in [auth.json](/config/auth) correctly, you can double click OF-DL.exe and
|
||||||
|
you should see a command prompt window appear, it should look something like this:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
It should locate `config.conf`, `rules.json`, FFmpeg, and FFprobe successfully. If anything doesn't get located
|
It should locate `config.conf`, `rules.json`, FFmpeg, and FFprobe successfully. If anything doesn't get located
|
||||||
successfully, then make sure the files exist or the path is correct.
|
successfully, then make sure the files exist or the path is correct.
|
||||||
|
|
||||||
OF-DL will open a new window, if needed, to allow you to log into your OnlyFans account. The window will automatically close once
|
OF-DL will open a new window, if needed, to allow you to log into your OnlyFans account. The window will automatically
|
||||||
|
close once
|
||||||
the authorization process has finished. If the auth info is correct then you should see a message in green text
|
the authorization process has finished. If the auth info is correct then you should see a message in green text
|
||||||
`Logged In successfully as {Your Username} {Your User Id}`. However, if the authorization has failed,
|
`Logged In successfully as {Your Username} {Your User Id}`. However, if the authorization has failed,
|
||||||
then a message in red text will appear `Auth failed, please check the values in auth.json are correct, press any key to exit.`
|
then a message in red text will appear
|
||||||
This means you need to go back and fill in the `auth.json` file again, this will usually indicate that your `user-agent` has changed or you need to re-copy your `sess` value.
|
`Auth failed, please check the values in auth.json are correct, press any key to exit.`
|
||||||
|
This means you need to go back and fill in the `auth.json` file again, this will usually indicate that your `user-agent`
|
||||||
|
has changed or you need to re-copy your `sess` value.
|
||||||
|
|
||||||
In GUI mode, the **Help** menu includes:
|
In GUI mode, the **Help** menu includes:
|
||||||
|
|
||||||
- **Join Discord** (opens the OF-DL Discord invite)
|
- **Join Discord** (opens the OF-DL Discord invite; in Docker, copies the link to clipboard)
|
||||||
- **FAQ** (opens the FAQ window; content coming soon)
|
- **FAQ** (opens the FAQ window; content coming soon)
|
||||||
- **Documentation** (opens https://docs.ofdl.tools/)
|
- **Documentation** (opens https://docs.ofdl.tools/; in Docker, copies the link to clipboard)
|
||||||
- **About** (shows version details and project/license links)
|
- **About** (shows version details and project/license links)
|
||||||
|
|
||||||
In GUI mode, the main download screen includes:
|
In GUI mode, the main download screen includes:
|
||||||
@ -33,30 +37,40 @@ For **Download Single Post/Message** in GUI:
|
|||||||
find the message, click `...`, and choose **Copy link to message**.
|
find the message, click `...`, and choose **Copy link to message**.
|
||||||
- Other message types cannot be downloaded individually by URL; scrape all messages for that creator instead.
|
- Other message types cannot be downloaded individually by URL; scrape all messages for that creator instead.
|
||||||
|
|
||||||
If you're logged in successfully then you will be greeted with a selection prompt. To navigate the menu the can use the ↑ & ↓ arrows and press `enter` to choose that option.
|
If you're logged in successfully then you will be greeted with a selection prompt. To navigate the menu the can use
|
||||||
|
the ↑ & ↓ arrows and press `enter` to choose that option.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
The `Select All` option will go through every account you are currently subscribed to and grab all of the media from the users.
|
The `Select All` option will go through every account you are currently subscribed to and grab all of the media from the
|
||||||
|
users.
|
||||||
|
|
||||||
The `List` option will show you all the lists you have created on OnlyFans and you can then select 1 or more lists to download the content of the users within those lists.
|
The `List` option will show you all the lists you have created on OnlyFans and you can then select 1 or more lists to
|
||||||
|
download the content of the users within those lists.
|
||||||
|
|
||||||
The `Custom` option allows you to select 1 or more accounts you want to scrape media from so if you only want to get media from a select number of accounts then you can do that.
|
The `Custom` option allows you to select 1 or more accounts you want to scrape media from so if you only want to get
|
||||||
To navigate the menu the can use the ↑ & ↓ arrows. You can also press keys A-Z on the keyboard whilst in the menu to easily navigate the menu and for example
|
media from a select number of accounts then you can do that.
|
||||||
pressing the letter 'c' on the keyboard will highlight the first user in the list whose username starts with the letter 'c'. To select/deselect an account,
|
To navigate the menu the can use the ↑ & ↓ arrows. You can also press keys A-Z on the keyboard whilst in the menu to
|
||||||
|
easily navigate the menu and for example
|
||||||
|
pressing the letter 'c' on the keyboard will highlight the first user in the list whose username starts with the
|
||||||
|
letter 'c'. To select/deselect an account,
|
||||||
press the space key, and after you are happy with your selection(s), press the enter key to start downloading.
|
press the space key, and after you are happy with your selection(s), press the enter key to start downloading.
|
||||||
|
|
||||||
The `Download Single Post` allows you to download a post from a URL, to get this URL go to any post and press the 3 dots, Copy link to post.
|
The `Download Single Post` allows you to download a post from a URL, to get this URL go to any post and press the 3
|
||||||
|
dots, Copy link to post.
|
||||||
|
|
||||||
The `Download Single Message` allows you to download a message from a URL, to get this URL go to any message in the **purchased tab** and press the 3 dots, Copy link to message.
|
The `Download Single Message` allows you to download a message from a URL, to get this URL go to any message in the *
|
||||||
|
*purchased tab** and press the 3 dots, Copy link to message.
|
||||||
|
|
||||||
The `Download Purchased Tab` option will download all the media from the purchased tab in OnlyFans.
|
The `Download Purchased Tab` option will download all the media from the purchased tab in OnlyFans.
|
||||||
|
|
||||||
The `Edit config.json` option allows you to change the config from within the program.
|
The `Edit config.json` option allows you to change the config from within the program.
|
||||||
|
|
||||||
The `Change logging level` option allows you to change the logging level that the program uses when writing logs to files in the `logs` folder.
|
The `Change logging level` option allows you to change the logging level that the program uses when writing logs to
|
||||||
|
files in the `logs` folder.
|
||||||
|
|
||||||
The `Logout and Exit` option allows you to remove your authentication from OF-DL. This is useful if you use multiple OnlyFans accounts.
|
The `Logout and Exit` option allows you to remove your authentication from OF-DL. This is useful if you use multiple
|
||||||
|
OnlyFans accounts.
|
||||||
|
|
||||||
After you have made your selection the content should start downloading. Content is downloaded in this order:
|
After you have made your selection the content should start downloading. Content is downloaded in this order:
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user