From 7d169acebc91ad777dddea9b628936ec5a199748 Mon Sep 17 00:00:00 2001 From: Andy <88590076+AAndyProgram@users.noreply.github.com> Date: Sun, 23 Oct 2022 16:39:01 +0300 Subject: [PATCH] 2022.10.23.0 PluginProvider: added 'DoNotUse' attribute. Channels: copying a channel to the 'ChannelsDeleted' folder before deleting. Twitter: updated status codes. AutoDownloader: removed base parameters initialization; updated 'ToEContainer' function; 'StartupDelay' default value = 1; added IIndexable; fixed NextExecutionDate; added task delay based on index and tasks count; updated user selections algorithms. MainFrame: coloring the button 'Download All' depending on the pause; updated user selections algorithms. DownloadGroups: added LabelsExcluded, Sites and SitesExcluded; updated initialization; updated ToEContainer function; updated user selections algorithms IGroup, GroupParameters: added LabelsExcluded, Sites and SitesExcluded; added Import and Export functions Removed TrayIcon notifications. All notifications are now ToastNotifications. SettingsHost: 'DoNotUse' attribute Settings: added 'GetUsers' predicate function --- Changelog.md | 19 +++ ProgramScreenshots/GroupCreating.png | Bin 7626 -> 9394 bytes ProgramScreenshots/SettingsAutoDownloader.png | Bin 16336 -> 18120 bytes ProgramScreenshots/SettingsSiteRedGifs.png | Bin 14501 -> 14475 bytes .../Attributes/Attributes.vb | 3 + .../My Project/AssemblyInfo.vb | 4 +- SCrawler/API/Base/UserDataBase.vb | 5 +- SCrawler/API/Instagram/UserData.vb | 2 +- SCrawler/API/Reddit/Channel.vb | 15 +- SCrawler/API/Reddit/ChannelsCollection.vb | 5 + SCrawler/API/Reddit/SiteSettings.vb | 13 +- SCrawler/API/Reddit/UserData.vb | 31 ++-- SCrawler/API/Redgifs/SiteSettings.vb | 75 +++++++++- SCrawler/API/Redgifs/UserData.vb | 48 ++++-- SCrawler/API/Twitter/UserData.vb | 26 ++-- SCrawler/Channels/ChannelsStatsForm.vb | 1 + SCrawler/Content/Icons/TagIcon_32.ico | Bin 0 -> 5430 bytes .../Download/Automation/AutoDownloader.vb | 116 ++++++++------- .../AutoDownloaderEditorForm.Designer.vb | 48 +++--- .../Automation/AutoDownloaderEditorForm.vb | 4 +- .../Automation/AutoDownloaderPauseButtons.vb | 13 +- SCrawler/Download/Automation/Scheduler.vb | 26 ++-- SCrawler/Download/Groups/DownloadGroup.vb | 60 +++++--- .../Groups/DownloadGroupCollection.vb | 23 ++- SCrawler/Download/Groups/GroupDefaults.vb | 140 +++++++++++++----- .../Groups/GroupEditorForm.Designer.vb | 18 ++- SCrawler/Download/Groups/GroupParameters.vb | 46 +++++- SCrawler/Editors/LabelsForm.Designer.vb | 2 +- SCrawler/Editors/LabelsForm.vb | 6 +- .../Editors/SiteSelectionForm.Designer.vb | 2 +- SCrawler/MainFrame.Designer.vb | 2 +- SCrawler/MainFrame.vb | 45 ++---- SCrawler/MainFrameObjects.vb | 28 ++-- SCrawler/MainMod.vb | 1 + SCrawler/My Project/AssemblyInfo.vb | 4 +- SCrawler/My Project/Resources.Designer.vb | 10 ++ SCrawler/My Project/Resources.resx | 3 + .../PluginsEnvironment/Hosts/SettingsHost.vb | 2 +- SCrawler/SCrawler.vbproj | 1 + SCrawler/SettingsCLS.vb | 18 +++ 40 files changed, 601 insertions(+), 264 deletions(-) create mode 100644 SCrawler/Content/Icons/TagIcon_32.ico diff --git a/Changelog.md b/Changelog.md index a60e6a0..406f621 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,22 @@ +# 2022.10.23.0 + +*2022-10-23* + +- Added + - RedGifs token Auto-Renewal + - Download groups: ability to select sites + - Download groups: ability to exclude labels and sites + - AutoDownloader: ability to exclude labels and sites in ```All```, ```Default``` and ```Specified``` modes + - The ```Download All``` button turns blue when pause is enabled + - Updated Twitter status codes + - Minor improvements +- Fixed + - Updated Twitter status codes + - AutoDownloader: incorrect next run date in scheduler task information + - AutoDownloader: minor bugs + - (Issue #69) **RedGifs data is not downloading**. Requires token. + - Minor bugs + # 2022.10.18.0 *2022-10-18* diff --git a/ProgramScreenshots/GroupCreating.png b/ProgramScreenshots/GroupCreating.png index eb476e35321458b9f12ccc7503792aa033432e82..5bf6886b8356b169f97075908f60f9605cb85c4b 100644 GIT binary patch literal 9394 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 literal 16336 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 R?@34$<N>@ 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 U`o|#UV%!}S1x-kW-19g83%t+oXaE2J literal 14501 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 diff --git a/SCrawler.PluginProvider/Attributes/Attributes.vb b/SCrawler.PluginProvider/Attributes/Attributes.vb index 9d2e3d7..f0f8389 100644 --- a/SCrawler.PluginProvider/Attributes/Attributes.vb +++ b/SCrawler.PluginProvider/Attributes/Attributes.vb @@ -53,6 +53,9 @@ Namespace Plugin.Attributes ElementName = XMLElementName End Sub End Class + ''' Attribute to disable some properties for host use + Public NotInheritable Class DoNotUse : Inherits Attribute + End Class ''' Special property updater Public NotInheritable Class PropertyUpdater : Inherits Attribute Public ReadOnly Name As String diff --git a/SCrawler.PluginProvider/My Project/AssemblyInfo.vb b/SCrawler.PluginProvider/My Project/AssemblyInfo.vb index 89259fb..2855725 100644 --- a/SCrawler.PluginProvider/My Project/AssemblyInfo.vb +++ b/SCrawler.PluginProvider/My Project/AssemblyInfo.vb @@ -32,6 +32,6 @@ Imports System.Runtime.InteropServices ' by using the '*' as shown below: ' - - + + diff --git a/SCrawler/API/Base/UserDataBase.vb b/SCrawler/API/Base/UserDataBase.vb index ee08159..14217c8 100644 --- a/SCrawler/API/Base/UserDataBase.vb +++ b/SCrawler/API/Base/UserDataBase.vb @@ -817,6 +817,9 @@ BlockNullPicture: If Not Responser Is Nothing Then Responser.Dispose() Responser = New Response If Not HOST.Responser Is Nothing Then Responser.Copy(HOST.Responser) + 'TODO: remove + Responser.DecodersError = New ErrorsDescriber(EDP.SendInLog + EDP.ReturnValue) With { + .DeclaredMessage = New MMessage($"SymbolsConverter error: [{ToStringForLog()}]", ToStringForLog())} Dim UpPic As Boolean = Settings.ViewModeIsPicture AndAlso GetPicture(Of Image)(False) Is Nothing Dim sEnvir() As Boolean = {UserExists, UserSuspended} @@ -976,7 +979,7 @@ BlockNullPicture: Dim v As UserMedia Using w As New OptionalWebClient(Me) - If vsf Then SFileShares.SFileExists($"{MyDir}\Video\", SFO.Path) + If vsf Then CSFileP($"{MyDir}\Video\").Exists(SFO.Path) Progress.Maximum += _ContentNew.Count For i = 0 To _ContentNew.Count - 1 ThrowAny(Token) diff --git a/SCrawler/API/Instagram/UserData.vb b/SCrawler/API/Instagram/UserData.vb index 04b9e5a..82c4ef2 100644 --- a/SCrawler/API/Instagram/UserData.vb +++ b/SCrawler/API/Instagram/UserData.vb @@ -702,7 +702,7 @@ Namespace API.Instagram UserExists = False ElseIf Responser.StatusCode = HttpStatusCode.BadRequest Then HasError = True - MyMainLOG = $"Instagram credentials have expired [{CInt(Responser.StatusCode)}]: {ToString()} [{s}]" + MyMainLOG = $"Instagram credentials have expired [{CInt(Responser.StatusCode)}]: {ToStringForLog()} [{s}]" DisableSection(s) ElseIf Responser.StatusCode = HttpStatusCode.Forbidden And s = Sections.Tagged Then Return 3 diff --git a/SCrawler/API/Reddit/Channel.vb b/SCrawler/API/Reddit/Channel.vb index ccb4078..fcc9d25 100644 --- a/SCrawler/API/Reddit/Channel.vb +++ b/SCrawler/API/Reddit/Channel.vb @@ -236,8 +236,19 @@ Namespace API.Reddit Return If(Name.IsEmptyString, ID, Name) End Function Friend Sub Delete() - File.Delete(, SFODelete.DeleteToRecycleBin) - FilePosts.Delete(, SFODelete.DeleteToRecycleBin) + Dim f As SFile = ChannelsCollection.ChannelsDeletedPath + With File + f.Name = .Name + f.Extension = .Extension + .Copy(f,, True, SFODelete.DeleteToRecycleBin) + .Delete(, SFODelete.DeleteToRecycleBin) + End With + With FilePosts + f.Name = .Name + f.Extension = .Extension + .Copy(f,, True, SFODelete.DeleteToRecycleBin) + .Delete(, SFODelete.DeleteToRecycleBin) + End With End Sub Friend Sub DownloadData(ByVal Token As CancellationToken, Optional ByVal SkipExists As Boolean = True, Optional ByVal p As MyProgress = Nothing) diff --git a/SCrawler/API/Reddit/ChannelsCollection.vb b/SCrawler/API/Reddit/ChannelsCollection.vb index cd4f776..799c496 100644 --- a/SCrawler/API/Reddit/ChannelsCollection.vb +++ b/SCrawler/API/Reddit/ChannelsCollection.vb @@ -17,6 +17,11 @@ Namespace API.Reddit Return $"{SettingsFolderName}\Channels\" End Get End Property + Friend Shared ReadOnly Property ChannelsDeletedPath As SFile + Get + Return $"{SettingsFolderName}\ChannelsDeleted\" + End Get + End Property Friend Shared ReadOnly Property ChannelsPathCache As SFile Get Return $"{Settings.GlobalPath.Value.PathWithSeparator}_CachedData\" diff --git a/SCrawler/API/Reddit/SiteSettings.vb b/SCrawler/API/Reddit/SiteSettings.vb index 1dadc94..a2bea8c 100644 --- a/SCrawler/API/Reddit/SiteSettings.vb +++ b/SCrawler/API/Reddit/SiteSettings.vb @@ -79,10 +79,15 @@ Namespace API.Reddit If Silent Then Return False Else - Return MsgBoxE({"Over the past hour, Reddit has received an average of " & - avg.NumToString(New ANumbers With {.FormatOptions = ANumbers.Options.GroupIntegral}) & " outage reports:" & vbCr & - dl.ListToString(vbCr) & vbCr & vbCr & - "Do you want to continue parsing Reddit data?", "There are outage reports on Reddit"}, vbYesNo) = vbYes + If MsgBoxE({"Over the past hour, Reddit has received an average of " & + avg.NumToString(New ANumbers With {.FormatOptions = ANumbers.Options.GroupIntegral}) & " outage reports:" & vbCr & + dl.ListToString(vbCr) & vbCr & vbCr & + "Do you want to continue parsing Reddit data?", "There are outage reports on Reddit"}, vbYesNo) = vbYes Then + DirectCast(Settings(RedGifs.RedGifsSiteKey).Source, RedGifs.SiteSettings).UpdateTokenIfRequired() + Return True + Else + Return False + End If End If End If End If diff --git a/SCrawler/API/Reddit/UserData.vb b/SCrawler/API/Reddit/UserData.vb index 368a9ab..dc1284e 100644 --- a/SCrawler/API/Reddit/UserData.vb +++ b/SCrawler/API/Reddit/UserData.vb @@ -793,7 +793,7 @@ Namespace API.Reddit End Function Dim m$ Using w As New WebClient - If vsf Then SFileShares.SFileExists($"{MyDir}\Video\", SFO.Path) + If vsf Then CSFileP($"{MyDir}\Video\").Exists(SFO.Path) Progress.Maximum += _ContentNew.Count For i = 0 To _ContentNew.Count - 1 ThrowAny(Token) @@ -850,7 +850,7 @@ Namespace API.Reddit End If If Not v.Type = UTypes.m3u8 Or Not f.IsEmptyString Then Select Case v.Type - Case UTypes.Picture : DownloadedPictures(False) += 1 + Case UTypes.Picture, UTypes.GIF : DownloadedPictures(False) += 1 Case UTypes.Video, UTypes.m3u8 : DownloadedVideos(False) += 1 End Select If Not IsChannel Or Not SaveToCache Then @@ -897,19 +897,20 @@ Namespace API.Reddit End Sub Protected Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False, Optional ByVal EObj As Object = Nothing) As Integer - If Responser.StatusCode = HttpStatusCode.NotFound Then - UserExists = False - ElseIf Responser.StatusCode = HttpStatusCode.Forbidden Then - UserSuspended = True - ElseIf Responser.StatusCode = HttpStatusCode.BadGateway Or - Responser.StatusCode = HttpStatusCode.ServiceUnavailable Then - MyMainLOG = $"[{CInt(Responser.StatusCode)}] Reddit is currently unavailable ({ToString()})" - ElseIf Responser.StatusCode = HttpStatusCode.GatewayTimeout Then - Return 1 - Else - If Not FromPE Then LogError(ex, Message) : HasError = True - Return 0 - End If + With Responser + If .StatusCode = HttpStatusCode.NotFound Then + UserExists = False + ElseIf .StatusCode = HttpStatusCode.Forbidden Then + UserSuspended = True + ElseIf .StatusCode = HttpStatusCode.BadGateway Or .StatusCode = HttpStatusCode.ServiceUnavailable Then + MyMainLOG = $"[{CInt(Responser.StatusCode)}] Reddit is currently unavailable ({ToString()})" + ElseIf .StatusCode = HttpStatusCode.GatewayTimeout Then + Return 1 + Else + If Not FromPE Then LogError(ex, Message) : HasError = True + Return 0 + End If + End With Return 1 End Function Protected Overrides Sub Dispose(ByVal disposing As Boolean) diff --git a/SCrawler/API/Redgifs/SiteSettings.vb b/SCrawler/API/Redgifs/SiteSettings.vb index a88f497..a963252 100644 --- a/SCrawler/API/Redgifs/SiteSettings.vb +++ b/SCrawler/API/Redgifs/SiteSettings.vb @@ -9,13 +9,16 @@ Imports SCrawler.API.Base Imports SCrawler.Plugin Imports SCrawler.Plugin.Attributes +Imports PersonalUtilities.Functions.XML Imports PersonalUtilities.Functions.RegularExpressions Imports PersonalUtilities.Tools.WEB +Imports PersonalUtilities.Tools.WebDocuments.JSON Imports UTypes = SCrawler.API.Base.UserMedia.Types Imports UStates = SCrawler.API.Base.UserMedia.States Namespace API.RedGifs Friend Class SiteSettings : Inherits SiteSettingsBase +#Region "Declarations" Friend Overrides ReadOnly Property Icon As Icon Get Return My.Resources.SiteResources.RedGifsIcon_32 @@ -28,7 +31,11 @@ Namespace API.RedGifs End Property Friend Property Token As PropertyValue + Friend Property TokenLastDateUpdated As PropertyValue + Friend ReadOnly Property NoCredentialsResponser As Response Private Const TokenName As String = "authorization" +#End Region +#Region "Initializer" Friend Sub New() MyBase.New(RedGifsSite, "redgifs.com") Dim t$ = String.Empty @@ -40,17 +47,83 @@ Namespace API.RedGifs If .Headers.Count > 0 AndAlso .Headers.ContainsKey(TokenName) Then t = .Headers(TokenName) If b Then .SaveSettings() End With + NoCredentialsResponser = New Response($"{SettingsFolderName}\Responser_{RedGifsSite}_NC.xml") With { + .CookiesEncryptKey = SettingsCLS.CookieEncryptKey, + .CookiesDomain = "redgifs.com" + } + With NoCredentialsResponser + If .File.Exists Then + .LoadSettings() + Else + .Cookies = New CookieKeeper(.CookiesDomain) With {.EncryptKey = SettingsCLS.CookieEncryptKey} + .SaveSettings() + End If + End With Token = New PropertyValue(t, GetType(String), Sub(v) UpdateResponse(v)) + TokenLastDateUpdated = New PropertyValue(Now.AddYears(-1), GetType(Date)) UrlPatternUser = "https://www.redgifs.com/users/{0}/" UserRegex = RParams.DMS("[htps:/]{7,8}.*?redgifs.com/users/([^/]+)", 1) ImageVideoContains = "redgifs" End Sub +#End Region +#Region "Response updater" Private Sub UpdateResponse(ByVal Value As String) With Responser.Headers If .Count = 0 OrElse Not .ContainsKey(TokenName) Then .Add(TokenName, Value) Else .Item(TokenName) = Value Responser.SaveSettings() End With End Sub +#End Region +#Region "Token updaters" + Friend Function UpdateTokenIfRequired() As Boolean + Dim d As Date? = AConvert(Of Date)(TokenLastDateUpdated.Value, AModes.Var, Nothing) + If Not d.HasValue OrElse d.Value < Now.AddDays(-1) Then + Return UpdateToken() + Else + Return True + End If + End Function + + Friend Function UpdateToken() As Boolean + Try + Dim r$ + Dim NewToken$ = String.Empty + Using resp As New Response : r = resp.GetResponse("https://api.redgifs.com/v2/auth/temporary",, EDP.ThrowException) : End Using + If Not r.IsEmptyString Then + Dim j As EContainer = JsonDocument.Parse(r) + If Not j Is Nothing Then + NewToken = j.Value("token") + j.Dispose() + End If + End If + If Not NewToken.IsEmptyString Then + Token.Value = $"Bearer {NewToken}" + TokenLastDateUpdated.Value = Now + Return True + Else + Return False + End If + Catch ex As Exception + Return ErrorsDescriber.Execute(EDP.SendInLog, ex, "[API.RedGifs.SiteSettings.UpdateToken]", False) + End Try + End Function +#End Region +#Region "Update settings" + Private _LastTokenValue As String = String.Empty + Friend Overrides Sub BeginEdit() + _LastTokenValue = AConvert(Of String)(Token.Value, AModes.Var, String.Empty) + MyBase.BeginEdit() + End Sub + Friend Overrides Sub Update() + Dim NewToken$ = AConvert(Of String)(Token.Value, AModes.Var, String.Empty) + If Not _LastTokenValue = NewToken Then TokenLastDateUpdated.Value = Now + MyBase.Update() + End Sub + Friend Overrides Sub EndEdit() + _LastTokenValue = String.Empty + MyBase.EndEdit() + End Sub +#End Region Friend Overrides Function GetInstance(ByVal What As ISiteSettings.Download) As IPluginContentProvider Return New UserData End Function @@ -84,7 +157,7 @@ Namespace API.RedGifs Return $"https://www.redgifs.com/watch/{PostID}" End Function Friend Overrides Function BaseAuthExists() As Boolean - Return If(Responser.Cookies?.Count, 0) > 0 AndAlso ACheck(Token.Value) + Return UpdateTokenIfRequired() AndAlso ACheck(Token.Value) End Function End Class End Namespace \ No newline at end of file diff --git a/SCrawler/API/Redgifs/UserData.vb b/SCrawler/API/Redgifs/UserData.vb index be6dbeb..1042d77 100644 --- a/SCrawler/API/Redgifs/UserData.vb +++ b/SCrawler/API/Redgifs/UserData.vb @@ -20,6 +20,11 @@ Namespace API.RedGifs Friend Const DataGone As HttpStatusCode = HttpStatusCode.Gone Private Const PostDataUrl As String = "https://api.redgifs.com/v2/gifs/{0}?views=yes&users=yes" #Region "Base declarations" + Private ReadOnly Property MySettings As SiteSettings + Get + Return DirectCast(HOST.Source, SiteSettings) + End Get + End Property Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean) End Sub #End Region @@ -29,15 +34,21 @@ Namespace API.RedGifs End Sub #End Region #Region "Download functions" + Private NoCredentialsResponser As Response Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken) - ReparseMissing(Token) - DownloadData(1, Token) + Try + NoCredentialsResponser = MySettings.NoCredentialsResponser.Copy + DownloadData(1, Token) + Finally + NoCredentialsResponser.Dispose() + End Try End Sub Private Overloads Sub DownloadData(ByVal Page As Integer, ByVal Token As CancellationToken) Dim URL$ = String.Empty Try - URL = $"https://api.redgifs.com/v2/users/{Name}/search?order=recent&page={Page}" - Dim r$ = Responser.DownloadString(URL, EDP.ThrowException) + Dim _page As Func(Of String) = Function() If(Page = 1, String.Empty, $"&page={Page}") + URL = $"https://api.redgifs.com/v2/users/{Name}/search?order=recent{_page.Invoke}" + Dim r$ = NoCredentialsResponser.GetResponse(URL,, EDP.ThrowException) Dim postDate$, postID$ Dim pTotal% = 0 If Not r.IsEmptyString Then @@ -59,7 +70,7 @@ Namespace API.RedGifs End If If pTotal > 0 And Page < pTotal Then DownloadData(Page + 1, Token) Catch ex As Exception - ProcessException(ex, Token, $"data downloading error [{URL}]") + ProcessException(ex, Token, $"data downloading error [{URL}]",, True) End Try End Sub #End Region @@ -129,7 +140,7 @@ Namespace API.RedGifs End If Catch dex As ObjectDisposedException When Disposed Catch ex As Exception - ProcessException(ex, Token, $"missing data downloading error") + ProcessException(ex, Token, $"missing data downloading error",, False) Finally If Not Disposed And rList.Count > 0 Then For i% = rList.Count - 1 To 0 Step -1 : _ContentList.RemoveAt(rList(i)) : Next @@ -192,9 +203,12 @@ Namespace API.RedGifs End If Return Nothing Catch ex As Exception - If Not Responser Is Nothing AndAlso Responser.Client.StatusCode = DataGone Then _ - Return New UserMedia With {.State = DataGone} - Return ErrorsDescriber.Execute(EDP.SendInLog, ex, $"[API.RedGifs.UserData.GetDataFromUrlId({URL})]", New UserMedia) + If Not Responser Is Nothing AndAlso (Responser.Client.StatusCode = DataGone Or Responser.Client.StatusCode = HttpStatusCode.NotFound) Then + Return New UserMedia With {.State = DataGone} + Else + Return ErrorsDescriber.Execute(EDP.SendInLog, ex, $"[API.RedGifs.UserData.GetDataFromUrlId({URL})]", + New UserMedia With {.State = UStates.Missing}) + End If End Try End Function #End Region @@ -218,8 +232,22 @@ Namespace API.RedGifs #Region "Exception" Protected Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False, Optional ByVal EObj As Object = Nothing) As Integer - If Responser.StatusCode = HttpStatusCode.NotFound Then + Dim IsNoCredentialsResponser As Boolean = AConvert(Of Boolean)(EObj, False) + Dim s As WebExceptionStatus = -1 + Dim sc As HttpStatusCode = -1 + If IsNoCredentialsResponser Then + If Not NoCredentialsResponser Is Nothing Then + s = NoCredentialsResponser.Status + sc = NoCredentialsResponser.StatusCode + End If + Else + s = Responser.Client.Status + sc = Responser.Client.StatusCode + End If + If sc = HttpStatusCode.NotFound Or s = DataGone Then UserExists = False + ElseIf sc = HttpStatusCode.Unauthorized Then + MyMainLOG = $"RedGifs credentials have expired [{CInt(sc)}]: {ToStringForLog()}" Else If Not FromPE Then LogError(ex, Message) : HasError = True Return 0 diff --git a/SCrawler/API/Twitter/UserData.vb b/SCrawler/API/Twitter/UserData.vb index 105ccc0..605de2e 100644 --- a/SCrawler/API/Twitter/UserData.vb +++ b/SCrawler/API/Twitter/UserData.vb @@ -355,18 +355,20 @@ Namespace API.Twitter #Region "Exception" Protected Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False, Optional ByVal EObj As Object = Nothing) As Integer - If Responser.StatusCode = HttpStatusCode.NotFound Then - UserExists = False - ElseIf Responser.StatusCode = HttpStatusCode.Unauthorized Then - UserSuspended = True - ElseIf Responser.StatusCode = HttpStatusCode.BadRequest Then - MyMainLOG = "Twitter has invalid credentials" - ElseIf Responser.StatusCode = HttpStatusCode.ServiceUnavailable Then - MyMainLOG = $"Twitter is currently unavailable ({ToString()})" - Else - If Not FromPE Then LogError(ex, Message) : HasError = True - Return 0 - End If + With Responser + If .StatusCode = HttpStatusCode.NotFound Then + UserExists = False + ElseIf .StatusCode = HttpStatusCode.Unauthorized Then + UserSuspended = True + ElseIf .StatusCode = HttpStatusCode.BadRequest Then + MyMainLOG = "Twitter has invalid credentials" + ElseIf .StatusCode = HttpStatusCode.ServiceUnavailable Or .StatusCode = HttpStatusCode.InternalServerError Then + MyMainLOG = $"[{CInt(.StatusCode)}] Twitter is currently unavailable ({ToString()})" + Else + If Not FromPE Then LogError(ex, Message) : HasError = True + Return 0 + End If + End With Return 1 End Function #End Region diff --git a/SCrawler/Channels/ChannelsStatsForm.vb b/SCrawler/Channels/ChannelsStatsForm.vb index 7b3d3e4..f415a9c 100644 --- a/SCrawler/Channels/ChannelsStatsForm.vb +++ b/SCrawler/Channels/ChannelsStatsForm.vb @@ -20,6 +20,7 @@ Friend Class ChannelsStatsForm .MyViewInitialize() .AddOkCancelToolbar() If Settings.Channels.Count > 0 Then RefillList() Else MsgBoxE("Channels not found", vbExclamation) + .DelegateClosingChecker = False .EndLoaderOperations() End With End Sub diff --git a/SCrawler/Content/Icons/TagIcon_32.ico b/SCrawler/Content/Icons/TagIcon_32.ico new file mode 100644 index 0000000000000000000000000000000000000000..8fee8e30c0721a3d60a5f58c327753383d43fa9f GIT binary patch literal 5430 zcmeI0drZ?;6vuBDBg>X0I-^@+jAU*`_fnT_W||<&(1}i+bCDn_FkrY1-KI=L9N+_1 zN}&}B$Qz141qzP04iQuyQbeJxJX%`7@)DGHDbKTWe+5H$IF{_6#r~4d@BV%#_ulV4 z=bqo~NeN+*m&l?;1Y2WrWC0eH;&%tX*io<5LpBJx5uAEabT#C%&hT ztq$=zRV=?(0&PudP*#;eZAB5(l;1&$KqadH{+XkVU#@+986AHm#Fr#p;+{?n;(U`D z&U+)2+ojjpkj*zF$%ZwN>{(+us9i>Kf40N#Rj#6~ka}7mjy-HhyXVlS8?<9?rh%ijvXc zp+OiK9)k9UT4+|tm^qpiav0L~LnPOub<0X4Mt|z~Y+)eDjOH)9krErLs45?8tdl{# z>>kurNuc%tQYir%mq5=Gr8_@Zy7HpW0`t7)T(?Z*A9~mF~{Qdn_Ti&(9I>FY`WQ{d) z7&&BVvUX0aOy9FU^u>qUah??pJ2x(%^Ude7*!(u3w6r}Xht1Zl!Zn;ceqek1N$cHk z%J%DVhVLd?=6w&^reU*Z{I^3p;b#l8O4R+y&is=_)Ac869a`_OCZFm0zW?eo2FG2S z7F(OGKYGH-y!~2=a7-bsfM&H4G%5vB2b%g?&?u{+N%0Vx>g0NAWzfVR1!-v^#PZIK zqwf+MjNx?sFwR+mtU`7nuhDmU2`;!&SOMo;jz#*Nm1l{fptVT_!@5C$u`w9NI;vHP zI`VR8lu1A>Edw>yW$MbipuArUYFRmGyE=f@R=HQA0rl&xx2}I{x<1w*1lg4kr1Q; zxyw_+{EyHzV9lw)XN4%lR}mk`R)qR^^j=RB;hrlXGcg*Zk|Jg;+P-d3RTe{aNgg~z z;Vkd+rhOJhCyuEo6=fUb58JZWiw&SpfTX6SgNg7!uQJjRyBWqf293Nn)B^q)z# zS#FpQ1YyPU&kJworlDRxX#0AY{2Lt|!8%_19DOBpXc|C=dxpshh1ueNtv`_$tkf{S zfIC^qx;FHiu3>a!1k-;MbOXJZ>lpvi$@tSf$O9Vxs$02GBe~7w24LU(jJfxJ7S|b5 zR%`%4SXnU_H)jchVhhs~$2wY?^!{T^M=%HV`GqiZ(-a;ikA@2JYkfdr!bOxHa^V9{mex-r*%BnAB^~JPP6LFWy}@|NKRZ3 z?nNLAb7BVgy|Ba){;cFMKb9zj%a9oC%c8j(7vPQg>&*)1c(6Fm4ut)yE#WxZ&n0EB u8zNwczzY-s{BfG!RX~gY>GC8-C4?B=Mhd2Ye^(ei!&BcE8pz6jn*47Z#q`Pm literal 0 HcmV?d00001 diff --git a/SCrawler/Download/Automation/AutoDownloader.vb b/SCrawler/Download/Automation/AutoDownloader.vb index bc14879..bb250f4 100644 --- a/SCrawler/Download/Automation/AutoDownloader.vb +++ b/SCrawler/Download/Automation/AutoDownloader.vb @@ -15,7 +15,7 @@ Imports PersonalUtilities.Functions.XML.Base Imports PersonalUtilities.Tools Imports PersonalUtilities.Tools.Notifications Namespace DownloadObjects - Friend Class AutoDownloader : Inherits GroupParameters : Implements IEContainerProvider + Friend Class AutoDownloader : Inherits GroupParameters : Implements IIndexable, IEContainerProvider Friend Event PauseDisabled() Private Shared ReadOnly Property CachePath As SFile Get @@ -135,8 +135,9 @@ Namespace DownloadObjects ErrorsDescriber.Execute(EDP.SendInLog, ex, "[AutoDownloader.NotifiedUser.ShowNotification]") If Not User Is Nothing Then MainFrameObj.ShowNotification(SettingsCLS.NotificationObjects.AutoDownloader, - $"Downloaded: {User.DownloadedPictures(False)} images, {User.DownloadedVideos(False)} videos", - User.ToString, IIf(User.HasError, ToolTipIcon.Warning, ToolTipIcon.Info)) + User.ToString & vbNewLine & + $"Downloaded: {User.DownloadedPictures(False)} images, {User.DownloadedVideos(False)} videos" & + If(User.HasError, vbNewLine & "With errors", String.Empty)) End If End Try End Sub @@ -182,7 +183,6 @@ Namespace DownloadObjects #Region "XML Names" Private Const Name_Mode As String = "Mode" Private Const Name_Groups As String = "Groups" - Private Const Name_Labels As String = "Labels" Private Const Name_Timer As String = "Timer" Private Const Name_StartupDelay As String = "StartupDelay" Private Const Name_LastDownloadDate As String = "LastDownloadDate" @@ -205,11 +205,16 @@ Namespace DownloadObjects End Property Friend ReadOnly Property Groups As List(Of String) Friend Property Timer As Integer = DefaultTimer - Friend Property StartupDelay As Integer = 0 + Friend Property StartupDelay As Integer = 1 Friend Property ShowNotifications As Boolean = True Friend Property ShowPictureDownloaded As Boolean = True Friend Property ShowPictureUser As Boolean = True Friend Property ShowSimpleNotification As Boolean = False + Private Property Index As Integer = -1 Implements IIndexable.Index + Private Function SetIndex(ByVal Obj As Object, ByVal Index As Integer) As Object Implements IIndexable.SetIndex + DirectCast(Obj, AutoDownloader).Index = Index + Return Obj + End Function #Region "Date" Private ReadOnly LastDownloadDateXML As Date? = Nothing Private _LastDownloadDate As Date = Now.AddYears(-1) @@ -223,20 +228,25 @@ Namespace DownloadObjects If Not Initialization Then _LastDownloadDateChanged = True End Set End Property + Private ReadOnly Property NextExecutionDate As Date + Get + If _PauseValue.HasValue Then + Return {LastDownloadDate.AddMinutes(Timer), _StartTime.AddMinutes(StartupDelay), _PauseValue.Value}.Max + Else + Return {LastDownloadDate.AddMinutes(Timer), _StartTime.AddMinutes(StartupDelay)}.Max + End If + End Get + End Property Private ReadOnly DateProvider As New ADateTime(ADateTime.Formats.BaseDateTime) Private Function GetLastDateString() As String If LastDownloadDateXML.HasValue Or _LastDownloadDateChanged Then - Return LastDownloadDate.ToStringDate(ADateTime.Formats.BaseDateTime) + Return LastDownloadDate.ToStringDate(DateProvider) Else Return "never" End If End Function Private Function GetNextDateString() As String - If _LastDownloadDateChanged Then - Return LastDownloadDate.AddMinutes(Timer).ToStringDate(ADateTime.Formats.BaseDateTime) - Else - Return _StartTime.AddMinutes(StartupDelay).ToStringDate(ADateTime.Formats.BaseDateTime) - End If + Return NextExecutionDate.ToStringDate(DateProvider) End Function #End Region #Region "Information" @@ -287,14 +297,11 @@ Namespace DownloadObjects End Sub Friend Sub New(ByVal x As EContainer) Me.New - Name = x.Value(Name_Name).FromXML(Of String)("Default") Mode = x.Value(Name_Mode).FromXML(Of Integer)(Modes.None) + Import(x) + If Name.IsEmptyString Then Name = "Default" Groups.ListAddList(x.Value(Name_Groups).StringToList(Of String)("|"), LAP.NotContainsOnly) - Labels.ListAddList(x.Value(Name_Labels).StringToList(Of String)("|"), LAP.NotContainsOnly) - Temporary = x.Value(Name_Temporary).FromXML(Of Integer)(CheckState.Indeterminate) - Favorite = x.Value(Name_Favorite).FromXML(Of Integer)(CheckState.Indeterminate) - ReadyForDownload = x.Value(Name_ReadyForDownload).FromXML(Of Boolean)(True) - ReadyForDownloadIgnore = x.Value(Name_ReadyForDownloadIgnore).FromXML(Of Boolean)(False) + Timer = x.Value(Name_Timer).FromXML(Of Integer)(DefaultTimer) If Timer <= 0 Then Timer = DefaultTimer StartupDelay = x.Value(Name_StartupDelay).FromXML(Of Integer)(0) @@ -331,24 +338,18 @@ Namespace DownloadObjects If Not Source Is Nothing Then Source.Update() End Sub Private Function ToEContainer(Optional ByVal e As ErrorsDescriber = Nothing) As EContainer Implements IEContainerProvider.ToEContainer - Return New EContainer(Scheduler.Name_Plan, String.Empty) From { - New EContainer(Name_Name, Name), - New EContainer(Name_Mode, CInt(Mode)), - New EContainer(Name_Groups, Groups.ListToString("|")), - New EContainer(Name_Labels, Labels.ListToString("|")), - New EContainer(Name_Temporary, CInt(Temporary)), - New EContainer(Name_Favorite, CInt(Favorite)), - New EContainer(Name_ReadyForDownload, ReadyForDownload.BoolToInteger), - New EContainer(Name_ReadyForDownloadIgnore, ReadyForDownloadIgnore.BoolToInteger), - New EContainer(Name_Timer, Timer), - New EContainer(Name_StartupDelay, StartupDelay), - New EContainer(Name_ShowNotifications, ShowNotifications.BoolToInteger), - New EContainer(Name_ShowPictureDown, ShowPictureDownloaded.BoolToInteger), - New EContainer(Name_ShowPictureUser, ShowPictureUser.BoolToInteger), - New EContainer(Name_ShowSimpleNotification, ShowSimpleNotification.BoolToInteger), - New EContainer(Name_LastDownloadDate, CStr(AConvert(Of String)(If(LastDownloadDateXML.HasValue Or _LastDownloadDateChanged, - CObj(LastDownloadDate), Nothing), DateProvider, String.Empty))) - } + Return Export(New EContainer(Scheduler.Name_Plan, String.Empty) From { + New EContainer(Name_Mode, CInt(Mode)), + New EContainer(Name_Groups, Groups.ListToString("|")), + New EContainer(Name_Timer, Timer), + New EContainer(Name_StartupDelay, StartupDelay), + New EContainer(Name_ShowNotifications, ShowNotifications.BoolToInteger), + New EContainer(Name_ShowPictureDown, ShowPictureDownloaded.BoolToInteger), + New EContainer(Name_ShowPictureUser, ShowPictureUser.BoolToInteger), + New EContainer(Name_ShowSimpleNotification, ShowSimpleNotification.BoolToInteger), + New EContainer(Name_LastDownloadDate, CStr(AConvert(Of String)(If(LastDownloadDateXML.HasValue Or _LastDownloadDateChanged, + CObj(LastDownloadDate), Nothing), DateProvider, String.Empty))) + }) End Function #End Region #Region "Execution" @@ -422,9 +423,20 @@ Namespace DownloadObjects End Sub Private Sub Checker() Try + Dim _StartDownload As Boolean While (Not _StopRequested Or Downloader.Working) And Not Mode = Modes.None If LastDownloadDate.AddMinutes(Timer) < Now And _StartTime.AddMinutes(StartupDelay) < Now And - Not Downloader.Working And Not IsPaused And Not _StopRequested And Not Mode = Modes.None Then Download() + Not Downloader.Working And Not IsPaused And Not _StopRequested And Not Mode = Modes.None Then + _StartDownload = False + If Settings.Automation.Count = 1 Then + _StartDownload = True + ElseIf Index = -1 Then + _StartDownload = True + Else + _StartDownload = NextExecutionDate.AddMilliseconds(1000 * (Index + 1)).Ticks <= Now.Ticks + End If + If _StartDownload Then Download() + End If Thread.Sleep(500) End While Catch ex As Exception @@ -447,7 +459,6 @@ Namespace DownloadObjects Dim GName$ Dim i% Dim DownloadedUsersCount% = 0 - Dim l As New ListAddParams(LAP.IgnoreICopier + LAP.NotContainsOnly) Dim simple As Boolean = ShowSimpleNotification And ShowNotifications Dim notify As Action = Sub() With Downloader.Downloaded @@ -464,7 +475,18 @@ Namespace DownloadObjects End With End Sub Select Case Mode - Case Modes.All : users.ListAddList(Settings.Users.Where(Function(u) u.Exists)) + Case Modes.All + Dim CheckLabels As Predicate(Of IUserData) = Function(ByVal u As IUserData) As Boolean + If LabelsExcluded.Count = 0 Then + Return True + ElseIf u.Labels.Count = 0 Then + Return True + Else + Return Not u.Labels.ListContains(LabelsExcluded) + End If + End Function + Dim CheckSites As Predicate(Of IUserData) = Function(u) SitesExcluded.Count = 0 OrElse Not SitesExcluded.Contains(u.Site) + users.ListAddList(Settings.GetUsers(Function(u) UserExistsPredicate(u) And CheckLabels.Invoke(u) And CheckSites.Invoke(u))) Case Modes.Default Using g As New GroupParameters : users.ListAddList(DownloadGroup.GetUsers(g, True)) : End Using Case Modes.Specified : users.ListAddList(DownloadGroup.GetUsers(Me, True)) @@ -472,24 +494,12 @@ Namespace DownloadObjects If Groups.Count > 0 And Settings.Groups.Count > 0 Then For Each GName In Groups i = Settings.Groups.IndexOf(GName) - If i >= 0 Then users.ListAddList(Settings.Groups(i).GetUsers, l) + If i >= 0 Then users.ListAddList(Settings.Groups(i).GetUsers, LAP.IgnoreICopier, LAP.NotContainsOnly) Next End If End Select If users.Count > 0 Then - Keys.ListAddList(users.SelectMany(Of String)(Function(ByVal user As IUserData) As IEnumerable(Of String) - If user.IsCollection Then - With DirectCast(user, UserDataBind) - If .Count > 0 Then - Return .Collections.Select(Function(u) u.Key) - Else - Return New String() {} - End If - End With - Else - Return {user.Key} - End If - End Function)) + Keys.ListAddList(users.Select(Function(u) u.Key)) With Downloader .AutoDownloaderWorking = True If .Downloaded.Count > 0 Then .Downloaded.RemoveAll(Function(u) Keys.Contains(u.Key)) : .InvokeDownloadsChangeEvent() @@ -499,7 +509,7 @@ Namespace DownloadObjects notify.Invoke If simple And DownloadedUsersCount > 0 Then _ MainFrameObj.ShowNotification(SettingsCLS.NotificationObjects.AutoDownloader, - $"{DownloadedUsersCount} user(s) downloaded with scheduler plan '{Name}'", $"Scheduler plan '{Name}'") + $"{DownloadedUsersCount} user(s) downloaded with scheduler plan '{Name}'") End With End If Catch ex As Exception diff --git a/SCrawler/Download/Automation/AutoDownloaderEditorForm.Designer.vb b/SCrawler/Download/Automation/AutoDownloaderEditorForm.Designer.vb index d806f53..88633df 100644 --- a/SCrawler/Download/Automation/AutoDownloaderEditorForm.Designer.vb +++ b/SCrawler/Download/Automation/AutoDownloaderEditorForm.Designer.vb @@ -66,13 +66,13 @@ Namespace DownloadObjects 'CONTAINER_MAIN.ContentPanel ' CONTAINER_MAIN.ContentPanel.Controls.Add(Me.DEF_GROUP) - CONTAINER_MAIN.ContentPanel.Size = New System.Drawing.Size(476, 276) + CONTAINER_MAIN.ContentPanel.Size = New System.Drawing.Size(476, 308) CONTAINER_MAIN.Dock = System.Windows.Forms.DockStyle.Fill CONTAINER_MAIN.LeftToolStripPanelVisible = False CONTAINER_MAIN.Location = New System.Drawing.Point(0, 0) CONTAINER_MAIN.Name = "CONTAINER_MAIN" CONTAINER_MAIN.RightToolStripPanelVisible = False - CONTAINER_MAIN.Size = New System.Drawing.Size(476, 301) + CONTAINER_MAIN.Size = New System.Drawing.Size(476, 333) CONTAINER_MAIN.TabIndex = 0 CONTAINER_MAIN.TopToolStripPanelVisible = False ' @@ -81,29 +81,30 @@ Namespace DownloadObjects Me.DEF_GROUP.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.[Single] Me.DEF_GROUP.ColumnCount = 1 Me.DEF_GROUP.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) - Me.DEF_GROUP.Controls.Add(Me.TXT_GROUPS, 0, 5) + Me.DEF_GROUP.Controls.Add(Me.TXT_GROUPS, 0, 6) Me.DEF_GROUP.Controls.Add(TP_MODE, 0, 0) - Me.DEF_GROUP.Controls.Add(Me.TXT_TIMER, 0, 7) - Me.DEF_GROUP.Controls.Add(Me.LBL_LAST_TIME_UP, 0, 9) - Me.DEF_GROUP.Controls.Add(Me.NUM_DELAY, 0, 8) - Me.DEF_GROUP.Controls.Add(TP_NOTIFY, 0, 6) + Me.DEF_GROUP.Controls.Add(Me.TXT_TIMER, 0, 8) + Me.DEF_GROUP.Controls.Add(Me.LBL_LAST_TIME_UP, 0, 10) + Me.DEF_GROUP.Controls.Add(Me.NUM_DELAY, 0, 9) + Me.DEF_GROUP.Controls.Add(TP_NOTIFY, 0, 7) Me.DEF_GROUP.Dock = System.Windows.Forms.DockStyle.Fill Me.DEF_GROUP.Location = New System.Drawing.Point(0, 0) Me.DEF_GROUP.Name = "DEF_GROUP" - Me.DEF_GROUP.RowCount = 11 + Me.DEF_GROUP.RowCount = 12 Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) - Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) + Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) + Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20.0!)) - Me.DEF_GROUP.Size = New System.Drawing.Size(476, 276) + Me.DEF_GROUP.Size = New System.Drawing.Size(476, 308) Me.DEF_GROUP.TabIndex = 0 ' 'TXT_GROUPS @@ -117,10 +118,11 @@ Namespace DownloadObjects Me.TXT_GROUPS.CaptionText = "Groups" Me.TXT_GROUPS.CaptionWidth = 50.0R Me.TXT_GROUPS.Dock = System.Windows.Forms.DockStyle.Fill - Me.TXT_GROUPS.Location = New System.Drawing.Point(4, 140) + Me.TXT_GROUPS.Location = New System.Drawing.Point(4, 169) Me.TXT_GROUPS.Name = "TXT_GROUPS" Me.TXT_GROUPS.Size = New System.Drawing.Size(468, 22) Me.TXT_GROUPS.TabIndex = 1 + Me.TXT_GROUPS.TextBoxReadOnly = True ' 'TP_MODE ' @@ -220,7 +222,7 @@ Namespace DownloadObjects Me.TXT_TIMER.CaptionToolTipText = "Timer (in minutes)" Me.TXT_TIMER.CaptionWidth = 50.0R Me.TXT_TIMER.Dock = System.Windows.Forms.DockStyle.Fill - Me.TXT_TIMER.Location = New System.Drawing.Point(4, 195) + Me.TXT_TIMER.Location = New System.Drawing.Point(4, 227) Me.TXT_TIMER.Name = "TXT_TIMER" Me.TXT_TIMER.Size = New System.Drawing.Size(468, 22) Me.TXT_TIMER.TabIndex = 3 @@ -230,7 +232,7 @@ Namespace DownloadObjects Me.LBL_LAST_TIME_UP.AutoSize = True Me.LBL_LAST_TIME_UP.Dock = System.Windows.Forms.DockStyle.Fill Me.LBL_LAST_TIME_UP.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, CType(204, Byte)) - Me.LBL_LAST_TIME_UP.Location = New System.Drawing.Point(4, 250) + Me.LBL_LAST_TIME_UP.Location = New System.Drawing.Point(4, 282) Me.LBL_LAST_TIME_UP.Name = "LBL_LAST_TIME_UP" Me.LBL_LAST_TIME_UP.Size = New System.Drawing.Size(468, 25) Me.LBL_LAST_TIME_UP.TabIndex = 5 @@ -249,7 +251,7 @@ Namespace DownloadObjects Me.NUM_DELAY.ClearTextByButtonClear = False Me.NUM_DELAY.ControlMode = PersonalUtilities.Forms.Controls.TextBoxExtended.ControlModes.NumericUpDown Me.NUM_DELAY.Dock = System.Windows.Forms.DockStyle.Fill - Me.NUM_DELAY.Location = New System.Drawing.Point(4, 224) + Me.NUM_DELAY.Location = New System.Drawing.Point(4, 256) Me.NUM_DELAY.Name = "NUM_DELAY" Me.NUM_DELAY.NumberMaximum = New Decimal(New Integer() {1440, 0, 0, 0}) Me.NUM_DELAY.NumberUpDownAlign = System.Windows.Forms.LeftRightAlignment.Left @@ -269,12 +271,12 @@ Namespace DownloadObjects TP_NOTIFY.Controls.Add(Me.CH_SHOW_PIC_USER, 3, 0) TP_NOTIFY.Controls.Add(Me.CH_NOTIFY_SIMPLE, 1, 0) TP_NOTIFY.Dock = System.Windows.Forms.DockStyle.Fill - TP_NOTIFY.Location = New System.Drawing.Point(1, 166) + TP_NOTIFY.Location = New System.Drawing.Point(1, 195) TP_NOTIFY.Margin = New System.Windows.Forms.Padding(0) TP_NOTIFY.Name = "TP_NOTIFY" TP_NOTIFY.RowCount = 1 TP_NOTIFY.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) - TP_NOTIFY.Size = New System.Drawing.Size(474, 25) + TP_NOTIFY.Size = New System.Drawing.Size(474, 28) TP_NOTIFY.TabIndex = 2 ' 'CH_NOTIFY @@ -283,7 +285,7 @@ Namespace DownloadObjects Me.CH_NOTIFY.Dock = System.Windows.Forms.DockStyle.Fill Me.CH_NOTIFY.Location = New System.Drawing.Point(3, 3) Me.CH_NOTIFY.Name = "CH_NOTIFY" - Me.CH_NOTIFY.Size = New System.Drawing.Size(112, 19) + Me.CH_NOTIFY.Size = New System.Drawing.Size(112, 22) Me.CH_NOTIFY.TabIndex = 0 Me.CH_NOTIFY.Text = "Show notifications" TT_MAIN.SetToolTip(Me.CH_NOTIFY, "Show notification when some data has been downloaded") @@ -295,7 +297,7 @@ Namespace DownloadObjects Me.CH_SHOW_PIC.Dock = System.Windows.Forms.DockStyle.Fill Me.CH_SHOW_PIC.Location = New System.Drawing.Point(239, 3) Me.CH_SHOW_PIC.Name = "CH_SHOW_PIC" - Me.CH_SHOW_PIC.Size = New System.Drawing.Size(112, 19) + Me.CH_SHOW_PIC.Size = New System.Drawing.Size(112, 22) Me.CH_SHOW_PIC.TabIndex = 2 Me.CH_SHOW_PIC.Text = "Image" TT_MAIN.SetToolTip(Me.CH_SHOW_PIC, "Show downloaded image in notification") @@ -307,7 +309,7 @@ Namespace DownloadObjects Me.CH_SHOW_PIC_USER.Dock = System.Windows.Forms.DockStyle.Fill Me.CH_SHOW_PIC_USER.Location = New System.Drawing.Point(357, 3) Me.CH_SHOW_PIC_USER.Name = "CH_SHOW_PIC_USER" - Me.CH_SHOW_PIC_USER.Size = New System.Drawing.Size(114, 19) + Me.CH_SHOW_PIC_USER.Size = New System.Drawing.Size(114, 22) Me.CH_SHOW_PIC_USER.TabIndex = 3 Me.CH_SHOW_PIC_USER.Text = "User icon" TT_MAIN.SetToolTip(Me.CH_SHOW_PIC_USER, "Show user image in notification") @@ -319,7 +321,7 @@ Namespace DownloadObjects Me.CH_NOTIFY_SIMPLE.Dock = System.Windows.Forms.DockStyle.Fill Me.CH_NOTIFY_SIMPLE.Location = New System.Drawing.Point(121, 3) Me.CH_NOTIFY_SIMPLE.Name = "CH_NOTIFY_SIMPLE" - Me.CH_NOTIFY_SIMPLE.Size = New System.Drawing.Size(112, 19) + Me.CH_NOTIFY_SIMPLE.Size = New System.Drawing.Size(112, 22) Me.CH_NOTIFY_SIMPLE.TabIndex = 1 Me.CH_NOTIFY_SIMPLE.Text = "Simple" TT_MAIN.SetToolTip(Me.CH_NOTIFY_SIMPLE, resources.GetString("CH_NOTIFY_SIMPLE.ToolTip")) @@ -329,15 +331,15 @@ Namespace DownloadObjects ' Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!) Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font - Me.ClientSize = New System.Drawing.Size(476, 301) + Me.ClientSize = New System.Drawing.Size(476, 333) Me.Controls.Add(CONTAINER_MAIN) Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle Me.Icon = Global.SCrawler.My.Resources.Resources.ArrowDownIcon_Blue_24 Me.KeyPreview = True Me.MaximizeBox = False - Me.MaximumSize = New System.Drawing.Size(492, 340) + Me.MaximumSize = New System.Drawing.Size(492, 372) Me.MinimizeBox = False - Me.MinimumSize = New System.Drawing.Size(492, 340) + Me.MinimumSize = New System.Drawing.Size(492, 372) Me.Name = "AutoDownloaderEditorForm" Me.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide Me.Text = "AutoDownloader settings" diff --git a/SCrawler/Download/Automation/AutoDownloaderEditorForm.vb b/SCrawler/Download/Automation/AutoDownloaderEditorForm.vb index e406377..a98d34d 100644 --- a/SCrawler/Download/Automation/AutoDownloaderEditorForm.vb +++ b/SCrawler/Download/Automation/AutoDownloaderEditorForm.vb @@ -101,7 +101,7 @@ Namespace DownloadObjects Private Sub TXT_GROUPS_ActionOnButtonClick(ByVal Sender As ActionButton, ByVal e As EventArgs) Handles TXT_GROUPS.ActionOnButtonClick Select Case Sender.DefaultButton Case ActionButton.DefaultButtons.Edit - Using f As New LabelsForm(MyGroups, Settings.Groups.Select(Function(g) g.Name)) With {.Text = "Groups"} + Using f As New LabelsForm(MyGroups, Settings.Groups.Select(Function(g) g.Name)) With {.Text = "Groups", .Icon = My.Resources.GroupByIcon_16} f.ShowDialog() If f.DialogResult = DialogResult.OK Then MyGroups.ListAddList(f.LabelsList, LAP.ClearBeforeAdd) : TXT_GROUPS.Text = MyGroups.ListToString End Using @@ -111,7 +111,7 @@ Namespace DownloadObjects Private Sub ChangeEnabled() Handles OPT_DISABLED.CheckedChanged, OPT_ALL.CheckedChanged, OPT_DEFAULT.CheckedChanged, OPT_SPEC.CheckedChanged, OPT_GROUP.CheckedChanged, CH_NOTIFY.CheckedChanged, CH_NOTIFY_SIMPLE.CheckedChanged - DEF_GROUP.Enabled = OPT_SPEC.Checked + DEF_GROUP.Enabled(OPT_ALL.Checked Or OPT_DEFAULT.Checked Or OPT_SPEC.Checked) = OPT_SPEC.Checked TXT_GROUPS.Enabled = OPT_GROUP.Checked TXT_TIMER.Enabled = Not OPT_DISABLED.Checked NUM_DELAY.Enabled = Not OPT_DISABLED.Checked diff --git a/SCrawler/Download/Automation/AutoDownloaderPauseButtons.vb b/SCrawler/Download/Automation/AutoDownloaderPauseButtons.vb index c339c61..9103979 100644 --- a/SCrawler/Download/Automation/AutoDownloaderPauseButtons.vb +++ b/SCrawler/Download/Automation/AutoDownloaderPauseButtons.vb @@ -178,7 +178,18 @@ Namespace DownloadObjects Private Sub UpdateBaseButton(ByVal Checked As Boolean) With MainFrameObj.MF Select Case Place - Case ButtonsPlace.MainFrame : ApplyButtonStyle(.BTT_DOWN_AUTOMATION_PAUSE, Sub() .BTT_DOWN_AUTOMATION_PAUSE.Checked = Checked) + Case ButtonsPlace.MainFrame : ApplyButtonStyle(.BTT_DOWN_AUTOMATION_PAUSE, Sub() + .BTT_DOWN_AUTOMATION_PAUSE.Checked = Checked + With .MENU_DOWN_ALL + If Checked Then + .BackColor = MyColor.UpdateBack + .ForeColor = MyColor.UpdateFore + Else + .BackColor = Control.DefaultBackColor + .ForeColor = Control.DefaultForeColor + End If + End With + End Sub) Case ButtonsPlace.Tray : ApplyButtonStyle(MainFrameObj.MF.BTT_TRAY_PAUSE_AUTOMATION, Sub() MainFrameObj.MF.BTT_TRAY_PAUSE_AUTOMATION.Checked = Checked) End Select End With diff --git a/SCrawler/Download/Automation/Scheduler.vb b/SCrawler/Download/Automation/Scheduler.vb index 848d098..f2fb9f9 100644 --- a/SCrawler/Download/Automation/Scheduler.vb +++ b/SCrawler/Download/Automation/Scheduler.vb @@ -40,7 +40,7 @@ Namespace DownloadObjects If Plans.Count > 0 Then Plans.ForEach(Sub(p) p.Source = Me AddHandler p.PauseDisabled, AddressOf OnPauseDisabled - End Sub) + End Sub) : Plans.ListReindex End Sub Default Friend ReadOnly Property Item(ByVal Index As Integer) As AutoDownloader Implements IMyEnumerator(Of AutoDownloader).MyEnumeratorObject Get @@ -59,6 +59,7 @@ Namespace DownloadObjects Plan.Source = Me AddHandler Plan.PauseDisabled, AddressOf OnPauseDisabled Plans.Add(Plan) + Plans.ListReindex Update() End Sub Friend Async Function RemoveAt(ByVal Index As Integer) As Task @@ -73,6 +74,7 @@ Namespace DownloadObjects .Dispose() End With Plans.RemoveAt(Index) + Plans.ListReindex Update() End If End Function @@ -98,16 +100,18 @@ Namespace DownloadObjects End Sub #End Region #Region "Execution" - Friend Async Sub Start(ByVal Init As Boolean) - If Count > 0 Then - If Plans.Exists(PlanDownloading) Then Await Task.Run(Sub() PlansWaiter(PlanDownloading)) - For Each Plan In Plans - Plan.Start(Init) - Thread.Sleep(1000) - Await Task.Run(Sub() PlansWaiter(PlanDownloading)) - Next - End If - End Sub + Friend Async Function Start(ByVal Init As Boolean) As Task + Await Task.Run(Sub() + If Count > 0 Then + If Plans.Exists(PlanDownloading) Then PlansWaiter(PlanDownloading) + For Each Plan In Plans + Plan.Start(Init) + PlansWaiter(PlanDownloading) + Thread.Sleep(1000) + Next + End If + End Sub) + End Function Friend Sub [Stop]() If Count > 0 Then Plans.ForEach(Sub(p) p.Stop()) End Sub diff --git a/SCrawler/Download/Groups/DownloadGroup.vb b/SCrawler/Download/Groups/DownloadGroup.vb index 3a63fb7..c384dc1 100644 --- a/SCrawler/Download/Groups/DownloadGroup.vb +++ b/SCrawler/Download/Groups/DownloadGroup.vb @@ -51,6 +51,7 @@ Namespace DownloadObjects.Groups End Function #End Region #Region "Initializers" + Friend ReadOnly NeedToSave As Boolean = False Friend Sub New() BTT_MENU = New ToolStripMenuItem With { .ToolTipText = "Download users of this group", @@ -90,12 +91,18 @@ Namespace DownloadObjects.Groups End Sub Friend Sub New(ByVal e As EContainer) Me.New - Name = e.Attribute(Name_Name) - Temporary = e.Attribute(Name_Temporary).Value.FromXML(Of Integer)(CInt(CheckState.Indeterminate)) - Favorite = e.Attribute(Name_Favorite).Value.FromXML(Of Integer)(CInt(CheckState.Indeterminate)) - ReadyForDownload = e.Attribute(Name_ReadyForDownload).Value.FromXML(Of Boolean)(True) - ReadyForDownloadIgnore = e.Attribute(Name_ReadyForDownloadIgnore).Value.FromXML(Of Boolean)(False) - If Not e.Value.IsEmptyString Then Labels.ListAddList(e.Value.Split("|"), LAP.NotContainsOnly) + If e.Attributes.Contains(New EAttribute(Name_Name)) Then + 'TODELETE: 2022.10.18.0 + NeedToSave = True + Name = e.Attribute(Name_Name) + Temporary = e.Attribute(Name_Temporary).Value.FromXML(Of Integer)(CInt(CheckState.Indeterminate)) + Favorite = e.Attribute(Name_Favorite).Value.FromXML(Of Integer)(CInt(CheckState.Indeterminate)) + ReadyForDownload = e.Attribute(Name_ReadyForDownload).Value.FromXML(Of Boolean)(True) + ReadyForDownloadIgnore = e.Attribute(Name_ReadyForDownloadIgnore).Value.FromXML(Of Boolean)(False) + If Not e.Value.IsEmptyString Then Labels.ListAddList(e.Value.Split("|"), LAP.NotContainsOnly) + Else + Import(e) + End If End Sub #End Region #Region "ToString" @@ -149,19 +156,28 @@ Namespace DownloadObjects.Groups (.Temporary = CheckState.Indeterminate Or user.Temporary = CBool(.Temporary)) And (.Favorite = CheckState.Indeterminate Or (user.Favorite = CBool(.Favorite))) And (Not UseReadyOption Or .ReadyForDownloadIgnore Or user.ReadyForDownload = .ReadyForDownload) And user.Exists - Dim f As Func(Of IUserData, IEnumerable(Of IUserData)) = Function(ByVal user As IUserData) As IEnumerable(Of IUserData) - If user.IsCollection Then - With DirectCast(user, UserDataBind) - If .Count > 0 Then Return .Collections.SelectMany(f) - End With - Else - If .Labels.Count = 0 OrElse user.Labels.ListContains(.Labels) Then - If CheckParams.Invoke(user) Then Return {user} - End If - End If - Return New IUserData() {} - End Function - Return Settings.Users.SelectMany(f) + Dim CheckLabelsExcluded As Predicate(Of IUserData) = Function(ByVal user As IUserData) As Boolean + If .LabelsExcluded.Count = 0 Then + Return True + ElseIf user.Labels.Count = 0 Then + Return True + Else + Return Not user.Labels.ListContains(.LabelsExcluded) + End If + End Function + Dim CheckLabels As Predicate(Of IUserData) = Function(ByVal user As IUserData) As Boolean + If .Labels.Count = 0 Then + Return CheckLabelsExcluded.Invoke(user) + ElseIf user.Labels.Count = 0 Then + Return False + Else + Return user.Labels.ListContains(.Labels) And CheckLabelsExcluded.Invoke(user) + End If + End Function + Dim CheckSites As Predicate(Of IUserData) = Function(user) _ + (.Sites.Count = 0 OrElse .Sites.Contains(user.Site)) AndAlso + (.SitesExcluded.Count = 0 OrElse Not .SitesExcluded.Contains(user.Site)) + Return Settings.GetUsers(Function(user) CheckLabels.Invoke(user) AndAlso CheckSites.Invoke(user) AndAlso CheckParams.Invoke(user)) End With Else Return Nothing @@ -189,11 +205,7 @@ Namespace DownloadObjects.Groups #End Region #Region "IEContainerProvider Support" Private Function ToEContainer(Optional ByVal e As ErrorsDescriber = Nothing) As EContainer Implements IEContainerProvider.ToEContainer - Return New EContainer("Group", Labels.ListToString("|"), {New EAttribute(Name_Name, Name), - New EAttribute(Name_Temporary, CInt(Temporary)), - New EAttribute(Name_Favorite, CInt(Favorite)), - New EAttribute(Name_ReadyForDownload, ReadyForDownload.BoolToInteger), - New EAttribute(Name_ReadyForDownloadIgnore, ReadyForDownloadIgnore.BoolToInteger)}) + Return Export(New EContainer("Group")) End Function #End Region #Region "IDisposable Support" diff --git a/SCrawler/Download/Groups/DownloadGroupCollection.vb b/SCrawler/Download/Groups/DownloadGroupCollection.vb index e604cca..4d5b1b5 100644 --- a/SCrawler/Download/Groups/DownloadGroupCollection.vb +++ b/SCrawler/Download/Groups/DownloadGroupCollection.vb @@ -22,10 +22,15 @@ Namespace DownloadObjects.Groups x.LoadData() If x.Count > 0 Then GroupsList.ListAddList(x, LAP.IgnoreICopier) End Using - If GroupsList.Count > 0 Then GroupsList.ForEach(Sub(ByVal g As DownloadGroup) - AddHandler g.Deleted, AddressOf OnGroupDeleted - AddHandler g.Updated, AddressOf OnGroupUpdated - End Sub) + With GroupsList + If .Count > 0 Then + .ForEach(Sub(ByVal g As DownloadGroup) + AddHandler g.Deleted, AddressOf OnGroupDeleted + AddHandler g.Updated, AddressOf OnGroupUpdated + End Sub) + If .Exists(Function(g) g.NeedToSave) Then Update() + End If + End With End If GroupsList.ListReindex End Sub @@ -49,9 +54,9 @@ Namespace DownloadObjects.Groups GroupFile.Delete() End If End Sub + Private _GroupAddInProgress As Boolean = False Private Sub OnGroupUpdated(ByVal Sender As DownloadGroup) - Update() - RaiseEvent Updated(Sender) + If Not _GroupAddInProgress Then Update() : RaiseEvent Updated(Sender) End Sub Private Sub OnGroupDeleted(ByVal Sender As DownloadGroup) RaiseEvent Deleted(Sender) @@ -67,10 +72,16 @@ Namespace DownloadObjects.Groups Using f As New GroupEditorForm(Nothing) f.ShowDialog() If f.DialogResult = DialogResult.OK Then + _GroupAddInProgress = True GroupsList.Add(f.MyGroup) + With GroupsList.Last + AddHandler .Deleted, AddressOf OnGroupDeleted + AddHandler .Updated, AddressOf OnGroupUpdated + End With GroupsList.ListReindex RaiseEvent Added(GroupsList.Last) Update() + _GroupAddInProgress = False End If End Using End Sub diff --git a/SCrawler/Download/Groups/GroupDefaults.vb b/SCrawler/Download/Groups/GroupDefaults.vb index 56a0c88..b8a8bcd 100644 --- a/SCrawler/Download/Groups/GroupDefaults.vb +++ b/SCrawler/Download/Groups/GroupDefaults.vb @@ -18,28 +18,28 @@ Namespace DownloadObjects.Groups Private ReadOnly CH_READY_FOR_DOWN As CheckBox Private ReadOnly CH_READY_FOR_DOWN_IGNORE As CheckBox Private WithEvents TXT_LABELS As TextBoxExtended + Private WithEvents TXT_SITES As TextBoxExtended Friend WithEvents TXT_NAME As TextBoxExtended Private ReadOnly Labels As List(Of String) + Private ReadOnly LabelsExcluded As List(Of String) + Private ReadOnly Sites As List(Of String) + Private ReadOnly SitesExcluded As List(Of String) Public Sub New() Labels = New List(Of String) - TXT_LABELS = New TextBoxExtended - With TXT_LABELS - .BeginInit() - .Buttons.AddRange({ADB.Edit, ADB.Clear}) - .CaptionText = "Labels" - .CaptionWidth = 50 - .Dock = DockStyle.Fill - .EndInit() - End With - TXT_NAME = New TextBoxExtended - With TXT_NAME - .BeginInit() - .Buttons.Add(ADB.Clear) - .CaptionText = "Name" - .CaptionWidth = 50 - .Dock = DockStyle.Fill - .EndInit() - End With + LabelsExcluded = New List(Of String) + Sites = New List(Of String) + SitesExcluded = New List(Of String) + + InitTextBox(TXT_LABELS, "Labels", {New ActionButton(ADB.Edit) With {.ToolTipText = "Edit selected labels"}, + New ActionButton(ADB.Delete) With {.ToolTipText = "Edit excluded labels"}, ADB.Clear}) + TXT_LABELS.TextBoxReadOnly = True + + InitTextBox(TXT_SITES, "Sites", {New ActionButton(ADB.Edit) With {.ToolTipText = "Edit selected sites"}, + New ActionButton(ADB.Delete) With {.ToolTipText = "Edit excluded sites"}, ADB.Clear}) + TXT_SITES.TextBoxReadOnly = True + + InitTextBox(TXT_NAME, "Name", {ADB.Clear}) + CH_TEMPORARY = New CheckBox With {.Text = "Temporary", .Name = "CH_TEMPORARY", .ThreeState = True, .CheckState = CheckState.Indeterminate, .Dock = DockStyle.Fill} CH_FAV = New CheckBox With {.Text = "Favorite", .Name = "CH_FAV", .ThreeState = True, .CheckState = CheckState.Indeterminate, .Dock = DockStyle.Fill} CH_READY_FOR_DOWN = New CheckBox With {.Text = "Ready for download", .Name = "CH_READY_FOR_DOWN", .Checked = True, .Dock = DockStyle.Fill} @@ -49,6 +49,17 @@ Namespace DownloadObjects.Groups TP_2 = New TableLayoutPanel With {.CellBorderStyle = TableLayoutPanelCellBorderStyle.Single, .Margin = New Padding(0), .Dock = DockStyle.Fill} FillTP(TP_2, CH_READY_FOR_DOWN, CH_READY_FOR_DOWN_IGNORE) End Sub + Private Sub InitTextBox(ByRef TXT As TextBoxExtended, ByVal Caption As String, ByVal Buttons As ActionButton()) + TXT = New TextBoxExtended + With TXT + .BeginInit() + .Buttons.AddRange(Buttons) + .CaptionText = Caption + .CaptionWidth = 50 + .Dock = DockStyle.Fill + .EndInit() + End With + End Sub Private Sub FillTP(ByRef TP As TableLayoutPanel, ByVal CNT1 As Control, ByVal CNT2 As Control) With TP .ColumnCount = 2 @@ -87,33 +98,63 @@ Namespace DownloadObjects.Groups CellBorderStyle = TableLayoutPanelCellBorderStyle.Single ColumnCount = 1 ColumnStyles.Add(New ColumnStyle(SizeType.Percent, 100)) - RowCount = 6 + RowCount = 7 RowStyles.Add(New RowStyle(SizeType.Absolute, 25)) RowStyles.Add(New RowStyle(SizeType.Absolute, 28)) RowStyles.Add(New RowStyle(SizeType.Absolute, 25)) RowStyles.Add(New RowStyle(SizeType.Absolute, 25)) RowStyles.Add(New RowStyle(SizeType.Absolute, 28)) + RowStyles.Add(New RowStyle(SizeType.Absolute, 28)) RowStyles.Add(New RowStyle(SizeType.Percent, 100)) End If Controls.Add(TXT_NAME, 0, 1) Controls.Add(TP_1, 0, 2) Controls.Add(TP_2, 0, 3) Controls.Add(TXT_LABELS, 0, 4) + Controls.Add(TXT_SITES, 0, 5) End Sub - Private Sub TXT_LABELS_ActionOnButtonClick(ByVal Sender As ActionButton, ByVal e As EventArgs) Handles TXT_LABELS.ActionOnButtonClick + Private Sub TXT_LABELS_ActionOnButtonClick(ByVal Sender As ActionButton, ByVal e As ActionButtonEventArgs) Handles TXT_LABELS.ActionOnButtonClick Select Case Sender.DefaultButton - Case ADB.Edit - Using f As New LabelsForm(Labels) - f.ShowDialog() - If f.DialogResult = DialogResult.OK Then - Labels.ListAddList(f.LabelsList, LAP.NotContainsOnly, LAP.ClearBeforeAdd) - TXT_LABELS.Clear() - TXT_LABELS.Text = Labels.ListToString - End If - End Using - Case ADB.Clear : Labels.Clear() + Case ADB.Edit, ADB.Delete + With If(Sender.DefaultButton = ADB.Edit, Labels, LabelsExcluded) + Using f As New LabelsForm(.ListSelf, True) + If Sender.DefaultButton = ADB.Delete Then f.Text &= " excluded" + f.ShowDialog() + If f.DialogResult = DialogResult.OK Then + .AsList.ListAddList(f.LabelsList, LAP.NotContainsOnly, LAP.ClearBeforeAdd) + UpdateLabelsText() + End If + End Using + End With + Case ADB.Clear : Labels.Clear() : LabelsExcluded.Clear() : TXT_LABELS.Clear() : UpdateLabelsText() End Select End Sub + Private Sub TXT_SITES_ActionOnButtonClick(ByVal Sender As ActionButton, ByVal e As ActionButtonEventArgs) Handles TXT_SITES.ActionOnButtonClick + Select Case Sender.DefaultButton + Case ADB.Edit, ADB.Delete + With If(Sender.DefaultButton = ADB.Edit, Sites, SitesExcluded) + Using f As New Editors.SiteSelectionForm(.ListSelf) + If Sender.DefaultButton = ADB.Delete Then f.Text &= " excluded" + f.ShowDialog() + If f.DialogResult = DialogResult.OK Then + .AsList.ListAddList(f.SelectedSites, LAP.NotContainsOnly, LAP.ClearBeforeAdd) + UpdateSitesText() + End If + End Using + End With + Case ADB.Clear : Sites.Clear() : SitesExcluded.Clear() : TXT_SITES.Clear() : UpdateSitesText() + End Select + End Sub + Private Sub UpdateLabelsText() + TXT_LABELS.Clear() + If Not _JustExcludeOptions Then TXT_LABELS.Text = Labels.ListToString + If LabelsExcluded.Count > 0 Then TXT_LABELS.Text.StringAppend($"EXCLUDED: {LabelsExcluded.ListToString}", "; ") + End Sub + Private Sub UpdateSitesText() + TXT_SITES.Clear() + If Not _JustExcludeOptions Then TXT_SITES.Text = Sites.ListToString + If SitesExcluded.Count > 0 Then TXT_SITES.Text.StringAppend($"EXCLUDED: {SitesExcluded.ListToString}", "; ") + End Sub Friend Sub [Get](ByRef Instance As IGroup) If Not Instance Is Nothing Then With Instance @@ -124,6 +165,12 @@ Namespace DownloadObjects.Groups .ReadyForDownloadIgnore = CH_READY_FOR_DOWN_IGNORE.Checked .Labels.Clear() .Labels.ListAddList(Labels) + .LabelsExcluded.Clear() + .LabelsExcluded.ListAddList(LabelsExcluded) + .Sites.Clear() + .Sites.ListAddList(Sites) + .SitesExcluded.Clear() + .SitesExcluded.ListAddList(SitesExcluded) End With End If End Sub @@ -135,21 +182,48 @@ Namespace DownloadObjects.Groups CH_FAV.CheckState = .Favorite CH_READY_FOR_DOWN.Checked = .ReadyForDownload CH_READY_FOR_DOWN_IGNORE.Checked = .ReadyForDownloadIgnore + Labels.ListAddList(.Labels) - TXT_LABELS.Text = Labels.ListToString + LabelsExcluded.ListAddList(.LabelsExcluded) + UpdateLabelsText() + + Sites.ListAddList(.Sites) + SitesExcluded.ListAddList(.SitesExcluded) + UpdateSitesText() End With End If End Sub Private _Enabled As Boolean = True - Friend Shadows Property Enabled As Boolean + Private _JustExcludeOptions As Boolean = False + Friend Overloads Property Enabled(Optional ByVal LeaveExcludeOptions As Boolean = False) As Boolean Get Return _Enabled End Get Set(ByVal e As Boolean) _Enabled = e + _JustExcludeOptions = False TP_1.Enabled = e TP_2.Enabled = e - TXT_LABELS.Enabled = e + If e Then + TXT_LABELS.Enabled = True + TXT_SITES.Enabled = True + ElseIf LeaveExcludeOptions Then + _JustExcludeOptions = True + TXT_LABELS.Enabled = True + TXT_LABELS.Button(ADB.Edit).Enabled = False + TXT_LABELS.Button(ADB.Delete).Enabled = True + TXT_LABELS.Button(ADB.Clear).Enabled = False + + TXT_SITES.Enabled = True + TXT_SITES.Button(ADB.Edit).Enabled = False + TXT_SITES.Button(ADB.Delete).Enabled = True + TXT_SITES.Button(ADB.Clear).Enabled = False + Else + TXT_LABELS.Enabled = False + TXT_SITES.Enabled = False + End If + UpdateLabelsText() + UpdateSitesText() End Set End Property End Class diff --git a/SCrawler/Download/Groups/GroupEditorForm.Designer.vb b/SCrawler/Download/Groups/GroupEditorForm.Designer.vb index e123168..9cc09f8 100644 --- a/SCrawler/Download/Groups/GroupEditorForm.Designer.vb +++ b/SCrawler/Download/Groups/GroupEditorForm.Designer.vb @@ -35,13 +35,13 @@ Namespace DownloadObjects.Groups 'CONTAINER_MAIN.ContentPanel ' CONTAINER_MAIN.ContentPanel.Controls.Add(Me.DEFS_GROUP) - CONTAINER_MAIN.ContentPanel.Size = New System.Drawing.Size(476, 109) + CONTAINER_MAIN.ContentPanel.Size = New System.Drawing.Size(476, 141) CONTAINER_MAIN.Dock = System.Windows.Forms.DockStyle.Fill CONTAINER_MAIN.LeftToolStripPanelVisible = False CONTAINER_MAIN.Location = New System.Drawing.Point(0, 0) CONTAINER_MAIN.Name = "CONTAINER_MAIN" CONTAINER_MAIN.RightToolStripPanelVisible = False - CONTAINER_MAIN.Size = New System.Drawing.Size(476, 134) + CONTAINER_MAIN.Size = New System.Drawing.Size(476, 166) CONTAINER_MAIN.TabIndex = 0 CONTAINER_MAIN.TopToolStripPanelVisible = False ' @@ -53,29 +53,30 @@ Namespace DownloadObjects.Groups Me.DEFS_GROUP.Dock = System.Windows.Forms.DockStyle.Fill Me.DEFS_GROUP.Location = New System.Drawing.Point(0, 0) Me.DEFS_GROUP.Name = "DEFS_GROUP" - Me.DEFS_GROUP.RowCount = 6 + Me.DEFS_GROUP.RowCount = 7 Me.DEFS_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 0!)) Me.DEFS_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) Me.DEFS_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) Me.DEFS_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) Me.DEFS_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) + Me.DEFS_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) Me.DEFS_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) - Me.DEFS_GROUP.Size = New System.Drawing.Size(476, 109) - Me.DEFS_GROUP.TabIndex = 1 + Me.DEFS_GROUP.Size = New System.Drawing.Size(476, 141) + Me.DEFS_GROUP.TabIndex = 0 ' 'GroupEditorForm ' Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!) Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font - Me.ClientSize = New System.Drawing.Size(476, 134) + Me.ClientSize = New System.Drawing.Size(476, 166) Me.Controls.Add(CONTAINER_MAIN) Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle Me.Icon = Global.SCrawler.My.Resources.Resources.GroupByIcon_16 Me.KeyPreview = True Me.MaximizeBox = False - Me.MaximumSize = New System.Drawing.Size(492, 173) + Me.MaximumSize = New System.Drawing.Size(492, 205) Me.MinimizeBox = False - Me.MinimumSize = New System.Drawing.Size(492, 173) + Me.MinimumSize = New System.Drawing.Size(492, 205) Me.Name = "GroupEditorForm" Me.ShowInTaskbar = False Me.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide @@ -86,6 +87,7 @@ Namespace DownloadObjects.Groups Me.ResumeLayout(False) End Sub + Private WithEvents DEFS_GROUP As GroupDefaults End Class End Namespace \ No newline at end of file diff --git a/SCrawler/Download/Groups/GroupParameters.vb b/SCrawler/Download/Groups/GroupParameters.vb index 1338deb..1d77172 100644 --- a/SCrawler/Download/Groups/GroupParameters.vb +++ b/SCrawler/Download/Groups/GroupParameters.vb @@ -6,10 +6,14 @@ ' ' This program is distributed in the hope that it will be useful, ' but WITHOUT ANY WARRANTY +Imports PersonalUtilities.Functions.XML Namespace DownloadObjects.Groups Friend Interface IGroup Property Name As String ReadOnly Property Labels As List(Of String) + ReadOnly Property LabelsExcluded As List(Of String) + ReadOnly Property Sites As List(Of String) + ReadOnly Property SitesExcluded As List(Of String) Property Temporary As CheckState Property Favorite As CheckState Property ReadyForDownload As Boolean @@ -21,20 +25,60 @@ Namespace DownloadObjects.Groups Protected Const Name_Favorite As String = "Favorite" Protected Const Name_ReadyForDownload As String = "RFD" Protected Const Name_ReadyForDownloadIgnore As String = "RFDI" + Protected Const Name_Labels As String = "Labels" + Protected Const Name_Labels_Excluded As String = "LabelsExcluded" + Protected Const Name_Sites As String = "Sites" + Protected Const Name_Sites_Excluded As String = "SitesExcluded" Friend Property Name As String Implements IGroup.Name Friend ReadOnly Property Labels As List(Of String) Implements IGroup.Labels + Friend ReadOnly Property LabelsExcluded As List(Of String) Implements IGroup.LabelsExcluded + Friend ReadOnly Property Sites As List(Of String) Implements IGroup.Sites + Friend ReadOnly Property SitesExcluded As List(Of String) Implements IGroup.SitesExcluded Friend Property Temporary As CheckState = CheckState.Indeterminate Implements IGroup.Temporary Friend Property Favorite As CheckState = CheckState.Indeterminate Implements IGroup.Favorite Friend Property ReadyForDownload As Boolean = True Implements IGroup.ReadyForDownload Friend Property ReadyForDownloadIgnore As Boolean = False Implements IGroup.ReadyForDownloadIgnore Friend Sub New() Labels = New List(Of String) + LabelsExcluded = New List(Of String) + Sites = New List(Of String) + SitesExcluded = New List(Of String) End Sub + Protected Sub Import(ByVal e As EContainer) + Name = e.Value(Name_Name) + Temporary = e.Value(Name_Temporary).FromXML(Of Integer)(CInt(CheckState.Indeterminate)) + Favorite = e.Value(Name_Favorite).FromXML(Of Integer)(CInt(CheckState.Indeterminate)) + ReadyForDownload = e.Value(Name_ReadyForDownload).FromXML(Of Boolean)(True) + ReadyForDownloadIgnore = e.Value(Name_ReadyForDownloadIgnore).FromXML(Of Boolean)(False) + + Dim l As New ListAddParams(LAP.NotContainsOnly) + If Not e.Value(Name_Labels).IsEmptyString Then Labels.ListAddList(e.Value(Name_Labels).Split("|"), l) + If Not e.Value(Name_Labels_Excluded).IsEmptyString Then LabelsExcluded.ListAddList(e.Value(Name_Labels_Excluded).Split("|"), l) + If Not e.Value(Name_Sites).IsEmptyString Then Sites.ListAddList(e.Value(Name_Sites).Split("|"), l) + If Not e.Value(Name_Sites_Excluded).IsEmptyString Then SitesExcluded.ListAddList(e.Value(Name_Sites_Excluded).Split("|"), l) + End Sub + Protected Function Export(ByVal e As EContainer) As EContainer + e.AddRange({New EContainer(Name_Name, Name), + New EContainer(Name_Temporary, CInt(Temporary)), + New EContainer(Name_Favorite, CInt(Favorite)), + New EContainer(Name_ReadyForDownload, ReadyForDownload.BoolToInteger), + New EContainer(Name_ReadyForDownloadIgnore, ReadyForDownloadIgnore.BoolToInteger), + New EContainer(Name_Labels, Labels.ListToString("|")), + New EContainer(Name_Labels_Excluded, LabelsExcluded.ListToString("|")), + New EContainer(Name_Sites, Sites.ListToString("|")), + New EContainer(Name_Sites_Excluded, SitesExcluded.ListToString("|"))}) + Return e + End Function #Region "IDisposable Support" Protected disposedValue As Boolean = False Protected Overridable Overloads Sub Dispose(ByVal disposing As Boolean) If Not disposedValue Then - If disposing Then Labels.Clear() + If disposing Then + Labels.Clear() + LabelsExcluded.Clear() + Sites.Clear() + SitesExcluded.Clear() + End If disposedValue = True End If End Sub diff --git a/SCrawler/Editors/LabelsForm.Designer.vb b/SCrawler/Editors/LabelsForm.Designer.vb index 05cc240..5366851 100644 --- a/SCrawler/Editors/LabelsForm.Designer.vb +++ b/SCrawler/Editors/LabelsForm.Designer.vb @@ -79,11 +79,11 @@ Partial Friend Class LabelsForm : Inherits System.Windows.Forms.Form Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font Me.ClientSize = New System.Drawing.Size(374, 421) Me.Controls.Add(CONTAINER_MAIN) + Me.Icon = Global.SCrawler.My.Resources.Resources.TagIcon_32 Me.KeyPreview = True Me.MinimizeBox = False Me.MinimumSize = New System.Drawing.Size(390, 460) Me.Name = "LabelsForm" - Me.ShowIcon = False Me.ShowInTaskbar = False Me.Text = "Labels" CONTAINER_MAIN.ContentPanel.ResumeLayout(False) diff --git a/SCrawler/Editors/LabelsForm.vb b/SCrawler/Editors/LabelsForm.vb index a2b9256..f74c65b 100644 --- a/SCrawler/Editors/LabelsForm.vb +++ b/SCrawler/Editors/LabelsForm.vb @@ -18,6 +18,8 @@ Friend Class LabelsForm Get If Not _Source Is Nothing Then Return _Source + ElseIf AddNoParsed Then + Return ListAddList(Nothing, Settings.Labels).ListAddValue(LabelsKeeper.NoParsedUser, LAP.NotContainsOnly) Else Return Settings.Labels End If @@ -27,8 +29,10 @@ Friend Class LabelsForm Friend Property MultiUser As Boolean = False Friend Property MultiUserClearExists As Boolean = False Friend Property WithDeleteButton As Boolean = False - Friend Sub New(ByVal LabelsArr As IEnumerable(Of String)) + Private ReadOnly AddNoParsed As Boolean = False + Friend Sub New(ByVal LabelsArr As IEnumerable(Of String), Optional ByVal AddNoParsed As Boolean = False) InitializeComponent() + Me.AddNoParsed = AddNoParsed LabelsList = New List(Of String) LabelsList.ListAddList(LabelsArr) MyDefs = New DefaultFormOptions(Me, Settings.Design) diff --git a/SCrawler/Editors/SiteSelectionForm.Designer.vb b/SCrawler/Editors/SiteSelectionForm.Designer.vb index bcbe3b4..d80436f 100644 --- a/SCrawler/Editors/SiteSelectionForm.Designer.vb +++ b/SCrawler/Editors/SiteSelectionForm.Designer.vb @@ -87,7 +87,7 @@ Namespace Editors Me.ShowIcon = False Me.ShowInTaskbar = False Me.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide - Me.Text = "Select Site" + Me.Text = "Sites" CONTAINER_MAIN.ContentPanel.ResumeLayout(False) CONTAINER_MAIN.ResumeLayout(False) CONTAINER_MAIN.PerformLayout() diff --git a/SCrawler/MainFrame.Designer.vb b/SCrawler/MainFrame.Designer.vb index 121bb95..9d6991b 100644 --- a/SCrawler/MainFrame.Designer.vb +++ b/SCrawler/MainFrame.Designer.vb @@ -910,7 +910,6 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form Private WithEvents BTT_ADD_USER As ToolStripButton Private WithEvents BTT_DELETE_USER As ToolStripButton Private WithEvents BTT_DOWN_SELECTED As ToolStripKeyMenuItem - Private WithEvents MENU_DOWN_ALL As ToolStripDropDownButton Private WithEvents Toolbar_BOTTOM As StatusStrip Private WithEvents PR_MAIN As ToolStripProgressBar Private WithEvents LBL_STATUS As ToolStripStatusLabel @@ -986,4 +985,5 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form Private WithEvents BTT_SILENT_MODE As ToolStripMenuItem Friend WithEvents BTT_DOWN_AUTOMATION_PAUSE As ToolStripMenuItem Private WithEvents BTT_TRAY_FEED_SHOW As ToolStripMenuItem + Friend WithEvents MENU_DOWN_ALL As ToolStripDropDownButton End Class \ No newline at end of file diff --git a/SCrawler/MainFrame.vb b/SCrawler/MainFrame.vb index 8247d2a..d6334b5 100644 --- a/SCrawler/MainFrame.vb +++ b/SCrawler/MainFrame.vb @@ -52,7 +52,7 @@ Public Class MainFrame End Sub #End Region #Region "Form handlers" - Private Sub MainFrame_Load(sender As Object, e As EventArgs) Handles Me.Load + Private Async Sub MainFrame_Load(sender As Object, e As EventArgs) Handles Me.Load If _VideoDownloadingMode Then GoTo FormClosingInvoker If Now.Month.ValueBetween(6, 8) Then Text = "SCrawler: Happy LGBT Pride Month! :-)" Settings.DeleteCachePath() @@ -115,7 +115,7 @@ Public Class MainFrame AddHandler Settings.Groups.Deleted, AddressOf Settings.Automation.GROUPS_Deleted AddHandler Settings.Automation.PauseDisabled, AddressOf MainFrameObj.PauseButtons.UpdatePauseButtons _UFinit = False - Settings.Automation.Start(True) + Await Settings.Automation.Start(True) UpdatePauseButtonsVisibility() GoTo EndFunction FormClosingInvoker: @@ -376,13 +376,13 @@ CloseResume: DownloadSelectedUser(DownUserLimits.None, e.IncludeInTheFeed) End Sub Private Sub BTT_DOWN_ALL_KeyClick(sender As Object, e As MyKeyEventArgs) Handles BTT_DOWN_ALL.KeyClick - Downloader.AddRange(Settings.Users.Where(Function(u) u.ReadyForDownload And u.Exists), e.IncludeInTheFeed) + Downloader.AddRange(Settings.GetUsers(Function(u) u.ReadyForDownload And u.Exists), e.IncludeInTheFeed) End Sub Private Sub BTT_DOWN_SITE_KeyClick(sender As Object, e As MyKeyEventArgs) Handles BTT_DOWN_SITE.KeyClick DownloadSiteFull(True, e.IncludeInTheFeed) End Sub Private Sub BTT_DOWN_ALL_FULL_KeyClick(sender As Object, e As MyKeyEventArgs) Handles BTT_DOWN_ALL_FULL.KeyClick - Downloader.AddRange(Settings.Users.Where(Function(u) u.Exists), e.IncludeInTheFeed) + Downloader.AddRange(Settings.GetUsers(UserExistsPredicate), e.IncludeInTheFeed) End Sub Private Sub BTT_DOWN_SITE_FULL_KeyClick(sender As Object, e As MyKeyEventArgs) Handles BTT_DOWN_SITE_FULL.KeyClick DownloadSiteFull(False, e.IncludeInTheFeed) @@ -395,18 +395,8 @@ CloseResume: Settings.LatestDownloadedSites.AddRange(f.SelectedSites) Settings.LatestDownloadedSites.Update() If f.SelectedSites.Count > 0 Then - Downloader.AddRange(Settings.Users.SelectMany(Function(ByVal u As IUserData) As IEnumerable(Of IUserData) - If u.IsCollection Then - Return DirectCast(u, UserDataBind).Collections. - Where(Function(uu) f.SelectedSites.Contains(uu.Site) And u.Exists And - (Not ReadyForDownloadOnly Or uu.ReadyForDownload)) - ElseIf f.SelectedSites.Contains(u.Site) And u.Exists And - (Not ReadyForDownloadOnly Or u.ReadyForDownload) Then - Return {u} - Else - Return New IUserData() {} - End If - End Function), IncludeInTheFeed) + Downloader.AddRange(Settings.GetUsers(Function(u) f.SelectedSites.Contains(u.Site) And u.Exists And + (Not ReadyForDownloadOnly Or u.ReadyForDownload)), IncludeInTheFeed) End If End If End Using @@ -421,7 +411,7 @@ CloseResume: End Sub Private Sub GROUPS_Updated(ByVal Sender As Groups.DownloadGroup) Dim i% = MENU_DOWN_ALL.DropDownItems.IndexOf(Sender.GetControl) - ControlInvoke(Toolbar_TOP, MENU_DOWN_ALL, Sub() MENU_DOWN_ALL.DropDownItems(i).Text = Sender.ToString) + If i >= 0 Then ControlInvoke(Toolbar_TOP, MENU_DOWN_ALL, Sub() MENU_DOWN_ALL.DropDownItems(i).Text = Sender.ToString) End Sub Private Sub GROUPS_Deleted(ByVal Sender As Groups.DownloadGroup) MENU_DOWN_ALL.DropDownItems.Remove(Sender.GetControl) @@ -442,9 +432,9 @@ CloseResume: ControlInvokeFast(Toolbar_TOP, BTT_DOWN_AUTOMATION_PAUSE, Sub() BTT_DOWN_AUTOMATION_PAUSE.Visible = b) ControlInvokeFast(Me, Sub() BTT_TRAY_PAUSE_AUTOMATION.Visible = b) End Sub - Private Sub BTT_DOWN_AUTOMATION_Click(sender As Object, e As EventArgs) Handles BTT_DOWN_AUTOMATION.Click + Private Async Sub BTT_DOWN_AUTOMATION_Click(sender As Object, e As EventArgs) Handles BTT_DOWN_AUTOMATION.Click Using f As New SchedulerEditorForm : f.ShowDialog() : End Using - Settings.Automation.Start(False) + Await Settings.Automation.Start(False) UpdatePauseButtonsVisibility() MainFrameObj.PauseButtons.UpdatePauseButtons() End Sub @@ -1438,21 +1428,14 @@ ResumeDownloadingOperation: Friend Sub User_OnUserUpdated(ByVal User As IUserData) UserListUpdate(User, False) End Sub - Private _LogColorChanged As Boolean = False Private Sub Downloader_UpdateJobsCount(ByVal TotalCount As Integer) - Dim a As Action = Sub() LBL_JOBS_COUNT.Text = IIf(TotalCount = 0, String.Empty, $"[Jobs {TotalCount}]") - If Toolbar_BOTTOM.InvokeRequired Then Toolbar_BOTTOM.Invoke(a) Else a.Invoke - If Not _LogColorChanged AndAlso Not MyMainLOG.IsEmptyString Then - MainFrameObj.UpdateLogButton() - _LogColorChanged = True - ElseIf _LogColorChanged And MyMainLOG.IsEmptyString Then - MainFrameObj.UpdateLogButton() - _LogColorChanged = False - End If + ControlInvokeFast(Toolbar_BOTTOM, LBL_JOBS_COUNT, Sub() LBL_JOBS_COUNT.Text = IIf(TotalCount = 0, String.Empty, $"[Jobs {TotalCount}]")) + MainFrameObj.UpdateLogButton() End Sub Private Sub Downloader_Downloading(ByVal Value As Boolean) - Dim a As Action = Sub() BTT_DOWN_STOP.Enabled = Value Or Downloader.Working - If Toolbar_TOP.InvokeRequired Then Toolbar_TOP.Invoke(a) Else a.Invoke + Dim __isDownloading As Boolean = Value Or Downloader.Working + ControlInvokeFast(Toolbar_TOP, BTT_DOWN_STOP, Sub() BTT_DOWN_STOP.Enabled = __isDownloading) + ControlInvokeFast(Me, Sub() TrayIcon.Icon = If(__isDownloading, My.Resources.ArrowDownIcon_Blue_24, My.Resources.RainbowIcon_48)) End Sub #End Region End Class \ No newline at end of file diff --git a/SCrawler/MainFrameObjects.vb b/SCrawler/MainFrameObjects.vb index 1cdd10d..06b2353 100644 --- a/SCrawler/MainFrameObjects.vb +++ b/SCrawler/MainFrameObjects.vb @@ -48,35 +48,31 @@ Friend Class MainFrameObjects #End Region #Region "Form functions" Friend Sub Focus(Optional ByVal Show As Boolean = False) - If Not MF.Visible And Show Then MF.Show() - If MF.Visible Then MF.BringToFront() : MF.Activate() + ControlInvokeFast(MF, Sub() + If Not MF.Visible And Show Then MF.Show() + If MF.Visible Then MF.BringToFront() : MF.Activate() + End Sub) End Sub Friend Sub ChangeCloseVisible() - Dim a As Action = Sub() MF.BTT_TRAY_CLOSE_NO_SCRIPT.Visible = Settings.ClosingCommand.Attribute And Not Settings.ClosingCommand.IsEmptyString - If MF.TRAY_CONTEXT.InvokeRequired Then MF.TRAY_CONTEXT.Invoke(a) Else a.Invoke + ControlInvokeFast(MF.TRAY_CONTEXT, Sub() MF.BTT_TRAY_CLOSE_NO_SCRIPT.Visible = + Settings.ClosingCommand.Attribute And Not Settings.ClosingCommand.IsEmptyString) End Sub Friend Sub UpdateLogButton() MyMainLOG_UpdateLogButton(MF.BTT_LOG, MF.Toolbar_TOP) End Sub #End Region #Region "Notifications" - Friend Overloads Sub ShowNotification(ByVal Sender As SettingsCLS.NotificationObjects, ByVal Message As String) - If Settings.ProcessNotification(Sender) Then MF.TrayIcon.ShowBalloonTip(2000, MF.TrayIcon.BalloonTipTitle, Message, ToolTipIcon.Info) - End Sub - Friend Overloads Sub ShowNotification(ByVal Sender As SettingsCLS.NotificationObjects, ByVal Message As String, ByVal Title As String) - If Settings.ProcessNotification(Sender) Then MF.TrayIcon.ShowBalloonTip(2000, Title, Message, ToolTipIcon.Info) - End Sub - Friend Overloads Sub ShowNotification(ByVal Sender As SettingsCLS.NotificationObjects, ByVal Message As String, ByVal Title As String, ByVal Icon As ToolTipIcon) - If Settings.ProcessNotification(Sender) Then MF.TrayIcon.ShowBalloonTip(2000, Title, Message, Icon) + Private Const NotificationInternalKey As String = "NotificationInternalKey" + Friend Sub ShowNotification(ByVal Sender As SettingsCLS.NotificationObjects, ByVal Message As String) + If Settings.ProcessNotification(Sender) Then + Using n As New Notification(Message) With {.Key = NotificationInternalKey} : n.Show() : End Using + End If End Sub Friend Sub ClearNotifications() Notificator.Clear() End Sub Private Sub Notificator_OnClicked(ByVal Key As String) Handles Notificator.OnClicked - If Settings.Automation Is Nothing OrElse Not Settings.Automation.NotificationClicked(Key) Then - If Not MF.Visible Then MF.Show() - Focus() - End If + If Key = NotificationInternalKey OrElse Settings.Automation Is Nothing OrElse Not Settings.Automation.NotificationClicked(Key) Then Focus(True) End Sub #End Region End Class \ No newline at end of file diff --git a/SCrawler/MainMod.vb b/SCrawler/MainMod.vb index f70de95..44afa60 100644 --- a/SCrawler/MainMod.vb +++ b/SCrawler/MainMod.vb @@ -101,6 +101,7 @@ Friend Module MainMod Friend MainFrameObj As MainFrameObjects Friend ReadOnly ParsersDataDateProvider As New ADateTime(ADateTime.Formats.BaseDateTime) Friend ReadOnly FeedVideoLengthProvider As New ADateTime("hh\:mm\:ss") With {.TimeParseMode = ADateTime.TimeModes.TimeSpan} + Friend ReadOnly UserExistsPredicate As New FPredicate(Of IUserData)(Function(u) u.Exists) Friend ReadOnly LogConnector As New LogHost #Region "File name operations" Friend FileDateAppenderProvider As IFormatProvider diff --git a/SCrawler/My Project/AssemblyInfo.vb b/SCrawler/My Project/AssemblyInfo.vb index 0282539..0e9b979 100644 --- a/SCrawler/My Project/AssemblyInfo.vb +++ b/SCrawler/My Project/AssemblyInfo.vb @@ -32,6 +32,6 @@ Imports System.Runtime.InteropServices ' by using the '*' as shown below: ' - - + + diff --git a/SCrawler/My Project/Resources.Designer.vb b/SCrawler/My Project/Resources.Designer.vb index e08c57b..424a8b3 100644 --- a/SCrawler/My Project/Resources.Designer.vb +++ b/SCrawler/My Project/Resources.Designer.vb @@ -360,6 +360,16 @@ Namespace My.Resources End Get End Property + ''' + ''' Looks up a localized resource of type System.Drawing.Icon similar to (Icon). + ''' + Friend ReadOnly Property TagIcon_32() As System.Drawing.Icon + Get + Dim obj As Object = ResourceManager.GetObject("TagIcon_32", resourceCulture) + Return CType(obj,System.Drawing.Icon) + End Get + End Property + ''' ''' Looks up a localized resource of type System.Drawing.Icon similar to (Icon). ''' diff --git a/SCrawler/My Project/Resources.resx b/SCrawler/My Project/Resources.resx index e3889b7..6583640 100644 --- a/SCrawler/My Project/Resources.resx +++ b/SCrawler/My Project/Resources.resx @@ -211,4 +211,7 @@ ..\Content\Pictures\PinPic_32.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + ..\Content\Icons\TagIcon_32.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + \ No newline at end of file diff --git a/SCrawler/PluginsEnvironment/Hosts/SettingsHost.vb b/SCrawler/PluginsEnvironment/Hosts/SettingsHost.vb index 15ed660..73ab6d8 100644 --- a/SCrawler/PluginsEnvironment/Hosts/SettingsHost.vb +++ b/SCrawler/PluginsEnvironment/Hosts/SettingsHost.vb @@ -187,7 +187,7 @@ Namespace Plugin.Hosts If m.MemberType = MemberTypes.Property Then PropList.Add(New PropertyValueHost(Source, m)) With DirectCast(m, PropertyInfo) - If .PropertyType Is GetType(Response) Then _ResponserGetMethod = .GetMethod + If .PropertyType Is GetType(Response) AndAlso m.GetCustomAttribute(Of DoNotUse)() Is Nothing Then _ResponserGetMethod = .GetMethod End With End If With m.GetCustomAttributes() diff --git a/SCrawler/SCrawler.vbproj b/SCrawler/SCrawler.vbproj index 5c55b7b..a00ca80 100644 --- a/SCrawler/SCrawler.vbproj +++ b/SCrawler/SCrawler.vbproj @@ -563,6 +563,7 @@ + PreserveNewest diff --git a/SCrawler/SettingsCLS.vb b/SCrawler/SettingsCLS.vb index 3012f33..b84817f 100644 --- a/SCrawler/SettingsCLS.vb +++ b/SCrawler/SettingsCLS.vb @@ -449,6 +449,24 @@ Friend Class SettingsCLS : Implements IDisposable End If Return Nothing End Function + Friend Function GetUsers(ByVal Predicate As Predicate(Of IUserData)) As IEnumerable(Of IUserData) + With Users + If .Count > 0 Then + Dim fp As Func(Of IUserData, Boolean) = FPredicate(Of IUserData).ToFunc(Predicate) + Return .SelectMany(Of IUserData)(Function(ByVal user As IUserData) As IEnumerable(Of IUserData) + If user.IsCollection Then + With DirectCast(user, UserDataBind) + If .Count > 0 Then Return .Where(fp) + End With + Else + If Predicate.Invoke(user) Then Return {user} + End If + Return New IUserData() {} + End Function) + End If + End With + Return New IUserData() {} + End Function #End Region Friend Sub UpdateBlackList() If BlackList.Count > 0 Then