mirror of
https://github.com/AAndyProgram/SCrawler.git
synced 2026-03-15 00:02:17 +00:00
Compare commits
7 Commits
2023.8.27.
...
2023.9.20.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a446df1f66 | ||
|
|
0026e905a4 | ||
|
|
f8116fd048 | ||
|
|
8d33fdc8f3 | ||
|
|
dab94acc32 | ||
|
|
c61c817585 | ||
|
|
3ea59a6acd |
16
Changelog.md
16
Changelog.md
@@ -1,3 +1,19 @@
|
||||
# 2023.9.20.0
|
||||
|
||||
*2023-09-20*
|
||||
|
||||
- Added
|
||||
- **Instagram: user active (non-pinned) stories (Issue #17)**
|
||||
- Reddit: reduce the number of token updates (refresh the token if there are Reddit users in the download queue)
|
||||
- YouTube (standalone app): priority download protocol *(`Settings` - `Defaults` - `Protocol`)* (you can now select the default protocol you want to download media on: `Any`, `https`, `m3u8`))
|
||||
- Automation: ability to change schedulers (`Download` - `Automation` - `Script icon`)
|
||||
- Collections: update colors for the added user
|
||||
- Fixed
|
||||
- YouTube: can't detect `shorts` links
|
||||
- Incorrect MD5 validation initial value
|
||||
- Instagram: handle error 500
|
||||
- Collections: update labels only for the added user
|
||||
|
||||
# 2023.8.27.0
|
||||
|
||||
*2023-08-27*
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 36 KiB |
@@ -1,5 +1,5 @@
|
||||
# :rainbow_flag: Happy LGBT Pride Month :tada:
|
||||
|
||||
<!-- # :rainbow_flag: Happy LGBT Pride Month :tada:
|
||||
-->
|
||||
# :rainbow_flag: Social networks crawler :rainbow_flag:
|
||||
|
||||
[](https://github.com/AAndyProgram/SCrawler/releases/latest)
|
||||
@@ -189,6 +189,10 @@ F5-->[*]
|
||||
|
||||
# Contact me
|
||||
|
||||
Discord server: https://discord.gg/uFNUXvFFmg
|
||||
|
||||
[e-mail](mailto:andyprogram@proton.me): andyprogram@proton.me
|
||||
<!--
|
||||
[e-mail](mailto:andyprogram@proton.me): andyprogram@proton.me
|
||||
|
||||
Matrix (Element): https://matrix.to/#/@andyprogram:matrix.org
|
||||
@@ -198,3 +202,4 @@ Discord (contact the developer): andyprogram
|
||||
Discord server: https://discord.gg/uFNUXvFFmg
|
||||
|
||||
[Wire](https://account.wire.com/user-profile/?id=93985052-cf2c-4b72-ac75-bbe3231cf544): @andyprogram
|
||||
-->
|
||||
@@ -6,6 +6,10 @@
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports System.Drawing.Design
|
||||
Imports System.ComponentModel
|
||||
Imports PersonalUtilities.Tools.Grid.Attributes
|
||||
Imports PersonalUtilities.Tools.Grid.EnumObjects
|
||||
Namespace API.YouTube.Base
|
||||
Public Structure Thumbnail : Implements IIndexable, IComparable(Of Thumbnail)
|
||||
Public ID As String
|
||||
@@ -47,6 +51,14 @@ Namespace API.YouTube.Base
|
||||
Channel = 2
|
||||
PlayList = 3
|
||||
End Enum
|
||||
<Editor(GetType(EnumDropDownEditor), GetType(UITypeEditor))>
|
||||
Public Enum Protocols As Integer
|
||||
<EnumValue(ExcludeFromList:=True)>
|
||||
Undefined = -1
|
||||
Any = 0
|
||||
https = 1
|
||||
m3u8 = 2
|
||||
End Enum
|
||||
Public Structure MediaObject : Implements IIndexable, IComparable(Of MediaObject)
|
||||
Public Type As Plugin.UserMediaTypes
|
||||
Public ID As String
|
||||
@@ -59,6 +71,17 @@ Namespace API.YouTube.Base
|
||||
Public Size As Double
|
||||
Public Codec As String
|
||||
Public Protocol As String
|
||||
Public ReadOnly Property ProtocolType As Protocols
|
||||
Get
|
||||
If Not Protocol.IsEmptyString Then
|
||||
Select Case Protocol.StringToLower.StringTrim
|
||||
Case "http", "https" : Return Protocols.https
|
||||
Case "m3u8" : Return Protocols.m3u8
|
||||
End Select
|
||||
End If
|
||||
Return Protocols.Undefined
|
||||
End Get
|
||||
End Property
|
||||
Public URL As String
|
||||
Public Property Index As Integer Implements IIndexable.Index
|
||||
Private Function SetIndex(ByVal Obj As Object, ByVal Index As Integer) As Object Implements IIndexable.SetIndex
|
||||
|
||||
@@ -23,17 +23,19 @@ Namespace API.YouTube.Base
|
||||
Public Shared Function IsMyUrl(ByVal URL As String) As Boolean
|
||||
Return Not Info_GetUrlType(URL) = YouTubeMediaType.Undefined
|
||||
End Function
|
||||
Public Shared Function Info_GetUrlType(ByVal URL As String, Optional ByRef IsMusic As Boolean = False,
|
||||
Public Shared Function Info_GetUrlType(ByVal URL As String, Optional ByRef IsMusic As Boolean = False, Optional ByRef IsShorts As Boolean = False,
|
||||
Optional ByRef IsChannelUser As Boolean = False, Optional ByRef Id As String = Nothing) As YouTubeMediaType
|
||||
If Not URL.IsEmptyString Then
|
||||
IsMusic = URL.Contains("music.youtube.com")
|
||||
IsChannelUser = False
|
||||
IsShorts = False
|
||||
Dim data As List(Of String) = RegexReplace(URL, RParams.DMS(UrlTypePattern, 0, RegexReturn.ListByMatch, EDP.ReturnValue))
|
||||
If data.ListExists Then
|
||||
If data.Count >= 6 Then Id = data(5)
|
||||
If data.Count >= 3 And Not data(2).IsEmptyString Then
|
||||
Select Case data(2).ToLower
|
||||
Case "watch" : Return YouTubeMediaType.Single
|
||||
Case "shorts" : IsShorts = True : Return YouTubeMediaType.Single
|
||||
Case "playlist" : Return YouTubeMediaType.PlayList
|
||||
Case UserChannelOption, "@" : IsChannelUser = data(2).ToLower = UserChannelOption : Return YouTubeMediaType.Channel
|
||||
End Select
|
||||
@@ -64,8 +66,8 @@ Namespace API.YouTube.Base
|
||||
Dim urlOrig$ = URL
|
||||
URL = RegexReplace(URL, TrueUrlRegEx)
|
||||
If URL.IsEmptyString Then Throw New ArgumentNullException("URL", $"Can't get true URL from [{urlOrig}]")
|
||||
Dim isMusic As Boolean = False
|
||||
Dim objType As YouTubeMediaType = Info_GetUrlType(URL, isMusic)
|
||||
Dim isMusic As Boolean = False, isShorts As Boolean = False
|
||||
Dim objType As YouTubeMediaType = Info_GetUrlType(URL, isMusic, isShorts)
|
||||
If Not objType = YouTubeMediaType.Undefined Then
|
||||
Dim __GetDefault As Boolean = If(GetDefault, True)
|
||||
Dim __GetShorts As Boolean = If(GetShorts, True)
|
||||
@@ -105,7 +107,7 @@ Namespace API.YouTube.Base
|
||||
|
||||
If result Then
|
||||
container.Parse(Nothing, _CachePathDefault, isMusic, Token, Progress)
|
||||
If Not container.HasError Then container.URL = URL : Return container
|
||||
If Not container.HasError Then container.URL = URL : container.IsShorts = isShorts : Return container
|
||||
End If
|
||||
container.Dispose()
|
||||
End If
|
||||
|
||||
@@ -139,6 +139,9 @@ Namespace API.YouTube.Base
|
||||
<Browsable(True), GridVisible, XMLVN({"Defaults"}), Category("Defaults"), DisplayName("Use cookies"),
|
||||
Description("By default, use cookies when downloading from YouTube.")>
|
||||
Public ReadOnly Property DefaultUseCookies As XMLValue(Of Boolean)
|
||||
<Browsable(True), GridVisible, XMLVN({"Defaults"}, Protocols.Any), Category("Defaults"), DisplayName("Protocol"),
|
||||
Description("Priority download protocol. Default: 'Any'")>
|
||||
Public ReadOnly Property DefaultProtocol As XMLValue(Of Protocols)
|
||||
<Browsable(True), GridVisible(False), XMLVN({"Defaults"}), Category("Defaults"),
|
||||
DisplayName("Auto remove"), Description("Automatically remove downloaded items from the list.")>
|
||||
Public ReadOnly Property RemoveDownloadedAutomatically As XMLValue(Of Boolean)
|
||||
|
||||
@@ -32,6 +32,6 @@ Imports System.Runtime.InteropServices
|
||||
' by using the '*' as shown below:
|
||||
' <Assembly: AssemblyVersion("1.0.*")>
|
||||
|
||||
<Assembly: AssemblyVersion("2023.8.27.0")>
|
||||
<Assembly: AssemblyFileVersion("2023.8.27.0")>
|
||||
<Assembly: AssemblyVersion("2023.9.20.0")>
|
||||
<Assembly: AssemblyFileVersion("2023.9.20.0")>
|
||||
<Assembly: NeutralResourcesLanguage("en")>
|
||||
|
||||
@@ -25,6 +25,7 @@ Namespace API.YouTube.Objects
|
||||
ReadOnly Property MediaType As UMTypes
|
||||
ReadOnly Property MediaState As UMStates
|
||||
Property IsMusic As Boolean
|
||||
Property IsShorts As Boolean
|
||||
Property ID As String
|
||||
Property Description As String
|
||||
Property PlaylistID As String
|
||||
|
||||
@@ -112,7 +112,7 @@ Namespace API.YouTube.Objects
|
||||
End Set
|
||||
End Property
|
||||
<XMLEC(Name_IsMusic)> Public Property IsMusic As Boolean = False Implements IYouTubeMediaContainer.IsMusic
|
||||
<XMLEC> Public Property IsShorts As Boolean = False
|
||||
<XMLEC> Public Property IsShorts As Boolean = False Implements IYouTubeMediaContainer.IsShorts
|
||||
<XMLEC> Public Property ID As String Implements IYouTubeMediaContainer.ID, IUserMedia.PostID
|
||||
<XMLEC> Public Property Title As String Implements IDownloadableMedia.Title
|
||||
<XMLEC> Public Property Description As String Implements IYouTubeMediaContainer.Description
|
||||
@@ -1309,9 +1309,29 @@ Namespace API.YouTube.Objects
|
||||
Next
|
||||
End If
|
||||
End Sub
|
||||
Dim protocolCleaner As Action =
|
||||
Sub()
|
||||
If Not MyYouTubeSettings.DefaultProtocol.Value = Protocols.Undefined And
|
||||
Not MyYouTubeSettings.DefaultProtocol.Value = Protocols.Any Then
|
||||
Dim data As New List(Of MediaObject)(MediaObjects.Where(Function(mo) mo.ProtocolType = MyYouTubeSettings.DefaultProtocol.Value))
|
||||
If data.ListExists Then
|
||||
Dim dRem As Protocols = IIf(MyYouTubeSettings.DefaultProtocol.Value = Protocols.https, Protocols.m3u8, Protocols.https)
|
||||
Dim d As MediaObject
|
||||
Dim dr As New FPredicate(Of MediaObject)(Function(mo) mo.Height = d.Height And mo.ProtocolType = dRem)
|
||||
For Each d In data
|
||||
If MediaObjects.Count = 0 Then
|
||||
Exit For
|
||||
ElseIf MediaObjects.LongCount(dr) > 0 Then
|
||||
MediaObjects.RemoveAll(dr)
|
||||
End If
|
||||
Next
|
||||
End If
|
||||
End If
|
||||
End Sub
|
||||
If MediaObjects.Count > 0 And Not MyYouTubeSettings.DefaultVideoIncludeNullSize Then MediaObjects.RemoveAll(Function(mo) mo.Size <= 0)
|
||||
If MediaObjects.Count > 0 Then DupRemover.Invoke(UMTypes.Audio)
|
||||
If MediaObjects.Count > 0 Then DupRemover.Invoke(UMTypes.Video)
|
||||
If MediaObjects.Count > 0 Then protocolCleaner.Invoke
|
||||
If MediaObjects.Count > 0 Then
|
||||
MediaObjects.Sort()
|
||||
SelectedAudioIndex = MediaObjects.FindIndex(Function(mo) mo.Type = UMTypes.Audio)
|
||||
|
||||
@@ -32,6 +32,6 @@ Imports System.Runtime.InteropServices
|
||||
' by using the '*' as shown below:
|
||||
' <Assembly: AssemblyVersion("1.0.*")>
|
||||
|
||||
<Assembly: AssemblyVersion("2023.8.27.0")>
|
||||
<Assembly: AssemblyFileVersion("2023.8.27.0")>
|
||||
<Assembly: AssemblyVersion("2023.9.20.0")>
|
||||
<Assembly: AssemblyFileVersion("2023.9.20.0")>
|
||||
<Assembly: NeutralResourcesLanguage("en")>
|
||||
|
||||
@@ -1371,7 +1371,7 @@ BlockNullPicture:
|
||||
#Region "MD5 support"
|
||||
Protected Const VALIDATE_MD5_ERROR As String = "VALIDATE_MD5_ERROR"
|
||||
Friend Property UseMD5Comparison As Boolean = False
|
||||
Protected Property StartMD5Checked As Boolean = True
|
||||
Protected Property StartMD5Checked As Boolean = False
|
||||
Friend Property RemoveExistingDuplicates As Boolean = False
|
||||
Protected Overridable Sub ValidateMD5(ByVal Token As CancellationToken)
|
||||
Try
|
||||
|
||||
@@ -11,14 +11,17 @@ Namespace API.Instagram
|
||||
Friend Class EditorExchangeOptions
|
||||
<PSetting(Caption:="Get timeline", ToolTip:="Download user timeline")>
|
||||
Friend Property GetTimeline As Boolean
|
||||
<PSetting(Caption:="Get stories", ToolTip:="Download user stories")>
|
||||
<PSetting(Caption:="Get stories", ToolTip:="Download user stories (pinned)")>
|
||||
Friend Property GetStories As Boolean
|
||||
<PSetting(Caption:="Get stories: user", ToolTip:="Download user stories")>
|
||||
Friend Property GetStoriesUser As Boolean
|
||||
<PSetting(Caption:="Get tagged posts", ToolTip:="Download user tagged posts")>
|
||||
Friend Property GetTagged As Boolean
|
||||
Friend Sub New(ByVal u As UserData)
|
||||
With u
|
||||
GetTimeline = .GetTimeline
|
||||
GetStories = .GetStories
|
||||
GetStoriesUser = .GetStoriesUser
|
||||
GetTagged = .GetTaggedData
|
||||
End With
|
||||
End Sub
|
||||
@@ -26,6 +29,7 @@ Namespace API.Instagram
|
||||
With s
|
||||
GetTimeline = CBool(.GetTimeline.Value)
|
||||
GetStories = CBool(.GetStories.Value)
|
||||
GetStoriesUser = CBool(.GetStoriesUser.Value)
|
||||
GetTagged = CBool(.GetTagged.Value)
|
||||
End With
|
||||
End Sub
|
||||
|
||||
@@ -139,11 +139,13 @@ Namespace API.Instagram
|
||||
Friend ReadOnly Property GetTimeline As PropertyValue
|
||||
<PropertyOption(ControlText:="Get stories", ControlToolTip:="Default value for new users"), PXML, ControlNumber(24)>
|
||||
Friend ReadOnly Property GetStories As PropertyValue
|
||||
<PropertyOption(ControlText:="Get tagged photos", ControlToolTip:="Default value for new users"), PXML, ControlNumber(25)>
|
||||
<PropertyOption(ControlText:="Get stories: user", ControlToolTip:="Default value for new users"), PXML, ControlNumber(25)>
|
||||
Friend ReadOnly Property GetStoriesUser As PropertyValue
|
||||
<PropertyOption(ControlText:="Get tagged photos", ControlToolTip:="Default value for new users"), PXML, ControlNumber(26)>
|
||||
Friend ReadOnly Property GetTagged As PropertyValue
|
||||
<PropertyOption(ControlText:="Tagged notify limit",
|
||||
ControlToolTip:="If the number of tagged posts exceeds this number you will be notified." & vbCr &
|
||||
"-1 to disable"), PXML, ControlNumber(26)>
|
||||
"-1 to disable"), PXML, ControlNumber(27)>
|
||||
Friend ReadOnly Property TaggedNotifyLimit As PropertyValue
|
||||
<Provider(NameOf(TaggedNotifyLimit), FieldsChecker:=True)>
|
||||
Private ReadOnly Property TaggedNotifyLimitProvider As IFormatProvider
|
||||
@@ -153,7 +155,9 @@ Namespace API.Instagram
|
||||
Friend ReadOnly Property DownloadTimeline As PropertyValue
|
||||
<PropertyOption(ControlText:="Download stories", ControlToolTip:="Download stories"), PXML, ControlNumber(11)>
|
||||
Friend ReadOnly Property DownloadStories As PropertyValue
|
||||
<PropertyOption(ControlText:="Download tagged", ControlToolTip:="Download tagged posts"), PXML, ControlNumber(12)>
|
||||
<PropertyOption(ControlText:="Download stories: user", ControlToolTip:="Download stories (user)"), PXML, ControlNumber(12)>
|
||||
Friend ReadOnly Property DownloadStoriesUser As PropertyValue
|
||||
<PropertyOption(ControlText:="Download tagged", ControlToolTip:="Download tagged posts"), PXML, ControlNumber(13)>
|
||||
Friend ReadOnly Property DownloadTagged As PropertyValue
|
||||
#End Region
|
||||
#Region "429 bypass"
|
||||
@@ -259,6 +263,7 @@ Namespace API.Instagram
|
||||
|
||||
DownloadTimeline = New PropertyValue(True)
|
||||
DownloadStories = New PropertyValue(True)
|
||||
DownloadStoriesUser = New PropertyValue(True)
|
||||
DownloadTagged = New PropertyValue(False)
|
||||
|
||||
RequestsWaitTimer = New PropertyValue(1000)
|
||||
@@ -270,6 +275,7 @@ Namespace API.Instagram
|
||||
|
||||
GetTimeline = New PropertyValue(True)
|
||||
GetStories = New PropertyValue(False)
|
||||
GetStoriesUser = New PropertyValue(False)
|
||||
GetTagged = New PropertyValue(False)
|
||||
TaggedNotifyLimit = New PropertyValue(200)
|
||||
TaggedNotifyLimitProvider = New TaggedNotifyLimitChecker
|
||||
|
||||
@@ -24,6 +24,7 @@ Namespace API.Instagram
|
||||
Private Const Name_FirstLoadingDone As String = "FirstLoadingDone"
|
||||
Private Const Name_GetTimeline As String = "GetTimeline"
|
||||
Private Const Name_GetStories As String = "GetStories"
|
||||
Private Const Name_GetStoriesUser As String = "GetStoriesUser"
|
||||
Private Const Name_GetTagged As String = "GetTaggedData"
|
||||
Private Const Name_TaggedChecked As String = "TaggedChecked"
|
||||
Private Const Name_NameTrue As String = "NameTrue"
|
||||
@@ -75,6 +76,7 @@ Namespace API.Instagram
|
||||
Private FirstLoadingDone As Boolean = False
|
||||
Friend Property GetTimeline As Boolean = True
|
||||
Friend Property GetStories As Boolean
|
||||
Friend Property GetStoriesUser As Boolean
|
||||
Friend Property GetTaggedData As Boolean
|
||||
Private _NameTrue As String = String.Empty
|
||||
Private ReadOnly Property NameTrue As String
|
||||
@@ -84,6 +86,31 @@ Namespace API.Instagram
|
||||
End Property
|
||||
Private UserNameRequested As Boolean = False
|
||||
#End Region
|
||||
#Region "Loader"
|
||||
Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean)
|
||||
With Container
|
||||
If Loading Then
|
||||
LastCursor = .Value(Name_LastCursor)
|
||||
FirstLoadingDone = .Value(Name_FirstLoadingDone).FromXML(Of Boolean)(False)
|
||||
GetTimeline = .Value(Name_GetTimeline).FromXML(Of Boolean)(CBool(MySiteSettings.GetTimeline.Value))
|
||||
GetStories = .Value(Name_GetStories).FromXML(Of Boolean)(CBool(MySiteSettings.GetStories.Value))
|
||||
GetStoriesUser = .Value(Name_GetStoriesUser).FromXML(Of Boolean)(MySiteSettings.GetStoriesUser.Value)
|
||||
GetTaggedData = .Value(Name_GetTagged).FromXML(Of Boolean)(CBool(MySiteSettings.GetTagged.Value))
|
||||
TaggedChecked = .Value(Name_TaggedChecked).FromXML(Of Boolean)(False)
|
||||
_NameTrue = .Value(Name_NameTrue)
|
||||
Else
|
||||
.Add(Name_LastCursor, LastCursor)
|
||||
.Add(Name_FirstLoadingDone, FirstLoadingDone.BoolToInteger)
|
||||
.Add(Name_GetTimeline, GetTimeline.BoolToInteger)
|
||||
.Add(Name_GetStories, GetStories.BoolToInteger)
|
||||
.Add(Name_GetStoriesUser, GetStoriesUser.BoolToInteger)
|
||||
.Add(Name_GetTagged, GetTaggedData.BoolToInteger)
|
||||
.Add(Name_TaggedChecked, TaggedChecked.BoolToInteger)
|
||||
.Add(Name_NameTrue, _NameTrue)
|
||||
End If
|
||||
End With
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Exchange options"
|
||||
Friend Overrides Function ExchangeOptionsGet() As Object
|
||||
Return New EditorExchangeOptions(Me)
|
||||
@@ -93,37 +120,17 @@ Namespace API.Instagram
|
||||
With DirectCast(Obj, EditorExchangeOptions)
|
||||
GetTimeline = .GetTimeline
|
||||
GetStories = .GetStories
|
||||
GetStoriesUser = .GetStoriesUser
|
||||
GetTaggedData = .GetTagged
|
||||
End With
|
||||
End If
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Initializer, loader"
|
||||
#Region "Initializer"
|
||||
Friend Sub New()
|
||||
PostsKVIDs = New List(Of PostKV)
|
||||
PostsToReparse = New List(Of PostKV)
|
||||
End Sub
|
||||
Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean)
|
||||
With Container
|
||||
If Loading Then
|
||||
LastCursor = .Value(Name_LastCursor)
|
||||
FirstLoadingDone = .Value(Name_FirstLoadingDone).FromXML(Of Boolean)(False)
|
||||
GetTimeline = .Value(Name_GetTimeline).FromXML(Of Boolean)(CBool(MySiteSettings.GetTimeline.Value))
|
||||
GetStories = .Value(Name_GetStories).FromXML(Of Boolean)(CBool(MySiteSettings.GetStories.Value))
|
||||
GetTaggedData = .Value(Name_GetTagged).FromXML(Of Boolean)(CBool(MySiteSettings.GetTagged.Value))
|
||||
TaggedChecked = .Value(Name_TaggedChecked).FromXML(Of Boolean)(False)
|
||||
_NameTrue = .Value(Name_NameTrue)
|
||||
Else
|
||||
.Add(Name_LastCursor, LastCursor)
|
||||
.Add(Name_FirstLoadingDone, FirstLoadingDone.BoolToInteger)
|
||||
.Add(Name_GetTimeline, GetTimeline.BoolToInteger)
|
||||
.Add(Name_GetStories, GetStories.BoolToInteger)
|
||||
.Add(Name_GetTagged, GetTaggedData.BoolToInteger)
|
||||
.Add(Name_TaggedChecked, TaggedChecked.BoolToInteger)
|
||||
.Add(Name_NameTrue, _NameTrue)
|
||||
End If
|
||||
End With
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Download data"
|
||||
Private E560Thrown As Boolean = False
|
||||
@@ -234,6 +241,7 @@ Namespace API.Instagram
|
||||
If FirstLoadingDone Then LastCursor = String.Empty
|
||||
If Not IsSavedPosts AndAlso MySiteSettings.BaseAuthExists() Then
|
||||
If CBool(MySiteSettings.DownloadStories.Value) And GetStories Then s = Sections.Stories : DownloadData(String.Empty, s, Token) : ProgressPre.Done()
|
||||
If CBool(MySiteSettings.DownloadStoriesUser.Value) And GetStoriesUser Then s = Sections.UserStories : DownloadData(String.Empty, s, Token) : ProgressPre.Done()
|
||||
If CBool(MySiteSettings.DownloadTagged.Value) And ACheck(MySiteSettings.HashTagged.Value) And GetTaggedData Then s = Sections.Tagged : DownloadData(String.Empty, s, Token) : ProgressPre.Done()
|
||||
End If
|
||||
If WaitNotificationMode = WNM.SkipTemp Or WaitNotificationMode = WNM.SkipCurrent Then WaitNotificationMode = WNM.Notify
|
||||
@@ -275,7 +283,7 @@ Namespace API.Instagram
|
||||
Private Sub Responser_ResponseReceived(ByVal Sender As Object, ByVal e As EventArguments.WebDataResponse)
|
||||
Declarations.UpdateResponser(e, Responser)
|
||||
End Sub
|
||||
Private Enum Sections : Timeline : Tagged : Stories : SavedPosts : End Enum
|
||||
Private Enum Sections : Timeline : Tagged : Stories : UserStories : SavedPosts : End Enum
|
||||
Private Const StoriesFolder As String = "Stories"
|
||||
Private Const TaggedFolder As String = "Tagged"
|
||||
#Region "429 bypass"
|
||||
@@ -455,7 +463,7 @@ Namespace API.Instagram
|
||||
ThrowAny(Token)
|
||||
End If
|
||||
If StoriesList.ListExists Then
|
||||
GetStoriesData(StoriesList, Token)
|
||||
GetStoriesData(StoriesList, False, Token)
|
||||
MySiteSettings.TooManyRequests(False)
|
||||
RequestsCount += 1
|
||||
End If
|
||||
@@ -464,6 +472,11 @@ Namespace API.Instagram
|
||||
Else
|
||||
Throw New ExitException
|
||||
End If
|
||||
Case Sections.UserStories
|
||||
GetStoriesData(Nothing, True, Token)
|
||||
MySiteSettings.TooManyRequests(False)
|
||||
RequestsCount += 1
|
||||
Throw New ExitException
|
||||
End Select
|
||||
|
||||
'Get response
|
||||
@@ -624,10 +637,8 @@ Namespace API.Instagram
|
||||
NextCursor = .Value("next_max_id")
|
||||
If .Contains("items") Then nodes = (From ee As EContainer In .Item("items") Where ee.Count > 0 Select ee(0))
|
||||
End With
|
||||
If nodes.ListExists Then
|
||||
DefaultParser(nodes, Sections.SavedPosts, Token)
|
||||
If HasNextPage And Not NextCursor.IsEmptyString Then SavedPostsDownload(NextCursor, Token)
|
||||
End If
|
||||
If nodes.ListExists AndAlso DefaultParser(nodes, Sections.SavedPosts, Token) AndAlso
|
||||
HasNextPage AndAlso Not NextCursor.IsEmptyString Then SavedPostsDownload(NextCursor, Token)
|
||||
End If
|
||||
End Using
|
||||
End If
|
||||
@@ -854,17 +865,21 @@ Namespace API.Instagram
|
||||
End Function
|
||||
#End Region
|
||||
#Region "Pinned stories"
|
||||
Private Sub GetStoriesData(ByRef StoriesList As List(Of String), ByVal Token As CancellationToken)
|
||||
Private Sub GetStoriesData(ByRef StoriesList As List(Of String), ByVal GetUserStory As Boolean, ByVal Token As CancellationToken)
|
||||
Const ReqUrl$ = "https://i.instagram.com/api/v1/feed/reels_media/?{0}"
|
||||
Dim tmpList As IEnumerable(Of String)
|
||||
Dim tmpList As IEnumerable(Of String) = Nothing
|
||||
Dim qStr$, r$, sFolder$, storyID$, pid$
|
||||
Dim i% = -1
|
||||
Dim jj As EContainer, s As EContainer
|
||||
ThrowAny(Token)
|
||||
If StoriesList.ListExists Then
|
||||
tmpList = StoriesList.Take(5)
|
||||
If tmpList.ListExists Then
|
||||
qStr = String.Format(ReqUrl, tmpList.Select(Function(q) $"reel_ids=highlight:{q}").ListToString("&"))
|
||||
If StoriesList.ListExists Or GetUserStory Then
|
||||
If Not GetUserStory Then tmpList = StoriesList.Take(5)
|
||||
If tmpList.ListExists Or GetUserStory Then
|
||||
If GetUserStory Then
|
||||
qStr = $"https://www.instagram.com/api/v1/feed/reels_media/?reel_ids={ID}"
|
||||
Else
|
||||
qStr = String.Format(ReqUrl, tmpList.Select(Function(q) $"reel_ids=highlight:{q}").ListToString("&"))
|
||||
End If
|
||||
r = Responser.GetResponse(qStr,, EDP.ThrowException)
|
||||
ThrowAny(Token)
|
||||
If Not r.IsEmptyString Then
|
||||
@@ -876,9 +891,13 @@ Namespace API.Instagram
|
||||
i += 1
|
||||
sFolder = jj.Value("title").StringRemoveWinForbiddenSymbols
|
||||
storyID = jj.Value("id").Replace("highlight:", String.Empty)
|
||||
If sFolder.IsEmptyString Then sFolder = $"Story_{storyID}"
|
||||
If sFolder.IsEmptyString Then sFolder = $"Story_{i}"
|
||||
sFolder = $"{StoriesFolder}\{sFolder}"
|
||||
If GetUserStory Then
|
||||
sFolder = $"{StoriesFolder} (user)"
|
||||
Else
|
||||
If sFolder.IsEmptyString Then sFolder = $"Story_{storyID}"
|
||||
If sFolder.IsEmptyString Then sFolder = $"Story_{i}"
|
||||
sFolder = $"{StoriesFolder}\{sFolder}"
|
||||
End If
|
||||
If Not storyID.IsEmptyString Then storyID &= ":"
|
||||
With jj("items").XmlIfNothing
|
||||
If .Count > 0 Then
|
||||
@@ -896,7 +915,7 @@ Namespace API.Instagram
|
||||
End If
|
||||
End Using
|
||||
End If
|
||||
StoriesList.RemoveRange(0, tmpList.Count)
|
||||
If Not GetUserStory Then StoriesList.RemoveRange(0, tmpList.Count)
|
||||
End If
|
||||
End If
|
||||
End Sub
|
||||
@@ -933,15 +952,15 @@ Namespace API.Instagram
|
||||
''' </summary>
|
||||
Protected Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False,
|
||||
Optional ByVal s As Object = Nothing) As Integer
|
||||
If Responser.StatusCode = HttpStatusCode.NotFound Then
|
||||
If Responser.StatusCode = HttpStatusCode.NotFound Then '404
|
||||
If Not UserNameRequested AndAlso GetUserNameById() Then Return 1 Else UserExists = False
|
||||
ElseIf Responser.StatusCode = HttpStatusCode.BadRequest Then
|
||||
ElseIf Responser.StatusCode = HttpStatusCode.BadRequest Then '400
|
||||
HasError = True
|
||||
MyMainLOG = $"Instagram credentials have expired [{CInt(Responser.StatusCode)}]: {ToStringForLog()} [{s}]"
|
||||
DisableSection(s)
|
||||
ElseIf Responser.StatusCode = HttpStatusCode.Forbidden And s = Sections.Tagged Then
|
||||
ElseIf Responser.StatusCode = HttpStatusCode.Forbidden And s = Sections.Tagged Then '403
|
||||
Return 3
|
||||
ElseIf Responser.StatusCode = 429 Then
|
||||
ElseIf Responser.StatusCode = 429 Then '429
|
||||
With MySiteSettings
|
||||
Dim WaiterExists As Boolean = .LastApplyingValue.HasValue
|
||||
.TooManyRequests(True)
|
||||
@@ -950,10 +969,10 @@ Namespace API.Instagram
|
||||
Caught429 = True
|
||||
MyMainLOG = $"Number of requests before error 429: {RequestsCount}"
|
||||
Return 1
|
||||
ElseIf Responser.StatusCode = 560 Then
|
||||
ElseIf Responser.StatusCode = 560 Or Responser.StatusCode = HttpStatusCode.InternalServerError Then '560, 500
|
||||
MySiteSettings.SkipUntilNextSession = True
|
||||
Else
|
||||
MyMainLOG = $"Something is wrong. Your credentials may have expired [{CInt(Responser.StatusCode)}]: {ToString()} [{s}]"
|
||||
MyMainLOG = $"Something is wrong. Your credentials may have expired [{CInt(Responser.StatusCode)}/{CInt(Responser.Status)}]: {ToString()} [{s}]"
|
||||
DisableSection(s)
|
||||
If Not FromPE Then LogError(ex, Message) : HasError = True
|
||||
Return 0
|
||||
@@ -965,6 +984,10 @@ Namespace API.Instagram
|
||||
Dim s As Sections = DirectCast(Section, Sections)
|
||||
Select Case s
|
||||
Case Sections.Timeline : MySiteSettings.DownloadTimeline.Value = False
|
||||
Case Sections.Stories, Sections.UserStories
|
||||
MySiteSettings.DownloadTimeline.Value = False
|
||||
MySiteSettings.DownloadStories.Value = False
|
||||
MySiteSettings.DownloadStoriesUser.Value = False
|
||||
Case Else : MySiteSettings.DownloadTagged.Value = False
|
||||
End Select
|
||||
MyMainLOG = $"[{s}] downloading is disabled until you update your credentials".ToUpper
|
||||
|
||||
@@ -104,12 +104,45 @@ Namespace API.Reddit
|
||||
Return New UserData
|
||||
End Function
|
||||
#End Region
|
||||
#Region "Available, UpdateRedGifsToken"
|
||||
#Region "DownloadStarted, ReadyToDownload, Available, DownloadDone, UpdateRedGifsToken"
|
||||
Private ____DownloadStarted As Boolean = False
|
||||
Friend Overrides Sub DownloadStarted(ByVal What As Download)
|
||||
If What = Download.Main Then ____DownloadStarted = True
|
||||
MyBase.DownloadStarted(What)
|
||||
End Sub
|
||||
Friend Property SessionInterrupted As Boolean = False
|
||||
Friend Overrides Function ReadyToDownload(ByVal What As Download) As Boolean
|
||||
If What = Download.Main Then Return Not SessionInterrupted Else Return True
|
||||
If What = Download.Main Then
|
||||
Dim result As Boolean = 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
|
||||
Return True
|
||||
End If
|
||||
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
|
||||
If What = Download.Main And ____DownloadStarted Then
|
||||
____AvailableRequested = True
|
||||
____AvailableSilent = Silent
|
||||
Return True
|
||||
Else
|
||||
Return AvailableImpl(What, Silent)
|
||||
End If
|
||||
End Function
|
||||
Private Function AvailableImpl(ByVal What As Download, ByVal Silent As Boolean) As Boolean
|
||||
Try
|
||||
Dim trueValue As Boolean = Not What = Download.SavedPosts OrElse (Responser.CookiesExists And ACheck(SavedPostsUserName.Value))
|
||||
If Not trueValue Then Return False
|
||||
@@ -141,6 +174,11 @@ Namespace API.Reddit
|
||||
End Function
|
||||
Friend Overrides Sub DownloadDone(ByVal What As Download)
|
||||
SessionInterrupted = False
|
||||
____DownloadStarted = False
|
||||
____AvailableRequested = False
|
||||
____AvailableChecked = False
|
||||
____AvailableSilent = True
|
||||
____AvailableResult = False
|
||||
MyBase.DownloadDone(What)
|
||||
End Sub
|
||||
Private Sub UpdateRedGifsToken()
|
||||
|
||||
@@ -507,8 +507,9 @@ Namespace API
|
||||
.Temporary = Temporary
|
||||
.Favorite = Favorite
|
||||
.ReadyForDownload = ReadyForDownload
|
||||
ConsolidateLabels()
|
||||
ConsolidateLabels(_Item)
|
||||
ConsolidateScripts()
|
||||
ConsolidateColors(_Item)
|
||||
.UpdateUserInformation()
|
||||
End If
|
||||
MainFrameObj.ImageHandler(_Item, False)
|
||||
@@ -546,12 +547,22 @@ Namespace API
|
||||
Catch ex As Exception
|
||||
End Try
|
||||
End Sub
|
||||
Private Sub ConsolidateLabels()
|
||||
UpdateLabels(Me, ListAddList(Nothing, Labels.ListWithRemove(SpecialLabels)), 1, True)
|
||||
Private Sub ConsolidateLabels(ByVal Destination As UserDataBase)
|
||||
UpdateLabels(If(Destination, Me), ListAddList(Nothing, Labels.ListWithRemove(SpecialLabels)), 1, True)
|
||||
End Sub
|
||||
Private Sub ConsolidateScripts()
|
||||
If Count > 1 AndAlso ScriptUse Then Collections.ForEach(Sub(c) c.ScriptUse = True)
|
||||
End Sub
|
||||
Private Sub ConsolidateColors(ByVal Destination As UserDataBase)
|
||||
If Count > 0 And Not Destination.ForeColor.HasValue And Not Destination.BackColor.HasValue Then
|
||||
Dim b As Color? = BackColor
|
||||
Dim f As Color? = ForeColor
|
||||
If b.HasValue AndAlso Not Collections.All(Function(u) Not u Is Destination AndAlso u.BackColor.HasValue AndAlso u.BackColor.Value = b.Value) Then b = Nothing
|
||||
If f.HasValue AndAlso Not Collections.All(Function(u) Not u Is Destination AndAlso u.ForeColor.HasValue AndAlso u.ForeColor.Value = f.Value) Then f = Nothing
|
||||
If b.HasValue Then Destination.BackColor = b
|
||||
If f.HasValue Then Destination.ForeColor = f
|
||||
End If
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Move, Merge"
|
||||
Friend Overrides Function MoveFiles(ByVal __CollectionName As String, ByVal __SpecialCollectionPath As SFile) As Boolean
|
||||
|
||||
@@ -82,7 +82,7 @@ Namespace API.YouTube
|
||||
Dim isMusic As Boolean = False
|
||||
Dim id$ = String.Empty
|
||||
Dim isChannelUser As Boolean = False
|
||||
Dim t As YouTubeMediaType = YouTubeFunctions.Info_GetUrlType(UserURL, isMusic, isChannelUser, id)
|
||||
Dim t As YouTubeMediaType = YouTubeFunctions.Info_GetUrlType(UserURL, isMusic,, isChannelUser, id)
|
||||
If Not t = YouTubeMediaType.Undefined And Not t = YouTubeMediaType.Single And Not id.IsEmptyString Then
|
||||
Return New ExchangeOptions(Site, $"{id}@{CInt(t) + IIf(isMusic, UserMedia.Types.Audio, 0) + IIf(isChannelUser, ChannelUserInt, 0)}")
|
||||
End If
|
||||
|
||||
@@ -15,7 +15,7 @@ Imports PersonalUtilities.Tools
|
||||
Imports PersonalUtilities.Tools.Notifications
|
||||
Namespace DownloadObjects
|
||||
Friend Class AutoDownloader : Inherits GroupParameters : Implements IIndexable, IEContainerProvider
|
||||
Friend Event PauseDisabled()
|
||||
Friend Event PauseChanged(ByVal Value As PauseModes)
|
||||
Friend Enum Modes As Integer
|
||||
None = 0
|
||||
[Default] = 1
|
||||
@@ -412,6 +412,7 @@ Namespace DownloadObjects
|
||||
Case PauseModes.Until : _PauseValue = DateLimit
|
||||
Case Else : _PauseValue = Nothing
|
||||
End Select
|
||||
RaiseEvent PauseChanged(p)
|
||||
End Set
|
||||
End Property
|
||||
Private ReadOnly Property IsPaused As Boolean
|
||||
@@ -423,7 +424,7 @@ Namespace DownloadObjects
|
||||
Else
|
||||
_Pause = PauseModes.Disabled
|
||||
_PauseValue = Nothing
|
||||
RaiseEvent PauseDisabled()
|
||||
RaiseEvent PauseChanged(_Pause)
|
||||
Return False
|
||||
End If
|
||||
Else
|
||||
|
||||
@@ -92,34 +92,39 @@ Namespace DownloadObjects
|
||||
BTT_PAUSE_H6.Click, BTT_PAUSE_H12.Click,
|
||||
BTT_PAUSE_UNTIL.Click, BTT_PAUSE_UNLIMITED.Click,
|
||||
BTT_PAUSE_DISABLE.Click
|
||||
If (Place = ButtonsPlace.Scheduler And PlanIndex >= 0 And PlanIndex.ValueBetween(0, Settings.Automation.Count - 1)) Or Not Place = ButtonsPlace.Scheduler Then
|
||||
Dim p As PauseModes = CInt(AConvert(Of Integer)(Sender.Tag, -10))
|
||||
If p > -10 Then
|
||||
Dim d As Date? = Nothing
|
||||
Dim _SetPauseValue As Action = Sub()
|
||||
If Place = ButtonsPlace.Scheduler And PlanIndex.ValueBetween(0, Settings.Automation.Count - 1) Then
|
||||
Settings.Automation(PlanIndex).Pause(d) = p
|
||||
ElseIf Not Place = ButtonsPlace.Scheduler Then
|
||||
Settings.Automation.Pause(d) = p
|
||||
End If
|
||||
End Sub
|
||||
If p = PauseModes.Until Then
|
||||
Using f As New DateTimeSelectionForm(TimeSelectionModes.End + TimeSelectionModes.Date + TimeSelectionModes.Time, Settings.Design)
|
||||
f.ShowDialog()
|
||||
If f.DialogResult = DialogResult.OK Then d = f.MyDateEnd
|
||||
End Using
|
||||
If d.HasValue Then _SetPauseValue.Invoke
|
||||
Else
|
||||
_SetPauseValue.Invoke
|
||||
End If
|
||||
UpdatePauseButtons()
|
||||
Dim p As PauseModes = CInt(AConvert(Of Integer)(Sender.Tag, -10))
|
||||
If p > -10 AndAlso ((Place = ButtonsPlace.Scheduler And PlanIndex >= 0 And PlanIndex.ValueBetween(0, Settings.Automation.Count - 1)) OrElse
|
||||
Not Place = ButtonsPlace.Scheduler OrElse
|
||||
(Place = ButtonsPlace.Scheduler AndAlso PlanIndex = -1 AndAlso
|
||||
MsgBoxE({$"Do you want to turn {IIf(p = PauseModes.Disabled, "off", "on")} pause for all plans?", "Pause plan"},
|
||||
vbExclamation + vbYesNo) = vbYes)) Then
|
||||
Dim d As Date? = Nothing
|
||||
Dim _SetPauseValue As Action = Sub()
|
||||
If Place = ButtonsPlace.Scheduler And PlanIndex.ValueBetween(0, Settings.Automation.Count - 1) Then
|
||||
Settings.Automation(PlanIndex).Pause(d) = p
|
||||
ElseIf Not Place = ButtonsPlace.Scheduler Or Place = ButtonsPlace.Scheduler And PlanIndex = -1 Then
|
||||
Settings.Automation.Pause(d) = p
|
||||
End If
|
||||
End Sub
|
||||
If p = PauseModes.Until Then
|
||||
Using f As New DateTimeSelectionForm(TimeSelectionModes.End + TimeSelectionModes.Date + TimeSelectionModes.Time, Settings.Design)
|
||||
f.ShowDialog()
|
||||
If f.DialogResult = DialogResult.OK Then d = f.MyDateEnd
|
||||
End Using
|
||||
If d.HasValue Then _SetPauseValue.Invoke
|
||||
Else
|
||||
_SetPauseValue.Invoke
|
||||
End If
|
||||
ElseIf Place = ButtonsPlace.Scheduler And PlanIndex = -1 Then
|
||||
UpdatePauseButtons()
|
||||
ElseIf p > -10 And Place = ButtonsPlace.Scheduler And PlanIndex = -1 Then
|
||||
MsgBoxE({"The plan to be paused is not selected", "Pause plan"}, vbExclamation)
|
||||
End If
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Update buttons"
|
||||
Friend Overloads Sub UpdatePauseButtons_Handler(ByVal Value As PauseModes)
|
||||
UpdatePauseButtons()
|
||||
End Sub
|
||||
Friend Overloads Sub UpdatePauseButtons() Handles TrayButtons.Updating
|
||||
UpdatePauseButtons(True)
|
||||
End Sub
|
||||
|
||||
@@ -14,12 +14,14 @@ Imports PauseModes = SCrawler.DownloadObjects.AutoDownloader.PauseModes
|
||||
Namespace DownloadObjects
|
||||
Friend Class Scheduler : Implements IEnumerable(Of AutoDownloader), IMyEnumerator(Of AutoDownloader), IDisposable
|
||||
Friend Const Name_Plan As String = "Plan"
|
||||
Friend Event PauseDisabled As AutoDownloader.PauseDisabledEventHandler
|
||||
Private Sub OnPauseDisabled()
|
||||
RaiseEvent PauseDisabled()
|
||||
Friend Event PauseChanged As AutoDownloader.PauseChangedEventHandler
|
||||
Private Sub OnPauseChanged(ByVal Value As PauseModes)
|
||||
RaiseEvent PauseChanged(Pause)
|
||||
End Sub
|
||||
Private ReadOnly Plans As List(Of AutoDownloader)
|
||||
Private ReadOnly File As SFile = $"Settings\AutoDownload.xml"
|
||||
Friend Const FileNameDefault As String = "AutoDownload"
|
||||
Friend ReadOnly FileDefault As SFile = $"{SettingsFolderName}\{FileNameDefault}.xml"
|
||||
Friend File As SFile = Nothing
|
||||
Private ReadOnly PlanWorking As Predicate(Of AutoDownloader) = Function(Plan) Plan.Working
|
||||
Private ReadOnly PlanDownloading As Predicate(Of AutoDownloader) = Function(Plan) Plan.Downloading
|
||||
Private ReadOnly PlansWaiter As Action(Of Predicate(Of AutoDownloader)) = Sub(ByVal Predicate As Predicate(Of AutoDownloader))
|
||||
@@ -27,20 +29,9 @@ Namespace DownloadObjects
|
||||
End Sub
|
||||
Friend Sub New()
|
||||
Plans = New List(Of AutoDownloader)
|
||||
If File.Exists Then
|
||||
Using x As New XmlFile(File,, False) With {.AllowSameNames = True}
|
||||
x.LoadData()
|
||||
If x.Contains(Name_Plan) Then
|
||||
For Each e In x : Plans.Add(New AutoDownloader(e)) : Next
|
||||
Else
|
||||
Plans.Add(New AutoDownloader(x))
|
||||
End If
|
||||
End Using
|
||||
End If
|
||||
If Plans.Count > 0 Then Plans.ForEach(Sub(p)
|
||||
p.Source = Me
|
||||
AddHandler p.PauseDisabled, AddressOf OnPauseDisabled
|
||||
End Sub) : Plans.ListReindex
|
||||
File = Settings.AutomationFile.Value.IfNullOrEmpty(FileDefault)
|
||||
If Not File.Exists Then File = FileDefault
|
||||
Reset(File, True)
|
||||
End Sub
|
||||
Default Friend ReadOnly Property Item(ByVal Index As Integer) As AutoDownloader Implements IMyEnumerator(Of AutoDownloader).MyEnumeratorObject
|
||||
Get
|
||||
@@ -62,7 +53,7 @@ Namespace DownloadObjects
|
||||
End Function
|
||||
Friend Sub Add(ByVal Plan As AutoDownloader)
|
||||
Plan.Source = Me
|
||||
AddHandler Plan.PauseDisabled, AddressOf OnPauseDisabled
|
||||
AddHandler Plan.PauseChanged, AddressOf OnPauseChanged
|
||||
Plans.Add(Plan)
|
||||
Plans.ListReindex
|
||||
Update()
|
||||
@@ -96,6 +87,39 @@ Namespace DownloadObjects
|
||||
Catch
|
||||
End Try
|
||||
End Sub
|
||||
Friend Function Reset(ByVal f As SFile, ByVal IsInit As Boolean) As Boolean
|
||||
If Plans.Count > 0 Then
|
||||
If Not Plans.Exists(PlanWorking) Then
|
||||
Pause = PauseModes.Unlimited
|
||||
If Plans.Exists(PlanWorking) Then
|
||||
MsgBoxE({$"Some plans are already being worked.{vbCr}Wait for the plans to complete their work and try again.",
|
||||
"Change scheduler"}, vbCritical)
|
||||
Pause = PauseModes.Unlimited
|
||||
Return False
|
||||
End If
|
||||
End If
|
||||
[Stop]()
|
||||
If _UpdateRequired Then Update()
|
||||
Plans.ListClearDispose(,, EDP.LogMessageValue)
|
||||
End If
|
||||
If f.Exists Then
|
||||
File = f
|
||||
Using x As New XmlFile(File,, False) With {.AllowSameNames = True}
|
||||
x.LoadData()
|
||||
If x.Contains(Name_Plan) Then
|
||||
For Each e In x : Plans.Add(New AutoDownloader(e)) : Next
|
||||
Else
|
||||
Plans.Add(New AutoDownloader(x))
|
||||
End If
|
||||
End Using
|
||||
If Plans.Count > 0 Then Plans.ForEach(Sub(ByVal p As AutoDownloader)
|
||||
p.Source = Me
|
||||
If Not IsInit Then p.Pause = PauseModes.Unlimited
|
||||
AddHandler p.PauseChanged, AddressOf OnPauseChanged
|
||||
End Sub) : Plans.ListReindex
|
||||
End If
|
||||
Return True
|
||||
End Function
|
||||
#Region "Groups Support"
|
||||
Friend Sub GROUPS_Updated(ByVal Sender As DownloadGroup)
|
||||
If Count > 0 Then Plans.ForEach(Sub(p) p.GROUPS_Updated(Sender))
|
||||
|
||||
@@ -63,7 +63,7 @@ Namespace DownloadObjects
|
||||
Me.KeyPreview = True
|
||||
Me.MinimumSize = New System.Drawing.Size(430, 380)
|
||||
Me.Name = "SchedulerEditorForm"
|
||||
Me.ShowIcon = False
|
||||
Me.ShowIcon = True
|
||||
Me.ShowInTaskbar = False
|
||||
Me.Text = "Scheduler"
|
||||
CONTAINER_MAIN.ContentPanel.ResumeLayout(False)
|
||||
|
||||
@@ -8,11 +8,14 @@
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports PersonalUtilities.Forms
|
||||
Imports PersonalUtilities.Forms.Toolbars
|
||||
Imports PersonalUtilities.Tools
|
||||
Imports ECI = PersonalUtilities.Forms.Toolbars.EditToolbar.ControlItem
|
||||
Imports ADB = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons
|
||||
Namespace DownloadObjects
|
||||
Friend Class SchedulerEditorForm
|
||||
#Region "Declarations"
|
||||
Private WithEvents MyDefs As DefaultFormOptions
|
||||
Private WithEvents BTT_SETTINGS As ToolStripButton
|
||||
Private WithEvents BTT_CLONE As ToolStripButton
|
||||
Private ReadOnly MENU_SKIP As ToolStripDropDownButton
|
||||
Private WithEvents BTT_SKIP As ToolStripMenuItem
|
||||
@@ -28,6 +31,13 @@ Namespace DownloadObjects
|
||||
Friend Sub New()
|
||||
InitializeComponent()
|
||||
MyDefs = New DefaultFormOptions(Me, Settings.Design)
|
||||
BTT_SETTINGS = New ToolStripButton With {
|
||||
.Text = String.Empty,
|
||||
.AutoToolTip = True,
|
||||
.ToolTipText = "Change scheduler",
|
||||
.DisplayStyle = ToolStripItemDisplayStyle.Image,
|
||||
.Image = My.Resources.ScriptPic_32
|
||||
}
|
||||
BTT_CLONE = New ToolStripButton With {
|
||||
.Text = "Clone",
|
||||
.DisplayStyle = ToolStripItemDisplayStyle.ImageAndText,
|
||||
@@ -89,13 +99,15 @@ Namespace DownloadObjects
|
||||
}
|
||||
PauseArr = New AutoDownloaderPauseButtons(AutoDownloaderPauseButtons.ButtonsPlace.Scheduler) With {
|
||||
.MainFrameButtonsInstance = MainFrameObj.PauseButtons}
|
||||
Icon = ImageRenderer.GetIcon(My.Resources.ScriptPic_32, EDP.ReturnValue)
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Form handlers"
|
||||
Private Sub SchedulerEditorForm_Load(sender As Object, e As EventArgs) Handles Me.Load
|
||||
With MyDefs
|
||||
.MyViewInitialize()
|
||||
.AddEditToolbar({ECI.Add, BTT_CLONE, ECI.Edit, ECI.Delete, ECI.Update, ECI.Separator, BTT_START, BTT_START_FORCE, MENU_SKIP, BTT_PAUSE})
|
||||
.AddEditToolbar({BTT_SETTINGS, ECI.Separator, ECI.Add, BTT_CLONE, ECI.Edit, ECI.Delete, ECI.Update, ECI.Separator,
|
||||
BTT_START, BTT_START_FORCE, MENU_SKIP, BTT_PAUSE})
|
||||
PauseArr.AddButtons(BTT_PAUSE, .MyEditToolbar.ToolStrip)
|
||||
Refill()
|
||||
.EndLoaderOperations(False)
|
||||
@@ -186,7 +198,80 @@ Namespace DownloadObjects
|
||||
Edit()
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Start, Skip, Pause"
|
||||
#Region "Settings, Start, Skip, Pause"
|
||||
Private Sub BTT_SETTINGS_Click(sender As Object, e As EventArgs) Handles BTT_SETTINGS.Click
|
||||
Const msgTitle$ = "Change scheduler"
|
||||
Try
|
||||
Const defName$ = "Default"
|
||||
Dim l As New Dictionary(Of SFile, String)
|
||||
With SFile.GetFiles(SettingsFolderName.CSFileP, $"{Scheduler.FileNameDefault}*.xml",, EDP.ReturnValue)
|
||||
If .ListExists Then .ForEach(Sub(ff) l.Add(ff, ff.Name.Replace(Scheduler.FileNameDefault, String.Empty).StringTrimStart("_").IfNullOrEmpty(defName)))
|
||||
End With
|
||||
If l.Count > 0 Then
|
||||
Using chooser As New SimpleListForm(Of String)(l.Values.Cast(Of String), Settings.Design) With {
|
||||
.DesignXMLNodeName = "SchedulerChooserForm",
|
||||
.Icon = PersonalUtilities.Tools.ImageRenderer.GetIcon(My.Resources.ScriptPic_32, EDP.ReturnValue),
|
||||
.FormText = "Schedulers",
|
||||
.Mode = SimpleListFormModes.SelectedItems,
|
||||
.MultiSelect = False
|
||||
}
|
||||
With chooser
|
||||
Dim i%
|
||||
Dim f As SFile
|
||||
Dim selectedName$
|
||||
Dim addedObj$ = String.Empty
|
||||
.ClearButtons()
|
||||
.Buttons = {ADB.Add, ADB.Delete}
|
||||
AddHandler .AddClick, Sub(ByVal obj As Object, ByVal args As SimpleListFormEventArgs)
|
||||
If addedObj.IsEmptyString Then
|
||||
addedObj = InputBoxE("Enter a new scheduler name:", msgTitle)
|
||||
args.Result = Not addedObj.IsEmptyString
|
||||
If args.Result Then args.Item = addedObj
|
||||
Else
|
||||
MsgBoxE({"You can only create one scheduler at a time", "Create a new scheduler"}, vbCritical)
|
||||
End If
|
||||
End Sub
|
||||
If Settings.Automation.File.Name = Scheduler.FileNameDefault Then
|
||||
.DataSelectedIndexes.Add(0)
|
||||
Else
|
||||
i = l.Keys.ListIndexOf(Function(ff) ff = Settings.Automation.File)
|
||||
If i >= 0 Then .DataSelectedIndexes.Add(i)
|
||||
End If
|
||||
If .ShowDialog() = DialogResult.OK Then
|
||||
selectedName = .DataResult.FirstOrDefault
|
||||
If Not selectedName.IsEmptyString Then
|
||||
If selectedName = defName Then
|
||||
f = Settings.Automation.FileDefault
|
||||
Else
|
||||
f = $"{SettingsFolderName}\{Scheduler.FileNameDefault}_{selectedName.StringRemoveWinForbiddenSymbols}.xml"
|
||||
End If
|
||||
If Not Settings.Automation.File = f AndAlso Settings.Automation.Reset(f, False) Then
|
||||
Settings.Automation.File = f
|
||||
If selectedName = defName Then
|
||||
Settings.AutomationFile.Value = Nothing
|
||||
Else
|
||||
Settings.AutomationFile.Value = f
|
||||
End If
|
||||
PauseArr.UpdatePauseButtons()
|
||||
Refill()
|
||||
If Not .DataSource.Count = l.Count Then
|
||||
For i = l.Count - 1 To 0 Step -1
|
||||
If Not .DataSource.Contains(l(l.Keys(i))) Then l.Keys(i).Delete(, SFODelete.DeleteToRecycleBin, EDP.SendToLog)
|
||||
Next
|
||||
End If
|
||||
End If
|
||||
End If
|
||||
End If
|
||||
End With
|
||||
End Using
|
||||
l.Clear()
|
||||
Else
|
||||
MsgBoxE({"There are no plans created", msgTitle}, vbExclamation)
|
||||
End If
|
||||
Catch ex As Exception
|
||||
ErrorsDescriber.Execute(EDP.LogMessageValue, ex, msgTitle)
|
||||
End Try
|
||||
End Sub
|
||||
Private Sub BTT_START_Click(sender As Object, e As EventArgs) Handles BTT_START.Click
|
||||
If _LatestSelected.ValueBetween(0, LIST_PLANS.Items.Count - 1) Then
|
||||
With Settings.Automation(_LatestSelected) : .Start(.IsNewPlan) : End With
|
||||
|
||||
@@ -8,4 +8,5 @@ Imports System.Diagnostics.CodeAnalysis
|
||||
<Assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification:="<Pending>", Scope:="member", Target:="~M:SCrawler.API.UserDataBind.DownloadData(System.Threading.CancellationToken,System.Boolean)")>
|
||||
<Assembly: SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification:="<Pending>", Scope:="member", Target:="~F:SCrawler.MainFrame.DownloadQueue")>
|
||||
<Assembly: SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification:="<Pending>", Scope:="member", Target:="~F:SCrawler.MainFrame.MyMissingPosts")>
|
||||
<Assembly: SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification:="<Pending>", Scope:="member", Target:="~F:SCrawler.MainFrame.MyUserMetrics")>
|
||||
<Assembly: SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification:="<Pending>", Scope:="member", Target:="~F:SCrawler.MainFrame.MyUserMetrics")>
|
||||
<Assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification:="<Pending>", Scope:="member", Target:="~M:SCrawler.DownloadObjects.AutoDownloaderPauseButtons.UpdatePauseButtons_Handler(SCrawler.DownloadObjects.AutoDownloader.PauseModes)")>
|
||||
|
||||
@@ -122,7 +122,7 @@ Public Class MainFrame
|
||||
.Automation = New Scheduler
|
||||
AddHandler .Groups.Updated, AddressOf .Automation.GROUPS_Updated
|
||||
AddHandler .Groups.Deleted, AddressOf .Automation.GROUPS_Deleted
|
||||
AddHandler .Automation.PauseDisabled, AddressOf MainFrameObj.PauseButtons.UpdatePauseButtons
|
||||
AddHandler .Automation.PauseChanged, AddressOf MainFrameObj.PauseButtons.UpdatePauseButtons_Handler
|
||||
If .Automation.Count > 0 Then .Labels.AddRange(.Automation.GetGroupsLabels, False) : .Labels.Update()
|
||||
_UFinit = False
|
||||
Await .Automation.Start(True)
|
||||
|
||||
@@ -32,6 +32,6 @@ Imports System.Runtime.InteropServices
|
||||
' by using the '*' as shown below:
|
||||
' <Assembly: AssemblyVersion("1.0.*")>
|
||||
|
||||
<Assembly: AssemblyVersion("2023.8.27.0")>
|
||||
<Assembly: AssemblyFileVersion("2023.8.27.0")>
|
||||
<Assembly: AssemblyVersion("2023.9.20.0")>
|
||||
<Assembly: AssemblyFileVersion("2023.9.20.0")>
|
||||
<Assembly: NeutralResourcesLanguage("en")>
|
||||
|
||||
@@ -130,6 +130,7 @@ Friend Class SettingsCLS : Implements IDownloaderSettings, IDisposable
|
||||
Friend ReadOnly Property DownloadLocations As STDownloader.DownloadLocationsCollection
|
||||
Friend ReadOnly Property GlobalLocations As STDownloader.DownloadLocationsCollection
|
||||
Friend Property Automation As Scheduler
|
||||
Friend ReadOnly Property AutomationFile As XMLValue(Of SFile)
|
||||
Friend ReadOnly Property BlackList As List(Of UserBan)
|
||||
Private ReadOnly BlackListFile As SFile = $"{SettingsFolderName}\BlackList.txt"
|
||||
Private ReadOnly UsersSettingsFile As SFile = $"{SettingsFolderName}\Users.xml"
|
||||
@@ -170,6 +171,7 @@ Friend Class SettingsCLS : Implements IDownloaderSettings, IDisposable
|
||||
|
||||
SeparateVideoFolder = New XMLValue(Of Boolean)("SeparateVideoFolder", True, MyXML)
|
||||
CollectionsPath = New XMLValue(Of String)("CollectionsPath", CollectionsFolderName, MyXML)
|
||||
AutomationFile = New XMLValue(Of SFile)("AutomationFile",, MyXML)
|
||||
|
||||
UserAgent = New XMLValue(Of String)("UserAgent",, MyXML)
|
||||
If Not UserAgent.IsEmptyString Then DefaultUserAgent = UserAgent
|
||||
|
||||
Reference in New Issue
Block a user