diff --git a/Changelog.md b/Changelog.md index 1b2280b..920162d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,17 @@ +# 2023.12.27.0 + +*2023-12-27* + +- Added + - Notification of new log data + - OnlyFans: **OF-Scrapper support to download DRM protected videos** + - Other improvements +- Fixed + - The default options are changed (`Favorite`, `Temporary`, etc.) when changing an account for a created user + - When changing the account for a created user, the new account does not apply to that user until SCrawler is restarted + - Saved posts: session file is not updated when new data is added + - Minor bugs + # 2023.12.15.0 *2023-12-15* diff --git a/ProgramScreenshots/SettingsGlobalNotifications.png b/ProgramScreenshots/SettingsGlobalNotifications.png index 20da7f9..87686aa 100644 Binary files a/ProgramScreenshots/SettingsGlobalNotifications.png and b/ProgramScreenshots/SettingsGlobalNotifications.png differ diff --git a/ProgramsComparison.md b/ProgramsComparison.md index 67a1066..1669f22 100644 --- a/ProgramsComparison.md +++ b/ProgramsComparison.md @@ -6,11 +6,19 @@ https://github.com/yt-dlp/yt-dlp/ SCrawler has advanced user management, collections, labels, groups, automatic downloads, a beautiful view, GUI, the ability to add plugins for other sites and much more. Just try it and compare. +# gallery-dl + +https://github.com/mikf/gallery-dl + +**Great powerful CLI tool that supports hundreds of sites.** + +SCrawler has advanced user management, collections, labels, groups, automatic downloads, a beautiful view, GUI, the ability to add plugins for other sites and much more. Just try it and compare. + # 4K Video Downloader https://www.4kdownload.com/-plbrz/video-downloader -| Option | SCrawler | 4K Stogram | +| Option | SCrawler | 4K Video Downloader | | ---- | ---- | ---- | | User managament | **Advanced** | No | | Automatic downloads | **Yes** | No | @@ -121,10 +129,3 @@ https://github.com/RipMeApp/ripme | Other sites support | **Yes** | No | | Still supported | **Yes** | **No (last release date May 4, 2021)** | -# gallery-dl - -https://github.com/mikf/gallery-dl - -**CLI tool** - -SCrawler has advanced user management, collections, labels, groups, automatic downloads, a beautiful view, GUI, the ability to add plugins for other sites and much more. Just try it and compare. \ No newline at end of file diff --git a/SCrawler/API/Base/ProfileSaved.vb b/SCrawler/API/Base/ProfileSaved.vb index e9e1f60..ffa067a 100644 --- a/SCrawler/API/Base/ProfileSaved.vb +++ b/SCrawler/API/Base/ProfileSaved.vb @@ -47,7 +47,7 @@ Namespace API.Base Progress.InformationTemporary = $"{HOST.Name} ({c - s}/{c}) Images: {_TotalImages}; Videos: {_TotalVideos}" End If End If - If _FeedDataExists Then Downloader.Files.Sort() + If _FeedDataExists Then Downloader.Files.Sort() : Downloader.FilesSave() End Sub Private Overloads Sub Download(ByVal Host As SettingsHost, ByVal Number As Integer, ByVal Count As Integer, ByVal Token As CancellationToken, ByVal Multiple As Boolean) diff --git a/SCrawler/API/OnlyFans/OFResources.Designer.vb b/SCrawler/API/OnlyFans/OFResources.Designer.vb new file mode 100644 index 0000000..138578b --- /dev/null +++ b/SCrawler/API/OnlyFans/OFResources.Designer.vb @@ -0,0 +1,77 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:4.0.30319.42000 +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict On +Option Explicit On + +Imports System + +Namespace My.Resources + + 'This class was auto-generated by the StronglyTypedResourceBuilder + 'class via a tool like ResGen or Visual Studio. + 'To add or remove a member, edit your .ResX file then rerun ResGen + 'with the /str option, or rebuild your VS project. + ''' + ''' A strongly-typed resource class, for looking up localized strings, etc. + ''' + _ + Friend Class OFResources + + Private Shared resourceMan As Global.System.Resources.ResourceManager + + Private Shared resourceCulture As Global.System.Globalization.CultureInfo + + _ + Friend Sub New() + MyBase.New + End Sub + + ''' + ''' Returns the cached ResourceManager instance used by this class. + ''' + _ + Friend Shared ReadOnly Property ResourceManager() As Global.System.Resources.ResourceManager + Get + If Object.ReferenceEquals(resourceMan, Nothing) Then + Dim temp As Global.System.Resources.ResourceManager = New Global.System.Resources.ResourceManager("SCrawler.OFResources", GetType(OFResources).Assembly) + resourceMan = temp + End If + Return resourceMan + End Get + End Property + + ''' + ''' Overrides the current thread's CurrentUICulture property for all + ''' resource lookups using this strongly typed resource class. + ''' + _ + Friend Shared Property Culture() As Global.System.Globalization.CultureInfo + Get + Return resourceCulture + End Get + Set + resourceCulture = value + End Set + End Property + + ''' + ''' Looks up a localized resource of type System.Byte[]. + ''' + Friend Shared ReadOnly Property OFScraperConfigPattern() As Byte() + Get + Dim obj As Object = ResourceManager.GetObject("OFScraperConfigPattern", resourceCulture) + Return CType(obj,Byte()) + End Get + End Property + End Class +End Namespace diff --git a/SCrawler/API/OnlyFans/OFResources.resx b/SCrawler/API/OnlyFans/OFResources.resx new file mode 100644 index 0000000..31b7c00 --- /dev/null +++ b/SCrawler/API/OnlyFans/OFResources.resx @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + + OFScraperConfigPattern.json;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/SCrawler/API/OnlyFans/OFScraperConfigPattern.json b/SCrawler/API/OnlyFans/OFScraperConfigPattern.json new file mode 100644 index 0000000..98780e4 --- /dev/null +++ b/SCrawler/API/OnlyFans/OFScraperConfigPattern.json @@ -0,0 +1,61 @@ +{ + "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": "deviint", + "backend": "aio", + "downloadbars": false, + "cache-mode": "sqlite", + "appendlog": true, + "custom": null, + "sanitize_text": false, + "avatar": true + }, + "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/SiteSettings.vb b/SCrawler/API/OnlyFans/SiteSettings.vb index 0883b5c..8fab91d 100644 --- a/SCrawler/API/OnlyFans/SiteSettings.vb +++ b/SCrawler/API/OnlyFans/SiteSettings.vb @@ -26,8 +26,8 @@ Namespace API.OnlyFans #Region "Headers" Private Const HeaderBrowser As String = "sec-ch-ua" Private Const HeaderUserID As String = "User-Id" - Private Const HeaderXBC As String = "X-Bc" - Private Const HeaderAppToken As String = "App-Token" + Friend Const HeaderXBC As String = "X-Bc" + Friend Const HeaderAppToken As String = "App-Token" Friend ReadOnly Property HH_USER_ID As PropertyValue @@ -37,7 +37,7 @@ Namespace API.OnlyFans Private ReadOnly Property HH_BROWSER As PropertyValue - Private ReadOnly Property UserAgent As PropertyValue + Friend ReadOnly Property UserAgent As PropertyValue Private Sub UpdateHeader(ByVal PropertyName As String, ByVal Value As String) Dim hName$ = String.Empty Dim isUserAgent As Boolean = False @@ -78,6 +78,42 @@ Namespace API.OnlyFans "Change this value only if you know what you are doing."), PXML, PClonable> Friend ReadOnly Property DynamicRules As PropertyValue #End Region +#Region "OFScraper" + Private ReadOnly Property OFScraperPath_XML As PropertyValue + + Friend ReadOnly Property OFScraperPath As PropertyValue + Get + If Not DefaultInstance Is Nothing Then + Return DirectCast(DefaultInstance, SiteSettings).OFScraperPath_XML + Else + Return OFScraperPath_XML + End If + End Get + End Property + Private ReadOnly Property OFScraperMP4decrypt_XML As PropertyValue + + Friend ReadOnly Property OFScraperMP4decrypt As PropertyValue + Get + If Not DefaultInstance Is Nothing Then + Return DirectCast(DefaultInstance, SiteSettings).OFScraperMP4decrypt_XML + Else + Return OFScraperMP4decrypt_XML + End If + End Get + End Property + Friend Const KeyModeDefault_Default As String = "cdrm" + Private ReadOnly Property KeyModeDefault_XML As PropertyValue + + Friend ReadOnly Property KeyModeDefault As PropertyValue + Get + If Not DefaultInstance Is Nothing Then + Return DirectCast(DefaultInstance, SiteSettings).KeyModeDefault_XML + Else + Return KeyModeDefault_XML + End If + End Get + End Property +#End Region #End Region #Region "Initializer" Friend Sub New(ByVal AccName As String, ByVal Temp As Boolean) @@ -117,6 +153,21 @@ Namespace API.OnlyFans 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 + If Not f.Exists AndAlso f.Exists(SFO.Path, False) Then + With SFile.GetFiles(f, "*.exe",, EDP.ReturnValue) + If .ListExists Then + f = .FirstOrDefault(Function(ff) ff.Name.StringToLower.StartsWith("ofscraper")) + If f.Exists Then OFScraperPath_XML.Value = f.ToString + End If + End With + End If + End If + OFScraperMP4decrypt_XML = New PropertyValue(String.Empty, GetType(String)) + KeyModeDefault_XML = New PropertyValue(KeyModeDefault_Default) + UserRegex = RParams.DMS("onlyfans.com/([\w\._]+)", 1, EDP.ReturnValue) UrlPatternUser = "https://onlyfans.com/{0}" ImageVideoContains = "onlyfans.com" @@ -151,6 +202,7 @@ Namespace API.OnlyFans End Sub #End Region #Region "GetUserUrl, GetUserPostUrl, UserOptions" + Friend Const UserPostPattern As String = "https://onlyfans.com/{0}/{1}" Friend Overrides Function GetUserUrl(ByVal User As IPluginContentProvider) As String Return String.Format(UrlPatternUser, If(User.ID.IsEmptyString, User.Name, $"u{User.ID}")) End Function @@ -168,7 +220,7 @@ Namespace API.OnlyFans If p.IsEmptyString Then Return GetUserUrl(User) Else - Return String.Format("https://onlyfans.com/{0}/{1}", p, If(User.ID.IsEmptyString, User.Name, $"u{User.ID}")) + Return String.Format(UserPostPattern, p, If(User.ID.IsEmptyString, User.Name, $"u{User.ID}")) End If Else Return String.Empty diff --git a/SCrawler/API/OnlyFans/UserData.vb b/SCrawler/API/OnlyFans/UserData.vb index e5a93a2..8bf09b5 100644 --- a/SCrawler/API/OnlyFans/UserData.vb +++ b/SCrawler/API/OnlyFans/UserData.vb @@ -7,10 +7,12 @@ ' This program is distributed in the hope that it will be useful, ' but WITHOUT ANY WARRANTY Imports System.Threading +Imports System.Text.RegularExpressions Imports SCrawler.API.Base Imports SCrawler.API.YouTube.Objects Imports PersonalUtilities.Functions.XML Imports PersonalUtilities.Functions.RegularExpressions +Imports PersonalUtilities.Tools Imports PersonalUtilities.Tools.Web.Clients Imports PersonalUtilities.Tools.Web.Clients.EventArguments Imports PersonalUtilities.Tools.Web.Cookies @@ -65,11 +67,20 @@ Namespace API.OnlyFans #Region "Initializer" Friend Sub New() HighlightsList = New List(Of String) + UseInternalDownloadFileFunction = True End Sub #End Region #Region "Download functions" + Private _OFScraperExists As Boolean = False + Private OFSCache As CacheKeeper = Nothing + Private _AbsMediaIndex As Integer = 0 + Private Sub ValidateOFScraper() + _OFScraperExists = ACheck(MySettings.OFScraperPath.Value) AndAlso CStr(MySettings.OFScraperPath.Value).CSFile.Exists + End Sub Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken) If Not MySettings.SessionAborted Then + ValidateOFScraper() + _AbsMediaIndex = 0 If Not CCookie Is Nothing Then CCookie.Dispose() CCookie = Responser.Cookies.Copy Responser.Cookies.Clear() @@ -307,8 +318,8 @@ Namespace API.OnlyFans #End Region Private Function TryCreateMedia(ByVal n As EContainer, ByVal PostID As String, Optional ByVal PostDate As String = Nothing, Optional ByRef Result As Boolean = False, Optional ByVal IsHL As Boolean = False, - Optional ByVal SpecFolder As String = Nothing) As List(Of UserMedia) - Dim postUrl$, ext$ + Optional ByVal SpecFolder As String = Nothing, Optional ByVal PostUserID As String = Nothing) As List(Of UserMedia) + Dim postUrl$, postUrlBase$, ext$ Dim t As UTypes Dim mList As New List(Of UserMedia) Result = False @@ -320,16 +331,27 @@ Namespace API.OnlyFans Else postUrl = m.Value({"source"}, "source").IfNullOrEmpty(m.Value("full")) End If + postUrlBase = String.Empty Select Case m.Value("type") Case "photo" : t = UTypes.Picture : ext = "jpg" - Case "video" : t = UTypes.Video : ext = "mp4" + Case "video" + t = UTypes.Video + ext = "mp4" + If postUrl.IsEmptyString And Not IsHL Then + t = UTypes.VideoPre + _AbsMediaIndex += 1 + If Not PostUserID.IsEmptyString And IsSingleObjectDownload Then _ + postUrlBase = String.Format(SiteSettings.UserPostPattern, PostID, $"u{PostUserID}") + End If Case Else : t = UTypes.Undefined : ext = String.Empty End Select - If Not t = UTypes.Undefined And Not postUrl.IsEmptyString Then - Dim media As New UserMedia(postUrl, t) With { + If Not t = UTypes.Undefined And (Not postUrl.IsEmptyString Or t = UTypes.VideoPre) Then + Dim media As New UserMedia(postUrl.IfNullOrEmpty(IIf(t = UTypes.VideoPre, $"{t}{_AbsMediaIndex}", String.Empty)), t) With { .Post = New UserPost(PostID, AConvert(Of Date)(PostDate, DateProvider, Nothing)), .SpecialFolder = SpecFolder } + If postUrlBase.IsEmptyString And Not IsSingleObjectDownload Then postUrlBase = GetPostUrl(Me, media) + If Not postUrlBase.IsEmptyString Then media.URL_BASE = postUrlBase media.File.Extension = ext Result = True mList.Add(media) @@ -387,7 +409,7 @@ Namespace API.OnlyFans End Function Dim mList As List(Of UserMedia) Dim mediaResult As Boolean - Dim r$, path$, postDate$ + Dim r$, path$, postDate$, postUserID$ Dim j As EContainer ProgressPre.ChangeMax(_ContentList.Count) For i% = 0 To _ContentList.Count - 1 @@ -404,8 +426,9 @@ Namespace API.OnlyFans j = JsonDocument.Parse(r) If Not j Is Nothing Then postDate = j.Value("postedAt") + postUserID = j.Value({"author"}, "id") mediaResult = False - mList = TryCreateMedia(j, m.Post.ID, postDate, mediaResult) + mList = TryCreateMedia(j, m.Post.ID, postDate, mediaResult,,, postUserID) If mediaResult Then _TempMediaList.ListAddList(mList.ListForEachCopy(stateRefill, True), LNC) rList.Add(i) @@ -531,10 +554,145 @@ Namespace API.OnlyFans Return result End Function #End Region +#Region "OFScraper support" + Private Function OFS_DownloadFile(ByVal URL As String, ByVal Token As CancellationToken) As List(Of SFile) + Try + Const requestPattern$ = """{0}"" manual --config ""{1}"" --url {2}" + Dim conf As SFile = OFS_CreateConfig() + If conf.Exists Then + Dim command$ = String.Format(requestPattern, MySettings.OFScraperPath.Value, conf, URL) + '#If DEBUG Then + 'Debug.WriteLine(command) + '#End If + Using b As New TokenBatch(Token) : b.Execute(command) : End Using + Return SFile.GetFiles(conf, "*.mp4", IO.SearchOption.AllDirectories, EDP.ReturnValue) + End If + Return Nothing + Catch ex As Exception + Return ErrorsDescriber.Execute(EDP.SendToLog, ex, "OnlyFans.UserData.OFS_DownloadFile", Nothing) + End Try + End Function + Private Function OFS_CreateConfig() As SFile + Try + Const confMainPattern$ = "{0}"": ""([^""]*)""" + 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 = $"{SettingsFolderName}\OFScraperConfigPattern.json" + Dim configText$ + If Not f.Exists Then + configText = Text.Encoding.UTF8.GetString(My.Resources.OFResources.OFScraperConfigPattern) + TextSaver.SaveTextToFile(configText, f, True) + End If + 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 + If Not configText.IsEmptyString Then + updateConf("save_location", cacheRoot.PathNoSeparator.Replace("\", "/")) + If ACheck(MySettings.OFScraperMP4decrypt.Value) Then + ff = CStr(MySettings.OFScraperMP4decrypt.Value) + If ff.Exists Then updateConf("mp4decrypt", ff.ToString.Replace("\", "/")) + 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)) + f = currentCache + f.Name = "config" + f.Extension = "json" + If TextSaver.SaveTextToFile(configText, f, True).Exists AndAlso OFS_CreateAuth(currentCache) Then Return f + End If + End If + Return Nothing + Catch ex As Exception + Return ErrorsDescriber.Execute(EDP.SendToLog, ex, "OnlyFans.UserData.OFS_CreateConfig", Nothing) + End Try + End Function + Private Function OFS_CreateAuth(ByVal DestinationPath As SFile) As Boolean + Const authText$ = """user_agent"": ""{0}"",""app-token"": ""{1}"",""x-bc"": ""{2}"",""auth_id"": ""{3}"",""sess"": ""{4}"",""auth_uid_"": ""{3}"",""cookie"": ""{5}""" + Try + Dim sess$ = If(If(CCookie, Responser.Cookies).FirstOrDefault(Function(c) c.Name.StringToLower = "sess")?.Value, String.Empty) + Dim outText$ = "{""auth"":{" & + String.Format(authText, + MySettings.UserAgent.Value, + Responser.Headers.Value(SiteSettings.HeaderAppToken), + Responser.Headers.Value(SiteSettings.HeaderXBC), + MySettings.HH_USER_ID.Value, + sess, + If(CCookie, Responser.Cookies).ToString()) & + "}}" + If DestinationPath.Exists(SFO.Path, False) Then + Dim f As SFile = $"{DestinationPath.PathWithSeparator}main_profile\auth.json" + Return TextSaver.SaveTextToFile(outText, f, True).Exists + End If + Return False + Catch ex As Exception + Return ErrorsDescriber.Execute(EDP.SendToLog, ex, "OnlyFans.UserData.OFS_CreateAuth", False) + End Try + End Function +#End Region #Region "DownloadContent" + Private OFSPostFiles As Dictionary(Of String, List(Of SFile)) = Nothing Protected Overrides Sub DownloadContent(ByVal Token As CancellationToken) DownloadContentDefault(Token) + OFSCache.DisposeIfReady + OFSPostFiles.ListClearDispose End Sub + Protected Overrides Function ValidateDownloadFile(ByVal URL As String, ByVal Media As UserMedia, ByRef Interrupt As Boolean) As Boolean + Return Media.Type = UTypes.VideoPre + End Function + Protected Overrides Function DownloadFile(ByVal URL As String, ByVal Media As UserMedia, ByVal DestinationFile As SFile, + ByVal Token As CancellationToken) As SFile + ValidateOFScraper() + If _OFScraperExists Then + If OFSPostFiles Is Nothing Then OFSPostFiles = New Dictionary(Of String, List(Of SFile)) + If IsSingleObjectDownload Then + URL = Media.URL_BASE + Else + URL = GetPostUrl(Me, Media) + End If + If Not URL.IsEmptyString Then + Dim f As SFile = Nothing + If OFSPostFiles.Count > 0 AndAlso OFSPostFiles.ContainsKey(Media.Post.ID) AndAlso OFSPostFiles(Media.Post.ID).Count > 0 Then + f = OFSPostFiles(Media.Post.ID)(0) + OFSPostFiles(Media.Post.ID).RemoveAt(0) + Else + Dim files As List(Of SFile) = OFS_DownloadFile(URL, Token) + If files.ListExists Then + Dim ff As SFile + For i% = files.Count - 1 To 0 Step -1 + ff = files(i) + DestinationFile.Name = ff.Name + DestinationFile.Extension = ff.Extension + If SFile.Move(ff, DestinationFile,,,, EDP.ThrowException) Then + files(i) = DestinationFile + Else + files.RemoveAt(i) + End If + Next + If files.Count > 0 Then + f = files(0) + files.RemoveAt(0) + If files.Count > 0 Then OFSPostFiles.Add(Media.Post.ID, files) + End If + End If + End If + Return f + End If + Return Nothing + Else + Throw New InvalidProgramException("OF-Scraper not found") + End If + End Function #End Region #Region "DownloadingException" Private _DownloadingException_AuthFileUpdate As Boolean = False @@ -546,7 +704,7 @@ Namespace API.OnlyFans Return 2 Else MySettings.SessionAborted = True - MyMainLOG = $"{ToStringForLog()}: OnlyFans credentials expired" + MyMainLOG = $"{ToStringForLog()} [{CInt(Responser.StatusCode)}]: OnlyFans credentials expired" Return 1 End If ElseIf Responser.StatusCode = Net.HttpStatusCode.NotFound Then '404 @@ -558,7 +716,7 @@ Namespace API.OnlyFans Return 1 ElseIf Responser.StatusCode = Net.HttpStatusCode.Unauthorized Then '401 MySettings.SessionAborted = True - MyMainLOG = $"{ToStringForLog()}: OnlyFans credentials expired" + MyMainLOG = $"{ToStringForLog()} [{CInt(Responser.StatusCode)}]: OnlyFans credentials expired" Return 1 Else Return 0 @@ -567,7 +725,13 @@ Namespace API.OnlyFans #End Region #Region "IDisposable Support" Protected Overrides Sub Dispose(ByVal disposing As Boolean) - If Not disposedValue And disposing Then CCookie.DisposeIfReady(False) : CCookie = Nothing : HighlightsList.Clear() + If Not disposedValue And disposing Then + CCookie.DisposeIfReady(False) + CCookie = Nothing + HighlightsList.Clear() + OFSCache.DisposeIfReady + OFSPostFiles.ListClearDispose + End If MyBase.Dispose(disposing) End Sub #End Region diff --git a/SCrawler/Download/TDownloader.vb b/SCrawler/Download/TDownloader.vb index 334dd51..e4d821b 100644 --- a/SCrawler/Download/TDownloader.vb +++ b/SCrawler/Download/TDownloader.vb @@ -122,7 +122,14 @@ Namespace DownloadObjects Return _FilesSessionActual End Get End Property + Private _FilesSaving As Boolean = False Friend Sub FilesSave() + While _FilesSaving Or _FilesUpdating : Thread.Sleep(100) : End While + Dim i% = 0 + While Not FilesSaveImpl() And i < 10 : i += 1 : End While + End Sub + Private Function FilesSaveImpl() As Boolean + _FilesSaving = True Try If Settings.FeedStoreSessionsData And Files.Count > 0 Then ClearSessions() @@ -131,10 +138,13 @@ Namespace DownloadObjects x.Save(FilesSessionActual) End Using End If + Return True Catch ex As Exception - ErrorsDescriber.Execute(EDP.SendToLog, ex, "[DownloadObjects.TDownloader.FilesSave]") + Return ErrorsDescriber.Execute(EDP.SendToLog, ex, "[DownloadObjects.TDownloader.FilesSave]", False) + Finally + _FilesSaving = False End Try - End Sub + End Function Private _FilesUpdating As Boolean = False Friend Sub FilesUpdatePendingUsers() _FilesUpdating = True diff --git a/SCrawler/MainFrameObjects.vb b/SCrawler/MainFrameObjects.vb index 13d300a..dcd50ee 100644 --- a/SCrawler/MainFrameObjects.vb +++ b/SCrawler/MainFrameObjects.vb @@ -99,7 +99,7 @@ Friend Class MainFrameObjects : Implements INotificator Case $"{NotificationInternalKey}_{NotifyObj.Channels}" : MF.MyChannels.FormShowS() Case $"{NotificationInternalKey}_{NotifyObj.SavedPosts}" : MF.MySavedPosts.FormShowS() Case $"{NotificationInternalKey}_{NotifyObj.STDownloader}" : VideoDownloader.FormShowS() - Case $"{NotificationInternalKey}_{NotifyObj.LOG}" : ShowLog() + Case $"{NotificationInternalKey}_{NotifyObj.LOG}" : ControlInvokeFast(MF, AddressOf ShowLog, EDP.LogMessageValue) Case Else : Focus(True) End Select ElseIf Settings.Automation Is Nothing OrElse Not Settings.Automation.NotificationClicked(Key, found, activateForm) Then diff --git a/SCrawler/My Project/AssemblyInfo.vb b/SCrawler/My Project/AssemblyInfo.vb index f1bdf57..e697e86 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 de66db4..78d5314 100644 --- a/SCrawler/SCrawler.vbproj +++ b/SCrawler/SCrawler.vbproj @@ -194,6 +194,11 @@ + + True + True + OFResources.resx + @@ -490,6 +495,11 @@ InternalSettingsForm.vb + + My.Resources + ResXFileCodeGenerator + OFResources.Designer.vb + RedditViewSettingsForm.vb @@ -588,6 +598,7 @@ +