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 @@
+