diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index a148167..d8a0a35 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -12,7 +12,7 @@ A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: -1. Profile URL: +1. **Profile URL**: 2. Do something 3. See error diff --git a/.github/ISSUE_TEMPLATE/plugin_add.md b/.github/ISSUE_TEMPLATE/plugin_add.md index 38fdeaa..a6d1c39 100644 --- a/.github/ISSUE_TEMPLATE/plugin_add.md +++ b/.github/ISSUE_TEMPLATE/plugin_add.md @@ -1,6 +1,6 @@ --- -name: Add plugin -about: Add plugin to plugin list +name: I developed a plugin for SCrawler +about: I developed a plugin for SCrawler. Add plugin to plugin list. title: "[NEW PLUGIN]" labels: 'New Plugin' assignees: '' diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5c32516..8166bd9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,30 +3,35 @@ I welcome requests! Follow these steps to contribute: 1. Find an [issue](https://github.com/AAndyProgram/SCrawler/issues) that needs assistance. -2. Let me know you are working on it by posting a comment on the issue. -3. If you find an error in the code, please provide a link to the file and the line number. -4. If you have a suggestion to change the code, you can post a block of code to replace. I don't currently have time to learn pull requests, so it might work this way. +1. Let me know you are working on it by posting a comment on the issue. +1. If you find an error in the code, please provide a link to the file and the line number. +1. If you have a code change suggestion, you can post a replacement code block. I also accept pull requests. # How to build from source 1. Delete the "PersonalUtilities" project from the solution. -2. Add the latest version of the "PersonalUtilities.dll" library (from the [latest release](https://github.com/AAndyProgram/SCrawler/releases/latest)). -3. Import PersonalUtilities.Functions for the whole project. +1. Add the latest version of the "PersonalUtilities.dll" library (from the [latest release](https://github.com/AAndyProgram/SCrawler/releases/latest)). +1. Import PersonalUtilities.Functions for the whole project. **Always use the correct "PersonalUtilities.dll" library. You must download this library from the release of the code you downloaded.** # How to request a new site 1. Check [issues](https://github.com/AAndyProgram/SCrawler/issues) (open and [closed](https://github.com/AAndyProgram/SCrawler/issues?q=is%3Aissue+is%3Aclosed)) and [discussions](https://github.com/AAndyProgram/SCrawler/discussions) to find your issue. Perhaps I have already answered your request. -2. If you don't find anything, create a new issue with your request. I usually reply as soon as possible (within the next few hours). - - If I'm interested in a site you want to add, it may be added in future releases. - - If the site has an API that does not require authorization, it may be added in the coming releases. - - You can make it faster by posting a link to the API. **I don't use OAuth authentication** in my application, so if it's not too hard to make a new parsing algorithm **without OAuth** authorization, I can start developing it in the coming days. Otherwise, I need time to figure out how to do it. - - If the site does not have an API that does not require authorization, this may take some time. - - If you will be posting request urls **without OAuth** authentication, I might consider adding your site if I have time. - - If I'm **not** interested in the site you want to add, you can pay to have it added by making a donation of approximately $10. **But before that, you still need to create an issue. If I'm not interested, you can offer me a deal to develop it for money. I'll check the site you want to add, check the availability of the API and tell you how much time I need to develop it and the price. If you agree, I will do it.** [![ko-fi](https://www.ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/andyprogram) - - +1. If you don't find anything, create a new issue with your request. I usually reply as soon as possible (within the next few hours). + +# Requirements for new site requests + +**Attention! I'll add a new site only if I'm interested. I also have a life, and any development takes time. ** + +- Post a link to the site's API +- Post request URLs **without OAuth** authentication +- Post a **complete cURL** request which provides the required information (JSON is better) + +**I don't use OAuth authentication** in my application, so if it's not too hard to make a new parsing algorithm **without OAuth** authorization, I can start developing it in the coming days. Otherwise, I need time to figure out how to do it. + +If I'm interested in a site you want to add, it may be added in future releases. + # Sites I will never develop - Facebook diff --git a/Changelog.md b/Changelog.md index d93f439..8b84bdd 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,16 @@ +# 2022.6.3.0 + +Changed version numbering method. From now on, new versions will be numbered by release date (YYYY.M.D) + +**Attention! Starting with this release, SCrawler may not work on windows 7 and 8 or may not work correctly. All future releases will only be guaranteed to work on windows 10 and 11.** + +- Added + - **Automation** (downloading data automatically every ```X``` minutes) + - Expanded settings for Instagram tagged posts that are downloaded for the first time. +- Fixed + - Videos hosted on Reddit that are downloaded via m3u8 playlists are missing an audio track. + - Instagram hash not able to be auto-filled from cookies + # 3.0.0.10 - Added diff --git a/ProgramScreenshots/MainWindowGroups.png b/ProgramScreenshots/MainWindowGroups.png index 225a380..784b3fb 100644 Binary files a/ProgramScreenshots/MainWindowGroups.png and b/ProgramScreenshots/MainWindowGroups.png differ diff --git a/ProgramScreenshots/SettingsAutoDownloader.png b/ProgramScreenshots/SettingsAutoDownloader.png new file mode 100644 index 0000000..b38f96f Binary files /dev/null and b/ProgramScreenshots/SettingsAutoDownloader.png differ diff --git a/ProgramScreenshots/SettingsGlobalDownloading.png b/ProgramScreenshots/SettingsGlobalDownloading.png index 051fa82..d2bedd1 100644 Binary files a/ProgramScreenshots/SettingsGlobalDownloading.png and b/ProgramScreenshots/SettingsGlobalDownloading.png differ diff --git a/ProgramScreenshots/SettingsSiteInstagram.png b/ProgramScreenshots/SettingsSiteInstagram.png index a83fc3b..00fd911 100644 Binary files a/ProgramScreenshots/SettingsSiteInstagram.png and b/ProgramScreenshots/SettingsSiteInstagram.png differ diff --git a/ProgramScreenshots/SettingsSiteReddit.png b/ProgramScreenshots/SettingsSiteReddit.png index bf63aa9..5029d26 100644 Binary files a/ProgramScreenshots/SettingsSiteReddit.png and b/ProgramScreenshots/SettingsSiteReddit.png differ diff --git a/ProgramScreenshots/SettingsSiteTwitter.png b/ProgramScreenshots/SettingsSiteTwitter.png index 55a1b68..6023ee6 100644 Binary files a/ProgramScreenshots/SettingsSiteTwitter.png and b/ProgramScreenshots/SettingsSiteTwitter.png differ diff --git a/ProgramsComparison.md b/ProgramsComparison.md index f163041..3acca59 100644 --- a/ProgramsComparison.md +++ b/ProgramsComparison.md @@ -5,6 +5,8 @@ https://www.4kdownload.com/products/product-stogram | Option | SCrawler | 4K Stogram | | ---- | ---- | ---- | | User managament | **Advanced** | Primitive | +| Automatic downloads | **Yes** | No | +| Downloading groups | **Yes** | No | | Labeling users | **Yes** | No | | Filtering | **Yes** | No | | Collections | **Yes** | No | @@ -44,6 +46,8 @@ https://github.com/RipMeApp/ripme | Option | SCrawler | RipMeApp | | ---- | ---- | ---- | | User managament | **Advanced** | No | +| Automatic downloads | **Yes** | No | +| Downloading groups | **Yes** | No | | Labeling users | **Yes** | No | | Filtering | **Yes** | No | | Collections | **Yes** | No | @@ -75,4 +79,4 @@ https://github.com/mikf/gallery-dl **CLI tool**! Configured with JSON files only. Users need to learn complex configuration options, JSON, commands to use that tool. Very difficult to configure. -SCrawler has advanced user management, collections, labels, groups, a beautiful view, GUI, the ability to add plugins for other sites and much more. Just try it and compare. \ No newline at end of file +SCrawler has advanced user management, collections, labels, groups, automatic downloads, a beautiful view, GUI, the ability to add plugins for other sites and much more. Just try it and compare. \ No newline at end of file diff --git a/README.md b/README.md index d0a0608..0c95f56 100644 --- a/README.md +++ b/README.md @@ -21,20 +21,24 @@ Do you like this program? Consider adding to my coffee fund by making a donation - Download pictures and videos from users' profiles and subreddits: - Reddit images; - Reddit galleries of images; - - Redgifs hosted videos (https://www.redgifs.com/); - - Reddit hosted videos (downloading Reddit hosted video is going through ffmpeg (**ffmpeg only works with the x64 program**)); + - Reddit videos (downloading Reddit hosted video is going through ffmpeg (**ffmpeg only works with the x64 program**)); + - Redgifs videos (https://www.redgifs.com/); - Twitter images and videos; - - Instagram images and videos. - - Imgur images, galleries and videos - - Gfycat videos + - Instagram images and videos; + - Instagram tagged posts; + - Instagram stories; + - Imgur images, galleries and videos; + - Gfycat videos; - [Other](#supported-sites) supported sites -- Parse [channel and view data](https://github.com/AAndyProgram/SCrawler/wiki/Channels). -- Download [saved Reddit, Twitter and Instagram posts](https://github.com/AAndyProgram/SCrawler/wiki/Home#saved-posts). -- Add users from parsed channel. -- **Advanced user management.** -- Labeling users. -- Adding users to favorites and temporary. -- Filter exists users by label or group. +- Parse [channel and view data](https://github.com/AAndyProgram/SCrawler/wiki/Channels) +- Download [saved Reddit, Twitter and Instagram posts](https://github.com/AAndyProgram/SCrawler/wiki/Home#saved-posts) +- Add users from parsed channel +- **Advanced user management** +- **Automation** (downloading data automatically every ```X``` minutes) +- Labeling users +- Create download groups +- Adding users to favorites and temporary +- Filter exists users by label or group - Selection of media types you want to download (images only, videos only, both) - Download a special video, image or gallery - Making collections (grouping users into collections) @@ -75,7 +79,7 @@ Read [here](CONTRIBUTING.md#how-to-request-a-new-site) about # Requirements -- Windows 7, 8, 9, 10, 11 with NET Framework 4.6.1 or higher (v4.6.1 must be installed). You can check version compatibility with this [tool](Tools/NET.FrameworkVersion.ps1). +- Windows 10, 11 with NET Framework 4.6.1 or higher (v4.6.1 must be installed). You can check version compatibility with this [tool](Tools/NET.FrameworkVersion.ps1). - Authorization [cookies](https://github.com/AAndyProgram/SCrawler/wiki/Settings#how-to-set-up-cookies) and [tokens](https://github.com/AAndyProgram/SCrawler/wiki/Settings#how-to-find-twitter-tokens) for Twitter (if you want to download data from Twitter) - Authorization [cookies](https://github.com/AAndyProgram/SCrawler/wiki/Settings#how-to-set-up-cookies) and [Hash](https://github.com/AAndyProgram/SCrawler/wiki/Settings#instagram) for Instagram (if you want to download data from Instagram), [Hash 2](https://github.com/AAndyProgram/SCrawler/wiki/Settings#how-to-find-instagram-hash-2) for saved Instagram posts, Instagram [stories authorization headers](https://github.com/AAndyProgram/SCrawler/wiki/Settings#how-to-find-instagram-stories-authorization-headers) for Stories and Tagged data - ffmpeg library for downloading videos hosted on Reddit (you can download it from the [official repo](https://github.com/GyanD/codexffmpeg/releases/tag/2021-01-12-git-ca21cb1e36) or [from my first release](https://github.com/AAndyProgram/SCrawler/releases/download/1.0.0.0/ffmpeg.zip)). **ffmpeg only works with the x64 version of the program.** @@ -86,7 +90,7 @@ Read [here](CONTRIBUTING.md#how-to-request-a-new-site) about # Installation -**Just unzip the program archive to any folder, copy the file ```ffmpeg.exe``` into it and enjoy.** :blush: +**Just download the [latest release](https://github.com/AAndyProgram/SCrawler/releases/latest), unzip the program archive to any folder, copy the file ```ffmpeg.exe``` into it and enjoy.** :blush: **Don't put program in the ```Program Files``` system folder (this is portable program and program settings are stored in the program folder)** diff --git a/SCrawler.Plugin.LPSG/My Project/AssemblyInfo.vb b/SCrawler.Plugin.LPSG/My Project/AssemblyInfo.vb index 294c942..aff7654 100644 --- a/SCrawler.Plugin.LPSG/My Project/AssemblyInfo.vb +++ b/SCrawler.Plugin.LPSG/My Project/AssemblyInfo.vb @@ -32,6 +32,6 @@ Imports System.Runtime.InteropServices ' by using the '*' as shown below: ' - - + + diff --git a/SCrawler.Plugin.LPSG/SiteSettings.vb b/SCrawler.Plugin.LPSG/SiteSettings.vb index 3e721c4..239d091 100644 --- a/SCrawler.Plugin.LPSG/SiteSettings.vb +++ b/SCrawler.Plugin.LPSG/SiteSettings.vb @@ -91,7 +91,7 @@ Public Class SiteSettings : Implements ISiteSettings Public Function GetSpecialData(ByVal URL As String, ByVal Path As String, ByVal AskForPath As Boolean) As IEnumerable(Of PluginUserMedia) Implements ISiteSettings.GetSpecialData Return Nothing End Function - Public Function Available(ByVal What As ISiteSettings.Download) As Boolean Implements ISiteSettings.Available + Public Function Available(ByVal What As ISiteSettings.Download, ByVal Silent As Boolean) As Boolean Implements ISiteSettings.Available Return True End Function Public Function ReadyToDownload(ByVal What As ISiteSettings.Download) As Boolean Implements ISiteSettings.ReadyToDownload diff --git a/SCrawler.Plugin.XVIDEOS/My Project/AssemblyInfo.vb b/SCrawler.Plugin.XVIDEOS/My Project/AssemblyInfo.vb index 74f2697..c06eed5 100644 --- a/SCrawler.Plugin.XVIDEOS/My Project/AssemblyInfo.vb +++ b/SCrawler.Plugin.XVIDEOS/My Project/AssemblyInfo.vb @@ -32,6 +32,6 @@ Imports System.Runtime.InteropServices ' by using the '*' as shown below: ' - - + + diff --git a/SCrawler.Plugin.XVIDEOS/SiteSettings.vb b/SCrawler.Plugin.XVIDEOS/SiteSettings.vb index 84ff274..6adce0a 100644 --- a/SCrawler.Plugin.XVIDEOS/SiteSettings.vb +++ b/SCrawler.Plugin.XVIDEOS/SiteSettings.vb @@ -83,7 +83,7 @@ Public Class SiteSettings : Implements ISiteSettings End If End Sub #Region "Downloading" - Public Function Available(ByVal What As ISiteSettings.Download) As Boolean Implements ISiteSettings.Available + Public Function Available(ByVal What As ISiteSettings.Download, ByVal Silent As Boolean) As Boolean Implements ISiteSettings.Available Return UseM3U8 End Function Public Function ReadyToDownload(ByVal What As ISiteSettings.Download) As Boolean Implements ISiteSettings.ReadyToDownload diff --git a/SCrawler.PluginProvider/Interfaces/ISiteSettings.vb b/SCrawler.PluginProvider/Interfaces/ISiteSettings.vb index 13bd746..eb42605 100644 --- a/SCrawler.PluginProvider/Interfaces/ISiteSettings.vb +++ b/SCrawler.PluginProvider/Interfaces/ISiteSettings.vb @@ -33,7 +33,7 @@ Namespace Plugin Sub EndUpdate() #End Region #Region "Site availability" - Function Available(ByVal What As Download) As Boolean + Function Available(ByVal What As Download, ByVal Silent As Boolean) As Boolean Function ReadyToDownload(ByVal What As Download) As Boolean #End Region #Region "Downloading" diff --git a/SCrawler.PluginProvider/My Project/AssemblyInfo.vb b/SCrawler.PluginProvider/My Project/AssemblyInfo.vb index 2006370..5d309d8 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.sln b/SCrawler.sln index 0123c75..9a208bb 100644 --- a/SCrawler.sln +++ b/SCrawler.sln @@ -21,6 +21,8 @@ Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "SCrawler.Plugin.LPSG", "SCr EndProject Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "SCrawler.Plugin.XVIDEOS", "SCrawler.Plugin.XVIDEOS\SCrawler.Plugin.XVIDEOS.vbproj", "{CCCF47F4-C97C-4193-AC4B-C56DF2F9AA8A}" EndProject +Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "PersonalUtilities.Notifications", "..\..\MyUtilities\PersonalUtilities.Notifications\PersonalUtilities.Notifications.vbproj", "{FC532253-1AB3-4DEF-A28A-DFDD9A481EB2}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -91,6 +93,18 @@ Global {CCCF47F4-C97C-4193-AC4B-C56DF2F9AA8A}.Release|x64.Build.0 = Release|x64 {CCCF47F4-C97C-4193-AC4B-C56DF2F9AA8A}.Release|x86.ActiveCfg = Release|x86 {CCCF47F4-C97C-4193-AC4B-C56DF2F9AA8A}.Release|x86.Build.0 = Release|x86 + {FC532253-1AB3-4DEF-A28A-DFDD9A481EB2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FC532253-1AB3-4DEF-A28A-DFDD9A481EB2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FC532253-1AB3-4DEF-A28A-DFDD9A481EB2}.Debug|x64.ActiveCfg = Debug|x64 + {FC532253-1AB3-4DEF-A28A-DFDD9A481EB2}.Debug|x64.Build.0 = Debug|x64 + {FC532253-1AB3-4DEF-A28A-DFDD9A481EB2}.Debug|x86.ActiveCfg = Debug|x86 + {FC532253-1AB3-4DEF-A28A-DFDD9A481EB2}.Debug|x86.Build.0 = Debug|x86 + {FC532253-1AB3-4DEF-A28A-DFDD9A481EB2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FC532253-1AB3-4DEF-A28A-DFDD9A481EB2}.Release|Any CPU.Build.0 = Release|Any CPU + {FC532253-1AB3-4DEF-A28A-DFDD9A481EB2}.Release|x64.ActiveCfg = Release|x64 + {FC532253-1AB3-4DEF-A28A-DFDD9A481EB2}.Release|x64.Build.0 = Release|x64 + {FC532253-1AB3-4DEF-A28A-DFDD9A481EB2}.Release|x86.ActiveCfg = Release|x86 + {FC532253-1AB3-4DEF-A28A-DFDD9A481EB2}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/SCrawler/API/Base/ProfileSaved.vb b/SCrawler/API/Base/ProfileSaved.vb index 842c0ac..525cb8d 100644 --- a/SCrawler/API/Base/ProfileSaved.vb +++ b/SCrawler/API/Base/ProfileSaved.vb @@ -21,7 +21,7 @@ Namespace API.Base Friend Sub Download(ByVal Token As CancellationToken) Try If HOST.Source.ReadyToDownload(PDownload.SavedPosts) Then - If HOST.Available(PDownload.SavedPosts) Then + If HOST.Available(PDownload.SavedPosts, False) Then HOST.DownloadStarted(PDownload.SavedPosts) Dim u As New UserInfo With {.Plugin = HOST.Key, .Site = HOST.Name, .SpecialPath = HOST.SavedPostsPath} Using user As IUserData = HOST.GetInstance(PDownload.SavedPosts, Nothing, False, False) diff --git a/SCrawler/API/Base/SiteSettingsBase.vb b/SCrawler/API/Base/SiteSettingsBase.vb index e114333..6dcb635 100644 --- a/SCrawler/API/Base/SiteSettingsBase.vb +++ b/SCrawler/API/Base/SiteSettingsBase.vb @@ -91,7 +91,7 @@ Namespace API.Base End Function #End Region #Region "Ready, Available" - Friend Overridable Function Available(ByVal What As Download) As Boolean Implements ISiteSettings.Available + Friend Overridable Function Available(ByVal What As Download, ByVal Silent As Boolean) As Boolean Implements ISiteSettings.Available Return True End Function Friend Overridable Function ReadyToDownload(ByVal What As Download) As Boolean Implements ISiteSettings.ReadyToDownload diff --git a/SCrawler/API/Base/UserDataBase.vb b/SCrawler/API/Base/UserDataBase.vb index 0f51e3c..ac67729 100644 --- a/SCrawler/API/Base/UserDataBase.vb +++ b/SCrawler/API/Base/UserDataBase.vb @@ -209,11 +209,14 @@ Namespace API.Base #Region "Images" Friend Overridable Function GetUserPicture() As Image Implements IUserData.GetPicture If Settings.ViewModeIsPicture Then - Return GetPicture() + Return GetPicture(Of Image)() Else Return Nothing End If End Function + Friend Function GetUserPictureAddress() As SFile + Return GetPicture(Of SFile)(False) + End Function Friend Overridable Sub SetPicture(ByVal f As SFile) Implements IUserData.SetPicture Try If Not f.IsEmptyString AndAlso f.Exists Then @@ -225,7 +228,8 @@ Namespace API.Base Protected Function GetNullPicture(ByVal MaxHeigh As XML.Base.XMLValue(Of Integer)) As Bitmap Return New Bitmap(CInt(DivideWithZeroChecking(MaxHeigh.Value, 100) * 75), MaxHeigh.Value) End Function - Protected Function GetPicture(Optional ByVal ReturnNullImageOnNothing As Boolean = True) As Image + Protected Function GetPicture(Of T)(Optional ByVal ReturnNullImageOnNothing As Boolean = True) As T + Dim rsfile As Boolean = GetType(T) Is GetType(SFile) Dim f As SFile = Nothing Dim p As UserImage = Nothing Dim DelPath As Boolean = True @@ -271,18 +275,21 @@ BlockReturn: On Error GoTo BlockNullPicture If Not p Is Nothing Then Dim i As Image = Nothing - Select Case Settings.ViewMode.Value - Case View.LargeIcon : i = p.Large.OriginalImage.Clone - Case View.SmallIcon : i = p.Small.OriginalImage.Clone - End Select + Dim a As SFile = p.Address + If Not rsfile Then + Select Case Settings.ViewMode.Value + Case View.LargeIcon : i = p.Large.OriginalImage.Clone + Case View.SmallIcon : i = p.Small.OriginalImage.Clone + End Select + End If p.Dispose() - Return i + If rsfile Then Return CObj(a) Else Return CObj(i) End If BlockNullPicture: If ReturnNullImageOnNothing Then Select Case Settings.ViewMode.Value - Case View.LargeIcon : Return GetNullPicture(Settings.MaxLargeImageHeigh) - Case View.SmallIcon : Return GetNullPicture(Settings.MaxSmallImageHeigh) + Case View.LargeIcon : Return CObj(GetNullPicture(Settings.MaxLargeImageHeigh)) + Case View.SmallIcon : Return CObj(GetNullPicture(Settings.MaxSmallImageHeigh)) End Select End If Return Nothing @@ -337,6 +344,13 @@ BlockNullPicture: Protected ReadOnly _ContentNew As List(Of UserMedia) Protected ReadOnly _TempMediaList As List(Of UserMedia) Protected ReadOnly _TempPostsList As List(Of String) + Friend Function GetLastImageAddress() As SFile + If _ContentList.Count > 0 Then + Return _ContentList.LastOrDefault(Function(c) c.Type = UTypes.Picture And Not c.File.IsEmptyString And Not c.File.Extension = "gif").File + Else + Return Nothing + End If + End Function #End Region #Region "Files" Friend Overridable Property MyFile As SFile Implements IUserData.File @@ -737,7 +751,7 @@ BlockNullPicture: Responser = New Response If Not HOST.Responser Is Nothing Then Responser.Copy(HOST.Responser) - Dim UpPic As Boolean = Settings.ViewModeIsPicture AndAlso GetPicture(False) Is Nothing + Dim UpPic As Boolean = Settings.ViewModeIsPicture AndAlso GetPicture(Of Image)(False) Is Nothing Dim sEnvir() As Boolean = {UserExists, UserSuspended} Dim EnvirChanged As Func(Of Boolean) = Function() Not sEnvir(0) = UserExists Or Not sEnvir(1) = UserSuspended UserExists = True diff --git a/SCrawler/API/Instagram/SiteSettings.vb b/SCrawler/API/Instagram/SiteSettings.vb index 7988e9a..bd03f70 100644 --- a/SCrawler/API/Instagram/SiteSettings.vb +++ b/SCrawler/API/Instagram/SiteSettings.vb @@ -57,6 +57,24 @@ Namespace API.Instagram Throw New NotImplementedException() End Function End Class + Private Class TaggedNotifyLimitChecker : Implements IFieldsCheckerProvider + Private Property ErrorMessage As String Implements IFieldsCheckerProvider.ErrorMessage + Private Property Name As String Implements IFieldsCheckerProvider.Name + Private Property TypeError As Boolean Implements IFieldsCheckerProvider.TypeError + Private Function Convert(ByVal Value As Object, ByVal DestinationType As Type, ByVal Provider As IFormatProvider, + Optional ByVal NothingArg As Object = Nothing, Optional ByVal e As ErrorsDescriber = Nothing) As Object Implements ICustomProvider.Convert + Dim v% = AConvert(Of Integer)(Value, -10) + If v > 0 Or v = -1 Then + Return Value + Else + ErrorMessage = $"The value of [{Name}] field must be greater than 0 or equal to -1" + Return Nothing + End If + End Function + Private Function GetFormat(ByVal FormatType As Type) As Object Implements IFormatProvider.GetFormat + Throw New NotImplementedException() + End Function + End Class #End Region #Region "Authorization properties" @@ -95,6 +113,12 @@ Namespace API.Instagram Friend ReadOnly Property GetStories As PropertyValue Friend ReadOnly Property GetTagged As PropertyValue + + Friend ReadOnly Property TaggedNotifyLimit As PropertyValue + + Private ReadOnly Property TaggedNotifyLimitProvider As IFormatProvider #End Region #Region "429 bypass" Friend ReadOnly Property DownloadingErrorDate As XMLValue(Of Date) @@ -184,6 +208,8 @@ Namespace API.Instagram GetStories = New PropertyValue(False) GetTagged = New PropertyValue(False) + TaggedNotifyLimit = New PropertyValue(200) + TaggedNotifyLimitProvider = New TaggedNotifyLimitChecker DownloadingErrorDate = New XMLValue(Of Date) With { .Provider = New XMLValueConversionProvider(Function(ss, vv) AConvert(Of String)(vv, AModes.Var, Nothing))} @@ -248,6 +274,24 @@ Namespace API.Instagram Return False End If End Function + + Private Function CheckNotifyLimit(ByVal p As IEnumerable(Of PropertyData)) As Boolean + If p.ListExists Then + Dim pi% = p.ListIndexOf(Function(pp) pp.Name = NameOf(TaggedNotifyLimit)) + If pi >= 0 Then + Dim v% = AConvert(Of Integer)(p(pi).Value, -10) + If v > 0 Then + Return True + ElseIf v = -1 Then + Return MsgBoxE({"You turn off notifications for tagged posts. This is highly undesirable. Do you still want to do it?", + "Disabling tagged notification limits "}, MsgBoxStyle.YesNo) = MsgBoxResult.Yes + Else + Return False + End If + End If + End If + Return False + End Function Friend Overrides Sub BeginInit() End Sub Friend Overrides Sub EndInit() @@ -259,13 +303,17 @@ Namespace API.Instagram #Region "Downloading" Private ActiveJobs As Integer = 0 Private _NextWNM As UserData.WNM = UserData.WNM.Notify + Private _NextTagged As Boolean = True Friend Overrides Sub DownloadStarted(ByVal What As Download) If CStr(Hash.Value).IsEmptyString Or HashUpdateRequired Then GatherInstaHash() ActiveJobs += 1 End Sub Friend Overrides Sub BeforeStartDownload(ByVal User As Object, ByVal What As Download) With DirectCast(User, UserData) - If What = Download.Main Then .WaitNotificationMode = _NextWNM + If What = Download.Main Then + .WaitNotificationMode = _NextWNM + .TaggedCheckSession = _NextTagged + End If If LastDownloadDate.Value.AddMinutes(60) > Now Then .RequestsCount = LastRequestsCount Else @@ -278,12 +326,14 @@ Namespace API.Instagram With DirectCast(User, UserData) _NextWNM = .WaitNotificationMode If _NextWNM = UserData.WNM.SkipTemp Or _NextWNM = UserData.WNM.SkipCurrent Then _NextWNM = UserData.WNM.Notify + _NextTagged = .TaggedCheckSession LastDownloadDate.Value = Now LastRequestsCount.Value = .RequestsCount End With End Sub Friend Overrides Sub DownloadDone(ByVal What As Download) _NextWNM = UserData.WNM.Notify + _NextTagged = True LastDownloadDate.Value = Now ActiveJobs -= 1 If HashUpdateRequired Then MyMainLOG = "Check your Instagram credentials" @@ -293,23 +343,24 @@ Namespace API.Instagram Friend Function GatherInstaHash() As Boolean Try If Not Responser.Cookies.ListExists Then Throw New Exception("Instagram cookies does not set") - Dim rs As New RParams("=""([^""]+?ConsumerLibCommons[^""]+?.js)""", Nothing, 1) With {.MatchTimeOut = 10} - Dim r$ = Responser.GetResponse("https://instagram.com",, EDP.ThrowException) + Dim rs As New RParams("preload"" href=""(https://static.cdninstagram.com/rsrc.php/[^""]+?.js[^""]*)""", Nothing, 1, RegexReturn.List) With {.MatchTimeOut = 10} + Dim h$ + Dim r$ = Responser.GetResponse("https://www.instagram.com",, EDP.ThrowException) If Not r.IsEmptyString Then - Dim hStr$ = RegexReplace(r, rs) - If Not hStr.IsEmptyString Then - Do While Left(hStr, 1) = "/" : hStr = Right(hStr, hStr.Length - 1) : Loop - hStr = $"https://instagram.com/{hStr}" - r = Responser.GetResponse(hStr,, EDP.ThrowException) - If Not r.IsEmptyString Then - rs = New RParams("generatePaginationActionCreators.+?.profilePosts.byUserId.get.+?queryId:.([\d\w\S]+?)""", Nothing, 1) With {.MatchTimeOut = 10} - Dim h$ = RegexReplace(r, rs) - If Not h.IsEmptyString Then - Hash.Value = h - HashUpdateRequired.Value = False - Return True + Dim JsUrls As List(Of String) = RegexReplace(r, rs) + If JsUrls.ListExists Then + rs = New RParams("\{.+?var h=""([\w\d\S]+?)"".+?\)\.generatePaginationActionCreators", Nothing, 1) With {.MatchTimeOut = 10} + For Each url$ In JsUrls + r = Responser.GetResponse(url,, EDP.ReturnValue) + If Not r.IsEmptyString Then + h = RegexReplace(r, rs) + If Not h.IsEmptyString AndAlso h.Length > 30 Then + Hash.Value = h + HashUpdateRequired.Value = False + Return True + End If End If - End If + Next End If End If Return False diff --git a/SCrawler/API/Instagram/UserData.vb b/SCrawler/API/Instagram/UserData.vb index ea3fff5..c4465bc 100644 --- a/SCrawler/API/Instagram/UserData.vb +++ b/SCrawler/API/Instagram/UserData.vb @@ -81,6 +81,7 @@ Namespace API.Instagram If IsSavedPosts Then DownloadPosts(Token) ElseIf MySiteSettings.StoriesAndTaggedReady Then + DownloadedTags = 0 If GetStories Then DownloadData(String.Empty, Sections.Stories, Token) If GetTaggedData Then DownloadData(String.Empty, Sections.Tagged, Token) End If @@ -96,6 +97,8 @@ Namespace API.Instagram Tagged Stories End Enum + Private Const StoriesFolder As String = "Stories" + Private Const TaggedFolder As String = "Tagged" #Region "429 bypass" Friend Property RequestsCount As Integer = 0 Friend Enum WNM As Integer @@ -149,9 +152,77 @@ Namespace API.Instagram End With End Sub #End Region - Private Const StoriesFolder As String = "Stories" - Private Const TaggedFolder As String = "Tagged" +#Region "Tags" Private TaggedChecked As Boolean = False + Friend TaggedCheckSession As Boolean = True + Private DownloadedTags As Integer = 0 + Private DownloadTagsLimit As Integer? = Nothing + Private ReadOnly Property TaggedLimitsNotifications(Optional ByVal v As Integer? = Nothing) As Boolean + Get + Return Not TaggedChecked AndAlso TaggedCheckSession AndAlso + CInt(MySiteSettings.TaggedNotifyLimit.Value) > 0 AndAlso + (Not v.HasValue OrElse v.Value > CInt(MySiteSettings.TaggedNotifyLimit.Value)) + End Get + End Property + Private Function SetTagsLimit(ByVal Max As Integer, ByVal p As ANumbers) As DialogResult + Dim v%? + Dim aStr$ = $"Enter the number of posts from user {ToString()} that you want to download{vbCr}" & + $"(Max: {Max.NumToString(p)}; Requests: {(Max / 12).RoundUp.NumToString(p)})" + Dim tryBtt As New MsgBoxButton("Try again") With {.ToolTip = "You will be asked again about the limit"} + Dim cancelBtt As New MsgBoxButton("Cancel") With {.ToolTip = "Cancel tagged posts download operation"} + Dim selectBtt As New MsgBoxButton("Other options") With {.ToolTip = "The main message with options will be displayed again"} + Dim m As New MMessage("You have not entered a valid posts limit", "Tagged posts download limit", {tryBtt, selectBtt, cancelBtt}) + Dim mh As New MMessage("", "Tagged posts download limit", {"Confirm", tryBtt, selectBtt, cancelBtt}) With {.ButtonsPerRow = 2} + Do + v = AConvert(Of Integer)(InputBoxE(aStr, "Tagged posts download limit", CInt(MySiteSettings.TaggedNotifyLimit.Value)), Nothing) + If v.HasValue Then + mh.Text = $"You have entered a limit of {v.Value.NumToString(p)} posts" + Select Case MsgBoxE(mh).Index + Case 0 : DownloadTagsLimit = v : Return DialogResult.OK + Case 1 : v = Nothing + Case 2 : Return DialogResult.Retry + Case 3 : Return DialogResult.Cancel + End Select + Else + Select Case MsgBoxE(m).Index + Case 1 : Return DialogResult.Retry + Case 2 : Return DialogResult.Cancel + End Select + End If + Loop While Not v.HasValue + Return DialogResult.Retry + End Function + Private Function TaggedContinue(ByVal TaggedCount As Integer) As DialogResult + Dim agi As New ANumbers With {.FormatOptions = ANumbers.Options.GroupIntegral} + Dim msg As New MMessage($"The number of tagged posts by user [{ToString()}] is {TaggedCount.NumToString(agi)}" & vbCr & + $"This is about {(TaggedCount / 12).RoundUp.NumToString(agi)} requests." & vbCr & + "The tagged data download operation can take a long time.", + "Too much tagged data", + { + "Continue", + New MsgBoxButton("Continue unnotified") With { + .ToolTip = "Continue downloading and cancel further notifications in the current downloading session."}, + New MsgBoxButton("Limit") With { + .ToolTip = "Enter the limit of posts you want to download."}, + New MsgBoxButton("Disable and cancel") With { + .ToolTip = "Disable downloading tagged data and cancel downloading tagged data."}, + "Cancel" + }, MsgBoxStyle.Exclamation) With {.DefaultButton = 0, .CancelButton = 4, .ButtonsPerRow = 2} + Do + Select Case MsgBoxE(msg).Index + Case 0 : Return DialogResult.OK + Case 1 : TaggedCheckSession = False : Return DialogResult.OK + Case 2 + Select Case SetTagsLimit(TaggedCount, agi) + Case DialogResult.OK : Return DialogResult.OK + Case DialogResult.Cancel : Return DialogResult.Cancel + End Select + Case 3 : GetTaggedData = False : Return DialogResult.Cancel + Case 4 : Return DialogResult.Cancel + End Select + Loop + End Function +#End Region Private Overloads Sub DownloadData(ByVal Cursor As String, ByVal Section As Sections, ByVal Token As CancellationToken) Dim URL$ = String.Empty Dim StoriesList As List(Of String) = Nothing @@ -258,23 +329,15 @@ Namespace API.Instagram If Not PostID.IsEmptyString And _TempPostsList.Contains(PostID) Then Throw New ExitException(_DownloadComplete) _TempPostsList.Add(PostID) ObtainMedia2(nn, PostID, SpecFolder) + DownloadedTags += 1 + If DownloadTagsLimit.HasValue AndAlso DownloadedTags >= DownloadTagsLimit.Value Then _ + Throw New ExitException(_DownloadComplete) Next - If Not TaggedChecked Then + If TaggedLimitsNotifications Then TaggedCount = j.Value("total_count").FromXML(Of Integer)(0) TaggedChecked = True - If TaggedCount > 200 Then - Dim a% = MsgBoxE({$"The number of tagged posts by user [{ToString()}] is { _ - TaggedCount.NumToString(New ANumbers With {.FormatOptions = ANumbers.Options.GroupIntegral})}" & vbCr & - "The tagged data download operation can take a long time.", "Too much tagged data"}, vbExclamation,,, - {"Continue", - New MsgBoxButton("Disable and cancel") With { - .ToolTip = "Disable downloading tagged data and cancel downloading tagged data."}, - "Cancel"}) - If a > 0 Then - If a = 1 Then GetTaggedData = False - Throw New ExitException(_DownloadComplete) - End If - End If + If TaggedLimitsNotifications(TaggedCount) AndAlso + TaggedContinue(TaggedCount) = DialogResult.Cancel Then Throw New ExitException(_DownloadComplete) End If End Select Else diff --git a/SCrawler/API/Reddit/M3U8.vb b/SCrawler/API/Reddit/M3U8.vb index f9109c6..265b86f 100644 --- a/SCrawler/API/Reddit/M3U8.vb +++ b/SCrawler/API/Reddit/M3U8.vb @@ -14,23 +14,32 @@ Namespace API.Reddit Namespace M3U8_Declarations Friend Module M3U8_Declarations Friend ReadOnly BaseUrlPattern As RParams = RParams.DM("([htps:/]{7,8}.+?/.+?)(?=/)", 0, EDP.ReturnValue) + ''' Video Friend ReadOnly PlayListRegEx_1 As RParams = RParams.DM("(#EXT-X-STREAM-INF)(.+)(RESOLUTION=)(\d+)(.+?[\r\n]{1,2})(.+?)([\r\n]{1,2})", 0, RegexReturn.List, EDP.SendInLog, EDP.ReturnValue) + ''' Audio, Video Friend ReadOnly PlayListRegEx_2 As RParams = RParams.DM("(?<=#EXT-X-BYTERANGE.+?[\r\n]{1,2})(.+)(?=[\r\n]{0,2})", 0, RegexReturn.List, EDP.SendInLog, EDP.ReturnValue) + Friend ReadOnly PlayListAudioRegEx As RParams = RParams.DM("(HLS_AUDIO_(\d+)[^""]+)", 0, RegexReturn.List, EDP.SendInLog, EDP.ReturnValue) Friend ReadOnly DPED As New ErrorsDescriber(EDP.SendInLog + EDP.ReturnValue) End Module End Namespace - Friend NotInheritable Class M3U8 - Private Sub New() - End Sub + Friend NotInheritable Class M3U8 : Implements IDisposable +#Region "Declarations" + Private Enum Types : Video : Audio : End Enum Private Structure Resolution : Implements IRegExCreator, IComparable(Of Resolution) Friend File As String Friend Resolution As Integer + Friend HasError As Boolean Friend Function CreateFromArray(ByVal ParamsArray() As String) As Object Implements IRegExCreator.CreateFromArray If ParamsArray.ArrayExists Then File = ParamsArray(0) - If ParamsArray.Length > 1 Then Resolution = AConvert(Of Integer)(ParamsArray(1), 0) + Try + If ParamsArray.Length > 1 Then Resolution = AConvert(Of Integer)(ParamsArray(1), EDP.ThrowException) + Catch ex As Exception + HasError = True + Resolution = 0 + End Try End If Return Me End Function @@ -38,20 +47,59 @@ Namespace API.Reddit Return Resolution.CompareTo(Other.Resolution) * -1 End Function End Structure - Private Shared Function GetPlaylistUrls(ByVal PlayListURL As String, ByVal BaseUrl As String) As List(Of String) + Private ReadOnly PlayListURL As String + Private ReadOnly BaseURL As String + Private ReadOnly Video As List(Of String) + Private ReadOnly Audio As List(Of String) + Private OutFile As SFile + Private VideoFile As SFile + Private AudioFile As SFile + Private CachePath As SFile +#End Region + Private Sub New(ByVal URL As String, ByVal OutFile As SFile) + PlayListURL = URL + BaseURL = RegexReplace(URL, BaseUrlPattern) + Video = New List(Of String) + Audio = New List(Of String) + Me.OutFile = OutFile + Me.OutFile.Name = "PlayListFile" + Me.OutFile.Extension = "mp4" + CachePath = $"{OutFile.PathWithSeparator}_Cache\{SFile.GetDirectories($"{OutFile.PathWithSeparator}_Cache\",,, EDP.ReturnValue).ListIfNothing.Count + 1}\" + End Sub +#Region "Internal functions" +#Region "GetPlaylistUrls" + Private Overloads Sub GetPlaylistUrls() + Video.ListAddList(GetPlaylistUrls(PlayListURL, Types.Video)) + Audio.ListAddList(GetPlaylistUrls(PlayListURL, Types.Audio)) + End Sub + Private Overloads Function GetPlaylistUrls(ByVal PlayListURL As String, ByVal Type As Types) As List(Of String) Try - If Not BaseUrl.IsEmptyString Then + If Not BaseURL.IsEmptyString Then Using w As New WebClient Dim r$ = w.DownloadString(PlayListURL) If Not r.IsEmptyString Then - Dim l As List(Of Resolution) = FNF.RegexFields(Of Resolution)(r, {PlayListRegEx_1}, {6, 4}) + Dim l As New List(Of Resolution) + If Type = Types.Video Then + l = FNF.RegexFields(Of Resolution)(r, {PlayListRegEx_1}, {6, 4}) + Else + Try + l = FNF.RegexFields(Of Resolution)(r, {PlayListAudioRegEx}, {1, 2}) + Catch anull As FNF.RegexFieldsTextBecameNullException + l.Clear() + End Try + End If If l.ListExists Then + Dim plError As Predicate(Of Resolution) = Function(lr) lr.HasError + If l.Exists(plError) Then + l.RemoveAll(plError) + If l.Count = 0 Then Return New List(Of String) + End If l.Sort() - Dim pls$ = $"{BaseUrl}/{l.First.File}" + Dim pls$ = $"{BaseURL}/{l.First.File}" r = w.DownloadString(pls) If Not r.IsEmptyString Then Dim lp As New ListAddParams(LAP.NotContainsOnly) With { - .Converter = Function(input) $"{BaseUrl}/{input}", + .Converter = Function(input) $"{BaseURL}/{input}", .Error = New ErrorsDescriber(False, False, True, New List(Of String))} Return ListAddList(Of String, List(Of String))(Nothing, DirectCast(RegexReplace(r, PlayListRegEx_2), List(Of String)), lp).ListIfNothing End If @@ -61,47 +109,94 @@ Namespace API.Reddit End If Return New List(Of String) Catch ex As Exception - Return ErrorsDescriber.Execute(DPED, ex, "[M3U8.GetPlaylistUrls]", New List(Of String)) + Return ErrorsDescriber.Execute(DPED, ex, $"[M3U8.GetPlaylistUrls({Type}): {PlayListURL}]", New List(Of String)) End Try End Function - Private Shared Function Save(ByVal URLs As List(Of String), ByVal f As SFile) As SFile - Dim CachePath As SFile = Nothing +#End Region +#Region "ConcatData" + Private Overloads Sub ConcatData() + ConcatData(Video, Types.Video, VideoFile) + ConcatData(Audio, Types.Audio, AudioFile) + MergeFiles() + End Sub + Private Overloads Sub ConcatData(ByVal Urls As List(Of String), ByVal Type As Types, ByRef TFile As SFile) Try - If URLs.ListExists Then - Dim ConcatFile As SFile = f - ConcatFile.Name = "PlayListFile" - ConcatFile.Extension = "mp4" - CachePath = $"{f.PathWithSeparator}_Cache\{SFile.GetDirectories($"{f.PathWithSeparator}_Cache\",,, EDP.ReturnValue).ListIfNothing.Count + 1}\" + If Urls.ListExists Then + Dim ConcatFile As SFile = OutFile + If Type = Types.Audio Then + ConcatFile.Name &= "_AUDIO" + ConcatFile.Extension = "aac" + Else + If Audio.Count > 0 Then ConcatFile.Name &= "_VIDEO" + ConcatFile.Extension = "mp4" + End If If CachePath.Exists(SFO.Path) Then Dim p As New SFileNumbers(ConcatFile.Name,,, New ANumbers With {.Format = ANumbers.Formats.General}) - ConcatFile = SFile.Indexed_IndexFile(ConcatFile,, p, EDP.ReturnValue) + ConcatFile = SFile.Indexed_IndexFile(ConcatFile,, p, EDP.ThrowException) 'EDP.ReturnValue Dim i% Dim eFiles As New List(Of SFile) Dim dFile As SFile = CachePath - dFile.Extension = New SFile(URLs(0)).Extension + dFile.Extension = New SFile(Urls(0)).Extension If dFile.Extension.IsEmptyString Then dFile.Extension = "ts" Using w As New WebClient - For i = 0 To URLs.Count - 1 + For i = 0 To Urls.Count - 1 dFile.Name = $"ConPart_{i}" - w.DownloadFile(URLs(i), dFile) + w.DownloadFile(Urls(i), dFile) eFiles.Add(dFile) Next End Using - f = FFMPEG.ConcatenateFiles(eFiles, Settings.FfmpegFile, ConcatFile, p, DPED) + TFile = FFMPEG.ConcatenateFiles(eFiles, Settings.FfmpegFile, ConcatFile, p, DPED) eFiles.Clear() - Return f End If End If - Return Nothing Catch ex As Exception - Return ErrorsDescriber.Execute(DPED, ex, "[M3U8.Save]", New SFile) - Finally - CachePath.Delete(SFO.Path, SFODelete.None, DPED) + ErrorsDescriber.Execute(DPED, ex, $"[M3U8.Save({Type})]") End Try + End Sub +#End Region + Private Sub MergeFiles() + Try + If Not VideoFile.IsEmptyString And Not AudioFile.IsEmptyString Then + Dim p As New SFileNumbers(OutFile.Name,, RParams.DMS("PlayListFile_(\d*)", 1), New ANumbers With {.Format = ANumbers.Formats.General}) + OutFile = FFMPEG.MergeFiles({VideoFile, AudioFile}, Settings.FfmpegFile, OutFile, p, DPED) + Else + OutFile = VideoFile + End If + Catch ex As Exception + ErrorsDescriber.Execute(DPED, ex, $"[M3U8.MergeFiles]") + End Try + End Sub + Friend Function Download() As SFile + GetPlaylistUrls() + ConcatData() + Return OutFile End Function +#End Region +#Region "Statics" Friend Shared Function Download(ByVal URL As String, ByVal f As SFile) As SFile - Dim BaseUrl$ = RegexReplace(URL, BaseUrlPattern) - Return Save(GetPlaylistUrls(URL, BaseUrl), f) + Using m As New M3U8(URL, f) : Return m.Download() : End Using End Function +#End Region +#Region "IDisposable Support" + Private disposedValue As Boolean = False + Private Overloads Sub Dispose(ByVal disposing As Boolean) + If Not disposedValue Then + If disposing Then + Video.Clear() + Audio.Clear() + CachePath.Delete(SFO.Path, SFODelete.None, DPED) + End If + disposedValue = True + End If + End Sub + Protected Overrides Sub Finalize() + Dispose(False) + MyBase.Finalize() + End Sub + Friend Overloads Sub Dispose() Implements IDisposable.Dispose + Dispose(True) + GC.SuppressFinalize(Me) + End Sub +#End Region End Class End Namespace \ No newline at end of file diff --git a/SCrawler/API/Reddit/SiteSettings.vb b/SCrawler/API/Reddit/SiteSettings.vb index d6760d2..dde1512 100644 --- a/SCrawler/API/Reddit/SiteSettings.vb +++ b/SCrawler/API/Reddit/SiteSettings.vb @@ -28,6 +28,8 @@ Namespace API.Reddit End Property Friend ReadOnly Property SavedPostsUserName As PropertyValue + + Friend ReadOnly Property UseM3U8 As PropertyValue Friend Overrides ReadOnly Property Responser As WEB.Response Friend Sub New() MyBase.New(RedditSite) @@ -43,6 +45,7 @@ Namespace API.Reddit End If End With SavedPostsUserName = New PropertyValue(String.Empty, GetType(String)) + UseM3U8 = New PropertyValue(True) UrlPatternUser = "https://www.reddit.com/user/{0}/" UrlPatternChannel = "https://www.reddit.com/r/{0}/" ImageVideoContains = "redgifs" @@ -75,17 +78,21 @@ Namespace API.Reddit Next Return Nothing End Function - Friend Overrides Function Available(ByVal What As Download) As Boolean + Friend Overrides Function Available(ByVal What As Download, ByVal Silent As Boolean) As Boolean Try Dim dl As List(Of DownDetector.Data) = DownDetector.GetData("reddit") If dl.ListExists Then dl = dl.Take(4).ToList Dim avg% = dl.Average(Function(d) d.Value) If avg > 100 Then - 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 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 + End If End If End If Return True diff --git a/SCrawler/API/Reddit/UserData.vb b/SCrawler/API/Reddit/UserData.vb index 4c0cddc..c58b1cc 100644 --- a/SCrawler/API/Reddit/UserData.vb +++ b/SCrawler/API/Reddit/UserData.vb @@ -31,6 +31,11 @@ Namespace API.Reddit Return If(IsChannel, DateProviderChannel, DateProvider) End Get End Property + Private ReadOnly Property UseM3U8 As Boolean + Get + Return Settings.UseM3U8 And CBool(DirectCast(HOST.Source, SiteSettings).UseM3U8.Value) + End Get + End Property #Region "Channels Support" #Region "IChannelLimits Support" Friend Property DownloadLimitCount As Integer? Implements IChannelLimits.DownloadLimitCount @@ -242,9 +247,12 @@ Namespace API.Reddit added = False End If Case "video" - If Settings.UseM3U8 AndAlso s("hlsUrl").XmlIfNothingValue("/").ToLower.Contains("m3u8") Then + If UseM3U8 AndAlso s("hlsUrl").XmlIfNothingValue("/").ToLower.Contains("m3u8") Then _TempMediaList.ListAddValue(MediaFromData(UTypes.m3u8, s.Value("hlsUrl"), _PostID(), PostDate,, IsChannel), LNC) + ElseIf Not UseM3U8 AndAlso s("fallback_url").XmlIfNothingValue("/").ToLower.Contains("mp4") Then + _TempMediaList.ListAddValue(MediaFromData(UTypes.Video, s.Value("fallback_url"), + _PostID(), PostDate,, IsChannel), LNC) Else added = False End If @@ -367,8 +375,12 @@ Namespace API.Reddit _TempMediaList.ListAddValue(MediaFromData(UTypes.Picture, tmpUrl, PostID, PostDate, _UserID, IsChannel), LNC) _TotalPostsDownloaded += 1 End If + ElseIf UseM3U8 AndAlso Not s.Value({"media", "reddit_video"}, "hls_url").IsEmptyString Then + _TempMediaList.ListAddValue(MediaFromData(UTypes.m3u8, s.Value({"media", "reddit_video"}, "hls_url"), + PostID, PostDate, _UserID, IsChannel), LNC) Else - _TempMediaList.ListAddValue(MediaFromData(UTypes.VideoPre + UTypes.m3u8, tmpUrl, PostID, PostDate, _UserID, IsChannel), LNC) + '_TempMediaList.ListAddValue(MediaFromData(UTypes.VideoPre + UTypes.m3u8, tmpUrl, PostID, PostDate, _UserID, IsChannel), LNC) + _TempMediaList.ListAddValue(MediaFromData(UTypes.Video, tmpUrl, PostID, PostDate, _UserID, IsChannel), LNC) _TotalPostsDownloaded += 1 End If ElseIf CreateImgurMedia(tmpUrl, PostID, PostDate, _UserID, IsChannel) Then @@ -718,7 +730,7 @@ Namespace API.Reddit ElseIf Responser.StatusCode = HttpStatusCode.BadGateway Or Responser.StatusCode = HttpStatusCode.ServiceUnavailable Or Responser.StatusCode = HttpStatusCode.GatewayTimeout Then - MyMainLOG = "Reddit is currently unavailable" + MyMainLOG = $"Reddit is currently unavailable ({ToString()})" 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 7fc9a4a..9cbc39d 100644 --- a/SCrawler/API/Twitter/UserData.vb +++ b/SCrawler/API/Twitter/UserData.vb @@ -266,6 +266,8 @@ Namespace API.Twitter 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 diff --git a/SCrawler/Channels/ChannelViewForm.vb b/SCrawler/Channels/ChannelViewForm.vb index f031241..0fa596b 100644 --- a/SCrawler/Channels/ChannelViewForm.vb +++ b/SCrawler/Channels/ChannelViewForm.vb @@ -20,7 +20,7 @@ Imports CmbDefaultButtons = PersonalUtilities.Forms.Controls.Base.ActionButton.D Imports RButton = PersonalUtilities.Tools.RangeSwitcherButton.Types Friend Class ChannelViewForm : Implements IChannelLimits Friend Event OnUsersAdded(ByVal StartIndex As Integer) - Friend Event OnDownloadDone(ByVal Message As String) + Friend Event OnDownloadDone As NotificationEventHandler #Region "Appended user structure" Private Structure PendingUser Friend ID As String @@ -326,7 +326,7 @@ Friend Class ChannelViewForm : Implements IChannelLimits Private Async Sub BTT_DOWNLOAD_Click(sender As Object, e As EventArgs) Handles BTT_DOWNLOAD.Click Try AppendPendingUsers() - If Not TokenSource Is Nothing OrElse Not HOST.Source.Available(Plugin.ISiteSettings.Download.Channel) Then Exit Sub + If Not TokenSource Is Nothing OrElse Not HOST.Source.Available(Plugin.ISiteSettings.Download.Channel, False) Then Exit Sub Dim InvokeToken As Action = Sub() If TokenSource Is Nothing Then CProgress.TotalCount = 0 diff --git a/SCrawler/Download/ActiveDownloadingProgress.vb b/SCrawler/Download/ActiveDownloadingProgress.vb index 67e4890..8bad9bf 100644 --- a/SCrawler/Download/ActiveDownloadingProgress.vb +++ b/SCrawler/Download/ActiveDownloadingProgress.vb @@ -39,37 +39,41 @@ Namespace DownloadObjects End Sub Private Sub Downloader_OnReconfigured() Const RowHeight% = 30 - With TP_MAIN - If .Controls.Count > 0 Then - For Each c As Control In .Controls - If Not c Is Nothing Then c.Dispose() - Next - .Controls.Clear() - End If - .RowStyles.Clear() - .RowCount = 0 - End With - JobsList.ListClearDispose - With Downloader - If .Pool.Count > 0 Then - For Each j As TDownloader.Job In .Pool - With TP_MAIN - .RowStyles.Add(New RowStyle(SizeType.Absolute, RowHeight)) - .RowCount += 1 - JobsList.Add(New DownloadProgress(j)) - AddHandler JobsList.Last.OnTotalCountChange, AddressOf Jobs_OnTotalCountChange - .Controls.Add(JobsList.Last.Get, 0, .RowStyles.Count - 1) - End With - Next - TP_MAIN.RowStyles.Add(New RowStyle(SizeType.Percent, 100)) - TP_MAIN.RowCount += 1 - End If - Dim s As Size = Size - s.Height = TP_MAIN.RowStyles.Count * RowHeight + PaddingE.GetOf({TP_MAIN}).Vertical(TP_MAIN.RowStyles.Count) - TP_MAIN.RowStyles.Count * 2 - MinimumSize = New Size(MinWidth, s.Height) - Size = s - End With - TP_MAIN.Refresh() + Dim a As Action = Sub() + With TP_MAIN + If .Controls.Count > 0 Then + For Each c As Control In .Controls + If Not c Is Nothing Then c.Dispose() + + Next + .Controls.Clear() + End If + .RowStyles.Clear() + .RowCount = 0 + End With + JobsList.ListClearDispose + With Downloader + If .Pool.Count > 0 Then + For Each j As TDownloader.Job In .Pool + With TP_MAIN + .RowStyles.Add(New RowStyle(SizeType.Absolute, RowHeight)) + .RowCount += 1 + JobsList.Add(New DownloadProgress(j)) + AddHandler JobsList.Last.OnTotalCountChange, AddressOf Jobs_OnTotalCountChange + .Controls.Add(JobsList.Last.Get, 0, .RowStyles.Count - 1) + End With + Next + TP_MAIN.RowStyles.Add(New RowStyle(SizeType.Percent, 100)) + TP_MAIN.RowCount += 1 + End If + Dim s As Size = Size + s.Height = TP_MAIN.RowStyles.Count * RowHeight + PaddingE.GetOf({TP_MAIN}).Vertical(TP_MAIN.RowStyles.Count) - TP_MAIN.RowStyles.Count * 2 + MinimumSize = New Size(MinWidth, s.Height) + Size = s + End With + TP_MAIN.Refresh() + End Sub + If TP_MAIN.InvokeRequired Then TP_MAIN.Invoke(a) Else a.Invoke End Sub Private Sub Jobs_OnTotalCountChange() If JobsList.Count > 0 And Not DisableProgressChange Then diff --git a/SCrawler/Download/AutoDownloader.vb b/SCrawler/Download/AutoDownloader.vb new file mode 100644 index 0000000..292db78 --- /dev/null +++ b/SCrawler/Download/AutoDownloader.vb @@ -0,0 +1,325 @@ +' Copyright (C) 2022 Andy +' This program is free software: you can redistribute it and/or modify +' it under the terms of the GNU General Public License as published by +' the Free Software Foundation, either version 3 of the License, or +' (at your option) any later version. +' +' This program is distributed in the hope that it will be useful, +' but WITHOUT ANY WARRANTY +Imports System.Threading +Imports PersonalUtilities.Functions.XML +Imports PersonalUtilities.Tools.Notifications +Imports SCrawler.DownloadObjects.Groups +Imports SCrawler.API +Imports SCrawler.API.Base +Namespace DownloadObjects + Friend Class AutoDownloader : Inherits GroupParameters + Friend Event UserFind(ByVal Key As String, ByVal Activate As Boolean) + Friend Enum Modes As Integer + None = 0 + [Default] = 1 + All = 2 + Specified = 3 + Groups = 4 + End Enum + Friend Const DefaultTimer As Integer = 60 +#Region "Notifications" + Private Const KeyOpenFolder As String = "_____OPEN_FOLDER_SCRAWLER_AUTOMATION" + Private Const KeyOpenSite As String = "_____OPEN_SITE_SCRAWLER_AUTOMATION" + Private Const KeyBttDismiss As String = "_____DISMISS_SCRAWLER_AUTOMATION" + Private Const KeyBttPhoto As String = "_____PHOTO_SCRAWLER_AUTOMATION" + Private ReadOnly UserKeys As List(Of NotifiedUser) + Private Class NotifiedUser : Implements IDisposable + Private ReadOnly Property User As IUserData + Private ReadOnly Property Key As String + Private ReadOnly Property KeyFolder As String + Private ReadOnly Property KeySite As String + Private ReadOnly Property KeyDismiss As String + Private ReadOnly Property Images As Dictionary(Of String, SFile) + Private Sub New() + Images = New Dictionary(Of String, SFile) + End Sub + Friend Sub New(ByVal _Key As String) + Me.New + Key = _Key + KeyFolder = $"{Key}{KeyOpenFolder}" + KeySite = $"{Key}{KeyOpenSite}" + KeyDismiss = $"{Key}{KeyBttDismiss}" + End Sub + Friend Sub New(ByVal _Key As String, ByRef _User As IUserData) + Me.New(_Key) + User = _User + End Sub + Public Shared Widening Operator CType(ByVal Key As String) As NotifiedUser + Return New NotifiedUser(Key) + End Operator + Friend Sub ShowNotification() + Try + If Not User Is Nothing Then + Dim Text$ = $"{User.Site} - {User.Name}{vbNewLine}" & + $"Downloaded: {User.DownloadedPictures(False)} images, {User.DownloadedVideos(False)} videos" + Dim Title$ + If Not User.CollectionName.IsEmptyString Then + Title = User.CollectionName + Else + Title = User.ToString + End If + Using Notify As New Notification(Text, Title) With {.Key = Key} + Dim uPic As SFile = DirectCast(User, UserDataBase).GetUserPictureAddress + Dim uif As SFile = Nothing + Dim uifKey$ = String.Empty + If uPic.Exists Then Notify.Images = {New ToastImage(uPic)} + If User.DownloadedPictures(False) > 0 Then + uif = DirectCast(User, UserDataBase).GetLastImageAddress + If uif.Exists Then + Notify.Images = {New ToastImage(uif, IImage.Modes.Inline)} + uifKey = $"{Key}_{Images.Keys.Count + 1}_{KeyBttPhoto}" + If Not Images.ContainsKey(uifKey) Then Images.Add(uifKey, uif) + End If + End If + Notify.Buttons = { + New ToastButton(KeyFolder, "Folder"), + New ToastButton(KeySite, "Site") + } + If Not uifKey.IsEmptyString Then Notify.Buttons = {New ToastButton(uifKey, "Photo")} + Notify.Buttons = {New ToastButton(KeyDismiss, "Dismiss")} + Notify.Show() + End Using + End If + Catch ex As Exception + ErrorsDescriber.Execute(EDP.SendInLog, ex, "[AutoDownloader.NotifiedUser.ShowNotification]") + If Not User Is Nothing Then + MainFrameObj.ShowNotification($"Downloaded: {User.DownloadedPictures(False)} images, {User.DownloadedVideos(False)} videos", + User.ToString, IIf(User.HasError, ToolTipIcon.Warning, ToolTipIcon.Info)) + End If + End Try + End Sub + ''' True to activate + Friend Function Open(ByVal _Key As String) As Boolean + If Not User Is Nothing Then + If Key = _Key Then + Return True + ElseIf KeyFolder = _Key Then + User.OpenFolder() + ElseIf KeySite = _Key Then + User.OpenSite() + ElseIf Images.ContainsKey(_Key) Then + Images(_Key).Open(, EDP.None) + End If + End If + Return False + End Function + Public Overrides Function Equals(ByVal Obj As Object) As Boolean + With CType(Obj, NotifiedUser) + Return .Key = Key Or .Key = KeyFolder Or .Key = KeySite Or .Key = KeyDismiss Or Images.ContainsKey(.Key) + End With + End Function +#Region "IDisposable Support" + Private disposedValue As Boolean = False + Protected Overridable Overloads Sub Dispose(ByVal disposing As Boolean) + If Not disposedValue Then + If disposing Then Images.Clear() + disposedValue = True + End If + End Sub + Protected Overrides Sub Finalize() + Dispose(False) + MyBase.Finalize() + End Sub + Friend Overloads Sub Dispose() Implements IDisposable.Dispose + Dispose(True) + GC.SuppressFinalize(Me) + End Sub +#End Region + End Class +#End Region +#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_LastDownloadDate As String = "LastDownloadDate" + Private Const Name_ShowNotifications As String = "Notify" +#End Region +#Region "Declarations" + Friend Property Mode As Modes = Modes.None + Friend ReadOnly Property Groups As List(Of String) + Friend Property Timer As Integer = DefaultTimer + Friend Property ShowNotifications As Boolean = True + Friend Property LastDownloadDate As Date = Now.AddYears(-1) + Private ReadOnly DateProvider As New ADateTime(ADateTime.Formats.BaseDateTime) + Private File As SFile = $"Settings\AutoDownload.xml" + Private AThread As Thread +#End Region +#Region "Initializer" + Friend Sub New() + Groups = New List(Of String) + UserKeys = New List(Of NotifiedUser) + If File.Exists Then + Using x As New XmlFile(File) + Mode = x.Value(Name_Mode).FromXML(Of Integer)(Modes.None) + 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) + ShowNotifications = x.Value(Name_ShowNotifications).FromXML(Of Boolean)(True) + LastDownloadDate = AConvert(Of Date)(x.Value(Name_LastDownloadDate), DateProvider, Now.AddYears(-1)) + End Using + End If + End Sub +#End Region +#Region "Groups Support" + Friend Sub GROUPS_Updated(ByVal Sender As DownloadGroup) + If Groups.Count > 0 Then + Dim i% = Groups.IndexOf(Sender.NameBefore) + If i >= 0 Then Groups(i) = Sender.Name : Update() + End If + End Sub + Friend Sub GROUPS_Deleted(ByVal Sender As DownloadGroup) + If Groups.Count > 0 Then + Dim i% = Groups.IndexOf(Sender.Name) + If i >= 0 Then Groups.RemoveAt(i) : Update() + End If + End Sub +#End Region +#Region "Update" + Friend Sub Update() + Try + Using x As New XmlFile With {.Name = "Settings"} + x.Add(Name_Mode, CInt(Mode)) + x.Add(Name_Groups, Groups.ListToString("|")) + x.Add(Name_Labels, Labels.ListToString("|")) + x.Add(Name_Temporary, CInt(Temporary)) + x.Add(Name_Favorite, CInt(Favorite)) + x.Add(Name_ReadyForDownload, ReadyForDownload.BoolToInteger) + x.Add(Name_ReadyForDownloadIgnore, ReadyForDownloadIgnore.BoolToInteger) + x.Add(Name_Timer, Timer) + x.Add(Name_ShowNotifications, ShowNotifications.BoolToInteger) + x.Add(Name_LastDownloadDate, CStr(AConvert(Of String)(LastDownloadDate, DateProvider, String.Empty))) + x.Save(File) + End Using + Catch ex As Exception + ErrorsDescriber.Execute(EDP.SendInLog, ex, "[AutoDownloader.Update]") + End Try + End Sub +#End Region +#Region "Execution" + Friend Sub Start() + If Not If(AThread?.IsAlive, False) And Not Mode = Modes.None Then + AThread = New Thread(New ThreadStart(AddressOf Checker)) + AThread.SetApartmentState(ApartmentState.MTA) + AThread.Start() + End If + End Sub + Private _StopRequested As Boolean = False + Friend Sub [Stop]() + If If(AThread?.IsAlive, False) Then _StopRequested = True + End Sub + Private Sub Checker() + Try + While Not _StopRequested + If LastDownloadDate.AddMinutes(Timer) < Now And Not Downloader.Working Then Download() + Thread.Sleep(500) + End While + Catch ex As Exception + ErrorsDescriber.Execute(EDP.SendInLog, ex, "[AutoDownloader.Checker]") + Finally + _StopRequested = False + End Try + End Sub + Private Sub Download() + Dim Keys As New List(Of String) + Try + Dim users As New List(Of IUserData) + Dim GName$ + Dim i% + Dim l As New ListAddParams(LAP.IgnoreICopier + LAP.NotContainsOnly) + Dim notify As Action = Sub() + With Downloader.Downloaded + If ShowNotifications And .Count > 0 Then .ForEach(Sub(ByVal u As IUserData) + If Keys.Contains(u.Key) Then + ShowNotification(u) + Keys.Remove(u.Key) + End If + End Sub) + End With + End Sub + Select Case Mode + Case Modes.All : users.ListAddList(Settings.Users) + 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)) + Case Modes.Groups + 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) + 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)) + With Downloader + .AutoDownloaderWorking = True + If .Downloaded.Count > 0 Then .Downloaded.RemoveAll(Function(u) Keys.Contains(u.Key)) : .InvokeDownloadsChangeEvent() + .AddRange(users) + .DisableOpenForms = False + While .Working Or .Count > 0 : notify.Invoke() : Thread.Sleep(200) : End While + .AutoDownloaderWorking = False + notify.Invoke + End With + End If + Catch ex As Exception + ErrorsDescriber.Execute(EDP.SendInLog, ex, "[AutoDownloader.Download]") + Finally + Keys.Clear() + LastDownloadDate = Now + Update() + End Try + End Sub + Private Sub ShowNotification(ByVal u As IUserData) + Dim i% = UserKeys.IndexOf(u.Key) + If i >= 0 Then + UserKeys(i).ShowNotification() + Else + UserKeys.Add(New NotifiedUser(u.Key, TDownloader.GetUserFromMainCollection(u))) + UserKeys.Last.ShowNotification() + End If + End Sub + Friend Function NotificationClicked(ByVal Key As String) As Boolean + Dim i% = UserKeys.IndexOf(Key) + If i >= 0 Then + RaiseEvent UserFind(Key, UserKeys(i).Open(Key)) + Return True + Else + Return False + End If + End Function +#End Region +#Region "IDisposable Support" + Protected Overrides Sub Dispose(ByVal disposing As Boolean) + If Not disposedValue And disposing Then + [Stop]() + UserKeys.ListClearDispose() + Groups.Clear() + End If + MyBase.Dispose(disposing) + End Sub +#End Region + End Class +End Namespace \ No newline at end of file diff --git a/SCrawler/Download/AutoDownloaderEditorForm.Designer.vb b/SCrawler/Download/AutoDownloaderEditorForm.Designer.vb new file mode 100644 index 0000000..24abdef --- /dev/null +++ b/SCrawler/Download/AutoDownloaderEditorForm.Designer.vb @@ -0,0 +1,277 @@ +' Copyright (C) 2022 Andy +' This program is free software: you can redistribute it and/or modify +' it under the terms of the GNU General Public License as published by +' the Free Software Foundation, either version 3 of the License, or +' (at your option) any later version. +' +' This program is distributed in the hope that it will be useful, +' but WITHOUT ANY WARRANTY +Namespace DownloadObjects + + Partial Friend Class AutoDownloaderEditorForm : Inherits System.Windows.Forms.Form + + Protected Overrides Sub Dispose(ByVal disposing As Boolean) + Try + If disposing AndAlso components IsNot Nothing Then + components.Dispose() + End If + Finally + MyBase.Dispose(disposing) + End Try + End Sub + Private components As System.ComponentModel.IContainer + + Private Sub InitializeComponent() + Me.components = New System.ComponentModel.Container() + Dim CONTAINER_MAIN As System.Windows.Forms.ToolStripContainer + Dim ActionButton1 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() + Dim resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(AutoDownloaderEditorForm)) + Dim ActionButton2 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() + Dim TP_MODE As System.Windows.Forms.TableLayoutPanel + Dim ActionButton3 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() + Dim TT_MAIN As System.Windows.Forms.ToolTip + Me.DEF_GROUP = New SCrawler.DownloadObjects.Groups.GroupDefaults() + Me.TXT_GROUPS = New PersonalUtilities.Forms.Controls.TextBoxExtended() + Me.OPT_ALL = New System.Windows.Forms.RadioButton() + Me.OPT_DEFAULT = New System.Windows.Forms.RadioButton() + Me.OPT_SPEC = New System.Windows.Forms.RadioButton() + Me.OPT_DISABLED = New System.Windows.Forms.RadioButton() + Me.OPT_GROUP = New System.Windows.Forms.RadioButton() + Me.CH_NOTIFY = New System.Windows.Forms.CheckBox() + Me.TXT_TIMER = New PersonalUtilities.Forms.Controls.TextBoxExtended() + Me.LBL_LAST_TIME_UP = New System.Windows.Forms.Label() + CONTAINER_MAIN = New System.Windows.Forms.ToolStripContainer() + TP_MODE = New System.Windows.Forms.TableLayoutPanel() + TT_MAIN = New System.Windows.Forms.ToolTip(Me.components) + CONTAINER_MAIN.ContentPanel.SuspendLayout() + CONTAINER_MAIN.SuspendLayout() + Me.DEF_GROUP.SuspendLayout() + CType(Me.TXT_GROUPS, System.ComponentModel.ISupportInitialize).BeginInit() + TP_MODE.SuspendLayout() + CType(Me.TXT_TIMER, System.ComponentModel.ISupportInitialize).BeginInit() + Me.SuspendLayout() + ' + 'CONTAINER_MAIN + ' + ' + 'CONTAINER_MAIN.ContentPanel + ' + CONTAINER_MAIN.ContentPanel.Controls.Add(Me.DEF_GROUP) + CONTAINER_MAIN.ContentPanel.Size = New System.Drawing.Size(476, 217) + 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, 242) + CONTAINER_MAIN.TabIndex = 0 + CONTAINER_MAIN.TopToolStripPanelVisible = False + ' + 'DEF_GROUP + ' + 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, 4) + Me.DEF_GROUP.Controls.Add(TP_MODE, 0, 0) + Me.DEF_GROUP.Controls.Add(Me.CH_NOTIFY, 0, 5) + Me.DEF_GROUP.Controls.Add(Me.TXT_TIMER, 0, 6) + Me.DEF_GROUP.Controls.Add(Me.LBL_LAST_TIME_UP, 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 = 9 + 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, 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, 25.0!)) + Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) + Me.DEF_GROUP.Size = New System.Drawing.Size(476, 217) + Me.DEF_GROUP.TabIndex = 0 + ' + 'TXT_GROUPS + ' + ActionButton1.BackgroundImage = CType(resources.GetObject("ActionButton1.BackgroundImage"), System.Drawing.Image) + ActionButton1.Index = 0 + ActionButton1.Name = "BTT_EDIT" + ActionButton2.BackgroundImage = CType(resources.GetObject("ActionButton2.BackgroundImage"), System.Drawing.Image) + ActionButton2.Index = 1 + ActionButton2.Name = "BTT_CLEAR" + Me.TXT_GROUPS.Buttons.Add(ActionButton1) + Me.TXT_GROUPS.Buttons.Add(ActionButton2) + 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, 111) + Me.TXT_GROUPS.Name = "TXT_GROUPS" + Me.TXT_GROUPS.Size = New System.Drawing.Size(468, 22) + Me.TXT_GROUPS.TabIndex = 4 + ' + 'TP_MODE + ' + TP_MODE.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.[Single] + TP_MODE.ColumnCount = 5 + TP_MODE.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 20.0!)) + TP_MODE.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 20.0!)) + TP_MODE.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 20.0!)) + TP_MODE.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 20.0!)) + TP_MODE.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 20.0!)) + TP_MODE.Controls.Add(Me.OPT_ALL, 1, 0) + TP_MODE.Controls.Add(Me.OPT_DEFAULT, 2, 0) + TP_MODE.Controls.Add(Me.OPT_SPEC, 3, 0) + TP_MODE.Controls.Add(Me.OPT_DISABLED, 0, 0) + TP_MODE.Controls.Add(Me.OPT_GROUP, 4, 0) + TP_MODE.Dock = System.Windows.Forms.DockStyle.Fill + TP_MODE.Location = New System.Drawing.Point(1, 1) + TP_MODE.Margin = New System.Windows.Forms.Padding(0) + TP_MODE.Name = "TP_MODE" + TP_MODE.RowCount = 1 + TP_MODE.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) + TP_MODE.Size = New System.Drawing.Size(474, 25) + TP_MODE.TabIndex = 8 + ' + 'OPT_ALL + ' + Me.OPT_ALL.AutoSize = True + Me.OPT_ALL.Dock = System.Windows.Forms.DockStyle.Fill + Me.OPT_ALL.Location = New System.Drawing.Point(98, 4) + Me.OPT_ALL.Name = "OPT_ALL" + Me.OPT_ALL.Size = New System.Drawing.Size(87, 17) + Me.OPT_ALL.TabIndex = 1 + Me.OPT_ALL.TabStop = True + Me.OPT_ALL.Text = "ALL" + TT_MAIN.SetToolTip(Me.OPT_ALL, "Download all users") + Me.OPT_ALL.UseVisualStyleBackColor = True + ' + 'OPT_DEFAULT + ' + Me.OPT_DEFAULT.AutoSize = True + Me.OPT_DEFAULT.Dock = System.Windows.Forms.DockStyle.Fill + Me.OPT_DEFAULT.Location = New System.Drawing.Point(192, 4) + Me.OPT_DEFAULT.Name = "OPT_DEFAULT" + Me.OPT_DEFAULT.Size = New System.Drawing.Size(87, 17) + Me.OPT_DEFAULT.TabIndex = 2 + Me.OPT_DEFAULT.TabStop = True + Me.OPT_DEFAULT.Text = "Default" + TT_MAIN.SetToolTip(Me.OPT_DEFAULT, "All users marked ""Ready for download""") + Me.OPT_DEFAULT.UseVisualStyleBackColor = True + ' + 'OPT_SPEC + ' + Me.OPT_SPEC.AutoSize = True + Me.OPT_SPEC.Dock = System.Windows.Forms.DockStyle.Fill + Me.OPT_SPEC.Location = New System.Drawing.Point(286, 4) + Me.OPT_SPEC.Name = "OPT_SPEC" + Me.OPT_SPEC.Size = New System.Drawing.Size(87, 17) + Me.OPT_SPEC.TabIndex = 3 + Me.OPT_SPEC.TabStop = True + Me.OPT_SPEC.Text = "Specified" + TT_MAIN.SetToolTip(Me.OPT_SPEC, "Select parameters") + Me.OPT_SPEC.UseVisualStyleBackColor = True + ' + 'OPT_DISABLED + ' + Me.OPT_DISABLED.AutoSize = True + Me.OPT_DISABLED.Dock = System.Windows.Forms.DockStyle.Fill + Me.OPT_DISABLED.Location = New System.Drawing.Point(4, 4) + Me.OPT_DISABLED.Name = "OPT_DISABLED" + Me.OPT_DISABLED.Size = New System.Drawing.Size(87, 17) + Me.OPT_DISABLED.TabIndex = 0 + Me.OPT_DISABLED.TabStop = True + Me.OPT_DISABLED.Text = "Disabled" + TT_MAIN.SetToolTip(Me.OPT_DISABLED, "Automation disabled") + Me.OPT_DISABLED.UseVisualStyleBackColor = True + ' + 'OPT_GROUP + ' + Me.OPT_GROUP.AutoSize = True + Me.OPT_GROUP.Dock = System.Windows.Forms.DockStyle.Fill + Me.OPT_GROUP.Location = New System.Drawing.Point(380, 4) + Me.OPT_GROUP.Name = "OPT_GROUP" + Me.OPT_GROUP.Size = New System.Drawing.Size(90, 17) + Me.OPT_GROUP.TabIndex = 4 + Me.OPT_GROUP.TabStop = True + Me.OPT_GROUP.Text = "Groups" + TT_MAIN.SetToolTip(Me.OPT_GROUP, "Download groups") + Me.OPT_GROUP.UseVisualStyleBackColor = True + ' + 'CH_NOTIFY + ' + Me.CH_NOTIFY.AutoSize = True + Me.CH_NOTIFY.Dock = System.Windows.Forms.DockStyle.Fill + Me.CH_NOTIFY.Location = New System.Drawing.Point(4, 140) + Me.CH_NOTIFY.Name = "CH_NOTIFY" + Me.CH_NOTIFY.Size = New System.Drawing.Size(468, 19) + Me.CH_NOTIFY.TabIndex = 12 + Me.CH_NOTIFY.Text = "Show notifications" + Me.CH_NOTIFY.UseVisualStyleBackColor = True + ' + 'TXT_TIMER + ' + ActionButton3.BackgroundImage = CType(resources.GetObject("ActionButton3.BackgroundImage"), System.Drawing.Image) + ActionButton3.Index = 0 + ActionButton3.Name = "BTT_REFRESH" + Me.TXT_TIMER.Buttons.Add(ActionButton3) + Me.TXT_TIMER.CaptionText = "Timer" + Me.TXT_TIMER.CaptionWidth = 50.0R + Me.TXT_TIMER.Dock = System.Windows.Forms.DockStyle.Fill + Me.TXT_TIMER.Location = New System.Drawing.Point(4, 166) + Me.TXT_TIMER.Name = "TXT_TIMER" + Me.TXT_TIMER.Size = New System.Drawing.Size(468, 22) + Me.TXT_TIMER.TabIndex = 16 + ' + 'LBL_LAST_TIME_UP + ' + 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, 192) + 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 = 20 + Me.LBL_LAST_TIME_UP.Text = "Last download date: " + Me.LBL_LAST_TIME_UP.TextAlign = System.Drawing.ContentAlignment.TopCenter + ' + 'AutoDownloaderEditorForm + ' + Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!) + Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font + Me.ClientSize = New System.Drawing.Size(476, 242) + Me.Controls.Add(CONTAINER_MAIN) + Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle + Me.Icon = CType(resources.GetObject("$this.Icon"), System.Drawing.Icon) + Me.MaximizeBox = False + Me.MaximumSize = New System.Drawing.Size(492, 281) + Me.MinimizeBox = False + Me.MinimumSize = New System.Drawing.Size(492, 281) + Me.Name = "AutoDownloaderEditorForm" + Me.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide + Me.Text = "AutoDownloader settings" + CONTAINER_MAIN.ContentPanel.ResumeLayout(False) + CONTAINER_MAIN.ResumeLayout(False) + CONTAINER_MAIN.PerformLayout() + Me.DEF_GROUP.ResumeLayout(False) + Me.DEF_GROUP.PerformLayout() + CType(Me.TXT_GROUPS, System.ComponentModel.ISupportInitialize).EndInit() + TP_MODE.ResumeLayout(False) + TP_MODE.PerformLayout() + CType(Me.TXT_TIMER, System.ComponentModel.ISupportInitialize).EndInit() + Me.ResumeLayout(False) + + End Sub + Private WithEvents DEF_GROUP As DownloadObjects.Groups.GroupDefaults + Private WithEvents TXT_GROUPS As PersonalUtilities.Forms.Controls.TextBoxExtended + Private WithEvents OPT_ALL As RadioButton + Private WithEvents OPT_DEFAULT As RadioButton + Private WithEvents OPT_SPEC As RadioButton + Private WithEvents OPT_DISABLED As RadioButton + Private WithEvents CH_NOTIFY As CheckBox + Friend WithEvents TXT_TIMER As PersonalUtilities.Forms.Controls.TextBoxExtended + Private WithEvents OPT_GROUP As RadioButton + Private WithEvents LBL_LAST_TIME_UP As Label + End Class +End Namespace \ No newline at end of file diff --git a/SCrawler/Download/AutoDownloaderEditorForm.resx b/SCrawler/Download/AutoDownloaderEditorForm.resx new file mode 100644 index 0000000..2a0eae1 --- /dev/null +++ b/SCrawler/Download/AutoDownloaderEditorForm.resx @@ -0,0 +1,718 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + False + + + + + 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 + AAAASUVORK5CYII= + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO + xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go + tbbB43rK5xSAQq1VYFtmeQBoqZTSreVZvgTknM8yyyjA/qodsDF9gspD2Bj6B+DH+NqzhQQAG+POMnSX + AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC + + + + False + + + False + + + 17, 17 + + + + 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 + js7n7sjAAAAAAAAAAAAAAAAAAAAAaM6Ozuzuzu5wAAAAAAAAAAAAAAAAAAAAbn7Obn5+jshgAAAAAAAA + AAAAAAAAAAAAbOjn7Ozs7O5wAAAAAAAAAAAAAAAAAAAAfnzn5+bn7n7AAAAAAAAAAAAAAAAAAAAAbOjs + 7OzuzshgAAAAAAAAAAAAAAAAAAAAaOyOfn7I5+5wAAAAAAAAAAAAAAAAAAAAbOfs7Ozm7OfAAAAAAAAA + AAAAAAAAAAAAbnzn5+bs5u5wAAAAAAAAAAAAAAAAAAAAfOfsjOx+zn7AAAAAAAAAAAAAAAAAAAAAbnzn + 5o7OfshgAAAAAAAAAAAAAAAAAAAAx+Z+zs5uzm5gAAAAAAAAAAAAAAAAAAAAbs7H5+fObsjAAAAAAAAA + AAAAAAAAAAAAZ2js585s7O5wAAAAAAAAAAAAAAAAAAAAjOyH5+jn53aAAAAAAAAAAAAAAAAAAAAACGZs + bHxsfGgAAAAAAAAAAAD///////8AAP///////wAA////////AAD///j///8AAP//8H///wAA///gP/// + AAD//+Af//8AAP//wB///wAA//+AD///AAD//wAH//8AAP/+AAP//wAA//4AAf//AAD//AAB//8AAP/4 + AAD//wAA//AAAH//AAD/4AAAP/8AAP/gAAAf/wAA/8AAAB//AAD/gAAAD/8AAP8AAAAH/wAA/gAAAAP/ + AAD+AAAAAf8AAPwAAAAB/wAA+AAAAAD/AADwAAAAAH8AAPAAAAAAPwAA4AAAAAA/AADgAAAAAD8AAP/8 + AAH//wAA//wAAf//AAD//AAB//8AAP/8AAH//wAA//wAAf//AAD//AAB//8AAP/8AAH//wAA//wAAf// + AAD//AAB//8AAP/8AAH//wAA//wAAf//AAD//AAB//8AAP/8AAH//wAA//wAAf//AAD//AAB//8AAP/8 + AAH//wAA//wAAf//AAD//AAB//8AAP/8AAH//wAA//4AA///AAAoAAAAIAAAAEAAAAABAAQAAAAAAAAC + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAgAAAAICAAIAAAACAAIAAgIAAAICAgADAwMAAAAD/AAD/ + AAAA//8A/wAAAP8A/wD//wAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5+AAAAAAAAAAAAAAAAAA + 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 + JgDTji0A05ImANiRIwDdnScA1JQpANmVKwDXmCoA25stAMGJNQDAhj0A0I84AM2VPwDTmTYA3JwyANKR + OQDSlDgA2Js6AOGeIADknS8A3qAxANyiPwDopCAA7qogAOKiLQDtqykA8asjAPOxJwD6syAA+LMkAPq1 + JwD2tSoA+rYsAPq4LgDiozEA6qUzAOepNQDpqTEA66wyAOmrNwDtqjUA6qw2AO2vNQDjpjoA6aw4AOir + PwDwrDgA67A3AO2xOQDssT8A8rIwAPazMwD2tzEA8bE2APq7NgDytDoA+Lc6APW5PAD5uToA+bw7APi7 + PQD5vD0A/L49APzAPgCxgEMAv4VAALaFSAC5h0sAvJZmALyacgC5mnYAxYlBAMOLRgDHi0QAyY1GAMaO + SgDJj0oAyZBEAMyQSgDPlEkAypFNAM6TTADNlE4A0pVHANebQwDUnUUA0JRNANaaSgDDkVUAzpZWAM2X + WwDOmVsAzZpdANGZVgDUmlcA0ppaANOcWwDQnF4A16BHANqiSADapUkA3adLAN2lTQDbo1QA1KFeANql + WwDgpkMA4KtOAO6sSwDxrUYA8rdAAPGxRQD1uUAA+r1AAPSySADirl0Ax5ljAMOYZgDFmWUAz51gAM+f + ZADBnG4AxJ1uANGeYgDQnmUA0aBjANalYwDVoWQA2KRgAN+sYwDbqWYA3aplAN2sZwDRo2sA1KdrANWl + bADZqmoA3q5qAMWhcgDNpHMAzKV1AMmmeQDUqXAA1Kp5ANuwcADhrWAA569jAOCvaQDismgA5LRoAOGy + bADitGwA+8BDALGchQCxnokAxa2OAMqwkQDPs5MA1LiXAN3AngDjwJYAAAAAAP///wAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADc2b4AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAOy6sLjRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + 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 + EADjmxEA5ZoQAOScEQDnnxQA6Z0SAOWcGgDpoBIA6aEUAO2iFADtpBYA5qAZAO+oGgDwphcA8acYAPGp + GAD0qxkA86waAPSsGgD1rhwA9rAbAPawHAD4sBoA+LEcAMyAIADXhSMA0IUnAN+NKgDRkScA2pYhAN2b + IwDcmyUA05MoANWWKQDali8A2ZksANucLQDcnS4A3Z0xAN2TOwDblT8A1po7ANKYPADVmj0A154+AOaO + JADjkCoA5ZIuAOqTKgDtkysA7ZQrAOiTLQDulSwA4pIxAOmWMQDrlzUA6J02AOOaOQDgnTwA6pw5APCa + NADynj0A36AvAN6hMADdoDgA2aE/AOSgIQDioSYA7KchAO2oJgDgoCkA6agrAPewIAD5tCIA+bQnAPOx + KAD5tigA+bcvAOOmMgDjpjQA5KU0AOepNQDnrD8A6Ko4AOyvOADprj8A86A/AO+zOgDysjAA8LE3APe4 + NgD7ujEA8rY7APCzPQDytj4A9Lg6APW6PQD7vDoA+b0+AMWARADPkEUA3ZlEANmbTQDPnF8A0phUAOqe + QgDdoEEA2aJGAN2kRADep0kA3aVOANuiUADdplQA4ahGAO2qRwDooEgA4alKAOOtTQDzoUAA869EAPap + TgDsskAA77JGAOewTgDusUoA6rBMAPC1QwD0ukUA9rxGAPm9QQDwskkA8rVNAPe+SAD1uk8A+L5IAPWp + UADyqlQA6rRRAO22UgDuuFQA47BbAPCyUQD0vFAA8r5WAPC2XAD4sV0AyZ5nAMSlfwDfr3MA2K19AOWt + YgDltmcA67NgAOm5YQDsu2oA77ptAPW+awDwvm0A+bhpAOizcQDiu34A7Lt9APS9cAD7wEIA+8FGAPnA + SAD4wk4A+MNZAPnEWgD6w2AA/ctkAMOnhQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAABta58AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbc2/a8oAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAKpvpabIawAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADWcqgdHaa/awAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAG2JLR8dIM6tXgAAAAAAAAAAAAAAAAAAAAAAAAAAAABlsT0xIx8dLNJzngAAAAAA + AAAAAAAAAAAAAAAAAAAAUq58PDMxIyMdWdVr4gAAAAAAAAAAAAAAAAAAAAAAAJypj0REMzMxJSMdochr + AAAAAAAAAAAAAAAAAAAAAADLcN9KRURERDMxJSMjw69lAAAAAAAAAAAAAAAAAAAAAFDhuH9KSkQzRDMx + JSMq0o1fAAAAAAAAAAAAAAAAAAAY4Nu4gUdHQ0VEM0QlJSNU2XPJAAAAAAAAAAAAAAAAT8TcuNuSTUpK + R0REMzsxJSNd1WsAAAAAAAAAAAAAAJtw3bi425hNTUpKSkRERDMzMSOryGsAAAAAAAAAAADMUd2Vl7i4 + mIFNTU1KSkRERDMzMSXQr2wAAAAAAAAAABfBsJOXmLi4g01NTU1KSkRERDk5MS3ZdJ0AAAAAAAAMz7S0 + urrFxbiETYFNTU1KSo+xlbq6tMfTa9gAAAAAAJoGBgYGCQkL35FLTU2BTU1KthkZKys1NmSgAAAAAAAA + AAAAAAAAAAnfl0pLTU1NgU29FgAAAAAAAAAAAAAAAAAAAAAAAAAABt2VgkpNgU1NTdwVAAAAAAAAAAAA + AAAAAAAAAAAAAAAGxrGPSkpKTU2B2xQAAAAAAAAAAAAAAAAAAAAAAAAAAAbCi5BDREpLTU3cEwAAAAAA + AAAAAAAAAAAAAAAAAAAABMGIi3xAR0pLTdwTAAAAAAAAAAAAAAAAAAAAAAAAAAADsoWKjzlEQ0pL3BEA + AAAAAAAAAAAAAAAAAAAAAAAAAAOrhYWFOztEREq7EQAAAAAAAAAAAAAAAAAAAAAAAAAAA6tdXIV5OTtE + Q7YLAAAAAAAAAAAAAAAAAAAAAAAAAAABpVxcdn0nMztEtgkAAAAAAAAAAAAAAAAAAAAAAAAAAAGhWFpd + di8nMzu1CQAAAAAAAAAAAAAAAAAAAAAAAAAAAaVTWFxdViUnJ7EGAAAAAAAAAAAAAAAAAAAAAAAAAAAB + YFNYWFhcKSQnsAYAAAAAAAAAAAAAAAAAAAAAAAAAAA/XYWBjoaF3VFZWDQAAAAAAAAAAAAAAAAAAAAAA + AAAAAA8BAQEBAQEEBA4AAAAAAAAAAAAAAAD///////x////4P///8D///+Af///gD///wAf//4AD//8A + A//+AAH//gAA//wAAH/4AAB/8AAAP+AAAB/gAAAPwAAAB8AAAA//gAf//4AH//+AB///gAf//4AH//+A + B///gAf//4AH//+AB///gAf//4AH//+AB///gAf//8AP/ygAAAAYAAAAMAAAAAEACAAAAAAAQAIAAAAA + AAAAAAAAAAEAAAABAAAAAAAApWYWAKhpGQCqbBsAqm0dAKxuHgCucCEAsHMjALJ2JgC2eioAuH4uALyC + MgC9gzQAvoU1AN+YEwDklw8A5ZkQAOmdEgDooBIA6aEUAO2hFQDtpRYA8KYXAPGqFwDxpxgA8qoZAPSr + GgD0rRoA86wcAPauHAD3sBoA97AcAPmwGgD4sRwAwIY3AMGJOQDDizwAxIs8AMWNPgDjnSUA5J4nAN6g + MADnoyAA5aAnAOSjKQDgoi8A7awpAPCsIwD6syIA+rcrAOKlMwDqqzIA6Kk3AOSpOgDpqz0A+rozAPq6 + NADytjsA97s+APe8PAD5uzgA36VCAN+sVADgqUQA5axHAOmuQADkrUoA6bNKAO63TgDws0EA9btDAPS6 + RQD6vkEA9LxMAPm+SADhrlUA5LJZAPK7UAD7wEMA+8NOAPbBUAD6xFIA+8VUAPDDaQD0xmwA9shtAPjK + bgD6zW8A8cmCAPPMhAD0zoYA99CHAPDPmQDw0JkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmAAAAAAAA + AAAAAAAAAAAAAAAAAAAAACZcJgAAAAAAAAAAAAAAAAAAAAAAAAAAJjYnXSYAAAAAAAAAAAAAAAAAAAAA + AAAiRREPJ10mAAAAAAAAAAAAAAAAAAAAAAtHFhYQECddJgAAAAAAAAAAAAAAAAAACkobGxYRERAnXSYA + AAAAAAAAAAAAAAAJUjAbGxgbFhEQKF0mAAAAAAAAAAAAAAhSTjEgGxsbFhYRECtdJgAAAAAAAAAABjpI + SDggICAbGBgWERAnXSYAAAAAAAAFOVBSUk84IDAgGxtUW1paWlwmAAAAAAAABQUGBgg8IDAgMBtWCwsL + IiIAAAAAAAAAAAAAAAY6HiAgMCBXCgAAAAAAAAAAAAAAAAAAAAVHGx4wICBXCgAAAAAAAAAAAAAAAAAA + AAVJGxseMCBXCQAAAAAAAAAAAAAAAAAAAAVJLxsbHjBXCAAAAAAAAAAAAAAAAAAAAAFELhYcGx5XCAAA + AAAAAAAAAAAAAAAAAAFDMxMVGxtXBgAAAAAAAAAAAAAAAAAAAAFANCsWFhxVBgAAAAAAAAAAAAAAAAAA + AAE/MisOEBZVBQAAAAAAAAAAAAAAAAAAAAE9KS0OEBNUAwAAAAAAAAAAAAAAAAAAAAE+S0xAND9TAwAA + AAAAAAAAAAAAAAAAAAABAQEBAwEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP/v + /wD/x/8A/4P/AP8B/wD+AP8A/AB/APgAPwDwAB8A4AAPAMAABwDgAA8A/gD/AP4A/wD+AP8A/gD/AP4A + /wD+AP8A/gD/AP4A/wD+AP8A/gD/AP8B/wD///8AKAAAABAAAAAgAAAAAQAIAAAAAAAAAQAAAAAAAAAA + AAAAAQAAAAEAAAAAAAClZhYAsnYmALN4KQC4fi8A1Y0XANWOGADXkBkA2pUbANuVHADclhwA3poeAOCc + HwDgmyAA46EiAOaiIwDmpiMA56cnAOilIwDppiUA66omAO2rJwDuricA46YsAOyrKADurikA7q4sAPCv + KQDvsS4A8LAqAO2vMQDkpjgA5q8+AOqwOADwszIA4qtBAOi0TwDyvEoA7LlSAOSyXADnt18Aw5ljAMui + bQDqumEA5LhoAOa7cADpv3MA78FlAPDFdgDyyXkA88x6APXNegD20IAA6MeQAOnIkQDpyZcAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP///wAAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAzcCAAAAAAAAAAAAAAAAAycFNQIAAAAA + AAAAAAAAAysLCAY1BAAAAAAAAAAAAy8SDg0IBjUEAAAAAAAAAzMaFBIODQgINQQAAAAAAzQlIhQaEg4f + Li01BAAAKQEBAQEbGhQUMAQEBAQqAAAAAAABGxsbFDMEAAAAAAAAAAAAARwbGxszBAAAAAAAAAAAAAEe + FBsbMwQAAAAAAAAAAAABIRQcHDMEAAAAAAAAAAAAASARFBQzBAAAAAAAAAAAAAEjFw4WMwQAAAAAAAAA + AAABLCgkJjMDAAAAAAAAAAAAAAEBAQEBAAAAAAAA/v8AAPx/AAD4PwAA8B8AAOAPAADABwAAgAMAAAAB + AADwHwAA8B8AAPAfAADwHwAA8B8AAPAfAADwHwAA+D8AACgAAAAwAAAAYAAAAAEAIAAAAAAAgCUAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAADAAAABgAAAAgAAAAGAAAAAwAA + AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAMAAAAFgAA + ABsAAAAWAAAACwAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAA + AAwAAAAfAAAAMwAAADwAAAAyAAAAHQAAAAsAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAACAAAACSccDyHPllLiypFQ4M2UUesdFQtZAAAANQAAABoAAAAJAAAAAgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAEAAAAHAAAAF8aOTb/RlU3z0pVH/82UU/K/iUvcAAAAUAAAADAAAAAWAAAABgAA + AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAUAAAATs4BGjtOXUfXLfxn/0pE5/9ikYP7Rl1T3pXdBvQAA + AEoAAAAqAAAAEgAAAAUAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + 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 + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACiaB9ppmsg9dKVL//nqjX/67A3/+6yOP/wtDr/87Y7//W4 + PP/4uz3/+b0+//m9QP/5vUD/+rw7//myHv/5sh3/+bId//myHf/5sh3/+LEd//iwHP/2rhv/9awb//Or + Gv/xqBn/8KYY/+6jFf/roBP/6p4Q/+icDv/llw3/4ZQK/9+QCf/cjQb/2okF/9mRHf/ismj40ZVQ9MGK + SogAAAALAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACmaR7PyYgn+uSdL//loDD/56Iy/+qk + M//rpjT/7qk3//CrOP/xrTn/8a05//CxOP/4vD7/+bxA//izJP/4sRz/+bId//myHf/5sh3/+bId//mx + Hf/4sBz/9q4c//WsG//zqxr/8qkY//GrI//xsDr/8bFF//W0Sf/2s0n/9LJI//OwSP/yr0f/8a1G//Cs + R//urEv/5ahV6dGVT+AAAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACkaR2XpGoe/6Vq + H/+mayD/pmwh/6hsIv+pbSP/qW4k/6twJf+scCf/rXIn/65zKf/6vDz/+LxA//a1Kv/2sBr/+bEd//my + Hf/5sh3/+bId//myHf/5sR3/+LAc//auHP/1rRv/9Ksa//a3Ov/EiED/xYlB/8aKQ//Hi0T/yY1G/8qO + R//Lj0n/zJBK/82RS//Ok0z/0JRN/9GVTpcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK1yJ//5uzv/9rs+//a3 + Mf/1rhr/9rAc//iyHf/5sh3/+bId//myHf/5sh3/+bEd//iwHP/2rhz/9a0b//m5Ov/Chj//AAAAKwAA + AA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKtw + Jf/4tzr/9Lg9//W3OP/0rRz/9a4b//awHP/4sh3/+bId//myHf/5sh3/+bId//mxHf/4sBz/9q4c//q8 + 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 + + + \ No newline at end of file diff --git a/SCrawler/Download/AutoDownloaderEditorForm.vb b/SCrawler/Download/AutoDownloaderEditorForm.vb new file mode 100644 index 0000000..7404430 --- /dev/null +++ b/SCrawler/Download/AutoDownloaderEditorForm.vb @@ -0,0 +1,136 @@ +' Copyright (C) 2022 Andy +' This program is free software: you can redistribute it and/or modify +' it under the terms of the GNU General Public License as published by +' the Free Software Foundation, either version 3 of the License, or +' (at your option) any later version. +' +' This program is distributed in the hope that it will be useful, +' but WITHOUT ANY WARRANTY +Imports PersonalUtilities.Forms +Imports PersonalUtilities.Forms.Controls.Base +Imports PersonalUtilities.Forms.Toolbars +Imports DModes = SCrawler.DownloadObjects.AutoDownloader.Modes +Namespace DownloadObjects + Friend Class AutoDownloaderEditorForm : Implements IOkCancelToolbar + Private ReadOnly MyDefs As DefaultFormProps + Private ReadOnly MyGroups As List(Of String) + Friend Property IsControlForm As Boolean = False + Friend Sub New() + InitializeComponent() + MyDefs = New DefaultFormProps + MyGroups.ListAddList(Settings.Automation.Groups, LAP.NotContainsOnly) + End Sub + Friend Class AutomationTimerChecker : Implements IFieldsCheckerProvider + Private Property ErrorMessage As String = "The timer value must be greater than 0" Implements IFieldsCheckerProvider.ErrorMessage + Private Property Name As String Implements IFieldsCheckerProvider.Name + Private Property TypeError As Boolean Implements IFieldsCheckerProvider.TypeError + Private Function Convert(ByVal Value As Object, ByVal DestinationType As Type, ByVal Provider As IFormatProvider, + Optional ByVal NothingArg As Object = Nothing, Optional ByVal e As ErrorsDescriber = Nothing) As Object Implements ICustomProvider.Convert + If CInt(AConvert(Of Integer)(Value, -10)) > 0 Then + Return Value + Else + Return Nothing + End If + End Function + Private Function GetFormat(ByVal FormatType As Type) As Object Implements IFormatProvider.GetFormat + Throw New NotImplementedException() + End Function + End Class + Private _Loaded As Boolean = False + Friend Shadows Sub Show() + MyBase.Show() + If Not _Loaded And IsControlForm Then AutoDownloaderEditorForm_Load(Nothing, EventArgs.Empty) + End Sub + Private Sub AutoDownloaderEditorForm_Load(sender As Object, e As EventArgs) Handles Me.Load + With MyDefs + If Not IsControlForm Then + .MyViewInitialize(Me, Settings.Design, True) + .AddOkCancelToolbar() + End If + With Settings.Automation + Select Case .Mode + Case DModes.None : OPT_DISABLED.Checked = True + Case DModes.All : OPT_ALL.Checked = True + Case DModes.Default : OPT_DEFAULT.Checked = True + Case DModes.Specified : OPT_SPEC.Checked = True + Case DModes.Groups : OPT_GROUP.Checked = True + End Select + ChangeEnabled() + DEF_GROUP.Set(Settings.Automation) + If MyGroups.Count > 0 Then TXT_GROUPS.Text = MyGroups.ListToString + If Settings.Groups.Count = 0 Then TXT_GROUPS.Clear() : TXT_GROUPS.Enabled = False + CH_NOTIFY.Checked = .ShowNotifications + TXT_TIMER.Text = .Timer + LBL_LAST_TIME_UP.Text &= .LastDownloadDate.ToStringDate(ADateTime.Formats.BaseDateTime) + End With + If Not IsControlForm Then + .MyFieldsChecker = New FieldsChecker + With DirectCast(.MyFieldsChecker, FieldsChecker) + .AddControl(Of Integer)(TXT_TIMER, TXT_TIMER.CaptionText,, New AutomationTimerChecker) + .EndLoaderOperations() + End With + .DelegateClosingChecker() + .AppendDetectors() + .EndLoaderOperations() + End If + End With + _Loaded = True + End Sub + Private Sub AutoDownloaderEditorForm_Disposed(sender As Object, e As EventArgs) Handles Me.Disposed + MyGroups.Clear() + End Sub + Friend Sub SaveSetiings() Implements IOkCancelToolbar.ToolbarBttOK + If If(MyDefs.MyFieldsChecker?.AllParamsOK, True) Then + With Settings.Automation + Select Case True + Case OPT_DISABLED.Checked : .Mode = DModes.None + Case OPT_ALL.Checked : .Mode = DModes.All + Case OPT_DEFAULT.Checked : .Mode = DModes.Default + Case OPT_SPEC.Checked : .Mode = DModes.Specified + Case OPT_GROUP.Checked : .Mode = DModes.Groups + End Select + DEF_GROUP.Get(Settings.Automation) + .Groups.Clear() + .Groups.ListAddList(MyGroups) + .Timer = AConvert(Of Integer)(TXT_TIMER.Text, AutoDownloader.DefaultTimer) + .Update() + End With + If Not IsControlForm Then MyDefs.CloseForm() + End If + End Sub + Private Sub ToolbarBttCancel() Implements IOkCancelToolbar.ToolbarBttCancel + MyDefs.CloseForm(DialogResult.Cancel) + End Sub + Private Sub TXT_GROUPS_ActionOnButtonClick(ByVal Sender As ActionButton) 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"} + f.ShowDialog() + If f.DialogResult = DialogResult.OK Then MyGroups.ListAddList(f.LabelsList, LAP.ClearBeforeAdd) : TXT_GROUPS.Text = MyGroups.ListToString + End Using + Case ActionButton.DefaultButtons.Clear : MyGroups.Clear() + End Select + End Sub + Private Sub OPT_DISABLED_CheckedChanged(sender As Object, e As EventArgs) Handles OPT_DISABLED.CheckedChanged + ChangeEnabled() + End Sub + Private Sub OPT_ALL_CheckedChanged(sender As Object, e As EventArgs) Handles OPT_ALL.CheckedChanged + ChangeEnabled() + End Sub + Private Sub OPT_DEFAULT_CheckedChanged(sender As Object, e As EventArgs) Handles OPT_DEFAULT.CheckedChanged + ChangeEnabled() + End Sub + Private Sub OPT_SPEC_CheckedChanged(sender As Object, e As EventArgs) Handles OPT_SPEC.CheckedChanged + ChangeEnabled() + End Sub + Private Sub OPT_GROUP_CheckedChanged(sender As Object, e As EventArgs) Handles OPT_GROUP.CheckedChanged + ChangeEnabled() + End Sub + Private Sub ChangeEnabled() + DEF_GROUP.Enabled = OPT_SPEC.Checked + TXT_GROUPS.Enabled = OPT_GROUP.Checked + TXT_TIMER.Enabled = Not OPT_DISABLED.Checked + CH_NOTIFY.Enabled = Not OPT_DISABLED.Checked + End Sub + End Class +End Namespace \ No newline at end of file diff --git a/SCrawler/Download/DownloadSavedPostsForm.vb b/SCrawler/Download/DownloadSavedPostsForm.vb index 424b82b..b21d7e5 100644 --- a/SCrawler/Download/DownloadSavedPostsForm.vb +++ b/SCrawler/Download/DownloadSavedPostsForm.vb @@ -11,7 +11,7 @@ Imports PersonalUtilities.Forms Imports SCrawler.DownloadObjects Imports SCrawler.Plugin.Hosts Friend Class DownloadSavedPostsForm - Friend Event OnDownloadDone(ByVal Message As String) + Friend Event OnDownloadDone As NotificationEventHandler Private MyView As FormsView Private ReadOnly JobsList As List(Of DownloadProgress) Friend ReadOnly Property Working As Boolean diff --git a/SCrawler/Download/DownloadedInfoForm.vb b/SCrawler/Download/DownloadedInfoForm.vb index f791985..8450451 100644 --- a/SCrawler/Download/DownloadedInfoForm.vb +++ b/SCrawler/Download/DownloadedInfoForm.vb @@ -11,7 +11,7 @@ Imports PersonalUtilities.Forms Imports SCrawler.API.Base Namespace DownloadObjects Friend Class DownloadedInfoForm - Friend Event OnUserLooking(ByVal Key As String) + Friend Event UserFind(ByVal Key As String) Private MyView As FormsView Private ReadOnly LParams As New ListAddParams(LAP.IgnoreICopier) With {.Error = EDP.None} Private Opened As Boolean = False @@ -143,7 +143,7 @@ Namespace DownloadObjects Try If _LatestSelected.ValueBetween(0, LIST_DOWN.Items.Count - 1) AndAlso _LatestSelected.ValueBetween(0, Downloader.Downloaded.Count - 1) Then Dim i% = Settings.Users.IndexOf(_TempUsersList(_LatestSelected)) - If i >= 0 Then RaiseEvent OnUserLooking(Settings.Users(i).Key) + If i >= 0 Then RaiseEvent UserFind(Settings.Users(i).Key) End If Catch ex As Exception End Try diff --git a/SCrawler/Download/Groups/DownloadGroup.vb b/SCrawler/Download/Groups/DownloadGroup.vb index e5194fc..0c0d9d6 100644 --- a/SCrawler/Download/Groups/DownloadGroup.vb +++ b/SCrawler/Download/Groups/DownloadGroup.vb @@ -11,16 +11,12 @@ Imports PersonalUtilities.Functions.XML.Base Imports SCrawler.API Imports SCrawler.API.Base Namespace DownloadObjects.Groups - Friend Class DownloadGroup : Implements IIndexable, IEContainerProvider, IDisposable + Friend Class DownloadGroup : Inherits GroupParameters : Implements IIndexable, IEContainerProvider Friend Delegate Sub GroupEventHandler(ByVal Sender As DownloadGroup) Friend Event Deleted As GroupEventHandler Friend Event Updated As GroupEventHandler #Region "XML Names" Private Const Name_Name As String = "Name" - Private Const Name_Temporary As String = "Temporary" - Private Const Name_Favorite As String = "Favorite" - Private Const Name_ReadyForDownload As String = "RFD" - Private Const Name_ReadyForDownloadIgnore As String = "RFDI" #End Region Private WithEvents BTT_EDIT As ToolStripMenuItem Private WithEvents BTT_DELETE As ToolStripMenuItem @@ -28,10 +24,7 @@ Namespace DownloadObjects.Groups Private WithEvents BTT_DOWNLOAD_FULL As ToolStripMenuItem Private ReadOnly SEP_1 As ToolStripSeparator Private WithEvents BTT_MENU As ToolStripMenuItem - Friend Property Temporary As CheckState = CheckState.Indeterminate - Friend Property Favorite As CheckState = CheckState.Indeterminate - Friend Property ReadyForDownload As Boolean = True - Friend Property ReadyForDownloadIgnore As Boolean = False + Friend Property NameBefore As String = String.Empty Friend Property Name As String Private _Key As String = String.Empty Friend ReadOnly Property Key As String @@ -51,14 +44,7 @@ Namespace DownloadObjects.Groups If b Then RaiseEvent Updated(Me) End Set End Property - Friend ReadOnly Property Labels As List(Of String) - Friend ReadOnly Property Count As Integer - Get - Return Labels.Count - End Get - End Property Friend Sub New() - Labels = New List(Of String) BTT_MENU = New ToolStripMenuItem With { .ToolTipText = "Download users of this group", .AutoToolTip = True, @@ -142,26 +128,42 @@ Namespace DownloadObjects.Groups Private Sub BTT_DOWNLOAD_FULL_Click(sender As Object, e As EventArgs) Handles BTT_DOWNLOAD_FULL.Click DownloadUsers(False) End Sub + Friend Overloads Function GetUsers() As IEnumerable(Of IUserData) + Return GetUsers(Me, True) + End Function + Friend Overloads Shared Function GetUsers(ByVal Instance As IGroup, ByVal UseReadyOption As Boolean) As IEnumerable(Of IUserData) + Try + If Settings.Users.Count > 0 Then + With Instance + Dim CheckParams As Predicate(Of IUserData) = Function(user) _ + (.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) + 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) + End With + Else + Return Nothing + End If + Catch ex As Exception + Return ErrorsDescriber.Execute(EDP.SendInLog, ex, "[DownloadGroup.GetUsers]") + End Try + End Function Friend Sub DownloadUsers(ByVal UseReadyOption As Boolean) Try If Settings.Users.Count > 0 Then - Dim CheckParams As Predicate(Of IUserData) = Function(user) _ - (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) - 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 - Dim u As IEnumerable(Of IUserData) = Settings.Users.SelectMany(f) + Dim u As IEnumerable(Of IUserData) = GetUsers(Me, UseReadyOption) If u.ListExists Then Downloader.AddRange(u) Else @@ -174,7 +176,7 @@ Namespace DownloadObjects.Groups End Sub #End Region #Region "IEContainerProvider Support" - Public Function ToEContainer(Optional ByVal e As ErrorsDescriber = Nothing) As EContainer Implements IEContainerProvider.ToEContainer + 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)), @@ -183,28 +185,16 @@ Namespace DownloadObjects.Groups End Function #End Region #Region "IDisposable Support" - Private disposedValue As Boolean = False - Protected Overridable Overloads Sub Dispose(ByVal disposing As Boolean) - If Not disposedValue Then - If disposing Then - Labels.Clear() - BTT_DELETE.Dispose() - BTT_EDIT.Dispose() - BTT_MENU.Dispose() - SEP_1.Dispose() - BTT_DOWNLOAD.Dispose() - BTT_DOWNLOAD_FULL.Dispose() - End If - disposedValue = True + Protected Overrides Sub Dispose(ByVal disposing As Boolean) + If Not disposedValue And disposing Then + BTT_DELETE.Dispose() + BTT_EDIT.Dispose() + BTT_MENU.Dispose() + SEP_1.Dispose() + BTT_DOWNLOAD.Dispose() + BTT_DOWNLOAD_FULL.Dispose() End If - End Sub - Protected Overrides Sub Finalize() - Dispose(False) - MyBase.Finalize() - End Sub - Friend Overloads Sub Dispose() Implements IDisposable.Dispose - Dispose(True) - GC.SuppressFinalize(Me) + MyBase.Dispose(disposing) End Sub #End Region End Class diff --git a/SCrawler/Download/Groups/DownloadGroupCollection.vb b/SCrawler/Download/Groups/DownloadGroupCollection.vb index 02b8c4c..36e9803 100644 --- a/SCrawler/Download/Groups/DownloadGroupCollection.vb +++ b/SCrawler/Download/Groups/DownloadGroupCollection.vb @@ -80,6 +80,13 @@ Namespace DownloadObjects.Groups Friend Function DownloadGroupIfExists(ByVal Index As Integer) As Boolean If Index.ValueBetween(0, Count - 1) Then Item(Index).DownloadUsers(True) : Return True Else Return False End Function + Friend Function IndexOf(ByVal Name As String) As Integer + If Count > 0 Then + Return GroupsList.FindIndex(Function(g) g.Name = Name) + Else + Return -1 + End If + End Function Private Function GetEnumerator() As IEnumerator(Of DownloadGroup) Implements IEnumerable(Of DownloadGroup).GetEnumerator Return New MyEnumerator(Of DownloadGroup)(Me) End Function diff --git a/SCrawler/Download/Groups/GroupDefaults.vb b/SCrawler/Download/Groups/GroupDefaults.vb new file mode 100644 index 0000000..bf61391 --- /dev/null +++ b/SCrawler/Download/Groups/GroupDefaults.vb @@ -0,0 +1,142 @@ +' Copyright (C) 2022 Andy +' This program is free software: you can redistribute it and/or modify +' it under the terms of the GNU General Public License as published by +' the Free Software Foundation, either version 3 of the License, or +' (at your option) any later version. +' +' This program is distributed in the hope that it will be useful, +' but WITHOUT ANY WARRANTY +Imports PersonalUtilities.Forms.Controls +Imports PersonalUtilities.Forms.Controls.Base +Imports ADB = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons +Namespace DownloadObjects.Groups + Public Class GroupDefaults : Inherits TableLayoutPanel + Private ReadOnly TP_1 As TableLayoutPanel + Private ReadOnly TP_2 As TableLayoutPanel + Private ReadOnly CH_TEMPORARY As CheckBox + Private ReadOnly CH_FAV As CheckBox + Private ReadOnly CH_READY_FOR_DOWN As CheckBox + Private ReadOnly CH_READY_FOR_DOWN_IGNORE As CheckBox + Private WithEvents TXT_LABELS As TextBoxExtended + Private ReadOnly Labels 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 + 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} + CH_READY_FOR_DOWN_IGNORE = New CheckBox With {.Text = "Ignore ready for download", .Name = "CH_READY_FOR_DOWN_IGNORE", .Checked = False, .Dock = DockStyle.Fill} + TP_1 = New TableLayoutPanel With {.CellBorderStyle = TableLayoutPanelCellBorderStyle.Single, .Margin = New Padding(0), .Dock = DockStyle.Fill} + FillTP(TP_1, CH_TEMPORARY, CH_FAV) + 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 FillTP(ByRef TP As TableLayoutPanel, ByVal CNT1 As Control, ByVal CNT2 As Control) + With TP + .ColumnCount = 2 + .ColumnStyles.Add(New ColumnStyle(SizeType.Percent, 50)) + .ColumnStyles.Add(New ColumnStyle(SizeType.Percent, 50)) + .RowCount = 1 + .RowStyles.Add(New RowStyle(SizeType.Percent, 100)) + With .Controls : .Add(CNT1, 0, 0) : .Add(CNT2, 1, 0) : End With + End With + End Sub + Private Sub GroupDefaults_Disposed(sender As Object, e As EventArgs) Handles Me.Disposed + Labels.Clear() + CH_TEMPORARY.Dispose() + CH_FAV.Dispose() + CH_READY_FOR_DOWN.Dispose() + CH_READY_FOR_DOWN_IGNORE.Dispose() + TXT_LABELS.Dispose() + With TP_1 + .Controls.Clear() + .RowStyles.Clear() + .ColumnStyles.Clear() + .Dispose() + End With + With TP_2 + .Controls.Clear() + .RowStyles.Clear() + .ColumnStyles.Clear() + .Dispose() + End With + End Sub + Protected Overrides Sub InitLayout() + MyBase.InitLayout() + If ColumnStyles.Count = 2 Or RowStyles.Count = 2 Then + ColumnStyles.Clear() + RowStyles.Clear() + CellBorderStyle = TableLayoutPanelCellBorderStyle.Single + ColumnCount = 1 + ColumnStyles.Add(New ColumnStyle(SizeType.Percent, 100)) + RowCount = 5 + RowStyles.Add(New RowStyle(SizeType.Absolute, 25)) + 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.Percent, 100)) + End If + Controls.Add(TP_1, 0, 1) + Controls.Add(TP_2, 0, 2) + Controls.Add(TXT_LABELS, 0, 3) + End Sub + Private Sub TXT_LABELS_ActionOnButtonClick(ByVal Sender As ActionButton) 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() + End Select + End Sub + Friend Sub [Get](ByRef Instance As IGroup) + If Not Instance Is Nothing Then + With Instance + .Temporary = CH_TEMPORARY.CheckState + .Favorite = CH_FAV.CheckState + .ReadyForDownload = CH_READY_FOR_DOWN.Checked + .ReadyForDownloadIgnore = CH_READY_FOR_DOWN_IGNORE.Checked + .Labels.Clear() + .Labels.ListAddList(Labels) + End With + End If + End Sub + Friend Sub [Set](ByVal Instance As IGroup) + If Not Instance Is Nothing Then + With Instance + CH_TEMPORARY.CheckState = .Temporary + 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 + End With + End If + End Sub + Private _Enabled As Boolean = True + Friend Shadows Property Enabled As Boolean + Get + Return _Enabled + End Get + Set(ByVal e As Boolean) + _Enabled = e + TP_1.Enabled = e + TP_2.Enabled = e + TXT_LABELS.Enabled = e + End Set + End Property + End Class +End Namespace \ No newline at end of file diff --git a/SCrawler/Download/Groups/GroupEditorForm.Designer.vb b/SCrawler/Download/Groups/GroupEditorForm.Designer.vb index 9100646..a468937 100644 --- a/SCrawler/Download/Groups/GroupEditorForm.Designer.vb +++ b/SCrawler/Download/Groups/GroupEditorForm.Designer.vb @@ -23,30 +23,15 @@ Namespace DownloadObjects.Groups Private Sub InitializeComponent() Dim CONTAINER_MAIN As System.Windows.Forms.ToolStripContainer - Dim TP_MAIN As System.Windows.Forms.TableLayoutPanel - Dim ActionButton7 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() + Dim ActionButton1 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() Dim resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(GroupEditorForm)) - Dim ActionButton8 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() - Dim ActionButton9 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() - Dim TP_TEMP_FAV As System.Windows.Forms.TableLayoutPanel - Dim TP_READY_FOR_DOWN As System.Windows.Forms.TableLayoutPanel + Me.DEFS_GROUP = New SCrawler.DownloadObjects.Groups.GroupDefaults() Me.TXT_NAME = New PersonalUtilities.Forms.Controls.TextBoxExtended() - Me.TXT_LABELS = New PersonalUtilities.Forms.Controls.TextBoxExtended() - Me.CH_TEMPORARY = New System.Windows.Forms.CheckBox() - Me.CH_FAV = New System.Windows.Forms.CheckBox() - Me.CH_READY_FOR_DOWN = New System.Windows.Forms.CheckBox() - Me.CH_READY_FOR_DOWN_IGNORE = New System.Windows.Forms.CheckBox() CONTAINER_MAIN = New System.Windows.Forms.ToolStripContainer() - TP_MAIN = New System.Windows.Forms.TableLayoutPanel() - TP_TEMP_FAV = New System.Windows.Forms.TableLayoutPanel() - TP_READY_FOR_DOWN = New System.Windows.Forms.TableLayoutPanel() CONTAINER_MAIN.ContentPanel.SuspendLayout() CONTAINER_MAIN.SuspendLayout() - TP_MAIN.SuspendLayout() + Me.DEFS_GROUP.SuspendLayout() CType(Me.TXT_NAME, System.ComponentModel.ISupportInitialize).BeginInit() - CType(Me.TXT_LABELS, System.ComponentModel.ISupportInitialize).BeginInit() - TP_TEMP_FAV.SuspendLayout() - TP_READY_FOR_DOWN.SuspendLayout() Me.SuspendLayout() ' 'CONTAINER_MAIN @@ -54,46 +39,41 @@ Namespace DownloadObjects.Groups ' 'CONTAINER_MAIN.ContentPanel ' - CONTAINER_MAIN.ContentPanel.Controls.Add(TP_MAIN) - CONTAINER_MAIN.ContentPanel.Size = New System.Drawing.Size(476, 111) + CONTAINER_MAIN.ContentPanel.Controls.Add(Me.DEFS_GROUP) + CONTAINER_MAIN.ContentPanel.Size = New System.Drawing.Size(476, 134) 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, 136) + CONTAINER_MAIN.Size = New System.Drawing.Size(476, 134) CONTAINER_MAIN.TabIndex = 0 CONTAINER_MAIN.TopToolStripPanelVisible = False ' - 'TP_MAIN + 'DEFS_GROUP ' - TP_MAIN.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.[Single] - TP_MAIN.ColumnCount = 1 - TP_MAIN.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) - TP_MAIN.Controls.Add(TP_READY_FOR_DOWN, 0, 2) - TP_MAIN.Controls.Add(Me.TXT_NAME, 0, 0) - TP_MAIN.Controls.Add(Me.TXT_LABELS, 0, 3) - TP_MAIN.Controls.Add(TP_TEMP_FAV, 0, 1) - TP_MAIN.Dock = System.Windows.Forms.DockStyle.Fill - TP_MAIN.Location = New System.Drawing.Point(0, 0) - TP_MAIN.Name = "TP_MAIN" - TP_MAIN.RowCount = 5 - TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) - TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) - TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) - TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) - TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) - TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20.0!)) - TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20.0!)) - TP_MAIN.Size = New System.Drawing.Size(476, 111) - TP_MAIN.TabIndex = 0 + Me.DEFS_GROUP.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.[Single] + Me.DEFS_GROUP.ColumnCount = 1 + Me.DEFS_GROUP.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) + Me.DEFS_GROUP.Controls.Add(Me.TXT_NAME, 0, 0) + 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 = 5 + 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, 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.Percent, 100.0!)) + Me.DEFS_GROUP.Size = New System.Drawing.Size(476, 134) + Me.DEFS_GROUP.TabIndex = 1 ' 'TXT_NAME ' - ActionButton7.BackgroundImage = CType(resources.GetObject("ActionButton7.BackgroundImage"), System.Drawing.Image) - ActionButton7.Index = 0 - ActionButton7.Name = "BTT_CLEAR" - Me.TXT_NAME.Buttons.Add(ActionButton7) + ActionButton1.BackgroundImage = CType(resources.GetObject("ActionButton1.BackgroundImage"), System.Drawing.Image) + ActionButton1.Index = 0 + ActionButton1.Name = "BTT_CLEAR" + Me.TXT_NAME.Buttons.Add(ActionButton1) Me.TXT_NAME.CaptionText = "Name" Me.TXT_NAME.CaptionToolTipEnabled = True Me.TXT_NAME.CaptionToolTipText = "Group name" @@ -104,127 +84,18 @@ Namespace DownloadObjects.Groups Me.TXT_NAME.Size = New System.Drawing.Size(468, 22) Me.TXT_NAME.TabIndex = 0 ' - 'TXT_LABELS - ' - ActionButton8.BackgroundImage = CType(resources.GetObject("ActionButton8.BackgroundImage"), System.Drawing.Image) - ActionButton8.Index = 0 - ActionButton8.Name = "BTT_EDIT" - ActionButton9.BackgroundImage = CType(resources.GetObject("ActionButton9.BackgroundImage"), System.Drawing.Image) - ActionButton9.Index = 1 - ActionButton9.Name = "BTT_CLEAR" - Me.TXT_LABELS.Buttons.Add(ActionButton8) - Me.TXT_LABELS.Buttons.Add(ActionButton9) - Me.TXT_LABELS.CaptionText = "Labels" - Me.TXT_LABELS.CaptionToolTipEnabled = True - Me.TXT_LABELS.CaptionToolTipText = "Group labels" - Me.TXT_LABELS.CaptionWidth = 50.0R - Me.TXT_LABELS.ClearTextByButtonClear = False - Me.TXT_LABELS.Dock = System.Windows.Forms.DockStyle.Fill - Me.TXT_LABELS.Location = New System.Drawing.Point(4, 85) - Me.TXT_LABELS.Name = "TXT_LABELS" - Me.TXT_LABELS.Size = New System.Drawing.Size(468, 22) - Me.TXT_LABELS.TabIndex = 1 - ' - 'TP_TEMP_FAV - ' - TP_TEMP_FAV.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.[Single] - TP_TEMP_FAV.ColumnCount = 2 - TP_TEMP_FAV.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50.0!)) - TP_TEMP_FAV.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50.0!)) - TP_TEMP_FAV.Controls.Add(Me.CH_TEMPORARY, 0, 0) - TP_TEMP_FAV.Controls.Add(Me.CH_FAV, 1, 0) - TP_TEMP_FAV.Dock = System.Windows.Forms.DockStyle.Fill - TP_TEMP_FAV.Location = New System.Drawing.Point(1, 30) - TP_TEMP_FAV.Margin = New System.Windows.Forms.Padding(0) - TP_TEMP_FAV.Name = "TP_TEMP_FAV" - TP_TEMP_FAV.RowCount = 1 - TP_TEMP_FAV.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) - TP_TEMP_FAV.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20.0!)) - TP_TEMP_FAV.Size = New System.Drawing.Size(474, 25) - TP_TEMP_FAV.TabIndex = 2 - ' - 'TP_READY_FOR_DOWN - ' - TP_READY_FOR_DOWN.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.[Single] - TP_READY_FOR_DOWN.ColumnCount = 2 - TP_READY_FOR_DOWN.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50.0!)) - TP_READY_FOR_DOWN.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50.0!)) - TP_READY_FOR_DOWN.Controls.Add(Me.CH_READY_FOR_DOWN, 0, 0) - TP_READY_FOR_DOWN.Controls.Add(Me.CH_READY_FOR_DOWN_IGNORE, 1, 0) - TP_READY_FOR_DOWN.Dock = System.Windows.Forms.DockStyle.Fill - TP_READY_FOR_DOWN.Location = New System.Drawing.Point(1, 56) - TP_READY_FOR_DOWN.Margin = New System.Windows.Forms.Padding(0) - TP_READY_FOR_DOWN.Name = "TP_READY_FOR_DOWN" - TP_READY_FOR_DOWN.RowCount = 1 - TP_READY_FOR_DOWN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) - TP_READY_FOR_DOWN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20.0!)) - TP_READY_FOR_DOWN.Size = New System.Drawing.Size(474, 25) - TP_READY_FOR_DOWN.TabIndex = 3 - ' - 'CH_TEMPORARY - ' - Me.CH_TEMPORARY.AutoSize = True - Me.CH_TEMPORARY.Checked = True - Me.CH_TEMPORARY.CheckState = System.Windows.Forms.CheckState.Indeterminate - Me.CH_TEMPORARY.Dock = System.Windows.Forms.DockStyle.Fill - Me.CH_TEMPORARY.Location = New System.Drawing.Point(4, 4) - Me.CH_TEMPORARY.Name = "CH_TEMPORARY" - Me.CH_TEMPORARY.Size = New System.Drawing.Size(229, 17) - Me.CH_TEMPORARY.TabIndex = 0 - Me.CH_TEMPORARY.Text = "Temporary" - Me.CH_TEMPORARY.ThreeState = True - Me.CH_TEMPORARY.UseVisualStyleBackColor = True - ' - 'CH_FAV - ' - Me.CH_FAV.AutoSize = True - Me.CH_FAV.Checked = True - Me.CH_FAV.CheckState = System.Windows.Forms.CheckState.Indeterminate - Me.CH_FAV.Dock = System.Windows.Forms.DockStyle.Fill - Me.CH_FAV.Location = New System.Drawing.Point(240, 4) - Me.CH_FAV.Name = "CH_FAV" - Me.CH_FAV.Size = New System.Drawing.Size(230, 17) - Me.CH_FAV.TabIndex = 1 - Me.CH_FAV.Text = "Favorite" - Me.CH_FAV.ThreeState = True - Me.CH_FAV.UseVisualStyleBackColor = True - ' - 'CH_READY_FOR_DOWN - ' - Me.CH_READY_FOR_DOWN.AutoSize = True - Me.CH_READY_FOR_DOWN.Checked = True - Me.CH_READY_FOR_DOWN.CheckState = System.Windows.Forms.CheckState.Checked - Me.CH_READY_FOR_DOWN.Dock = System.Windows.Forms.DockStyle.Fill - Me.CH_READY_FOR_DOWN.Location = New System.Drawing.Point(4, 4) - Me.CH_READY_FOR_DOWN.Name = "CH_READY_FOR_DOWN" - Me.CH_READY_FOR_DOWN.Size = New System.Drawing.Size(229, 17) - Me.CH_READY_FOR_DOWN.TabIndex = 0 - Me.CH_READY_FOR_DOWN.Text = "Ready for download" - Me.CH_READY_FOR_DOWN.UseVisualStyleBackColor = True - ' - 'CH_READY_FOR_DOWN_IGNORE - ' - Me.CH_READY_FOR_DOWN_IGNORE.AutoSize = True - Me.CH_READY_FOR_DOWN_IGNORE.Dock = System.Windows.Forms.DockStyle.Fill - Me.CH_READY_FOR_DOWN_IGNORE.Location = New System.Drawing.Point(240, 4) - Me.CH_READY_FOR_DOWN_IGNORE.Name = "CH_READY_FOR_DOWN_IGNORE" - Me.CH_READY_FOR_DOWN_IGNORE.Size = New System.Drawing.Size(230, 17) - Me.CH_READY_FOR_DOWN_IGNORE.TabIndex = 1 - Me.CH_READY_FOR_DOWN_IGNORE.Text = "Ignore ready for download" - Me.CH_READY_FOR_DOWN_IGNORE.UseVisualStyleBackColor = True - ' '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, 136) + Me.ClientSize = New System.Drawing.Size(476, 134) Me.Controls.Add(CONTAINER_MAIN) Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle Me.KeyPreview = True Me.MaximizeBox = False - Me.MaximumSize = New System.Drawing.Size(492, 175) + Me.MaximumSize = New System.Drawing.Size(492, 173) Me.MinimizeBox = False - Me.MinimumSize = New System.Drawing.Size(492, 175) + Me.MinimumSize = New System.Drawing.Size(492, 173) Me.Name = "GroupEditorForm" Me.ShowIcon = False Me.ShowInTaskbar = False @@ -233,21 +104,12 @@ Namespace DownloadObjects.Groups CONTAINER_MAIN.ContentPanel.ResumeLayout(False) CONTAINER_MAIN.ResumeLayout(False) CONTAINER_MAIN.PerformLayout() - TP_MAIN.ResumeLayout(False) + Me.DEFS_GROUP.ResumeLayout(False) CType(Me.TXT_NAME, System.ComponentModel.ISupportInitialize).EndInit() - CType(Me.TXT_LABELS, System.ComponentModel.ISupportInitialize).EndInit() - TP_TEMP_FAV.ResumeLayout(False) - TP_TEMP_FAV.PerformLayout() - TP_READY_FOR_DOWN.ResumeLayout(False) - TP_READY_FOR_DOWN.PerformLayout() Me.ResumeLayout(False) End Sub Private WithEvents TXT_NAME As PersonalUtilities.Forms.Controls.TextBoxExtended - Private WithEvents TXT_LABELS As PersonalUtilities.Forms.Controls.TextBoxExtended - Private WithEvents CH_READY_FOR_DOWN As CheckBox - Private WithEvents CH_READY_FOR_DOWN_IGNORE As CheckBox - Private WithEvents CH_TEMPORARY As CheckBox - Private WithEvents CH_FAV As CheckBox + Private WithEvents DEFS_GROUP As GroupDefaults End Class End Namespace \ No newline at end of file diff --git a/SCrawler/Download/Groups/GroupEditorForm.resx b/SCrawler/Download/Groups/GroupEditorForm.resx index 8feac98..7e5ab0d 100644 --- a/SCrawler/Download/Groups/GroupEditorForm.resx +++ b/SCrawler/Download/Groups/GroupEditorForm.resx @@ -120,14 +120,8 @@ False - - False - - - False - - + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go @@ -135,73 +129,4 @@ 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 - AAAASUVORK5CYII= - - - - - iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO - xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go - tbbB43rK5xSAQq1VYFtmeQBoqZTSreVZvgTknM8yyyjA/qodsDF9gspD2Bj6B+DH+NqzhQQAG+POMnSX - AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC - - - - False - \ No newline at end of file diff --git a/SCrawler/Download/Groups/GroupEditorForm.vb b/SCrawler/Download/Groups/GroupEditorForm.vb index b5b4e3b..f52478f 100644 --- a/SCrawler/Download/Groups/GroupEditorForm.vb +++ b/SCrawler/Download/Groups/GroupEditorForm.vb @@ -7,18 +7,14 @@ ' This program is distributed in the hope that it will be useful, ' but WITHOUT ANY WARRANTY Imports PersonalUtilities.Forms -Imports PersonalUtilities.Forms.Controls.Base Imports PersonalUtilities.Forms.Toolbars Namespace DownloadObjects.Groups Friend Class GroupEditorForm : Implements IOkCancelToolbar Private ReadOnly MyDefs As DefaultFormProps Friend Property MyGroup As DownloadGroup - Private ReadOnly MyLabels As List(Of String) Friend Sub New(ByRef g As DownloadGroup) InitializeComponent() MyGroup = g - MyLabels = New List(Of String) - If Not MyGroup Is Nothing Then MyLabels.ListAddList(MyGroup.Labels) MyDefs = New DefaultFormProps End Sub Private Class NameChecker : Implements IFieldsCheckerProvider @@ -54,11 +50,7 @@ Namespace DownloadObjects.Groups If Not MyGroup Is Nothing Then With MyGroup TXT_NAME.Text = .Name - CH_TEMPORARY.CheckState = .Temporary - CH_FAV.CheckState = .Favorite - CH_READY_FOR_DOWN.Checked = .ReadyForDownload - CH_READY_FOR_DOWN_IGNORE.Checked = .ReadyForDownloadIgnore - TXT_LABELS.Text = MyLabels.ListToString + DEFS_GROUP.Set(MyGroup) Text &= $" { .Name}" End With Else @@ -71,19 +63,13 @@ Namespace DownloadObjects.Groups .EndLoaderOperations() End With End Sub - Private Sub GroupEditorForm_Disposed(sender As Object, e As EventArgs) Handles Me.Disposed - MyLabels.Clear() - End Sub Private Sub ToolbarBttOK() Implements IOkCancelToolbar.ToolbarBttOK If MyDefs.MyFieldsChecker.AllParamsOK Then If MyGroup Is Nothing Then MyGroup = New DownloadGroup With MyGroup + .NameBefore = .Name .Name = TXT_NAME.Text - .Temporary = CH_TEMPORARY.CheckState - .Favorite = CH_FAV.CheckState - .ReadyForDownload = CH_READY_FOR_DOWN.Checked - .ReadyForDownloadIgnore = CH_READY_FOR_DOWN_IGNORE.Checked - .Labels.ListAddList(MyLabels, LAP.ClearBeforeAdd, LAP.NotContainsOnly) + DEFS_GROUP.Get(MyGroup) End With MyDefs.CloseForm() End If @@ -91,21 +77,5 @@ Namespace DownloadObjects.Groups Private Sub ToolbarBttCancel() Implements IOkCancelToolbar.ToolbarBttCancel MyDefs.CloseForm(DialogResult.Cancel) End Sub - Private Sub TXT_LABELS_ActionOnButtonClick(ByVal Sender As ActionButton) Handles TXT_LABELS.ActionOnButtonClick - Select Case Sender.DefaultButton - Case ActionButton.DefaultButtons.Edit - Using f As New LabelsForm(MyLabels) - f.ShowDialog() - If f.DialogResult = DialogResult.OK Then - MyLabels.ListAddList(f.LabelsList, LAP.NotContainsOnly, LAP.ClearBeforeAdd) - TXT_LABELS.Clear() - TXT_LABELS.Text = MyLabels.ListToString - End If - End Using - Case ActionButton.DefaultButtons.Clear - MyLabels.Clear() - TXT_LABELS.Clear() - End Select - End Sub End Class End Namespace \ No newline at end of file diff --git a/SCrawler/Download/Groups/GroupParameters.vb b/SCrawler/Download/Groups/GroupParameters.vb new file mode 100644 index 0000000..71a62eb --- /dev/null +++ b/SCrawler/Download/Groups/GroupParameters.vb @@ -0,0 +1,48 @@ +' Copyright (C) 2022 Andy +' This program is free software: you can redistribute it and/or modify +' it under the terms of the GNU General Public License as published by +' the Free Software Foundation, either version 3 of the License, or +' (at your option) any later version. +' +' This program is distributed in the hope that it will be useful, +' but WITHOUT ANY WARRANTY +Namespace DownloadObjects.Groups + Friend Interface IGroup + ReadOnly Property Labels As List(Of String) + Property Temporary As CheckState + Property Favorite As CheckState + Property ReadyForDownload As Boolean + Property ReadyForDownloadIgnore As Boolean + End Interface + Friend Class GroupParameters : Implements IGroup, IDisposable + Protected Const Name_Temporary As String = "Temporary" + Protected Const Name_Favorite As String = "Favorite" + Protected Const Name_ReadyForDownload As String = "RFD" + Protected Const Name_ReadyForDownloadIgnore As String = "RFDI" + Friend ReadOnly Property Labels As List(Of String) Implements IGroup.Labels + 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) + End Sub +#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() + disposedValue = True + End If + End Sub + Protected Overrides Sub Finalize() + Dispose(False) + MyBase.Finalize() + End Sub + Friend Overloads Sub Dispose() Implements IDisposable.Dispose + Dispose(True) + GC.SuppressFinalize(Me) + End Sub +#End Region + End Class +End Namespace \ No newline at end of file diff --git a/SCrawler/Download/TDownloader.vb b/SCrawler/Download/TDownloader.vb index 09b94f2..038a54f 100644 --- a/SCrawler/Download/TDownloader.vb +++ b/SCrawler/Download/TDownloader.vb @@ -15,18 +15,44 @@ Imports SCrawler.Plugin.Hosts Imports Download = SCrawler.Plugin.ISiteSettings.Download Namespace DownloadObjects Friend Class TDownloader : Implements IDisposable +#Region "Events" Friend Event OnJobsChange(ByVal JobsCount As Integer) Friend Event OnDownloadCountChange() Friend Event OnDownloading(ByVal Value As Boolean) - Friend Event SendNotification(ByVal Message As String) + Friend Event SendNotification As NotificationEventHandler Friend Event OnReconfigured() +#End Region +#Region "Declarations" Friend ReadOnly Property Downloaded As List(Of IUserData) Private ReadOnly NProv As IFormatProvider +#End Region +#Region "Working, Count" Friend ReadOnly Property Working As Boolean Get Return Pool.Count > 0 AndAlso Pool.Exists(Function(j) j.Working) End Get End Property + Friend ReadOnly Property Count As Integer + Get + If Pool.Count = 0 Then Return 0 Else Return Pool.Sum(Function(j) j.Count) + End Get + End Property +#End Region +#Region "Automation Support" + Friend Property DisableOpenForms As Boolean = False + Private _DisableCompleteNotification As Boolean = False + Private _AutoDownloaderWorking As Boolean = False + Friend WriteOnly Property AutoDownloaderWorking As Boolean + Set(ByVal adw As Boolean) + _AutoDownloaderWorking = adw + DisableOpenForms = adw + _DisableCompleteNotification = adw + End Set + End Property + Friend Sub InvokeDownloadsChangeEvent() + RaiseEvent OnDownloadCountChange() + End Sub +#End Region #Region "Jobs" Friend Class Job : Implements IDisposable Friend Event OnItemsCountChange(ByVal Sender As Job, ByVal Count As Integer) @@ -105,11 +131,11 @@ Namespace DownloadObjects Dim i% = Keys.IndexOf(DirectCast(User, UserDataBase).User.Plugin) If i >= 0 Then Return Hosts(i) Else Throw New KeyNotFoundException($"Plugin key [{DirectCast(User, UserDataBase).User.Plugin}] not found") End Function - Friend Function Available() As Boolean + Friend Function Available(ByVal Silent As Boolean) As Boolean If Hosts.Count > 0 Then Dim k$ For i% = Hosts.Count - 1 To 0 Step -1 - If Not Hosts(i).Available(Type) Then + If Not Hosts(i).Available(Type, Silent) Then k = Hosts(i).Key If Not RemovingKeys.Contains(k) Then RemovingKeys.Add(k) Hosts(i).DownloadDone(Type) @@ -182,11 +208,14 @@ Namespace DownloadObjects End Class Friend ReadOnly Pool As List(Of Job) #End Region +#Region "Initializer" Friend Sub New() Downloaded = New List(Of IUserData) NProv = New ANumbers With {.FormatOptions = ANumbers.Options.GroupIntegral} Pool = New List(Of Job) End Sub +#End Region +#Region "Pool" Friend Sub ReconfPool() If Pool.Count = 0 OrElse Not Pool.Exists(Function(j) j.Working Or j.Count > 0) Then Pool.ListClearDispose @@ -204,12 +233,14 @@ Namespace DownloadObjects RaiseEvent OnReconfigured() End If End Sub +#End Region +#Region "Thread" Private CheckerThread As Thread Private Sub [Start]() - If MyProgressForm.ReadyToOpen AndAlso Pool.LongCount(Function(p) p.Count > 0) > 1 Then MyProgressForm.Show() : MainFrameObj.Focus() + If Not DisableOpenForms AndAlso MyProgressForm.ReadyToOpen AndAlso Pool.LongCount(Function(p) p.Count > 0) > 1 Then MyProgressForm.Show() : MainFrameObj.Focus() If Not If(CheckerThread?.IsAlive, False) Then MainProgress.Enabled = True - If InfoForm.ReadyToOpen Then InfoForm.Show() : MainFrameObj.Focus() + If Not DisableOpenForms AndAlso InfoForm.ReadyToOpen Then InfoForm.Show() : MainFrameObj.Focus() CheckerThread = New Thread(New ThreadStart(AddressOf JobsChecker)) CheckerThread.SetApartmentState(ApartmentState.MTA) CheckerThread.Start() @@ -245,7 +276,7 @@ Namespace DownloadObjects Dim n$ = _Job.Name Dim pt As Func(Of String, String) = Function(ByVal t As String) As String Dim _t$ = If(isSeparated, $"{n} {Left(t, 1).ToLower}{Right(t, t.Length - 1)}", t) - RaiseEvent SendNotification(_t) + If Not _DisableCompleteNotification Then RaiseEvent SendNotification(_t) Return _t End Function Try @@ -256,7 +287,7 @@ Namespace DownloadObjects Dim SiteChecked As Boolean = False Do While _Job.Count > 0 _Job.ThrowIfCancellationRequested() - If Not SiteChecked Then _Job.Available() : SiteChecked = True : Continue Do + If Not SiteChecked Then _Job.Available(_AutoDownloaderWorking) : SiteChecked = True : Continue Do UpdateJobsLabel() DownloadData(_Job, _Job.Token) _Job.ThrowIfCancellationRequested() @@ -282,7 +313,7 @@ Namespace DownloadObjects End If End Sub Private Sub UpdateJobsLabel() - RaiseEvent OnJobsChange(Pool.Sum(Function(j) j.Count)) + RaiseEvent OnJobsChange(Count) End Sub Private Sub DownloadData(ByRef _Job As Job, ByVal Token As CancellationToken) Try @@ -351,7 +382,7 @@ Namespace DownloadObjects End Sub)) End Try End Sub - Private Function GetUserFromMainCollection(ByVal User As IUserData) As IUserData + Friend Shared Function GetUserFromMainCollection(ByVal User As IUserData) As IUserData Dim uSimple As Predicate(Of IUserData) = Function(u) u.Equals(DirectCast(User, UserDataBase)) Dim uCol As Predicate(Of IUserData) = Function(ByVal u As IUserData) As Boolean If u.IsCollection Then @@ -375,6 +406,8 @@ Namespace DownloadObjects End If Return Nothing End Function +#End Region +#Region "Add" Private Sub AddItem(ByVal Item As IUserData, ByVal _UpdateJobsLabel As Boolean) ReconfPool() If Item.IsCollection Then @@ -401,6 +434,8 @@ Namespace DownloadObjects End If Start() End Sub +#End Region +#Region "Contains, Remove" Private Function Contains(ByVal _Item As IUserData) If Pool.Count > 0 Then For Each j As Job In Pool @@ -412,6 +447,7 @@ Namespace DownloadObjects Friend Sub UserRemove(ByVal _Item As IUserData) If Downloaded.Count > 0 AndAlso Downloaded.Contains(_Item) Then Downloaded.Remove(_Item) : RaiseEvent OnDownloadCountChange() End Sub +#End Region #Region "IDisposable Support" Private disposedValue As Boolean = False Protected Overridable Overloads Sub Dispose(ByVal disposing As Boolean) diff --git a/SCrawler/Editors/GlobalSettingsForm.Designer.vb b/SCrawler/Editors/GlobalSettingsForm.Designer.vb index 24f9080..db59fad 100644 --- a/SCrawler/Editors/GlobalSettingsForm.Designer.vb +++ b/SCrawler/Editors/GlobalSettingsForm.Designer.vb @@ -37,12 +37,12 @@ Dim TAB_BEHAVIOR As System.Windows.Forms.TabPage Dim TP_BEHAVIOR As System.Windows.Forms.TableLayoutPanel Dim ActionButton7 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() + Dim TP_OPEN_INFO As System.Windows.Forms.TableLayoutPanel + Dim TP_OPEN_PROGRESS As System.Windows.Forms.TableLayoutPanel Dim TAB_DOWN As System.Windows.Forms.TabPage Dim TP_DOWNLOADING As System.Windows.Forms.TableLayoutPanel Dim ActionButton8 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() Dim ActionButton9 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() - Dim TP_OPEN_INFO As System.Windows.Forms.TableLayoutPanel - Dim TP_OPEN_PROGRESS As System.Windows.Forms.TableLayoutPanel Me.TXT_GLOBAL_PATH = New PersonalUtilities.Forms.Controls.TextBoxExtended() Me.TXT_IMAGE_LARGE = New PersonalUtilities.Forms.Controls.TextBoxExtended() Me.TXT_IMAGE_SMALL = New PersonalUtilities.Forms.Controls.TextBoxExtended() @@ -69,6 +69,8 @@ Me.CH_CHANNELS_USERS_TEMP = New System.Windows.Forms.CheckBox() Me.CH_COPY_CHANNEL_USER_IMAGE_ALL = New System.Windows.Forms.CheckBox() Me.CH_UDESCR_UP = New System.Windows.Forms.CheckBox() + Me.CH_DOWN_OPEN_INFO_SUSPEND = New System.Windows.Forms.CheckBox() + Me.CH_DOWN_OPEN_PROGRESS_SUSPEND = New System.Windows.Forms.CheckBox() Me.TXT_CHANNELS_ROWS = New PersonalUtilities.Forms.Controls.TextBoxExtended() Me.TXT_CHANNELS_COLUMNS = New PersonalUtilities.Forms.Controls.TextBoxExtended() Me.TXT_CHANNEL_USER_POST_LIMIT = New PersonalUtilities.Forms.Controls.TextBoxExtended() @@ -76,16 +78,16 @@ Me.CH_EXIT_CONFIRM = New System.Windows.Forms.CheckBox() Me.CH_CLOSE_TO_TRAY = New System.Windows.Forms.CheckBox() Me.CH_SHOW_NOTIFY = New System.Windows.Forms.CheckBox() - Me.CH_RECYCLE_DEL = New System.Windows.Forms.CheckBox() - Me.CH_DOWN_OPEN_INFO = New System.Windows.Forms.CheckBox() - Me.CH_DOWN_OPEN_PROGRESS = New System.Windows.Forms.CheckBox() Me.TXT_CLOSE_SCRIPT = New PersonalUtilities.Forms.Controls.TextBoxExtended() + Me.CH_DOWN_OPEN_INFO = New System.Windows.Forms.CheckBox() + Me.CH_RECYCLE_DEL = New System.Windows.Forms.CheckBox() + Me.CH_DOWN_OPEN_PROGRESS = New System.Windows.Forms.CheckBox() Me.TXT_SCRIPT = New PersonalUtilities.Forms.Controls.TextBoxExtended() Me.TXT_DOWN_COMPLETE_SCRIPT = New PersonalUtilities.Forms.Controls.TextBoxExtended() Me.TAB_MAIN = New System.Windows.Forms.TabControl() + Me.TAB_AUTO = New System.Windows.Forms.TabPage() + Me.PANEL_AUTO = New System.Windows.Forms.Panel() Me.CONTAINER_MAIN = New System.Windows.Forms.ToolStripContainer() - Me.CH_DOWN_OPEN_INFO_SUSPEND = New System.Windows.Forms.CheckBox() - Me.CH_DOWN_OPEN_PROGRESS_SUSPEND = New System.Windows.Forms.CheckBox() TP_BASIS = New System.Windows.Forms.TableLayoutPanel() TP_IMAGES = New System.Windows.Forms.TableLayoutPanel() TP_FILE_NAME = New System.Windows.Forms.TableLayoutPanel() @@ -100,10 +102,10 @@ TP_CHANNELS = New System.Windows.Forms.TableLayoutPanel() TAB_BEHAVIOR = New System.Windows.Forms.TabPage() TP_BEHAVIOR = New System.Windows.Forms.TableLayoutPanel() - TAB_DOWN = New System.Windows.Forms.TabPage() - TP_DOWNLOADING = New System.Windows.Forms.TableLayoutPanel() TP_OPEN_INFO = New System.Windows.Forms.TableLayoutPanel() TP_OPEN_PROGRESS = New System.Windows.Forms.TableLayoutPanel() + TAB_DOWN = New System.Windows.Forms.TabPage() + TP_DOWNLOADING = New System.Windows.Forms.TableLayoutPanel() TP_BASIS.SuspendLayout() CType(Me.TXT_GLOBAL_PATH, System.ComponentModel.ISupportInitialize).BeginInit() TP_IMAGES.SuspendLayout() @@ -128,15 +130,16 @@ TP_BEHAVIOR.SuspendLayout() CType(Me.TXT_FOLDER_CMD, System.ComponentModel.ISupportInitialize).BeginInit() CType(Me.TXT_CLOSE_SCRIPT, System.ComponentModel.ISupportInitialize).BeginInit() + TP_OPEN_INFO.SuspendLayout() + TP_OPEN_PROGRESS.SuspendLayout() TAB_DOWN.SuspendLayout() TP_DOWNLOADING.SuspendLayout() CType(Me.TXT_SCRIPT, System.ComponentModel.ISupportInitialize).BeginInit() CType(Me.TXT_DOWN_COMPLETE_SCRIPT, System.ComponentModel.ISupportInitialize).BeginInit() Me.TAB_MAIN.SuspendLayout() + Me.TAB_AUTO.SuspendLayout() Me.CONTAINER_MAIN.ContentPanel.SuspendLayout() Me.CONTAINER_MAIN.SuspendLayout() - TP_OPEN_INFO.SuspendLayout() - TP_OPEN_PROGRESS.SuspendLayout() Me.SuspendLayout() ' 'TP_BASIS @@ -167,7 +170,7 @@ TP_BASIS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) TP_BASIS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) TP_BASIS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) - TP_BASIS.Size = New System.Drawing.Size(570, 253) + TP_BASIS.Size = New System.Drawing.Size(570, 278) TP_BASIS.TabIndex = 0 ' 'TXT_GLOBAL_PATH @@ -586,6 +589,30 @@ " will be added via a new line") Me.CH_UDESCR_UP.UseVisualStyleBackColor = True ' + 'CH_DOWN_OPEN_INFO_SUSPEND + ' + Me.CH_DOWN_OPEN_INFO_SUSPEND.AutoSize = True + Me.CH_DOWN_OPEN_INFO_SUSPEND.Dock = System.Windows.Forms.DockStyle.Fill + Me.CH_DOWN_OPEN_INFO_SUSPEND.Location = New System.Drawing.Point(290, 4) + Me.CH_DOWN_OPEN_INFO_SUSPEND.Name = "CH_DOWN_OPEN_INFO_SUSPEND" + Me.CH_DOWN_OPEN_INFO_SUSPEND.Size = New System.Drawing.Size(280, 17) + Me.CH_DOWN_OPEN_INFO_SUSPEND.TabIndex = 1 + Me.CH_DOWN_OPEN_INFO_SUSPEND.Text = "Don't open again" + TT_MAIN.SetToolTip(Me.CH_DOWN_OPEN_INFO_SUSPEND, "Do not open the form automatically if it was once closed") + Me.CH_DOWN_OPEN_INFO_SUSPEND.UseVisualStyleBackColor = True + ' + 'CH_DOWN_OPEN_PROGRESS_SUSPEND + ' + Me.CH_DOWN_OPEN_PROGRESS_SUSPEND.AutoSize = True + Me.CH_DOWN_OPEN_PROGRESS_SUSPEND.Dock = System.Windows.Forms.DockStyle.Fill + Me.CH_DOWN_OPEN_PROGRESS_SUSPEND.Location = New System.Drawing.Point(290, 4) + Me.CH_DOWN_OPEN_PROGRESS_SUSPEND.Name = "CH_DOWN_OPEN_PROGRESS_SUSPEND" + Me.CH_DOWN_OPEN_PROGRESS_SUSPEND.Size = New System.Drawing.Size(280, 17) + Me.CH_DOWN_OPEN_PROGRESS_SUSPEND.TabIndex = 1 + Me.CH_DOWN_OPEN_PROGRESS_SUSPEND.Text = "Don't open again" + TT_MAIN.SetToolTip(Me.CH_DOWN_OPEN_PROGRESS_SUSPEND, "Do not open the form automatically if it was once closed") + Me.CH_DOWN_OPEN_PROGRESS_SUSPEND.UseVisualStyleBackColor = True + ' 'TP_CHANNELS_IMGS ' TP_CHANNELS_IMGS.ColumnCount = 2 @@ -637,7 +664,7 @@ TAB_BASIS.Location = New System.Drawing.Point(4, 22) TAB_BASIS.Name = "TAB_BASIS" TAB_BASIS.Padding = New System.Windows.Forms.Padding(3) - TAB_BASIS.Size = New System.Drawing.Size(576, 259) + TAB_BASIS.Size = New System.Drawing.Size(576, 284) TAB_BASIS.TabIndex = 0 TAB_BASIS.Text = "Basis" ' @@ -822,39 +849,6 @@ Me.CH_SHOW_NOTIFY.Text = "Show notifications" Me.CH_SHOW_NOTIFY.UseVisualStyleBackColor = True ' - 'CH_RECYCLE_DEL - ' - Me.CH_RECYCLE_DEL.AutoSize = True - Me.CH_RECYCLE_DEL.Dock = System.Windows.Forms.DockStyle.Fill - Me.CH_RECYCLE_DEL.Location = New System.Drawing.Point(4, 108) - Me.CH_RECYCLE_DEL.Name = "CH_RECYCLE_DEL" - Me.CH_RECYCLE_DEL.Size = New System.Drawing.Size(568, 19) - Me.CH_RECYCLE_DEL.TabIndex = 4 - Me.CH_RECYCLE_DEL.Text = "Delete data to recycle bin" - Me.CH_RECYCLE_DEL.UseVisualStyleBackColor = True - ' - 'CH_DOWN_OPEN_INFO - ' - Me.CH_DOWN_OPEN_INFO.AutoSize = True - Me.CH_DOWN_OPEN_INFO.Dock = System.Windows.Forms.DockStyle.Fill - Me.CH_DOWN_OPEN_INFO.Location = New System.Drawing.Point(4, 4) - Me.CH_DOWN_OPEN_INFO.Name = "CH_DOWN_OPEN_INFO" - Me.CH_DOWN_OPEN_INFO.Size = New System.Drawing.Size(279, 17) - Me.CH_DOWN_OPEN_INFO.TabIndex = 0 - Me.CH_DOWN_OPEN_INFO.Text = "Open the 'Info' form when the download starts" - Me.CH_DOWN_OPEN_INFO.UseVisualStyleBackColor = True - ' - 'CH_DOWN_OPEN_PROGRESS - ' - Me.CH_DOWN_OPEN_PROGRESS.AutoSize = True - Me.CH_DOWN_OPEN_PROGRESS.Dock = System.Windows.Forms.DockStyle.Fill - Me.CH_DOWN_OPEN_PROGRESS.Location = New System.Drawing.Point(4, 4) - Me.CH_DOWN_OPEN_PROGRESS.Name = "CH_DOWN_OPEN_PROGRESS" - Me.CH_DOWN_OPEN_PROGRESS.Size = New System.Drawing.Size(279, 17) - Me.CH_DOWN_OPEN_PROGRESS.TabIndex = 0 - Me.CH_DOWN_OPEN_PROGRESS.Text = "Open the 'Progress' form when the download starts" - Me.CH_DOWN_OPEN_PROGRESS.UseVisualStyleBackColor = True - ' 'TXT_CLOSE_SCRIPT ' Me.TXT_CLOSE_SCRIPT.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.CheckBox @@ -869,6 +863,75 @@ Me.TXT_CLOSE_SCRIPT.Size = New System.Drawing.Size(568, 22) Me.TXT_CLOSE_SCRIPT.TabIndex = 8 ' + 'TP_OPEN_INFO + ' + TP_OPEN_INFO.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.[Single] + TP_OPEN_INFO.ColumnCount = 2 + TP_OPEN_INFO.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50.0!)) + TP_OPEN_INFO.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50.0!)) + TP_OPEN_INFO.Controls.Add(Me.CH_DOWN_OPEN_INFO, 0, 0) + TP_OPEN_INFO.Controls.Add(Me.CH_DOWN_OPEN_INFO_SUSPEND, 1, 0) + TP_OPEN_INFO.Dock = System.Windows.Forms.DockStyle.Fill + TP_OPEN_INFO.Location = New System.Drawing.Point(1, 131) + TP_OPEN_INFO.Margin = New System.Windows.Forms.Padding(0) + TP_OPEN_INFO.Name = "TP_OPEN_INFO" + TP_OPEN_INFO.RowCount = 1 + TP_OPEN_INFO.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) + TP_OPEN_INFO.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 24.0!)) + TP_OPEN_INFO.Size = New System.Drawing.Size(574, 25) + TP_OPEN_INFO.TabIndex = 5 + ' + 'CH_DOWN_OPEN_INFO + ' + Me.CH_DOWN_OPEN_INFO.AutoSize = True + Me.CH_DOWN_OPEN_INFO.Dock = System.Windows.Forms.DockStyle.Fill + Me.CH_DOWN_OPEN_INFO.Location = New System.Drawing.Point(4, 4) + Me.CH_DOWN_OPEN_INFO.Name = "CH_DOWN_OPEN_INFO" + Me.CH_DOWN_OPEN_INFO.Size = New System.Drawing.Size(279, 17) + Me.CH_DOWN_OPEN_INFO.TabIndex = 0 + Me.CH_DOWN_OPEN_INFO.Text = "Open the 'Info' form when the download starts" + Me.CH_DOWN_OPEN_INFO.UseVisualStyleBackColor = True + ' + 'CH_RECYCLE_DEL + ' + Me.CH_RECYCLE_DEL.AutoSize = True + Me.CH_RECYCLE_DEL.Dock = System.Windows.Forms.DockStyle.Fill + Me.CH_RECYCLE_DEL.Location = New System.Drawing.Point(4, 108) + Me.CH_RECYCLE_DEL.Name = "CH_RECYCLE_DEL" + Me.CH_RECYCLE_DEL.Size = New System.Drawing.Size(568, 19) + Me.CH_RECYCLE_DEL.TabIndex = 4 + Me.CH_RECYCLE_DEL.Text = "Delete data to recycle bin" + Me.CH_RECYCLE_DEL.UseVisualStyleBackColor = True + ' + 'TP_OPEN_PROGRESS + ' + TP_OPEN_PROGRESS.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.[Single] + TP_OPEN_PROGRESS.ColumnCount = 2 + TP_OPEN_PROGRESS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50.0!)) + TP_OPEN_PROGRESS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50.0!)) + TP_OPEN_PROGRESS.Controls.Add(Me.CH_DOWN_OPEN_PROGRESS, 0, 0) + TP_OPEN_PROGRESS.Controls.Add(Me.CH_DOWN_OPEN_PROGRESS_SUSPEND, 1, 0) + TP_OPEN_PROGRESS.Dock = System.Windows.Forms.DockStyle.Fill + TP_OPEN_PROGRESS.Location = New System.Drawing.Point(1, 157) + TP_OPEN_PROGRESS.Margin = New System.Windows.Forms.Padding(0) + TP_OPEN_PROGRESS.Name = "TP_OPEN_PROGRESS" + TP_OPEN_PROGRESS.RowCount = 1 + TP_OPEN_PROGRESS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) + TP_OPEN_PROGRESS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 24.0!)) + TP_OPEN_PROGRESS.Size = New System.Drawing.Size(574, 25) + TP_OPEN_PROGRESS.TabIndex = 6 + ' + 'CH_DOWN_OPEN_PROGRESS + ' + Me.CH_DOWN_OPEN_PROGRESS.AutoSize = True + Me.CH_DOWN_OPEN_PROGRESS.Dock = System.Windows.Forms.DockStyle.Fill + Me.CH_DOWN_OPEN_PROGRESS.Location = New System.Drawing.Point(4, 4) + Me.CH_DOWN_OPEN_PROGRESS.Name = "CH_DOWN_OPEN_PROGRESS" + Me.CH_DOWN_OPEN_PROGRESS.Size = New System.Drawing.Size(279, 17) + Me.CH_DOWN_OPEN_PROGRESS.TabIndex = 0 + Me.CH_DOWN_OPEN_PROGRESS.Text = "Open the 'Progress' form when the download starts" + Me.CH_DOWN_OPEN_PROGRESS.UseVisualStyleBackColor = True + ' 'TAB_DOWN ' TAB_DOWN.Controls.Add(TP_DOWNLOADING) @@ -946,6 +1009,7 @@ Me.TAB_MAIN.Controls.Add(TAB_BEHAVIOR) Me.TAB_MAIN.Controls.Add(TAB_DEFAULTS) Me.TAB_MAIN.Controls.Add(TAB_DOWN) + Me.TAB_MAIN.Controls.Add(Me.TAB_AUTO) Me.TAB_MAIN.Controls.Add(TAB_DEFS_CHANNELS) Me.TAB_MAIN.Dock = System.Windows.Forms.DockStyle.Fill Me.TAB_MAIN.Location = New System.Drawing.Point(0, 0) @@ -954,6 +1018,24 @@ Me.TAB_MAIN.Size = New System.Drawing.Size(584, 310) Me.TAB_MAIN.TabIndex = 1 ' + 'TAB_AUTO + ' + Me.TAB_AUTO.Controls.Add(Me.PANEL_AUTO) + Me.TAB_AUTO.Location = New System.Drawing.Point(4, 22) + Me.TAB_AUTO.Name = "TAB_AUTO" + Me.TAB_AUTO.Size = New System.Drawing.Size(576, 284) + Me.TAB_AUTO.TabIndex = 7 + Me.TAB_AUTO.Text = "Automation" + ' + 'PANEL_AUTO + ' + Me.PANEL_AUTO.AutoSize = True + Me.PANEL_AUTO.Dock = System.Windows.Forms.DockStyle.Fill + Me.PANEL_AUTO.Location = New System.Drawing.Point(0, 0) + Me.PANEL_AUTO.Name = "PANEL_AUTO" + Me.PANEL_AUTO.Size = New System.Drawing.Size(576, 284) + Me.PANEL_AUTO.TabIndex = 0 + ' 'CONTAINER_MAIN ' ' @@ -970,66 +1052,6 @@ Me.CONTAINER_MAIN.TabIndex = 0 Me.CONTAINER_MAIN.TopToolStripPanelVisible = False ' - 'TP_OPEN_INFO - ' - TP_OPEN_INFO.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.[Single] - TP_OPEN_INFO.ColumnCount = 2 - TP_OPEN_INFO.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50.0!)) - TP_OPEN_INFO.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50.0!)) - TP_OPEN_INFO.Controls.Add(Me.CH_DOWN_OPEN_INFO, 0, 0) - TP_OPEN_INFO.Controls.Add(Me.CH_DOWN_OPEN_INFO_SUSPEND, 1, 0) - TP_OPEN_INFO.Dock = System.Windows.Forms.DockStyle.Fill - TP_OPEN_INFO.Location = New System.Drawing.Point(1, 131) - TP_OPEN_INFO.Margin = New System.Windows.Forms.Padding(0) - TP_OPEN_INFO.Name = "TP_OPEN_INFO" - TP_OPEN_INFO.RowCount = 1 - TP_OPEN_INFO.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) - TP_OPEN_INFO.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20.0!)) - TP_OPEN_INFO.Size = New System.Drawing.Size(574, 25) - TP_OPEN_INFO.TabIndex = 5 - ' - 'TP_OPEN_PROGRESS - ' - TP_OPEN_PROGRESS.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.[Single] - TP_OPEN_PROGRESS.ColumnCount = 2 - TP_OPEN_PROGRESS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50.0!)) - TP_OPEN_PROGRESS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50.0!)) - TP_OPEN_PROGRESS.Controls.Add(Me.CH_DOWN_OPEN_PROGRESS, 0, 0) - TP_OPEN_PROGRESS.Controls.Add(Me.CH_DOWN_OPEN_PROGRESS_SUSPEND, 1, 0) - TP_OPEN_PROGRESS.Dock = System.Windows.Forms.DockStyle.Fill - TP_OPEN_PROGRESS.Location = New System.Drawing.Point(1, 157) - TP_OPEN_PROGRESS.Margin = New System.Windows.Forms.Padding(0) - TP_OPEN_PROGRESS.Name = "TP_OPEN_PROGRESS" - TP_OPEN_PROGRESS.RowCount = 1 - TP_OPEN_PROGRESS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) - TP_OPEN_PROGRESS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20.0!)) - TP_OPEN_PROGRESS.Size = New System.Drawing.Size(574, 25) - TP_OPEN_PROGRESS.TabIndex = 6 - ' - 'CH_DOWN_OPEN_INFO_SUSPEND - ' - Me.CH_DOWN_OPEN_INFO_SUSPEND.AutoSize = True - Me.CH_DOWN_OPEN_INFO_SUSPEND.Dock = System.Windows.Forms.DockStyle.Fill - Me.CH_DOWN_OPEN_INFO_SUSPEND.Location = New System.Drawing.Point(290, 4) - Me.CH_DOWN_OPEN_INFO_SUSPEND.Name = "CH_DOWN_OPEN_INFO_SUSPEND" - Me.CH_DOWN_OPEN_INFO_SUSPEND.Size = New System.Drawing.Size(280, 17) - Me.CH_DOWN_OPEN_INFO_SUSPEND.TabIndex = 1 - Me.CH_DOWN_OPEN_INFO_SUSPEND.Text = "Don't open again" - TT_MAIN.SetToolTip(Me.CH_DOWN_OPEN_INFO_SUSPEND, "Do not open the form automatically if it was once closed") - Me.CH_DOWN_OPEN_INFO_SUSPEND.UseVisualStyleBackColor = True - ' - 'CH_DOWN_OPEN_PROGRESS_SUSPEND - ' - Me.CH_DOWN_OPEN_PROGRESS_SUSPEND.AutoSize = True - Me.CH_DOWN_OPEN_PROGRESS_SUSPEND.Dock = System.Windows.Forms.DockStyle.Fill - Me.CH_DOWN_OPEN_PROGRESS_SUSPEND.Location = New System.Drawing.Point(290, 4) - Me.CH_DOWN_OPEN_PROGRESS_SUSPEND.Name = "CH_DOWN_OPEN_PROGRESS_SUSPEND" - Me.CH_DOWN_OPEN_PROGRESS_SUSPEND.Size = New System.Drawing.Size(280, 17) - Me.CH_DOWN_OPEN_PROGRESS_SUSPEND.TabIndex = 1 - Me.CH_DOWN_OPEN_PROGRESS_SUSPEND.Text = "Don't open again" - TT_MAIN.SetToolTip(Me.CH_DOWN_OPEN_PROGRESS_SUSPEND, "Do not open the form automatically if it was once closed") - Me.CH_DOWN_OPEN_PROGRESS_SUSPEND.UseVisualStyleBackColor = True - ' 'GlobalSettingsForm ' Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!) @@ -1077,19 +1099,21 @@ TP_BEHAVIOR.PerformLayout() CType(Me.TXT_FOLDER_CMD, System.ComponentModel.ISupportInitialize).EndInit() CType(Me.TXT_CLOSE_SCRIPT, System.ComponentModel.ISupportInitialize).EndInit() + TP_OPEN_INFO.ResumeLayout(False) + TP_OPEN_INFO.PerformLayout() + TP_OPEN_PROGRESS.ResumeLayout(False) + TP_OPEN_PROGRESS.PerformLayout() TAB_DOWN.ResumeLayout(False) TP_DOWNLOADING.ResumeLayout(False) TP_DOWNLOADING.PerformLayout() CType(Me.TXT_SCRIPT, System.ComponentModel.ISupportInitialize).EndInit() CType(Me.TXT_DOWN_COMPLETE_SCRIPT, System.ComponentModel.ISupportInitialize).EndInit() Me.TAB_MAIN.ResumeLayout(False) + Me.TAB_AUTO.ResumeLayout(False) + Me.TAB_AUTO.PerformLayout() Me.CONTAINER_MAIN.ContentPanel.ResumeLayout(False) Me.CONTAINER_MAIN.ResumeLayout(False) Me.CONTAINER_MAIN.PerformLayout() - TP_OPEN_INFO.ResumeLayout(False) - TP_OPEN_INFO.PerformLayout() - TP_OPEN_PROGRESS.ResumeLayout(False) - TP_OPEN_PROGRESS.PerformLayout() Me.ResumeLayout(False) End Sub @@ -1137,5 +1161,7 @@ Private WithEvents TXT_DOWN_COMPLETE_SCRIPT As PersonalUtilities.Forms.Controls.TextBoxExtended Private WithEvents CH_DOWN_OPEN_INFO_SUSPEND As CheckBox Private WithEvents CH_DOWN_OPEN_PROGRESS_SUSPEND As CheckBox + Private WithEvents TAB_AUTO As TabPage + Private WithEvents PANEL_AUTO As Panel End Class End Namespace \ No newline at end of file diff --git a/SCrawler/Editors/GlobalSettingsForm.vb b/SCrawler/Editors/GlobalSettingsForm.vb index b2b2727..8c5c159 100644 --- a/SCrawler/Editors/GlobalSettingsForm.vb +++ b/SCrawler/Editors/GlobalSettingsForm.vb @@ -12,9 +12,18 @@ Imports PersonalUtilities.Forms.Toolbars Namespace Editors Friend Class GlobalSettingsForm : Implements IOkCancelToolbar Private ReadOnly MyDefs As DefaultFormProps + Private ReadOnly Automation As DownloadObjects.AutoDownloaderEditorForm Friend Sub New() InitializeComponent() MyDefs = New DefaultFormProps + Automation = New DownloadObjects.AutoDownloaderEditorForm With { + .MaximumSize = New Size(0, 0), + .MinimumSize = New Size(0, 0), + .Dock = DockStyle.Fill, + .FormBorderStyle = FormBorderStyle.None, + .TopLevel = False, + .IsControlForm = True + } End Sub Private Sub GlobalSettingsForm_Load(sender As Object, e As EventArgs) Handles Me.Load Try @@ -77,10 +86,14 @@ Namespace Editors CH_COPY_CHANNEL_USER_IMAGE_ALL.Enabled = CH_COPY_CHANNEL_USER_IMAGE.Checked CH_CHANNELS_USERS_TEMP.Checked = .ChannelsDefaultTemporary End With + PANEL_AUTO.Controls.Add(Automation) + Automation.Show() .MyFieldsChecker = New FieldsChecker With DirectCast(.MyFieldsChecker, FieldsChecker) .AddControl(Of String)(TXT_GLOBAL_PATH, TXT_GLOBAL_PATH.CaptionText) .AddControl(Of String)(TXT_COLLECTIONS_PATH, TXT_COLLECTIONS_PATH.CaptionText) + .AddControl(Of Integer)(Automation.TXT_TIMER, Automation.TXT_TIMER.CaptionText,, + New DownloadObjects.AutoDownloaderEditorForm.AutomationTimerChecker) .EndLoaderOperations() End With .AppendDetectors() @@ -91,6 +104,9 @@ Namespace Editors MyDefs.InvokeLoaderError(ex) End Try End Sub + Private Sub GlobalSettingsForm_Disposed(sender As Object, e As EventArgs) Handles Me.Disposed + Automation.Dispose() + End Sub Private Sub ToolbarBttOK() Implements IOkCancelToolbar.ToolbarBttOK If MyDefs.MyFieldsChecker.AllParamsOK Then With Settings @@ -179,6 +195,8 @@ Namespace Editors .ChannelsAddUserImagesFromAllChannels.Value = CH_COPY_CHANNEL_USER_IMAGE_ALL.Checked .ChannelsDefaultTemporary.Value = CH_CHANNELS_USERS_TEMP.Checked + Automation.SaveSetiings() + .EndUpdate() End With MyDefs.CloseForm() diff --git a/SCrawler/Editors/LabelsForm.vb b/SCrawler/Editors/LabelsForm.vb index a097b76..05bb29d 100644 --- a/SCrawler/Editors/LabelsForm.vb +++ b/SCrawler/Editors/LabelsForm.vb @@ -14,6 +14,16 @@ Imports PersonalUtilities.Functions.Messaging Friend Class LabelsForm : Implements IOkCancelDeleteToolbar Private ReadOnly MyDefs As DefaultFormProps Friend ReadOnly Property LabelsList As List(Of String) + Private ReadOnly _Source As IEnumerable(Of String) = Nothing + Private ReadOnly Property Source As IEnumerable(Of String) + Get + If Not _Source Is Nothing Then + Return _Source + Else + Return Settings.Labels + End If + End Get + End Property Private _AnyLabelAdd As Boolean = False Friend Property MultiUser As Boolean = False Friend Property MultiUserClearExists As Boolean = False @@ -24,19 +34,24 @@ Friend Class LabelsForm : Implements IOkCancelDeleteToolbar LabelsList.ListAddList(LabelsArr) MyDefs = New DefaultFormProps End Sub + Friend Sub New(ByVal Current As IEnumerable(Of String), ByVal Source As IEnumerable(Of String)) + Me.New(Current) + _Source = Source + End Sub Private Sub LabelsForm_Load(sender As Object, e As EventArgs) Handles Me.Load Try With MyDefs .MyViewInitialize(Me, Settings.Design) .AddOkCancelToolbar(, WithDeleteButton) .DelegateClosingChecker() - If Settings.Labels.Count > 0 Then + If Source.Count > 0 Then Dim items As New List(Of Integer) CMB_LABELS.BeginUpdate() - For i% = 0 To Settings.Labels.Count - 1 - If LabelsList.Contains(Settings.Labels(i)) Then items.Add(i) - CMB_LABELS.Items.Add(Settings.Labels(i)) + For i% = 0 To Source.Count - 1 + If LabelsList.Contains(Source(i)) Then items.Add(i) + CMB_LABELS.Items.Add(Source(i)) Next + If Not _Source Is Nothing Then CMB_LABELS.Buttons.Clear() CMB_LABELS.EndUpdate() CMB_LABELS.ListCheckedIndexes = items End If @@ -48,7 +63,7 @@ Friend Class LabelsForm : Implements IOkCancelDeleteToolbar End Try End Sub Private Sub LabelsForm_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown - If e.KeyCode = Keys.Insert Then AddNewLabel() : e.Handled = True + If e.KeyCode = Keys.Insert And _Source Is Nothing Then AddNewLabel() : e.Handled = True End Sub Private Sub LabelsForm_Disposed(sender As Object, e As EventArgs) Handles Me.Disposed LabelsList.Clear() @@ -69,7 +84,7 @@ Friend Class LabelsForm : Implements IOkCancelDeleteToolbar End Select End If LabelsList.ListAddList(CMB_LABELS.Items.CheckedItems.Select(Function(l) CStr(l.Value(0))), LAP.ClearBeforeAdd, LAP.NotContainsOnly) - If _AnyLabelAdd Then Settings.Labels.Update() + If _AnyLabelAdd And _Source Is Nothing Then Settings.Labels.Update() MyDefs.CloseForm() Catch ex As Exception ErrorsDescriber.Execute(EDP.LogMessageValue, ex, "Choosing labels") diff --git a/SCrawler/Editors/SiteEditorForm.Designer.vb b/SCrawler/Editors/SiteEditorForm.Designer.vb index b7ef02b..411dbdb 100644 --- a/SCrawler/Editors/SiteEditorForm.Designer.vb +++ b/SCrawler/Editors/SiteEditorForm.Designer.vb @@ -14,13 +14,14 @@ Private components As System.ComponentModel.IContainer Private Sub InitializeComponent() - Dim ActionButton7 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() + Me.components = New System.ComponentModel.Container() + Dim ActionButton1 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() Dim resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(SiteEditorForm)) - Dim ActionButton8 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() - Dim ActionButton9 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() - Dim ActionButton10 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() - Dim ActionButton11 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() - Dim ActionButton12 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() + Dim ActionButton2 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() + Dim ActionButton3 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() + Dim ActionButton4 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() + Dim ActionButton5 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() + Dim ActionButton6 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() Me.TP_MAIN = New System.Windows.Forms.TableLayoutPanel() Me.TXT_PATH = New PersonalUtilities.Forms.Controls.TextBoxExtended() Me.TXT_COOKIES = New PersonalUtilities.Forms.Controls.TextBoxExtended() @@ -28,6 +29,7 @@ Me.TXT_PATH_SAVED_POSTS = New PersonalUtilities.Forms.Controls.TextBoxExtended() Me.CH_GET_USER_MEDIA_ONLY = New System.Windows.Forms.CheckBox() Me.CONTAINER_MAIN = New System.Windows.Forms.ToolStripContainer() + Me.TT_MAIN = New System.Windows.Forms.ToolTip(Me.components) Me.TP_MAIN.SuspendLayout() CType(Me.TXT_PATH, System.ComponentModel.ISupportInitialize).BeginInit() CType(Me.TXT_COOKIES, System.ComponentModel.ISupportInitialize).BeginInit() @@ -59,14 +61,14 @@ ' 'TXT_PATH ' - ActionButton7.BackgroundImage = CType(resources.GetObject("ActionButton7.BackgroundImage"), System.Drawing.Image) - ActionButton7.Index = 0 - ActionButton7.Name = "BTT_OPEN" - ActionButton8.BackgroundImage = CType(resources.GetObject("ActionButton8.BackgroundImage"), System.Drawing.Image) - ActionButton8.Index = 1 - ActionButton8.Name = "BTT_CLEAR" - Me.TXT_PATH.Buttons.Add(ActionButton7) - Me.TXT_PATH.Buttons.Add(ActionButton8) + ActionButton1.BackgroundImage = CType(resources.GetObject("ActionButton1.BackgroundImage"), System.Drawing.Image) + ActionButton1.Index = 0 + ActionButton1.Name = "BTT_OPEN" + ActionButton2.BackgroundImage = CType(resources.GetObject("ActionButton2.BackgroundImage"), System.Drawing.Image) + ActionButton2.Index = 1 + ActionButton2.Name = "BTT_CLEAR" + Me.TXT_PATH.Buttons.Add(ActionButton1) + Me.TXT_PATH.Buttons.Add(ActionButton2) Me.TXT_PATH.CaptionText = "Path" Me.TXT_PATH.CaptionToolTipEnabled = True Me.TXT_PATH.CaptionToolTipText = "Specific path to store Twitter files" @@ -78,14 +80,14 @@ ' 'TXT_COOKIES ' - ActionButton9.BackgroundImage = CType(resources.GetObject("ActionButton9.BackgroundImage"), System.Drawing.Image) - ActionButton9.Index = 0 - ActionButton9.Name = "BTT_EDIT" - ActionButton10.BackgroundImage = CType(resources.GetObject("ActionButton10.BackgroundImage"), System.Drawing.Image) - ActionButton10.Index = 1 - ActionButton10.Name = "BTT_CLEAR" - Me.TXT_COOKIES.Buttons.Add(ActionButton9) - Me.TXT_COOKIES.Buttons.Add(ActionButton10) + ActionButton3.BackgroundImage = CType(resources.GetObject("ActionButton3.BackgroundImage"), System.Drawing.Image) + ActionButton3.Index = 0 + ActionButton3.Name = "BTT_EDIT" + ActionButton4.BackgroundImage = CType(resources.GetObject("ActionButton4.BackgroundImage"), System.Drawing.Image) + ActionButton4.Index = 1 + ActionButton4.Name = "BTT_CLEAR" + Me.TXT_COOKIES.Buttons.Add(ActionButton3) + Me.TXT_COOKIES.Buttons.Add(ActionButton4) Me.TXT_COOKIES.CaptionText = "Cookies" Me.TXT_COOKIES.ClearTextByButtonClear = False Me.TXT_COOKIES.Dock = System.Windows.Forms.DockStyle.Fill @@ -114,14 +116,14 @@ ' 'TXT_PATH_SAVED_POSTS ' - ActionButton11.BackgroundImage = CType(resources.GetObject("ActionButton11.BackgroundImage"), System.Drawing.Image) - ActionButton11.Index = 0 - ActionButton11.Name = "BTT_OPEN" - ActionButton12.BackgroundImage = CType(resources.GetObject("ActionButton12.BackgroundImage"), System.Drawing.Image) - ActionButton12.Index = 1 - ActionButton12.Name = "BTT_CLEAR" - Me.TXT_PATH_SAVED_POSTS.Buttons.Add(ActionButton11) - Me.TXT_PATH_SAVED_POSTS.Buttons.Add(ActionButton12) + ActionButton5.BackgroundImage = CType(resources.GetObject("ActionButton5.BackgroundImage"), System.Drawing.Image) + ActionButton5.Index = 0 + ActionButton5.Name = "BTT_OPEN" + ActionButton6.BackgroundImage = CType(resources.GetObject("ActionButton6.BackgroundImage"), System.Drawing.Image) + ActionButton6.Index = 1 + ActionButton6.Name = "BTT_CLEAR" + Me.TXT_PATH_SAVED_POSTS.Buttons.Add(ActionButton5) + Me.TXT_PATH_SAVED_POSTS.Buttons.Add(ActionButton6) Me.TXT_PATH_SAVED_POSTS.CaptionText = "Saved posts path" Me.TXT_PATH_SAVED_POSTS.Dock = System.Windows.Forms.DockStyle.Fill Me.TXT_PATH_SAVED_POSTS.Location = New System.Drawing.Point(3, 31) @@ -192,5 +194,6 @@ Private WithEvents TXT_PATH_SAVED_POSTS As PersonalUtilities.Forms.Controls.TextBoxExtended Private WithEvents TP_SITE_PROPS As SiteDefaults Private WithEvents CH_GET_USER_MEDIA_ONLY As CheckBox + Private WithEvents TT_MAIN As ToolTip End Class End Namespace \ No newline at end of file diff --git a/SCrawler/Editors/SiteEditorForm.resx b/SCrawler/Editors/SiteEditorForm.resx index 9b002d8..0f3ab90 100644 --- a/SCrawler/Editors/SiteEditorForm.resx +++ b/SCrawler/Editors/SiteEditorForm.resx @@ -118,7 +118,7 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP @@ -129,7 +129,7 @@ cMaRN0UdBBkAAAAASUVORK5CYII= - + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go @@ -137,7 +137,7 @@ AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC - + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAACH DwAAjA8AAP1SAACBQAAAfXkAAOmLAAA85QAAGcxzPIV3AAAKOWlDQ1BQaG90b3Nob3AgSUNDIHByb2Zp @@ -195,7 +195,7 @@ AAAASUVORK5CYII= - + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go @@ -203,7 +203,7 @@ AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC - + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP @@ -214,7 +214,7 @@ cMaRN0UdBBkAAAAASUVORK5CYII= - + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go @@ -222,4 +222,7 @@ AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC + + 17, 17 + \ No newline at end of file diff --git a/SCrawler/Editors/SiteEditorForm.vb b/SCrawler/Editors/SiteEditorForm.vb index 1e9a16a..5079671 100644 --- a/SCrawler/Editors/SiteEditorForm.vb +++ b/SCrawler/Editors/SiteEditorForm.vb @@ -113,7 +113,7 @@ Namespace Editors End Select End If - .CreateControl() + .CreateControl(TT_MAIN) AddTpControl(.Control, .ControlHeight) If .LeftOffset > offset Then offset = .LeftOffset If Not .Options.AllowNull Or Not .ProviderFieldsChecker Is Nothing Then diff --git a/SCrawler/MainFrame.Designer.vb b/SCrawler/MainFrame.Designer.vb index 5d38f22..abaca85 100644 --- a/SCrawler/MainFrame.Designer.vb +++ b/SCrawler/MainFrame.Designer.vb @@ -30,6 +30,7 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form Dim TRAY_SEP_1 As System.Windows.Forms.ToolStripSeparator Dim MENU_DOWN_ALL_SEP_1 As System.Windows.Forms.ToolStripSeparator Dim MENU_DOWN_ALL_SEP_2 As System.Windows.Forms.ToolStripSeparator + Dim MENU_DOWN_ALL_SEP_3 As System.Windows.Forms.ToolStripSeparator Dim resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(MainFrame)) Me.MENU_SETTINGS = New System.Windows.Forms.ToolStripDropDownButton() Me.BTT_SETTINGS = New System.Windows.Forms.ToolStripMenuItem() @@ -48,6 +49,7 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form Me.BTT_DOWN_ALL_FULL = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_DOWN_SITE_FULL = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_ADD_NEW_GROUP = New System.Windows.Forms.ToolStripMenuItem() + Me.BTT_DOWN_AUTOMATION = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_DOWN_VIDEO = New System.Windows.Forms.ToolStripButton() Me.BTT_DOWN_STOP = New System.Windows.Forms.ToolStripButton() Me.MENU_VIEW = New System.Windows.Forms.ToolStripDropDownButton() @@ -118,6 +120,7 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form TRAY_SEP_1 = New System.Windows.Forms.ToolStripSeparator() MENU_DOWN_ALL_SEP_1 = New System.Windows.Forms.ToolStripSeparator() MENU_DOWN_ALL_SEP_2 = New System.Windows.Forms.ToolStripSeparator() + MENU_DOWN_ALL_SEP_3 = New System.Windows.Forms.ToolStripSeparator() Me.Toolbar_TOP.SuspendLayout() Me.Toolbar_BOTTOM.SuspendLayout() Me.USER_CONTEXT.SuspendLayout() @@ -192,7 +195,7 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form 'TRAY_SEP_1 ' TRAY_SEP_1.Name = "TRAY_SEP_1" - TRAY_SEP_1.Size = New System.Drawing.Size(177, 6) + TRAY_SEP_1.Size = New System.Drawing.Size(157, 6) ' 'MENU_DOWN_ALL_SEP_1 ' @@ -204,6 +207,11 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form MENU_DOWN_ALL_SEP_2.Name = "MENU_DOWN_ALL_SEP_2" MENU_DOWN_ALL_SEP_2.Size = New System.Drawing.Size(228, 6) ' + 'MENU_DOWN_ALL_SEP_3 + ' + MENU_DOWN_ALL_SEP_3.Name = "MENU_DOWN_ALL_SEP_3" + MENU_DOWN_ALL_SEP_3.Size = New System.Drawing.Size(228, 6) + ' 'MENU_SETTINGS ' Me.MENU_SETTINGS.AutoToolTip = False @@ -304,7 +312,7 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form 'MENU_DOWN_ALL ' Me.MENU_DOWN_ALL.AutoToolTip = False - Me.MENU_DOWN_ALL.DropDownItems.AddRange(New System.Windows.Forms.ToolStripItem() {Me.BTT_DOWN_ALL, Me.BTT_DOWN_SITE, MENU_DOWN_ALL_SEP_1, Me.BTT_DOWN_ALL_FULL, Me.BTT_DOWN_SITE_FULL, MENU_DOWN_ALL_SEP_2, Me.BTT_ADD_NEW_GROUP}) + Me.MENU_DOWN_ALL.DropDownItems.AddRange(New System.Windows.Forms.ToolStripItem() {Me.BTT_DOWN_ALL, Me.BTT_DOWN_SITE, MENU_DOWN_ALL_SEP_1, Me.BTT_DOWN_ALL_FULL, Me.BTT_DOWN_SITE_FULL, MENU_DOWN_ALL_SEP_2, Me.BTT_ADD_NEW_GROUP, MENU_DOWN_ALL_SEP_3, Me.BTT_DOWN_AUTOMATION}) Me.MENU_DOWN_ALL.Image = Global.SCrawler.My.Resources.Resources.StartPic_01_Green_16 Me.MENU_DOWN_ALL.ImageTransparentColor = System.Drawing.Color.Magenta Me.MENU_DOWN_ALL.Name = "MENU_DOWN_ALL" @@ -356,6 +364,13 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form Me.BTT_ADD_NEW_GROUP.Size = New System.Drawing.Size(231, 22) Me.BTT_ADD_NEW_GROUP.Text = "Add a new download group" ' + 'BTT_DOWN_AUTOMATION + ' + Me.BTT_DOWN_AUTOMATION.Image = Global.SCrawler.My.Resources.Resources.ScriptPic32 + Me.BTT_DOWN_AUTOMATION.Name = "BTT_DOWN_AUTOMATION" + Me.BTT_DOWN_AUTOMATION.Size = New System.Drawing.Size(231, 22) + Me.BTT_DOWN_AUTOMATION.Text = "Automation" + ' 'BTT_DOWN_VIDEO ' Me.BTT_DOWN_VIDEO.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text @@ -721,12 +736,12 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form ' Me.TRAY_CONTEXT.Items.AddRange(New System.Windows.Forms.ToolStripItem() {Me.BTT_TRAY_SHOW_HIDE, TRAY_SEP_1, Me.BTT_TRAY_CLOSE, Me.BTT_TRAY_CLOSE_NO_SCRIPT}) Me.TRAY_CONTEXT.Name = "TRAY_CONTEXT" - Me.TRAY_CONTEXT.Size = New System.Drawing.Size(181, 98) + Me.TRAY_CONTEXT.Size = New System.Drawing.Size(161, 76) ' 'BTT_TRAY_SHOW_HIDE ' Me.BTT_TRAY_SHOW_HIDE.Name = "BTT_TRAY_SHOW_HIDE" - Me.BTT_TRAY_SHOW_HIDE.Size = New System.Drawing.Size(180, 22) + Me.BTT_TRAY_SHOW_HIDE.Size = New System.Drawing.Size(160, 22) Me.BTT_TRAY_SHOW_HIDE.Text = "Show/Hide" ' 'BTT_TRAY_CLOSE @@ -735,17 +750,19 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form Me.BTT_TRAY_CLOSE.ForeColor = System.Drawing.Color.Maroon Me.BTT_TRAY_CLOSE.Image = CType(resources.GetObject("BTT_TRAY_CLOSE.Image"), System.Drawing.Image) Me.BTT_TRAY_CLOSE.Name = "BTT_TRAY_CLOSE" - Me.BTT_TRAY_CLOSE.Size = New System.Drawing.Size(180, 22) + Me.BTT_TRAY_CLOSE.Size = New System.Drawing.Size(160, 22) Me.BTT_TRAY_CLOSE.Text = "Close" ' 'BTT_TRAY_CLOSE_NO_SCRIPT ' + Me.BTT_TRAY_CLOSE_NO_SCRIPT.AutoToolTip = True Me.BTT_TRAY_CLOSE_NO_SCRIPT.BackColor = System.Drawing.Color.FromArgb(CType(CType(255, Byte), Integer), CType(CType(192, Byte), Integer), CType(CType(192, Byte), Integer)) Me.BTT_TRAY_CLOSE_NO_SCRIPT.ForeColor = System.Drawing.Color.Maroon Me.BTT_TRAY_CLOSE_NO_SCRIPT.Image = Global.SCrawler.My.Resources.Resources.Delete Me.BTT_TRAY_CLOSE_NO_SCRIPT.Name = "BTT_TRAY_CLOSE_NO_SCRIPT" Me.BTT_TRAY_CLOSE_NO_SCRIPT.Size = New System.Drawing.Size(160, 22) Me.BTT_TRAY_CLOSE_NO_SCRIPT.Text = "Close (no script)" + Me.BTT_TRAY_CLOSE_NO_SCRIPT.ToolTipText = "Close the program without executing the script" Me.BTT_TRAY_CLOSE_NO_SCRIPT.Visible = False ' 'MainFrame @@ -819,7 +836,7 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form Private WithEvents BTT_SITE_SPECIFIC As ToolStripMenuItem Private WithEvents BTT_CONTEXT_CHANGE_FOLDER As ToolStripMenuItem Private WithEvents BTT_DOWN_SAVED As ToolStripButton - Private WithEvents TrayIcon As NotifyIcon + Friend WithEvents TrayIcon As NotifyIcon Private WithEvents BTT_TRAY_SHOW_HIDE As ToolStripMenuItem Private WithEvents BTT_TRAY_CLOSE As ToolStripMenuItem Private WithEvents BTT_DONATE As ToolStripButton @@ -842,4 +859,5 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form Private WithEvents BTT_ADD_NEW_GROUP As ToolStripMenuItem Friend WithEvents BTT_TRAY_CLOSE_NO_SCRIPT As ToolStripMenuItem Friend WithEvents TRAY_CONTEXT As ContextMenuStrip + Private WithEvents BTT_DOWN_AUTOMATION As ToolStripMenuItem End Class \ No newline at end of file diff --git a/SCrawler/MainFrame.resx b/SCrawler/MainFrame.resx index 01d77b8..a12484e 100644 --- a/SCrawler/MainFrame.resx +++ b/SCrawler/MainFrame.resx @@ -165,6 +165,9 @@ False + + False + 132, 17 diff --git a/SCrawler/MainFrame.vb b/SCrawler/MainFrame.vb index f98bad4..1544e69 100644 --- a/SCrawler/MainFrame.vb +++ b/SCrawler/MainFrame.vb @@ -15,6 +15,7 @@ Imports SCrawler.API.Base Imports SCrawler.Editors Imports SCrawler.DownloadObjects Imports SCrawler.Plugin.Hosts +Imports PersonalUtilities.Functions.Messaging Public Class MainFrame Private MyView As FormsView Private ReadOnly _VideoDownloadingMode As Boolean = False @@ -56,7 +57,7 @@ Public Class MainFrame AddHandler Downloader.OnDownloading, AddressOf Downloader_OnDownloading AddHandler Downloader.OnDownloadCountChange, AddressOf InfoForm.Downloader_OnDownloadCountChange AddHandler Downloader.SendNotification, AddressOf NotificationMessage - AddHandler InfoForm.OnUserLooking, AddressOf Info_OnUserLooking + AddHandler InfoForm.UserFind, AddressOf FocusUser Settings.LoadUsers() MyView = New FormsView(Me) MyView.ImportFromXML(Settings.Design) @@ -93,7 +94,12 @@ Public Class MainFrame For Each ugroup As Groups.DownloadGroup In Settings.Groups : GROUPS_Added(ugroup) : Next End If End With + Settings.Automation = New AutoDownloader + AddHandler Settings.Groups.Updated, AddressOf Settings.Automation.GROUPS_Updated + AddHandler Settings.Groups.Deleted, AddressOf Settings.Automation.GROUPS_Deleted + AddHandler Settings.Automation.UserFind, AddressOf FocusUser _UFinit = False + Settings.Automation.Start() GoTo EndFunction FormClosingInvoker: Close() @@ -122,6 +128,7 @@ EndFunction: If Downloader.Working Then _CloseInvoked = True : Downloader.Stop() If ChannelsWorking.Invoke Then _CloseInvoked = True : MyChannels.Stop(False) If SP_Working.Invoke Then _CloseInvoked = True : MySavedPosts.Stop() + Settings.Automation.Stop() If _CloseInvoked Then e.Cancel = True Await Task.Run(Sub() @@ -131,6 +138,7 @@ EndFunction: Downloader.Dispose() MyProgressForm.Dispose() InfoForm.Dispose() + MainFrameObj.CLearNotifications() If Not MyChannels Is Nothing Then MyChannels.Dispose() If Not VideoDownloader Is Nothing Then VideoDownloader.Dispose() If Not MySavedPosts Is Nothing Then MySavedPosts.Dispose() @@ -264,6 +272,7 @@ CloseResume: (Not sg = Settings.ShowGroups And .UseGrouping) Then RefillList() TrayIcon.Visible = .CloseToTray LIST_PROFILES.ShowGroups = .UseGrouping + If Not Settings.Automation.Mode = AutoDownloader.Modes.None Then Settings.Automation.Start() End If End Using End With @@ -415,6 +424,12 @@ CloseResume: MENU_DOWN_ALL.DropDownItems.Remove(Sender.GetControl) End Sub #End Region + Private Sub BTT_DOWN_AUTOMATION_Click(sender As Object, e As EventArgs) Handles BTT_DOWN_AUTOMATION.Click + Using f As New AutoDownloaderEditorForm + f.ShowDialog() + If f.DialogResult = DialogResult.OK AndAlso Not Settings.Automation.Mode = AutoDownloader.Modes.None Then Settings.Automation.Start() + End Using + End Sub Private Sub BTT_DOWN_VIDEO_Click(sender As Object, e As EventArgs) Handles BTT_DOWN_VIDEO.Click DownloadVideoByURL() End Sub @@ -1135,27 +1150,30 @@ ResumeDownloadingOperation: If Not user Is Nothing Then user.OpenFolder() End Sub #End Region - Private Sub Info_OnUserLooking(ByVal Key As String) + Private Overloads Sub FocusUser(ByVal Key As String) FocusUser(Key, True) End Sub - Private Sub FocusUser(ByVal Key As String, Optional ByVal ActivateMe As Boolean = False) - Dim i% = LIST_PROFILES.Items.IndexOfKey(Key) - If i < 0 Then - i = Settings.Users.FindIndex(Function(u) u.Key = Key) - If i >= 0 Then - UserListUpdate(Settings.Users(i), True) - i = LIST_PROFILES.Items.IndexOfKey(Key) - End If - End If - If i >= 0 Then - LIST_PROFILES.Select() - LIST_PROFILES.SelectedIndices.Clear() - With LIST_PROFILES.Items(i) : .Selected = True : .Focused = True : End With - LIST_PROFILES.EnsureVisible(i) - If ActivateMe Then - If Visible Then BringToFront() Else Visible = True - End If - End If + Private Overloads Sub FocusUser(ByVal Key As String, Optional ByVal ActivateMe As Boolean = False) + Dim a As Action = Sub() + Dim i% = LIST_PROFILES.Items.IndexOfKey(Key) + If i < 0 Then + i = Settings.Users.FindIndex(Function(u) u.Key = Key) + If i >= 0 Then + UserListUpdate(Settings.Users(i), True) + i = LIST_PROFILES.Items.IndexOfKey(Key) + End If + End If + If i >= 0 Then + LIST_PROFILES.Select() + LIST_PROFILES.SelectedIndices.Clear() + With LIST_PROFILES.Items(i) : .Selected = True : .Focused = True : End With + LIST_PROFILES.EnsureVisible(i) + If ActivateMe Then + If Visible Then BringToFront() Else Visible = True + End If + End If + End Sub + If LIST_PROFILES.InvokeRequired Then LIST_PROFILES.Invoke(a) Else a.Invoke End Sub Friend Sub User_OnUserUpdated(ByVal User As IUserData) UserListUpdate(User, False) @@ -1179,7 +1197,7 @@ ResumeDownloadingOperation: If Toolbar_TOP.InvokeRequired Then Toolbar_TOP.Invoke(a) Else a.Invoke End Sub Private Sub NotificationMessage(ByVal Message As String) - If Settings.ShowNotifications Then TrayIcon.ShowBalloonTip(2000, TrayIcon.BalloonTipTitle, Message, ToolTipIcon.Info) + If Settings.ShowNotifications Then MainFrameObj.ShowNotification(Message) End Sub Private Sub BTT_PR_INFO_Click(sender As Object, e As EventArgs) Handles BTT_PR_INFO.Click If MyProgressForm.Visible Then MyProgressForm.BringToFront() Else MyProgressForm.Show() diff --git a/SCrawler/MainFrameObjects.vb b/SCrawler/MainFrameObjects.vb index 6d216df..73182ed 100644 --- a/SCrawler/MainFrameObjects.vb +++ b/SCrawler/MainFrameObjects.vb @@ -8,10 +8,13 @@ ' but WITHOUT ANY WARRANTY Imports SCrawler.API Imports SCrawler.API.Base +Imports PersonalUtilities.Tools.Notifications Friend Class MainFrameObjects Private ReadOnly Property MF As MainFrame + Private WithEvents Notificator As NotificationsManager Friend Sub New(ByRef f As MainFrame) MF = f + Notificator = New NotificationsManager End Sub Friend Sub ImageHandler(ByVal User As IUserData) ImageHandler(User, False) @@ -34,11 +37,30 @@ Friend Class MainFrameObjects Catch ex As Exception End Try End Sub - Friend Sub Focus() + 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() 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 End Sub + Friend Overloads Sub ShowNotification(ByVal Message As String) + MF.TrayIcon.ShowBalloonTip(2000, MF.TrayIcon.BalloonTipTitle, Message, ToolTipIcon.Info) + End Sub + Friend Overloads Sub ShowNotification(ByVal Message As String, ByVal Title As String) + MF.TrayIcon.ShowBalloonTip(2000, Title, Message, ToolTipIcon.Info) + End Sub + Friend Overloads Sub ShowNotification(ByVal Message As String, ByVal Title As String, ByVal Icon As ToolTipIcon) + MF.TrayIcon.ShowBalloonTip(2000, Title, Message, Icon) + 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 + End Sub End Class \ No newline at end of file diff --git a/SCrawler/MainMod.vb b/SCrawler/MainMod.vb index 0c5f8dc..e7d117d 100644 --- a/SCrawler/MainMod.vb +++ b/SCrawler/MainMod.vb @@ -22,6 +22,7 @@ Friend Module MainMod Friend Const SettingsFolderName As String = "Settings" Friend ReadOnly LinkPattern As RParams = RParams.DMS("[htps:]{0,6}[/]{0,2}(.+)", 1) Friend ReadOnly FilesPattern As RParams = RParams.DM("[^\./]+?\.\w+", 1, EDP.ReturnValue) + Friend Delegate Sub NotificationEventHandler(ByVal Message As String) Friend Const LVI_TempOption As String = "Temp" Friend Const LVI_FavOption As String = "Favorite" Friend Const CannelsLabelName As String = "Channels" diff --git a/SCrawler/My Project/AssemblyInfo.vb b/SCrawler/My Project/AssemblyInfo.vb index 0c86b2e..80df32c 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/PluginsEnvironment/Hosts/SettingsHost.vb b/SCrawler/PluginsEnvironment/Hosts/SettingsHost.vb index 022ad31..597eae9 100644 --- a/SCrawler/PluginsEnvironment/Hosts/SettingsHost.vb +++ b/SCrawler/PluginsEnvironment/Hosts/SettingsHost.vb @@ -310,9 +310,9 @@ Namespace Plugin.Hosts Private _AvailableValue As Boolean = True Private _AvailableAsked As Boolean = False Private _ActiveTaskCount As Integer = 0 - Friend Function Available(ByVal What As Download) As Boolean + Friend Function Available(ByVal What As Download, ByVal Silent As Boolean) As Boolean If Not _AvailableAsked Then - _AvailableValue = Source.Available(What) + _AvailableValue = Source.Available(What, Silent) _AvailableAsked = True End If Return _AvailableValue diff --git a/SCrawler/SCrawler.vbproj b/SCrawler/SCrawler.vbproj index e99b93f..652e263 100644 --- a/SCrawler/SCrawler.vbproj +++ b/SCrawler/SCrawler.vbproj @@ -160,15 +160,26 @@ Form + + + AutoDownloaderEditorForm.vb + + + Form + + + Component + GroupEditorForm.vb Form + @@ -313,6 +324,9 @@ ChannelViewForm.vb + + AutoDownloaderEditorForm.vb + DownloadedInfoForm.vb @@ -374,6 +388,10 @@ + + {fc532253-1ab3-4def-a28a-dfdd9a481eb2} + PersonalUtilities.Notifications + {8405896b-2685-4916-bc93-1fb514c323a9} PersonalUtilities diff --git a/SCrawler/SettingsCLS.vb b/SCrawler/SettingsCLS.vb index 752990d..0a53473 100644 --- a/SCrawler/SettingsCLS.vb +++ b/SCrawler/SettingsCLS.vb @@ -34,6 +34,7 @@ Friend Class SettingsCLS : Implements IDisposable Friend Property Channels As Reddit.ChannelsCollection Friend ReadOnly Property Labels As LabelsKeeper Friend ReadOnly Property Groups As Groups.DownloadGroupCollection + Friend Property Automation As AutoDownloader Friend ReadOnly Property BlackList As List(Of UserBan) Private ReadOnly BlackListFile As SFile = $"{SettingsFolderName}\BlackList.txt" Private ReadOnly UsersSettingsFile As SFile = $"{SettingsFolderName}\Users.xml" @@ -477,6 +478,7 @@ Friend Class SettingsCLS : Implements IDisposable Channels.Dispose() DeleteCachePath() End If + If Not Automation Is Nothing Then Automation.Dispose() Plugins.Clear() Users.ListClearDispose UsersList.Clear()