2025.1.12.0

YT
YouTubeSettings: add 'FileAddChannelToFileName' property
YouTubeMediaContainerBase: add channel name and video URL to info file; add channel name to file name

SCrawler
DownDetector: fix 403 error; add 'IDownDetector' interface and 'Checker' class; create an isolated environment
API.Instagram: update 'SiteSettings' to the new 'DownDetector' environment; make 'PostKV' public; add static function 'LoadSavePostsKV'
API.OnlyFans: add 'EnableCookiesUpdate' hidden property; add support for DRM keys; add the ability to disable cookie updates
API.Pinterest: add 'x-pinterest-pws-handler' header
API.Reddit: update 'SiteSettings' to the new 'DownDetector' environment
API.ThisVid: fix subscription videos images
API.Threads: change 'heic' extension to 'jpg'
API.Twitter: add broadcasts download
API.Xhamster: fix absolute M3U8 URLs
API.YouTube: add support of personal API instances ('YouTube-operational-API') for download communities
SiteEditorForm: add 'Ctrl+Enter' hotkey to force save settings, ignoring  requirements
PluginsEnvironment.Attributes: add 'UseDownDetectorAttribute' attribute
SettingsHost: update to the new 'DownDetector' environment; add 'AvailableDownDetector' property
SettingsHostCollection: update to the new 'DownDetector' environment; minor bugs in multiprofile
SettingsCLS: add 'DownDetectorEnabled' property
This commit is contained in:
Andy
2025-01-12 23:16:57 +03:00
parent b42832719f
commit 4d74f5204b
30 changed files with 747 additions and 285 deletions

View File

@@ -1,3 +1,28 @@
# 2025.1.12.0
*2025-01-12*
- Added
- Sites:
- YouTube (standalone app):
- ability to add channel name to file name (`Add channel to file name`)
- adding channel name and video URL to info file
- OnlyFans: **built-in usage of DRM keys**
- Threads: automatically change `heic` extension to `jpg`
- Twitter: download broadcasts *(user option)*
- Minor improvements
- Updated
- yt-dlp up to version **2024.12.23**
- gallery-dl up to version **1.28.3**
- **OF-Scraper** up to version **3.12.9** *(you must update it personally)*
- Fixed
- Sites:
- DownDetector: fixed 403 error
- OnlyFans: **DRM videos not downloading**
- xHamster: some videos are not downloading
- YouTube: **communities are not downloading** *(see settings in wiki)*
- Minor bugs
# 2024.11.21.0 # 2024.11.21.0
*2024-11-21* *2024-11-21*

4
FAQ.md
View File

@@ -55,7 +55,7 @@ I strongly recommend you to **regularly** create backup copies of the settings f
- [Video how to configure](#video-how-to-configure) - [Video how to configure](#video-how-to-configure)
- **Antivirus** - **Antivirus**
- **Antivirus detects SCrawler as a virus** :arrow_forward: SCrawler doesn't contain any viruses at all. All code is posted on GitHub. You can review it. I have nothing to hide. SCrawler just downloads pictures and videos. That's all. If you trust SCrawler, you should just add it to the antivirus exceptions, as I did. Sometimes antiviruses identify SCawler as a virus. This is usually related to the number of files being edited (users' settings files) and the number of files being downloaded. In this case, the antivirus can also remove these files, which will damage users' settings. **If you don't trust SCrawler, just delete it.** - **Antivirus detects SCrawler as a virus** :arrow_forward: SCrawler doesn't contain any viruses at all. All code is posted on GitHub. You can review it. I have nothing to hide. SCrawler just downloads pictures and videos. That's all. If you trust SCrawler, you should just add it to the antivirus exceptions, as I did. Sometimes antiviruses identify SCawler as a virus. This is usually related to the number of files being edited (users' settings files) and the number of files being downloaded. In this case, the antivirus can also remove these files, which will damage users' settings. **If you don't trust SCrawler, just delete it.**
- **Antivirus detects gallery-dl as a virus** :arrow_forward: it's a trustworthy program that is trusted by thousands of people around the world. Antiviruses identify some builds as containing viruses, but this is not true. **If you don't trust gallery-dl, you can simply delete it**. **But if you delete it, you won't be able to download [Twitter & Pinterest](https://github.com/AAndyProgram/SCrawler/wiki/Settings#gallery-dl).** You should decide for yourself. - **Antivirus detects gallery-dl as a virus** :arrow_forward: it's a trustworthy program that is trusted by thousands of people around the world. Antiviruses identify some builds as containing viruses, but this is not true. **If you don't trust gallery-dl, you can simply delete it. But if you delete it, you won't be able to download [Twitter & Pinterest](https://github.com/AAndyProgram/SCrawler/wiki/Settings#gallery-dl).** You should decide for yourself.
## Sites questions ## Sites questions
@@ -67,7 +67,7 @@ I strongly recommend you to **regularly** create backup copies of the settings f
- TikTok: works via yt-dlp. If something doesn't download, we need to wait until yt-dlp fixes it. TikTok doesn't require cookies to download. - TikTok: works via yt-dlp. If something doesn't download, we need to wait until yt-dlp fixes it. TikTok doesn't require cookies to download.
- Porn sites: **COOKIES**! - Porn sites: **COOKIES**!
- ThisVid: https://github.com/AAndyProgram/SCrawler/wiki/Settings#thisvid-faq - ThisVid: https://github.com/AAndyProgram/SCrawler/wiki/Settings#thisvid-faq
- **OnlyFans**: cookies + **all fields** + [OF-Scraper (download the correct version that I pointed)](https://github.com/AAndyProgram/SCrawler/wiki/Settings#of-scraper) & [mp4decrypt](https://www.bento4.com/downloads/) to download DRM protected videos. [OF-Scraper support](https://github.com/AAndyProgram/SCrawler/wiki/Settings#of-scraper-support). Also read [this](https://github.com/AAndyProgram/SCrawler/wiki/Settings#onlyfans-faq) - **OnlyFans**: cookies + **all fields** + [OF-Scraper (download the correct version that I pointed)](https://github.com/AAndyProgram/SCrawler/wiki/Settings#of-scraper) & [mp4decrypt](https://www.bento4.com/downloads/) & **DRM keys** to download DRM protected videos. [OF-Scraper support](https://github.com/AAndyProgram/SCrawler/wiki/Settings#of-scraper-support). Also read [this](https://github.com/AAndyProgram/SCrawler/wiki/Settings#onlyfans-faq)
- **JustForFans**: **THE VIDEO ISN'T DOWNLOADING AT THE MOMENT** ([Issue](https://discord.com/channels/1124032649682493462/1205547615199039551/1231349555132366870)) - **JustForFans**: **THE VIDEO ISN'T DOWNLOADING AT THE MOMENT** ([Issue](https://discord.com/channels/1124032649682493462/1205547615199039551/1231349555132366870))
## Other questions ## Other questions

View File

@@ -191,7 +191,7 @@ Namespace API.YouTube.Base
<Browsable(True), GridVisible, XMLVN({"Defaults"}), Category("Defaults"), DisplayName("Use cookies"), <Browsable(True), GridVisible, XMLVN({"Defaults"}), Category("Defaults"), DisplayName("Use cookies"),
Description("By default, use cookies when downloading from YouTube.")> Description("By default, use cookies when downloading from YouTube.")>
Public ReadOnly Property DefaultUseCookies As XMLValue(Of Boolean) Public ReadOnly Property DefaultUseCookies As XMLValue(Of Boolean)
<Browsable(True), GridVisible, XMLVN({"Defaults"}, Protocols.Any), Category("Defaults"), DisplayName("Protocol"), <Browsable(True), GridVisible, XMLVN({"Defaults"}, Protocols.https), Category("Defaults"), DisplayName("Protocol"),
Description("Priority download protocol. Default: 'Any'")> Description("Priority download protocol. Default: 'Any'")>
Public ReadOnly Property DefaultProtocol As XMLValue(Of Protocols) Public ReadOnly Property DefaultProtocol As XMLValue(Of Protocols)
<Browsable(True), GridVisible(False), XMLVN({"Defaults"}), Category("Defaults"), <Browsable(True), GridVisible(False), XMLVN({"Defaults"}), Category("Defaults"),
@@ -267,6 +267,9 @@ Namespace API.YouTube.Base
<Browsable(True), GridVisible, XMLVN({"Defaults"}), Category("Defaults"), DisplayName("Add date to title: video list"), <Browsable(True), GridVisible, XMLVN({"Defaults"}), Category("Defaults"), DisplayName("Add date to title: video list"),
Description("Add video upload date before video title (visual only) in the video list")> Description("Add video upload date before video title (visual only) in the video list")>
Public ReadOnly Property FileAddDateToFileName_VideoList As XMLValue(Of Boolean) Public ReadOnly Property FileAddDateToFileName_VideoList As XMLValue(Of Boolean)
<Browsable(True), GridVisible, XMLVN({"Defaults"}, FileDateMode.None), Category("Defaults"), DisplayName("Add channel to file name"),
Description("Add channel name before/after the file name")>
Public ReadOnly Property FileAddChannelToFileName As XMLValue(Of FileDateMode)
#End Region #End Region
#Region "Defaults ChannelsDownload" #Region "Defaults ChannelsDownload"
<Browsable(True), GridVisible, XMLVN({"Defaults", "Channels"}), Category("Defaults"), DisplayName("Default download tabs for channels"), <Browsable(True), GridVisible, XMLVN({"Defaults", "Channels"}), Category("Defaults"), DisplayName("Default download tabs for channels"),

View File

@@ -13,7 +13,7 @@ Imports System.Runtime.InteropServices
<Assembly: AssemblyDescription("YouTube plugin environment")> <Assembly: AssemblyDescription("YouTube plugin environment")>
<Assembly: AssemblyCompany("AndyProgram")> <Assembly: AssemblyCompany("AndyProgram")>
<Assembly: AssemblyProduct("SCrawler.YouTube")> <Assembly: AssemblyProduct("SCrawler.YouTube")>
<Assembly: AssemblyCopyright("Copyright © 2024")> <Assembly: AssemblyCopyright("Copyright © 2025")>
<Assembly: AssemblyTrademark("AndyProgram")> <Assembly: AssemblyTrademark("AndyProgram")>
<Assembly: ComVisible(False)> <Assembly: ComVisible(False)>
@@ -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("2024.10.24.0")> <Assembly: AssemblyVersion("2025.1.12.0")>
<Assembly: AssemblyFileVersion("2024.10.24.0")> <Assembly: AssemblyFileVersion("2025.1.12.0")>
<Assembly: NeutralResourcesLanguage("en")> <Assembly: NeutralResourcesLanguage("en")>

View File

@@ -685,10 +685,17 @@ Namespace API.YouTube.Objects
Friend Sub FileDateUpdate() Friend Sub FileDateUpdate()
Dim n$ = _File.Name.StringTrim Dim n$ = _File.Name.StringTrim
Dim s$ = IIf(n.IsEmptyString, String.Empty, " ") Dim s$ = IIf(n.IsEmptyString, String.Empty, " ")
Dim c$ = AccountName.IfNullOrEmpty(UserID)
Select Case MyYouTubeSettings.FileAddDateToFileName.Value Select Case MyYouTubeSettings.FileAddDateToFileName.Value
Case FileDateMode.Before : n = $"[{DateAdded:yyyy-MM-dd}]{s}{n}" Case FileDateMode.Before : n = $"[{DateAdded:yyyy-MM-dd}]{s}{n}"
Case FileDateMode.After : n = $"{n}{s}[{DateAdded:yyyy-MM-dd}]" Case FileDateMode.After : n = $"{n}{s}[{DateAdded:yyyy-MM-dd}]"
End Select End Select
If Not c.IsEmptyString Then
Select Case MyYouTubeSettings.FileAddChannelToFileName.Value
Case FileDateMode.Before : n = $"[{c}] {n}"
Case FileDateMode.After : n = $"{n} [{c}]"
End Select
End If
_File.Name = n _File.Name = n
End Sub End Sub
Public Property FileSettings As SFile Public Property FileSettings As SFile
@@ -1214,6 +1221,9 @@ Namespace API.YouTube.Objects
fileDesr.Extension = "txt" fileDesr.Extension = "txt"
Using fileDesrText As New TextSaver(fileDesr) Using fileDesrText As New TextSaver(fileDesr)
If .CreateDescriptionFiles_AddUploadDate Then fileDesrText.Append($"Uploaded: {DateAdded:yyyy-MM-dd HH:mm:ss}") If .CreateDescriptionFiles_AddUploadDate Then fileDesrText.Append($"Uploaded: {DateAdded:yyyy-MM-dd HH:mm:ss}")
fileDesrText.AppendLine($"URL: {URL}")
fileDesrText.AppendLine($"Channel name: {AccountName}")
fileDesrText.AppendLine($"Channel ID: {UserID}")
If Not Description.IsEmptyString Then If Not Description.IsEmptyString Then
If Not fileDesrText.IsEmptyString Then fileDesrText.AppendLine.AppendLine() If Not fileDesrText.IsEmptyString Then fileDesrText.AppendLine.AppendLine()
fileDesrText.Append(Description) fileDesrText.Append(Description)

View File

@@ -13,7 +13,7 @@ Imports System.Runtime.InteropServices
<Assembly: AssemblyDescription("SCrawler YouTube downloader")> <Assembly: AssemblyDescription("SCrawler YouTube downloader")>
<Assembly: AssemblyCompany("AndyProgram")> <Assembly: AssemblyCompany("AndyProgram")>
<Assembly: AssemblyProduct("SCrawler.YouTubeDownloader")> <Assembly: AssemblyProduct("SCrawler.YouTubeDownloader")>
<Assembly: AssemblyCopyright("Copyright © 2024")> <Assembly: AssemblyCopyright("Copyright © 2025")>
<Assembly: AssemblyTrademark("AndyProgram")> <Assembly: AssemblyTrademark("AndyProgram")>
<Assembly: ComVisible(False)> <Assembly: ComVisible(False)>
@@ -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("2024.10.24.0")> <Assembly: AssemblyVersion("2025.1.12.0")>
<Assembly: AssemblyFileVersion("2024.10.24.0")> <Assembly: AssemblyFileVersion("2025.1.12.0")>
<Assembly: NeutralResourcesLanguage("en")> <Assembly: NeutralResourcesLanguage("en")>

View File

@@ -6,8 +6,9 @@
' '
' 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.Net Imports SCrawler.Plugin
Imports PersonalUtilities.Functions.RegularExpressions Imports PersonalUtilities.Functions.RegularExpressions
Imports Download = SCrawler.Plugin.ISiteSettings.Download
Namespace API.Base Namespace API.Base
Friend NotInheritable Class DownDetector Friend NotInheritable Class DownDetector
Private Shared ReadOnly Property Params As New RParams("x:.'([\S]+?)',.y:.(\d+)", -1, Nothing, RegexReturn.List) Private Shared ReadOnly Property Params As New RParams("x:.'([\S]+?)',.y:.(\d+)", -1, Nothing, RegexReturn.List)
@@ -34,34 +35,106 @@ Namespace API.Base
Try Try
Dim l As List(Of Data) = Nothing Dim l As List(Of Data) = Nothing
Dim l2 As List(Of Data) = Nothing Dim l2 As List(Of Data) = Nothing
Using w As New WebClient Dim r$ = GetWebString($"https://downdetector.co.uk/status/{Site}/",, EDP.ThrowException)
Dim r$ = w.DownloadString($"https://downdetector.co.uk/status/{Site}/") If Not r.IsEmptyString Then
If Not r.IsEmptyString Then l = RegexFields(Of Data)(r, {Params}, {1, 2})
l = RegexFields(Of Data)(r, {Params}, {1, 2}) If l.ListExists(2) Then
If l.ListExists(2) Then l.Sort()
l.Sort() l2 = New List(Of Data)
l2 = New List(Of Data) Dim d As Data
Dim d As Data Dim eDates As New List(Of Date)
Dim eDates As New List(Of Date) Dim MaxValue As Func(Of Date, Integer) = Function(dd) (From ddd As Data In l Where ddd.Date = dd Select ddd.Value).DefaultIfEmpty(0).Max
Dim MaxValue As Func(Of Date, Integer) = Function(dd) (From ddd In l Where ddd.Date = dd Select ddd.Value).DefaultIfEmpty(0).Max For i% = 0 To l.Count - 1
For i% = 0 To l.Count - 1 If Not eDates.Contains(l(i).Date) Then
If Not eDates.Contains(l(i).Date) Then d = l(i)
d = l(i) d.Value = MaxValue(d.Date)
d.Value = MaxValue(d.Date) l2.Add(d)
l2.Add(d) eDates.Add(d.Date)
eDates.Add(d.Date) End If
End If Next
Next eDates.Clear()
eDates.Clear() l.Clear()
l.Clear() l2.Sort()
l2.Sort()
End If
End If End If
End Using End If
Return l2 Return l2
Catch ex As Exception Catch ex As Exception
Return ErrorsDescriber.Execute(EDP.SendToLog + EDP.ReturnValue, ex, $"[DownDetector.GetData({Site})]") Return ErrorsDescriber.Execute(EDP.SendToLog + EDP.ReturnValue, ex, $"[DownDetector.GetData({Site})]")
End Try End Try
End Function End Function
Friend Interface IDownDetector
ReadOnly Property Value As Integer
ReadOnly Property AddToLog As Boolean
ReadOnly Property CheckSite As String
Function Available(ByVal What As Download, ByVal Silent As Boolean) As Boolean
End Interface
Friend Class Checker(Of T As {ISiteSettings, IDownDetector})
Protected ReadOnly Property Source As T
Private ReadOnly NP As New ANumbers With {.FormatOptions = ANumbers.Options.GroupIntegral}
Friend Sub New(ByRef _Source As T)
Source = _Source
End Sub
Private ____AvailableChecked As Boolean = False
Private ____AvailableResult As Boolean = False
Friend Overridable Function Available(ByVal What As Download, ByVal Silent As Boolean) As Boolean
If Settings.DownDetectorEnabled And Source.Value >= 0 Then
If Not ____AvailableChecked Then
____AvailableResult = AvailableImpl(What, Silent)
____AvailableChecked = True
End If
Return ____AvailableResult
Else
Return True
End If
End Function
Protected Overridable Function AvailableImpl(ByVal What As Download, ByVal Silent As Boolean) As Boolean
Try
Source.AvailableText = String.Empty
If Source.Value < 0 Then
Return True
Else
Dim dl As List(Of Data) = GetData(Source.CheckSite)
If dl.ListExists Then
dl = dl.Take(4).ToList
Dim avg% = dl.Average(Function(d) d.Value)
If avg > Source.Value Then
Source.AvailableText = $"Over the past hour, {Source.Site} has received an average of {avg.NumToString(NP)} outage reports:{vbCr}{dl.ListToString(vbCr)}"
If Source.AddToLog Then MyMainLOG = Source.AvailableText
If Silent Then
Return AvailableImpl_FALSE_SILENT()
Else
If MsgBoxE({$"{Source.AvailableText}{vbCr}{vbCr}Do you want to continue parsing {Source.Site} data?",
$"There are outage reports on {Source.Site}"}, vbYesNo) = vbYes Then
Return AvailableImpl_FALSE_SILENT_NOT_MSG_YES()
Else
Return AvailableImpl_FALSE_SILENT_NOT_MSG_NO()
End If
End If
End If
End If
Return AvailableImpl_TRUE()
End If
Catch ex As Exception
Return ErrorsDescriber.Execute(EDP.SendToLog + EDP.ReturnValue, ex, $"[API.{Source.Site}.SiteSettings.Available([DownDetector])]", True)
End Try
End Function
Protected Overridable Function AvailableImpl_TRUE() As Boolean
Return True
End Function
Protected Overridable Function AvailableImpl_FALSE_SILENT() As Boolean
Return False
End Function
Protected Overridable Function AvailableImpl_FALSE_SILENT_NOT_MSG_YES() As Boolean
Return True
End Function
Protected Overridable Function AvailableImpl_FALSE_SILENT_NOT_MSG_NO() As Boolean
Return False
End Function
Friend Overridable Sub Reset()
____AvailableChecked = False
____AvailableResult = False
Source.AvailableText = String.Empty
End Sub
End Class
End Class End Class
End Namespace End Namespace

View File

@@ -16,8 +16,8 @@ Imports PersonalUtilities.Tools.Web.Cookies
Imports Download = SCrawler.Plugin.ISiteSettings.Download Imports Download = SCrawler.Plugin.ISiteSettings.Download
Imports DN = SCrawler.API.Base.DeclaredNames Imports DN = SCrawler.API.Base.DeclaredNames
Namespace API.Instagram Namespace API.Instagram
<Manifest(InstagramSiteKey), SeparatedTasks(1), SavedPosts, SpecialForm(False)> <Manifest(InstagramSiteKey), SeparatedTasks(1), SavedPosts, SpecialForm(False), UseDownDetector>
Friend Class SiteSettings : Inherits SiteSettingsBase Friend Class SiteSettings : Inherits SiteSettingsBase : Implements DownDetector.IDownDetector
#Region "Declarations" #Region "Declarations"
#Region "Providers" #Region "Providers"
Friend Class TimersChecker : Inherits FieldsCheckerProviderBase Friend Class TimersChecker : Inherits FieldsCheckerProviderBase
@@ -270,6 +270,26 @@ Namespace API.Instagram
Private ReadOnly Property TaggedNotifyLimitProvider As IFormatProvider Private ReadOnly Property TaggedNotifyLimitProvider As IFormatProvider
#End Region #End Region
#End Region #End Region
#Region "IDownDetector Support"
Private ReadOnly Property IDownDetector_Value As Integer Implements DownDetector.IDownDetector.Value
Get
Return DownDetectorValue.Value
End Get
End Property
Private ReadOnly Property IDownDetector_AddToLog As Boolean Implements DownDetector.IDownDetector.AddToLog
Get
Return DownDetectorValueAddToLog.Value
End Get
End Property
Private ReadOnly Property IDownDetector_CheckSite As String Implements DownDetector.IDownDetector.CheckSite
Get
Return "instagram"
End Get
End Property
Private Function IDownDetector_Available(ByVal What As Download, ByVal Silent As Boolean) As Boolean Implements DownDetector.IDownDetector.Available
Return MDD.Available(What, Silent)
End Function
#End Region
#Region "429 bypass" #Region "429 bypass"
<PXML("InstagramDownloadingErrorDate")> <PXML("InstagramDownloadingErrorDate")>
Private ReadOnly Property DownloadingErrorDate As PropertyValue Private ReadOnly Property DownloadingErrorDate As PropertyValue
@@ -504,6 +524,8 @@ Namespace API.Instagram
LastRequestsCountLabel = New PropertyValue(String.Empty, GetType(String)) LastRequestsCountLabel = New PropertyValue(String.Empty, GetType(String))
MyLastRequests = New Dictionary(Of Date, Integer) MyLastRequests = New Dictionary(Of Date, Integer)
MDD = New DownDetector.Checker(Of SiteSettings)(Me)
_AllowUserAgentUpdate = False _AllowUserAgentUpdate = False
UrlPatternUser = "https://www.instagram.com/{0}/" UrlPatternUser = "https://www.instagram.com/{0}/"
UserRegex = RParams.DMS(String.Format(UserRegexDefaultPattern, "instagram.com/"), 1) UserRegex = RParams.DMS(String.Format(UserRegexDefaultPattern, "instagram.com/"), 1)
@@ -551,18 +573,10 @@ Namespace API.Instagram
End Function End Function
#End Region #End Region
#Region "Downloading" #Region "Downloading"
Private ____DownloadStarted As Boolean = False Private ReadOnly MDD As DownDetector.Checker(Of SiteSettings)
Private ____AvailableRequested As Boolean = False
Private ____AvailableSilent As Boolean = True
Private ____AvailableChecked As Boolean = False
Private ____AvailableResult As Boolean = False
Private Sub ResetDownloadOptions() Private Sub ResetDownloadOptions()
If ActiveJobs < 1 Then If ActiveJobs < 1 Then
____DownloadStarted = False MDD.Reset()
____AvailableRequested = False
____AvailableChecked = False
____AvailableSilent = True
____AvailableResult = False
If ActiveSessionRequestsExists Then RefreshMyLastRequests(Now) If ActiveSessionRequestsExists Then RefreshMyLastRequests(Now)
ActiveSessionRequestsExists = False ActiveSessionRequestsExists = False
_NextWNM = UserData.WNM.Notify _NextWNM = UserData.WNM.Notify
@@ -573,69 +587,11 @@ Namespace API.Instagram
End If End If
End Sub End Sub
Friend Overrides Function Available(ByVal What As Download, ByVal Silent As Boolean) As Boolean Friend Overrides Function Available(ByVal What As Download, ByVal Silent As Boolean) As Boolean
If MyBase.Available(What, Silent) And ActiveJobs < 2 Then Return MyBase.Available(What, Silent) And ActiveJobs < 2
If CInt(DownDetectorValue.Value) >= 0 Then
If ____DownloadStarted Then
____AvailableRequested = True
____AvailableSilent = Silent
Return True
Else
Return AvailableImpl(What, Silent)
End If
Else
Return True
End If
Else
Return False
End If
End Function
#Disable Warning IDE0060
Private Function AvailableImpl(ByVal What As Download, ByVal Silent As Boolean) As Boolean
#Enable Warning
Try
AvailableText = String.Empty
If CInt(DownDetectorValue.Value) = -1 Then
Return True
Else
Dim dl As List(Of DownDetector.Data) = DownDetector.GetData("instagram")
If dl.ListExists Then
dl = dl.Take(4).ToList
Dim avg% = dl.Average(Function(d) d.Value)
If avg > CInt(DownDetectorValue.Value) Then
AvailableText = "Over the past hour, Instagram has received an average of " &
avg.NumToString(New ANumbers With {.FormatOptions = ANumbers.Options.GroupIntegral}) & " outage reports:" & vbCr &
dl.ListToString(vbCr)
If CBool(DownDetectorValueAddToLog.Value) Then MyMainLOG = AvailableText
If Silent Then
Return False
Else
Return MsgBoxE({$"{AvailableText}{vbCr}{vbCr}Do you want to continue parsing Instagram data?",
"There are outage reports on Instagram"}, vbYesNo) = vbYes
End If
End If
End If
Return True
End If
Catch ex As Exception
Return ErrorsDescriber.Execute(EDP.SendToLog + EDP.ReturnValue, ex, "[API.Instagram.SiteSettings.Available]", True)
End Try
End Function End Function
Friend Property SkipUntilNextSession As Boolean = False Friend Property SkipUntilNextSession As Boolean = False
Friend Overrides Function ReadyToDownload(ByVal What As Download) As Boolean Friend Overrides Function ReadyToDownload(ByVal What As Download) As Boolean
If ActiveJobs < 2 AndAlso Not SkipUntilNextSession AndAlso ReadyForDownload AndAlso BaseAuthExists() AndAlso CBool(DownloadTimeline.Value) Then Return ActiveJobs < 2 AndAlso Not SkipUntilNextSession AndAlso ReadyForDownload AndAlso BaseAuthExists() AndAlso CBool(DownloadTimeline.Value)
If ____DownloadStarted And ____AvailableRequested Then
____AvailableResult = AvailableImpl(What, ____AvailableSilent)
____AvailableChecked = True
____AvailableRequested = False
Return ____AvailableResult
ElseIf ____AvailableChecked Then
Return ____AvailableResult
Else
Return True
End If
Else
Return False
End If
End Function End Function
Private ActiveJobs As Integer = 0 Private ActiveJobs As Integer = 0
Private ActiveSessionDate As Date Private ActiveSessionDate As Date
@@ -645,7 +601,7 @@ Namespace API.Instagram
Friend Overrides Sub DownloadStarted(ByVal What As Download) Friend Overrides Sub DownloadStarted(ByVal What As Download)
ResetDownloadOptions() ResetDownloadOptions()
ActiveJobs += 1 ActiveJobs += 1
If ActiveJobs = 1 Then ____DownloadStarted = True : ActiveSessionDate = Now If ActiveJobs = 1 Then ActiveSessionDate = Now
If Not HH_IG_WWW_CLAIM_IS_ZERO AndAlso If Not HH_IG_WWW_CLAIM_IS_ZERO AndAlso
( (
(CBool(HH_IG_WWW_CLAIM_USE_DEFAULT_ALGO.Value) AndAlso MyLastRequestsDate.AddMinutes(HH_IG_WWW_CLAIM_UPDATE_INTERVAL.Value) < Now) Or (CBool(HH_IG_WWW_CLAIM_USE_DEFAULT_ALGO.Value) AndAlso MyLastRequestsDate.AddMinutes(HH_IG_WWW_CLAIM_UPDATE_INTERVAL.Value) < Now) Or

View File

@@ -42,7 +42,7 @@ Namespace API.Instagram
Private Const Name_ForceUpdateUserInfo As String = "ForceUpdateUserInfo" Private Const Name_ForceUpdateUserInfo As String = "ForceUpdateUserInfo"
#End Region #End Region
#Region "Declarations" #Region "Declarations"
Protected Structure PostKV : Implements IEContainerProvider Friend Structure PostKV : Implements IEContainerProvider
Private Const Name_Code As String = "Code" Private Const Name_Code As String = "Code"
Private Const Name_Section As String = "Section" Private Const Name_Section As String = "Section"
Friend Code As String Friend Code As String
@@ -252,25 +252,28 @@ Namespace API.Instagram
End If End If
End Get End Get
End Property End Property
Protected Sub LoadSavePostsKV(ByVal Load As Boolean) Friend Overloads Shared Sub LoadSavePostsKV(ByVal Load As Boolean, ByVal fPosts As SFile, ByRef List As List(Of PostKV))
Dim x As XmlFile Dim x As XmlFile
Dim f As SFile = MyFilePostsKV Dim f As SFile = fPosts
If Not f.IsEmptyString Then If Not f.IsEmptyString Then
If Load Then If Load Then
PostsKVIDs.Clear() List.Clear()
x = New XmlFile(f, Protector.Modes.All, False) With {.AllowSameNames = True, .XmlReadOnly = True} x = New XmlFile(f, Protector.Modes.All, False) With {.AllowSameNames = True, .XmlReadOnly = True}
x.LoadData() x.LoadData()
If x.Count > 0 Then PostsKVIDs.ListAddList(x, LAP.IgnoreICopier) If x.Count > 0 Then List.ListAddList(x, LAP.IgnoreICopier)
x.Dispose() x.Dispose()
Else Else
x = New XmlFile With {.AllowSameNames = True} x = New XmlFile With {.AllowSameNames = True}
x.AddRange(PostsKVIDs) x.AddRange(List)
x.Name = "Posts" x.Name = "Posts"
x.Save(f, EDP.SendToLog) x.Save(f, EDP.SendToLog)
x.Dispose() x.Dispose()
End If End If
End If End If
End Sub End Sub
Protected Overloads Sub LoadSavePostsKV(ByVal Load As Boolean)
LoadSavePostsKV(Load, MyFilePostsKV, PostsKVIDs)
End Sub
Protected Overloads Function PostKvExists(ByVal pkv As PostKV) As Boolean Protected Overloads Function PostKvExists(ByVal pkv As PostKV) As Boolean
Return PostKvExists(pkv.ID, False, pkv.Section) OrElse PostKvExists(pkv.Code, True, pkv.Section) Return PostKvExists(pkv.ID, False, pkv.Section) OrElse PostKvExists(pkv.Code, True, pkv.Section)
End Function End Function
@@ -476,7 +479,7 @@ Namespace API.Instagram
If Not errorFound Then LoadSavePostsKV(False) If Not errorFound Then LoadSavePostsKV(False)
End Try End Try
End Sub End Sub
Private Sub ValidateExtension() Protected Sub ValidateExtension()
Try Try
Const heic$ = "heic" Const heic$ = "heic"
If _TempMediaList.Count > 0 AndAlso _TempMediaList.Exists(Function(mm) mm.File.Extension = heic) Then If _TempMediaList.Count > 0 AndAlso _TempMediaList.Exists(Function(mm) mm.File.Extension = heic) Then
@@ -503,7 +506,7 @@ Namespace API.Instagram
Protected Overrides Sub Responser_ResponseReceived(ByVal Sender As Object, ByVal e As EventArguments.WebDataResponse) Protected Overrides Sub Responser_ResponseReceived(ByVal Sender As Object, ByVal e As EventArguments.WebDataResponse)
Declarations.UpdateResponser(e, Responser, WwwClaimUpdate) Declarations.UpdateResponser(e, Responser, WwwClaimUpdate)
End Sub End Sub
Protected Enum Sections : Timeline : Reels : Tagged : Stories : UserStories : SavedPosts : End Enum Friend Enum Sections : Timeline : Reels : Tagged : Stories : UserStories : SavedPosts : End Enum
Protected Const StoriesFolder As String = "Stories" Protected Const StoriesFolder As String = "Stories"
Private Const TaggedFolder As String = "Tagged" Private Const TaggedFolder As String = "Tagged"
#Region "429 bypass" #Region "429 bypass"

View File

@@ -15,6 +15,7 @@ Namespace API.Mastodon
<PSetting(Address:=SettingAddress.None)> Friend Overrides Property DownloadModelSearch As Boolean <PSetting(Address:=SettingAddress.None)> Friend Overrides Property DownloadModelSearch As Boolean
<PSetting(Address:=SettingAddress.None)> Friend Overrides Property DownloadModelForceApply As Boolean <PSetting(Address:=SettingAddress.None)> Friend Overrides Property DownloadModelForceApply As Boolean
<PSetting(Address:=SettingAddress.None)> Friend Overrides Property DownloadModelLikes As Boolean <PSetting(Address:=SettingAddress.None)> Friend Overrides Property DownloadModelLikes As Boolean
<PSetting(Address:=SettingAddress.None)> Friend Overrides Property DownloadBroadcasts As Boolean
<PSetting(Address:=SettingAddress.None)> Friend Overrides Property UserName As String <PSetting(Address:=SettingAddress.None)> Friend Overrides Property UserName As String
Friend Sub New(ByVal s As SiteSettings) Friend Sub New(ByVal s As SiteSettings)
MyBase.New(s) MyBase.New(s)

View File

@@ -73,6 +73,10 @@ Namespace API.OnlyFans
End If End If
Return String.Empty Return String.Empty
End Function End Function
<PropertyOption(ControlText:="Update cookies during requests",
ControlToolTip:="If unchecked, cookies will not be updated during requests. Initial cookies will always be used.", IsAuth:=True),
PClonable, PXML, HiddenControl>
Friend ReadOnly Property EnableCookiesUpdate As PropertyValue
#End Region #End Region
#Region "Errors" #Region "Errors"
<PClonable, PXML("UpdateRules401")> Private ReadOnly Property UpdateRules401_XML As PropertyValue <PClonable, PXML("UpdateRules401")> Private ReadOnly Property UpdateRules401_XML As PropertyValue
@@ -112,7 +116,7 @@ Namespace API.OnlyFans
End Property End Property
Friend Const KeyModeDefault_Default As String = "cdrm" Friend Const KeyModeDefault_Default As String = "cdrm"
<PClonable, PXML("KeyModeDefault")> Private ReadOnly Property KeyModeDefault_XML As PropertyValue <PClonable, PXML("KeyModeDefault")> Private ReadOnly Property KeyModeDefault_XML As PropertyValue
<PropertyOption(ControlText:="key-mode-default", Category:=CAT_OFS)> <PropertyOption(ControlText:="key-mode-default", ControlToolTip:="Examples: cdrm, cdrm2, keydb, manual", Category:=CAT_OFS)>
Friend ReadOnly Property KeyModeDefault As PropertyValue Friend ReadOnly Property KeyModeDefault As PropertyValue
Get Get
If Not DefaultInstance Is Nothing Then If Not DefaultInstance Is Nothing Then
@@ -133,6 +137,62 @@ Namespace API.OnlyFans
End If End If
End Get End Get
End Property End Property
<PClonable, PXML("KEYS_Key")> Private ReadOnly Property OFS_KEYS_Key_XML As PropertyValue
<PropertyOption(ControlText:="Private key", ControlToolTip:="Path to the DRM key file 'private_key.pem'", Category:=CAT_OFS)>
Friend ReadOnly Property OFS_KEYS_Key As PropertyValue
Get
If Not DefaultInstance Is Nothing Then
Return DirectCast(DefaultInstance, SiteSettings).OFS_KEYS_Key_XML
Else
Return OFS_KEYS_Key_XML
End If
End Get
End Property
<PClonable, PXML("KEYS_ClientID")> Private ReadOnly Property OFS_KEYS_ClientID_XML As PropertyValue
<PropertyOption(ControlText:="Client ID", ControlToolTip:="Path to the DRM key file 'client_id.bin'", Category:=CAT_OFS)>
Friend ReadOnly Property OFS_KEYS_ClientID As PropertyValue
Get
If Not DefaultInstance Is Nothing Then
Return DirectCast(DefaultInstance, SiteSettings).OFS_KEYS_ClientID_XML
Else
Return OFS_KEYS_ClientID_XML
End If
End Get
End Property
<PropertiesDataChecker({NameOf(KeyModeDefault), NameOf(OFS_KEYS_Key), NameOf(OFS_KEYS_ClientID)})>
Private Function OFS_KEYS_CHECKER(ByVal p As IEnumerable(Of PropertyData)) As Boolean
Const manualMode$ = "manual"
If p.ListExists Then
Dim m$ = String.Empty, k$ = String.Empty, cid$ = String.Empty
For Each pp As PropertyData In p
Select Case pp.Name
Case NameOf(KeyModeDefault) : m = pp.Value
Case NameOf(OFS_KEYS_Key) : k = pp.Value
Case NameOf(OFS_KEYS_ClientID) : cid = pp.Value
Case Else : Throw New ArgumentException($"Property name '{pp.Name}' is not implemented", "Property Name")
End Select
Next
If k.IsEmptyString And cid.IsEmptyString Then
Return True
ElseIf Not k.IsEmptyString And Not cid.IsEmptyString Then
If m = manualMode Then
Return True
Else
Return MsgBoxE({$"You are using key files and have selected '{m}' mode." & vbCr &
$"To use key files, you should use the '{manualMode}' mode" & vbCr &
"Are you sure you want to use this mode?", "Incorrect mode"}, vbExclamation + vbYesNo) = vbYes
End If
End If
Dim t As New MMessage("", "Key missing",, vbCritical)
If k.IsEmptyString Then
t.Text = "'Private key' is missing"
ElseIf cid.IsEmptyString Then
t.Text = "'Client ID' is missing"
End If
If Not t.Text.IsEmptyString Then t.Show()
End If
Return False
End Function
#End Region #End Region
#End Region #End Region
#Region "Initializer" #Region "Initializer"
@@ -171,6 +231,8 @@ Namespace API.OnlyFans
UserAgent = New PropertyValue(IIf(.UserAgentExists, .UserAgent, String.Empty), GetType(String), Sub(v) UpdateHeader(NameOf(UserAgent), v)) UserAgent = New PropertyValue(IIf(.UserAgentExists, .UserAgent, String.Empty), GetType(String), Sub(v) UpdateHeader(NameOf(UserAgent), v))
End With End With
EnableCookiesUpdate = New PropertyValue(True)
DownloadTimeline = New PropertyValue(True) DownloadTimeline = New PropertyValue(True)
DownloadStories = New PropertyValue(True) DownloadStories = New PropertyValue(True)
DownloadHighlights = New PropertyValue(True) DownloadHighlights = New PropertyValue(True)
@@ -191,6 +253,8 @@ Namespace API.OnlyFans
OFScraperMP4decrypt_XML = New PropertyValue(String.Empty, GetType(String)) OFScraperMP4decrypt_XML = New PropertyValue(String.Empty, GetType(String))
KeyModeDefault_XML = New PropertyValue(KeyModeDefault_Default) KeyModeDefault_XML = New PropertyValue(KeyModeDefault_Default)
Keydb_Api_XML = New PropertyValue(String.Empty, GetType(String)) Keydb_Api_XML = New PropertyValue(String.Empty, GetType(String))
OFS_KEYS_Key_XML = New PropertyValue(String.Empty, GetType(String))
OFS_KEYS_ClientID_XML = New PropertyValue(String.Empty, GetType(String))
UpdateRules401_XML = New PropertyValue(False) UpdateRules401_XML = New PropertyValue(False)

View File

@@ -99,7 +99,7 @@ Namespace API.OnlyFans
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()
AddHandler Responser.ResponseReceived, AddressOf Responser_ResponseReceived If MySettings.EnableCookiesUpdate.Value Then AddHandler Responser.ResponseReceived, AddressOf Responser_ResponseReceived
UpdateCookieHeader() UpdateCookieHeader()
If Not IsSavedPosts Then If Not IsSavedPosts Then
@@ -119,7 +119,7 @@ Namespace API.OnlyFans
End Try End Try
End Sub End Sub
Protected Overrides Sub Responser_ResponseReceived(ByVal Sender As Object, ByVal e As WebDataResponse) Protected Overrides Sub Responser_ResponseReceived(ByVal Sender As Object, ByVal e As WebDataResponse)
If e.CookiesExists Then If e.CookiesExists And CBool(MySettings.EnableCookiesUpdate.Value) Then
CCookie.Update(e.Cookies, CookieKeeper.UpdateModes.ReplaceByNameAll,, EDP.ReturnValue) CCookie.Update(e.Cookies, CookieKeeper.UpdateModes.ReplaceByNameAll,, EDP.ReturnValue)
UpdateCookieHeader() UpdateCookieHeader()
End If End If
@@ -612,7 +612,7 @@ Namespace API.OnlyFans
'#If DEBUG Then '#If DEBUG Then
'Debug.WriteLine(command) 'Debug.WriteLine(command)
'#End If '#End If
Using b As New TokenBatch(Token) : b.Execute(command) : End Using Using b As New TokenBatch(Token) With {.DebugMode = False} : b.Execute(command) : End Using
Return SFile.GetFiles(conf, "*.mp4", IO.SearchOption.AllDirectories, EDP.ReturnValue) Return SFile.GetFiles(conf, "*.mp4", IO.SearchOption.AllDirectories, EDP.ReturnValue)
End If End If
Return Nothing Return Nothing
@@ -623,7 +623,13 @@ Namespace API.OnlyFans
Private Function OFS_CreateConfig() As SFile Private Function OFS_CreateConfig() As SFile
Try Try
Const confMainPattern$ = "{0}"": ""([^""]*)""" Const confMainPattern$ = "{0}"": ""([^""]*)"""
Const confMainPattern_Keys$ = "{0}"": ([^,]*)"
Const confMainPatternRulesManual$ = "DYNAMIC_RULE"": (""[^""]*"")" Const confMainPatternRulesManual$ = "DYNAMIC_RULE"": (""[^""]*"")"
Const m1 As Byte = 0 'not rules
Const m2 As Byte = 1 'rules
Const m3 As Byte = 2 'keys
If OFSCache Is Nothing Then OFSCache = If(IsSingleObjectDownload, Settings.Cache.NewInstance, CreateCache()) If OFSCache Is Nothing Then OFSCache = If(IsSingleObjectDownload, Settings.Cache.NewInstance, CreateCache())
Dim currentCache As CacheKeeper = OFSCache.NewInstance Dim currentCache As CacheKeeper = OFSCache.NewInstance
currentCache.Validate() currentCache.Validate()
@@ -637,35 +643,47 @@ Namespace API.OnlyFans
CType(Function(input) replaceValue, Func(Of String, String)), String.Empty, EDP.ReturnValue) CType(Function(input) replaceValue, Func(Of String, String)), String.Empty, EDP.ReturnValue)
Dim ff As SFile Dim ff As SFile
configText = f.GetText configText = f.GetText
Dim updateConf As Action(Of String, String, Boolean) = Dim updateConf As Action(Of String, String, Byte) =
Sub(ByVal patternValue As String, ByVal __replaceValue As String, ByVal __isRules As Boolean) Sub(ByVal patternValue As String, ByVal __replaceValue As String, ByVal mode As Byte)
rp.Pattern = String.Format(IIf(__isRules, confMainPatternRulesManual, confMainPattern), patternValue) Select Case mode
Case m1 : rp.Pattern = String.Format(confMainPattern, patternValue)
Case m2 : rp.Pattern = String.Format(confMainPatternRulesManual, patternValue)
Case m3 : rp.Pattern = String.Format(confMainPattern_Keys, patternValue) : __replaceValue = $"""{__replaceValue}"""
Case Else : Throw New ArgumentException($"Mode '{mode}' is not implemented", "mode")
End Select
rp.Nothing = configText rp.Nothing = configText
replaceValue = __replaceValue replaceValue = __replaceValue
configText = RegexReplace(configText, rp) configText = RegexReplace(configText, rp)
End Sub End Sub
If Not configText.IsEmptyString Then If Not configText.IsEmptyString Then
updateConf("save_location", cacheRoot.PathNoSeparator.Replace("\", "/"), False) updateConf("save_location", cacheRoot.PathNoSeparator.Replace("\", "/"), m1)
If ACheck(MySettings.OFScraperMP4decrypt.Value) Then If ACheck(MySettings.OFScraperMP4decrypt.Value) Then
ff = CStr(MySettings.OFScraperMP4decrypt.Value) ff = CStr(MySettings.OFScraperMP4decrypt.Value)
If ff.Exists Then updateConf("mp4decrypt", ff.ToString.Replace("\", "/"), False) If ff.Exists Then updateConf("mp4decrypt", ff.ToString.Replace("\", "/"), m1)
End If End If
If Settings.FfmpegFile.Exists Then updateConf("ffmpeg", Settings.FfmpegFile.File.ToString.Replace("\", "/"), False) If Settings.FfmpegFile.Exists Then updateConf("ffmpeg", Settings.FfmpegFile.File.ToString.Replace("\", "/"), m1)
updateConf("key-mode-default", CStr(MySettings.KeyModeDefault.Value).IfNullOrEmpty(SiteSettings.KeyModeDefault_Default), False)
updateConf("keydb_api", CStr(MySettings.Keydb_Api.Value), False) updateConf("key-mode-default", CStr(MySettings.KeyModeDefault.Value).IfNullOrEmpty(SiteSettings.KeyModeDefault_Default), m1)
updateConf("keydb_api", CStr(MySettings.Keydb_Api.Value), m1)
If Not CStr(MySettings.OFS_KEYS_Key.Value).IsEmptyString And Not CStr(MySettings.OFS_KEYS_ClientID.Value).IsEmptyString Then
updateConf("private-key", CStr(MySettings.OFS_KEYS_Key.Value).Replace("\", "/"), m3)
updateConf("client-id", CStr(MySettings.OFS_KEYS_ClientID.Value).Replace("\", "/"), m3)
End If
If Rules.RulesReplaceConfig Then If Rules.RulesReplaceConfig Then
If Rules.RulesConfigManualMode Then If Rules.RulesConfigManualMode Then
updateConf(DynamicRulesEnv.DynamicRulesConfig_Mode_NodeName, "manual", False) updateConf(DynamicRulesEnv.DynamicRulesConfig_Mode_NodeName, "manual", m1)
configText = configText.Replace(DynamicRulesEnv.DynamicRulesConfigNodeName_URL, DynamicRulesEnv.DynamicRulesConfigNodeName_RULES) configText = configText.Replace(DynamicRulesEnv.DynamicRulesConfigNodeName_URL, DynamicRulesEnv.DynamicRulesConfigNodeName_RULES)
updateConf(DynamicRulesEnv.DynamicRulesConfigNodeName_RULES, Rules.CurrentContainerRulesText, True) updateConf(DynamicRulesEnv.DynamicRulesConfigNodeName_RULES, Rules.CurrentContainerRulesText, m2)
Else Else
Dim confUrlNode$ = If(Rules.RulesConstants.ContainsKey(DynamicRulesEnv.DynamicRulesConfigNodeName_URL_CONST_NAME), Dim confUrlNode$ = If(Rules.RulesConstants.ContainsKey(DynamicRulesEnv.DynamicRulesConfigNodeName_URL_CONST_NAME),
Rules.RulesConstants(DynamicRulesEnv.DynamicRulesConfigNodeName_URL_CONST_NAME), Rules.RulesConstants(DynamicRulesEnv.DynamicRulesConfigNodeName_URL_CONST_NAME),
DynamicRulesEnv.DynamicRulesConfigNodeName_URL) DynamicRulesEnv.DynamicRulesConfigNodeName_URL)
updateConf(DynamicRulesEnv.DynamicRulesConfigNodeName_URL, Rules.CurrentRule.UrlRaw, False) updateConf(DynamicRulesEnv.DynamicRulesConfigNodeName_URL, Rules.CurrentRule.UrlRaw, m1)
configText = configText.Replace(DynamicRulesEnv.DynamicRulesConfigNodeName_URL, confUrlNode) configText = configText.Replace(DynamicRulesEnv.DynamicRulesConfigNodeName_URL, confUrlNode)
If Rules.RulesConstants.ContainsKey(DynamicRulesEnv.DynamicRulesConfig_Mode_NodeName) Then _ If Rules.RulesConstants.ContainsKey(DynamicRulesEnv.DynamicRulesConfig_Mode_NodeName) Then _
updateConf(DynamicRulesEnv.DynamicRulesConfig_Mode_NodeName, Rules.RulesConstants(DynamicRulesEnv.DynamicRulesConfig_Mode_NodeName), False) updateConf(DynamicRulesEnv.DynamicRulesConfig_Mode_NodeName, Rules.RulesConstants(DynamicRulesEnv.DynamicRulesConfig_Mode_NodeName), m1)
End If End If
End If End If
f = currentCache f = currentCache

View File

@@ -7,9 +7,11 @@
' 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.Globalization Imports System.Globalization
Imports PersonalUtilities.Tools.Web.Clients
Namespace API.Pinterest Namespace API.Pinterest
Friend Module Declarations Friend Module Declarations
Friend ReadOnly DateProvider As ADateTime = GetDateProvider() Friend ReadOnly DateProvider As ADateTime = GetDateProvider()
Friend ReadOnly PwsHeader As New HttpHeader("x-pinterest-pws-handler", "www/[username]/pins.js")
Private Function GetDateProvider() As ADateTime Private Function GetDateProvider() As ADateTime
Dim n As DateTimeFormatInfo = CultureInfo.GetCultureInfo("en-us").DateTimeFormat.Clone Dim n As DateTimeFormatInfo = CultureInfo.GetCultureInfo("en-us").DateTimeFormat.Clone
n.FullDateTimePattern = "ddd dd MMM yyyy HH:mm:ss" n.FullDateTimePattern = "ddd dd MMM yyyy HH:mm:ss"

View File

@@ -186,6 +186,7 @@ Namespace API.Pinterest
Dim l As List(Of String) = GetDataFromGalleryDL(Board.URL, False, Token) Dim l As List(Of String) = GetDataFromGalleryDL(Board.URL, False, Token)
If l.ListExists Then l.RemoveAll(Function(ll) Not ll.Contains("BoardFeedResource/get/")) If l.ListExists Then l.RemoveAll(Function(ll) Not ll.Contains("BoardFeedResource/get/"))
If l.ListExists Then If l.ListExists Then
Responser.Headers.Add(PwsHeader)
ProgressPre.ChangeMax(l.Count) ProgressPre.ChangeMax(l.Count)
For Each bUrl In l For Each bUrl In l
ProgressPre.Perform() ProgressPre.Perform()
@@ -252,6 +253,8 @@ Namespace API.Pinterest
End If End If
Catch ex As Exception Catch ex As Exception
ProcessException(ex, Token, $"data (gallery-dl images) downloading error [{bUrl}]") ProcessException(ex, Token, $"data (gallery-dl images) downloading error [{bUrl}]")
Finally
Responser.Headers.Remove(PwsHeader)
End Try End Try
End Sub End Sub
#End Region #End Region

View File

@@ -17,8 +17,8 @@ Imports PersonalUtilities.Functions.RegularExpressions
Imports DownDetector = SCrawler.API.Base.DownDetector Imports DownDetector = SCrawler.API.Base.DownDetector
Imports Download = SCrawler.Plugin.ISiteSettings.Download Imports Download = SCrawler.Plugin.ISiteSettings.Download
Namespace API.Reddit Namespace API.Reddit
<Manifest(RedditSiteKey), SavedPosts, SpecialForm(False)> <Manifest(RedditSiteKey), SavedPosts, SpecialForm(False), UseDownDetector>
Friend Class SiteSettings : Inherits SiteSettingsBase Friend Class SiteSettings : Inherits SiteSettingsBase : Implements DownDetector.IDownDetector
#Region "Declarations" #Region "Declarations"
#Region "Authorization" #Region "Authorization"
<PropertyOption(ControlText:="Login", ControlToolTip:="Your authorization username", IsAuth:=True), PXML, PClonable(Clone:=False)> <PropertyOption(ControlText:="Login", ControlToolTip:="Your authorization username", IsAuth:=True), PXML, PClonable(Clone:=False)>
@@ -67,6 +67,26 @@ Namespace API.Reddit
<PropertyOption(ControlText:="Check image: get original", ControlToolTip:="Get the original image if it exists", IsAuth:=False), PXML, PClonable> <PropertyOption(ControlText:="Check image: get original", ControlToolTip:="Get the original image if it exists", IsAuth:=False), PXML, PClonable>
Friend ReadOnly Property CheckImageReturnOrig As PropertyValue Friend ReadOnly Property CheckImageReturnOrig As PropertyValue
#End Region #End Region
#Region "IDownDetector Support"
Private ReadOnly Property IDownDetector_Value As Integer Implements DownDetector.IDownDetector.Value
Get
Return 100
End Get
End Property
Private ReadOnly Property IDownDetector_AddToLog As Boolean Implements DownDetector.IDownDetector.AddToLog
Get
Return False
End Get
End Property
Private ReadOnly Property IDownDetector_CheckSite As String Implements DownDetector.IDownDetector.CheckSite
Get
Return "reddit"
End Get
End Property
Private Function IDownDetector_Available(ByVal What As Download, ByVal Silent As Boolean) As Boolean Implements DownDetector.IDownDetector.Available
Return MDD.Available(What, Silent)
End Function
#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)
@@ -97,6 +117,8 @@ Namespace API.Reddit
CheckImage = New PropertyValue(False) CheckImage = New PropertyValue(False)
CheckImageReturnOrig = New PropertyValue(True) CheckImageReturnOrig = New PropertyValue(True)
MDD = New MyDownDetector(Me)
UrlPatternUser = "https://www.reddit.com/{0}/{1}/" UrlPatternUser = "https://www.reddit.com/{0}/{1}/"
ImageVideoContains = "reddit.com" ImageVideoContains = "reddit.com"
UserRegex = RParams.DM("[htps:/]{7,8}.*?reddit.com/([user]{1,4})/([^/\?&]+)", 0, RegexReturn.ListByMatch, EDP.ReturnValue) UserRegex = RParams.DM("[htps:/]{7,8}.*?reddit.com/([user]{1,4})/([^/\?&]+)", 0, RegexReturn.ListByMatch, EDP.ReturnValue)
@@ -116,81 +138,48 @@ Namespace API.Reddit
End Function End Function
#End Region #End Region
#Region "DownloadStarted, ReadyToDownload, Available, DownloadDone, UpdateRedGifsToken" #Region "DownloadStarted, ReadyToDownload, Available, DownloadDone, UpdateRedGifsToken"
Private ____DownloadStarted As Boolean = False Private ReadOnly MDD As MyDownDetector
Friend Overrides Sub DownloadStarted(ByVal What As Download) Private Class MyDownDetector : Inherits DownDetector.Checker(Of SiteSettings)
If What = Download.Main Then ____DownloadStarted = True Private __TrueValue As Boolean = False
MyBase.DownloadStarted(What) Friend Sub New(ByRef _Source As SiteSettings)
End Sub MyBase.New(_Source)
End Sub
Protected Overrides Function AvailableImpl(ByVal What As Download, ByVal Silent As Boolean) As Boolean
__TrueValue = Source.AvailableTrueValue(What)
Return MyBase.AvailableImpl(What, Silent)
End Function
Protected Overrides Function AvailableImpl_TRUE() As Boolean
Return AvailableImpl_TrueValueReturn()
End Function
Protected Overrides Function AvailableImpl_FALSE_SILENT_NOT_MSG_YES() As Boolean
Return AvailableImpl_TrueValueReturn()
End Function
Private Function AvailableImpl_TrueValueReturn() As Boolean
If __TrueValue Then Source.UpdateRedGifsToken()
Return __TrueValue AndAlso Source.UpdateTokenIfRequired()
End Function
Friend Overrides Sub Reset()
__TrueValue = False
MyBase.Reset()
End Sub
End Class
Friend Property SessionInterrupted As Boolean = False Friend Property SessionInterrupted As Boolean = False
Friend Overrides Function ReadyToDownload(ByVal What As Download) As Boolean Friend Overrides Function ReadyToDownload(ByVal What As Download) As Boolean
If What = Download.Main Then If What = Download.Main Then
Dim result As Boolean = Not SessionInterrupted Return Not SessionInterrupted
If result Then
If ____DownloadStarted And ____AvailableRequested Then
____AvailableResult = AvailableImpl(What, ____AvailableSilent)
____AvailableChecked = True
____AvailableRequested = False
result = ____AvailableResult
ElseIf ____AvailableChecked Then
result = ____AvailableResult
End If
End If
Return result
Else Else
Return True Return True
End If End If
End Function End Function
Private ____AvailableRequested As Boolean = False
Private ____AvailableSilent As Boolean = True
Private ____AvailableChecked As Boolean = False
Private ____AvailableResult As Boolean = False
Friend Overrides Function Available(ByVal What As Download, ByVal Silent As Boolean) As Boolean Friend Overrides Function Available(ByVal What As Download, ByVal Silent As Boolean) As Boolean
If What = Download.Main And ____DownloadStarted Then Return AvailableTrueValue(What)
____AvailableRequested = True
____AvailableSilent = Silent
Return True
Else
Return AvailableImpl(What, Silent)
End If
End Function End Function
Private Function AvailableImpl(ByVal What As Download, ByVal Silent As Boolean) As Boolean Private Function AvailableTrueValue(ByVal What As Download) As Boolean
Try Return Not What = Download.SavedPosts OrElse (Responser.CookiesExists And ACheck(SavedPostsUserName.Value))
AvailableText = String.Empty
Dim trueValue As Boolean = Not What = Download.SavedPosts OrElse (Responser.CookiesExists And ACheck(SavedPostsUserName.Value))
If Not trueValue Then Return False
Dim dl As List(Of DownDetector.Data) = DownDetector.GetData("reddit")
If dl.ListExists Then
dl = dl.Take(4).ToList
Dim avg% = dl.Average(Function(d) d.Value)
If avg > 100 Then
AvailableText = "Over the past hour, Reddit has received an average of " &
avg.NumToString(New ANumbers With {.FormatOptions = ANumbers.Options.GroupIntegral}) & " outage reports:" & vbCr &
dl.ListToString(vbCr)
If Silent Then
Return False
Else
If MsgBoxE({$"{AvailableText}{vbCr}{vbCr}Do you want to continue parsing Reddit data?", "There are outage reports on Reddit"}, vbYesNo) = vbYes Then
If trueValue Then UpdateRedGifsToken()
Return trueValue AndAlso UpdateTokenIfRequired()
Else
Return False
End If
End If
End If
End If
If trueValue Then UpdateRedGifsToken()
Return trueValue AndAlso UpdateTokenIfRequired()
Catch ex As Exception
Return ErrorsDescriber.Execute(EDP.SendToLog + EDP.ReturnValue, ex, "[API.Reddit.SiteSettings.Available]", True)
End Try
End Function End Function
Friend Overrides Sub DownloadDone(ByVal What As Download) Friend Overrides Sub DownloadDone(ByVal What As Download)
SessionInterrupted = False SessionInterrupted = False
____DownloadStarted = False MDD.Reset()
____AvailableRequested = False
____AvailableChecked = False
____AvailableSilent = True
____AvailableResult = False
MyBase.DownloadDone(What) MyBase.DownloadDone(What)
End Sub End Sub
Private Sub UpdateRedGifsToken() Private Sub UpdateRedGifsToken()

View File

@@ -473,35 +473,47 @@ Namespace API.ThisVid
Dim u As UserMedia Dim u As UserMedia
Dim n$, r$ Dim n$, r$
Dim c% = 0 Dim c% = 0
Dim ii As Byte
Dim repeat As Boolean
Progress.Maximum += _TempMediaList.Count Progress.Maximum += _TempMediaList.Count
For i% = _TempMediaList.Count - 1 To 0 Step -1 For i% = _TempMediaList.Count - 1 To 0 Step -1
Progress.Perform() Progress.Perform()
u = _TempMediaList(i) u = _TempMediaList(i)
If u.Type = UserMedia.Types.VideoPre Then If u.Type = UserMedia.Types.VideoPre Then
If Not DownloadTopCount.HasValue OrElse c <= DownloadTopCount.Value Then If Not DownloadTopCount.HasValue OrElse c <= DownloadTopCount.Value Then
ThrowAny(Token) repeat = False
r = Responser.GetResponse(u.URL,, EDP.ReturnValue) For ii = 0 To 1
If Not r.IsEmptyString Then ThrowAny(Token)
n = TitleHtmlConverter(RegexReplace(r, RegExVideoTitle)) r = Responser.GetResponse(u.URL,, EDP.ReturnValue)
u.Post.ID = u.URL If Not r.IsEmptyString Then
If Not n.IsEmptyString Then n = n.Replace("ThisVid.com", String.Empty).StringTrim.StringTrimEnd("-").StringTrim n = TitleHtmlConverter(RegexReplace(r, RegExVideoTitle))
If n.IsEmptyString Then n = TitleHtmlConverter(u.URL.Replace("https://thisvid.com/videos/", String.Empty).StringTrim.StringTrimEnd("-").StringTrim) u.Post.ID = u.URL
If n.IsEmptyString Then n = "VideoFile" If Not n.IsEmptyString Then n = n.Replace("ThisVid.com", String.Empty).StringTrim.StringTrimEnd("-").StringTrim
u.File = $"{n}.mp4" If n.IsEmptyString Then n = TitleHtmlConverter(u.URL.Replace("https://thisvid.com/videos/", String.Empty).StringTrim.StringTrimEnd("-").StringTrim)
u.PictureOption = n If n.IsEmptyString Then n = "VideoFile"
u.URL = RegexReplace(r, Regex_VideosThumb_OG_IMAGE) u.File = $"{n}.mp4"
If u.URL.IsEmptyString Then u.URL = RegexReplace(r, RegExVideosThumb1) u.PictureOption = n
If u.URL.IsEmptyString Then u.URL = RegexReplace(r, RegExVideosThumb2) u.URL = RegexReplace(r, Regex_VideosThumb_OG_IMAGE)
If Not u.URL.IsEmptyString Then If u.URL.IsEmptyString And Not repeat And ii = 0 Then
u.URL = LinkFormatterSecure(u.URL) Thread.Sleep(250)
u.Type = UserMedia.Types.Video u = _TempMediaList(i)
_TempPostsList.Add(u.Post.ID) repeat = True
_TempMediaList(i) = u Continue For
c += 1 End If
Else If u.URL.IsEmptyString Then u.URL = RegexReplace(r, RegExVideosThumb1)
_TempMediaList.RemoveAt(i) If u.URL.IsEmptyString Then u.URL = RegexReplace(r, RegExVideosThumb2)
If Not u.URL.IsEmptyString Then
u.URL = LinkFormatterSecure(u.URL)
u.Type = UserMedia.Types.Video
_TempPostsList.Add(u.Post.ID)
_TempMediaList(i) = u
c += 1
Else
_TempMediaList.RemoveAt(i)
End If
End If End If
End If If Not repeat Then Exit For
Next
Else Else
_TempMediaList.RemoveAt(i) _TempMediaList.RemoveAt(i)
End If End If

View File

@@ -115,6 +115,7 @@ Namespace API.ThreadsNet
Responser.Method = "POST" Responser.Method = "POST"
UpdateResponser() UpdateResponser()
MySettings.UpdateResponserData(Responser) MySettings.UpdateResponserData(Responser)
ValidateExtension()
If Not errorFound Then LoadSavePostsKV(False) If Not errorFound Then LoadSavePostsKV(False)
End Try End Try
End If End If

View File

@@ -16,6 +16,7 @@ Namespace API.Twitter
Friend ReadOnly DateProvider As ADateTime = GetDateProvider() Friend ReadOnly DateProvider As ADateTime = GetDateProvider()
Friend ReadOnly VideoSizeRegEx As RParams = RParams.DMS("\d+x(\d+)", 1, EDP.ReturnValue) Friend ReadOnly VideoSizeRegEx As RParams = RParams.DMS("\d+x(\d+)", 1, EDP.ReturnValue)
Friend ReadOnly StatusRegEx As RParams = RParams.DM(".*?(twitter|x)\.com/\S+/status/\d+", 0, EDP.ReturnValue) Friend ReadOnly StatusRegEx As RParams = RParams.DM(".*?(twitter|x)\.com/\S+/status/\d+", 0, EDP.ReturnValue)
Friend ReadOnly BroadcastsUrls As Object() = {"entities", "urls", 0, "expanded_url"}
Private Function GetDateProvider() As ADateTime Private Function GetDateProvider() As ADateTime
Dim n As DateTimeFormatInfo = CultureInfo.GetCultureInfo("en-us").DateTimeFormat.Clone Dim n As DateTimeFormatInfo = CultureInfo.GetCultureInfo("en-us").DateTimeFormat.Clone
n.FullDateTimePattern = "ddd MMM dd HH:mm:ss +ffff yyyy" n.FullDateTimePattern = "ddd MMM dd HH:mm:ss +ffff yyyy"

View File

@@ -43,6 +43,10 @@ Namespace API.Twitter
Caption:="Download model 'Likes'", Caption:="Download model 'Likes'",
ToolTip:="Download the data using the 'https://x.com/UserName/likes' command.", LeftOffset:=DefaultOffset)> ToolTip:="Download the data using the 'https://x.com/UserName/likes' command.", LeftOffset:=DefaultOffset)>
Friend Overridable Property DownloadModelLikes As Boolean = False Friend Overridable Property DownloadModelLikes As Boolean = False
<PSetting(Address:=SettingAddress.User,
Caption:="Download 'Broadcasts'",
ToolTip:="Download broadcasts posted by the user using the 'https://x.com/i/broadcasts/abcdef1234567' URLs", LeftOffset:=DefaultOffset)>
Friend Overridable Property DownloadBroadcasts As Boolean = False
<PSetting(Address:=SettingAddress.User, <PSetting(Address:=SettingAddress.User,
Caption:="Force apply", Caption:="Force apply",
ToolTip:="Force overrides the default parameters for the first download." & vbCr & "Applies to first download only.", LeftOffset:=DefaultOffset)> ToolTip:="Force overrides the default parameters for the first download." & vbCr & "Applies to first download only.", LeftOffset:=DefaultOffset)>
@@ -75,6 +79,7 @@ Namespace API.Twitter
MediaModelAllowNonUserTweets = u.MediaModelAllowNonUserTweets MediaModelAllowNonUserTweets = u.MediaModelAllowNonUserTweets
If Not TypeOf u Is Mastodon.UserData Then If Not TypeOf u Is Mastodon.UserData Then
DownloadModelForceApply = u.DownloadModelForceApply DownloadModelForceApply = u.DownloadModelForceApply
DownloadBroadcasts = u.DownloadBroadcasts
Dim dm As DModels() = EnumExtract(Of DModels)(u.DownloadModel) Dim dm As DModels() = EnumExtract(Of DModels)(u.DownloadModel)
If dm.ListExists Then If dm.ListExists Then
DownloadModelMedia = dm.Contains(DModels.Media) DownloadModelMedia = dm.Contains(DModels.Media)

View File

@@ -162,6 +162,13 @@ Namespace API.Twitter
Return Nothing Return Nothing
End If End If
End Function End Function
Friend Overrides Function IsMyImageVideo(ByVal URL As String) As ExchangeOptions
If Not URL.IsEmptyString AndAlso (URL.Contains("twitter") Or URL.Contains("x.com")) Then
Return New ExchangeOptions(Site, String.Empty) With {.Exists = True}
Else
Return Nothing
End If
End Function
Friend Overrides Function GetUserUrl(ByVal User As IPluginContentProvider) As String Friend Overrides Function GetUserUrl(ByVal User As IPluginContentProvider) As String
Return DirectCast(User, UserData).GetUserUrl Return DirectCast(User, UserData).GetUserUrl
End Function End Function

View File

@@ -7,6 +7,7 @@
' 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
@@ -16,6 +17,7 @@ Imports PersonalUtilities.Tools.Web.Documents
Imports PersonalUtilities.Tools.Web.Documents.JSON Imports PersonalUtilities.Tools.Web.Documents.JSON
Imports UStates = SCrawler.API.Base.UserMedia.States Imports UStates = SCrawler.API.Base.UserMedia.States
Imports UTypes = SCrawler.API.Base.UserMedia.Types Imports UTypes = SCrawler.API.Base.UserMedia.Types
Imports PKV = SCrawler.API.Instagram.UserData.PostKV
Namespace API.Twitter Namespace API.Twitter
Friend Class UserData : Inherits UserDataBase Friend Class UserData : Inherits UserDataBase
#Region "XML names" #Region "XML names"
@@ -23,12 +25,15 @@ Namespace API.Twitter
Private Const Name_DownloadModel As String = "DownloadModel" Private Const Name_DownloadModel As String = "DownloadModel"
Private Const Name_DownloadModelForceApply As String = "DownloadModelForceApply" Private Const Name_DownloadModelForceApply As String = "DownloadModelForceApply"
Private Const Name_MediaModelAllowNonUserTweets As String = "MediaModelAllowNonUserTweets" Private Const Name_MediaModelAllowNonUserTweets As String = "MediaModelAllowNonUserTweets"
Private Const Name_DownloadBroadcasts As String = "DownloadBroadcasts"
Private Const Name_GifsDownload As String = "GifsDownload" Private Const Name_GifsDownload As String = "GifsDownload"
Private Const Name_GifsSpecialFolder As String = "GifsSpecialFolder" Private Const Name_GifsSpecialFolder As String = "GifsSpecialFolder"
Private Const Name_GifsPrefix As String = "GifsPrefix" Private Const Name_GifsPrefix As String = "GifsPrefix"
Private Const Name_IsCommunity As String = "IsCommunity" Private Const Name_IsCommunity As String = "IsCommunity"
Private Const Name_DownloadModelChanged As String = "DownloadModelChanged"
#End Region #End Region
#Region "Declarations" #Region "Declarations"
Private Const BroadCastPartUrl As String = "i/broadcasts"
Private Const Label_Community As String = "Community" Private Const Label_Community As String = "Community"
Private _NameTrue As String = String.Empty Private _NameTrue As String = String.Empty
Friend Property NameTrue(Optional ByVal Exact As Boolean = False) As String Friend Property NameTrue(Optional ByVal Exact As Boolean = False) As String
@@ -54,12 +59,20 @@ Namespace API.Twitter
Private FirstDownloadComplete As Boolean = False Private FirstDownloadComplete As Boolean = False
Friend Property DownloadModelForceApply As Boolean = False Friend Property DownloadModelForceApply As Boolean = False
Friend Property DownloadModel As DownloadModels = DownloadModels.Undefined Friend Property DownloadModel As DownloadModels = DownloadModels.Undefined
Private ReadOnly Property IsMultiMode As Boolean
Get
Return EnumExtract(Of DownloadModels)(DownloadModel).ListIfNothing.Count > 1
End Get
End Property
Private Property DownloadModelChanged As Boolean = False
Friend Property MediaModelAllowNonUserTweets As Boolean = False Friend Property MediaModelAllowNonUserTweets As Boolean = False
Friend Property DownloadBroadcasts As Boolean = False
Friend Property GifsDownload As Boolean = True Friend Property GifsDownload As Boolean = True
Friend Property GifsSpecialFolder As String = String.Empty Friend Property GifsSpecialFolder As String = String.Empty
Friend Property GifsPrefix As String = String.Empty Friend Property GifsPrefix As String = String.Empty
Friend Property IsCommunity As Boolean = False Friend Property IsCommunity As Boolean = False
Private ReadOnly LikesPosts As List(Of String) Private ReadOnly LikesPosts As List(Of String)
Private ReadOnly PostsKV As List(Of PKV)
Private ReadOnly _DataNames As List(Of String) Private ReadOnly _DataNames As List(Of String)
Private ReadOnly Property MySettings As SiteSettings Private ReadOnly Property MySettings As SiteSettings
Get Get
@@ -94,10 +107,13 @@ Namespace API.Twitter
DownloadModel = DownloadModels.Undefined DownloadModel = DownloadModels.Undefined
DownloadModelForceApply = .DownloadModelForceApply DownloadModelForceApply = .DownloadModelForceApply
MediaModelAllowNonUserTweets = .MediaModelAllowNonUserTweets MediaModelAllowNonUserTweets = .MediaModelAllowNonUserTweets
DownloadBroadcasts = .DownloadBroadcasts
Dim dModel As DownloadModels = DownloadModel
If .DownloadModelMedia Then DownloadModel += DownloadModels.Media If .DownloadModelMedia Then DownloadModel += DownloadModels.Media
If .DownloadModelProfile Then DownloadModel += DownloadModels.Profile If .DownloadModelProfile Or .DownloadBroadcasts Then DownloadModel += DownloadModels.Profile
If .DownloadModelSearch Then DownloadModel += DownloadModels.Search If .DownloadModelSearch Then DownloadModel += DownloadModels.Search
If .DownloadModelLikes Then DownloadModel += DownloadModels.Likes If .DownloadModelLikes Then DownloadModel += DownloadModels.Likes
If Not dModel = DownloadModel Then DownloadModelChanged = True
_NameTrue = .UserName _NameTrue = .UserName
End With End With
End If End If
@@ -107,11 +123,15 @@ Namespace API.Twitter
Friend Sub New() Friend Sub New()
_DataNames = New List(Of String) _DataNames = New List(Of String)
LikesPosts = New List(Of String) LikesPosts = New List(Of String)
PostsKV = New List(Of PKV)
UseInternalM3U8Function = True
End Sub End Sub
Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean) Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean)
With Container With Container
If Loading Then If Loading Then
DownloadBroadcasts = .Value(Name_DownloadBroadcasts).FromXML(Of Boolean)(False)
DownloadModelForceApply = .Value(Name_DownloadModelForceApply).FromXML(Of Boolean)(False) DownloadModelForceApply = .Value(Name_DownloadModelForceApply).FromXML(Of Boolean)(False)
DownloadModelChanged = .Value(Name_DownloadModelChanged).FromXML(Of Boolean)(False)
If .Contains(Name_FirstDownloadComplete) Then If .Contains(Name_FirstDownloadComplete) Then
FirstDownloadComplete = .Value(Name_FirstDownloadComplete).FromXML(Of Boolean)(False) FirstDownloadComplete = .Value(Name_FirstDownloadComplete).FromXML(Of Boolean)(False)
DownloadModel = .Value(Name_DownloadModel).FromXML(Of Integer)(DownloadModels.Undefined) DownloadModel = .Value(Name_DownloadModel).FromXML(Of Integer)(DownloadModels.Undefined)
@@ -159,7 +179,9 @@ Namespace API.Twitter
End If End If
.Add(Name_FirstDownloadComplete, FirstDownloadComplete.BoolToInteger) .Add(Name_FirstDownloadComplete, FirstDownloadComplete.BoolToInteger)
.Add(Name_DownloadModelForceApply, DownloadModelForceApply.BoolToInteger) .Add(Name_DownloadModelForceApply, DownloadModelForceApply.BoolToInteger)
.Add(Name_DownloadModelChanged, DownloadModelChanged.BoolToInteger)
.Add(Name_DownloadModel, CInt(DownloadModel)) .Add(Name_DownloadModel, CInt(DownloadModel))
.Add(Name_DownloadBroadcasts, DownloadBroadcasts.BoolToInteger)
.Add(Name_GifsDownload, GifsDownload.BoolToInteger) .Add(Name_GifsDownload, GifsDownload.BoolToInteger)
.Add(Name_GifsSpecialFolder, GifsSpecialFolder) .Add(Name_GifsSpecialFolder, GifsSpecialFolder)
.Add(Name_GifsPrefix, GifsPrefix) .Add(Name_GifsPrefix, GifsPrefix)
@@ -182,6 +204,68 @@ Namespace API.Twitter
{{"item", "itemContent", "tweet_results", "result", "tweet", "legacy"}} {{"item", "itemContent", "tweet_results", "result", "tweet", "legacy"}}
} }
End Function End Function
Private Function ExtractBroadcast(ByVal e As EContainer, Optional ByVal PostID As String = Nothing, Optional ByVal PostDate As String = Nothing,
Optional ByVal Nodes As List(Of String()) = Nothing,
Optional ByVal IgnoreNodes As Boolean = False) As UserMedia
If e.ListExists Then
Dim __nodes As List(Of String()) = If(Nodes, GetContainerSubnodes())
Dim urlValue$
Dim m As UserMedia = Nothing
Dim __parseContainer As Func(Of EContainer, Boolean) =
Function(ByVal ee As EContainer) As Boolean
With ee
If .ListExists Then
urlValue = .ItemF(BroadcastsUrls, EDP.ReturnValue).XmlIfNothingValue
If Not urlValue.IsEmptyString AndAlso urlValue.Contains(BroadCastPartUrl) Then
m = MediaFromData(urlValue, PostID, PostDate,,, UTypes.m3u8)
If Not IsSingleObjectDownload Then m.SpecialFolder = "Broadcasts*"
Return True
End If
End If
End With
Return False
End Function
If IgnoreNodes Then
If __parseContainer(e) Then Return m
Else
For Each n As String() In __nodes
If __parseContainer(e(n)) Then Return m
Next
End If
m = ExtractBroadcast(e.ItemF(Of Object)({0}), PostID, PostDate, Nodes)
If Not m.URL.IsEmptyString Then Return m
End If
Return Nothing
End Function
Private ReadOnly Property MyFilePostsKV As SFile
Get
Dim f As SFile = MyFilePosts
If Not f.IsEmptyString Then
f.Name &= "_KV"
f.Extension = "xml"
Return f
Else
Return Nothing
End If
End Get
End Property
Protected Sub LoadSavePostsKV(ByVal Load As Boolean)
Instagram.UserData.LoadSavePostsKV(Load, MyFilePostsKV, PostsKV)
End Sub
Private Function PostKVExists(ByVal PID As String, ByVal Model As DownloadModels,
ByVal MultiMode As Boolean, ByVal IgnorePKV As Boolean, ByVal AutoAdd As Boolean) As Boolean
Dim result As Boolean
If IgnorePKV Or PostsKV.Count = 0 Then
result = _TempPostsList.Contains(PID)
Else
result = PostsKV.Contains(New PKV(PID, PID, Model)) Or (Not MultiMode AndAlso _TempPostsList.Contains(PID))
End If
If Not result And AutoAdd Then
PostsKV.ListAddValue(New PKV(PID, PID, Model), LNC)
_TempPostsList.ListAddValue(PID, LNC)
End If
Return result
End Function
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken) Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
Try Try
If MySettings.LIMIT_ABORT Then If MySettings.LIMIT_ABORT Then
@@ -191,9 +275,17 @@ Namespace API.Twitter
If _ContentList.Count > 0 Then _DataNames.ListAddList(_ContentList.Select(Function(c) c.Post.ID), LAP.ClearBeforeAdd, LAP.NotContainsOnly) If _ContentList.Count > 0 Then _DataNames.ListAddList(_ContentList.Select(Function(c) c.Post.ID), LAP.ClearBeforeAdd, LAP.NotContainsOnly)
DownloadData_SavedPosts(Token) DownloadData_SavedPosts(Token)
Else Else
LoadSavePostsKV(True)
If PostsKV.Count = 0 And (_ContentList.Count > 0 Or _TempPostsList.Count > 0) Then
Dim m As DownloadModels = IIf(IsMultiMode, DownloadModels.Media, DownloadModel)
PostsKV.ListAddList(_TempPostsList.Select(Function(p) New PKV(p, p, m)), LNC)
PostsKV.ListAddList(_ContentList.Select(Function(p) New PKV(p.Post.ID, p.Post.ID, m)), LNC)
_ForceSaveUserData = True
End If
LikesPosts.Clear() LikesPosts.Clear()
If _ContentList.Count > 0 Then _DataNames.ListAddList(_ContentList.Select(Function(c) c.File.File), LAP.ClearBeforeAdd, LAP.NotContainsOnly) If _ContentList.Count > 0 Then _DataNames.ListAddList(_ContentList.Select(Function(c) c.File.File), LAP.ClearBeforeAdd, LAP.NotContainsOnly)
DownloadData_Timeline(Token) DownloadData_Timeline(Token)
LoadSavePostsKV(False)
If LikesPosts.Count > 0 Then If LikesPosts.Count > 0 Then
_ReparseLikes = True _ReparseLikes = True
ReparseMissing(Token) ReparseMissing(Token)
@@ -228,6 +320,8 @@ Namespace API.Twitter
Dim indxChanged As Boolean = False Dim indxChanged As Boolean = False
Dim isOneNode As Boolean, isPins As Boolean, ExistsDetected As Boolean, userInfoParsed As Boolean = False Dim isOneNode As Boolean, isPins As Boolean, ExistsDetected As Boolean, userInfoParsed As Boolean = False
Dim j As EContainer, rootNode As EContainer, optionalNode As EContainer, workingNode As EContainer, tmpNode As EContainer, nn As EContainer = Nothing Dim j As EContainer, rootNode As EContainer, optionalNode As EContainer, workingNode As EContainer, tmpNode As EContainer, nn As EContainer = Nothing
Dim multiMode As Boolean = IsMultiMode
Dim currentModel As DownloadModels = DownloadModels.Undefined
Dim __parseContainer As Func(Of EContainer, Boolean) = Dim __parseContainer As Func(Of EContainer, Boolean) =
Function(ByVal ee As EContainer) As Boolean Function(ByVal ee As EContainer) As Boolean
@@ -250,14 +344,13 @@ Namespace API.Twitter
Case DateResult.Skip, DateResult.Exit : Return False Case DateResult.Skip, DateResult.Exit : Return False
End Select End Select
If Not _TempPostsList.Contains(PostID) Then If Not PostKVExists(PostID, currentModel, multiMode, False, True) Then
_TempPostsList.Add(PostID)
ElseIf dirIndx = 3 Then ElseIf dirIndx = 3 Then
ElseIf isPins Then ElseIf isPins Then
Return False Return False
Else Else
ExistsDetected = True ExistsDetected = Not multiMode
Return False Return multiMode
End If End If
tmpUserId = nn({"retweeted_status_result", "result", "legacy", "user_id_str"}).XmlIfNothingValue tmpUserId = nn({"retweeted_status_result", "result", "legacy", "user_id_str"}).XmlIfNothingValue
@@ -268,12 +361,21 @@ Namespace API.Twitter
If (Not ParseUserMediaOnly Or dirIndx = 3) OrElse If (Not ParseUserMediaOnly Or dirIndx = 3) OrElse
(dirIndx = 0 AndAlso MediaModelAllowNonUserTweets) OrElse (dirIndx = 0 AndAlso MediaModelAllowNonUserTweets) OrElse
(Not ID.IsEmptyString AndAlso tmpUserId = ID) Then (Not ID.IsEmptyString AndAlso tmpUserId = ID) Then
If dirIndx = 1 And DownloadBroadcasts Then
Dim m As UserMedia = ExtractBroadcast(nn, PostID, PostDate, nodes)
If Not m.URL.IsEmptyString Then
_TempMediaList.ListAddValue(m, LNC)
Else
m = ExtractBroadcast(ee, PostID, PostDate, nodes)
If Not m.URL.IsEmptyString Then _TempMediaList.ListAddValue(m, LNC)
End If
End If
If dirIndx = 3 Then If dirIndx = 3 Then
Dim lUrl$ = nn.ItemF({"content", "itemContent", "tweet_results", "result", "legacy", "entities", "media", 0}, "expanded_url").XmlIfNothingValue Dim lUrl$ = nn.ItemF({"content", "itemContent", "tweet_results", "result", "legacy", "entities", "media", 0}, "expanded_url").XmlIfNothingValue
If Not lUrl.IsEmptyString Then If Not lUrl.IsEmptyString Then
lUrl = RegexReplace(lUrl, StatusRegEx) lUrl = RegexReplace(lUrl, StatusRegEx)
If Not lUrl.IsEmptyString Then If Not lUrl.IsEmptyString Then
If Not _TempPostsList.Contains(lUrl) Then _TempPostsList.Add(lUrl) Else Return False If PostKVExists(lUrl, currentModel, multiMode, False, True) Then Return multiMode
LikesPosts.ListAddValue(lUrl, LNC) LikesPosts.ListAddValue(lUrl, LNC)
End If End If
End If End If
@@ -287,11 +389,23 @@ Namespace API.Twitter
tCache = CreateCache() tCache = CreateCache()
'0 - media
'1 - profile
'2 - search
'3 - likes
Dim dirs As List(Of SFile) = GetTimelineFromGalleryDL(tCache, Token) Dim dirs As List(Of SFile) = GetTimelineFromGalleryDL(tCache, Token)
If dirs.ListExists Then If dirs.ListExists Then
For Each dir As SFile In dirs For Each dir As SFile In dirs
dirIndx += 1 dirIndx += 1
Select Case dirIndx
Case 0 : currentModel = DownloadModels.Media
Case 1 : currentModel = DownloadModels.Profile
Case 2 : currentModel = DownloadModels.Search
Case 3 : currentModel = DownloadModels.Likes
Case Else : currentModel = DownloadModels.Undefined
End Select
If dirIndx = 3 Then likesDetected = True If dirIndx = 3 Then likesDetected = True
ExistsDetected = False ExistsDetected = False
@@ -431,7 +545,7 @@ Namespace API.Twitter
ProgressPre.ChangeMax(If(isOneNode, 1, .Count)) ProgressPre.ChangeMax(If(isOneNode, 1, .Count))
If isOneNode Then If isOneNode Then
ProgressPre.Perform() ProgressPre.Perform()
If Not __parseContainer(.Self) Then Exit For If Not __parseContainer(.Self) Then Continue For 'Exit For
Else Else
For nodeIndx = 0 To 1 For nodeIndx = 0 To 1
If nodeIndx = 0 Then If nodeIndx = 0 Then
@@ -446,14 +560,19 @@ Namespace API.Twitter
.ItemF(newTwitterNodes), .ItemF(newTwitterNodes),
.Self) .Self)
ProgressPre.Perform() ProgressPre.Perform()
If Not __parseContainer(tmpNode) Then Exit For If Not __parseContainer(tmpNode) Then
If isPins Then GoTo nextpIndx
Exit For
End If
Next Next
End With End With
End If End If
nextNodeIndx:
Next Next
End If End If
End With End With
End If End If
nextpIndx:
Next Next
If ExistsDetected And i = 1 Then Exit For Else ExistsDetected = False If ExistsDetected And i = 1 Then Exit For Else ExistsDetected = False
@@ -473,6 +592,7 @@ Namespace API.Twitter
If DownloadModel = DownloadModels.Undefined Then If DownloadModel = DownloadModels.Undefined Then
If ParseUserMediaOnly Then If ParseUserMediaOnly Then
DownloadModel = DownloadModels.Media DownloadModel = DownloadModels.Media
If DownloadBroadcasts Then DownloadModel += DownloadModels.Profile
Else Else
DownloadModel = DownloadModels.Media + DownloadModels.Profile + DownloadModels.Search DownloadModel = DownloadModels.Media + DownloadModels.Profile + DownloadModels.Search
End If End If
@@ -731,7 +851,9 @@ Namespace API.Twitter
Dim conf As SFile = GdlCreateConf(confCache.RootDirectory) Dim conf As SFile = GdlCreateConf(confCache.RootDirectory)
If DownloadModel = DownloadModels.Undefined And Not FirstDownloadComplete And DownloadModelForceApply Then If DownloadModel = DownloadModels.Undefined And Not FirstDownloadComplete And DownloadModelForceApply Then
If ParseUserMediaOnly Then If ParseUserMediaOnly And DownloadBroadcasts Then
DownloadModel = DownloadModels.Media + DownloadModels.Profile
ElseIf ParseUserMediaOnly Then
DownloadModel = DownloadModels.Media DownloadModel = DownloadModels.Media
Else Else
DownloadModel = DownloadModels.Media + DownloadModels.Profile + DownloadModels.Search DownloadModel = DownloadModels.Media + DownloadModels.Profile + DownloadModels.Search
@@ -742,11 +864,15 @@ Namespace API.Twitter
Dim rootDir As CacheKeeper = Cache.NewInstance Dim rootDir As CacheKeeper = Cache.NewInstance
Dim dir As SFile Dim dir As SFile
Dim dm As List(Of DownloadModels) = EnumExtract(Of DownloadModels)(DownloadModel).ListIfNothing Dim dm As List(Of DownloadModels) = EnumExtract(Of DownloadModels)(DownloadModel).ListIfNothing
Dim process As Boolean Dim process As Boolean, multiMode As Boolean
Dim currentModel As DownloadModels
Dim urlPrePattern$ = $"https://x.com{IIf(IsCommunity, SiteSettings.CommunitiesUser, String.Empty)}/" Dim urlPrePattern$ = $"https://x.com{IIf(IsCommunity, SiteSettings.CommunitiesUser, String.Empty)}/"
If DownloadBroadcasts AndAlso Not dm.Contains(DownloadModels.Profile) Then dm.Add(DownloadModels.Profile)
multiMode = dm.Count > 1
Using tgdl As New TwitterGDL(Nothing, Token, MySettings.AbortOnLimit.Value) With { Using tgdl As New TwitterGDL(Nothing, Token, MySettings.AbortOnLimit.Value) With {
.TempPostsList = _TempPostsList,
.AutoClear = True, .AutoClear = True,
.AutoReset = True, .AutoReset = True,
.CommandPermanent = $"chcp {BatchExecutor.UnicodeEncoding}", .CommandPermanent = $"chcp {BatchExecutor.UnicodeEncoding}",
@@ -760,12 +886,16 @@ Namespace API.Twitter
outList.Add(dir) outList.Add(dir)
tgdl.ChangeDirectory(dir) tgdl.ChangeDirectory(dir)
command = $"""{Settings.GalleryDLFile}"" --verbose --no-download --no-skip --config ""{conf}"" --write-pages " command = $"""{Settings.GalleryDLFile}"" --verbose --no-download --no-skip --config ""{conf}"" --write-pages "
command &= GdlGetIdFilterString() If multiMode Then
command &= "{0}"
Else
command &= GdlGetIdFilterString()
End If
Select Case i Select Case i
Case 0 : command &= $"{urlPrePattern}{NameTrue}/media" : process = dm.Contains(DownloadModels.Media) Or IsCommunity Case 0 : command &= $"{urlPrePattern}{NameTrue}/media" : currentModel = DownloadModels.Media : process = dm.Contains(currentModel) Or IsCommunity
Case 1 : command &= $"{urlPrePattern}{NameTrue}" : process = dm.Contains(DownloadModels.Profile) Case 1 : command &= $"{urlPrePattern}{NameTrue}" : currentModel = DownloadModels.Profile : process = dm.Contains(currentModel)
Case 2 : command &= $"-o search-endpoint=graphql https://x.com/search?q=from:{NameTrue}+include:nativeretweets" : process = dm.Contains(DownloadModels.Search) And Not IsCommunity Case 2 : command &= $"-o search-endpoint=graphql https://x.com/search?q=from:{NameTrue}+include:nativeretweets" : currentModel = DownloadModels.Search : process = dm.Contains(currentModel) And Not IsCommunity
Case 3 : command &= $"{urlPrePattern}{NameTrue}/likes" : process = dm.Contains(DownloadModels.Likes) Case 3 : command &= $"{urlPrePattern}{NameTrue}/likes" : currentModel = DownloadModels.Likes : process = dm.Contains(currentModel)
Case Else : process = False Case Else : process = False
End Select End Select
'#If DEBUG Then '#If DEBUG Then
@@ -773,6 +903,16 @@ Namespace API.Twitter
'#End If '#End If
ThrowAny(Token) ThrowAny(Token)
If process Then If process Then
If multiMode Then
If PostsKV.Count = 0 Then
tgdl.TempPostsList = New List(Of String)
Else
tgdl.TempPostsList = (From p As PKV In PostsKV Where p.Section = currentModel Select p.ID).ListIfNothing
End If
command = String.Format(command, GdlGetIdFilterString(tgdl.TempPostsList))
Else
tgdl.TempPostsList = _TempPostsList
End If
tgdl.Execute(command) tgdl.Execute(command)
If tgdl.LimitReached Then If tgdl.LimitReached Then
If CBool(MySettings.DownloadAlreadyParsed.Value) And If CBool(MySettings.DownloadAlreadyParsed.Value) And
@@ -798,8 +938,9 @@ Namespace API.Twitter
Return Nothing Return Nothing
End Try End Try
End Function End Function
Private Function GdlGetIdFilterString() As String Private Function GdlGetIdFilterString(Optional ByVal TL As List(Of String) = Nothing) As String
Return If(_TempPostsList.Count > 0, $"--filter ""int(tweet_id) > {_TempPostsList.Last} or abort()"" ", String.Empty) If TL.ListExists Then TL.Sort()
With If(TL, _TempPostsList) : Return If(.Count > 0, $"--filter ""int(tweet_id) > { .Last} or abort()"" ", String.Empty) : End With
End Function End Function
Private Function GdlCreateConf(ByVal Path As SFile) As SFile Private Function GdlCreateConf(ByVal Path As SFile) As SFile
Try Try
@@ -824,7 +965,7 @@ Namespace API.Twitter
Dim cache As CacheKeeper = Nothing Dim cache As CacheKeeper = Nothing
Try Try
If ContentMissingExists Or (_ReparseLikes And LikesPosts.Count > 0) Then If ContentMissingExists Or (_ReparseLikes And LikesPosts.Count > 0) Then
Dim m As UserMedia Dim m As UserMedia, mTmp As UserMedia
Dim PostDate$ Dim PostDate$
Dim nodes As List(Of String()) = GetContainerSubnodes() Dim nodes As List(Of String()) = GetContainerSubnodes()
Dim node$() Dim node$()
@@ -844,7 +985,11 @@ Namespace API.Twitter
m = If(_ReparseLikes, Nothing, _ContentList(i)) m = If(_ReparseLikes, Nothing, _ContentList(i))
If Not m.Post.ID.IsEmptyString Or (IsSingleObjectDownload And Not m.URL_BASE.IsEmptyString) Or _ReparseLikes Then If Not m.Post.ID.IsEmptyString Or (IsSingleObjectDownload And Not m.URL_BASE.IsEmptyString) Or _ReparseLikes Then
ThrowAny(Token) ThrowAny(Token)
If IsSingleObjectDownload Then If m.Type = UTypes.m3u8 Then
_TempMediaList.Add(m)
rList.ListAddValue(i, LNC)
Continue For
ElseIf IsSingleObjectDownload Then
URL = m.URL_BASE URL = m.URL_BASE
ElseIf _ReparseLikes Then ElseIf _ReparseLikes Then
URL = LikesPosts(i) URL = LikesPosts(i)
@@ -861,6 +1006,13 @@ Namespace API.Twitter
If Not j Is Nothing Then If Not j Is Nothing Then
With j.ItemF({"data", 0, "instructions", 0, "entries"}) With j.ItemF({"data", 0, "instructions", 0, "entries"})
If .ListExists Then If .ListExists Then
If IsSingleObjectDownload Or DownloadBroadcasts Then
mTmp = ExtractBroadcast(.Self, m.Post.ID, String.Empty, nodes)
If Not mTmp.URL.IsEmptyString Then
_TempMediaList.ListAddValue(mTmp, LNC)
rList.ListAddValue(i, LNC)
End If
End If
For Each n In .Self For Each n In .Self
For Each node In nodes For Each node In nodes
With n(node) With n(node)
@@ -898,7 +1050,9 @@ Namespace API.Twitter
#End Region #End Region
#Region "DownloadSingleObject" #Region "DownloadSingleObject"
Protected Overrides Sub DownloadSingleObject_GetPosts(ByVal Data As IYouTubeMediaContainer, ByVal Token As CancellationToken) Protected Overrides Sub DownloadSingleObject_GetPosts(ByVal Data As IYouTubeMediaContainer, ByVal Token As CancellationToken)
_ContentList.Add(New UserMedia(Data.URL) With {.State = UStates.Missing}) Dim um As New UserMedia(Data.URL) With {.State = UStates.Missing}
If Not Data.URL.IsEmptyString AndAlso Data.URL.Contains(BroadCastPartUrl) Then um.Type = UTypes.m3u8
_ContentList.Add(um)
ReparseMissing(Token) ReparseMissing(Token)
End Sub End Sub
#End Region #End Region
@@ -955,6 +1109,13 @@ Namespace API.Twitter
End Try End Try
End Function End Function
#End Region #End Region
#Region "Clear"
Protected Overrides Sub EraseData_AdditionalDataFiles()
MyFilePostsKV.Delete(SFO.File, SFODelete.DeleteToRecycleBin, EDP.SendToLog + EDP.ReturnValue)
_DataNames.Clear()
MyBase.EraseData_AdditionalDataFiles()
End Sub
#End Region
#Region "Create media" #Region "Create media"
Private Function MediaFromData(ByVal _URL As String, ByVal PostID As String, ByVal PostDate As String, Private Function MediaFromData(ByVal _URL As String, ByVal PostID As String, ByVal PostDate As String,
Optional ByVal _PictureOption As String = Nothing, Optional ByVal _PictureOption As String = Nothing,
@@ -977,6 +1138,31 @@ Namespace API.Twitter
Protected Overrides Sub DownloadContent(ByVal Token As CancellationToken) Protected Overrides Sub DownloadContent(ByVal Token As CancellationToken)
DownloadContentDefault(Token) DownloadContentDefault(Token)
End Sub End Sub
Protected Overrides Function DownloadM3U8(ByVal URL As String, ByVal Media As UserMedia, ByVal DestinationFile As SFile, ByVal Token As CancellationToken) As SFile
Const ytDest$ = "[download] destination"
Dim f As SFile = Nothing
If MySettings.CookiesNetscapeFile.Exists And Settings.YtdlpFile.Exists And (Not URL.IsEmptyString AndAlso URL.Contains(BroadCastPartUrl)) Then
Dim destPath$ = DestinationFile.PathWithSeparator.Replace("\", "\\")
Dim rr As RParams = RParams.DM($"{destPath}.+mp4", 0, RegexOptions.IgnoreCase, EDP.ReturnValue)
Dim cmd$ = $"""{Settings.YtdlpFile.File}"" --no-cookies-from-browser --cookies ""{MySettings.CookiesNetscapeFile}"" "
cmd &= $"{URL} -P ""{destPath}"" --no-mtime"
Using ytdlp As New YTDLP.YTDLPBatch(Token)
With ytdlp
.Execute(cmd)
If .OutputData.Count > 0 Then
For Each outStr$ In .OutputData
If Not outStr.IsEmptyString AndAlso outStr.ToLower.Trim.StartsWith(ytDest) Then
f = CStr(RegexReplace(outStr, rr))
If Not f.Exists Then f = Nothing
Exit For
End If
Next
End If
End With
End Using
End If
Return f
End Function
#End Region #End Region
#Region "Exception" #Region "Exception"
Protected Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False, Protected Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False,
@@ -986,7 +1172,7 @@ Namespace API.Twitter
#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 _DataNames.Clear() : LikesPosts.Clear() If Not disposedValue And disposing Then _DataNames.Clear() : LikesPosts.Clear() : PostsKV.Clear()
MyBase.Dispose(disposing) MyBase.Dispose(disposing)
End Sub End Sub
#End Region #End Region

View File

@@ -93,7 +93,11 @@ Namespace API.Xhamster
Dim position% = InStr(URL, sk) Dim position% = InStr(URL, sk)
If position > 0 Then appender = URL.Remove(position - 1) If position > 0 Then appender = URL.Remove(position - 1)
End If End If
URL = M3U8Base.CreateUrl(appender, file) If file.StartsWith("//") Then
URL = LinkFormatterSecure(file.TrimStart("/"))
Else
URL = M3U8Base.CreateUrl(appender, file)
End If
Dim l As List(Of M3U8URL) = ParseSecondM3U8(URL, Responser, appender) Dim l As List(Of M3U8URL) = ParseSecondM3U8(URL, Responser, appender)
If l.ListExists Then Return l If l.ListExists Then Return l
End If End If

View File

@@ -10,36 +10,52 @@ Imports SCrawler.Plugin
Imports SCrawler.Plugin.Attributes Imports SCrawler.Plugin.Attributes
Imports SCrawler.API.Base Imports SCrawler.API.Base
Imports SCrawler.API.YouTube.Base Imports SCrawler.API.YouTube.Base
Imports DN = SCrawler.API.Base.DeclaredNames
Namespace API.YouTube Namespace API.YouTube
<Manifest(YouTubeSiteKey), SpecialForm(True), SpecialForm(False), SeparatedTasks(1)> <Manifest(YouTubeSiteKey), SpecialForm(True), SpecialForm(False), SeparatedTasks(1)>
Friend Class SiteSettings : Inherits SiteSettingsBase Friend Class SiteSettings : Inherits SiteSettingsBase
#Region "Categories"
Private Const CAT_COMMUNITY As String = "Communities"
#End Region
#Region "Declarations" #Region "Declarations"
<PXML, PropertyOption(ControlText:="Download user videos"), PClonable> <PXML, PropertyOption(ControlText:="Use cookies", ControlToolTip:="Default value for new users." & vbCr & "Use cookies when downloading data.", IsAuth:=True), PClonable>
Friend ReadOnly Property DownloadVideos As PropertyValue
<PXML, PropertyOption(ControlText:="Download user shorts"), PClonable>
Friend ReadOnly Property DownloadShorts As PropertyValue
<PXML, PropertyOption(ControlText:="Download user playlists"), PClonable>
Friend ReadOnly Property DownloadPlaylists As PropertyValue
<PXML, PropertyOption(ControlText:="Download user community: images"), PClonable>
Friend ReadOnly Property DownloadCommunityImages As PropertyValue
<PXML, PropertyOption(ControlText:="Download user community: videos"), PClonable>
Friend ReadOnly Property DownloadCommunityVideos As PropertyValue
<PXML, PropertyOption(ControlText:="Ignore community errors", ControlToolTip:="If true, community errors will not be added to the log."), PClonable>
Friend ReadOnly Property IgnoreCommunityErrors As PropertyValue
<PXML, PropertyOption(ControlText:="Use cookies", ControlToolTip:="Default value for new users." & vbCr & "Use cookies when downloading data."), PClonable>
Friend ReadOnly Property UseCookies As PropertyValue Friend ReadOnly Property UseCookies As PropertyValue
#Region "New user defaults"
<PXML, PropertyOption(ControlText:="Download user videos", Category:=DN.CAT_UserDefs), PClonable>
Friend ReadOnly Property DownloadVideos As PropertyValue
<PXML, PropertyOption(ControlText:="Download user shorts", Category:=DN.CAT_UserDefs), PClonable>
Friend ReadOnly Property DownloadShorts As PropertyValue
<PXML, PropertyOption(ControlText:="Download user playlists", Category:=DN.CAT_UserDefs), PClonable>
Friend ReadOnly Property DownloadPlaylists As PropertyValue
<PXML, PropertyOption(ControlText:="Download user community: images", Category:=DN.CAT_UserDefs), PClonable>
Friend ReadOnly Property DownloadCommunityImages As PropertyValue
<PXML, PropertyOption(ControlText:="Download user community: videos", Category:=DN.CAT_UserDefs), PClonable>
Friend ReadOnly Property DownloadCommunityVideos As PropertyValue
#End Region
#Region "Communities"
<PXML, PropertyOption(ControlText:="YouTube API host",
ControlToolTip:="YouTube API instance host (YouTube-operational-API). Example: 'localhost/YouTube-operational-API', 'http://localhost/YouTube-operational-API'.",
Category:=CAT_COMMUNITY), PClonable>
Friend ReadOnly Property CommunityHost As PropertyValue
<PXML, PropertyOption(ControlText:="YouTube API key", ControlToolTip:="YouTube Data API v3 developer key", Category:=CAT_COMMUNITY), PClonable>
Friend ReadOnly Property YouTubeAPIKey As PropertyValue
<PXML, PropertyOption(ControlText:="Ignore community errors", ControlToolTip:="If true, community errors will not be added to the log.", Category:=CAT_COMMUNITY), PClonable>
Friend ReadOnly Property IgnoreCommunityErrors As PropertyValue
#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)
MyBase.New(YouTubeSite, "youtube.com", AccName, Temp, My.Resources.SiteYouTube.YouTubeIcon_32, My.Resources.SiteYouTube.YouTubePic_96) MyBase.New(YouTubeSite, "youtube.com", AccName, Temp, My.Resources.SiteYouTube.YouTubeIcon_32, My.Resources.SiteYouTube.YouTubePic_96)
Responser.Cookies.ChangedAllowInternalDrop = False Responser.Cookies.ChangedAllowInternalDrop = False
UseCookies = New PropertyValue(False)
DownloadVideos = New PropertyValue(True) DownloadVideos = New PropertyValue(True)
DownloadShorts = New PropertyValue(False) DownloadShorts = New PropertyValue(False)
DownloadPlaylists = New PropertyValue(False) DownloadPlaylists = New PropertyValue(False)
DownloadCommunityImages = New PropertyValue(False) DownloadCommunityImages = New PropertyValue(False)
DownloadCommunityVideos = New PropertyValue(False) DownloadCommunityVideos = New PropertyValue(False)
CommunityHost = New PropertyValue(String.Empty, GetType(String))
YouTubeAPIKey = New PropertyValue(String.Empty, GetType(String))
IgnoreCommunityErrors = New PropertyValue(False) IgnoreCommunityErrors = New PropertyValue(False)
UseCookies = New PropertyValue(False)
_SubscriptionsAllowed = True _SubscriptionsAllowed = True
UseNetscapeCookies = True UseNetscapeCookies = True
End Sub End Sub

View File

@@ -33,6 +33,11 @@ Namespace API.YouTube
Private Const Name_LastDownloadDatePlaylist As String = "YTLastDownloadDatePlaylist" Private Const Name_LastDownloadDatePlaylist As String = "YTLastDownloadDatePlaylist"
#End Region #End Region
#Region "Declarations" #Region "Declarations"
Private ReadOnly Property MySettings As SiteSettings
Get
Return HOST.Source
End Get
End Property
Friend Property DownloadYTVideos As Boolean = True Friend Property DownloadYTVideos As Boolean = True
Friend Property DownloadYTShorts As Boolean = False Friend Property DownloadYTShorts As Boolean = False
Friend Property DownloadYTPlaylists As Boolean = False Friend Property DownloadYTPlaylists As Boolean = False
@@ -263,7 +268,17 @@ Namespace API.YouTube
If ChannelID.IsEmptyString Then GetChannelID() If ChannelID.IsEmptyString Then GetChannelID()
If ChannelID.IsEmptyString Then Throw New ArgumentNullException("ChannelID", "Channel ID cannot be null") If ChannelID.IsEmptyString Then Throw New ArgumentNullException("ChannelID", "Channel ID cannot be null")
URL = $"https://yt.lemnoslife.com/channels?part=community&id={ChannelID}" URL = MySettings.CommunityHost.Value
If URL.IsEmptyString Then
If Not CBool(MySettings.IgnoreCommunityErrors.Value) Then _
MyMainLOG = $"{ToStringForLog()}: YouTube API instance host is not specified for downloading communities"
Exit Sub
Else
URL = LinkFormatterSecure(URL.Trim, "http").TrimEnd("/")
End If
URL = $"{URL}/channels?part=community&id={ChannelID}"
If Not CStr(MySettings.YouTubeAPIKey.Value).IsEmptyString Then URL &= $"&key={CStr(MySettings.YouTubeAPIKey.Value).Trim}"
If Not Cursor.IsEmptyString Then URL &= $"&pageToken={Cursor}" If Not Cursor.IsEmptyString Then URL &= $"&pageToken={Cursor}"
ProgressPre.ChangeMax(1) ProgressPre.ChangeMax(1)

View File

@@ -357,6 +357,9 @@ Namespace Editors
MyDefs.InvokeLoaderError(ex) MyDefs.InvokeLoaderError(ex)
End Try End Try
End Sub End Sub
Private Sub SiteEditorForm_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown
If e.Control And e.KeyCode = Keys.Enter Then MyDefs_ButtonOkClick(sender, New KeyHandleEventArgs With {.KeyEventArgs = e})
End Sub
Private Sub SiteEditorForm_Disposed(sender As Object, e As EventArgs) Handles Me.Disposed Private Sub SiteEditorForm_Disposed(sender As Object, e As EventArgs) Handles Me.Disposed
If Host.PropList.Count > 0 Then Host.PropList.ForEach(Sub(p) p.DisposeControl()) If Host.PropList.Count > 0 Then Host.PropList.ForEach(Sub(p) p.DisposeControl())
If Not SpecialButton Is Nothing Then SpecialButton.Dispose() If Not SpecialButton Is Nothing Then SpecialButton.Dispose()
@@ -366,7 +369,11 @@ Namespace Editors
If Not Cookies Is Nothing Then Cookies.Dispose() If Not Cookies Is Nothing Then Cookies.Dispose()
End Sub End Sub
Private Sub MyDefs_ButtonOkClick(ByVal Sender As Object, ByVal e As KeyHandleEventArgs) Handles MyDefs.ButtonOkClick Private Sub MyDefs_ButtonOkClick(ByVal Sender As Object, ByVal e As KeyHandleEventArgs) Handles MyDefs.ButtonOkClick
If MyDefs.MyFieldsChecker.AllParamsOK Then Dim ctrl As Boolean = Not e Is Nothing AndAlso (If(e.KeyEventArgs?.Control, False) OrElse e.Key.Control)
If ctrl OrElse MyDefs.MyFieldsChecker.AllParamsOK Then
If (Not MyDefs.MyFieldsCheckerE.AllParamsOK(EDP.ReturnValue) And ctrl) AndAlso
MsgBoxE({$"Some required fields are not filled in!{vbCr}{vbCr}{MyDefs.MyFieldsChecker.ComparisonInformation}
{vbCr}{vbCr}Are you sure you want to process?", "Required fields are missing"}, vbCritical,,, {"Process", "Cancel"}) = 1 Then Exit Sub
Dim i%, ii% Dim i%, ii%
With Host With Host
Dim indxList As New List(Of Integer) Dim indxList As New List(Of Integer)
@@ -376,6 +383,7 @@ Namespace Editors
If indxList.Count > 0 Then If indxList.Count > 0 Then
Dim pList As New List(Of PropertyData) Dim pList As New List(Of PropertyData)
Dim n$() Dim n$()
Dim errorsDetected As Boolean = False
For i = 0 To indxList.Count - 1 For i = 0 To indxList.Count - 1
n = .PropList(indxList(i)).PropertiesChecking n = .PropList(indxList(i)).PropertiesChecking
For ii = 0 To .PropList.Count - 1 For ii = 0 To .PropList.Count - 1
@@ -383,8 +391,13 @@ Namespace Editors
If n.Contains(.Name) Then pList.Add(New PropertyData(.Name, .GetControlValue)) If n.Contains(.Name) Then pList.Add(New PropertyData(.Name, .GetControlValue))
End With End With
Next Next
If pList.Count > 0 AndAlso Not CBool(.PropList(indxList(i)).PropertiesCheckingMethod.Invoke(.Source, {pList})) Then Exit Sub If pList.Count > 0 AndAlso Not CBool(.PropList(indxList(i)).PropertiesCheckingMethod.Invoke(.Source, {pList})) Then
If ctrl Then errorsDetected = True Else Exit Sub
End If
Next Next
If (ctrl And errorsDetected) AndAlso MsgBoxE({$"Some settings may be incorrect. Do you still want to save?",
"Incorrect settings detected"},
vbCritical,,, {"Process", "Cancel"}) = 1 Then Exit Sub
End If End If
If TXT_PATH.Text.IsEmptyString Then TXT_PATH.Text = .PathGenerate.CSFilePS If TXT_PATH.Text.IsEmptyString Then TXT_PATH.Text = .PathGenerate.CSFilePS

View File

@@ -13,7 +13,7 @@ Imports System.Runtime.InteropServices
<Assembly: AssemblyDescription("Social networks media downloader")> <Assembly: AssemblyDescription("Social networks media downloader")>
<Assembly: AssemblyCompany("AndyProgram")> <Assembly: AssemblyCompany("AndyProgram")>
<Assembly: AssemblyProduct("SCrawler")> <Assembly: AssemblyProduct("SCrawler")>
<Assembly: AssemblyCopyright("Copyright © 2024")> <Assembly: AssemblyCopyright("Copyright © 2025")>
<Assembly: AssemblyTrademark("AndyProgram")> <Assembly: AssemblyTrademark("AndyProgram")>
<Assembly: ComVisible(False)> <Assembly: ComVisible(False)>
@@ -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("2024.11.21.0")> <Assembly: AssemblyVersion("2025.1.12.0")>
<Assembly: AssemblyFileVersion("2024.11.21.0")> <Assembly: AssemblyFileVersion("2025.1.12.0")>
<Assembly: NeutralResourcesLanguage("en")> <Assembly: NeutralResourcesLanguage("en")>

View File

@@ -60,4 +60,7 @@ Namespace Plugin.Attributes
PropertyName = _PropertyName PropertyName = _PropertyName
End Sub End Sub
End Class End Class
<AttributeUsage(AttributeTargets.Class, AllowMultiple:=False)>
Public Class UseDownDetectorAttribute : Inherits Attribute
End Class
End Namespace End Namespace

View File

@@ -184,6 +184,16 @@ Namespace Plugin.Hosts
End Sub End Sub
Friend ReadOnly Property IsSeparatedTasks As Boolean = False Friend ReadOnly Property IsSeparatedTasks As Boolean = False
Friend ReadOnly Property IsSavedPostsCompatible As Boolean = False Friend ReadOnly Property IsSavedPostsCompatible As Boolean = False
Friend ReadOnly Property IsDownDetectorCompatible As Boolean = False
Friend ReadOnly Property DownDetectorValue As Integer
Get
If IsDownDetectorCompatible Then
Return DirectCast(Source, DownDetector.IDownDetector).Value
Else
Return -1
End If
End Get
End Property
Private ReadOnly _TaskCountDefined As Integer? = Nothing Private ReadOnly _TaskCountDefined As Integer? = Nothing
Friend ReadOnly Property TaskCount As Integer Friend ReadOnly Property TaskCount As Integer
Get Get
@@ -296,6 +306,8 @@ Namespace Plugin.Hosts
End With End With
ElseIf TypeOf a Is ReplaceInternalPluginAttribute Then ElseIf TypeOf a Is ReplaceInternalPluginAttribute Then
Replacer = a Replacer = a
ElseIf TypeOf a Is UseDownDetectorAttribute Then
IsDownDetectorCompatible = True
End If End If
Next Next
End If End If
@@ -521,18 +533,34 @@ Namespace Plugin.Hosts
Friend Function GetUserPostUrl(ByVal User As IPluginContentProvider, ByVal Media As IUserMedia) As String Friend Function GetUserPostUrl(ByVal User As IPluginContentProvider, ByVal Media As IUserMedia) As String
Return Source.GetUserPostUrl(User, Media) Return Source.GetUserPostUrl(User, Media)
End Function End Function
Private _AvailableValue As Boolean = True Friend Property AvailableValue As Boolean = True
Private _AvailableAsked As Boolean = False Friend Property AvailableAsked As Boolean = False
Friend Property AvailableDownDetectorAsked As Boolean = False
Private _ActiveTaskCount As Integer = 0 Private _ActiveTaskCount As Integer = 0
Friend Property AvailableText As String = String.Empty Friend Property AvailableText As String = String.Empty
Friend Function AvailableDownDetector(ByVal What As Download, ByVal Silent As Boolean) As Boolean
If Not AvailableDownDetectorAsked Then
AvailableDownDetectorAsked = True
If IsDownDetectorCompatible Then
AvailableValue = DirectCast(Source, DownDetector.IDownDetector).Available(What, Silent)
If Not AvailableValue Then AvailableText = Source.AvailableText : AvailableAsked = True
Return AvailableValue
Else
Return True
End If
Else
Return AvailableValue
End If
End Function
Friend Function Available(ByVal What As Download, ByVal Silent As Boolean) As Boolean Friend Function Available(ByVal What As Download, ByVal Silent As Boolean) As Boolean
If DownloadSiteData Then If DownloadSiteData Then
If Not _AvailableAsked Then If Not AvailableDownDetectorAsked AndAlso Not AvailableDownDetector(What, Silent) Then Return AvailableValue
_AvailableValue = Source.Available(What, Silent) If Not AvailableAsked Then
AvailableValue = Source.Available(What, Silent)
AvailableText = Source.AvailableText AvailableText = Source.AvailableText
_AvailableAsked = True AvailableAsked = True
End If End If
Return _AvailableValue Return AvailableValue
Else Else
AvailableText = $"Downloading data for the site {Name} - {AccountName.IfNullOrEmpty(NameAccountNameDefault)} has been disabled by you." AvailableText = $"Downloading data for the site {Name} - {AccountName.IfNullOrEmpty(NameAccountNameDefault)} has been disabled by you."
If Not Silent Then MsgBoxE({AvailableText, $"{Name} downloading disabled"}, vbExclamation) If Not Silent Then MsgBoxE({AvailableText, $"{Name} downloading disabled"}, vbExclamation)
@@ -551,7 +579,7 @@ Namespace Plugin.Hosts
End Sub End Sub
Friend Sub DownloadDone(ByVal What As Download) Friend Sub DownloadDone(ByVal What As Download)
_ActiveTaskCount -= 1 _ActiveTaskCount -= 1
If _ActiveTaskCount = 0 Then _AvailableAsked = False : AvailableText = String.Empty If _ActiveTaskCount = 0 Then AvailableAsked = False : AvailableDownDetectorAsked = False : AvailableText = String.Empty
Source.DownloadDone(What) Source.DownloadDone(What)
End Sub End Sub
Private Function ConvertUser(ByVal User As IUserData) As Object Private Function ConvertUser(ByVal User As IUserData) As Object

View File

@@ -469,37 +469,59 @@ Namespace Plugin.Hosts
Return False Return False
End If End If
Else Else
Dim a As Boolean = False, n As Boolean = False Dim a As Boolean = False, n As Boolean = False, aDown As Boolean = True
Dim t$ = String.Empty Dim t$ = String.Empty
Dim tExists As Boolean = False Dim tExists As Boolean = False
Dim singleHost As Boolean = hnExists AndAlso HostNames.Count = 1 Dim singleHost As Boolean = hnExists AndAlso HostNames.Count = 1
Dim m As New MMessage("", "Some of the hosts are unavailable",, vbExclamation) Dim m As New MMessage("", "Some of the hosts are unavailable",, vbExclamation)
For i% = 0 To Count - 1 If [Default].IsDownDetectorCompatible Then
If Not hnExists OrElse HostNames.Contains(Hosts(i).AccountName) Then Dim sh As SettingsHost = Nothing
If Hosts(i).Available(What, True) Then Dim defdvalue% = [Default].DownDetectorValue
a = True If hnExists AndAlso Not Hosts.All(Function(h) h.DownDetectorValue = defdvalue) Then _
Else sh = Hosts.Find(Function(h) h.AccountName = HostNames(0))
n = True If sh Is Nothing Then sh = [Default]
If Not Hosts(i).AvailableText.IsEmptyString Then aDown = sh.AvailableDownDetector(What, Silent)
t &= vbCr Hosts.ForEach(Sub(ByVal h As SettingsHost)
t.StringAppendLine($"{Name} - {Hosts(i).AccountName.IfNullOrEmpty(SettingsHost.NameAccountNameDefault)}:") h.AvailableDownDetectorAsked = True
t.StringAppendLine(Hosts(i).AvailableText) If Not aDown And Not Silent Then h.AvailableValue = False : h.AvailableAsked = True
tExists = True End Sub)
End If
If aDown Then
For i% = 0 To Count - 1
If Not hnExists OrElse HostNames.Contains(Hosts(i).AccountName) Then
If Hosts(i).Available(What, True) Then
a = True
Else Else
t.StringAppendLine($"{Name} - {Hosts(i).AccountName.IfNullOrEmpty(SettingsHost.NameAccountNameDefault)}") n = True
If Not Hosts(i).AvailableText.IsEmptyString Then
t &= vbCr
t.StringAppendLine($"{Name} - {Hosts(i).AccountName.IfNullOrEmpty(SettingsHost.NameAccountNameDefault)}:")
t.StringAppendLine(Hosts(i).AvailableText)
tExists = True
Else
t.StringAppendLine($"{Name} - {Hosts(i).AccountName.IfNullOrEmpty(SettingsHost.NameAccountNameDefault)}")
End If
If FillIndexes Then HostsUnavailableIndexes.Add(i)
End If End If
If FillIndexes Then HostsUnavailableIndexes.Add(i)
End If End If
End If Next
Next Else
If Not Silent Then Silent = True
a = False
n = True
If Not [Default].AvailableText.IsEmptyString Then t = [Default].AvailableText : tExists = Not t.IsEmptyString
End If
t = t.StringTrim t = t.StringTrim
If singleHost Then If singleHost Then
m.Text = "The host is unavailable." m.Text = "The host is unavailable."
Else Else
m.Text = "Some of the hosts are unavailable." m.Text = "Some of the hosts are unavailable."
End If End If
If HostNamesPassed And Not hnExists Then Silent = True If HostNamesPassed And Not hnExists And aDown Then Silent = True
If a And Not n Then If Not aDown And Not Silent Then
Return False
ElseIf a And Not n Then
Return True Return True
ElseIf Not a And n Then ElseIf Not a And n Then
If Not Silent And tExists Then m.Text &= $"{vbCr}{vbCr}{t}" : m.Show() If Not Silent And tExists Then m.Text &= $"{vbCr}{vbCr}{t}" : m.Show()

View File

@@ -366,6 +366,7 @@ Friend Class SettingsCLS : Implements IDownloaderSettings, IDisposable
ReparseMissingInTheRoutine = New XMLValue(Of Boolean)("ReparseMissingInTheRoutine", False, MyXML, n) ReparseMissingInTheRoutine = New XMLValue(Of Boolean)("ReparseMissingInTheRoutine", False, MyXML, n)
UseDefaultAccountIfMissing = New XMLValue(Of Boolean)("UseDefaultAccountIfMissing", True, MyXML, n) UseDefaultAccountIfMissing = New XMLValue(Of Boolean)("UseDefaultAccountIfMissing", True, MyXML, n)
AutomationBrushUndownloadedPlansMinutes = New XMLValue(Of Integer)("AutomationBrushUndownloadedPlansMinutes", 10080, MyXML, n) AutomationBrushUndownloadedPlansMinutes = New XMLValue(Of Integer)("AutomationBrushUndownloadedPlansMinutes", 10080, MyXML, n)
DownDetectorEnabled = New XMLValue(Of Boolean)("DownDetectorEnabled", True, MyXML, n)
'Downloading: file naming 'Downloading: file naming
n = {"Downloading", "FileName"} n = {"Downloading", "FileName"}
@@ -1064,6 +1065,7 @@ Friend Class SettingsCLS : Implements IDownloaderSettings, IDisposable
Friend ReadOnly Property ReparseMissingInTheRoutine As XMLValue(Of Boolean) Friend ReadOnly Property ReparseMissingInTheRoutine As XMLValue(Of Boolean)
Friend ReadOnly Property UseDefaultAccountIfMissing As XMLValue(Of Boolean) Friend ReadOnly Property UseDefaultAccountIfMissing As XMLValue(Of Boolean)
Friend ReadOnly Property AutomationBrushUndownloadedPlansMinutes As XMLValue(Of Integer) Friend ReadOnly Property AutomationBrushUndownloadedPlansMinutes As XMLValue(Of Integer)
Friend ReadOnly Property DownDetectorEnabled As XMLValue(Of Boolean)
#End Region #End Region
#Region "Downloading: file naming" #Region "Downloading: file naming"
Friend ReadOnly Property FileAddDateToFileName As XMLValue(Of Boolean) Friend ReadOnly Property FileAddDateToFileName As XMLValue(Of Boolean)