From dea14d35af8aa44116cb3f567a022d39d408e57a Mon Sep 17 00:00:00 2001 From: Andy <88590076+AAndyProgram@users.noreply.github.com> Date: Tue, 25 Jun 2024 11:46:33 +0300 Subject: [PATCH] 2024.6.25.0 API.OnlyFans: new dynamic rules updating algo API.Instagram: update settings Feed: add ability to set the last session as the current one; wrong marking data as saved posts when moving a file --- Changelog.md | 14 + ProgramScreenshots/SettingsSiteOnlyFans.png | Bin 32243 -> 29620 bytes .../SettingsSiteOnlyFansAdditional.png | Bin 0 -> 25896 bytes SCrawler.YouTube/My Project/AssemblyInfo.vb | 4 +- .../Objects/YouTubeMediaContainerBase.vb | 2 +- .../My Project/AssemblyInfo.vb | 4 +- SCrawler/API/Instagram/SiteSettings.vb | 7 +- SCrawler/API/OnlyFans/Declarations.vb | 11 +- SCrawler/API/OnlyFans/DynamicRules.txt | 6 + SCrawler/API/OnlyFans/DynamicRulesAll.txt | 9 + SCrawler/API/OnlyFans/DynamicRulesEnv.vb | 744 ++++++++++++++++++ SCrawler/API/OnlyFans/OFResources.Designer.vb | 24 + SCrawler/API/OnlyFans/OFResources.resx | 6 + .../API/OnlyFans/OFScraperConfigPattern.json | 120 ++- .../OFScraperConfigPatternConstants.txt | 2 + .../OnlyFansAdvancedSettingsForm.Designer.vb | 361 +++++++++ .../OnlyFansAdvancedSettingsForm.resx | 186 +++++ .../OnlyFans/OnlyFansAdvancedSettingsForm.vb | 165 ++++ SCrawler/API/OnlyFans/SiteSettings.vb | 58 +- SCrawler/API/OnlyFans/UserData.vb | 116 +-- .../Feed/DownloadFeedForm.Designer.vb | 77 +- SCrawler/Download/Feed/DownloadFeedForm.resx | 6 +- SCrawler/Download/Feed/DownloadFeedForm.vb | 39 +- SCrawler/Download/Feed/FeedSpecial.vb | 6 +- SCrawler/MainFrameObjects.vb | 1 + SCrawler/MainMod.vb | 3 + SCrawler/My Project/AssemblyInfo.vb | 4 +- SCrawler/SCrawler.vbproj | 13 + 28 files changed, 1736 insertions(+), 252 deletions(-) create mode 100644 ProgramScreenshots/SettingsSiteOnlyFansAdditional.png create mode 100644 SCrawler/API/OnlyFans/DynamicRules.txt create mode 100644 SCrawler/API/OnlyFans/DynamicRulesAll.txt create mode 100644 SCrawler/API/OnlyFans/DynamicRulesEnv.vb create mode 100644 SCrawler/API/OnlyFans/OFScraperConfigPatternConstants.txt create mode 100644 SCrawler/API/OnlyFans/OnlyFansAdvancedSettingsForm.Designer.vb create mode 100644 SCrawler/API/OnlyFans/OnlyFansAdvancedSettingsForm.resx create mode 100644 SCrawler/API/OnlyFans/OnlyFansAdvancedSettingsForm.vb diff --git a/Changelog.md b/Changelog.md index 828d6d5..6c81421 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,17 @@ +# 2024.6.25.0 + +*2024-06-25* + +**ATTENTION! To support downloading of DRM protected videos (OnlyFans), please update OF-Scraper to version [3.10.7](https://github.com/datawhores/OF-Scraper/releases/tag/3.10.7) (download `zip`, not `exe`).** + +- Added + - OnlyFans: **new dynamic rules updating algorithm** + - Feed: ability to set the last session as the current one +- Updated + - gallery-dl up to version **1.27.1** +- Fixed + - Minor bugs + # 2024.6.10.0 *2024-06-10* diff --git a/ProgramScreenshots/SettingsSiteOnlyFans.png b/ProgramScreenshots/SettingsSiteOnlyFans.png index 7aa9a9c025549ee091e671ef25bb763cae4fc2f6..f0514a210c259db309b84124057a8b420989469c 100644 GIT binary patch literal 29620 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 R1H;h2X{cz!3zaY5{(rK88W;co diff --git a/ProgramScreenshots/SettingsSiteOnlyFansAdditional.png b/ProgramScreenshots/SettingsSiteOnlyFansAdditional.png new file mode 100644 index 0000000000000000000000000000000000000000..3727d80942b868b7d6560ea2f7d0d21ce565ff1f GIT binary patch literal 25896 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 D&;-@r literal 0 HcmV?d00001 diff --git a/SCrawler.YouTube/My Project/AssemblyInfo.vb b/SCrawler.YouTube/My Project/AssemblyInfo.vb index de27cb4..a651acc 100644 --- a/SCrawler.YouTube/My Project/AssemblyInfo.vb +++ b/SCrawler.YouTube/My Project/AssemblyInfo.vb @@ -32,6 +32,6 @@ Imports System.Runtime.InteropServices ' by using the '*' as shown below: ' - - + + diff --git a/SCrawler.YouTube/Objects/YouTubeMediaContainerBase.vb b/SCrawler.YouTube/Objects/YouTubeMediaContainerBase.vb index bc8d2c5..7804a98 100644 --- a/SCrawler.YouTube/Objects/YouTubeMediaContainerBase.vb +++ b/SCrawler.YouTube/Objects/YouTubeMediaContainerBase.vb @@ -1194,7 +1194,7 @@ Namespace API.YouTube.Objects End With If PlaylistCount > 0 And Not CoverDownloaded And Not PlaylistID.IsEmptyString Then DownloadPlaylistCover(PlaylistID, File, UseCookies) - If prExists Then Progress.InformationTemporary = $"Download {MediaType}: post processing" + If prExists Then Progress.InformationTemporary = "Downloading: post processing" _ThumbnailFile = File _ThumbnailFile.Name &= "_thumb" _ThumbnailFile.Extension = "jpg" diff --git a/SCrawler.YouTubeDownloader/My Project/AssemblyInfo.vb b/SCrawler.YouTubeDownloader/My Project/AssemblyInfo.vb index 8779f05..ac7ce54 100644 --- a/SCrawler.YouTubeDownloader/My Project/AssemblyInfo.vb +++ b/SCrawler.YouTubeDownloader/My Project/AssemblyInfo.vb @@ -32,6 +32,6 @@ Imports System.Runtime.InteropServices ' by using the '*' as shown below: ' - - + + diff --git a/SCrawler/API/Instagram/SiteSettings.vb b/SCrawler/API/Instagram/SiteSettings.vb index 01ecf3e..11a0b46 100644 --- a/SCrawler/API/Instagram/SiteSettings.vb +++ b/SCrawler/API/Instagram/SiteSettings.vb @@ -449,8 +449,11 @@ Namespace API.Instagram If CInt(SettingsVersion.Value) < 2 Then SettingsVersion.Value = 2 HH_IG_WWW_CLAIM_UPDATE_INTERVAL.Value = 120 - HH_IG_WWW_CLAIM_USE_DEFAULT_ALGO.Value = 1 - HH_IG_WWW_CLAIM_RESET_EACH_TARGET.Value = 0 + HH_IG_WWW_CLAIM_ALWAYS_ZERO.Value = False + HH_IG_WWW_CLAIM_RESET_EACH_SESSION.Value = True + HH_IG_WWW_CLAIM_RESET_EACH_TARGET.Value = True + HH_IG_WWW_CLAIM_USE.Value = True + HH_IG_WWW_CLAIM_USE_DEFAULT_ALGO.Value = True End If MyBase.EndInit() End Sub diff --git a/SCrawler/API/OnlyFans/Declarations.vb b/SCrawler/API/OnlyFans/Declarations.vb index b6d6532..a7938f3 100644 --- a/SCrawler/API/OnlyFans/Declarations.vb +++ b/SCrawler/API/OnlyFans/Declarations.vb @@ -11,15 +11,6 @@ Namespace API.OnlyFans Friend Module Declarations Friend ReadOnly DateProvider As New ADateTime("O") Friend ReadOnly RegExPostID As RParams = RParams.DM("(?<=onlyfans\.com/)(\d+)", 0, EDP.ReturnValue) - Friend ReadOnly OFScraperConfigPatternFile As SFile = $"{SettingsFolderName}\OFScraperConfigPattern.json" - Friend Function CheckOFSConfig() As Boolean - If Not OFScraperConfigPatternFile.Exists Then - Dim t$ = Text.Encoding.UTF8.GetString(My.Resources.OFResources.OFScraperConfigPattern) - TextSaver.SaveTextToFile(t, OFScraperConfigPatternFile, True) - Return OFScraperConfigPatternFile.Exists - Else - Return True - End If - End Function + Friend Property Rules As DynamicRulesEnv End Module End Namespace \ No newline at end of file diff --git a/SCrawler/API/OnlyFans/DynamicRules.txt b/SCrawler/API/OnlyFans/DynamicRules.txt new file mode 100644 index 0000000..5dc3930 --- /dev/null +++ b/SCrawler/API/OnlyFans/DynamicRules.txt @@ -0,0 +1,6 @@ +https://github.com/datawhores/onlyfans-dynamic-rules/blob/main/dynamicRules.json +https://github.com/riley-access-labs/onlyfans-dynamic-rules-1/blob/main/dynamicRules.json +https://github.com/riley-access-labs/onlyfans-dynamic-rules-1/blob/patch-1/dynamicRules.json +https://github.com/DATAHOARDERS/dynamic-rules/blob/main/onlyfans.json +https://github.com/DIGITALCRIMINAL/dynamic-rules/blob/main/onlyfans.json +https://github.com/deviint/onlyfans-dynamic-rules/blob/main/dynamicRules.json \ No newline at end of file diff --git a/SCrawler/API/OnlyFans/DynamicRulesAll.txt b/SCrawler/API/OnlyFans/DynamicRulesAll.txt new file mode 100644 index 0000000..87d88c4 --- /dev/null +++ b/SCrawler/API/OnlyFans/DynamicRulesAll.txt @@ -0,0 +1,9 @@ +https://github.com/datawhores/onlyfans-dynamic-rules/blob/main/dynamicRules.json +https://github.com/riley-access-labs/onlyfans-dynamic-rules-1/blob/main/dynamicRules.json +https://github.com/riley-access-labs/onlyfans-dynamic-rules-1/blob/patch-1/dynamicRules.json +https://github.com/DATAHOARDERS/dynamic-rules/blob/main/onlyfans.json +https://github.com/DIGITALCRIMINAL/dynamic-rules/blob/main/onlyfans.json +https://github.com/deviint/onlyfans-dynamic-rules/blob/main/dynamicRules.json + +https://github.com/SneakyOvis/onlyfans-dynamic-rules/blob/main/rules.json +https://github.com/Growik/onlyfans-dynamic-rules/blob/main/rules.json \ No newline at end of file diff --git a/SCrawler/API/OnlyFans/DynamicRulesEnv.vb b/SCrawler/API/OnlyFans/DynamicRulesEnv.vb new file mode 100644 index 0000000..446e9c5 --- /dev/null +++ b/SCrawler/API/OnlyFans/DynamicRulesEnv.vb @@ -0,0 +1,744 @@ +' Copyright (C) Andy https://github.com/AAndyProgram +' This program is free software: you can redistribute it and/or modify +' it under the terms of the GNU General Public License as published by +' the Free Software Foundation, either version 3 of the License, or +' (at your option) any later version. +' +' This program is distributed in the hope that it will be useful, +' but WITHOUT ANY WARRANTY +Imports PersonalUtilities.Functions.XML +Imports PersonalUtilities.Functions.XML.Base +Imports PersonalUtilities.Functions.RegularExpressions +Imports PersonalUtilities.Tools +Imports PersonalUtilities.Tools.Web.Clients +Imports PersonalUtilities.Tools.Web.Clients.Base +Imports PersonalUtilities.Tools.Web.Documents.JSON +Imports System.Text.RegularExpressions +Namespace API.OnlyFans + Friend Structure DynamicRulesValue : Implements IComparable(Of DynamicRulesValue), IEquatable(Of DynamicRulesValue), IEContainerProvider +#Region "XML names" + Private Const Name_UrlRepo As String = "UrlRepo" + Private Const Name_UrlRaw As String = "UrlRaw" + Private Const Name_UrlLatestCommit As String = "UrlLatestCommit" + Private Const Name_UpdatedAt As String = "UpdatedAt" + Private Const Name_Broken As String = "Broken" + Private Const Name_Exists As String = "Exists" +#End Region +#Region "Declarations" + Friend UrlRepo As String + Friend UrlRaw As String + Friend UrlLatestCommit As String + Friend UpdatedAt As Date + Friend Broken As Boolean + Friend Exists As Boolean + Friend ReadOnly Property Valid As Boolean + Get + Return Not UrlRepo.IsEmptyString And Not UrlRaw.IsEmptyString + End Get + End Property +#End Region +#Region "Initializers" + Friend Sub New(ByVal e As EContainer) + UrlRepo = e.Value(Name_UrlRepo) + UrlRaw = e.Value(Name_UrlRaw) + UrlLatestCommit = e.Value(Name_UrlLatestCommit) + UpdatedAt = e.Value(Name_UpdatedAt).ToDateDef(Now.AddYears(-10)) + Broken = e.Value(Name_Broken).FromXML(Of Boolean)(False) + Exists = e.Value(Name_Exists).FromXML(Of Boolean)(True) + End Sub + Public Shared Widening Operator CType(ByVal e As EContainer) As DynamicRulesValue + Return New DynamicRulesValue(e) + End Operator + Public Shared Widening Operator CType(ByVal rule As DynamicRulesValue) As String + Return rule.ToString + End Operator +#End Region +#Region "Base functions" + Public Overrides Function GetHashCode() As Integer + Return ToString.GetHashCode + End Function + Public Overrides Function ToString() As String + Return UrlRaw + End Function +#End Region +#Region "IComparable Support" + Private Function CompareTo(ByVal Other As DynamicRulesValue) As Integer Implements IComparable(Of DynamicRulesValue).CompareTo + Return UpdatedAt.CompareTo(Other.UpdatedAt) * -1 + End Function +#End Region +#Region "IEquatable Support" + Public Overloads Overrides Function Equals(ByVal Obj As Object) As Boolean + If Not IsNothing(Obj) Then + If TypeOf Obj Is String Then + Dim _obj$ = CStr(Obj).StringTrim.StringToLower + Return UrlRepo = _obj Or UrlRaw = _obj + Else + Return Equals(DirectCast(Obj, DynamicRulesValue)) + End If + Else + Return False + End If + End Function + Friend Overloads Function Equals(ByVal Other As DynamicRulesValue) As Boolean Implements IEquatable(Of DynamicRulesValue).Equals + Return UrlRepo = Other.UrlRepo Or UrlRaw = Other.UrlRaw + End Function +#End Region +#Region "IEContainerProvider Support" + Private Function ToEContainer(Optional ByVal e As ErrorsDescriber = Nothing) As EContainer Implements IEContainerProvider.ToEContainer + Return New EContainer("Rule") From { + New EContainer(Name_UrlRepo, UrlRepo), + New EContainer(Name_UrlRaw, UrlRaw), + New EContainer(Name_UrlLatestCommit, UrlLatestCommit), + New EContainer(Name_UpdatedAt, UpdatedAt.ToStringDateDef), + New EContainer(Name_Broken, Broken.BoolToInteger), + New EContainer(Name_Exists, Exists.BoolToInteger) + } + End Function +#End Region + End Structure + Friend Class DynamicRulesEnv : Implements ICopier, IEnumerable(Of DynamicRulesValue), IMyEnumerator(Of DynamicRulesValue), IDisposable + Friend Enum Modes As Integer + List = 0 + Personal = 1 + End Enum +#Region "Constants" + Friend Const UpdateIntervalDefault As Integer = 1440 '60 * 24 + Friend Const DynamicRulesConfigNodeName_URL As String = "DYNAMIC_GENERIC_URL" + Friend Const DynamicRulesConfigNodeName_RULES As String = "DYNAMIC_RULE" + + Friend Const DynamicRulesConfig_Mode_NodeName As String = "dynamic-mode-default" + 'Friend Const DynamicRulesConfig_Mode_NodeValue As String = "generic" + + Friend Const DynamicRulesConfigNodeName_URL_CONST_NAME As String = "RULE_VALUE" +#End Region +#Region "XML names" + Private Const Name_LastUpdateTimeFile As String = "LastUpdateTimeFile" + Private Const Name_LastUpdateTimeRules As String = "LastUpdateTimeRules" + Private Const Name_ProtectFile As String = "ProtectFile" + Private Const Name_UpdateInterval As String = "UpdateInterval" + Private Const Name_Mode As String = "Mode" + Private Const Name_PersonalRule As String = "PersonalRule" + Private Const Name_RulesForceUpdateRequired As String = "RulesForceUpdateRequired" + Private Const Name_AddErrorsToLog As String = "AddErrorsToLog" + Private Const Name_ConfigLastDateUpdate As String = "ConfigLastDateUpdate" + Private Const Name_ConfigAutoUpdate As String = "ConfigAutoUpdate" + Private Const Name_RulesConfigManualMode As String = "RulesConfigManualMode" + Private Const Name_RulesUpdateConst As String = "RulesUpdateConst" + Private Const Name_RulesReplaceConfig As String = "RulesReplaceConfig" +#End Region +#Region "Declarations" + Private ReadOnly Rules As List(Of DynamicRulesValue) + Friend ReadOnly Property RulesConstants As Dictionary(Of String, String) +#Region "Regex patterns" + Private ReadOnly ReplacePattern_RepoToRaw As RParams + Private ReadOnly ReplacePattern_RawToRepo As RParams + Private ReadOnly ReplacePattern_JsonInfo As RParams + Private ReadOnly ConfigRulesExtract As RParams +#End Region +#Region "Dates" + Private LastUpdateTimeFile As Date = Now.AddYears(-1) + Private LastUpdateTimeRules As Date = Now.AddYears(-1) +#End Region +#Region "Files" + Friend ReadOnly OFScraperConfigPatternFile As SFile = $"{SettingsFolderName}\OFScraperConfigPattern.json" + Friend ReadOnly OFScraperConfigPatternFileConst As SFile = $"{SettingsFolderName}\OFScraperConfigPatternConstants.txt" + Friend ReadOnly Property AuthFile As New SFile($"{SettingsFolderName}\OnlyFans_Auth.json") + Private ReadOnly DynamicRulesFile As SFile + Private ReadOnly DynamicRulesXml As SFile + Private Shared ReadOnly Property DynamicRulesFileImpl As SFile + Get + Return $"{SettingsFolderName}\OnlyFansDynamicRules.txt" + End Get + End Property + Friend Shared Sub ValidateRulesFile() + Dim f As SFile = DynamicRulesFileImpl + If Not f.Exists Then TextSaver.SaveTextToFile(My.Resources.OFResources.DynamicRules, DynamicRulesFileImpl, True) + End Sub + Friend Property ProtectFile As Boolean = False +#End Region + Friend Property UpdateInterval As Integer = UpdateIntervalDefault + Friend Property Mode As Modes = Modes.List + Friend Property PersonalRule As String = String.Empty + Friend Property RulesForceUpdateRequired As Boolean = False + Friend Property RulesUpdateConst As Boolean = True + Friend Property RulesReplaceConfig As Boolean = True + Private ReadOnly Responser As New Responser With {.Accept = "application/json"} + Private ReadOnly RulesLinesComparer As New FComparer(Of String)(Function(x, y) x.StringToLower = y.StringToLower) + Private ReadOnly OFLOG As TextSaver + Private ReadOnly OFError As ErrorsDescriber + Friend Property AddErrorsToLog As Boolean = True + Friend Property NeedToSave As Boolean = False + Private ReadOnly Property ConfigAddress As DynamicRulesValue + Private ReadOnly Property ConfigConstAddress As DynamicRulesValue + Private Property ConfigLastDateUpdate As Date = Now.AddYears(-1) + Friend Property ConfigAutoUpdate As Boolean = True + Friend Property RulesConfigManualMode As Boolean = True +#End Region +#Region "Current, Item, Count" + Private _CurrentRule As DynamicRulesValue + Private _CurrentContainer As EContainer + Private _CurrentContainerRulesText As String = String.Empty + Friend ReadOnly Property CurrentRule As DynamicRulesValue + Get + Return _CurrentRule + End Get + End Property + Friend ReadOnly Property CurrentContainer As EContainer + Get + Return _CurrentContainer + End Get + End Property + Friend ReadOnly Property CurrentContainerRulesText As String + Get + If _CurrentContainerRulesText.IsEmptyString AndAlso AuthFile.Exists Then _ + _CurrentContainerRulesText = AuthFile.GetText(OFError).StringTrim + Return _CurrentContainerRulesText + End Get + End Property + Friend ReadOnly Property Exists As Boolean + Get + Return CurrentContainer.ListExists + End Get + End Property + Default Friend ReadOnly Property Item(ByVal Index As Integer) As DynamicRulesValue Implements IMyEnumerator(Of DynamicRulesValue).MyEnumeratorObject + Get + Return Rules(Index) + End Get + End Property + Friend ReadOnly Property Count As Integer Implements IMyEnumerator(Of DynamicRulesValue).MyEnumeratorCount + Get + Return Rules.Count + End Get + End Property +#End Region +#Region "Initializer" + Friend Sub New() + Rules = New List(Of DynamicRulesValue) + DynamicRulesFile = DynamicRulesFileImpl + DynamicRulesXml = DynamicRulesFile + DynamicRulesXml.Extension = "xml" + ReplacePattern_RepoToRaw = New RParams("(.*github.com/([^/]+)/([^/]+)/blob/(.+))", Nothing, 0, + RegexReturn.ReplaceChangeListMatch, EDP.ReturnValue) With { + .PatternReplacement = "https://raw.githubusercontent.com/{2}/{3}/{4}"} + ReplacePattern_JsonInfo = ReplacePattern_RepoToRaw.Copy + ReplacePattern_JsonInfo.PatternReplacement = "https://github.com/{2}/{3}/latest-commit/{4}" + ReplacePattern_RawToRepo = ReplacePattern_RepoToRaw.Copy + ReplacePattern_RawToRepo.Pattern = "(.*raw.githubusercontent.com/([^/]+)/([^/]+)/([^/]+)/(.+))" + ReplacePattern_RawToRepo.PatternReplacement = "https://github.com/{2}/{3}/blob/{4}/{5}" + ConfigRulesExtract = RParams.DMS("DYNAMIC_RULE"":(\{.+?\}[\r\n]+)", 1, RegexOptions.Singleline, EDP.ReturnValue) + OFLOG = New TextSaver($"LOGs\OF_{Now:yyyyMMdd_HHmmss}.txt") With {.LogMode = True, .AutoSave = True, .AutoClear = True} + AddHandler OFLOG.TextSaved, AddressOf OFLOG_TextSaved + OFError = New ErrorsDescriber(EDP.SendToLog + EDP.ReturnValue) With {.DeclaredMessage = New MMessage With {.Loggers = {OFLOG}, .Exists = True}} + Responser.DeclaredError = OFError + Responser.ProcessExceptionDecision = + Function(ByVal Status As IResponserStatus, ByVal NullArg As Object, ByVal CurrentError As ErrorsDescriber) As ErrorsDescriber + If Status.StatusCode = Net.HttpStatusCode.NotFound Then + CurrentError.SendToLogOnlyMessage = True + Dim m As MMessage = CurrentError.DeclaredMessage.Clone + m.Text = $"Nothing found at URL: {Responser.LatestUrlString}" + CurrentError.DeclaredMessage = m + Status.ErrorException = New ErrorsDescriberException(m.Text,,, Status.ErrorException) With {.ReplaceMainMessage = True} + End If + Return CurrentError + End Function + ConfigAddress = ParseURL("https://github.com/AAndyProgram/SCrawler/blob/main/SCrawler/API/OnlyFans/OFScraperConfigPattern.json") + ConfigConstAddress = ParseURL("https://github.com/AAndyProgram/SCrawler/blob/main/SCrawler/API/OnlyFans/OFScraperConfigPatternConstants.txt") + RulesConstants = New Dictionary(Of String, String) + End Sub +#End Region +#Region "Log handlers" + Private _OFLOG_ProcessNotify As Boolean = True + Private Sub OFLOG_TextSaved(sender As Object, e As EventArgs) + If _OFLOG_ProcessNotify And AddErrorsToLog Then _OFLOG_ProcessNotify = False : MyMainLOG = $"The OnlyFans log contains errors: {OFLOG.File}" + End Sub +#End Region +#Region "ParseURL" + Private Const SiteGitHub As String = "github.com" + Private Const SiteGitHubRaw As String = "raw.githubusercontent.com" + Friend Function ParseURL(ByVal URL As String) As DynamicRulesValue + URL = URL.StringTrim + If Not URL.IsEmptyString Then + Dim r As New DynamicRulesValue + Dim rGet As Func(Of String, RParams, String) = Function(__url, pattern) DirectCast(RegexReplace(__url, pattern), IEnumerable(Of String)).FirstOrDefault + If URL.ToLower.Contains(SiteGitHubRaw) Then + r.UrlRaw = URL + r.UrlRepo = rGet(URL, ReplacePattern_RawToRepo) + ElseIf URL.ToLower.Contains(SiteGitHub) Then + r.UrlRepo = URL + r.UrlRaw = rGet(URL, ReplacePattern_RepoToRaw) + End If + + If r.Valid Then + r.UpdatedAt = Now.AddYears(-1) + r.UrlLatestCommit = rGet(r.UrlRepo, ReplacePattern_JsonInfo) + r.Exists = True + Return r + End If + End If + Return Nothing + End Function +#End Region +#Region "GetFormat" + Private Shared ReadOnly Property ConfigNodes As String() + Get + Return {"advanced_options", "DYNAMIC_RULE"} + End Get + End Property + Private Const FormatMidPart As String = ":{0}:{1:x}:" + Private ReadOnly FormatExtract As RParams = RParams.DM("(\S+)\s*:\s*\{\s*\d?\s*\}\s*:\s*\{\s*\d?\s*:\s*x\s*\}\s*:\s*(\S+)", 0, RegexReturn.ListByMatch, EDP.ReturnValue) + Private ReadOnly ContainerStrConv As New CustomProvider(Function(input) If(ACheck(Of Integer)(input), input, $"""{input}""")) + Private ReadOnly ContainerConv As New CustomProvider(Function(ByVal e As Object) As Object + With DirectCast(e, EContainer) + Dim value$ = String.Empty + If .ListExists Then + value = .Select(Function(ee) ee(0).Value).ListToStringE(",", ContainerStrConv, False, String.Empty, EDP.ReturnValue) + If Not value.IsEmptyString Then value = $"[{value}]" + Else + value = AConvert(Of String)(.Value, ContainerStrConv, String.Empty, EDP.SendToLog, EDP.ReturnValue) + End If + If Not value.IsEmptyString Then + value = $"""{ .Name}"": {value}" + Else + value = $"""{ .Name}"": """"" + End If + Return value + End With + End Function) + Friend Shared Function GetFormat(ByVal j As EContainer, Optional ByVal Check As Boolean = False, + Optional ByRef CheckResult As Boolean = False, + Optional ByVal TryConfig As Boolean = False, Optional ByRef IsConfig As Boolean = False) As String + Dim pattern$ = String.Empty + With If(TryConfig, j(ConfigNodes), j) + If .ListExists Then + If Not .Value("format").IsEmptyString Then + pattern = .Value("format").Replace("{}", "{0}").Replace("{:x}", "{1:x}") + ElseIf Not .Value("prefix").IsEmptyString And Not .Value("suffix").IsEmptyString Then + pattern = .Value("prefix") & FormatMidPart & .Value("suffix") + ElseIf Not .Value("start").IsEmptyString And Not .Value("end").IsEmptyString Then + pattern = .Value("start") & FormatMidPart & .Value("end") + End If + + Dim result As Boolean = Not pattern.IsEmptyString And .Item("checksum_indexes").ListExists And + Not .Value("static_param").IsEmptyString And Not .Value("checksum_constant").IsEmptyString + If Check Then CheckResult = result + If Not result And Not TryConfig Then Return GetFormat(j, Check, CheckResult, True, IsConfig) + End If + End With + Return pattern + End Function + Private Function ConvertAuthText() As String + Dim result$ = String.Empty + With CurrentContainer + If .ListExists Then + Dim f$ = GetFormat(.Self) + If Not f.IsEmptyString Then + Dim l As List(Of String) = RegexReplace(f, FormatExtract) + If l.ListExists(3) Then + Dim s$ = l(1), e$ = l(2) + .Value("format") = s & FormatMidPart & e + .Value("prefix") = s + .Value("suffix") = e + .Value("start") = s + .Value("end") = e + Dim t$ = .ListToStringE(",", ContainerConv, False) + If Not t.IsEmptyString Then t = "{" & t & "}" + Return t + End If + End If + End If + End With + Return String.Empty + End Function +#End Region +#Region "Load, Save" + Private Function GetTextLines(ByVal Input As String) As List(Of String) + If Not Input.IsEmptyString Then + Return ListAddList(Nothing, Input.StringTrim.Split(vbCrLf), LAP.NotContainsOnly, EDP.ReturnValue, + CType(Function(inp$) inp.StringTrim, Func(Of Object, Object))) + Else + Return New List(Of String) + End If + End Function + Private Sub ParseConsts(ByVal Source As String) + If Not Source.IsEmptyString Then + Dim l As List(Of String) = GetTextLines(Source) + Dim v$() + If l.ListExists Then + RulesConstants.Clear() + For Each value$ In l + If Not value.IsEmptyString Then + v = value.Split("=") + If v.ListExists(2) Then RulesConstants.Add(v(0), v(1)) + End If + Next + End If + End If + End Sub + Private Const RulesNode As String = "Rules" + Private _InitialValuesLoaded As Boolean = False + Private Sub LoadInitialValues() + If Not _InitialValuesLoaded Then + _InitialValuesLoaded = True + + If Not OFScraperConfigPatternFile.Exists Then + Dim t$ = Text.Encoding.UTF8.GetString(My.Resources.OFResources.OFScraperConfigPattern) + TextSaver.SaveTextToFile(t, OFScraperConfigPatternFile, True) + End If + + If Not OFScraperConfigPatternFileConst.Exists Then _ + TextSaver.SaveTextToFile(My.Resources.OFResources.OFScraperConfigPatternConstants, OFScraperConfigPatternFileConst, True) + + If OFScraperConfigPatternFileConst.Exists Then ParseConsts(OFScraperConfigPatternFileConst.GetText(OFError)) + + If DynamicRulesXml.Exists Then + Rules.Clear() + Using x As New XmlFile(DynamicRulesXml, Protector.Modes.All, False) With {.XmlReadOnly = True, .AllowSameNames = True} + x.LoadData(OFError) + Dim dNull As Date = Now.AddYears(-1) + LastUpdateTimeFile = x.Value(Name_LastUpdateTimeFile).ToDateDef(dNull) + LastUpdateTimeRules = x.Value(Name_LastUpdateTimeRules).ToDateDef(dNull) + ProtectFile = x.Value(Name_ProtectFile).FromXML(Of Boolean)(False) + Mode = x.Value(Name_Mode).FromXML(Of Integer)(Modes.List) + UpdateInterval = x.Value(Name_UpdateInterval).FromXML(Of Integer)(UpdateIntervalDefault) + PersonalRule = x.Value(Name_PersonalRule) + RulesForceUpdateRequired = x.Value(Name_RulesForceUpdateRequired).FromXML(Of Boolean)(False) + RulesUpdateConst = x.Value(Name_RulesUpdateConst).FromXML(Of Boolean)(True) + RulesReplaceConfig = x.Value(Name_RulesReplaceConfig).FromXML(Of Boolean)(True) + AddErrorsToLog = x.Value(Name_AddErrorsToLog).FromXML(Of Boolean)(False) + ConfigAutoUpdate = x.Value(Name_ConfigAutoUpdate).FromXML(Of Boolean)(True) + RulesConfigManualMode = x.Value(Name_RulesConfigManualMode).FromXML(Of Boolean)(True) + ConfigLastDateUpdate = x.Value(Name_ConfigLastDateUpdate).ToDateDef(Now.AddYears(-1)) + If x.Contains(RulesNode) Then Rules.ListAddList(x({RulesNode}), LAP.IgnoreICopier, OFError) + End Using + End If + End If + End Sub + Friend Sub Save() + Using x As New XmlFile With {.AllowSameNames = True, .Name = "DynamicRules"} + x.Add(Name_LastUpdateTimeFile, LastUpdateTimeFile.ToStringDateDef) + x.Add(Name_LastUpdateTimeRules, LastUpdateTimeRules.ToStringDateDef) + x.Add(Name_ProtectFile, ProtectFile.BoolToInteger) + x.Add(Name_Mode, CInt(Mode)) + x.Add(Name_UpdateInterval, UpdateInterval) + x.Add(Name_PersonalRule, PersonalRule) + x.Add(Name_RulesForceUpdateRequired, RulesForceUpdateRequired.BoolToInteger) + x.Add(Name_RulesUpdateConst, RulesUpdateConst.BoolToInteger) + x.Add(Name_RulesReplaceConfig, RulesReplaceConfig.BoolToInteger) + x.Add(Name_AddErrorsToLog, AddErrorsToLog.BoolToInteger) + x.Add(Name_ConfigAutoUpdate, ConfigAutoUpdate.BoolToInteger) + x.Add(Name_RulesConfigManualMode, RulesConfigManualMode.BoolToInteger) + x.Add(Name_ConfigLastDateUpdate, ConfigLastDateUpdate.ToStringDateDef) + If Count > 0 Then + Rules.Sort() + x.Add(New EContainer(RulesNode)) + x.Last.AddRange(Rules) + End If + x.Save(DynamicRulesXml, OFError) + End Using + If Count > 0 Then + Using t As New TextSaver(DynamicRulesFile) + Rules.ForEach(Sub(r) If Not r.UrlRepo.IsEmptyString Then t.AppendLine(r.UrlRepo)) + t.Save(OFError) + End Using + End If + End Sub +#End Region +#Region "Update" + Private _UpdateInProgress As Boolean = False + Private _ForcedUpdate As Boolean = False + Friend Function Update(ByVal Force As Boolean, Optional ByVal LoadListOnly As Boolean = False) As Boolean + Dim skip As Boolean = _UpdateInProgress + If skip And _ForcedUpdate Then Force = False + _ForcedUpdate = Force + While _UpdateInProgress : Threading.Thread.Sleep(200) : End While + If Not skip Or Force Then UpdateImpl(Force Or RulesForceUpdateRequired, LoadListOnly) + Return Exists + End Function + Private Sub UpdateImpl(ByVal Force As Boolean, Optional ByVal LoadListOnly As Boolean = False) + Try + If Not _UpdateInProgress Then + _UpdateInProgress = True + + LoadInitialValues() + + Dim r$ + Dim process As Boolean = False, updated As Boolean = False + Dim forceSave As Boolean = RulesForceUpdateRequired Or Not DynamicRulesFile.Exists Or Not DynamicRulesXml.Exists + Dim textLocal As List(Of String) + Dim i% + Dim rule As DynamicRulesValue + Dim e As EContainer + Dim errDate As Date = Now.AddYears(-1) + Dim d As Date? + '2024-06-12T12:44:06.000-05:00 + Dim dateProvider As New ADateTime("yyyy-MM-ddTHH:mm:ss.fff%K") + + RulesForceUpdateRequired = False + + If Not DynamicRulesFile.Exists Then process = True : ValidateRulesFile() + + 'update rules list + If Not LoadListOnly And (LastUpdateTimeFile.AddMinutes(UpdateInterval) < Now Or process Or Force) Then + LastUpdateTimeFile = Now + r = Responser.GetResponse("https://raw.githubusercontent.com/AAndyProgram/SCrawler/main/SCrawler/API/OnlyFans/DynamicRules.txt") + If Not r.IsEmptyString Then + Dim textWeb As List(Of String) = GetTextLines(r) + Dim fileText$ + If textWeb.ListExists Then + Using t As New TextSaver(DynamicRulesFile) + If ProtectFile Then + fileText = DynamicRulesFile.GetText(OFError) + t.Append(fileText) + textLocal = GetTextLines(fileText) + If textLocal.ListExists Then _ + textLocal.ForEach(Sub(tt) If Not tt.IsEmptyString AndAlso Not textWeb.Contains(tt, RulesLinesComparer) Then _ + t.AppendLine(tt) : updated = True) : textLocal.Clear() + Else + t.Append(r) + updated = True + End If + t.Save(OFError) + End Using + textWeb.Clear() + End If + End If + End If + + 'update config and consts + If Not LoadListOnly AndAlso ConfigAutoUpdate AndAlso ConfigLastDateUpdate.AddMinutes(UpdateInterval) < Now Then + Dim __upConf As Boolean = False + Dim __dConf As Date = ConfigLastDateUpdate + Dim parseConfigFiles As Action(Of DynamicRulesValue, SFile, Boolean) = + Sub(ByVal __rule As DynamicRulesValue, ByVal __fileSave As SFile, ByVal isConstFile As Boolean) + r = Responser.GetResponse(__rule.UrlLatestCommit) + If Not r.IsEmptyString Then + e = JsonDocument.Parse(r, OFError) + If e.ListExists Then + d = AConvert(Of Date)(e.Value("date"), dateProvider, Nothing) + Dim dConf As Date = If(d, errDate) + If dConf > __dConf Then + __dConf = dConf + __upConf = True + updated = True + r = Responser.GetResponse(__rule.UrlRaw) + If Not r.IsEmptyString Then + TextSaver.SaveTextToFile(r, __fileSave, True, False, OFError) + If isConstFile Then ParseConsts(r) + End If + End If + e.Dispose() + End If + End If + End Sub + 'Update consts + If RulesUpdateConst Then parseConfigFiles(ConfigConstAddress, OFScraperConfigPatternFileConst, True) + 'Update config + parseConfigFiles(ConfigAddress, OFScraperConfigPatternFile, False) + If __upConf Then ConfigLastDateUpdate = Now + End If + + 'generate rules, update rules dates + If LastUpdateTimeRules.AddMinutes(UpdateInterval) < Now Or updated Or Force Or LoadListOnly Then + process = True + If Mode = Modes.Personal And Not PersonalRule.IsEmptyString Then + If Not LoadListOnly Then LastUpdateTimeRules = Now : updated = True + Else + If Not LoadListOnly Then LastUpdateTimeRules = Now : updated = True + textLocal = GetTextLines(DynamicRulesFile.GetText(OFError)) + If textLocal.ListExists Then + For Each url$ In textLocal + url = url.StringTrim + If Not url.IsEmptyString Then + i = IndexOf(url) + If i >= 0 Then + rule = Rules(i) + Else + rule = ParseURL(url) + If rule.Valid Then + i = Add(rule, False, False) + Else + rule = Nothing + End If + End If + + If Not LoadListOnly Then + If i >= 0 And rule.Valid And Not rule.UrlLatestCommit.IsEmptyString Then + r = Responser.GetResponse(rule.UrlLatestCommit) + If Not r.IsEmptyString Then + e = JsonDocument.Parse(r, OFError) + If e.ListExists Then + d = AConvert(Of Date)(e.Value("date"), dateProvider, Nothing) + rule.UpdatedAt = If(d, errDate) + e.Dispose() + Else + rule.Broken = True + End If + Else + rule.Broken = True + End If + Rules(i) = rule + End If + End If + End If + Next + End If + End If + End If + + If Count > 0 Then Rules.Sort() + + 'download and load the rule + If (LoadListOnly And AuthFile.Exists) Or (Not LoadListOnly And ((updated And Count > 0) Or Not AuthFile.Exists)) Then + _CurrentRule = Nothing + _CurrentContainer.DisposeIfReady + _CurrentContainer = Nothing + Dim processRule As Func(Of DynamicRulesValue, Boolean, DialogResult) = + Function(ByVal __rule As DynamicRulesValue, ByVal reparseAuth As Boolean) As DialogResult + Dim fromAuthFile As Boolean = (LoadListOnly Or reparseAuth) AndAlso AuthFile.Exists + If fromAuthFile Then + r = AuthFile.GetText(OFError) + Else + r = GetWebString(__rule.UrlRaw,, OFError) + End If + Dim j As EContainer = JsonDocument.Parse(r, OFError) + Dim checkResult As Boolean = False + Dim isConfig As Boolean = False + Dim textToSave As String = r + If j.ListExists AndAlso Not GetFormat(j, True, checkResult,, isConfig).IsEmptyString AndAlso checkResult Then + If isConfig Then textToSave = RegexReplace(r, ConfigRulesExtract) + If textToSave.IsEmptyString Then + Return DialogResult.Retry + Else + _CurrentRule = __rule + _CurrentContainer = If(isConfig, j(ConfigNodes), j) + textToSave = ConvertAuthText() + _CurrentContainerRulesText = textToSave + If (Not fromAuthFile Or Not textToSave.StringTrim = r.StringTrim) And Not textToSave.IsEmptyString Then + TextSaver.SaveTextToFile(textToSave, AuthFile, True, False, OFError) + If Not reparseAuth Then processRule(__rule, True) + End If + Return DialogResult.OK + End If + End If + Return DialogResult.No + End Function + If Mode = Modes.Personal And Not PersonalRule.IsEmptyString Then + processRule(New DynamicRulesValue With {.UrlRepo = PersonalRule, .UrlRaw = PersonalRule}, False) + Else + For Each rule In Rules + If rule.Valid And Not rule.Broken Then + Select Case processRule(rule, False) + Case DialogResult.Retry : Continue For + Case DialogResult.OK : Exit For + End Select + End If + Next + End If + End If + + If updated Or forceSave Then Save() + + _UpdateInProgress = False + End If + Catch ex As Exception + ErrorsDescriber.Execute(OFError, ex, "[OnlyFans.DynamicRulesEnv.UpdateImpl]") + _UpdateInProgress = False + End Try + End Sub +#End Region +#Region "Add, IndexOf" + Friend Function Add(ByVal Rule As DynamicRulesValue, Optional ByVal AutoSort As Boolean = True, Optional ByVal AutoSave As Boolean = False) As Integer + If Rule.Valid Then + Dim i% = IndexOf(Rule) + If i = -1 Then + Rules.Add(Rule) + i = Count - 1 + If AutoSort Then Rules.Sort() : i = IndexOf(Rule) + If AutoSave Then Save() + End If + Return i + Else + Return -1 + End If + End Function + Friend Function RemoveAt(ByVal Index As Integer) As Boolean + If Index.ValueBetween(0, Count - 1) Then + Rules.RemoveAt(Index) + Return True + Else + Return False + End If + End Function + Friend Function IndexOf(ByVal URL As String) As Integer + If Count > 0 Then + URL = URL.StringToLower.Trim + Return Rules.FindIndex(Function(r) r.UrlRepo.StringToLower = URL Or r.UrlRaw.StringToLower = URL Or r.UrlLatestCommit.StringToLower = URL) + Else + Return -1 + End If + End Function +#End Region +#Region "ICopier Support" + Friend Overloads Function Copy() As Object Implements ICopier.Copy + Return (New DynamicRulesEnv).Copy(Me) + End Function + Friend Overloads Function Copy(ByVal Source As Object) As Object Implements ICopier.Copy + Return Copy(Source, False) + End Function + Friend Overloads Function Copy(ByVal Source As Object, ByVal UpdateForceProperty As Boolean) As Object + If Not Source Is Nothing Then + With DirectCast(Source, DynamicRulesEnv) + If Not RulesForceUpdateRequired And UpdateForceProperty Then _ + RulesForceUpdateRequired = Not Rules.ListEquals(.Rules) Or Not Mode = .Mode Or + (.Mode = Modes.Personal And Not PersonalRule = .PersonalRule) + ProtectFile = .ProtectFile + Mode = .Mode + UpdateInterval = .UpdateInterval + PersonalRule = .PersonalRule + If Not RulesForceUpdateRequired Then RulesForceUpdateRequired = .RulesForceUpdateRequired + RulesUpdateConst = .RulesUpdateConst + RulesReplaceConfig = .RulesReplaceConfig + AddErrorsToLog = .AddErrorsToLog + ConfigAutoUpdate = .ConfigAutoUpdate + RulesConfigManualMode = .RulesConfigManualMode + Rules.Clear() + If .Count > 0 Then Rules.AddRange(.Rules) + End With + Return Me + Else + Return Nothing + End If + End Function +#End Region +#Region "IEnumerable Support" + Private Function GetEnumerator() As IEnumerator(Of DynamicRulesValue) Implements IEnumerable(Of DynamicRulesValue).GetEnumerator + Return New MyEnumerator(Of DynamicRulesValue)(Me) + End Function + Private Function IEnumerable_GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator + Return GetEnumerator() + End Function +#End Region +#Region "IDisposable Support" + Private disposedValue As Boolean = False + Protected Overridable Overloads Sub Dispose(ByVal disposing As Boolean) + If Not disposedValue Then + If disposing Then + Rules.Clear() + _CurrentContainer.DisposeIfReady + Responser.DisposeIfReady + End If + _CurrentContainer = Nothing + disposedValue = True + End If + End Sub + Protected Overrides Sub Finalize() + Dispose(False) + MyBase.Finalize() + End Sub + Friend Overloads Sub Dispose() Implements IDisposable.Dispose + Dispose(True) + GC.SuppressFinalize(Me) + End Sub +#End Region + End Class +End Namespace \ No newline at end of file diff --git a/SCrawler/API/OnlyFans/OFResources.Designer.vb b/SCrawler/API/OnlyFans/OFResources.Designer.vb index 138578b..cbc92de 100644 --- a/SCrawler/API/OnlyFans/OFResources.Designer.vb +++ b/SCrawler/API/OnlyFans/OFResources.Designer.vb @@ -64,6 +64,20 @@ Namespace My.Resources End Set End Property + ''' + ''' Looks up a localized string similar to https://github.com/datawhores/onlyfans-dynamic-rules/blob/main/dynamicRules.json + '''https://github.com/riley-access-labs/onlyfans-dynamic-rules-1/blob/main/dynamicRules.json + '''https://github.com/riley-access-labs/onlyfans-dynamic-rules-1/blob/patch-1/dynamicRules.json + '''https://github.com/DATAHOARDERS/dynamic-rules/blob/main/onlyfans.json + '''https://github.com/DIGITALCRIMINAL/dynamic-rules/blob/main/onlyfans.json + '''https://github.com/deviint/onlyfans-dynamic-rules/blob/main/dynamicRules.json. + ''' + Friend Shared ReadOnly Property DynamicRules() As String + Get + Return ResourceManager.GetString("DynamicRules", resourceCulture) + End Get + End Property + ''' ''' Looks up a localized resource of type System.Byte[]. ''' @@ -73,5 +87,15 @@ Namespace My.Resources Return CType(obj,Byte()) End Get End Property + + ''' + ''' Looks up a localized string similar to dynamic-mode-default=generic + '''RULE_VALUE=DYNAMIC_GENERIC_URL. + ''' + Friend Shared ReadOnly Property OFScraperConfigPatternConstants() As String + Get + Return ResourceManager.GetString("OFScraperConfigPatternConstants", resourceCulture) + End Get + End Property End Class End Namespace diff --git a/SCrawler/API/OnlyFans/OFResources.resx b/SCrawler/API/OnlyFans/OFResources.resx index 31b7c00..9612d0b 100644 --- a/SCrawler/API/OnlyFans/OFResources.resx +++ b/SCrawler/API/OnlyFans/OFResources.resx @@ -118,7 +118,13 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + DynamicRules.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + OFScraperConfigPattern.json;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + OFScraperConfigPatternConstants.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + \ No newline at end of file diff --git a/SCrawler/API/OnlyFans/OFScraperConfigPattern.json b/SCrawler/API/OnlyFans/OFScraperConfigPattern.json index fb702f7..cee67f6 100644 --- a/SCrawler/API/OnlyFans/OFScraperConfigPattern.json +++ b/SCrawler/API/OnlyFans/OFScraperConfigPattern.json @@ -1,65 +1,63 @@ { - "config": { - "main_profile": "main_profile", - "metadata": "{configpath}/{profile}/.data/{model_username}_{model_id}", - "discord": "", - "file_options": { - "save_location": "", - "dir_format": "", - "file_format": "{filename}.{ext}", - "textlength": 0, - "space-replacer": " ", - "date": "YYYY-MM-DD" - }, - "download_options": { - "file_size_limit": 0, - "file_size_min": 0, - "filter": [ - "Images", - "Audios", - "Videos" - ], - "auto_resume": false - }, - "binary_options": { - "mp4decrypt": "", - "ffmpeg": "" - }, - "cdm_options": { - "private-key": null, - "client-id": null, - "key-mode-default": "cdrm", - "keydb_api": "" - }, - "performance_options": { - "download-sems": 6, - "maxfile-sem": 0, - "threads": 5 - }, - "advanced_options": { - "code-execution": false, - "dynamic-mode-default": "sneaky", - "backend": "aio", - "downloadbars": false, - "cache-mode": "sqlite", - "appendlog": true, - "custom": null, - "sanitize_text": false, - "avatar": true, - "custom_values": { - "SNEAKY": "https://raw.githubusercontent.com/Growik/onlyfans-dynamic-rules/main/rules.json", - "CDRM": "https://old.cdrm-project.com/wv" - } - }, - "responsetype": { - "timeline": "Posts", - "message": "Messages", - "archived": "Archived", - "paid": "Messages", - "stories": "Stories", - "highlights": "Stories", - "profile": "Profile", - "pinned": "Posts" + "main_profile": "main_profile", + "metadata": "{configpath}/{profile}/.data/{model_username}_{model_id}", + "discord": "", + "file_options": { + "save_location": "", + "dir_format": "", + "file_format": "{filename}.{ext}", + "textlength": 0, + "space_replacer": " ", + "date": "YYYY-MM-DD" + }, + "download_options": { + "file_size_max": 0, + "file_size_min": 0, + "filter": [ + "Images", + "Audios", + "Videos" + ], + "auto_resume": false + }, + "binary_options": { + "mp4decrypt": "", + "ffmpeg": "" + }, + "cdm_options": { + "private-key": null, + "client-id": null, + "key-mode-default": "cdrm", + "keydb_api": "" + }, + "performance_options": { + "download-sems": 6, + "maxfile-sem": 0, + "threads": 5 + }, + "advanced_options": { + "code-execution": false, + "dynamic-mode-default": "generic", + "backend": "aio", + "downloadbars": false, + "cache-mode": "sqlite", + "appendlog": true, + "custom": null, + "sanitize_text": false, + "avatar": true, + "custom_values": { + "DYNAMIC_GENERIC_URL": "https://raw.githubusercontent.com/datawhores/onlyfans-dynamic-rules/main/dynamicRules.json", + "CDRM": "https://old.cdrm-project.com/wv" } + }, + "responsetype": { + "timeline": "Posts", + "message": "Messages", + "archived": "Archived", + "paid": "Messages", + "stories": "Stories", + "highlights": "Stories", + "profile": "Profile", + "pinned": "Posts" } } \ No newline at end of file diff --git a/SCrawler/API/OnlyFans/OFScraperConfigPatternConstants.txt b/SCrawler/API/OnlyFans/OFScraperConfigPatternConstants.txt new file mode 100644 index 0000000..1dd4c07 --- /dev/null +++ b/SCrawler/API/OnlyFans/OFScraperConfigPatternConstants.txt @@ -0,0 +1,2 @@ +dynamic-mode-default=generic +RULE_VALUE=DYNAMIC_GENERIC_URL \ No newline at end of file diff --git a/SCrawler/API/OnlyFans/OnlyFansAdvancedSettingsForm.Designer.vb b/SCrawler/API/OnlyFans/OnlyFansAdvancedSettingsForm.Designer.vb new file mode 100644 index 0000000..afee3df --- /dev/null +++ b/SCrawler/API/OnlyFans/OnlyFansAdvancedSettingsForm.Designer.vb @@ -0,0 +1,361 @@ +' Copyright (C) Andy https://github.com/AAndyProgram +' This program is free software: you can redistribute it and/or modify +' it under the terms of the GNU General Public License as published by +' the Free Software Foundation, either version 3 of the License, or +' (at your option) any later version. +' +' This program is distributed in the hope that it will be useful, +' but WITHOUT ANY WARRANTY +Namespace API.OnlyFans + + Partial Friend Class OnlyFansAdvancedSettingsForm : Inherits System.Windows.Forms.Form + + Protected Overrides Sub Dispose(ByVal disposing As Boolean) + Try + If disposing AndAlso components IsNot Nothing Then + components.Dispose() + End If + Finally + MyBase.Dispose(disposing) + End Try + End Sub + Private components As System.ComponentModel.IContainer + + Private Sub InitializeComponent() + Me.components = New System.ComponentModel.Container() + Dim CONTAINER_MAIN As System.Windows.Forms.ToolStripContainer + Dim TP_MAIN As System.Windows.Forms.TableLayoutPanel + Dim ActionButton7 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() + Dim resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(OnlyFansAdvancedSettingsForm)) + Dim ActionButton8 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() + Dim ActionButton9 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() + Dim TP_RULES_LIST As System.Windows.Forms.TableLayoutPanel + Dim TP_RULES_LIST_LEFT As System.Windows.Forms.TableLayoutPanel + Dim TT_MAIN As System.Windows.Forms.ToolTip + Me.TXT_UP_INTERVAL = New PersonalUtilities.Forms.Controls.TextBoxExtended() + Me.TXT_PERSONAL_RULE = New PersonalUtilities.Forms.Controls.TextBoxExtended() + Me.CONTAINER_LIST = New System.Windows.Forms.ToolStripContainer() + Me.LIST_RULES = New System.Windows.Forms.ListBox() + Me.OPT_RULES_LIST = New System.Windows.Forms.RadioButton() + Me.CH_PROTECTED = New System.Windows.Forms.CheckBox() + Me.CH_FORCE_UPDATE = New System.Windows.Forms.CheckBox() + Me.CH_LOG_ERR = New System.Windows.Forms.CheckBox() + Me.CH_RULES_REPLACE_CONFIG = New System.Windows.Forms.CheckBox() + Me.CH_UPDATE_CONF = New System.Windows.Forms.CheckBox() + Me.CH_UPDATE_RULES_CONST = New System.Windows.Forms.CheckBox() + Me.CH_CONFIG_MANUAL_MODE = New System.Windows.Forms.CheckBox() + CONTAINER_MAIN = New System.Windows.Forms.ToolStripContainer() + TP_MAIN = New System.Windows.Forms.TableLayoutPanel() + TP_RULES_LIST = New System.Windows.Forms.TableLayoutPanel() + TP_RULES_LIST_LEFT = New System.Windows.Forms.TableLayoutPanel() + TT_MAIN = New System.Windows.Forms.ToolTip(Me.components) + CONTAINER_MAIN.ContentPanel.SuspendLayout() + CONTAINER_MAIN.SuspendLayout() + TP_MAIN.SuspendLayout() + CType(Me.TXT_UP_INTERVAL, System.ComponentModel.ISupportInitialize).BeginInit() + CType(Me.TXT_PERSONAL_RULE, System.ComponentModel.ISupportInitialize).BeginInit() + TP_RULES_LIST.SuspendLayout() + Me.CONTAINER_LIST.ContentPanel.SuspendLayout() + Me.CONTAINER_LIST.SuspendLayout() + TP_RULES_LIST_LEFT.SuspendLayout() + Me.SuspendLayout() + ' + 'CONTAINER_MAIN + ' + ' + 'CONTAINER_MAIN.ContentPanel + ' + CONTAINER_MAIN.ContentPanel.Controls.Add(TP_MAIN) + CONTAINER_MAIN.ContentPanel.Size = New System.Drawing.Size(464, 341) + CONTAINER_MAIN.Dock = System.Windows.Forms.DockStyle.Fill + CONTAINER_MAIN.LeftToolStripPanelVisible = False + CONTAINER_MAIN.Location = New System.Drawing.Point(0, 0) + CONTAINER_MAIN.Name = "CONTAINER_MAIN" + CONTAINER_MAIN.RightToolStripPanelVisible = False + CONTAINER_MAIN.Size = New System.Drawing.Size(464, 341) + CONTAINER_MAIN.TabIndex = 1 + CONTAINER_MAIN.TopToolStripPanelVisible = False + ' + 'TP_MAIN + ' + TP_MAIN.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.[Single] + TP_MAIN.ColumnCount = 1 + TP_MAIN.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) + TP_MAIN.Controls.Add(Me.TXT_UP_INTERVAL, 0, 5) + TP_MAIN.Controls.Add(Me.TXT_PERSONAL_RULE, 0, 6) + TP_MAIN.Controls.Add(TP_RULES_LIST, 0, 7) + TP_MAIN.Controls.Add(Me.CH_LOG_ERR, 0, 0) + TP_MAIN.Controls.Add(Me.CH_RULES_REPLACE_CONFIG, 0, 1) + TP_MAIN.Controls.Add(Me.CH_UPDATE_CONF, 0, 4) + TP_MAIN.Controls.Add(Me.CH_UPDATE_RULES_CONST, 0, 2) + TP_MAIN.Controls.Add(Me.CH_CONFIG_MANUAL_MODE, 0, 3) + TP_MAIN.Dock = System.Windows.Forms.DockStyle.Fill + TP_MAIN.Location = New System.Drawing.Point(0, 0) + TP_MAIN.Name = "TP_MAIN" + TP_MAIN.RowCount = 8 + TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) + TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) + TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) + TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) + TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) + TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) + TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) + TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) + TP_MAIN.Size = New System.Drawing.Size(464, 341) + TP_MAIN.TabIndex = 0 + ' + 'TXT_UP_INTERVAL + ' + ActionButton7.BackgroundImage = CType(resources.GetObject("ActionButton7.BackgroundImage"), System.Drawing.Image) + ActionButton7.Name = "Refresh" + ActionButton7.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Refresh + ActionButton8.BackgroundImage = CType(resources.GetObject("ActionButton8.BackgroundImage"), System.Drawing.Image) + ActionButton8.Name = "Clear" + ActionButton8.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear + Me.TXT_UP_INTERVAL.Buttons.Add(ActionButton7) + Me.TXT_UP_INTERVAL.Buttons.Add(ActionButton8) + Me.TXT_UP_INTERVAL.CaptionText = "Dynamic rules update" + Me.TXT_UP_INTERVAL.CaptionToolTipEnabled = True + Me.TXT_UP_INTERVAL.CaptionToolTipText = "'Dynamic rules' update interval (minutes). Default: 1440" + Me.TXT_UP_INTERVAL.CaptionWidth = 120.0R + Me.TXT_UP_INTERVAL.Dock = System.Windows.Forms.DockStyle.Fill + Me.TXT_UP_INTERVAL.Location = New System.Drawing.Point(4, 134) + Me.TXT_UP_INTERVAL.Name = "TXT_UP_INTERVAL" + Me.TXT_UP_INTERVAL.Size = New System.Drawing.Size(456, 22) + Me.TXT_UP_INTERVAL.TabIndex = 5 + ' + 'TXT_PERSONAL_RULE + ' + Me.TXT_PERSONAL_RULE.AutoShowClearButton = True + ActionButton9.BackgroundImage = CType(resources.GetObject("ActionButton9.BackgroundImage"), System.Drawing.Image) + ActionButton9.Enabled = False + ActionButton9.Name = "Clear" + ActionButton9.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear + ActionButton9.Visible = False + Me.TXT_PERSONAL_RULE.Buttons.Add(ActionButton9) + Me.TXT_PERSONAL_RULE.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.RadioButton + Me.TXT_PERSONAL_RULE.CaptionText = "Dynamic rules URL" + Me.TXT_PERSONAL_RULE.CaptionToolTipEnabled = True + Me.TXT_PERSONAL_RULE.CaptionToolTipText = "Overwrite 'Dynamic rules' with this URL." & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "Change this value only if you know what" & + " you are doing." + Me.TXT_PERSONAL_RULE.CaptionWidth = 120.0R + Me.TXT_PERSONAL_RULE.Dock = System.Windows.Forms.DockStyle.Fill + Me.TXT_PERSONAL_RULE.LeaveDefaultButtons = True + Me.TXT_PERSONAL_RULE.Location = New System.Drawing.Point(4, 163) + Me.TXT_PERSONAL_RULE.Name = "TXT_PERSONAL_RULE" + Me.TXT_PERSONAL_RULE.Size = New System.Drawing.Size(456, 22) + Me.TXT_PERSONAL_RULE.TabIndex = 6 + ' + 'TP_RULES_LIST + ' + TP_RULES_LIST.ColumnCount = 2 + TP_RULES_LIST.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 120.0!)) + TP_RULES_LIST.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) + TP_RULES_LIST.Controls.Add(Me.CONTAINER_LIST, 1, 0) + TP_RULES_LIST.Controls.Add(TP_RULES_LIST_LEFT, 0, 0) + TP_RULES_LIST.Dock = System.Windows.Forms.DockStyle.Fill + TP_RULES_LIST.Location = New System.Drawing.Point(4, 192) + TP_RULES_LIST.Name = "TP_RULES_LIST" + TP_RULES_LIST.RowCount = 1 + TP_RULES_LIST.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) + TP_RULES_LIST.Size = New System.Drawing.Size(456, 145) + TP_RULES_LIST.TabIndex = 7 + ' + 'CONTAINER_LIST + ' + Me.CONTAINER_LIST.BottomToolStripPanelVisible = False + ' + 'CONTAINER_LIST.ContentPanel + ' + Me.CONTAINER_LIST.ContentPanel.Controls.Add(Me.LIST_RULES) + Me.CONTAINER_LIST.ContentPanel.Size = New System.Drawing.Size(330, 114) + Me.CONTAINER_LIST.Dock = System.Windows.Forms.DockStyle.Fill + Me.CONTAINER_LIST.LeftToolStripPanelVisible = False + Me.CONTAINER_LIST.Location = New System.Drawing.Point(123, 3) + Me.CONTAINER_LIST.Name = "CONTAINER_LIST" + Me.CONTAINER_LIST.RightToolStripPanelVisible = False + Me.CONTAINER_LIST.Size = New System.Drawing.Size(330, 139) + Me.CONTAINER_LIST.TabIndex = 1 + ' + 'LIST_RULES + ' + Me.LIST_RULES.Dock = System.Windows.Forms.DockStyle.Fill + Me.LIST_RULES.FormattingEnabled = True + Me.LIST_RULES.Location = New System.Drawing.Point(0, 0) + Me.LIST_RULES.Name = "LIST_RULES" + Me.LIST_RULES.Size = New System.Drawing.Size(330, 114) + Me.LIST_RULES.TabIndex = 0 + ' + 'TP_RULES_LIST_LEFT + ' + TP_RULES_LIST_LEFT.ColumnCount = 1 + TP_RULES_LIST_LEFT.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) + TP_RULES_LIST_LEFT.Controls.Add(Me.OPT_RULES_LIST, 0, 0) + TP_RULES_LIST_LEFT.Controls.Add(Me.CH_PROTECTED, 0, 1) + TP_RULES_LIST_LEFT.Controls.Add(Me.CH_FORCE_UPDATE, 0, 2) + TP_RULES_LIST_LEFT.Dock = System.Windows.Forms.DockStyle.Fill + TP_RULES_LIST_LEFT.Location = New System.Drawing.Point(0, 0) + TP_RULES_LIST_LEFT.Margin = New System.Windows.Forms.Padding(0) + TP_RULES_LIST_LEFT.Name = "TP_RULES_LIST_LEFT" + TP_RULES_LIST_LEFT.RowCount = 4 + TP_RULES_LIST_LEFT.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) + TP_RULES_LIST_LEFT.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) + TP_RULES_LIST_LEFT.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) + TP_RULES_LIST_LEFT.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) + TP_RULES_LIST_LEFT.Size = New System.Drawing.Size(120, 145) + TP_RULES_LIST_LEFT.TabIndex = 0 + ' + 'OPT_RULES_LIST + ' + Me.OPT_RULES_LIST.AutoSize = True + Me.OPT_RULES_LIST.CheckAlign = System.Drawing.ContentAlignment.MiddleRight + Me.OPT_RULES_LIST.Dock = System.Windows.Forms.DockStyle.Fill + Me.OPT_RULES_LIST.Location = New System.Drawing.Point(3, 3) + Me.OPT_RULES_LIST.Margin = New System.Windows.Forms.Padding(3, 3, 0, 3) + Me.OPT_RULES_LIST.Name = "OPT_RULES_LIST" + Me.OPT_RULES_LIST.Size = New System.Drawing.Size(117, 19) + Me.OPT_RULES_LIST.TabIndex = 0 + Me.OPT_RULES_LIST.TabStop = True + Me.OPT_RULES_LIST.Text = "Dynamic rules list" + Me.OPT_RULES_LIST.TextAlign = System.Drawing.ContentAlignment.MiddleRight + TT_MAIN.SetToolTip(Me.OPT_RULES_LIST, "List of dynamic rules sources." & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "If selected, the most recently updated source wil" & + "l be selected.") + Me.OPT_RULES_LIST.UseVisualStyleBackColor = True + ' + 'CH_PROTECTED + ' + Me.CH_PROTECTED.AutoSize = True + Me.CH_PROTECTED.CheckAlign = System.Drawing.ContentAlignment.MiddleRight + Me.CH_PROTECTED.Dock = System.Windows.Forms.DockStyle.Fill + Me.CH_PROTECTED.Location = New System.Drawing.Point(3, 28) + Me.CH_PROTECTED.Margin = New System.Windows.Forms.Padding(3, 3, 0, 3) + Me.CH_PROTECTED.Name = "CH_PROTECTED" + Me.CH_PROTECTED.Size = New System.Drawing.Size(117, 19) + Me.CH_PROTECTED.TabIndex = 1 + Me.CH_PROTECTED.Text = "Protected list" + Me.CH_PROTECTED.TextAlign = System.Drawing.ContentAlignment.MiddleRight + TT_MAIN.SetToolTip(Me.CH_PROTECTED, "If checked, the new source will be added, but the rules list will not be overwrit" & + "ten by the updated one.") + Me.CH_PROTECTED.UseVisualStyleBackColor = True + ' + 'CH_FORCE_UPDATE + ' + Me.CH_FORCE_UPDATE.AutoSize = True + Me.CH_FORCE_UPDATE.CheckAlign = System.Drawing.ContentAlignment.MiddleRight + Me.CH_FORCE_UPDATE.Dock = System.Windows.Forms.DockStyle.Fill + Me.CH_FORCE_UPDATE.Location = New System.Drawing.Point(3, 53) + Me.CH_FORCE_UPDATE.Margin = New System.Windows.Forms.Padding(3, 3, 0, 3) + Me.CH_FORCE_UPDATE.Name = "CH_FORCE_UPDATE" + Me.CH_FORCE_UPDATE.Size = New System.Drawing.Size(117, 19) + Me.CH_FORCE_UPDATE.TabIndex = 2 + Me.CH_FORCE_UPDATE.Text = "Force update" + Me.CH_FORCE_UPDATE.TextAlign = System.Drawing.ContentAlignment.MiddleRight + TT_MAIN.SetToolTip(Me.CH_FORCE_UPDATE, "Check this if you want to force the rules to update.") + Me.CH_FORCE_UPDATE.UseVisualStyleBackColor = True + ' + 'CH_LOG_ERR + ' + Me.CH_LOG_ERR.AutoSize = True + Me.CH_LOG_ERR.Dock = System.Windows.Forms.DockStyle.Fill + Me.CH_LOG_ERR.Location = New System.Drawing.Point(4, 4) + Me.CH_LOG_ERR.Name = "CH_LOG_ERR" + Me.CH_LOG_ERR.Size = New System.Drawing.Size(456, 19) + Me.CH_LOG_ERR.TabIndex = 0 + Me.CH_LOG_ERR.Text = "Add dynamic rules errors to the log" + TT_MAIN.SetToolTip(Me.CH_LOG_ERR, "OnlyFans errors will be added to a separate log." & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "A checked checkbox means that e" & + "rror notification will be added to the main log.") + Me.CH_LOG_ERR.UseVisualStyleBackColor = True + ' + 'CH_RULES_REPLACE_CONFIG + ' + Me.CH_RULES_REPLACE_CONFIG.AutoSize = True + Me.CH_RULES_REPLACE_CONFIG.Dock = System.Windows.Forms.DockStyle.Fill + Me.CH_RULES_REPLACE_CONFIG.Location = New System.Drawing.Point(4, 30) + Me.CH_RULES_REPLACE_CONFIG.Name = "CH_RULES_REPLACE_CONFIG" + Me.CH_RULES_REPLACE_CONFIG.Size = New System.Drawing.Size(456, 19) + Me.CH_RULES_REPLACE_CONFIG.TabIndex = 1 + Me.CH_RULES_REPLACE_CONFIG.Text = "Replace rules in OF-Scraper configuration file" + TT_MAIN.SetToolTip(Me.CH_RULES_REPLACE_CONFIG, "If checked, the dynamic rules (in the config) will be replaced with actual values" & + ".") + Me.CH_RULES_REPLACE_CONFIG.UseVisualStyleBackColor = True + ' + 'CH_UPDATE_CONF + ' + Me.CH_UPDATE_CONF.AutoSize = True + Me.CH_UPDATE_CONF.Dock = System.Windows.Forms.DockStyle.Fill + Me.CH_UPDATE_CONF.Location = New System.Drawing.Point(4, 108) + Me.CH_UPDATE_CONF.Name = "CH_UPDATE_CONF" + Me.CH_UPDATE_CONF.Size = New System.Drawing.Size(456, 19) + Me.CH_UPDATE_CONF.TabIndex = 4 + Me.CH_UPDATE_CONF.Text = "Update configuration file during update" + TT_MAIN.SetToolTip(Me.CH_UPDATE_CONF, "Update the configuration pattern from the site during update.") + Me.CH_UPDATE_CONF.UseVisualStyleBackColor = True + ' + 'CH_UPDATE_RULES_CONST + ' + Me.CH_UPDATE_RULES_CONST.AutoSize = True + Me.CH_UPDATE_RULES_CONST.Dock = System.Windows.Forms.DockStyle.Fill + Me.CH_UPDATE_RULES_CONST.Location = New System.Drawing.Point(4, 56) + Me.CH_UPDATE_RULES_CONST.Name = "CH_UPDATE_RULES_CONST" + Me.CH_UPDATE_RULES_CONST.Size = New System.Drawing.Size(456, 19) + Me.CH_UPDATE_RULES_CONST.TabIndex = 2 + Me.CH_UPDATE_RULES_CONST.Text = "Update rules constants file during update" + TT_MAIN.SetToolTip(Me.CH_UPDATE_RULES_CONST, "Update rules constants from the site during update") + Me.CH_UPDATE_RULES_CONST.UseVisualStyleBackColor = True + ' + 'CH_CONFIG_MANUAL_MODE + ' + Me.CH_CONFIG_MANUAL_MODE.AutoSize = True + Me.CH_CONFIG_MANUAL_MODE.Dock = System.Windows.Forms.DockStyle.Fill + Me.CH_CONFIG_MANUAL_MODE.Location = New System.Drawing.Point(4, 82) + Me.CH_CONFIG_MANUAL_MODE.Name = "CH_CONFIG_MANUAL_MODE" + Me.CH_CONFIG_MANUAL_MODE.Size = New System.Drawing.Size(456, 19) + Me.CH_CONFIG_MANUAL_MODE.TabIndex = 3 + Me.CH_CONFIG_MANUAL_MODE.Text = "Dynamic rules 'Manual' mode" + TT_MAIN.SetToolTip(Me.CH_CONFIG_MANUAL_MODE, "The rules will be added to the config as is, without using a link.") + Me.CH_CONFIG_MANUAL_MODE.UseVisualStyleBackColor = True + ' + 'OnlyFansAdvancedSettingsForm + ' + Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!) + Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font + Me.ClientSize = New System.Drawing.Size(464, 341) + Me.Controls.Add(CONTAINER_MAIN) + Me.Icon = Global.SCrawler.My.Resources.SiteResources.OnlyFansIcon_32 + Me.KeyPreview = True + Me.MinimizeBox = False + Me.MinimumSize = New System.Drawing.Size(480, 380) + Me.Name = "OnlyFansAdvancedSettingsForm" + Me.ShowInTaskbar = False + Me.Text = "Settings" + CONTAINER_MAIN.ContentPanel.ResumeLayout(False) + CONTAINER_MAIN.ResumeLayout(False) + CONTAINER_MAIN.PerformLayout() + TP_MAIN.ResumeLayout(False) + TP_MAIN.PerformLayout() + CType(Me.TXT_UP_INTERVAL, System.ComponentModel.ISupportInitialize).EndInit() + CType(Me.TXT_PERSONAL_RULE, System.ComponentModel.ISupportInitialize).EndInit() + TP_RULES_LIST.ResumeLayout(False) + Me.CONTAINER_LIST.ContentPanel.ResumeLayout(False) + Me.CONTAINER_LIST.ResumeLayout(False) + Me.CONTAINER_LIST.PerformLayout() + TP_RULES_LIST_LEFT.ResumeLayout(False) + TP_RULES_LIST_LEFT.PerformLayout() + Me.ResumeLayout(False) + + End Sub + + Private WithEvents TXT_UP_INTERVAL As PersonalUtilities.Forms.Controls.TextBoxExtended + Private WithEvents TXT_PERSONAL_RULE As PersonalUtilities.Forms.Controls.TextBoxExtended + Private WithEvents CONTAINER_LIST As ToolStripContainer + Private WithEvents LIST_RULES As ListBox + Private WithEvents OPT_RULES_LIST As RadioButton + Private WithEvents CH_PROTECTED As CheckBox + Private WithEvents CH_FORCE_UPDATE As CheckBox + Private WithEvents CH_LOG_ERR As CheckBox + Private WithEvents CH_RULES_REPLACE_CONFIG As CheckBox + Private WithEvents CH_UPDATE_CONF As CheckBox + Private WithEvents CH_UPDATE_RULES_CONST As CheckBox + Private WithEvents CH_CONFIG_MANUAL_MODE As CheckBox + End Class +End Namespace \ No newline at end of file diff --git a/SCrawler/API/OnlyFans/OnlyFansAdvancedSettingsForm.resx b/SCrawler/API/OnlyFans/OnlyFansAdvancedSettingsForm.resx new file mode 100644 index 0000000..596c17c --- /dev/null +++ b/SCrawler/API/OnlyFans/OnlyFansAdvancedSettingsForm.resx @@ -0,0 +1,186 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + False + + + False + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6 + JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAACOElE + QVQ4T2P4//8/QczOJyyqHpzfiE0OQwAZC8iqszAzs7CJ69o4BR768V/W2jcGXQ0KB4aFNS3dDQtnrbCb + ePCK48wTN1wXXXzge/jXf/clV55zC4hIIatF0cjIyMikElzc57z0wX+XHd/+2+//99/ywP//xlu//tdb + +eK/4Zp3/1WTOhYzARViNUAluKjTdf37/0ZTTn9TbdhwXblhwwW1/qOP1Ja9+K8w+95/6cm3/6v2Xvkv + qKjniGGAoIqRpW3/4e8S9uGdzFz82gwMDFxAzCxm4ZegtuLDf+VJ1/8rZM25IqLvnM/CximCYYCic1QN + v7x2JIwPwyrJ3XNUylddE9G2TWNmZOBDl4czmJiZMSRBmFdSyYyJgUEQmxwIYxWEYXZBCUls4sgYq6CA + prWNbtG8nXKeaVPR5XiVjSxEzf0yYXy4BBMLO6eQjoOXZvrkbbazrv53Xf/2v4CSbjBMXkhBl1/CMyNZ + qWnvGy5pNQ+YONwAfjXzAOupl/47LLr333L50/96q9/8l23YdES6cO5KuYqVW+R7Tj6SnfP0v4hryjyY + HhQDmFjYeHVKFp7WX/Xuv9Kq9/+Vd/z7r7rv/3+l7f//y676DEwDN/9L+BVvYkKLCTgDhNkkVUyVlr74 + qbbz73/VOTc/qsy89kWx+9h7qbQpJwS1bbOAscGGrB6EUTggLOqf16C55ft/HlnNAFZOXgVWdi4FRgYG + VnR1MIwhwMTCyqEQ37qEmZVDFF0OE/9nAACtFF4Ey6OP+wAAAABJRU5ErkJggg== + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO + xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go + tbbB43rK5xSAQq1VYFtmeQBoqZTSreVZvgTknM8yyyjA/qodsDF9gspD2Bj6B+DH+NqzhQQAG+POMnSX + AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO + xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go + tbbB43rK5xSAQq1VYFtmeQBoqZTSreVZvgTknM8yyyjA/qodsDF9gspD2Bj6B+DH+NqzhQQAG+POMnSX + AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC + + + + False + + + False + + + False + + + 17, 17 + + + False + + + 17, 17 + + + False + + + False + + + 17, 17 + + \ No newline at end of file diff --git a/SCrawler/API/OnlyFans/OnlyFansAdvancedSettingsForm.vb b/SCrawler/API/OnlyFans/OnlyFansAdvancedSettingsForm.vb new file mode 100644 index 0000000..fb3799b --- /dev/null +++ b/SCrawler/API/OnlyFans/OnlyFansAdvancedSettingsForm.vb @@ -0,0 +1,165 @@ +' Copyright (C) Andy https://github.com/AAndyProgram +' This program is free software: you can redistribute it and/or modify +' it under the terms of the GNU General Public License as published by +' the Free Software Foundation, either version 3 of the License, or +' (at your option) any later version. +' +' This program is distributed in the hope that it will be useful, +' but WITHOUT ANY WARRANTY +Imports PersonalUtilities.Forms +Imports PersonalUtilities.Forms.Controls.Base +Imports PersonalUtilities.Forms.Toolbars +Namespace API.OnlyFans + Friend Class OnlyFansAdvancedSettingsForm +#Region "Declarations" + Private WithEvents MyDefs As DefaultFormOptions + Friend Property CurrentRulesEnv As DynamicRulesEnv + Private ReadOnly Property CurrentRulesEnv_LIST As DynamicRulesEnv +#End Region +#Region "Initializer" + Friend Sub New(ByVal rules As DynamicRulesEnv) + InitializeComponent() + MyDefs = New DefaultFormOptions(Me, Settings.Design) + CurrentRulesEnv = rules + CurrentRulesEnv_LIST = New DynamicRulesEnv + CurrentRulesEnv_LIST.Copy(rules, False) + End Sub +#End Region +#Region "Form handlers" + Private Sub OnlyFansAdvancedSettingsForm_Load(sender As Object, e As EventArgs) Handles Me.Load + With MyDefs + .MyViewInitialize() + .AddOkCancelToolbar() + + .MyEditToolbar = New EditToolbar(Me,, CONTAINER_LIST.TopToolStripPanel) With {.Buttons = Nothing} + .MyEditToolbar.AddThisToolbar() + + With CurrentRulesEnv + Select Case .Mode + Case DynamicRulesEnv.Modes.List : OPT_RULES_LIST.Checked = True + Case DynamicRulesEnv.Modes.Personal : TXT_PERSONAL_RULE.Checked = True + End Select + CH_LOG_ERR.Checked = .AddErrorsToLog + CH_RULES_REPLACE_CONFIG.Checked = .RulesReplaceConfig + CH_UPDATE_RULES_CONST.Checked = .RulesUpdateConst + CH_CONFIG_MANUAL_MODE.Checked = .RulesConfigManualMode + CH_UPDATE_CONF.Checked = .ConfigAutoUpdate + TXT_UP_INTERVAL.Text = .UpdateInterval + If Not .PersonalRule.IsEmptyString Then TXT_PERSONAL_RULE.Text = .PersonalRule + Refill() + CH_PROTECTED.Checked = .ProtectFile + CH_FORCE_UPDATE.Checked = .RulesForceUpdateRequired + End With + + .MyFieldsCheckerE = New FieldsChecker + .MyFieldsCheckerE.AddControl(Of Integer)(TXT_UP_INTERVAL, TXT_UP_INTERVAL.CaptionText,, + New FieldsCheckerProviderSimple(Function(v) IIf(AConvert(Of Integer)(v, 0, EDP.ReturnValue) > 0, v, Nothing), + "The value of [{0}] field must be greater than 0")) + .MyFieldsCheckerE.EndLoaderOperations() + + .EndLoaderOperations() + End With + End Sub + Private Sub OnlyFansAdvancedSettingsForm_Disposed(sender As Object, e As EventArgs) Handles Me.Disposed + CurrentRulesEnv_LIST.Dispose() + End Sub +#End Region +#Region "Refill" + Private Sub Refill() + With CurrentRulesEnv_LIST + Dim ls% = _LatestSelected + LIST_RULES.Items.Clear() + If .Count > 0 Then LIST_RULES.Items.AddRange(.Select(Function(r) r.UrlRepo).Cast(Of Object).ToArray) + Dim lim% = LIST_RULES.Items.Count - 1 + If (ls - 1).ValueBetween(0, lim) Then + ls -= 1 + ElseIf (ls + 1).ValueBetween(0, lim) Then + ls += 1 + End If + If ls.ValueBetween(0, lim) Then LIST_RULES.SelectedIndex = ls Else ls = -1 + _LatestSelected = ls + End With + End Sub +#End Region +#Region "OK, Cancel" + Private Sub MyDefs_ButtonOkClick(ByVal Sender As Object, ByVal e As KeyHandleEventArgs) Handles MyDefs.ButtonOkClick + If MyDefs.MyFieldsChecker.AllParamsOK Then + With CurrentRulesEnv + .Copy(CurrentRulesEnv_LIST, True) + .ProtectFile = CH_PROTECTED.Checked + .UpdateInterval = AConvert(Of Integer)(TXT_UP_INTERVAL.Text, DynamicRulesEnv.UpdateIntervalDefault) + .Mode = If(TXT_PERSONAL_RULE.Checked, DynamicRulesEnv.Modes.Personal, DynamicRulesEnv.Modes.List) + .PersonalRule = TXT_PERSONAL_RULE.Text + .RulesReplaceConfig = CH_RULES_REPLACE_CONFIG.Checked + .RulesUpdateConst = CH_UPDATE_RULES_CONST.Checked + .RulesConfigManualMode = CH_CONFIG_MANUAL_MODE.Checked + .ConfigAutoUpdate = CH_UPDATE_CONF.Checked + .AddErrorsToLog = CH_LOG_ERR.Checked + If CH_FORCE_UPDATE.Checked Then .RulesForceUpdateRequired = True + .NeedToSave = True + End With + MyDefs.CloseForm() + End If + End Sub +#End Region +#Region "Add, Delete" + Private Sub MyDefs_ButtonAddClick(ByVal Sender As Object, ByVal e As EditToolbarEventArgs) Handles MyDefs.ButtonAddClick + Const msgTitle$ = "Add a rule" + Dim i% + Dim rule As DynamicRulesValue + Dim r$ = InputBoxE("Enter a valid rules URL:", msgTitle) + If Not r.IsEmptyString Then + rule = Rules.ParseURL(r) + If rule.Valid Then + i = CurrentRulesEnv_LIST.IndexOf(r) + If i >= 0 Then + MsgBoxE({$"The rule you entered already exists:{vbCr}{rule.UrlRepo}", msgTitle}, vbCritical) + Else + CurrentRulesEnv_LIST.Add(rule, False, False) + Refill() + End If + Else + MsgBoxE({$"The rule you entered has an incompatible format:{vbCr}{r}", msgTitle}, vbCritical) + End If + End If + End Sub + Private Sub MyDefs_ButtonDeleteClickE(ByVal Sender As Object, ByVal e As EditToolbarEventArgs) Handles MyDefs.ButtonDeleteClickE + If _LatestSelected.ValueBetween(0, LIST_RULES.Items.Count - 1) Then + Dim r$ = LIST_RULES.Items(_LatestSelected) + If MsgBoxE({$"Are you sure you want to delete the following rule?{vbCr}{r}", "Delete a rule"}, vbExclamation,,, {"Process", "Cancel"}) = 0 Then + If CurrentRulesEnv_LIST.RemoveAt(_LatestSelected) Then + LIST_RULES.Items.RemoveAt(_LatestSelected) + Refill() + Else + MsgBoxE({$"The following rule cannot be deleted:{vbCr}{r}", "Delete a rule"}, vbCritical) + End If + End If + End If + End Sub +#End Region +#Region "Options" + Private Sub TXT_UP_INTERVAL_ActionOnButtonClick(ByVal Sender As Object, ByVal e As ActionButtonEventArgs) Handles TXT_UP_INTERVAL.ActionOnButtonClick + If e.DefaultButton = ActionButton.DefaultButtons.Refresh Then TXT_UP_INTERVAL.Text = DynamicRulesEnv.UpdateIntervalDefault + End Sub + Private Sub TXT_PERSONAL_RULE_ActionOnCheckedChange(ByVal Sender As Object, ByVal e As EventArgs, ByVal Checked As Boolean) Handles TXT_PERSONAL_RULE.ActionOnCheckedChange + Mode_CheckedChanged() + End Sub + Private Sub OPT_RULES_LIST_CheckedChanged(sender As Object, e As EventArgs) + Mode_CheckedChanged() + End Sub + Private Sub Mode_CheckedChanged() + Dim e As Boolean = TXT_PERSONAL_RULE.Checked + TXT_PERSONAL_RULE.Enabled(False) = e + CONTAINER_LIST.Enabled = Not e + CH_PROTECTED.Enabled = Not e + CH_FORCE_UPDATE.Enabled = Not e + End Sub +#End Region +#Region "List handlers" + Private _LatestSelected As Integer = -1 + Private Sub LIST_RULES_SelectedIndexChanged(sender As Object, e As EventArgs) Handles LIST_RULES.SelectedIndexChanged + _LatestSelected = LIST_RULES.SelectedIndex + End Sub +#End Region + End Class +End Namespace \ No newline at end of file diff --git a/SCrawler/API/OnlyFans/SiteSettings.vb b/SCrawler/API/OnlyFans/SiteSettings.vb index 0698f86..a2ebed7 100644 --- a/SCrawler/API/OnlyFans/SiteSettings.vb +++ b/SCrawler/API/OnlyFans/SiteSettings.vb @@ -9,13 +9,12 @@ Imports SCrawler.API.Base Imports SCrawler.Plugin Imports SCrawler.Plugin.Attributes -Imports PersonalUtilities.Forms Imports PersonalUtilities.Tools.Web.Clients Imports PersonalUtilities.Tools.Web.Cookies Imports PersonalUtilities.Functions.RegularExpressions Imports DN = SCrawler.API.Base.DeclaredNames Namespace API.OnlyFans - + Friend Class SiteSettings : Inherits SiteSettingsBase #Region "Declarations" #Region "Categories" @@ -74,30 +73,6 @@ Namespace API.OnlyFans Return String.Empty End Function #End Region -#Region "Rules" - Private ReadOnly Property LastDateUpdated_XML As PropertyValue - Friend Property LastDateUpdated As Date - Get - Return LastDateUpdated_XML.Value - End Get - Set(ByVal d As Date) - LastDateUpdated_XML.Value = d - End Set - End Property - - Friend ReadOnly Property UseOldAuthRules As PropertyValue - - Friend ReadOnly Property DynamicRulesUpdateInterval As PropertyValue - - Private ReadOnly Property DynamicRulesUpdateIntervalProvider As IFormatProvider - - Friend ReadOnly Property DynamicRules As PropertyValue -#End Region #Region "OFScraper" Private ReadOnly Property OFScraperPath_XML As PropertyValue @@ -150,7 +125,10 @@ Namespace API.OnlyFans Friend Sub New(ByVal AccName As String, ByVal Temp As Boolean) MyBase.New("OnlyFans", ".onlyfans.com", AccName, Temp, My.Resources.SiteResources.OnlyFansIcon_32, My.Resources.SiteResources.OnlyFansPic_32) - CheckOFSConfig() + If Rules Is Nothing Then + Rules = New DynamicRulesEnv + Rules.Update(False, True) + End If _AllowUserAgentUpdate = False @@ -184,13 +162,6 @@ Namespace API.OnlyFans DownloadHighlights = New PropertyValue(True) DownloadChatMedia = New PropertyValue(True) - LastDateUpdated_XML = New PropertyValue(Now.AddYears(-1), GetType(Date)) - 'URGENT: OF [UseOldAuthRules = True] - UseOldAuthRules = New PropertyValue(True) - DynamicRulesUpdateInterval = New PropertyValue(60 * 24) - DynamicRulesUpdateIntervalProvider = New FieldsCheckerProviderSimple(Function(v) IIf(AConvert(Of Integer)(v, 0) > 0, v, Nothing), - "The value of [{0}] field must be greater than 0") - DynamicRules = New PropertyValue(String.Empty, GetType(String)) OFScraperPath_XML = New PropertyValue(String.Empty, GetType(String)) If ACheck(OFScraperPath_XML.Value) Then Dim f As SFile = OFScraperPath_XML.Value @@ -218,21 +189,28 @@ Namespace API.OnlyFans End Function #End Region #Region "Update" - Private __UseOldAuthRules As Boolean = True - Private __DynamicRules As String = String.Empty Friend Overrides Sub BeginUpdate() - __UseOldAuthRules = UseOldAuthRules.Value - __DynamicRules = AConvert(Of String)(DynamicRules.Value, String.Empty) + _TempSettingsEnv.DisposeIfReady + _TempSettingsEnv = Nothing MyBase.BeginUpdate() End Sub Friend Overrides Sub Update() If _SiteEditorFormOpened Then - If Not __UseOldAuthRules = CBool(UseOldAuthRules.Value) Or Not AEquals(Of String)(__DynamicRules, DynamicRules.Value) Then _ - LastDateUpdated = Now.AddYears(-1) Responser.Cookies.Changed = False + If If(_TempSettingsEnv?.NeedToSave, False) Then Rules.Copy(_TempSettingsEnv, True) : Rules.Save() End If MyBase.Update() End Sub + Friend Overrides Sub EndUpdate() + _TempSettingsEnv.DisposeIfReady + _TempSettingsEnv = Nothing + MyBase.EndUpdate() + End Sub + Private _TempSettingsEnv As DynamicRulesEnv = Nothing + Friend Overrides Sub OpenSettingsForm() + If _TempSettingsEnv Is Nothing Then _TempSettingsEnv = New DynamicRulesEnv : _TempSettingsEnv.Copy(Rules, False) + Using f As New OnlyFansAdvancedSettingsForm(_TempSettingsEnv) : f.ShowDialog() : End Using + End Sub #End Region #Region "Download" Friend Overrides Function BaseAuthExists() As Boolean diff --git a/SCrawler/API/OnlyFans/UserData.vb b/SCrawler/API/OnlyFans/UserData.vb index 60e7176..777e623 100644 --- a/SCrawler/API/OnlyFans/UserData.vb +++ b/SCrawler/API/OnlyFans/UserData.vb @@ -91,6 +91,7 @@ Namespace API.OnlyFans End Sub Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken) Try + _DownloadingException_AuthFileUpdate = False If Not MySettings.SessionAborted Then ValidateOFScraper() _AbsMediaIndex = 0 @@ -537,45 +538,16 @@ Namespace API.OnlyFans End Sub #End Region #Region "Auth" - Private ReadOnly Property AuthFile As SFile - Get - Dim f As SFile = MySettings.Responser.File - f.Name &= "_Auth" - f.Extension = "json" - Return f - End Get - End Property - Private Function UpdateSignature(ByVal Path As String, Optional ByVal ForceUpdateAuth As Boolean = False, - Optional ByVal Round As Integer = 0) As Boolean + Private Function UpdateSignature(ByVal Path As String) As Boolean Try - If UpdateAuthFile(ForceUpdateAuth) Then + If Not Rules.Update(False) Then Rules.Update(True) + If Rules.Exists Then Const nullMsg$ = "The auth parameter(s) is null" - Const formatMidPart$ = ":{0}:{1:x}:" - Dim j As EContainer - Try - j = JsonDocument.Parse(AuthFile.GetText) - Catch jex As Exception - If Round = 0 Then - AuthFile.Delete() - UpdateAuthFile(True) - Return UpdateSignature(Path, ForceUpdateAuth, Round + 1) - Else - MySettings.SessionAborted = True - Return False - End If - End Try + Dim j As EContainer = Rules.CurrentContainer If Not j Is Nothing Then - Dim pattern$ = j.Value("format") + Dim pattern$ = DynamicRulesEnv.GetFormat(j) - If Not pattern.IsEmptyString Then - pattern = pattern.Replace("{}", "{0}").Replace("{:x}", "{1:x}") - ElseIf Not j.Value("prefix").IsEmptyString And Not j.Value("suffix").IsEmptyString Then - pattern = j.Value("prefix") & formatMidPart & j.Value("suffix") - ElseIf Not j.Value("start").IsEmptyString And Not j.Value("end").IsEmptyString Then - pattern = j.Value("start") & formatMidPart & j.Value("end") - Else - Throw New ArgumentNullException("format", nullMsg) - End If + If pattern.IsEmptyString Then Throw New ArgumentNullException("format", nullMsg) Dim li%() = j("checksum_indexes").Select(Function(e) CInt(e(0).Value)).ToArray @@ -599,42 +571,16 @@ Namespace API.OnlyFans Responser.Headers.Add(HeaderSign, sign) Responser.Headers.Add(HeaderTime, t) - j.Dispose() Return True End If + Else + MySettings.SessionAborted = True End If Return False Catch ex As Exception Return ErrorsDescriber.Execute(EDP.SendToLog + EDP.ReturnValue, ex, $"{ToStringForLog()}: UpdateSignature", False) End Try End Function - Private Function UpdateAuthFile(ByVal Force As Boolean) As Boolean - Const urlOld$ = "https://raw.githubusercontent.com/DATAHOARDERS/dynamic-rules/main/onlyfans.json" - Const urlNew$ = "https://raw.githubusercontent.com/DIGITALCRIMINALS/dynamic-rules/main/onlyfans.json" - Try - If MySettings.LastDateUpdated.AddMinutes(CInt(MySettings.DynamicRulesUpdateInterval.Value)) < Now Or Not AuthFile.Exists Or Force Then - Dim r$ = GetWebString(If(ACheck(Of String)(MySettings.DynamicRules.Value), - CStr(MySettings.DynamicRules.Value), - IIf(MySettings.UseOldAuthRules.Value, urlOld, urlNew)),, EDP.ReturnValue) - Dim checkFormat As Func(Of EContainer, Boolean) = - Function(jj) Not jj.Value("format").IsEmptyString OrElse - (Not jj.Value("prefix").IsEmptyString And Not jj.Value("suffix").IsEmptyString) OrElse - (Not jj.Value("start").IsEmptyString And Not jj.Value("end").IsEmptyString) - If Not r.IsEmptyString Then - Using j As EContainer = JsonDocument.Parse(r, EDP.ReturnValue) - If j.ListExists Then - If checkFormat(j) And j("checksum_indexes").ListExists And - Not j.Value("static_param").IsEmptyString And Not j.Value("checksum_constant").IsEmptyString Then _ - TextSaver.SaveTextToFile(r, AuthFile, True, False, EDP.ThrowException) : MySettings.LastDateUpdated = Now - End If - End Using - End If - End If - Return AuthFile.Exists - Catch ex As Exception - Return ErrorsDescriber.Execute(EDP.SendToLog + EDP.ReturnValue, ex, $"{ToStringForLog()}: UpdateAuthFile", False) - End Try - End Function Private Function GetHashSha1(ByVal Input As String) As String Dim s As New Security.Cryptography.SHA1CryptoServiceProvider Dim inputBytes() As Byte = System.Text.Encoding.UTF8.GetBytes(Input) @@ -666,35 +612,51 @@ Namespace API.OnlyFans Private Function OFS_CreateConfig() As SFile Try Const confMainPattern$ = "{0}"": ""([^""]*)""" + Const confMainPatternRulesManual$ = "DYNAMIC_RULE"": (""[^""]*"")" If OFSCache Is Nothing Then OFSCache = If(IsSingleObjectDownload, Settings.Cache.NewInstance, CreateCache()) Dim currentCache As CacheKeeper = OFSCache.NewInstance currentCache.Validate() Dim cacheRoot As SFile = currentCache.NewPath cacheRoot.Exists(SFO.Path, True, EDP.ThrowException) - Dim f As SFile = OFScraperConfigPatternFile + Dim f As SFile = Rules.OFScraperConfigPatternFile Dim configText$ - CheckOFSConfig() If f.Exists Then Dim replaceValue$ = String.Empty Dim rp As RParams = RParams.DMS(String.Empty, 1, RegexReturn.Replace, RegexOptions.IgnoreCase, CType(Function(input) replaceValue, Func(Of String, String)), String.Empty, EDP.ReturnValue) Dim ff As SFile configText = f.GetText - Dim updateConf As Action(Of String, String) = Sub(ByVal patternValue As String, ByVal __replaceValue As String) - rp.Pattern = String.Format(confMainPattern, patternValue) - rp.Nothing = configText - replaceValue = __replaceValue - configText = RegexReplace(configText, rp) - End Sub + Dim updateConf As Action(Of String, String, Boolean) = + Sub(ByVal patternValue As String, ByVal __replaceValue As String, ByVal __isRules As Boolean) + rp.Pattern = String.Format(IIf(__isRules, confMainPatternRulesManual, confMainPattern), patternValue) + rp.Nothing = configText + replaceValue = __replaceValue + configText = RegexReplace(configText, rp) + End Sub If Not configText.IsEmptyString Then - updateConf("save_location", cacheRoot.PathNoSeparator.Replace("\", "/")) + updateConf("save_location", cacheRoot.PathNoSeparator.Replace("\", "/"), False) If ACheck(MySettings.OFScraperMP4decrypt.Value) Then ff = CStr(MySettings.OFScraperMP4decrypt.Value) - If ff.Exists Then updateConf("mp4decrypt", ff.ToString.Replace("\", "/")) + If ff.Exists Then updateConf("mp4decrypt", ff.ToString.Replace("\", "/"), False) + End If + If Settings.FfmpegFile.Exists Then updateConf("ffmpeg", Settings.FfmpegFile.File.ToString.Replace("\", "/"), False) + updateConf("key-mode-default", CStr(MySettings.KeyModeDefault.Value).IfNullOrEmpty(SiteSettings.KeyModeDefault_Default), False) + updateConf("keydb_api", CStr(MySettings.Keydb_Api.Value), False) + If Rules.RulesReplaceConfig Then + If Rules.RulesConfigManualMode Then + updateConf(DynamicRulesEnv.DynamicRulesConfig_Mode_NodeName, "manual", False) + configText = configText.Replace(DynamicRulesEnv.DynamicRulesConfigNodeName_URL, DynamicRulesEnv.DynamicRulesConfigNodeName_RULES) + updateConf(DynamicRulesEnv.DynamicRulesConfigNodeName_RULES, Rules.CurrentContainerRulesText, True) + Else + Dim confUrlNode$ = If(Rules.RulesConstants.ContainsKey(DynamicRulesEnv.DynamicRulesConfigNodeName_URL_CONST_NAME), + Rules.RulesConstants(DynamicRulesEnv.DynamicRulesConfigNodeName_URL_CONST_NAME), + DynamicRulesEnv.DynamicRulesConfigNodeName_URL) + updateConf(DynamicRulesEnv.DynamicRulesConfigNodeName_URL, Rules.CurrentRule.UrlRaw, False) + configText = configText.Replace(DynamicRulesEnv.DynamicRulesConfigNodeName_URL, confUrlNode) + If Rules.RulesConstants.ContainsKey(DynamicRulesEnv.DynamicRulesConfig_Mode_NodeName) Then _ + updateConf(DynamicRulesEnv.DynamicRulesConfig_Mode_NodeName, Rules.RulesConstants(DynamicRulesEnv.DynamicRulesConfig_Mode_NodeName), False) + End If End If - If Settings.FfmpegFile.Exists Then updateConf("ffmpeg", Settings.FfmpegFile.File.ToString.Replace("\", "/")) - updateConf("key-mode-default", CStr(MySettings.KeyModeDefault.Value).IfNullOrEmpty(SiteSettings.KeyModeDefault_Default)) - updateConf("keydb_api", CStr(MySettings.Keydb_Api.Value)) f = currentCache f.Name = "config" f.Extension = "json" @@ -788,7 +750,7 @@ Namespace API.OnlyFans Protected Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False, Optional ByVal EObj As Object = Nothing) As Integer If Responser.StatusCode = Net.HttpStatusCode.BadRequest Then '400 - If Not _DownloadingException_AuthFileUpdate AndAlso UpdateAuthFile(True) Then + If Not _DownloadingException_AuthFileUpdate AndAlso Rules.Update(True) Then _DownloadingException_AuthFileUpdate = True Return 2 Else diff --git a/SCrawler/Download/Feed/DownloadFeedForm.Designer.vb b/SCrawler/Download/Feed/DownloadFeedForm.Designer.vb index 80a407b..3abf7f1 100644 --- a/SCrawler/Download/Feed/DownloadFeedForm.Designer.vb +++ b/SCrawler/Download/Feed/DownloadFeedForm.Designer.vb @@ -45,6 +45,8 @@ Namespace DownloadObjects Me.BTT_LOAD_SESSION_CHOOSE = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_COPY_TO = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_MOVE_TO = New System.Windows.Forms.ToolStripMenuItem() + Me.BTT_COPY_SPEC_TO = New System.Windows.Forms.ToolStripMenuItem() + Me.BTT_MOVE_SPEC_TO = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_LOAD_FAV = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_LOAD_SPEC = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_FEED_ADD_FAV = New System.Windows.Forms.ToolStripMenuItem() @@ -61,6 +63,7 @@ Namespace DownloadObjects Me.BTT_CURR_SESSION_SET = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_MERGE_SESSIONS = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_CLEAR_DAILY = New System.Windows.Forms.ToolStripMenuItem() + Me.BTT_RESET_DAILY = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_MERGE_FEEDS = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_CHECK_ALL = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_CHECK_NONE = New System.Windows.Forms.ToolStripMenuItem() @@ -72,9 +75,7 @@ Namespace DownloadObjects Me.BTT_DOWN_SELECTED = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_REFRESH = New System.Windows.Forms.ToolStripButton() Me.TP_DATA = New System.Windows.Forms.TableLayoutPanel() - Me.BTT_COPY_SPEC_TO = New System.Windows.Forms.ToolStripMenuItem() - Me.BTT_MOVE_SPEC_TO = New System.Windows.Forms.ToolStripMenuItem() - Me.BTT_RESET_DAILY = New System.Windows.Forms.ToolStripMenuItem() + Me.BTT_CURR_SESSION_SET_LAST = New System.Windows.Forms.ToolStripMenuItem() SEP_1 = New System.Windows.Forms.ToolStripSeparator() SEP_2 = New System.Windows.Forms.ToolStripSeparator() MENU_VIEW = New System.Windows.Forms.ToolStripDropDownButton() @@ -168,6 +169,11 @@ Namespace DownloadObjects MENU_LOAD_SEP_8.Name = "MENU_LOAD_SEP_8" MENU_LOAD_SEP_8.Size = New System.Drawing.Size(349, 6) ' + 'MENU_LOAD_SEP_9 + ' + MENU_LOAD_SEP_9.Name = "MENU_LOAD_SEP_9" + MENU_LOAD_SEP_9.Size = New System.Drawing.Size(349, 6) + ' 'ToolbarTOP ' Me.ToolbarTOP.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden @@ -181,7 +187,7 @@ Namespace DownloadObjects ' Me.MENU_LOAD_SESSION.AutoToolTip = False Me.MENU_LOAD_SESSION.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image - Me.MENU_LOAD_SESSION.DropDownItems.AddRange(New System.Windows.Forms.ToolStripItem() {Me.BTT_LOAD_SESSION_CURRENT, Me.BTT_LOAD_SESSION_LAST, Me.BTT_LOAD_SESSION_CHOOSE, MENU_LOAD_SEP_0, Me.BTT_COPY_TO, Me.BTT_MOVE_TO, MENU_LOAD_SEP_1, Me.BTT_COPY_SPEC_TO, Me.BTT_MOVE_SPEC_TO, MENU_LOAD_SEP_2, Me.BTT_LOAD_FAV, Me.BTT_LOAD_SPEC, MENU_LOAD_SEP_3, Me.BTT_FEED_ADD_FAV, Me.BTT_FEED_ADD_FAV_REMOVE, Me.BTT_FEED_REMOVE_FAV, MENU_LOAD_SEP_4, Me.BTT_FEED_ADD_SPEC, Me.BTT_FEED_ADD_SPEC_REMOVE, Me.BTT_FEED_REMOVE_SPEC, MENU_LOAD_SEP_5, Me.BTT_FEED_CLEAR_FAV, Me.BTT_FEED_CLEAR_SPEC, Me.BTT_FEED_DELETE_SPEC, Me.BTT_FEED_DELETE_DAILY_LIST, Me.BTT_FEED_DELETE_DAILY_DATE, MENU_LOAD_SEP_6, Me.BTT_CURR_SESSION_SET, Me.BTT_MERGE_SESSIONS, Me.BTT_CLEAR_DAILY, Me.BTT_RESET_DAILY, MENU_LOAD_SEP_7, Me.BTT_MERGE_FEEDS, MENU_LOAD_SEP_8, Me.BTT_CHECK_ALL, Me.BTT_CHECK_NONE, MENU_LOAD_SEP_9, Me.BTT_VIEW_SAVE, Me.BTT_VIEW_LOAD}) + Me.MENU_LOAD_SESSION.DropDownItems.AddRange(New System.Windows.Forms.ToolStripItem() {Me.BTT_LOAD_SESSION_CURRENT, Me.BTT_LOAD_SESSION_LAST, Me.BTT_LOAD_SESSION_CHOOSE, MENU_LOAD_SEP_0, Me.BTT_COPY_TO, Me.BTT_MOVE_TO, MENU_LOAD_SEP_1, Me.BTT_COPY_SPEC_TO, Me.BTT_MOVE_SPEC_TO, MENU_LOAD_SEP_2, Me.BTT_LOAD_FAV, Me.BTT_LOAD_SPEC, MENU_LOAD_SEP_3, Me.BTT_FEED_ADD_FAV, Me.BTT_FEED_ADD_FAV_REMOVE, Me.BTT_FEED_REMOVE_FAV, MENU_LOAD_SEP_4, Me.BTT_FEED_ADD_SPEC, Me.BTT_FEED_ADD_SPEC_REMOVE, Me.BTT_FEED_REMOVE_SPEC, MENU_LOAD_SEP_5, Me.BTT_FEED_CLEAR_FAV, Me.BTT_FEED_CLEAR_SPEC, Me.BTT_FEED_DELETE_SPEC, Me.BTT_FEED_DELETE_DAILY_LIST, Me.BTT_FEED_DELETE_DAILY_DATE, MENU_LOAD_SEP_6, Me.BTT_CURR_SESSION_SET, Me.BTT_CURR_SESSION_SET_LAST, Me.BTT_MERGE_SESSIONS, Me.BTT_CLEAR_DAILY, Me.BTT_RESET_DAILY, MENU_LOAD_SEP_7, Me.BTT_MERGE_FEEDS, MENU_LOAD_SEP_8, Me.BTT_CHECK_ALL, Me.BTT_CHECK_NONE, MENU_LOAD_SEP_9, Me.BTT_VIEW_SAVE, Me.BTT_VIEW_LOAD}) Me.MENU_LOAD_SESSION.Image = Global.SCrawler.My.Resources.Resources.ArrowDownPic_Blue_24 Me.MENU_LOAD_SESSION.ImageTransparentColor = System.Drawing.Color.Magenta Me.MENU_LOAD_SESSION.Name = "MENU_LOAD_SESSION" @@ -223,6 +229,24 @@ Namespace DownloadObjects Me.BTT_MOVE_TO.Size = New System.Drawing.Size(352, 22) Me.BTT_MOVE_TO.Text = "Move checked to..." ' + 'BTT_COPY_SPEC_TO + ' + Me.BTT_COPY_SPEC_TO.AutoToolTip = True + Me.BTT_COPY_SPEC_TO.Image = Global.SCrawler.My.Resources.Resources.PastePic_32 + Me.BTT_COPY_SPEC_TO.Name = "BTT_COPY_SPEC_TO" + Me.BTT_COPY_SPEC_TO.Size = New System.Drawing.Size(352, 22) + Me.BTT_COPY_SPEC_TO.Text = "Copy feed/session files to..." + Me.BTT_COPY_SPEC_TO.ToolTipText = "Copy all the files of the loaded feed/session to..." + ' + 'BTT_MOVE_SPEC_TO + ' + Me.BTT_MOVE_SPEC_TO.AutoToolTip = True + Me.BTT_MOVE_SPEC_TO.Image = Global.SCrawler.My.Resources.Resources.CutPic_48 + Me.BTT_MOVE_SPEC_TO.Name = "BTT_MOVE_SPEC_TO" + Me.BTT_MOVE_SPEC_TO.Size = New System.Drawing.Size(352, 22) + Me.BTT_MOVE_SPEC_TO.Text = "Move feed/session files to..." + Me.BTT_MOVE_SPEC_TO.ToolTipText = "Move all the files of the loaded feed/session to..." + ' 'BTT_LOAD_FAV ' Me.BTT_LOAD_FAV.Image = Global.SCrawler.My.Resources.Resources.HeartPic_32 @@ -342,6 +366,15 @@ Namespace DownloadObjects Me.BTT_CLEAR_DAILY.Text = "Clear session" Me.BTT_CLEAR_DAILY.ToolTipText = "Clear current session" ' + 'BTT_RESET_DAILY + ' + Me.BTT_RESET_DAILY.AutoToolTip = True + Me.BTT_RESET_DAILY.Image = Global.SCrawler.My.Resources.Resources.RefreshPic_24 + Me.BTT_RESET_DAILY.Name = "BTT_RESET_DAILY" + Me.BTT_RESET_DAILY.Size = New System.Drawing.Size(352, 22) + Me.BTT_RESET_DAILY.Text = "Reset current session" + Me.BTT_RESET_DAILY.ToolTipText = "A new file will be created for the current session" + ' 'BTT_MERGE_FEEDS ' Me.BTT_MERGE_FEEDS.AutoToolTip = True @@ -443,37 +476,12 @@ Namespace DownloadObjects Me.TP_DATA.Size = New System.Drawing.Size(484, 436) Me.TP_DATA.TabIndex = 1 ' - 'MENU_LOAD_SEP_9 + 'BTT_CURR_SESSION_SET_LAST ' - MENU_LOAD_SEP_9.Name = "MENU_LOAD_SEP_9" - MENU_LOAD_SEP_9.Size = New System.Drawing.Size(349, 6) - ' - 'BTT_COPY_SPEC_TO - ' - Me.BTT_COPY_SPEC_TO.AutoToolTip = True - Me.BTT_COPY_SPEC_TO.Image = Global.SCrawler.My.Resources.Resources.PastePic_32 - Me.BTT_COPY_SPEC_TO.Name = "BTT_COPY_SPEC_TO" - Me.BTT_COPY_SPEC_TO.Size = New System.Drawing.Size(352, 22) - Me.BTT_COPY_SPEC_TO.Text = "Copy feed/session files to..." - Me.BTT_COPY_SPEC_TO.ToolTipText = "Copy all the files of the loaded feed/session to..." - ' - 'BTT_MOVE_SPEC_TO - ' - Me.BTT_MOVE_SPEC_TO.AutoToolTip = True - Me.BTT_MOVE_SPEC_TO.Image = Global.SCrawler.My.Resources.Resources.CutPic_48 - Me.BTT_MOVE_SPEC_TO.Name = "BTT_MOVE_SPEC_TO" - Me.BTT_MOVE_SPEC_TO.Size = New System.Drawing.Size(352, 22) - Me.BTT_MOVE_SPEC_TO.Text = "Move feed/session files to..." - Me.BTT_MOVE_SPEC_TO.ToolTipText = "Move all the files of the loaded feed/session to..." - ' - 'BTT_RESET_DAILY - ' - Me.BTT_RESET_DAILY.AutoToolTip = True - Me.BTT_RESET_DAILY.Image = Global.SCrawler.My.Resources.Resources.RefreshPic_24 - Me.BTT_RESET_DAILY.Name = "BTT_RESET_DAILY" - Me.BTT_RESET_DAILY.Size = New System.Drawing.Size(352, 22) - Me.BTT_RESET_DAILY.Text = "Reset current session" - Me.BTT_RESET_DAILY.ToolTipText = "A new file will be created for the current session" + Me.BTT_CURR_SESSION_SET_LAST.Image = Global.SCrawler.My.Resources.Resources.ArrowDownPic_Blue_24 + Me.BTT_CURR_SESSION_SET_LAST.Name = "BTT_CURR_SESSION_SET_LAST" + Me.BTT_CURR_SESSION_SET_LAST.Size = New System.Drawing.Size(352, 22) + Me.BTT_CURR_SESSION_SET_LAST.Text = "Set last download session as current session" ' 'DownloadFeedForm ' @@ -534,5 +542,6 @@ Namespace DownloadObjects Private WithEvents BTT_COPY_SPEC_TO As ToolStripMenuItem Private WithEvents BTT_MOVE_SPEC_TO As ToolStripMenuItem Private WithEvents BTT_RESET_DAILY As ToolStripMenuItem + Private WithEvents BTT_CURR_SESSION_SET_LAST As ToolStripMenuItem End Class End Namespace \ No newline at end of file diff --git a/SCrawler/Download/Feed/DownloadFeedForm.resx b/SCrawler/Download/Feed/DownloadFeedForm.resx index 042d779..b7cdfd6 100644 --- a/SCrawler/Download/Feed/DownloadFeedForm.resx +++ b/SCrawler/Download/Feed/DownloadFeedForm.resx @@ -162,10 +162,10 @@ False - - 17, 17 - False + + 17, 17 + \ No newline at end of file diff --git a/SCrawler/Download/Feed/DownloadFeedForm.vb b/SCrawler/Download/Feed/DownloadFeedForm.vb index 5f2557c..a526191 100644 --- a/SCrawler/Download/Feed/DownloadFeedForm.vb +++ b/SCrawler/Download/Feed/DownloadFeedForm.vb @@ -515,17 +515,21 @@ Namespace DownloadObjects f = Downloader.FilesSessionActual(False) End If If f.Exists Then - If SelectedMode >= 0 Then - If SelectedMode = FeedModes.Saved Then LoadedSessionName = f.Name - FeedChangeMode(SelectedMode) + If GetSessionFile Then + If Not Downloader.FilesSessionActual(False) = f Then SessionFile = f + Else + If SelectedMode >= 0 Then + If SelectedMode = FeedModes.Saved Then LoadedSessionName = f.Name + FeedChangeMode(SelectedMode) + End If + DataList.Clear() + x = New XmlFile(f,, False) With {.AllowSameNames = True, .XmlReadOnly = True} + x.LoadData() + If x.Count > 0 Then DataList.ListAddList(x, lcr) + x.Dispose() + CleanDataList() + RefillList(False, False) End If - DataList.Clear() - x = New XmlFile(f,, False) With {.AllowSameNames = True, .XmlReadOnly = True} - x.LoadData() - If x.Count > 0 Then DataList.ListAddList(x, lcr) - x.Dispose() - CleanDataList() - RefillList(False, False) End If Else m.Text = "Saved sessions not found" @@ -553,6 +557,7 @@ Namespace DownloadObjects Dim data As IEnumerable(Of UserMediaD) = Nothing Dim dd As UserMediaD Dim __user As UserInfo + Dim __isSavedPosts As Boolean Dim data_files As IEnumerable(Of SFile) = Nothing Dim new_files As New List(Of SFile) Dim mm As UserMediaD @@ -671,8 +676,9 @@ Namespace DownloadObjects __user = mm.UserInfo mm_data = mm.Data mm_data.File = df - mm = New UserMediaD(mm_data, If(moveOptions.ReplaceUserProfile_Profile, mm.User), mm.Session, mm.Date) With {.IsSavedPosts = mm.IsSavedPosts} - If moveOptions.ReplaceUserProfile_Profile Is Nothing And mm.IsSavedPosts Then mm.UserInfo = __user + __isSavedPosts = mm.IsSavedPosts And moveOptions.ReplaceUserProfile_Profile Is Nothing + mm = New UserMediaD(mm_data, If(moveOptions.ReplaceUserProfile_Profile, mm.User), mm.Session, mm.Date) With {.IsSavedPosts = __isSavedPosts} + If __isSavedPosts Then mm.UserInfo = __user Downloader.Files(indx) = mm downloaderFilesUpdated = True End If @@ -701,8 +707,9 @@ Namespace DownloadObjects __user = mm.UserInfo mm_data = mm.Data mm_data.File = df - mm = New UserMediaD(mm_data, If(moveOptions.ReplaceUserProfile_Profile, mm.User), mm.Session, mm.Date) With {.IsSavedPosts = mm.IsSavedPosts} - If moveOptions.ReplaceUserProfile_Profile Is Nothing And mm.IsSavedPosts Then mm.UserInfo = __user + __isSavedPosts = mm.IsSavedPosts And moveOptions.ReplaceUserProfile_Profile Is Nothing + mm = New UserMediaD(mm_data, If(moveOptions.ReplaceUserProfile_Profile, mm.User), mm.Session, mm.Date) With {.IsSavedPosts = __isSavedPosts} + If __isSavedPosts Then mm.UserInfo = __user sessionData(indx) = mm sesFilesReplaced = True If DataList.Count > 0 Then @@ -922,10 +929,10 @@ Namespace DownloadObjects End Sub #End Region #Region "Sessions set, merge, clear" - Private Sub BTT_CURR_SESSION_SET_Click(sender As Object, e As EventArgs) Handles BTT_CURR_SESSION_SET.Click + Private Sub BTT_CURR_SESSION_SET_Click(sender As Object, e As EventArgs) Handles BTT_CURR_SESSION_SET.Click, BTT_CURR_SESSION_SET_LAST.Click Try Dim f As SFile = Nothing - SessionChooser(False,,,, True, f) + SessionChooser(sender Is BTT_CURR_SESSION_SET_LAST,,,, True, f) If Not f.IsEmptyString AndAlso f.Exists Then Downloader.FilesLoadLastSession(f) FeedChangeMode(FeedModes.Current) diff --git a/SCrawler/Download/Feed/FeedSpecial.vb b/SCrawler/Download/Feed/FeedSpecial.vb index c283ec7..288ade1 100644 --- a/SCrawler/Download/Feed/FeedSpecial.vb +++ b/SCrawler/Download/Feed/FeedSpecial.vb @@ -157,6 +157,7 @@ Namespace DownloadObjects Else user = Item.User End If + If isSaved Then isSaved = Item.UserInfo.Equals(DirectCast(user, UserDataBase).User) If Not If(user?.IsSubscription, False) Then Item = New UserMediaD(data, user, Item.Session, Item.Date) With {.IsSavedPosts = isSaved} Result = True @@ -176,9 +177,10 @@ Namespace DownloadObjects Dim m As UserMediaD = Items(indx) Dim mm As UserMedia = m.Data Dim user As UserInfo = m.UserInfo + Dim __isSavedPosts As Boolean = m.IsSavedPosts And MCTOptions.ReplaceUserProfile_Profile Is Nothing mm.File = NewFile - m = New UserMediaD(mm, If(MCTOptions.ReplaceUserProfile_Profile, m.User), m.Session, m.Date) With {.IsSavedPosts = m.IsSavedPosts} - If MCTOptions.ReplaceUserProfile_Profile Is Nothing And m.IsSavedPosts Then m.UserInfo = user + m = New UserMediaD(mm, If(MCTOptions.ReplaceUserProfile_Profile, m.User), m.Session, m.Date) With {.IsSavedPosts = __isSavedPosts} + If __isSavedPosts Then m.UserInfo = user Items(indx) = m _FilesUpdated = True End If diff --git a/SCrawler/MainFrameObjects.vb b/SCrawler/MainFrameObjects.vb index b46ff38..297a077 100644 --- a/SCrawler/MainFrameObjects.vb +++ b/SCrawler/MainFrameObjects.vb @@ -26,6 +26,7 @@ Friend Class MainFrameObjects : Implements INotificator AddHandler .TextAdded, AddressOf ProgramLog_TextAdded AddHandler .TextCleared, AddressOf ProgramLog_TextCleared End With + UpdateLogButton() End Sub #Region "Users" Friend Sub FocusUser(ByVal Key As String, Optional ByVal ActivateForm As Boolean = False) diff --git a/SCrawler/MainMod.vb b/SCrawler/MainMod.vb index 60d2d1f..ba5972a 100644 --- a/SCrawler/MainMod.vb +++ b/SCrawler/MainMod.vb @@ -83,6 +83,9 @@ Friend Module MainMod Friend Function ToStringDateDef(ByVal _Date As Date) As String Return ToStringDateDef(_DateN:=_Date) End Function + Friend Function ToDateDef(ByVal DateStr As String, Optional ByVal NothingArg As Object = Nothing) As Object + Return AConvert(Of Date)(DateStr, DateTimeDefaultProvider, NothingArg) + End Function Friend ReadOnly SessionDateTimeProvider As New ADateTime("yyyyMMdd_HHmmss") Friend ReadOnly FeedVideoLengthProvider As New ADateTime("hh\:mm\:ss") With {.TimeParseMode = ADateTime.TimeModes.TimeSpan} Friend ReadOnly LogConnector As New LogHost diff --git a/SCrawler/My Project/AssemblyInfo.vb b/SCrawler/My Project/AssemblyInfo.vb index f00c2f0..c09cf36 100644 --- a/SCrawler/My Project/AssemblyInfo.vb +++ b/SCrawler/My Project/AssemblyInfo.vb @@ -32,6 +32,6 @@ Imports System.Runtime.InteropServices ' by using the '*' as shown below: ' - - + + diff --git a/SCrawler/SCrawler.vbproj b/SCrawler/SCrawler.vbproj index 56dfb9e..cda0848 100644 --- a/SCrawler/SCrawler.vbproj +++ b/SCrawler/SCrawler.vbproj @@ -208,11 +208,18 @@ + True True OFResources.resx + + OnlyFansAdvancedSettingsForm.vb + + + Form + @@ -542,6 +549,9 @@ ResXFileCodeGenerator OFResources.Designer.vb + + OnlyFansAdvancedSettingsForm.vb + RedditViewSettingsForm.vb @@ -784,6 +794,9 @@ + + +