Compare commits

..

7 Commits

Author SHA1 Message Date
Andy
a446df1f66 2023.9.20.0 2023-09-20 12:17:00 +03:00
Andy
0026e905a4 2023.9.19.0
YT: add priority download protocol
2023-09-19 12:55:53 +03:00
Andy
f8116fd048 2023.9.18.0
API.Instagram: handle error 500; fix saved posts bug
API.Reddit: disable token refresh if there are no profiles to download
API.UserDataBind: consolidate colors; update labels only for the added user
AutoDownloader: change pause event; update pause function for scheduler; fix incorrect pause in scheduler; add icon for SchedulerEditorForm
2023-09-18 07:54:26 +03:00
Andy
8d33fdc8f3 Update README.md 2023-09-18 07:34:00 +03:00
Andy
dab94acc32 2023.9.6.0
Add scheduler changer
2023-09-06 23:38:16 +03:00
Andy
c61c817585 2023.9.3.0
API.Instagram: add user (non-pinned) stories
API.UserDataBase: fix 'StartMD5Checked' initial value
2023-09-03 19:54:01 +03:00
Andy
3ea59a6acd 2023.8.27.0
YT: fix 'Shorts' downloading
2023-09-03 19:38:18 +03:00
26 changed files with 386 additions and 116 deletions

View File

@@ -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.8.27.0
*2023-08-27* *2023-08-27*

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 36 KiB

View File

@@ -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: # :rainbow_flag: Social networks crawler :rainbow_flag:
[![GitHub release (latest by date)](https://img.shields.io/github/v/release/AAndyProgram/SCrawler)](https://github.com/AAndyProgram/SCrawler/releases/latest) [![GitHub release (latest by date)](https://img.shields.io/github/v/release/AAndyProgram/SCrawler)](https://github.com/AAndyProgram/SCrawler/releases/latest)
@@ -189,6 +189,10 @@ F5-->[*]
# Contact me # 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 [e-mail](mailto:andyprogram@proton.me): andyprogram@proton.me
Matrix (Element): https://matrix.to/#/@andyprogram:matrix.org Matrix (Element): https://matrix.to/#/@andyprogram:matrix.org
@@ -198,3 +202,4 @@ Discord (contact the developer): andyprogram
Discord server: https://discord.gg/uFNUXvFFmg Discord server: https://discord.gg/uFNUXvFFmg
[Wire](https://account.wire.com/user-profile/?id=93985052-cf2c-4b72-ac75-bbe3231cf544): @andyprogram [Wire](https://account.wire.com/user-profile/?id=93985052-cf2c-4b72-ac75-bbe3231cf544): @andyprogram
-->

View File

@@ -6,6 +6,10 @@
' '
' 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.Drawing.Design
Imports System.ComponentModel
Imports PersonalUtilities.Tools.Grid.Attributes
Imports PersonalUtilities.Tools.Grid.EnumObjects
Namespace API.YouTube.Base Namespace API.YouTube.Base
Public Structure Thumbnail : Implements IIndexable, IComparable(Of Thumbnail) Public Structure Thumbnail : Implements IIndexable, IComparable(Of Thumbnail)
Public ID As String Public ID As String
@@ -47,6 +51,14 @@ Namespace API.YouTube.Base
Channel = 2 Channel = 2
PlayList = 3 PlayList = 3
End Enum 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 Structure MediaObject : Implements IIndexable, IComparable(Of MediaObject)
Public Type As Plugin.UserMediaTypes Public Type As Plugin.UserMediaTypes
Public ID As String Public ID As String
@@ -59,6 +71,17 @@ Namespace API.YouTube.Base
Public Size As Double Public Size As Double
Public Codec As String Public Codec As String
Public Protocol 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 URL As String
Public Property Index As Integer Implements IIndexable.Index Public Property Index As Integer Implements IIndexable.Index
Private Function SetIndex(ByVal Obj As Object, ByVal Index As Integer) As Object Implements IIndexable.SetIndex Private Function SetIndex(ByVal Obj As Object, ByVal Index As Integer) As Object Implements IIndexable.SetIndex

View File

@@ -23,17 +23,19 @@ Namespace API.YouTube.Base
Public Shared Function IsMyUrl(ByVal URL As String) As Boolean Public Shared Function IsMyUrl(ByVal URL As String) As Boolean
Return Not Info_GetUrlType(URL) = YouTubeMediaType.Undefined Return Not Info_GetUrlType(URL) = YouTubeMediaType.Undefined
End Function 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 Optional ByRef IsChannelUser As Boolean = False, Optional ByRef Id As String = Nothing) As YouTubeMediaType
If Not URL.IsEmptyString Then If Not URL.IsEmptyString Then
IsMusic = URL.Contains("music.youtube.com") IsMusic = URL.Contains("music.youtube.com")
IsChannelUser = False IsChannelUser = False
IsShorts = False
Dim data As List(Of String) = RegexReplace(URL, RParams.DMS(UrlTypePattern, 0, RegexReturn.ListByMatch, EDP.ReturnValue)) Dim data As List(Of String) = RegexReplace(URL, RParams.DMS(UrlTypePattern, 0, RegexReturn.ListByMatch, EDP.ReturnValue))
If data.ListExists Then If data.ListExists Then
If data.Count >= 6 Then Id = data(5) If data.Count >= 6 Then Id = data(5)
If data.Count >= 3 And Not data(2).IsEmptyString Then If data.Count >= 3 And Not data(2).IsEmptyString Then
Select Case data(2).ToLower Select Case data(2).ToLower
Case "watch" : Return YouTubeMediaType.Single Case "watch" : Return YouTubeMediaType.Single
Case "shorts" : IsShorts = True : Return YouTubeMediaType.Single
Case "playlist" : Return YouTubeMediaType.PlayList Case "playlist" : Return YouTubeMediaType.PlayList
Case UserChannelOption, "@" : IsChannelUser = data(2).ToLower = UserChannelOption : Return YouTubeMediaType.Channel Case UserChannelOption, "@" : IsChannelUser = data(2).ToLower = UserChannelOption : Return YouTubeMediaType.Channel
End Select End Select
@@ -64,8 +66,8 @@ Namespace API.YouTube.Base
Dim urlOrig$ = URL Dim urlOrig$ = URL
URL = RegexReplace(URL, TrueUrlRegEx) URL = RegexReplace(URL, TrueUrlRegEx)
If URL.IsEmptyString Then Throw New ArgumentNullException("URL", $"Can't get true URL from [{urlOrig}]") If URL.IsEmptyString Then Throw New ArgumentNullException("URL", $"Can't get true URL from [{urlOrig}]")
Dim isMusic As Boolean = False Dim isMusic As Boolean = False, isShorts As Boolean = False
Dim objType As YouTubeMediaType = Info_GetUrlType(URL, isMusic) Dim objType As YouTubeMediaType = Info_GetUrlType(URL, isMusic, isShorts)
If Not objType = YouTubeMediaType.Undefined Then If Not objType = YouTubeMediaType.Undefined Then
Dim __GetDefault As Boolean = If(GetDefault, True) Dim __GetDefault As Boolean = If(GetDefault, True)
Dim __GetShorts As Boolean = If(GetShorts, True) Dim __GetShorts As Boolean = If(GetShorts, True)
@@ -105,7 +107,7 @@ Namespace API.YouTube.Base
If result Then If result Then
container.Parse(Nothing, _CachePathDefault, isMusic, Token, Progress) 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 End If
container.Dispose() container.Dispose()
End If End If

View File

@@ -139,6 +139,9 @@ 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"),
Description("Priority download protocol. Default: 'Any'")>
Public ReadOnly Property DefaultProtocol As XMLValue(Of Protocols)
<Browsable(True), GridVisible(False), XMLVN({"Defaults"}), Category("Defaults"), <Browsable(True), GridVisible(False), XMLVN({"Defaults"}), Category("Defaults"),
DisplayName("Auto remove"), Description("Automatically remove downloaded items from the list.")> DisplayName("Auto remove"), Description("Automatically remove downloaded items from the list.")>
Public ReadOnly Property RemoveDownloadedAutomatically As XMLValue(Of Boolean) Public ReadOnly Property RemoveDownloadedAutomatically As XMLValue(Of Boolean)

View File

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

View File

@@ -25,6 +25,7 @@ Namespace API.YouTube.Objects
ReadOnly Property MediaType As UMTypes ReadOnly Property MediaType As UMTypes
ReadOnly Property MediaState As UMStates ReadOnly Property MediaState As UMStates
Property IsMusic As Boolean Property IsMusic As Boolean
Property IsShorts As Boolean
Property ID As String Property ID As String
Property Description As String Property Description As String
Property PlaylistID As String Property PlaylistID As String

View File

@@ -112,7 +112,7 @@ Namespace API.YouTube.Objects
End Set End Set
End Property End Property
<XMLEC(Name_IsMusic)> Public Property IsMusic As Boolean = False Implements IYouTubeMediaContainer.IsMusic <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 ID As String Implements IYouTubeMediaContainer.ID, IUserMedia.PostID
<XMLEC> Public Property Title As String Implements IDownloadableMedia.Title <XMLEC> Public Property Title As String Implements IDownloadableMedia.Title
<XMLEC> Public Property Description As String Implements IYouTubeMediaContainer.Description <XMLEC> Public Property Description As String Implements IYouTubeMediaContainer.Description
@@ -1309,9 +1309,29 @@ Namespace API.YouTube.Objects
Next Next
End If End If
End Sub 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 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.Audio)
If MediaObjects.Count > 0 Then DupRemover.Invoke(UMTypes.Video) If MediaObjects.Count > 0 Then DupRemover.Invoke(UMTypes.Video)
If MediaObjects.Count > 0 Then protocolCleaner.Invoke
If MediaObjects.Count > 0 Then If MediaObjects.Count > 0 Then
MediaObjects.Sort() MediaObjects.Sort()
SelectedAudioIndex = MediaObjects.FindIndex(Function(mo) mo.Type = UMTypes.Audio) SelectedAudioIndex = MediaObjects.FindIndex(Function(mo) mo.Type = UMTypes.Audio)

View File

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

View File

@@ -1371,7 +1371,7 @@ BlockNullPicture:
#Region "MD5 support" #Region "MD5 support"
Protected Const VALIDATE_MD5_ERROR As String = "VALIDATE_MD5_ERROR" Protected Const VALIDATE_MD5_ERROR As String = "VALIDATE_MD5_ERROR"
Friend Property UseMD5Comparison As Boolean = False Friend Property UseMD5Comparison As Boolean = False
Protected Property StartMD5Checked As Boolean = True Protected Property StartMD5Checked As Boolean = False
Friend Property RemoveExistingDuplicates As Boolean = False Friend Property RemoveExistingDuplicates As Boolean = False
Protected Overridable Sub ValidateMD5(ByVal Token As CancellationToken) Protected Overridable Sub ValidateMD5(ByVal Token As CancellationToken)
Try Try

View File

@@ -11,14 +11,17 @@ Namespace API.Instagram
Friend Class EditorExchangeOptions Friend Class EditorExchangeOptions
<PSetting(Caption:="Get timeline", ToolTip:="Download user timeline")> <PSetting(Caption:="Get timeline", ToolTip:="Download user timeline")>
Friend Property GetTimeline As Boolean 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 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")> <PSetting(Caption:="Get tagged posts", ToolTip:="Download user tagged posts")>
Friend Property GetTagged As Boolean Friend Property GetTagged As Boolean
Friend Sub New(ByVal u As UserData) Friend Sub New(ByVal u As UserData)
With u With u
GetTimeline = .GetTimeline GetTimeline = .GetTimeline
GetStories = .GetStories GetStories = .GetStories
GetStoriesUser = .GetStoriesUser
GetTagged = .GetTaggedData GetTagged = .GetTaggedData
End With End With
End Sub End Sub
@@ -26,6 +29,7 @@ Namespace API.Instagram
With s With s
GetTimeline = CBool(.GetTimeline.Value) GetTimeline = CBool(.GetTimeline.Value)
GetStories = CBool(.GetStories.Value) GetStories = CBool(.GetStories.Value)
GetStoriesUser = CBool(.GetStoriesUser.Value)
GetTagged = CBool(.GetTagged.Value) GetTagged = CBool(.GetTagged.Value)
End With End With
End Sub End Sub

View File

@@ -139,11 +139,13 @@ Namespace API.Instagram
Friend ReadOnly Property GetTimeline As PropertyValue Friend ReadOnly Property GetTimeline As PropertyValue
<PropertyOption(ControlText:="Get stories", ControlToolTip:="Default value for new users"), PXML, ControlNumber(24)> <PropertyOption(ControlText:="Get stories", ControlToolTip:="Default value for new users"), PXML, ControlNumber(24)>
Friend ReadOnly Property GetStories As PropertyValue 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 Friend ReadOnly Property GetTagged As PropertyValue
<PropertyOption(ControlText:="Tagged notify limit", <PropertyOption(ControlText:="Tagged notify limit",
ControlToolTip:="If the number of tagged posts exceeds this number you will be notified." & vbCr & 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 Friend ReadOnly Property TaggedNotifyLimit As PropertyValue
<Provider(NameOf(TaggedNotifyLimit), FieldsChecker:=True)> <Provider(NameOf(TaggedNotifyLimit), FieldsChecker:=True)>
Private ReadOnly Property TaggedNotifyLimitProvider As IFormatProvider Private ReadOnly Property TaggedNotifyLimitProvider As IFormatProvider
@@ -153,7 +155,9 @@ Namespace API.Instagram
Friend ReadOnly Property DownloadTimeline As PropertyValue Friend ReadOnly Property DownloadTimeline As PropertyValue
<PropertyOption(ControlText:="Download stories", ControlToolTip:="Download stories"), PXML, ControlNumber(11)> <PropertyOption(ControlText:="Download stories", ControlToolTip:="Download stories"), PXML, ControlNumber(11)>
Friend ReadOnly Property DownloadStories As PropertyValue 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 Friend ReadOnly Property DownloadTagged As PropertyValue
#End Region #End Region
#Region "429 bypass" #Region "429 bypass"
@@ -259,6 +263,7 @@ Namespace API.Instagram
DownloadTimeline = New PropertyValue(True) DownloadTimeline = New PropertyValue(True)
DownloadStories = New PropertyValue(True) DownloadStories = New PropertyValue(True)
DownloadStoriesUser = New PropertyValue(True)
DownloadTagged = New PropertyValue(False) DownloadTagged = New PropertyValue(False)
RequestsWaitTimer = New PropertyValue(1000) RequestsWaitTimer = New PropertyValue(1000)
@@ -270,6 +275,7 @@ Namespace API.Instagram
GetTimeline = New PropertyValue(True) GetTimeline = New PropertyValue(True)
GetStories = New PropertyValue(False) GetStories = New PropertyValue(False)
GetStoriesUser = New PropertyValue(False)
GetTagged = New PropertyValue(False) GetTagged = New PropertyValue(False)
TaggedNotifyLimit = New PropertyValue(200) TaggedNotifyLimit = New PropertyValue(200)
TaggedNotifyLimitProvider = New TaggedNotifyLimitChecker TaggedNotifyLimitProvider = New TaggedNotifyLimitChecker

View File

@@ -24,6 +24,7 @@ Namespace API.Instagram
Private Const Name_FirstLoadingDone As String = "FirstLoadingDone" Private Const Name_FirstLoadingDone As String = "FirstLoadingDone"
Private Const Name_GetTimeline As String = "GetTimeline" Private Const Name_GetTimeline As String = "GetTimeline"
Private Const Name_GetStories As String = "GetStories" Private Const Name_GetStories As String = "GetStories"
Private Const Name_GetStoriesUser As String = "GetStoriesUser"
Private Const Name_GetTagged As String = "GetTaggedData" Private Const Name_GetTagged As String = "GetTaggedData"
Private Const Name_TaggedChecked As String = "TaggedChecked" Private Const Name_TaggedChecked As String = "TaggedChecked"
Private Const Name_NameTrue As String = "NameTrue" Private Const Name_NameTrue As String = "NameTrue"
@@ -75,6 +76,7 @@ Namespace API.Instagram
Private FirstLoadingDone As Boolean = False Private FirstLoadingDone As Boolean = False
Friend Property GetTimeline As Boolean = True Friend Property GetTimeline As Boolean = True
Friend Property GetStories As Boolean Friend Property GetStories As Boolean
Friend Property GetStoriesUser As Boolean
Friend Property GetTaggedData As Boolean Friend Property GetTaggedData As Boolean
Private _NameTrue As String = String.Empty Private _NameTrue As String = String.Empty
Private ReadOnly Property NameTrue As String Private ReadOnly Property NameTrue As String
@@ -84,6 +86,31 @@ Namespace API.Instagram
End Property End Property
Private UserNameRequested As Boolean = False Private UserNameRequested As Boolean = False
#End Region #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" #Region "Exchange options"
Friend Overrides Function ExchangeOptionsGet() As Object Friend Overrides Function ExchangeOptionsGet() As Object
Return New EditorExchangeOptions(Me) Return New EditorExchangeOptions(Me)
@@ -93,37 +120,17 @@ Namespace API.Instagram
With DirectCast(Obj, EditorExchangeOptions) With DirectCast(Obj, EditorExchangeOptions)
GetTimeline = .GetTimeline GetTimeline = .GetTimeline
GetStories = .GetStories GetStories = .GetStories
GetStoriesUser = .GetStoriesUser
GetTaggedData = .GetTagged GetTaggedData = .GetTagged
End With End With
End If End If
End Sub End Sub
#End Region #End Region
#Region "Initializer, loader" #Region "Initializer"
Friend Sub New() Friend Sub New()
PostsKVIDs = New List(Of PostKV) PostsKVIDs = New List(Of PostKV)
PostsToReparse = New List(Of PostKV) PostsToReparse = New List(Of PostKV)
End Sub 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 #End Region
#Region "Download data" #Region "Download data"
Private E560Thrown As Boolean = False Private E560Thrown As Boolean = False
@@ -234,6 +241,7 @@ Namespace API.Instagram
If FirstLoadingDone Then LastCursor = String.Empty If FirstLoadingDone Then LastCursor = String.Empty
If Not IsSavedPosts AndAlso MySiteSettings.BaseAuthExists() Then 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.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() 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 End If
If WaitNotificationMode = WNM.SkipTemp Or WaitNotificationMode = WNM.SkipCurrent Then WaitNotificationMode = WNM.Notify 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) Private Sub Responser_ResponseReceived(ByVal Sender As Object, ByVal e As EventArguments.WebDataResponse)
Declarations.UpdateResponser(e, Responser) Declarations.UpdateResponser(e, Responser)
End Sub 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 StoriesFolder As String = "Stories"
Private Const TaggedFolder As String = "Tagged" Private Const TaggedFolder As String = "Tagged"
#Region "429 bypass" #Region "429 bypass"
@@ -455,7 +463,7 @@ Namespace API.Instagram
ThrowAny(Token) ThrowAny(Token)
End If End If
If StoriesList.ListExists Then If StoriesList.ListExists Then
GetStoriesData(StoriesList, Token) GetStoriesData(StoriesList, False, Token)
MySiteSettings.TooManyRequests(False) MySiteSettings.TooManyRequests(False)
RequestsCount += 1 RequestsCount += 1
End If End If
@@ -464,6 +472,11 @@ Namespace API.Instagram
Else Else
Throw New ExitException Throw New ExitException
End If End If
Case Sections.UserStories
GetStoriesData(Nothing, True, Token)
MySiteSettings.TooManyRequests(False)
RequestsCount += 1
Throw New ExitException
End Select End Select
'Get response 'Get response
@@ -624,10 +637,8 @@ Namespace API.Instagram
NextCursor = .Value("next_max_id") NextCursor = .Value("next_max_id")
If .Contains("items") Then nodes = (From ee As EContainer In .Item("items") Where ee.Count > 0 Select ee(0)) If .Contains("items") Then nodes = (From ee As EContainer In .Item("items") Where ee.Count > 0 Select ee(0))
End With End With
If nodes.ListExists Then If nodes.ListExists AndAlso DefaultParser(nodes, Sections.SavedPosts, Token) AndAlso
DefaultParser(nodes, Sections.SavedPosts, Token) HasNextPage AndAlso Not NextCursor.IsEmptyString Then SavedPostsDownload(NextCursor, Token)
If HasNextPage And Not NextCursor.IsEmptyString Then SavedPostsDownload(NextCursor, Token)
End If
End If End If
End Using End Using
End If End If
@@ -854,17 +865,21 @@ Namespace API.Instagram
End Function End Function
#End Region #End Region
#Region "Pinned stories" #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}" 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 qStr$, r$, sFolder$, storyID$, pid$
Dim i% = -1 Dim i% = -1
Dim jj As EContainer, s As EContainer Dim jj As EContainer, s As EContainer
ThrowAny(Token) ThrowAny(Token)
If StoriesList.ListExists Then If StoriesList.ListExists Or GetUserStory Then
tmpList = StoriesList.Take(5) If Not GetUserStory Then tmpList = StoriesList.Take(5)
If tmpList.ListExists Then If tmpList.ListExists Or GetUserStory Then
qStr = String.Format(ReqUrl, tmpList.Select(Function(q) $"reel_ids=highlight:{q}").ListToString("&")) 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) r = Responser.GetResponse(qStr,, EDP.ThrowException)
ThrowAny(Token) ThrowAny(Token)
If Not r.IsEmptyString Then If Not r.IsEmptyString Then
@@ -876,9 +891,13 @@ Namespace API.Instagram
i += 1 i += 1
sFolder = jj.Value("title").StringRemoveWinForbiddenSymbols sFolder = jj.Value("title").StringRemoveWinForbiddenSymbols
storyID = jj.Value("id").Replace("highlight:", String.Empty) storyID = jj.Value("id").Replace("highlight:", String.Empty)
If sFolder.IsEmptyString Then sFolder = $"Story_{storyID}" If GetUserStory Then
If sFolder.IsEmptyString Then sFolder = $"Story_{i}" sFolder = $"{StoriesFolder} (user)"
sFolder = $"{StoriesFolder}\{sFolder}" 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 &= ":" If Not storyID.IsEmptyString Then storyID &= ":"
With jj("items").XmlIfNothing With jj("items").XmlIfNothing
If .Count > 0 Then If .Count > 0 Then
@@ -896,7 +915,7 @@ Namespace API.Instagram
End If End If
End Using End Using
End If End If
StoriesList.RemoveRange(0, tmpList.Count) If Not GetUserStory Then StoriesList.RemoveRange(0, tmpList.Count)
End If End If
End If End If
End Sub End Sub
@@ -933,15 +952,15 @@ Namespace API.Instagram
''' </summary> ''' </summary>
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,
Optional ByVal s As Object = Nothing) As Integer 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 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 HasError = True
MyMainLOG = $"Instagram credentials have expired [{CInt(Responser.StatusCode)}]: {ToStringForLog()} [{s}]" MyMainLOG = $"Instagram credentials have expired [{CInt(Responser.StatusCode)}]: {ToStringForLog()} [{s}]"
DisableSection(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 Return 3
ElseIf Responser.StatusCode = 429 Then ElseIf Responser.StatusCode = 429 Then '429
With MySiteSettings With MySiteSettings
Dim WaiterExists As Boolean = .LastApplyingValue.HasValue Dim WaiterExists As Boolean = .LastApplyingValue.HasValue
.TooManyRequests(True) .TooManyRequests(True)
@@ -950,10 +969,10 @@ Namespace API.Instagram
Caught429 = True Caught429 = True
MyMainLOG = $"Number of requests before error 429: {RequestsCount}" MyMainLOG = $"Number of requests before error 429: {RequestsCount}"
Return 1 Return 1
ElseIf Responser.StatusCode = 560 Then ElseIf Responser.StatusCode = 560 Or Responser.StatusCode = HttpStatusCode.InternalServerError Then '560, 500
MySiteSettings.SkipUntilNextSession = True MySiteSettings.SkipUntilNextSession = True
Else 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) DisableSection(s)
If Not FromPE Then LogError(ex, Message) : HasError = True If Not FromPE Then LogError(ex, Message) : HasError = True
Return 0 Return 0
@@ -965,6 +984,10 @@ Namespace API.Instagram
Dim s As Sections = DirectCast(Section, Sections) Dim s As Sections = DirectCast(Section, Sections)
Select Case s Select Case s
Case Sections.Timeline : MySiteSettings.DownloadTimeline.Value = False 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 Case Else : MySiteSettings.DownloadTagged.Value = False
End Select End Select
MyMainLOG = $"[{s}] downloading is disabled until you update your credentials".ToUpper MyMainLOG = $"[{s}] downloading is disabled until you update your credentials".ToUpper

View File

@@ -104,12 +104,45 @@ Namespace API.Reddit
Return New UserData Return New UserData
End Function End Function
#End Region #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 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 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 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
____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 Try
Dim trueValue As Boolean = Not What = Download.SavedPosts OrElse (Responser.CookiesExists And ACheck(SavedPostsUserName.Value)) Dim trueValue As Boolean = Not What = Download.SavedPosts OrElse (Responser.CookiesExists And ACheck(SavedPostsUserName.Value))
If Not trueValue Then Return False If Not trueValue Then Return False
@@ -141,6 +174,11 @@ Namespace API.Reddit
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
____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

@@ -507,8 +507,9 @@ Namespace API
.Temporary = Temporary .Temporary = Temporary
.Favorite = Favorite .Favorite = Favorite
.ReadyForDownload = ReadyForDownload .ReadyForDownload = ReadyForDownload
ConsolidateLabels() ConsolidateLabels(_Item)
ConsolidateScripts() ConsolidateScripts()
ConsolidateColors(_Item)
.UpdateUserInformation() .UpdateUserInformation()
End If End If
MainFrameObj.ImageHandler(_Item, False) MainFrameObj.ImageHandler(_Item, False)
@@ -546,12 +547,22 @@ Namespace API
Catch ex As Exception Catch ex As Exception
End Try End Try
End Sub End Sub
Private Sub ConsolidateLabels() Private Sub ConsolidateLabels(ByVal Destination As UserDataBase)
UpdateLabels(Me, ListAddList(Nothing, Labels.ListWithRemove(SpecialLabels)), 1, True) UpdateLabels(If(Destination, Me), ListAddList(Nothing, Labels.ListWithRemove(SpecialLabels)), 1, True)
End Sub End Sub
Private Sub ConsolidateScripts() Private Sub ConsolidateScripts()
If Count > 1 AndAlso ScriptUse Then Collections.ForEach(Sub(c) c.ScriptUse = True) If Count > 1 AndAlso ScriptUse Then Collections.ForEach(Sub(c) c.ScriptUse = True)
End Sub 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 #End Region
#Region "Move, Merge" #Region "Move, Merge"
Friend Overrides Function MoveFiles(ByVal __CollectionName As String, ByVal __SpecialCollectionPath As SFile) As Boolean Friend Overrides Function MoveFiles(ByVal __CollectionName As String, ByVal __SpecialCollectionPath As SFile) As Boolean

View File

@@ -82,7 +82,7 @@ Namespace API.YouTube
Dim isMusic As Boolean = False Dim isMusic As Boolean = False
Dim id$ = String.Empty Dim id$ = String.Empty
Dim isChannelUser As Boolean = False 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 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)}") Return New ExchangeOptions(Site, $"{id}@{CInt(t) + IIf(isMusic, UserMedia.Types.Audio, 0) + IIf(isChannelUser, ChannelUserInt, 0)}")
End If End If

View File

@@ -15,7 +15,7 @@ Imports PersonalUtilities.Tools
Imports PersonalUtilities.Tools.Notifications Imports PersonalUtilities.Tools.Notifications
Namespace DownloadObjects Namespace DownloadObjects
Friend Class AutoDownloader : Inherits GroupParameters : Implements IIndexable, IEContainerProvider Friend Class AutoDownloader : Inherits GroupParameters : Implements IIndexable, IEContainerProvider
Friend Event PauseDisabled() Friend Event PauseChanged(ByVal Value As PauseModes)
Friend Enum Modes As Integer Friend Enum Modes As Integer
None = 0 None = 0
[Default] = 1 [Default] = 1
@@ -412,6 +412,7 @@ Namespace DownloadObjects
Case PauseModes.Until : _PauseValue = DateLimit Case PauseModes.Until : _PauseValue = DateLimit
Case Else : _PauseValue = Nothing Case Else : _PauseValue = Nothing
End Select End Select
RaiseEvent PauseChanged(p)
End Set End Set
End Property End Property
Private ReadOnly Property IsPaused As Boolean Private ReadOnly Property IsPaused As Boolean
@@ -423,7 +424,7 @@ Namespace DownloadObjects
Else Else
_Pause = PauseModes.Disabled _Pause = PauseModes.Disabled
_PauseValue = Nothing _PauseValue = Nothing
RaiseEvent PauseDisabled() RaiseEvent PauseChanged(_Pause)
Return False Return False
End If End If
Else Else

View File

@@ -92,34 +92,39 @@ Namespace DownloadObjects
BTT_PAUSE_H6.Click, BTT_PAUSE_H12.Click, BTT_PAUSE_H6.Click, BTT_PAUSE_H12.Click,
BTT_PAUSE_UNTIL.Click, BTT_PAUSE_UNLIMITED.Click, BTT_PAUSE_UNTIL.Click, BTT_PAUSE_UNLIMITED.Click,
BTT_PAUSE_DISABLE.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))
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
If p > -10 Then Not Place = ButtonsPlace.Scheduler OrElse
Dim d As Date? = Nothing (Place = ButtonsPlace.Scheduler AndAlso PlanIndex = -1 AndAlso
Dim _SetPauseValue As Action = Sub() MsgBoxE({$"Do you want to turn {IIf(p = PauseModes.Disabled, "off", "on")} pause for all plans?", "Pause plan"},
If Place = ButtonsPlace.Scheduler And PlanIndex.ValueBetween(0, Settings.Automation.Count - 1) Then vbExclamation + vbYesNo) = vbYes)) Then
Settings.Automation(PlanIndex).Pause(d) = p Dim d As Date? = Nothing
ElseIf Not Place = ButtonsPlace.Scheduler Then Dim _SetPauseValue As Action = Sub()
Settings.Automation.Pause(d) = p If Place = ButtonsPlace.Scheduler And PlanIndex.ValueBetween(0, Settings.Automation.Count - 1) Then
End If Settings.Automation(PlanIndex).Pause(d) = p
End Sub ElseIf Not Place = ButtonsPlace.Scheduler Or Place = ButtonsPlace.Scheduler And PlanIndex = -1 Then
If p = PauseModes.Until Then Settings.Automation.Pause(d) = p
Using f As New DateTimeSelectionForm(TimeSelectionModes.End + TimeSelectionModes.Date + TimeSelectionModes.Time, Settings.Design) End If
f.ShowDialog() End Sub
If f.DialogResult = DialogResult.OK Then d = f.MyDateEnd If p = PauseModes.Until Then
End Using Using f As New DateTimeSelectionForm(TimeSelectionModes.End + TimeSelectionModes.Date + TimeSelectionModes.Time, Settings.Design)
If d.HasValue Then _SetPauseValue.Invoke f.ShowDialog()
Else If f.DialogResult = DialogResult.OK Then d = f.MyDateEnd
_SetPauseValue.Invoke End Using
End If If d.HasValue Then _SetPauseValue.Invoke
UpdatePauseButtons() Else
_SetPauseValue.Invoke
End If 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) MsgBoxE({"The plan to be paused is not selected", "Pause plan"}, vbExclamation)
End If End If
End Sub End Sub
#End Region #End Region
#Region "Update buttons" #Region "Update buttons"
Friend Overloads Sub UpdatePauseButtons_Handler(ByVal Value As PauseModes)
UpdatePauseButtons()
End Sub
Friend Overloads Sub UpdatePauseButtons() Handles TrayButtons.Updating Friend Overloads Sub UpdatePauseButtons() Handles TrayButtons.Updating
UpdatePauseButtons(True) UpdatePauseButtons(True)
End Sub End Sub

View File

@@ -14,12 +14,14 @@ Imports PauseModes = SCrawler.DownloadObjects.AutoDownloader.PauseModes
Namespace DownloadObjects Namespace DownloadObjects
Friend Class Scheduler : Implements IEnumerable(Of AutoDownloader), IMyEnumerator(Of AutoDownloader), IDisposable Friend Class Scheduler : Implements IEnumerable(Of AutoDownloader), IMyEnumerator(Of AutoDownloader), IDisposable
Friend Const Name_Plan As String = "Plan" Friend Const Name_Plan As String = "Plan"
Friend Event PauseDisabled As AutoDownloader.PauseDisabledEventHandler Friend Event PauseChanged As AutoDownloader.PauseChangedEventHandler
Private Sub OnPauseDisabled() Private Sub OnPauseChanged(ByVal Value As PauseModes)
RaiseEvent PauseDisabled() RaiseEvent PauseChanged(Pause)
End Sub End Sub
Private ReadOnly Plans As List(Of AutoDownloader) 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 PlanWorking As Predicate(Of AutoDownloader) = Function(Plan) Plan.Working
Private ReadOnly PlanDownloading As Predicate(Of AutoDownloader) = Function(Plan) Plan.Downloading 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)) Private ReadOnly PlansWaiter As Action(Of Predicate(Of AutoDownloader)) = Sub(ByVal Predicate As Predicate(Of AutoDownloader))
@@ -27,20 +29,9 @@ Namespace DownloadObjects
End Sub End Sub
Friend Sub New() Friend Sub New()
Plans = New List(Of AutoDownloader) Plans = New List(Of AutoDownloader)
If File.Exists Then File = Settings.AutomationFile.Value.IfNullOrEmpty(FileDefault)
Using x As New XmlFile(File,, False) With {.AllowSameNames = True} If Not File.Exists Then File = FileDefault
x.LoadData() Reset(File, True)
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
End Sub End Sub
Default Friend ReadOnly Property Item(ByVal Index As Integer) As AutoDownloader Implements IMyEnumerator(Of AutoDownloader).MyEnumeratorObject Default Friend ReadOnly Property Item(ByVal Index As Integer) As AutoDownloader Implements IMyEnumerator(Of AutoDownloader).MyEnumeratorObject
Get Get
@@ -62,7 +53,7 @@ Namespace DownloadObjects
End Function End Function
Friend Sub Add(ByVal Plan As AutoDownloader) Friend Sub Add(ByVal Plan As AutoDownloader)
Plan.Source = Me Plan.Source = Me
AddHandler Plan.PauseDisabled, AddressOf OnPauseDisabled AddHandler Plan.PauseChanged, AddressOf OnPauseChanged
Plans.Add(Plan) Plans.Add(Plan)
Plans.ListReindex Plans.ListReindex
Update() Update()
@@ -96,6 +87,39 @@ Namespace DownloadObjects
Catch Catch
End Try End Try
End Sub 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" #Region "Groups Support"
Friend Sub GROUPS_Updated(ByVal Sender As DownloadGroup) Friend Sub GROUPS_Updated(ByVal Sender As DownloadGroup)
If Count > 0 Then Plans.ForEach(Sub(p) p.GROUPS_Updated(Sender)) If Count > 0 Then Plans.ForEach(Sub(p) p.GROUPS_Updated(Sender))

View File

@@ -63,7 +63,7 @@ Namespace DownloadObjects
Me.KeyPreview = True Me.KeyPreview = True
Me.MinimumSize = New System.Drawing.Size(430, 380) Me.MinimumSize = New System.Drawing.Size(430, 380)
Me.Name = "SchedulerEditorForm" Me.Name = "SchedulerEditorForm"
Me.ShowIcon = False Me.ShowIcon = True
Me.ShowInTaskbar = False Me.ShowInTaskbar = False
Me.Text = "Scheduler" Me.Text = "Scheduler"
CONTAINER_MAIN.ContentPanel.ResumeLayout(False) CONTAINER_MAIN.ContentPanel.ResumeLayout(False)

View File

@@ -8,11 +8,14 @@
' but WITHOUT ANY WARRANTY ' but WITHOUT ANY WARRANTY
Imports PersonalUtilities.Forms Imports PersonalUtilities.Forms
Imports PersonalUtilities.Forms.Toolbars Imports PersonalUtilities.Forms.Toolbars
Imports PersonalUtilities.Tools
Imports ECI = PersonalUtilities.Forms.Toolbars.EditToolbar.ControlItem Imports ECI = PersonalUtilities.Forms.Toolbars.EditToolbar.ControlItem
Imports ADB = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons
Namespace DownloadObjects Namespace DownloadObjects
Friend Class SchedulerEditorForm Friend Class SchedulerEditorForm
#Region "Declarations" #Region "Declarations"
Private WithEvents MyDefs As DefaultFormOptions Private WithEvents MyDefs As DefaultFormOptions
Private WithEvents BTT_SETTINGS As ToolStripButton
Private WithEvents BTT_CLONE As ToolStripButton Private WithEvents BTT_CLONE As ToolStripButton
Private ReadOnly MENU_SKIP As ToolStripDropDownButton Private ReadOnly MENU_SKIP As ToolStripDropDownButton
Private WithEvents BTT_SKIP As ToolStripMenuItem Private WithEvents BTT_SKIP As ToolStripMenuItem
@@ -28,6 +31,13 @@ Namespace DownloadObjects
Friend Sub New() Friend Sub New()
InitializeComponent() InitializeComponent()
MyDefs = New DefaultFormOptions(Me, Settings.Design) 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 { BTT_CLONE = New ToolStripButton With {
.Text = "Clone", .Text = "Clone",
.DisplayStyle = ToolStripItemDisplayStyle.ImageAndText, .DisplayStyle = ToolStripItemDisplayStyle.ImageAndText,
@@ -89,13 +99,15 @@ Namespace DownloadObjects
} }
PauseArr = New AutoDownloaderPauseButtons(AutoDownloaderPauseButtons.ButtonsPlace.Scheduler) With { PauseArr = New AutoDownloaderPauseButtons(AutoDownloaderPauseButtons.ButtonsPlace.Scheduler) With {
.MainFrameButtonsInstance = MainFrameObj.PauseButtons} .MainFrameButtonsInstance = MainFrameObj.PauseButtons}
Icon = ImageRenderer.GetIcon(My.Resources.ScriptPic_32, EDP.ReturnValue)
End Sub End Sub
#End Region #End Region
#Region "Form handlers" #Region "Form handlers"
Private Sub SchedulerEditorForm_Load(sender As Object, e As EventArgs) Handles Me.Load Private Sub SchedulerEditorForm_Load(sender As Object, e As EventArgs) Handles Me.Load
With MyDefs With MyDefs
.MyViewInitialize() .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) PauseArr.AddButtons(BTT_PAUSE, .MyEditToolbar.ToolStrip)
Refill() Refill()
.EndLoaderOperations(False) .EndLoaderOperations(False)
@@ -186,7 +198,80 @@ Namespace DownloadObjects
Edit() Edit()
End Sub End Sub
#End Region #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 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 If _LatestSelected.ValueBetween(0, LIST_PLANS.Items.Count - 1) Then
With Settings.Automation(_LatestSelected) : .Start(.IsNewPlan) : End With With Settings.Automation(_LatestSelected) : .Start(.IsNewPlan) : End With

View File

@@ -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", "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.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.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)")>

View File

@@ -122,7 +122,7 @@ Public Class MainFrame
.Automation = New Scheduler .Automation = New Scheduler
AddHandler .Groups.Updated, AddressOf .Automation.GROUPS_Updated AddHandler .Groups.Updated, AddressOf .Automation.GROUPS_Updated
AddHandler .Groups.Deleted, AddressOf .Automation.GROUPS_Deleted 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() If .Automation.Count > 0 Then .Labels.AddRange(.Automation.GetGroupsLabels, False) : .Labels.Update()
_UFinit = False _UFinit = False
Await .Automation.Start(True) Await .Automation.Start(True)

View File

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

View File

@@ -130,6 +130,7 @@ Friend Class SettingsCLS : Implements IDownloaderSettings, IDisposable
Friend ReadOnly Property DownloadLocations As STDownloader.DownloadLocationsCollection Friend ReadOnly Property DownloadLocations As STDownloader.DownloadLocationsCollection
Friend ReadOnly Property GlobalLocations As STDownloader.DownloadLocationsCollection Friend ReadOnly Property GlobalLocations As STDownloader.DownloadLocationsCollection
Friend Property Automation As Scheduler Friend Property Automation As Scheduler
Friend ReadOnly Property AutomationFile As XMLValue(Of SFile)
Friend ReadOnly Property BlackList As List(Of UserBan) Friend ReadOnly Property BlackList As List(Of UserBan)
Private ReadOnly BlackListFile As SFile = $"{SettingsFolderName}\BlackList.txt" Private ReadOnly BlackListFile As SFile = $"{SettingsFolderName}\BlackList.txt"
Private ReadOnly UsersSettingsFile As SFile = $"{SettingsFolderName}\Users.xml" 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) SeparateVideoFolder = New XMLValue(Of Boolean)("SeparateVideoFolder", True, MyXML)
CollectionsPath = New XMLValue(Of String)("CollectionsPath", CollectionsFolderName, MyXML) CollectionsPath = New XMLValue(Of String)("CollectionsPath", CollectionsFolderName, MyXML)
AutomationFile = New XMLValue(Of SFile)("AutomationFile",, MyXML)
UserAgent = New XMLValue(Of String)("UserAgent",, MyXML) UserAgent = New XMLValue(Of String)("UserAgent",, MyXML)
If Not UserAgent.IsEmptyString Then DefaultUserAgent = UserAgent If Not UserAgent.IsEmptyString Then DefaultUserAgent = UserAgent