2023.12.27.0

API.OnlyFans: add OF-Scraper support
ProfileSaved: save files when adding new data
This commit is contained in:
Andy
2023-12-27 16:10:02 +03:00
parent c458f1cd1d
commit 0246af9b69
13 changed files with 542 additions and 28 deletions

View File

@@ -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.0
*2023-12-15* *2023-12-15*

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -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. 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 # 4K Video Downloader
https://www.4kdownload.com/-plbrz/video-downloader https://www.4kdownload.com/-plbrz/video-downloader
| Option | SCrawler | 4K Stogram | | Option | SCrawler | 4K Video Downloader |
| ---- | ---- | ---- | | ---- | ---- | ---- |
| User managament | **Advanced** | No | | User managament | **Advanced** | No |
| Automatic downloads | **Yes** | No | | Automatic downloads | **Yes** | No |
@@ -121,10 +129,3 @@ https://github.com/RipMeApp/ripme
| Other sites support | **Yes** | No | | Other sites support | **Yes** | No |
| Still supported | **Yes** | **No (last release date May 4, 2021)** | | 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.

View File

@@ -47,7 +47,7 @@ Namespace API.Base
Progress.InformationTemporary = $"{HOST.Name} ({c - s}/{c}) Images: {_TotalImages}; Videos: {_TotalVideos}" Progress.InformationTemporary = $"{HOST.Name} ({c - s}/{c}) Images: {_TotalImages}; Videos: {_TotalVideos}"
End If End If
End If End If
If _FeedDataExists Then Downloader.Files.Sort() If _FeedDataExists Then Downloader.Files.Sort() : Downloader.FilesSave()
End Sub End Sub
Private Overloads Sub Download(ByVal Host As SettingsHost, ByVal Number As Integer, ByVal Count As Integer, Private Overloads Sub Download(ByVal Host As SettingsHost, ByVal Number As Integer, ByVal Count As Integer,
ByVal Token As CancellationToken, ByVal Multiple As Boolean) ByVal Token As CancellationToken, ByVal Multiple As Boolean)

View File

@@ -0,0 +1,77 @@
'------------------------------------------------------------------------------
' <auto-generated>
' 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.
' </auto-generated>
'------------------------------------------------------------------------------
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.
'''<summary>
''' A strongly-typed resource class, for looking up localized strings, etc.
'''</summary>
<Global.System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0"), _
Global.System.Diagnostics.DebuggerNonUserCodeAttribute(), _
Global.System.Runtime.CompilerServices.CompilerGeneratedAttribute()> _
Friend Class OFResources
Private Shared resourceMan As Global.System.Resources.ResourceManager
Private Shared resourceCulture As Global.System.Globalization.CultureInfo
<Global.System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")> _
Friend Sub New()
MyBase.New
End Sub
'''<summary>
''' Returns the cached ResourceManager instance used by this class.
'''</summary>
<Global.System.ComponentModel.EditorBrowsableAttribute(Global.System.ComponentModel.EditorBrowsableState.Advanced)> _
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
'''<summary>
''' Overrides the current thread's CurrentUICulture property for all
''' resource lookups using this strongly typed resource class.
'''</summary>
<Global.System.ComponentModel.EditorBrowsableAttribute(Global.System.ComponentModel.EditorBrowsableState.Advanced)> _
Friend Shared Property Culture() As Global.System.Globalization.CultureInfo
Get
Return resourceCulture
End Get
Set
resourceCulture = value
End Set
End Property
'''<summary>
''' Looks up a localized resource of type System.Byte[].
'''</summary>
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

View File

@@ -0,0 +1,124 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="OFScraperConfigPattern" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>OFScraperConfigPattern.json;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
</root>

View File

@@ -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"
}
}
}

View File

@@ -26,8 +26,8 @@ Namespace API.OnlyFans
#Region "Headers" #Region "Headers"
Private Const HeaderBrowser As String = "sec-ch-ua" Private Const HeaderBrowser As String = "sec-ch-ua"
Private Const HeaderUserID As String = "User-Id" Private Const HeaderUserID As String = "User-Id"
Private Const HeaderXBC As String = "X-Bc" Friend Const HeaderXBC As String = "X-Bc"
Private Const HeaderAppToken As String = "App-Token" Friend Const HeaderAppToken As String = "App-Token"
<PropertyOption(ControlText:=HeaderUserID, AllowNull:=False), PClonable(Clone:=False)> <PropertyOption(ControlText:=HeaderUserID, AllowNull:=False), PClonable(Clone:=False)>
Friend ReadOnly Property HH_USER_ID As PropertyValue Friend ReadOnly Property HH_USER_ID As PropertyValue
<PropertyOption(ControlText:=HeaderXBC, AllowNull:=False), PClonable(Clone:=False)> <PropertyOption(ControlText:=HeaderXBC, AllowNull:=False), PClonable(Clone:=False)>
@@ -37,7 +37,7 @@ Namespace API.OnlyFans
<PropertyOption(ControlText:=HeaderBrowser, ControlToolTip:="Can be null", AllowNull:=True), PClonable> <PropertyOption(ControlText:=HeaderBrowser, ControlToolTip:="Can be null", AllowNull:=True), PClonable>
Private ReadOnly Property HH_BROWSER As PropertyValue Private ReadOnly Property HH_BROWSER As PropertyValue
<PropertyOption(AllowNull:=False), PClonable> <PropertyOption(AllowNull:=False), PClonable>
Private ReadOnly Property UserAgent As PropertyValue Friend ReadOnly Property UserAgent As PropertyValue
Private Sub UpdateHeader(ByVal PropertyName As String, ByVal Value As String) Private Sub UpdateHeader(ByVal PropertyName As String, ByVal Value As String)
Dim hName$ = String.Empty Dim hName$ = String.Empty
Dim isUserAgent As Boolean = False 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> "Change this value only if you know what you are doing."), PXML, PClonable>
Friend ReadOnly Property DynamicRules As PropertyValue Friend ReadOnly Property DynamicRules As PropertyValue
#End Region #End Region
#Region "OFScraper"
<PClonable, PXML("OFScraperPath")> Private ReadOnly Property OFScraperPath_XML As PropertyValue
<PropertyOption(ControlText:="OF-Scraper path", ControlToolTip:="The path to the 'ofscraper.exe'")>
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
<PClonable, PXML("OFScraperMP4decrypt")> Private ReadOnly Property OFScraperMP4decrypt_XML As PropertyValue
<PropertyOption(ControlText:="mp4decrypt path", ControlToolTip:="The path to the 'mp4decrypt.exe'")>
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"
<PClonable, PXML("KeyModeDefault")> Private ReadOnly Property KeyModeDefault_XML As PropertyValue
<PropertyOption(ControlText:="key-mode-default")>
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 #End Region
#Region "Initializer" #Region "Initializer"
Friend Sub New(ByVal AccName As String, ByVal Temp As Boolean) 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), DynamicRulesUpdateIntervalProvider = New FieldsCheckerProviderSimple(Function(v) IIf(AConvert(Of Integer)(v, 0) > 0, v, Nothing),
"The value of [{0}] field must be greater than 0") "The value of [{0}] field must be greater than 0")
DynamicRules = New PropertyValue(String.Empty, GetType(String)) 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) UserRegex = RParams.DMS("onlyfans.com/([\w\._]+)", 1, EDP.ReturnValue)
UrlPatternUser = "https://onlyfans.com/{0}" UrlPatternUser = "https://onlyfans.com/{0}"
ImageVideoContains = "onlyfans.com" ImageVideoContains = "onlyfans.com"
@@ -151,6 +202,7 @@ Namespace API.OnlyFans
End Sub End Sub
#End Region #End Region
#Region "GetUserUrl, GetUserPostUrl, UserOptions" #Region "GetUserUrl, GetUserPostUrl, UserOptions"
Friend Const UserPostPattern As String = "https://onlyfans.com/{0}/{1}"
Friend Overrides Function GetUserUrl(ByVal User As IPluginContentProvider) As String Friend Overrides Function GetUserUrl(ByVal User As IPluginContentProvider) As String
Return String.Format(UrlPatternUser, If(User.ID.IsEmptyString, User.Name, $"u{User.ID}")) Return String.Format(UrlPatternUser, If(User.ID.IsEmptyString, User.Name, $"u{User.ID}"))
End Function End Function
@@ -168,7 +220,7 @@ Namespace API.OnlyFans
If p.IsEmptyString Then If p.IsEmptyString Then
Return GetUserUrl(User) Return GetUserUrl(User)
Else 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 End If
Else Else
Return String.Empty Return String.Empty

View File

@@ -7,10 +7,12 @@
' This program is distributed in the hope that it will be useful, ' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY ' but WITHOUT ANY WARRANTY
Imports System.Threading Imports System.Threading
Imports System.Text.RegularExpressions
Imports SCrawler.API.Base Imports SCrawler.API.Base
Imports SCrawler.API.YouTube.Objects Imports SCrawler.API.YouTube.Objects
Imports PersonalUtilities.Functions.XML Imports PersonalUtilities.Functions.XML
Imports PersonalUtilities.Functions.RegularExpressions Imports PersonalUtilities.Functions.RegularExpressions
Imports PersonalUtilities.Tools
Imports PersonalUtilities.Tools.Web.Clients Imports PersonalUtilities.Tools.Web.Clients
Imports PersonalUtilities.Tools.Web.Clients.EventArguments Imports PersonalUtilities.Tools.Web.Clients.EventArguments
Imports PersonalUtilities.Tools.Web.Cookies Imports PersonalUtilities.Tools.Web.Cookies
@@ -65,11 +67,20 @@ Namespace API.OnlyFans
#Region "Initializer" #Region "Initializer"
Friend Sub New() Friend Sub New()
HighlightsList = New List(Of String) HighlightsList = New List(Of String)
UseInternalDownloadFileFunction = True
End Sub End Sub
#End Region #End Region
#Region "Download functions" #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) Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
If Not MySettings.SessionAborted Then If Not MySettings.SessionAborted Then
ValidateOFScraper()
_AbsMediaIndex = 0
If Not CCookie Is Nothing Then CCookie.Dispose() If Not CCookie Is Nothing Then CCookie.Dispose()
CCookie = Responser.Cookies.Copy CCookie = Responser.Cookies.Copy
Responser.Cookies.Clear() Responser.Cookies.Clear()
@@ -307,8 +318,8 @@ Namespace API.OnlyFans
#End Region #End Region
Private Function TryCreateMedia(ByVal n As EContainer, ByVal PostID As String, Optional ByVal PostDate As String = Nothing, 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 ByRef Result As Boolean = False, Optional ByVal IsHL As Boolean = False,
Optional ByVal SpecFolder As String = Nothing) As List(Of UserMedia) Optional ByVal SpecFolder As String = Nothing, Optional ByVal PostUserID As String = Nothing) As List(Of UserMedia)
Dim postUrl$, ext$ Dim postUrl$, postUrlBase$, ext$
Dim t As UTypes Dim t As UTypes
Dim mList As New List(Of UserMedia) Dim mList As New List(Of UserMedia)
Result = False Result = False
@@ -320,16 +331,27 @@ Namespace API.OnlyFans
Else Else
postUrl = m.Value({"source"}, "source").IfNullOrEmpty(m.Value("full")) postUrl = m.Value({"source"}, "source").IfNullOrEmpty(m.Value("full"))
End If End If
postUrlBase = String.Empty
Select Case m.Value("type") Select Case m.Value("type")
Case "photo" : t = UTypes.Picture : ext = "jpg" 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 Case Else : t = UTypes.Undefined : ext = String.Empty
End Select End Select
If Not t = UTypes.Undefined And Not postUrl.IsEmptyString Then If Not t = UTypes.Undefined And (Not postUrl.IsEmptyString Or t = UTypes.VideoPre) Then
Dim media As New UserMedia(postUrl, t) With { 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)), .Post = New UserPost(PostID, AConvert(Of Date)(PostDate, DateProvider, Nothing)),
.SpecialFolder = SpecFolder .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 media.File.Extension = ext
Result = True Result = True
mList.Add(media) mList.Add(media)
@@ -387,7 +409,7 @@ Namespace API.OnlyFans
End Function End Function
Dim mList As List(Of UserMedia) Dim mList As List(Of UserMedia)
Dim mediaResult As Boolean Dim mediaResult As Boolean
Dim r$, path$, postDate$ Dim r$, path$, postDate$, postUserID$
Dim j As EContainer Dim j As EContainer
ProgressPre.ChangeMax(_ContentList.Count) ProgressPre.ChangeMax(_ContentList.Count)
For i% = 0 To _ContentList.Count - 1 For i% = 0 To _ContentList.Count - 1
@@ -404,8 +426,9 @@ Namespace API.OnlyFans
j = JsonDocument.Parse(r) j = JsonDocument.Parse(r)
If Not j Is Nothing Then If Not j Is Nothing Then
postDate = j.Value("postedAt") postDate = j.Value("postedAt")
postUserID = j.Value({"author"}, "id")
mediaResult = False mediaResult = False
mList = TryCreateMedia(j, m.Post.ID, postDate, mediaResult) mList = TryCreateMedia(j, m.Post.ID, postDate, mediaResult,,, postUserID)
If mediaResult Then If mediaResult Then
_TempMediaList.ListAddList(mList.ListForEachCopy(stateRefill, True), LNC) _TempMediaList.ListAddList(mList.ListForEachCopy(stateRefill, True), LNC)
rList.Add(i) rList.Add(i)
@@ -531,10 +554,145 @@ Namespace API.OnlyFans
Return result Return result
End Function End Function
#End Region #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" #Region "DownloadContent"
Private OFSPostFiles As Dictionary(Of String, List(Of SFile)) = Nothing
Protected Overrides Sub DownloadContent(ByVal Token As CancellationToken) Protected Overrides Sub DownloadContent(ByVal Token As CancellationToken)
DownloadContentDefault(Token) DownloadContentDefault(Token)
OFSCache.DisposeIfReady
OFSPostFiles.ListClearDispose
End Sub 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 #End Region
#Region "DownloadingException" #Region "DownloadingException"
Private _DownloadingException_AuthFileUpdate As Boolean = False Private _DownloadingException_AuthFileUpdate As Boolean = False
@@ -546,7 +704,7 @@ Namespace API.OnlyFans
Return 2 Return 2
Else Else
MySettings.SessionAborted = True MySettings.SessionAborted = True
MyMainLOG = $"{ToStringForLog()}: OnlyFans credentials expired" MyMainLOG = $"{ToStringForLog()} [{CInt(Responser.StatusCode)}]: OnlyFans credentials expired"
Return 1 Return 1
End If End If
ElseIf Responser.StatusCode = Net.HttpStatusCode.NotFound Then '404 ElseIf Responser.StatusCode = Net.HttpStatusCode.NotFound Then '404
@@ -558,7 +716,7 @@ Namespace API.OnlyFans
Return 1 Return 1
ElseIf Responser.StatusCode = Net.HttpStatusCode.Unauthorized Then '401 ElseIf Responser.StatusCode = Net.HttpStatusCode.Unauthorized Then '401
MySettings.SessionAborted = True MySettings.SessionAborted = True
MyMainLOG = $"{ToStringForLog()}: OnlyFans credentials expired" MyMainLOG = $"{ToStringForLog()} [{CInt(Responser.StatusCode)}]: OnlyFans credentials expired"
Return 1 Return 1
Else Else
Return 0 Return 0
@@ -567,7 +725,13 @@ Namespace API.OnlyFans
#End Region #End Region
#Region "IDisposable Support" #Region "IDisposable Support"
Protected Overrides Sub Dispose(ByVal disposing As Boolean) 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) MyBase.Dispose(disposing)
End Sub End Sub
#End Region #End Region

View File

@@ -122,7 +122,14 @@ Namespace DownloadObjects
Return _FilesSessionActual Return _FilesSessionActual
End Get End Get
End Property End Property
Private _FilesSaving As Boolean = False
Friend Sub FilesSave() 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 Try
If Settings.FeedStoreSessionsData And Files.Count > 0 Then If Settings.FeedStoreSessionsData And Files.Count > 0 Then
ClearSessions() ClearSessions()
@@ -131,10 +138,13 @@ Namespace DownloadObjects
x.Save(FilesSessionActual) x.Save(FilesSessionActual)
End Using End Using
End If End If
Return True
Catch ex As Exception 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 Try
End Sub End Function
Private _FilesUpdating As Boolean = False Private _FilesUpdating As Boolean = False
Friend Sub FilesUpdatePendingUsers() Friend Sub FilesUpdatePendingUsers()
_FilesUpdating = True _FilesUpdating = True

View File

@@ -99,7 +99,7 @@ Friend Class MainFrameObjects : Implements INotificator
Case $"{NotificationInternalKey}_{NotifyObj.Channels}" : MF.MyChannels.FormShowS() Case $"{NotificationInternalKey}_{NotifyObj.Channels}" : MF.MyChannels.FormShowS()
Case $"{NotificationInternalKey}_{NotifyObj.SavedPosts}" : MF.MySavedPosts.FormShowS() Case $"{NotificationInternalKey}_{NotifyObj.SavedPosts}" : MF.MySavedPosts.FormShowS()
Case $"{NotificationInternalKey}_{NotifyObj.STDownloader}" : VideoDownloader.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) Case Else : Focus(True)
End Select End Select
ElseIf Settings.Automation Is Nothing OrElse Not Settings.Automation.NotificationClicked(Key, found, activateForm) Then ElseIf Settings.Automation Is Nothing OrElse Not Settings.Automation.NotificationClicked(Key, found, activateForm) Then

View File

@@ -32,6 +32,6 @@ Imports System.Runtime.InteropServices
' by using the '*' as shown below: ' by using the '*' as shown below:
' <Assembly: AssemblyVersion("1.0.*")> ' <Assembly: AssemblyVersion("1.0.*")>
<Assembly: AssemblyVersion("2023.12.15.0")> <Assembly: AssemblyVersion("2023.12.27.0")>
<Assembly: AssemblyFileVersion("2023.12.15.0")> <Assembly: AssemblyFileVersion("2023.12.27.0")>
<Assembly: NeutralResourcesLanguage("en")> <Assembly: NeutralResourcesLanguage("en")>

View File

@@ -194,6 +194,11 @@
<Compile Include="API\Mastodon\SiteSettings.vb" /> <Compile Include="API\Mastodon\SiteSettings.vb" />
<Compile Include="API\Mastodon\UserData.vb" /> <Compile Include="API\Mastodon\UserData.vb" />
<Compile Include="API\OnlyFans\Declarations.vb" /> <Compile Include="API\OnlyFans\Declarations.vb" />
<Compile Include="API\OnlyFans\OFResources.Designer.vb">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>OFResources.resx</DependentUpon>
</Compile>
<Compile Include="API\OnlyFans\SiteSettings.vb" /> <Compile Include="API\OnlyFans\SiteSettings.vb" />
<Compile Include="API\OnlyFans\UserData.vb" /> <Compile Include="API\OnlyFans\UserData.vb" />
<Compile Include="API\OnlyFans\UserExchangeOptions.vb" /> <Compile Include="API\OnlyFans\UserExchangeOptions.vb" />
@@ -490,6 +495,11 @@
<EmbeddedResource Include="API\BaseObjects\InternalSettingsForm.resx"> <EmbeddedResource Include="API\BaseObjects\InternalSettingsForm.resx">
<DependentUpon>InternalSettingsForm.vb</DependentUpon> <DependentUpon>InternalSettingsForm.vb</DependentUpon>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="API\OnlyFans\OFResources.resx">
<CustomToolNamespace>My.Resources</CustomToolNamespace>
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>OFResources.Designer.vb</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Include="API\Reddit\RedditViewSettingsForm.resx"> <EmbeddedResource Include="API\Reddit\RedditViewSettingsForm.resx">
<DependentUpon>RedditViewSettingsForm.vb</DependentUpon> <DependentUpon>RedditViewSettingsForm.vb</DependentUpon>
</EmbeddedResource> </EmbeddedResource>
@@ -588,6 +598,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include=".editorconfig" /> <None Include=".editorconfig" />
<None Include="API\OnlyFans\OFScraperConfigPattern.json" />
<None Include="Content\Pictures\ApplicationPic_16.png" /> <None Include="Content\Pictures\ApplicationPic_16.png" />
<None Include="Content\Pictures\BookmarkBlack_16.png" /> <None Include="Content\Pictures\BookmarkBlack_16.png" />
<None Include="Content\Pictures\DBPic_32.png" /> <None Include="Content\Pictures\DBPic_32.png" />