diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 02a349c..d5ccc3d 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -30,7 +30,9 @@ A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. -**Release information (please complete the following information):** +**Release information:** + +**Please complete the following information or replace the following text with data copied from SCrawler (click the top right info button in the main window, then the `Environment` button, then the `Copy` button, and paste the copied text here).** - OS: [e.g. Windows 10, Windows 11] - Architecture: [e.g. x86, x64] - Version: [e.g. 2023.3.5.0] @@ -38,6 +40,7 @@ If applicable, add screenshots to help explain your problem. - ffmpeg version (command `ffmpeg -version`): - yt-dlp version (command `yt-dlp --version`): - gallery-dl version (command `gallery-dl --version`): + - cURL version (command `curl --version`): **Additional context** Add any other context about the problem here. diff --git a/.gitignore b/.gitignore index 2798902..47ade89 100644 --- a/.gitignore +++ b/.gitignore @@ -10,8 +10,7 @@ *.userosscache *.sln.docstates .obsidian/ -ToDo.txt -ToDo.md +BugReporterFormDiscordWebHook.vb # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs diff --git a/Changelog.md b/Changelog.md index 247c90a..1d79ff8 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,91 @@ +# 2023.8.6.0 + +*2023-08-06* + +- Added + - The ability to remove user data and/or download history for redownload + - **Subscription** mode + - Settings to change the program title and information in the program information + - Settings for saving video thumbnail along with the file or in the cache (temporary cache or permanent cache) + - A bug report form to create a bug report or say something nice to the developer :blush: + - Prevent adding site-specific labels when adding to a collection + - Ability to select custom user highlighting in the main window and feed. + - Add a notification to the log if the user is not found on the site + - Added visualization of users download queue + - Ability to set more than one global paths + - Improve user paths changing: now you can also simply move the user/collection to another global location + - Ability to move multiple user/collection to another location + - Download groups: added `Subscription` options + - Download groups: the ability to set the number of users to download + - Auto downloader: new group options + - Auto downloader: additional skip options + - Auto downloader: added force start + - Feed: press `Ctrl+G` to go to a specific page + - Feed: added site icon to post + - Feed: always using `Friendly name` instead of `UserName` if it exists + - Missing posts: the ability to delete all missing posts + - Standalone downloader: add the ability to store download locations and quickly select after + - Standalone downloader: add `Ctrl+O` hotkey to select destination path + - Standalone downloader: add `Alt+O` hotkey to select destination path and save it to download locations + - User editor: ability to hide/show site-specific labels in collection editing mode + - Main window: filters by subscription and user + - Instagram: if the user is not found on the site, SCrawler will check for a new user name + - OnlyFans: handling of `504` and `429` errors + - OnlyFans: the `sec-ch-ua` header is now optional + - OnlyFans: ability to download 'Highlights" and media from chats + - PathPlugin: incorrect detection of path existence + - PornHub: completely rewritten videos parser + - PornHub: now you choose which videos you want to download (uploaded, tagged, private, favorites) + - PornHub: subscription mode + - PornHub: ability to download search queries and search categories + - Reddit: ability to set the number of concurrent downloads + - Reddit: added bearer token (optional) + - Reddit: added OAuth authorization (optional) + - Reddit: options to use the bearer token for the timeline and/or saved posts + - Reddit: option to disable the use of cookies for the timeline + - ThisVid: now you can also download user's favorite videos + - ThisVid: ability to download search queries, search categories and search tags + - ThisVid: subscription mode + - Twitter: new options: `Use the appropriate model`, `New endpoint: search`, `New endpoint: profiles`, `Abort on limit`, `Download already parsed` and `Media Model: allow non-user tweets` + - Twitter: new user option `Force apply` + - xHamster: ability to download search queries, search categories and search tags + - xHamster: subscription mode + - xHamster: pornstars download + - XVideos: ability to download search queries, search categories and search tags + - XVideos: subscription mode + - YouTube: added `Output path: ask for a name` and `Output path: auto add` settings + - YouTube: added the ability to store download locations and quickly select after + - YouTube: subscription mode + - Plugins.Attributes: added `DependentFields` attribute + - Plugins.Attributes: replace `Dependencies` with `Arguments` (`PropertyUpdater` attribute) + - Plugins.IPluginContentProvider: added `Options` and `IsSubscription` properties + - Plugins.ISiteSettings: added `SubscriptionsAllowed` property + - Plugins.ExchangeOptions: added `Options` field + - Plugins: added `ExitException` + - Other improvements +- Updated + - gallery-dl up to version 1.25.8 + - yt-dlp up to version 2023.07.06 + - LibVLCSharp up to 3.7.0 + - VideoLAN up to 3.0.18 +- Fixed + - **TikTok** supported again! + - Auto downloader: excluded labels and sites in default mode are not respected + - Download info: does not remember the last size and location + - Download info: hide unnecessary error + - Feed: `webm` photos not showing + - Search users: incorrect search by name + - OnlyFans: incorrect parsing of username containing dots + - OnlyFans: incorrect error handler + - Reddit: Handling error 502 (Reddit data not downloading) + - RedGifs: incorrect behavior when updating token + - Twitter: gifs are not downloading + - xHamster: some channels cannot be downloaded or are not fully downloaded + - YouTube: re-saving elements when loading a video list + - YouTube: files were not deleted when the delete button was clicked + - YouTube: a bug that caused the video to redownload + - Minor bugs + # 2023.6.19.0 *2023-06-19* diff --git a/ProgramScreenshots/AppYouTube.png b/ProgramScreenshots/AppYouTube.png index e8313ae..afc11d2 100644 Binary files a/ProgramScreenshots/AppYouTube.png and b/ProgramScreenshots/AppYouTube.png differ diff --git a/ProgramScreenshots/BugReport.png b/ProgramScreenshots/BugReport.png new file mode 100644 index 0000000..2b97b02 Binary files /dev/null and b/ProgramScreenshots/BugReport.png differ diff --git a/ProgramScreenshots/CreateUserClear.png b/ProgramScreenshots/CreateUserClear.png index 9bac0b3..4670124 100644 Binary files a/ProgramScreenshots/CreateUserClear.png and b/ProgramScreenshots/CreateUserClear.png differ diff --git a/ProgramScreenshots/FeedWindow.png b/ProgramScreenshots/FeedWindow.png index 7746573..609e90d 100644 Binary files a/ProgramScreenshots/FeedWindow.png and b/ProgramScreenshots/FeedWindow.png differ diff --git a/ProgramScreenshots/GroupCreating.png b/ProgramScreenshots/GroupCreating.png index 5bf6886..8d50eb9 100644 Binary files a/ProgramScreenshots/GroupCreating.png and b/ProgramScreenshots/GroupCreating.png differ diff --git a/ProgramScreenshots/LocationsChanger.png b/ProgramScreenshots/LocationsChanger.png new file mode 100644 index 0000000..208940d Binary files /dev/null and b/ProgramScreenshots/LocationsChanger.png differ diff --git a/ProgramScreenshots/MainContext.png b/ProgramScreenshots/MainContext.png index 914d9fa..6855080 100644 Binary files a/ProgramScreenshots/MainContext.png and b/ProgramScreenshots/MainContext.png differ diff --git a/ProgramScreenshots/MainWindow.png b/ProgramScreenshots/MainWindow.png index d5e847a..eae5981 100644 Binary files a/ProgramScreenshots/MainWindow.png and b/ProgramScreenshots/MainWindow.png differ diff --git a/ProgramScreenshots/MainWindowPause.png b/ProgramScreenshots/MainWindowPause.png index 311eebe..1c08d87 100644 Binary files a/ProgramScreenshots/MainWindowPause.png and b/ProgramScreenshots/MainWindowPause.png differ diff --git a/ProgramScreenshots/MainWindowView.png b/ProgramScreenshots/MainWindowView.png index c1e9d81..696fb51 100644 Binary files a/ProgramScreenshots/MainWindowView.png and b/ProgramScreenshots/MainWindowView.png differ diff --git a/ProgramScreenshots/SettingsAutoDownloader.png b/ProgramScreenshots/SettingsAutoDownloader.png index 691c7e3..4ba775a 100644 Binary files a/ProgramScreenshots/SettingsAutoDownloader.png and b/ProgramScreenshots/SettingsAutoDownloader.png differ diff --git a/ProgramScreenshots/SettingsGlobalBasis.png b/ProgramScreenshots/SettingsGlobalBasis.png index 5f191d5..a36abb9 100644 Binary files a/ProgramScreenshots/SettingsGlobalBasis.png and b/ProgramScreenshots/SettingsGlobalBasis.png differ diff --git a/ProgramScreenshots/SettingsGlobalDownloader.png b/ProgramScreenshots/SettingsGlobalDownloader.png index a5b26ff..384b3d7 100644 Binary files a/ProgramScreenshots/SettingsGlobalDownloader.png and b/ProgramScreenshots/SettingsGlobalDownloader.png differ diff --git a/ProgramScreenshots/SettingsGlobalFeed.png b/ProgramScreenshots/SettingsGlobalFeed.png index e7f8130..7f77db4 100644 Binary files a/ProgramScreenshots/SettingsGlobalFeed.png and b/ProgramScreenshots/SettingsGlobalFeed.png differ diff --git a/ProgramScreenshots/SettingsScheduler.png b/ProgramScreenshots/SettingsScheduler.png index 1a8c64e..d1bcf3a 100644 Binary files a/ProgramScreenshots/SettingsScheduler.png and b/ProgramScreenshots/SettingsScheduler.png differ diff --git a/ProgramScreenshots/SettingsSiteInstagram.png b/ProgramScreenshots/SettingsSiteInstagram.png index 73cdc92..76cd565 100644 Binary files a/ProgramScreenshots/SettingsSiteInstagram.png and b/ProgramScreenshots/SettingsSiteInstagram.png differ diff --git a/ProgramScreenshots/SettingsSiteOnlyFans.png b/ProgramScreenshots/SettingsSiteOnlyFans.png index 6603b68..d60a057 100644 Binary files a/ProgramScreenshots/SettingsSiteOnlyFans.png and b/ProgramScreenshots/SettingsSiteOnlyFans.png differ diff --git a/ProgramScreenshots/SettingsSitePornHub.png b/ProgramScreenshots/SettingsSitePornHub.png index 2a02a1d..53d52f4 100644 Binary files a/ProgramScreenshots/SettingsSitePornHub.png and b/ProgramScreenshots/SettingsSitePornHub.png differ diff --git a/ProgramScreenshots/SettingsSiteReddit.png b/ProgramScreenshots/SettingsSiteReddit.png index 721a33a..8fd79cc 100644 Binary files a/ProgramScreenshots/SettingsSiteReddit.png and b/ProgramScreenshots/SettingsSiteReddit.png differ diff --git a/ProgramScreenshots/SettingsSiteThisVid.png b/ProgramScreenshots/SettingsSiteThisVid.png index cc304cd..a724569 100644 Binary files a/ProgramScreenshots/SettingsSiteThisVid.png and b/ProgramScreenshots/SettingsSiteThisVid.png differ diff --git a/ProgramScreenshots/SettingsSiteTikTok.png b/ProgramScreenshots/SettingsSiteTikTok.png index 6ddb4a9..3694505 100644 Binary files a/ProgramScreenshots/SettingsSiteTikTok.png and b/ProgramScreenshots/SettingsSiteTikTok.png differ diff --git a/ProgramScreenshots/SettingsSiteTwitter.png b/ProgramScreenshots/SettingsSiteTwitter.png index c24fb2c..dd7b574 100644 Binary files a/ProgramScreenshots/SettingsSiteTwitter.png and b/ProgramScreenshots/SettingsSiteTwitter.png differ diff --git a/ProgramScreenshots/SettingsTwitterUser.png b/ProgramScreenshots/SettingsTwitterUser.png index af6ec99..5389e0a 100644 Binary files a/ProgramScreenshots/SettingsTwitterUser.png and b/ProgramScreenshots/SettingsTwitterUser.png differ diff --git a/ProgramScreenshots/TrayContextMenu.png b/ProgramScreenshots/TrayContextMenu.png index 4b7e67c..5ce9712 100644 Binary files a/ProgramScreenshots/TrayContextMenu.png and b/ProgramScreenshots/TrayContextMenu.png differ diff --git a/ProgramScreenshots/UserDefaultQueryOptions.png b/ProgramScreenshots/UserDefaultQueryOptions.png new file mode 100644 index 0000000..ab33df8 Binary files /dev/null and b/ProgramScreenshots/UserDefaultQueryOptions.png differ diff --git a/README.md b/README.md index e9efeec..d1ca639 100644 --- a/README.md +++ b/README.md @@ -40,20 +40,21 @@ A program to download photo and video from [any site](#supported-sites) (e.g. Yo - Pinterest boards, users, saved posts; - Imgur images, galleries and videos; - Gfycat videos; - - PornHub images, videos, save (liked) posts; - - XHamster images, videos, saved posts; - - XVIDEOS videos, saved posts; - - ThisVid images, videos, saved posts; + - PornHub images, videos, save (liked) posts, search queries, search categories; + - XHamster images, videos, saved posts, search queries, search categories, search tags; + - XVIDEOS videos, saved posts, search queries, search categories; + - ThisVid images, videos, saved posts, search queries, search categories, search tags; - [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) +- Parse [Reddit channel and view data](https://github.com/AAndyProgram/SCrawler/wiki/Channels) +- Download [saved posts](https://github.com/AAndyProgram/SCrawler/wiki/Home#saved-posts) - Add users from parsed channel - **Advanced user management** - **Automation** ([downloading data automatically](https://github.com/AAndyProgram/SCrawler/wiki/Settings#automation) every ```X``` minutes) -- **Feed** ([feed](https://github.com/AAndyProgram/SCrawler/wiki#feed) of downloaded media files) +- **Feed** ([feed](https://github.com/AAndyProgram/SCrawler/wiki#feed) of downloaded media files and subscriptions posts) - Labeling users - Create [download groups](https://github.com/AAndyProgram/SCrawler/wiki/Settings#download-groups) - Adding users to favorites and temporary +- Adding users and search queries in the **Subscription** mode (download post preview, but do not download the media file) - [Filter exists users](https://github.com/AAndyProgram/SCrawler/wiki#view) by label or group - Selection of media types you want to download (images only, videos only, both) - [Download a special video](https://github.com/AAndyProgram/SCrawler/wiki#download-separate-video), image or gallery @@ -71,7 +72,7 @@ A program to download photo and video from [any site](#supported-sites) (e.g. Yo - **OnlyFans** - **Mastodon** - **Instagram** -- TikTok (*currently broken*; [limited](https://github.com/AAndyProgram/SCrawler/wiki/Settings#tiktok-limits)) +- TikTok - RedGifs - Pinterest - Imgur @@ -193,6 +194,8 @@ F5-->[*] # Contact me +[e-mail](mailto:andyprogram@proton.me): andyprogram@proton.me + Matrix (Element): https://matrix.to/#/@andyprogram:matrix.org Discord (contact the developer): andyprogram diff --git a/SCrawler.PluginProvider/Attributes/Attributes.vb b/SCrawler.PluginProvider/Attributes/Attributes.vb index 0dd2c6a..371d5e2 100644 --- a/SCrawler.PluginProvider/Attributes/Attributes.vb +++ b/SCrawler.PluginProvider/Attributes/Attributes.vb @@ -44,6 +44,16 @@ Namespace Plugin.Attributes Name = PropertyName End Sub End Class + ''' Set the dependent fields that need to be updated when this property is changed internally. + Public NotInheritable Class DependentFields : Inherits Attribute + Public ReadOnly Fields As String() + Public Sub New(ByVal Field As String) + Fields = {Field} + End Sub + Public Sub New(ByVal Fields As String()) + Me.Fields = Fields + End Sub + End Class ''' Store property value in settings XML file Public NotInheritable Class PXML : Inherits Attribute Public ReadOnly ElementName As String @@ -59,16 +69,16 @@ Namespace Plugin.Attributes ''' Special property updater Public NotInheritable Class PropertyUpdater : Inherits Attribute Public ReadOnly Name As String - Public ReadOnly Dependencies As String() + Public ReadOnly Arguments As String() ''' Public Sub New(ByVal UpdatingPropertyName As String) Name = UpdatingPropertyName End Sub ''' Initialize a new PropertyUpdater attribute ''' The name of the property to be updated - Public Sub New(ByVal UpdatingPropertyName As String, ByVal Dependent As String()) + Public Sub New(ByVal UpdatingPropertyName As String, ByVal Arguments As String()) Name = UpdatingPropertyName - Dependencies = Dependent + Me.Arguments = Arguments End Sub End Class ''' Plugin key diff --git a/SCrawler.PluginProvider/Interfaces/IPluginContentProvider.vb b/SCrawler.PluginProvider/Interfaces/IPluginContentProvider.vb index 2bf0c9a..0b6e93f 100644 --- a/SCrawler.PluginProvider/Interfaces/IPluginContentProvider.vb +++ b/SCrawler.PluginProvider/Interfaces/IPluginContentProvider.vb @@ -17,6 +17,7 @@ Namespace Plugin Property Settings As ISiteSettings Property Name As String Property ID As String + Property Options As String Property ParseUserMediaOnly As Boolean Property UserDescription As String Property ExistingContentList As List(Of IUserMedia) @@ -25,6 +26,7 @@ Namespace Plugin Property UserExists As Boolean Property UserSuspended As Boolean Property IsSavedPosts As Boolean + Property IsSubscription As Boolean Property SeparateVideoFolder As Boolean Property DataPath As String Property PostsNumberLimit As Integer? diff --git a/SCrawler.PluginProvider/Interfaces/ISiteSettings.vb b/SCrawler.PluginProvider/Interfaces/ISiteSettings.vb index 0563589..30ed1f0 100644 --- a/SCrawler.PluginProvider/Interfaces/ISiteSettings.vb +++ b/SCrawler.PluginProvider/Interfaces/ISiteSettings.vb @@ -17,6 +17,7 @@ Namespace Plugin ReadOnly Property Icon As Icon ReadOnly Property Image As Image ReadOnly Property Site As String + ReadOnly Property SubscriptionsAllowed As Boolean Property Logger As ILogProvider Function GetUserUrl(ByVal User As IPluginContentProvider) As String Function IsMyUser(ByVal UserURL As String) As ExchangeOptions diff --git a/SCrawler.PluginProvider/My Project/AssemblyInfo.vb b/SCrawler.PluginProvider/My Project/AssemblyInfo.vb index fbd419a..668c898 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.PluginProvider/Objects/ExchangeOptions.vb b/SCrawler.PluginProvider/Objects/ExchangeOptions.vb index e761d6a..f6d6e54 100644 --- a/SCrawler.PluginProvider/Objects/ExchangeOptions.vb +++ b/SCrawler.PluginProvider/Objects/ExchangeOptions.vb @@ -11,6 +11,7 @@ Namespace Plugin Public UserName As String Public SiteName As String Public HostKey As String + Public Options As String Public Exists As Boolean Public Sub New(ByVal Site As String, ByVal Name As String) UserName = Name diff --git a/SCrawler.PluginProvider/Objects/ExitException.vb b/SCrawler.PluginProvider/Objects/ExitException.vb new file mode 100644 index 0000000..28173a2 --- /dev/null +++ b/SCrawler.PluginProvider/Objects/ExitException.vb @@ -0,0 +1,36 @@ +' Copyright (C) 2023 Andy https://github.com/AAndyProgram +' 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 Plugin + ''' Represents errors that occur during downloading to be thrown to the root downloading function. + Public Class ExitException : Inherits Exception + ''' Add only the message to the log, without adding a . Default: . + ''' if only the message should be added to the log; otherwise the stack trace will also be added. + Public Property SimpleLogLine As Boolean = True + ''' Don't add a message to the log. Default: . + ''' if the error is exit-only and there is no need to add a message to the log; otherwise add a message to the log. + Public Property Silent As Boolean = False + ''' Initializes a new instance of the class. + Public Sub New() + End Sub + ''' Initializes a new instance of the class with a specified error message. + ''' The message that describes the error. + Public Sub New(ByVal Message As String) + MyBase.New(Message) + End Sub + ''' + ''' Initializes a new instance of the class with a specified error message + ''' and a reference to the inner exception that is the cause of this exception. + ''' + ''' The error message that explains the reason for the exception. + ''' The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. + Public Sub New(ByVal Message As String, ByVal InnerException As Exception) + MyBase.New(Message, InnerException) + End Sub + End Class +End Namespace \ No newline at end of file diff --git a/SCrawler.PluginProvider/SCrawler.PluginProvider.vbproj b/SCrawler.PluginProvider/SCrawler.PluginProvider.vbproj index a45fa8b..6e9e354 100644 --- a/SCrawler.PluginProvider/SCrawler.PluginProvider.vbproj +++ b/SCrawler.PluginProvider/SCrawler.PluginProvider.vbproj @@ -107,6 +107,7 @@ + diff --git a/SCrawler.YouTube/Base/Structures.vb b/SCrawler.YouTube/Base/Structures.vb index 58b168b..cf3bc77 100644 --- a/SCrawler.YouTube/Base/Structures.vb +++ b/SCrawler.YouTube/Base/Structures.vb @@ -58,7 +58,7 @@ Namespace API.YouTube.Base ''' Kb Public Size As Double Public Codec As String - Public Info As String + Public Protocol As String Public URL As String Public Property Index As Integer Implements IIndexable.Index Private Function SetIndex(ByVal Obj As Object, ByVal Index As Integer) As Object Implements IIndexable.SetIndex diff --git a/SCrawler.YouTube/Base/YouTubeSettings.vb b/SCrawler.YouTube/Base/YouTubeSettings.vb index 46fe325..68f2e35 100644 --- a/SCrawler.YouTube/Base/YouTubeSettings.vb +++ b/SCrawler.YouTube/Base/YouTubeSettings.vb @@ -34,13 +34,36 @@ Namespace API.YouTube.Base Friend ReadOnly Property DesignXml As XmlFile Private Property Mode As GridUpdateModes = GridUpdateModes.OnConfirm Implements IGridValuesContainer.Mode Friend ReadOnly Property PlaylistFormSplitterDistance As XMLValue(Of Integer) + Friend ReadOnly Property DownloadLocations As DownloadLocationsCollection #Region "Environment" - Public ReadOnly Property YTDLP As XMLValue(Of SFile) - Public ReadOnly Property FFMPEG As XMLValue(Of SFile) + Private ReadOnly Property ENVIR_FFMPEG As SFile Implements IDownloaderSettings.ENVIR_FFMPEG + Get + Return FFMPEG + End Get + End Property + Private ReadOnly Property ENVIR_YTDLP As SFile Implements IDownloaderSettings.ENVIR_YTDLP + Get + Return YTDLP + End Get + End Property + Private ReadOnly Property ENVIR_GDL As SFile Implements IDownloaderSettings.ENVIR_GDL + Get + Return Nothing + End Get + End Property + Private ReadOnly Property ENVIR_CURL As SFile Implements IDownloaderSettings.ENVIR_CURL + Get + Return Nothing + End Get + End Property +#End Region Public ReadOnly Property Cookies As CookieKeeper @@ -62,6 +85,22 @@ Namespace API.YouTube.Base Public ReadOnly Property OutputPathAutoChange As XMLValue(Of Boolean) + + Public ReadOnly Property OutputPathAskForName As XMLValue(Of Boolean) + Private ReadOnly Property IDownloaderSettings_OutputPathAskForName As Boolean Implements IDownloaderSettings.OutputPathAskForName + Get + Return OutputPathAskForName + End Get + End Property + + Public ReadOnly Property OutputPathAutoAddPaths As XMLValue(Of Boolean) + Private ReadOnly Property IDownloaderSettings_OutputPathAutoAddPaths As Boolean Implements IDownloaderSettings.OutputPathAutoAddPaths + Get + Return OutputPathAutoAddPaths + End Get + End Property Public ReadOnly Property OnItemDoubleClick As XMLValue(Of DoubleClickBehavior) @@ -155,6 +194,12 @@ Namespace API.YouTube.Base Public ReadOnly Property ShowFormDownTrayClick As XMLValue(Of Boolean) + + Friend ReadOnly Property ProgramText As XMLValue(Of String) + + Friend ReadOnly Property ProgramDescription As XMLValue(Of String) #End Region #Region "Defaults Video" Public ReadOnly Property DefaultVideoDefinition As XMLValue(Of Integer) + + Public ReadOnly Property DefaultVideoIncludeNullSize As XMLValue(Of Boolean) #End Region #Region "Defaults Audio" + + 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 + AAAASUVORK5CYII= \ No newline at end of file diff --git a/SCrawler.YouTube/Controls/MusicPlaylistsForm.vb b/SCrawler.YouTube/Controls/MusicPlaylistsForm.vb index 02b6620..df6482e 100644 --- a/SCrawler.YouTube/Controls/MusicPlaylistsForm.vb +++ b/SCrawler.YouTube/Controls/MusicPlaylistsForm.vb @@ -8,6 +8,7 @@ ' but WITHOUT ANY WARRANTY Imports System.ComponentModel Imports SCrawler.API.YouTube.Objects +Imports SCrawler.DownloadObjects.STDownloader Imports PersonalUtilities.Forms Imports PersonalUtilities.Forms.Controls Imports PersonalUtilities.Forms.Controls.Base @@ -50,6 +51,8 @@ Namespace API.YouTube.Controls MyView.SetFormSize() End If + MyYouTubeSettings.DownloadLocations.PopulateComboBox(TXT_OUTPUT_PATH) + CMB_FORMATS.Items.AddRange(AvailableAudioFormats) If MyYouTubeSettings.PlaylistFormSplitterDistance > 0 Then SPLITTER_MAIN.SplitterDistancePercentageSet(MyYouTubeSettings.PlaylistFormSplitterDistance) @@ -102,6 +105,17 @@ Namespace API.YouTube.Controls MyYouTubeSettings.PlaylistFormSplitterDistance.Value = SPLITTER_MAIN.SplitterDistancePercentageGet MyView.DisposeIfReady() End Sub + Private Sub MusicPlaylistsForm_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown + Dim b As Boolean = True + If e.KeyCode = Keys.O And e.Control Then + MyYouTubeSettings.DownloadLocations.ChooseNewLocation(TXT_OUTPUT_PATH, False, MyDownloaderSettings.OutputPathAskForName) + ElseIf e.KeyCode = Keys.O And e.Alt Then + MyYouTubeSettings.DownloadLocations.ChooseNewLocation(TXT_OUTPUT_PATH, True, MyDownloaderSettings.OutputPathAskForName) + Else + b = False + End If + If b Then e.Handled = True + End Sub #End Region #Region "Form text" Private _InitialFormText As String = String.Empty @@ -159,10 +173,8 @@ Namespace API.YouTube.Controls End With End Sub Private Sub TXT_OUTPUT_PATH_ActionOnButtonClick(ByVal Sender As ActionButton, ByVal e As ActionButtonEventArgs) Handles TXT_OUTPUT_PATH.ActionOnButtonClick - If Sender.DefaultButton = ADB.Open Then - Dim f As SFile = SFile.SelectPath(TXT_OUTPUT_PATH.Text, "Select files destination", EDP.ReturnValue) - If Not f.IsEmptyString Then TXT_OUTPUT_PATH.Text = f - End If + If Sender.DefaultButton = ADB.Open Or Sender.DefaultButton = ADB.Add Then _ + MyYouTubeSettings.DownloadLocations.ChooseNewLocation(TXT_OUTPUT_PATH, Sender.DefaultButton = ADB.Add, MyDownloaderSettings.OutputPathAskForName) End Sub #End Region #Region "Lists' handlers" @@ -256,6 +268,7 @@ Namespace API.YouTube.Controls If Not TXT_FORMATS_ADDIT.Checked Then .PostProcessing_OutputAudioFormats.Clear() .File = TXT_OUTPUT_PATH.Text.CSFileP If MyYouTubeSettings.OutputPathAutoChange Then MyYouTubeSettings.OutputPath.Value = .File + If MyDownloaderSettings.OutputPathAutoAddPaths Then MyYouTubeSettings.DownloadLocations.Add(.File, False) End With DialogResult = DialogResult.OK Close() diff --git a/SCrawler.YouTube/Controls/VideoOption.vb b/SCrawler.YouTube/Controls/VideoOption.vb index a730200..71cdd3b 100644 --- a/SCrawler.YouTube/Controls/VideoOption.vb +++ b/SCrawler.YouTube/Controls/VideoOption.vb @@ -52,6 +52,7 @@ Namespace API.YouTube.Controls End If LBL_DEFINITION.Text = $"{m.Height}p" LBL_CODECS.Text = $"{m.Extension.StringToUpper}{d}{m.Codec.StringToUpper}{d}{m.FPS}fps{d}{m.Bitrate}k" + If Not m.Protocol.IsEmptyString Then LBL_CODECS.Text &= $" ({m.Protocol})" If Not SelectedAudio.ID.IsEmptyString Then LBL_CODECS.Text &= $" / {SelectedAudio.Extension}{d}{SelectedAudio.Codec}{d}{SelectedAudio.Bitrate}k" End If diff --git a/SCrawler.YouTube/Controls/VideoOptionsForm.Designer.vb b/SCrawler.YouTube/Controls/VideoOptionsForm.Designer.vb index 633c461..66d6b91 100644 --- a/SCrawler.YouTube/Controls/VideoOptionsForm.Designer.vb +++ b/SCrawler.YouTube/Controls/VideoOptionsForm.Designer.vb @@ -29,6 +29,10 @@ Namespace API.YouTube.Controls Dim ICON_LINK As System.Windows.Forms.PictureBox Dim TP_FOOTER As System.Windows.Forms.TableLayoutPanel Dim TP_DESTINATION As System.Windows.Forms.TableLayoutPanel + 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(VideoOptionsForm)) + Dim ListColumn1 As PersonalUtilities.Forms.Controls.Base.ListColumn = New PersonalUtilities.Forms.Controls.Base.ListColumn() + Dim ListColumn2 As PersonalUtilities.Forms.Controls.Base.ListColumn = New PersonalUtilities.Forms.Controls.Base.ListColumn() Dim TP_OK_CANCEL As System.Windows.Forms.TableLayoutPanel Dim LB_SEP_1 As System.Windows.Forms.Label Dim LB_SEP_2 As System.Windows.Forms.Label @@ -37,8 +41,6 @@ Namespace API.YouTube.Controls Dim LBL_FORMAT As System.Windows.Forms.Label Dim LBL_SUBS_FORMAT As System.Windows.Forms.Label Dim TT_MAIN As System.Windows.Forms.ToolTip - 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(VideoOptionsForm)) 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() @@ -47,12 +49,13 @@ Namespace API.YouTube.Controls Dim ActionButton7 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() 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() Me.ICON_VIDEO = New System.Windows.Forms.PictureBox() Me.LBL_TITLE = New System.Windows.Forms.Label() Me.TP_HEADER_INFO_2 = New System.Windows.Forms.TableLayoutPanel() Me.LBL_TIME = New System.Windows.Forms.Label() Me.LBL_URL = New System.Windows.Forms.LinkLabel() - Me.TXT_FILE = New System.Windows.Forms.TextBox() + Me.TXT_FILE = New PersonalUtilities.Forms.Controls.ComboBoxExtended() Me.BTT_BROWSE = New System.Windows.Forms.Button() Me.BTT_DOWN = New System.Windows.Forms.Button() Me.BTT_CANCEL = New System.Windows.Forms.Button() @@ -93,6 +96,7 @@ Namespace API.YouTube.Controls CType(ICON_LINK, System.ComponentModel.ISupportInitialize).BeginInit() TP_FOOTER.SuspendLayout() TP_DESTINATION.SuspendLayout() + CType(Me.TXT_FILE, System.ComponentModel.ISupportInitialize).BeginInit() TP_OK_CANCEL.SuspendLayout() TP_WHAT.SuspendLayout() Me.TP_HEADER_BASE.SuspendLayout() @@ -267,12 +271,27 @@ Namespace API.YouTube.Controls ' 'TXT_FILE ' + ActionButton1.BackgroundImage = CType(resources.GetObject("ActionButton1.BackgroundImage"), System.Drawing.Image) + ActionButton1.Name = "ArrowDown" + ActionButton1.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.ArrowDown + Me.TXT_FILE.Buttons.Add(ActionButton1) + ListColumn1.Name = "COL_NAME" + ListColumn1.Text = "Name" + ListColumn1.Width = -1 + ListColumn2.DisplayMember = True + ListColumn2.Name = "COL_VALUE" + ListColumn2.Text = "Value" + ListColumn2.ValueMember = True + ListColumn2.Visible = False + Me.TXT_FILE.Columns.Add(ListColumn1) + Me.TXT_FILE.Columns.Add(ListColumn2) Me.TXT_FILE.Dock = System.Windows.Forms.DockStyle.Fill - Me.TXT_FILE.Location = New System.Drawing.Point(3, 3) + Me.TXT_FILE.Location = New System.Drawing.Point(1, 1) + Me.TXT_FILE.Margin = New System.Windows.Forms.Padding(1) Me.TXT_FILE.Name = "TXT_FILE" - Me.TXT_FILE.Size = New System.Drawing.Size(503, 20) + Me.TXT_FILE.Size = New System.Drawing.Size(507, 22) Me.TXT_FILE.TabIndex = 0 - Me.TXT_FILE.WordWrap = False + Me.TXT_FILE.TextBoxBorderStyle = System.Windows.Forms.BorderStyle.FixedSingle ' 'BTT_BROWSE ' @@ -283,7 +302,7 @@ Namespace API.YouTube.Controls Me.BTT_BROWSE.Size = New System.Drawing.Size(74, 22) Me.BTT_BROWSE.TabIndex = 1 Me.BTT_BROWSE.Text = "Browse" - TT_MAIN.SetToolTip(Me.BTT_BROWSE, "Choose an output file") + TT_MAIN.SetToolTip(Me.BTT_BROWSE, "Choose an output file (Right click for add a new location to the list)") Me.BTT_BROWSE.UseVisualStyleBackColor = True ' 'TP_OK_CANCEL @@ -473,21 +492,21 @@ Namespace API.YouTube.Controls ' 'TXT_SUBS ' - ActionButton1.BackgroundImage = CType(resources.GetObject("ActionButton1.BackgroundImage"), System.Drawing.Image) - ActionButton1.Name = "Open" - ActionButton1.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open - ActionButton1.ToolTipText = "Choose subtitles" ActionButton2.BackgroundImage = CType(resources.GetObject("ActionButton2.BackgroundImage"), System.Drawing.Image) - ActionButton2.Name = "Refresh" - ActionButton2.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Refresh - ActionButton2.ToolTipText = "Reset subtitles to initial selected" + ActionButton2.Name = "Open" + ActionButton2.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open + ActionButton2.ToolTipText = "Choose subtitles" ActionButton3.BackgroundImage = CType(resources.GetObject("ActionButton3.BackgroundImage"), System.Drawing.Image) - ActionButton3.Name = "Clear" - ActionButton3.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear - ActionButton3.ToolTipText = "Clear subtitles selection (don't download subtitles)" - Me.TXT_SUBS.Buttons.Add(ActionButton1) + ActionButton3.Name = "Refresh" + ActionButton3.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Refresh + ActionButton3.ToolTipText = "Reset subtitles to initial selected" + ActionButton4.BackgroundImage = CType(resources.GetObject("ActionButton4.BackgroundImage"), System.Drawing.Image) + ActionButton4.Name = "Clear" + ActionButton4.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear + ActionButton4.ToolTipText = "Clear subtitles selection (don't download subtitles)" Me.TXT_SUBS.Buttons.Add(ActionButton2) Me.TXT_SUBS.Buttons.Add(ActionButton3) + Me.TXT_SUBS.Buttons.Add(ActionButton4) Me.TXT_SUBS.CaptionText = "Subtitles" Me.TXT_SUBS.CaptionToolTipEnabled = True Me.TXT_SUBS.CaptionToolTipText = "The selected subtitles will also be downloaded" @@ -611,24 +630,24 @@ Namespace API.YouTube.Controls ' 'TXT_SUBS_ADDIT ' - ActionButton4.BackgroundImage = CType(resources.GetObject("ActionButton4.BackgroundImage"), System.Drawing.Image) - ActionButton4.Enabled = False - ActionButton4.Name = "Open" - ActionButton4.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open - ActionButton4.ToolTipText = "Choose additional formats" ActionButton5.BackgroundImage = CType(resources.GetObject("ActionButton5.BackgroundImage"), System.Drawing.Image) ActionButton5.Enabled = False - ActionButton5.Name = "Refresh" - ActionButton5.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Refresh - ActionButton5.ToolTipText = "Fill in additional formats from the defaults" + ActionButton5.Name = "Open" + ActionButton5.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open + ActionButton5.ToolTipText = "Choose additional formats" ActionButton6.BackgroundImage = CType(resources.GetObject("ActionButton6.BackgroundImage"), System.Drawing.Image) ActionButton6.Enabled = False - ActionButton6.Name = "Clear" - ActionButton6.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear - ActionButton6.ToolTipText = "Remove all additional formats" - Me.TXT_SUBS_ADDIT.Buttons.Add(ActionButton4) + ActionButton6.Name = "Refresh" + ActionButton6.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Refresh + ActionButton6.ToolTipText = "Fill in additional formats from the defaults" + ActionButton7.BackgroundImage = CType(resources.GetObject("ActionButton7.BackgroundImage"), System.Drawing.Image) + ActionButton7.Enabled = False + ActionButton7.Name = "Clear" + ActionButton7.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear + ActionButton7.ToolTipText = "Remove all additional formats" Me.TXT_SUBS_ADDIT.Buttons.Add(ActionButton5) Me.TXT_SUBS_ADDIT.Buttons.Add(ActionButton6) + Me.TXT_SUBS_ADDIT.Buttons.Add(ActionButton7) Me.TXT_SUBS_ADDIT.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.CheckBox Me.TXT_SUBS_ADDIT.CaptionText = "Additional subtitle formats" Me.TXT_SUBS_ADDIT.CaptionToolTipEnabled = True @@ -646,24 +665,24 @@ Namespace API.YouTube.Controls ' 'TXT_EXTRA_AUDIO_FORMATS ' - ActionButton7.BackgroundImage = CType(resources.GetObject("ActionButton7.BackgroundImage"), System.Drawing.Image) - ActionButton7.Enabled = False - ActionButton7.Name = "Open" - ActionButton7.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open - ActionButton7.ToolTipText = "Choose additional formats" ActionButton8.BackgroundImage = CType(resources.GetObject("ActionButton8.BackgroundImage"), System.Drawing.Image) ActionButton8.Enabled = False - ActionButton8.Name = "Refresh" - ActionButton8.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Refresh - ActionButton8.ToolTipText = "Fill in additional formats from the defaults" + ActionButton8.Name = "Open" + ActionButton8.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open + ActionButton8.ToolTipText = "Choose additional formats" ActionButton9.BackgroundImage = CType(resources.GetObject("ActionButton9.BackgroundImage"), System.Drawing.Image) ActionButton9.Enabled = False - ActionButton9.Name = "Clear" - ActionButton9.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear - ActionButton9.ToolTipText = "Choose additional formats" - Me.TXT_EXTRA_AUDIO_FORMATS.Buttons.Add(ActionButton7) + ActionButton9.Name = "Refresh" + ActionButton9.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Refresh + ActionButton9.ToolTipText = "Fill in additional formats from the defaults" + ActionButton10.BackgroundImage = CType(resources.GetObject("ActionButton10.BackgroundImage"), System.Drawing.Image) + ActionButton10.Enabled = False + ActionButton10.Name = "Clear" + ActionButton10.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear + ActionButton10.ToolTipText = "Choose additional formats" Me.TXT_EXTRA_AUDIO_FORMATS.Buttons.Add(ActionButton8) Me.TXT_EXTRA_AUDIO_FORMATS.Buttons.Add(ActionButton9) + Me.TXT_EXTRA_AUDIO_FORMATS.Buttons.Add(ActionButton10) Me.TXT_EXTRA_AUDIO_FORMATS.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.CheckBox Me.TXT_EXTRA_AUDIO_FORMATS.CaptionText = "Additional audio formats" Me.TXT_EXTRA_AUDIO_FORMATS.CaptionToolTipEnabled = True @@ -704,7 +723,7 @@ Namespace API.YouTube.Controls CType(ICON_LINK, System.ComponentModel.ISupportInitialize).EndInit() TP_FOOTER.ResumeLayout(False) TP_DESTINATION.ResumeLayout(False) - TP_DESTINATION.PerformLayout() + CType(Me.TXT_FILE, System.ComponentModel.ISupportInitialize).EndInit() TP_OK_CANCEL.ResumeLayout(False) TP_WHAT.ResumeLayout(False) TP_WHAT.PerformLayout() @@ -740,7 +759,7 @@ Namespace API.YouTube.Controls Private WithEvents CMB_SUBS_FORMAT As ComboBox Private WithEvents TXT_SUBS_ADDIT As PersonalUtilities.Forms.Controls.TextBoxExtended Private WithEvents TXT_EXTRA_AUDIO_FORMATS As PersonalUtilities.Forms.Controls.TextBoxExtended - Private WithEvents TXT_FILE As TextBox + Private WithEvents TXT_FILE As PersonalUtilities.Forms.Controls.ComboBoxExtended Private WithEvents BTT_BROWSE As Button Private WithEvents BTT_DOWN As Button Private WithEvents BTT_CANCEL As Button diff --git a/SCrawler.YouTube/Controls/VideoOptionsForm.resx b/SCrawler.YouTube/Controls/VideoOptionsForm.resx index f3ef8e9..b101c5e 100644 --- a/SCrawler.YouTube/Controls/VideoOptionsForm.resx +++ b/SCrawler.YouTube/Controls/VideoOptionsForm.resx @@ -135,6 +135,97 @@ False + + + + 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 + AAAASUVORK5CYII= + + False @@ -162,8 +253,7 @@ False - - + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP @@ -174,7 +264,7 @@ cMaRN0UdBBkAAAAASUVORK5CYII= - + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6 JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAACOElE @@ -188,17 +278,17 @@ HhQDmFjYeHVKFp7WX/Xuv9Kq9/+Vd/z7r7rv/3+l7f//y676DEwDN/9L+BVvYkKLCTgDhNkkVUyVlr74 qbbz73/VOTc/qsy89kWx+9h7qbQpJwS1bbOAscGGrB6EUTggLOqf16C55ft/HlnNAFZOXgVWdi4FRgYG VnR1MIwhwMTCyqEQ37qEmZVDFF0OE/9nAACtFF4Ey6OP+wAAAABJRU5ErkJggg== - - - - - iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO - xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go - tbbB43rK5xSAQq1VYFtmeQBoqZTSreVZvgTknM8yyyjA/qodsDF9gspD2Bj6B+DH+NqzhQQAG+POMnSX - AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO + xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go + tbbB43rK5xSAQq1VYFtmeQBoqZTSreVZvgTknM8yyyjA/qodsDF9gspD2Bj6B+DH+NqzhQQAG+POMnSX + AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP @@ -209,7 +299,7 @@ cMaRN0UdBBkAAAAASUVORK5CYII= - + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6 JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAACOElE @@ -225,7 +315,7 @@ VnR1MIwhwMTCyqEQ37qEmZVDFF0OE/9nAACtFF4Ey6OP+wAAAABJRU5ErkJggg== - + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go @@ -233,7 +323,7 @@ AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC - + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP @@ -244,7 +334,7 @@ cMaRN0UdBBkAAAAASUVORK5CYII= - + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6 JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAACOElE @@ -260,7 +350,7 @@ VnR1MIwhwMTCyqEQ37qEmZVDFF0OE/9nAACtFF4Ey6OP+wAAAABJRU5ErkJggg== - + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go diff --git a/SCrawler.YouTube/Controls/VideoOptionsForm.vb b/SCrawler.YouTube/Controls/VideoOptionsForm.vb index 534f5ed..e92e677 100644 --- a/SCrawler.YouTube/Controls/VideoOptionsForm.vb +++ b/SCrawler.YouTube/Controls/VideoOptionsForm.vb @@ -47,6 +47,8 @@ Namespace API.YouTube.Controls MyView.SetFormSize() End If + MyYouTubeSettings.DownloadLocations.PopulateComboBox(TXT_FILE) + If Not MyContainer Is Nothing Then With MyContainer Dim i% @@ -299,7 +301,7 @@ Namespace API.YouTube.Controls .FileSetManually = True .UpdateInfoFields() '#If DEBUG Then - ' Debug.WriteLine(.Command(False)) + 'Debug.WriteLine(.Command(False)) '#End If Else If OPT_AUDIO.Checked Then @@ -312,6 +314,7 @@ Namespace API.YouTube.Controls End With If MyYouTubeSettings.OutputPathAutoChange Then MyYouTubeSettings.OutputPath.Value = f + If MyDownloaderSettings.OutputPathAutoAddPaths Then MyYouTubeSettings.DownloadLocations.Add(f, False) DialogResult = DialogResult.OK Close() @@ -430,7 +433,7 @@ Namespace API.YouTube.Controls End Sub #End Region #Region "Footer" - Private Sub BTT_BROWSE_Click(sender As Object, e As EventArgs) Handles BTT_BROWSE.Click + Private Sub BTT_BROWSE_MouseClick(sender As Object, e As MouseEventArgs) Handles BTT_BROWSE.MouseClick Dim f As SFile #Disable Warning BC40000 If MyContainer.HasElements Then @@ -444,7 +447,13 @@ Namespace API.YouTube.Controls f = SFile.SaveAs(f, "Select the destination of the video file",,, sPattern, EDP.ReturnValue) End If #Enable Warning - If Not f.IsEmptyString Then TXT_FILE.Text = f + If Not f.IsEmptyString Then + If e.Button = MouseButtons.Right Then + MyYouTubeSettings.DownloadLocations.Add(f, MyDownloaderSettings.OutputPathAskForName) + MyYouTubeSettings.DownloadLocations.PopulateComboBox(TXT_FILE, f) + End If + TXT_FILE.Text = f + End If End Sub #End Region #End Region diff --git a/SCrawler.YouTube/Downloader/DownloadLocationsCollection.vb b/SCrawler.YouTube/Downloader/DownloadLocationsCollection.vb new file mode 100644 index 0000000..ba15afd --- /dev/null +++ b/SCrawler.YouTube/Downloader/DownloadLocationsCollection.vb @@ -0,0 +1,204 @@ +' Copyright (C) 2023 Andy https://github.com/AAndyProgram +' 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.Functions.XML +Imports PersonalUtilities.Functions.XML.Base +Imports PersonalUtilities.Functions.XML.Attributes +Imports PersonalUtilities.Forms.Controls +Imports PersonalUtilities.Forms.Controls.Base +Imports PersonalUtilities.Tools +Namespace DownloadObjects.STDownloader + Public Structure DownloadLocation : Implements IComparable(Of DownloadLocation), IEquatable(Of DownloadLocation), IEContainerProvider + Public Name As String + Public Path As String + + Public Model As Integer + ''' with separator + Public Sub New(ByVal Path As String) + Me.New(Path, -1) + End Sub + ''' + Public Sub New(ByVal Path As String, ByVal Model As Integer) + Me.Path = Path + Me.Model = Model + End Sub + Public Shared Widening Operator CType(ByVal Path As String) As DownloadLocation + Return New DownloadLocation(Path) + End Operator + Public Shared Narrowing Operator CType(ByVal Path As SFile) As DownloadLocation + Return New DownloadLocation(Path.PathWithSeparator) + End Operator + Public Shared Widening Operator CType(ByVal Location As DownloadLocation) As String + Return Location.Path + End Operator + Public Overrides Function ToString() As String + Return Path + End Function + Public Overloads Overrides Function Equals(ByVal Obj As Object) As Boolean + If Not IsNothing(Obj) Then + If TypeOf Obj Is DownloadLocation Then + Return Equals(DirectCast(Obj, DownloadLocation)) + Else + Return Obj.ToString = Path + End If + Else + Return False + End If + End Function + Public Overloads Function Equals(ByVal Other As DownloadLocation) As Boolean Implements IEquatable(Of DownloadLocation).Equals + Return Path = Other.Path And Model = Other.Model + End Function + Private Function ToEContainer(Optional ByVal e As ErrorsDescriber = Nothing) As EContainer Implements IEContainerProvider.ToEContainer + Return XMLGenerateContainers(Me).FirstOrDefault + End Function + Private Function CompareTo(ByVal Other As DownloadLocation) As Integer Implements IComparable(Of DownloadLocation).CompareTo + Return Name.CompareTo(Other.Name) + End Function + End Structure + Public Class DownloadLocationsCollection : Implements ICollection(Of DownloadLocation), IMyEnumerator(Of DownloadLocation) + Private ReadOnly Property Locations As List(Of DownloadLocation) + Private WorkingFile As SFile + Public Sub New() + Locations = New List(Of DownloadLocation) + End Sub + Public Sub Load(ByVal IsGlobal As Boolean, Optional ByVal IsYT As Boolean = False, Optional ByVal File As SFile = Nothing) + If Not IsGlobal Then + WorkingFile = $"Settings\DownloadLocations{IIf(IsYT, "YouTube", String.Empty)}.xml" + ElseIf Not File.IsEmptyString Then + WorkingFile = File + Else + Throw New ArgumentNullException("File", "File cannot be null in global locations instance") + End If + If WorkingFile.Exists Then + Using x As New XmlFile(WorkingFile, Protector.Modes.All, False) With {.AllowSameNames = True} + x.LoadData() + Locations.ListAddList(x.XMLGenerateInstances(Of DownloadLocation), LAP.NotContainsOnly) + End Using + End If + End Sub + Private ReadOnly Property IsReadOnly As Boolean = False Implements ICollection(Of DownloadLocation).IsReadOnly + Public ReadOnly Property Count As Integer Implements ICollection(Of DownloadLocation).Count, IMyEnumerator(Of DownloadLocation).MyEnumeratorCount + Get + Return Locations.Count + End Get + End Property + Default Public ReadOnly Property Item(ByVal Index As Integer) As DownloadLocation Implements IMyEnumerator(Of DownloadLocation).MyEnumeratorObject + Get + Return Locations(Index) + End Get + End Property + Public Shared Sub AddCmbColumns(ByRef CMB As ComboBoxExtended, Optional ByVal UseUpdate As Boolean = True) + With CMB + If UseUpdate Then .BeginUpdate() + With .Columns + .Clear() + .Add(New ListColumn("COL_NAME", "Name") With {.DisplayMember = False, .ValueMember = False, .AutoWidth = True, .Width = -1}) + .Add(New ListColumn("COL_VALUE", "Value") With {.DisplayMember = True, .ValueMember = True, .Visible = False}) + End With + If UseUpdate Then .EndUpdate(True) + End With + End Sub + Public Sub PopulateComboBox(ByRef CMB As ComboBoxExtended, Optional ByVal Current As SFile = Nothing) + Locations.Sort() + With CMB + .BeginUpdate() + + If .Columns.Count = 0 Then AddCmbColumns(CMB, False) + + .Items.Clear() + + If Count > 0 Then + .Items.AddRange(Locations.Select(Function(l) New ListItem({l.Name, l.Path}))) + .LeaveDefaultButtons = True + Else + .LeaveDefaultButtons = False + End If + + .ListAutoCompleteMode = ComboBoxExtended.AutoCompleteModes.Disabled + + .EndUpdate() + + If Not Current.IsEmptyString And Locations.Count > 0 Then + Dim i% = IndexOf(Current.PathWithSeparator) + If i.ValueBetween(0, .Items.Count - 1) Then .SelectedIndex = i + If Current.File.IsEmptyString Then CMB.Text = Current.PathWithSeparator Else CMB.Text = Current + End If + End With + End Sub + Public Function ChooseNewLocation(ByRef CMB As ComboBoxExtended, ByVal AddToList As Boolean, ByVal AskForName As Boolean) As SFile + Dim f As SFile = SFile.SelectPath(CMB.Text.CSFileP, "Select output directory", EDP.ReturnValue) + If Not f.IsEmptyString Then + CMB.Text = f.PathWithSeparator + If AddToList Then + Add(New DownloadLocation(f.PathWithSeparator), AskForName) + PopulateComboBox(CMB, f) + End If + End If + Return f + End Function + Private Sub Update() + If Locations.Count > 0 Then + Using x As New XmlFile With {.AllowSameNames = True} + x.AddRange(Locations) + x.Name = "Locations" + x.Save(WorkingFile, EDP.SendToLog) + End Using + Else + WorkingFile.Delete(,, EDP.None) + End If + End Sub + Public Sub Clear() Implements ICollection(Of DownloadLocation).Clear + If Locations.Count > 0 Then Locations.Clear() : Update() + End Sub + Public Overloads Sub Add(ByVal Item As DownloadLocation) Implements ICollection(Of DownloadLocation).Add + Add(Item, True) + End Sub + Public Overloads Sub Add(ByVal Item As DownloadLocation, ByVal AskForName As Boolean) + If Not Item.Path.IsEmptyString Then + Dim i% = IndexOf(Item) + Dim processUpdate As Boolean = True + If i >= 0 Then + If Locations(i).Model = Item.Model Then + processUpdate = False + Else + Locations(i) = Item + End If + Else + If Item.Name.IsEmptyString And AskForName Then Item.Name = InputBoxE("Enter a new name for the new location", "Location name", Item.Path) + If Item.Name.IsEmptyString Then Item.Name = Item.Path + Locations.Add(Item) + Locations.Sort() + End If + If processUpdate Then Update() + End If + End Sub + Private Sub CopyTo(ByVal Array() As DownloadLocation, ByVal ArrayIndex As Integer) Implements ICollection(Of DownloadLocation).CopyTo + Locations.CopyTo(Array, ArrayIndex) + End Sub + Public Function Contains(ByVal Item As DownloadLocation) As Boolean Implements ICollection(Of DownloadLocation).Contains + Return Not Item.Path.IsEmptyString AndAlso Locations.Contains(Item) + End Function + Public Function IndexOf(ByVal Item As DownloadLocation, Optional ByVal IgnoreModel As Boolean = False) As Integer + Return Locations.FindIndex(Function(d) d.Path = Item.Path And (d.Model = Item.Model Or IgnoreModel)) + End Function + Public Function Remove(ByVal Item As DownloadLocation) As Boolean Implements ICollection(Of DownloadLocation).Remove + If Locations.Remove(Item) Then + Update() + Return True + Else + Return False + End If + End Function + Private Function GetEnumerator() As IEnumerator(Of DownloadLocation) Implements IEnumerable(Of DownloadLocation).GetEnumerator + Return New MyEnumerator(Of DownloadLocation)(Me) + End Function + Private Function IEnumerable_GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator + Return GetEnumerator() + End Function + End Class +End Namespace diff --git a/SCrawler.YouTube/Downloader/IDownloaderSettings.vb b/SCrawler.YouTube/Downloader/IDownloaderSettings.vb index 14ae81f..d4e5299 100644 --- a/SCrawler.YouTube/Downloader/IDownloaderSettings.vb +++ b/SCrawler.YouTube/Downloader/IDownloaderSettings.vb @@ -16,5 +16,11 @@ Namespace DownloadObjects.STDownloader ReadOnly Property OnItemDoubleClick As DoubleClickBehavior ReadOnly Property OpenFolderInOtherProgram As Boolean ReadOnly Property OpenFolderInOtherProgram_Command As String + ReadOnly Property OutputPathAskForName As Boolean + ReadOnly Property OutputPathAutoAddPaths As Boolean + ReadOnly Property ENVIR_FFMPEG As SFile + ReadOnly Property ENVIR_YTDLP As SFile + ReadOnly Property ENVIR_GDL As SFile + ReadOnly Property ENVIR_CURL As SFile End Interface End Namespace \ No newline at end of file diff --git a/SCrawler.YouTube/Downloader/VideoListForm.Designer.vb b/SCrawler.YouTube/Downloader/VideoListForm.Designer.vb index c76cf4a..882fcac 100644 --- a/SCrawler.YouTube/Downloader/VideoListForm.Designer.vb +++ b/SCrawler.YouTube/Downloader/VideoListForm.Designer.vb @@ -47,6 +47,7 @@ Namespace DownloadObjects.STDownloader Me.BTT_LOG = New System.Windows.Forms.ToolStripButton() Me.BTT_INFO = New System.Windows.Forms.ToolStripButton() Me.BTT_DONATE = New System.Windows.Forms.ToolStripButton() + Me.BTT_BUG_REPORT = New System.Windows.Forms.ToolStripButton() SEP_2 = New System.Windows.Forms.ToolStripSeparator() SEP_3 = New System.Windows.Forms.ToolStripSeparator() MENU_ADD_SEP_1 = New System.Windows.Forms.ToolStripSeparator() @@ -104,7 +105,7 @@ Namespace DownloadObjects.STDownloader 'TOOLBAR_TOP ' Me.TOOLBAR_TOP.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden - Me.TOOLBAR_TOP.Items.AddRange(New System.Windows.Forms.ToolStripItem() {Me.BTT_SETTINGS, Me.SEP_1, Me.MENU_ADD, SEP_2, Me.BTT_DOWN, Me.BTT_STOP, SEP_3, Me.BTT_DELETE, Me.BTT_CLEAR_DONE, Me.BTT_CLEAR_ALL, Me.SEP_LOG, Me.BTT_LOG, Me.BTT_INFO, Me.BTT_DONATE}) + Me.TOOLBAR_TOP.Items.AddRange(New System.Windows.Forms.ToolStripItem() {Me.BTT_SETTINGS, Me.SEP_1, Me.MENU_ADD, SEP_2, Me.BTT_DOWN, Me.BTT_STOP, SEP_3, Me.BTT_DELETE, Me.BTT_CLEAR_DONE, Me.BTT_CLEAR_ALL, Me.SEP_LOG, Me.BTT_LOG, Me.BTT_INFO, Me.BTT_DONATE, Me.BTT_BUG_REPORT}) Me.TOOLBAR_TOP.Location = New System.Drawing.Point(0, 0) Me.TOOLBAR_TOP.Name = "TOOLBAR_TOP" Me.TOOLBAR_TOP.Size = New System.Drawing.Size(584, 25) @@ -262,6 +263,16 @@ Namespace DownloadObjects.STDownloader Me.BTT_DONATE.Size = New System.Drawing.Size(23, 22) Me.BTT_DONATE.ToolTipText = "Support" ' + 'BTT_BUG_REPORT + ' + Me.BTT_BUG_REPORT.Alignment = System.Windows.Forms.ToolStripItemAlignment.Right + Me.BTT_BUG_REPORT.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image + Me.BTT_BUG_REPORT.Image = Global.SCrawler.My.Resources.Resources.MailPic_16 + Me.BTT_BUG_REPORT.ImageTransparentColor = System.Drawing.Color.Magenta + Me.BTT_BUG_REPORT.Name = "BTT_BUG_REPORT" + Me.BTT_BUG_REPORT.Size = New System.Drawing.Size(23, 22) + Me.BTT_BUG_REPORT.Text = "Bug report" + ' 'VideoListForm ' Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!) @@ -305,5 +316,6 @@ Namespace DownloadObjects.STDownloader Protected WithEvents BTT_ADD_SHORTS_ONLY As PersonalUtilities.Forms.Controls.KeyClick.ToolStripMenuItemKeyClick Protected WithEvents MENU_ADD As ToolStripDropDownButton Protected WithEvents BTT_DOWN As ToolStripButton + Private WithEvents BTT_BUG_REPORT As ToolStripButton End Class End Namespace \ No newline at end of file diff --git a/SCrawler.YouTube/Downloader/VideoListForm.resx b/SCrawler.YouTube/Downloader/VideoListForm.resx index 79365c2..5c0502f 100644 --- a/SCrawler.YouTube/Downloader/VideoListForm.resx +++ b/SCrawler.YouTube/Downloader/VideoListForm.resx @@ -136,88 +136,46 @@ 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 @@ -236,122 +194,164 @@ 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 - 4dDtOAAAAABJRU5ErkJggg== + YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAVESURBVEhLjZVrTJNXGMcLQmdHO6AdarLSOcQBQgv0raV1 + XnBcpGjBlgo6RtSMiisoXlCiGI3RxMVLsmQftg/bhy0zmXFLdFk2JoyaOYFMGKiwlIsOaym9F152SRbo + 2f+UVmcsG0/yy9v3nOf8n3+fc9735dDoLyqKM6ekfHxz2bKRGxLJR5vF4lcwHBOc/J8YVipVv0kkt0aX + Lu27lppqwFAciApO0hgwmfh3ioq+HG5uJmOXLpHepqbADZWqI08gWInp2LmsyGFhmLX2HTsesefPk+mL + F8lIZaX3g/T0ekl8fCKm54p0rFjx4dChQ+Th6dNk7NQpYkOy9ehR0qpS3ZIJBK8jJWKRUbX6TYhb2QsX + CIu1bEsLmT55kgxptb6vxeL3kSIEMZybIpGNij9saiKPjh8nNiRNnDtH7jc3B24olT9JBYI0JD5TBG0p + tldV2ahzKj4NcfbIETINY/6aGmJOShpEmgoIOO3JyZdHamvJGApYge3YMWLHAufZs2QQ7WpXqbqy+Px0 + JAeLDMvlxROVlbap994jLMxMwxSLddMHD5LJvXtJq1Lp3ikSXUVqKRBy9MuXv9qqUNzq2b078BhJ42AC + bpzYEw9a5jp8mNxEkVw+P+MXhikaNxjsU2fOBFvCwjGL9rKNjWQK4ubcXG8aj/cZhI1ABuiGc2IZkSgd + RbqsO3cS+759xNHQQJwHDhA3ivkgZGloCHSr1XcflJfb2BMngo5ZzLHIZevrn4hn8HiXoVcLMkPi0SAY + 3FyhMON7ubzzXnV1wFFXR1zAYzIRLwT8KObfv59Mwe0UFcY9i/ugONrbqVD4ZHw+FafOV4HF4OlRDQVX + KRSuas/O7rJv20Zcu3YRD/AZjcQPh5MoOIkNnCwvJ/6NG4lfrSZe0JmX588RCK5g/R5AnfPAc+Lh4K5O + TMxsl8m6BtaunfFu3048Gg3xMAxxi8XELRAQd2wscS9aRCx8fqAtJcWnSkigG1oHssCLYF7xcATbdUcm + 65+AoANiTuACVDhITAzpy87+y5Ce/i3y3wVSsCDxYNhrakx2tdrq4PGeFwd0bCA1dcas1T7ISE4ux5J4 + sDBxv9F42F1Q4HbA/Xzi4+ARsGZmkj6dbvCtNWtysPQ/XyvB8BiNLa5NmzyOuLjnxF3oPW0XFR8DI9HR + ZAB05+QE+isqfjWoVLRN8xfx7dlz3FlS4o3k3CUSkcclJX8PpqUFqPhwSLwXdIIfGWa2n/6T+YrgKLY4 + Cgp8Dj4/KPxMW5YsIZbS0t/PrF/f3btly4Q1I4Pch2gPuE3FgZmyevXszwbD3QKZjL4gn77qvbW1zVTc + GUk8KYlYNmz4o55h2pHaUJKV9fZgWdkDC3ofFu8AbaCVFlEqZ7srKno/1WrpxnNBFMe7davNmZAQ2TnE + 6+TyNiSaAN1I4YH8/Df69PrRu1LpE/HvwDfgOuiXSmd69Poh5C4HXI5Dp7N4JZKIzk0M8wOS6kPi4S8V + 92BhoaqvomK0XSoN/Fv8q6go0pOVNWPWaMaRVwQSOd3V1TsmNBqPB6Jh5wPFxX/uiyweDm5jfn4eNnbk + TnY2uRYSN+flzX6h0TwuXbnyE+TMFUAsbqusPGErK/MNqVQB6rxRoejA+Hzi4eAa162T03a0YZ1ZrZ69 + Cucv8XgXMacDcy1CRDEpKfG9ev3nD8vKXFcKC3H6ohsxzgA+nadJ8wT3elXVO/d0uvHbWq27JC2Nfgv0 + QAzocX2ylv5IAAqwOXRd6OP/AqBHk56cLeA1EPGBWwSoY/qxpld6v5CgJuj7/+UQtGDIGIfzD+o72WmD + vfrkAAAAAElFTkSuQmCC iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 - YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAVDSURBVEhLjZVtTFNXGMcLQmdHO6AdarLSOcTxXqC3lFs3 - FceLFC3YgqBjRM2ouILiC0oUozGauPiSLNmH7cP2YctMZtwSXdzGhFE3J5ABAg4XBHRYS+l74bKXZJGe - /U9pdcay8SS/3N5znvN//n3OuffyaAwUFkaZEhI+ur5s2eg1mezDDVLpSxiO8E/+T4yoVOxvMtmNsaVL - +y8nJlZgKAqE+SdpDBmNwp7Cwi9GmpvJ+PnzpLOpyXeNZTtyRaKVmI6cywodwwzzunXr1gfcmTNk5tw5 - MlpZ6X4/ObleFh0di+m5Ih0rVnxwd/9+cv/ECTJ+/DixINl86BBpZdkbcpHoVaSELDKmVr8BcTN39izh - sJZraSEzx46Ru1qt5yup9D2kiEEE77pEYqHi95uayIMjR4gFSZOnT5Pe5mbfNZXqpwyRKAmJTxVBW4qs - VVUW6pyKz0CcO3iQzMCYt6aGmOLi7iCNBSJee3z8hdHaWjKOAmZgOXyYWLHAfuoUuYV2tbNsV7pQmIxk - f5ERhaJosrLSMv3uu4SDmRmY4rBuZt8+MrVrF2lVqZzbJJJLSC0BYp5++fKXW5XKGzd37PA9RNIEmIQb - O/bEhZY5Dhwg11EkWyhMucUwhRMVFdbpkyf9LeHgmEN7ucZGMg1xU3a2O0kg+BTCBiAHdMN5kYxEkowi - XeZt24h1925ia2gg9r17iRPFPBAabGjwdavVg/fKyizc0aN+xxzmOORy9fWPxVMEggvQqwVpAfFw4A9+ - tlic8p1C0dlTXe2z1dURB3AZjcQNAS+KeffsIdNwO02Fcc/h3i+O9nYqlR65UEjFqfNUsBg8OaqB4KvE - 4tT2zMwu6+bNxLF9O3EBj8FAvHA4hYJT2MCpsjLiXbeOeNVq4gadubneLJHoItbvBNS5ADwjHgx+Tmxs - Wrtc3jWUk/PIvWULcWk0xMUwxCmVEqdIRJyRkcS5aBEZFAp9bQkJHjYmhm5oHUgHz4N5xYPhb1ePXD4w - CUEbxOzAAaiwn4gI0p+Z+VdFcvI3yH8HZIAFifvDWlNjtKrVZptA8Kw4oGNDiYmPTFrtvZT4+DIsiQYL - E/caDAec+flOG9zPJz4BHgBzWhrp1+nuvLlqVRaW/udrxR8ug6HFsX69yxYV9Yy4A72n7aLi42A0PJwM - gR+ysnwD5eW/VrAsbdP8RTw7dx6xFxe7Qzl3SCTkYXHx37eSknxUfCQg3gc6gYlhZgfoP5mvCI5iiy0/ - 32MTCv3CT7VlyRIyXFLy+8k1a7r7Nm6cNKekkF8g2gtugh9pAUpOzuzPFRWD+XI5fUE+edW7a2ubqbg9 - lHhcHBleu/aPeoZpR2pDcXr6W3dKS+8No/dB8Q7QBlppEZVqtru8vO8TrZZuPB+E8dybNlnsMTGhnUO8 - TqFoQ6IR0I0U783Le61frx8bzMh4LP4tuAqugIHU1Ee9ev1d5C4HfJ5Npxt2y2QhnRsZ5nsk1QfEg18q - /r6CAra/vHzs64wM37/FvwwLI70oYNJoJpBXCGJ53dXVWyc1GpcLokHnQ0VFf+4OLR4MfmNeXi42drQn - M5NcDoibcnNnP9doHpasXPkxcuYKIBa3VVYetZSWem6zrI86b1QqOzA+n3gw+IbVqxW0HVexzqRWz16C - 8xcEgnOY04G5FiHCmISE6D69/rP7paWOiwUFOH3hjRhngJDO06R5gn+lqurt2zrdxE2t1lmclES/BXog - BfS4Pl5Lf8QAJdgQuC708X8O0KNJT85G8AoI+cAtAtQx/VjTK71fSFAT9P3/YgBaMGCMx/sHyjLY+hqD - P/QAAAAASUVORK5CYII= + YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAVCSURBVEhLjZVtTFNXGMcLQmdHO6AdarLSOcTxXqC3ltZN + xfEiRQu2VNAxomZUXEHxBSWK0RhNNIrJkn3YPmwftsxkxi3RzWxMGCVTgYiMsuGCgA5rKX0vXPaSLNKz + /ymtzlg2nuSX23vOc/7Pv885914ODXNRUYwpKenjrmXLRq9LJB9tFItfwXBUYPJ/YkShUP4mkdwYW7p0 + 4Epysh5DMSAiMEljyGjk9xUVfTnS3EzGL1wgt5qa/NeVys48gWAlpqPnssLHMMO8adu27SF77hyZaW0l + o5WVng9SU+slsbHxmJ4r0rlixYf3DhwgD06eJOMnThArki2HD5M2pfKGVCB4HSlhi4ypVG9B3MKeP09Y + rGVbWsjM8ePknkbj/Vosfh8pQhDF6RKJrFT8QVMTeXj0KLEiafLMGdLX3Oy/rlDczBIIUpD4TBG0pdhW + VWWlzqn4DMTZQ4fIDIz5amqIKSHhLtKUQMDpSEy8OFpbS8ZRwAKsR44QGxY4Tp8m/WhXh1LZk8nnpyI5 + UGREJiuerKy0Tp89S1iYmYEpFutm9u8nU7t3kzaFwrVdJLqM1FIg5OiWL3+1TS6/cXPnTv8jJE2ASbhx + YE/caJnz4EHShSK5fH7aTwxTNKHX26ZPnQq0hIVjFu1lGxvJNMRNubmeFB7vMwgbgBTQDedEMyJRKor0 + WLZvJ7Y9e4i9oYE49u0jLhTzQsjc0ODvVakG75eXW9ljxwKOWcyxyGXr65+Ip/F4F6FXCzKC4pEgENxc + oTDte5ms+3Z1td9eV0ecwG00Eg8EfCjm27uXTMPtNBXGPYv7gDja2y2Xe6V8PhWnztPBYvD0qAaDqxAK + 0zuys3tsW7YQ544dxA28BgPxweEUCk5hA6fKy4lv/XriU6mIB3Tn5flyBIJLWL8LUOc88Jx4KLir4uMz + OqTSniG5/LFn61biVquJm2GISywmLoGAuKKjiWvRImLm8/3tSUleZVwc3dA6kAleBPOKhyLQrj6p1DwJ + QTvEHMAJqHCAqCgykJ39lz419VvkvweywILEA2GrqTHaVCqLncd7XhzQsaHk5McmjeZ+WmJiOZbEgoWJ + +wyGg66CApcd7ucTnwAPgSUjgwxotXffXr06B0v/87USCLfB0OLcsMFtj4l5TtyJ3tN2UfFxMBoZSYZA + V06O31xR8ateqaRtmr+Id9euo46SEk84506RiDwqKfm7PyXFT8VHguL9oBuYGGbWTP/JfEVwFFvsBQVe + O58fEH6mLUuWkOHS0t9PrV3b279p06QlLY38AtE74Bb4kRagrFo1e1uvHyyQSukL8umr3lNb20zFHeHE + ExLI8Lp1f9QzTAdSG0oyM9+5W1Z2fxi9D4l3gnbQRosoFLO9FRX9n2o0dOO5IILj2bzZ6oiLC+8c4nUy + WTsSjYBupHBffv4bAzrd2GBW1hPx78A1cBWY09Mf39Hp7iF3OeBy7FrtsEciCevcyDA/IKk+KB76UnH3 + FxYqByoqxq5lZfn/Lf5VRAS5gwImtXoCeUUgntNbXb1tUq12uyEacj5UXPznnvDioeA25ufnYWNH+7Kz + yZWguCkvb/YLtfpR6cqVnyBnrgBicXtl5TFrWZl3UKn0U+eNcnknxucTDwXXsGaNjLbjG6wzqVSzl+H8 + JR6vFXNaMNciRASTlBTbr9N9/qCszHmpsBCnL7IR4wzg03maNE9wr1ZVvfuzVjtxS6NxlaSk0G+BDogB + Pa5P1tIfcUAONgavC338XwD0aNKTswm8BsI+cIsAdUw/1vRK7xcS1AR9/78chBYMGuNw/gGBHdjskDgc + +QAAAABJRU5ErkJggg== iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 - YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAVDSURBVEhLjZVtTFNXGMcLQmdHO6AdarLSOcTxXqC3lFs3 - FceLFC3YgqBjRM2ouILiC0oUozGauPiSLNmH7cP2YctMZtwSXdzGhFE3J5ABAg4XBHRYS+l74bKXZJGe - /U9pdcay8SS/3N5znvN//n3OuffyaAwUFkaZEhI+ur5s2eg1mezDDVLpSxiO8E/+T4yoVOxvMtmNsaVL - +y8nJlZgKAqE+SdpDBmNwp7Cwi9GmpvJ+PnzpLOpyXeNZTtyRaKVmI6cywodwwzzunXr1gfcmTNk5tw5 - MlpZ6X4/ObleFh0di+m5Ih0rVnxwd/9+cv/ECTJ+/DixINl86BBpZdkbcpHoVaSELDKmVr8BcTN39izh - sJZraSEzx46Ru1qt5yup9D2kiEEE77pEYqHi95uayIMjR4gFSZOnT5Pe5mbfNZXqpwyRKAmJTxVBW4qs - VVUW6pyKz0CcO3iQzMCYt6aGmOLi7iCNBSJee3z8hdHaWjKOAmZgOXyYWLHAfuoUuYV2tbNsV7pQmIxk - f5ERhaJosrLSMv3uu4SDmRmY4rBuZt8+MrVrF2lVqZzbJJJLSC0BYp5++fKXW5XKGzd37PA9RNIEmIQb - O/bEhZY5Dhwg11EkWyhMucUwhRMVFdbpkyf9LeHgmEN7ucZGMg1xU3a2O0kg+BTCBiAHdMN5kYxEkowi - XeZt24h1925ia2gg9r17iRPFPBAabGjwdavVg/fKyizc0aN+xxzmOORy9fWPxVMEggvQqwVpAfFw4A9+ - tlic8p1C0dlTXe2z1dURB3AZjcQNAS+KeffsIdNwO02Fcc/h3i+O9nYqlR65UEjFqfNUsBg8OaqB4KvE - 4tT2zMwu6+bNxLF9O3EBj8FAvHA4hYJT2MCpsjLiXbeOeNVq4gadubneLJHoItbvBNS5ADwjHgx+Tmxs - Wrtc3jWUk/PIvWULcWk0xMUwxCmVEqdIRJyRkcS5aBEZFAp9bQkJHjYmhm5oHUgHz4N5xYPhb1ePXD4w - CUEbxOzAAaiwn4gI0p+Z+VdFcvI3yH8HZIAFifvDWlNjtKrVZptA8Kw4oGNDiYmPTFrtvZT4+DIsiQYL - E/caDAec+flOG9zPJz4BHgBzWhrp1+nuvLlqVRaW/udrxR8ug6HFsX69yxYV9Yy4A72n7aLi42A0PJwM - gR+ysnwD5eW/VrAsbdP8RTw7dx6xFxe7Qzl3SCTkYXHx37eSknxUfCQg3gc6gYlhZgfoP5mvCI5iiy0/ - 32MTCv3CT7VlyRIyXFLy+8k1a7r7Nm6cNKekkF8g2gtugh9pAUpOzuzPFRWD+XI5fUE+edW7a2ubqbg9 - lHhcHBleu/aPeoZpR2pDcXr6W3dKS+8No/dB8Q7QBlppEZVqtru8vO8TrZZuPB+E8dybNlnsMTGhnUO8 - TqFoQ6IR0I0U783Le61frx8bzMh4LP4tuAqugIHU1Ee9ev1d5C4HfJ5Npxt2y2QhnRsZ5nsk1QfEg18q - /r6CAra/vHzs64wM37/FvwwLI70oYNJoJpBXCGJ53dXVWyc1GpcLokHnQ0VFf+4OLR4MfmNeXi42drQn - M5NcDoibcnNnP9doHpasXPkxcuYKIBa3VVYetZSWem6zrI86b1QqOzA+n3gw+IbVqxW0HVexzqRWz16C - 8xcEgnOY04G5FiHCmISE6D69/rP7paWOiwUFOH3hjRhngJDO06R5gn+lqurt2zrdxE2t1lmclES/BXog - BfS4Pl5Lf8QAJdgQuC708X8O0KNJT85G8AoI+cAtAtQx/VjTK71fSFAT9P3/YgBaMGCMx/sHyjLY+hqD - P/QAAAAASUVORK5CYII= + YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAVCSURBVEhLjZVtTFNXGMcLQmdHO6AdarLSOcTxXqC3ltZN + xfEiRQu2VNAxomZUXEHxBSWK0RhNNIrJkn3YPmwftsxkxi3RzWxMGCVTgYiMsuGCgA5rKX0vXPaSLNKz + /ymtzlg2nuSX23vOc/7Pv885914ODXNRUYwpKenjrmXLRq9LJB9tFItfwXBUYPJ/YkShUP4mkdwYW7p0 + 4Epysh5DMSAiMEljyGjk9xUVfTnS3EzGL1wgt5qa/NeVys48gWAlpqPnssLHMMO8adu27SF77hyZaW0l + o5WVng9SU+slsbHxmJ4r0rlixYf3DhwgD06eJOMnThArki2HD5M2pfKGVCB4HSlhi4ypVG9B3MKeP09Y + rGVbWsjM8ePknkbj/Vosfh8pQhDF6RKJrFT8QVMTeXj0KLEiafLMGdLX3Oy/rlDczBIIUpD4TBG0pdhW + VWWlzqn4DMTZQ4fIDIz5amqIKSHhLtKUQMDpSEy8OFpbS8ZRwAKsR44QGxY4Tp8m/WhXh1LZk8nnpyI5 + UGREJiuerKy0Tp89S1iYmYEpFutm9u8nU7t3kzaFwrVdJLqM1FIg5OiWL3+1TS6/cXPnTv8jJE2ASbhx + YE/caJnz4EHShSK5fH7aTwxTNKHX26ZPnQq0hIVjFu1lGxvJNMRNubmeFB7vMwgbgBTQDedEMyJRKor0 + WLZvJ7Y9e4i9oYE49u0jLhTzQsjc0ODvVakG75eXW9ljxwKOWcyxyGXr65+Ip/F4F6FXCzKC4pEgENxc + oTDte5ms+3Z1td9eV0ecwG00Eg8EfCjm27uXTMPtNBXGPYv7gDja2y2Xe6V8PhWnztPBYvD0qAaDqxAK + 0zuys3tsW7YQ544dxA28BgPxweEUCk5hA6fKy4lv/XriU6mIB3Tn5flyBIJLWL8LUOc88Jx4KLir4uMz + OqTSniG5/LFn61biVquJm2GISywmLoGAuKKjiWvRImLm8/3tSUleZVwc3dA6kAleBPOKhyLQrj6p1DwJ + QTvEHMAJqHCAqCgykJ39lz419VvkvweywILEA2GrqTHaVCqLncd7XhzQsaHk5McmjeZ+WmJiOZbEgoWJ + +wyGg66CApcd7ucTnwAPgSUjgwxotXffXr06B0v/87USCLfB0OLcsMFtj4l5TtyJ3tN2UfFxMBoZSYZA + V06O31xR8ateqaRtmr+Id9euo46SEk84506RiDwqKfm7PyXFT8VHguL9oBuYGGbWTP/JfEVwFFvsBQVe + O58fEH6mLUuWkOHS0t9PrV3b279p06QlLY38AtE74Bb4kRagrFo1e1uvHyyQSukL8umr3lNb20zFHeHE + ExLI8Lp1f9QzTAdSG0oyM9+5W1Z2fxi9D4l3gnbQRosoFLO9FRX9n2o0dOO5IILj2bzZ6oiLC+8c4nUy + WTsSjYBupHBffv4bAzrd2GBW1hPx78A1cBWY09Mf39Hp7iF3OeBy7FrtsEciCevcyDA/IKk+KB76UnH3 + FxYqByoqxq5lZfn/Lf5VRAS5gwImtXoCeUUgntNbXb1tUq12uyEacj5UXPznnvDioeA25ufnYWNH+7Kz + yZWguCkvb/YLtfpR6cqVnyBnrgBicXtl5TFrWZl3UKn0U+eNcnknxucTDwXXsGaNjLbjG6wzqVSzl+H8 + JR6vFXNaMNciRASTlBTbr9N9/qCszHmpsBCnL7IR4wzg03maNE9wr1ZVvfuzVjtxS6NxlaSk0G+BDogB + Pa5P1tIfcUAONgavC338XwD0aNKTswm8BsI+cIsAdUw/1vRK7xcS1AR9/78chBYMGuNw/gGBHdjskDgc + +QAAAABJRU5ErkJggg== iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 - YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAVDSURBVEhLjZVtTFNXGMcLQmdHO6AdarLSOcTxXqC3lFs3 - FceLFC3YgqBjRM2ouILiC0oUozGauPiSLNmH7cP2YctMZtwSXdzGhFE3J5ABAg4XBHRYS+l74bKXZJGe - /U9pdcay8SS/3N5znvN//n3OuffyaAwUFkaZEhI+ur5s2eg1mezDDVLpSxiO8E/+T4yoVOxvMtmNsaVL - +y8nJlZgKAqE+SdpDBmNwp7Cwi9GmpvJ+PnzpLOpyXeNZTtyRaKVmI6cywodwwzzunXr1gfcmTNk5tw5 - MlpZ6X4/ObleFh0di+m5Ih0rVnxwd/9+cv/ECTJ+/DixINl86BBpZdkbcpHoVaSELDKmVr8BcTN39izh - sJZraSEzx46Ru1qt5yup9D2kiEEE77pEYqHi95uayIMjR4gFSZOnT5Pe5mbfNZXqpwyRKAmJTxVBW4qs - VVUW6pyKz0CcO3iQzMCYt6aGmOLi7iCNBSJee3z8hdHaWjKOAmZgOXyYWLHAfuoUuYV2tbNsV7pQmIxk - f5ERhaJosrLSMv3uu4SDmRmY4rBuZt8+MrVrF2lVqZzbJJJLSC0BYp5++fKXW5XKGzd37PA9RNIEmIQb - O/bEhZY5Dhwg11EkWyhMucUwhRMVFdbpkyf9LeHgmEN7ucZGMg1xU3a2O0kg+BTCBiAHdMN5kYxEkowi - XeZt24h1925ia2gg9r17iRPFPBAabGjwdavVg/fKyizc0aN+xxzmOORy9fWPxVMEggvQqwVpAfFw4A9+ - tlic8p1C0dlTXe2z1dURB3AZjcQNAS+KeffsIdNwO02Fcc/h3i+O9nYqlR65UEjFqfNUsBg8OaqB4KvE - 4tT2zMwu6+bNxLF9O3EBj8FAvHA4hYJT2MCpsjLiXbeOeNVq4gadubneLJHoItbvBNS5ADwjHgx+Tmxs - Wrtc3jWUk/PIvWULcWk0xMUwxCmVEqdIRJyRkcS5aBEZFAp9bQkJHjYmhm5oHUgHz4N5xYPhb1ePXD4w - CUEbxOzAAaiwn4gI0p+Z+VdFcvI3yH8HZIAFifvDWlNjtKrVZptA8Kw4oGNDiYmPTFrtvZT4+DIsiQYL - E/caDAec+flOG9zPJz4BHgBzWhrp1+nuvLlqVRaW/udrxR8ug6HFsX69yxYV9Yy4A72n7aLi42A0PJwM - gR+ysnwD5eW/VrAsbdP8RTw7dx6xFxe7Qzl3SCTkYXHx37eSknxUfCQg3gc6gYlhZgfoP5mvCI5iiy0/ - 32MTCv3CT7VlyRIyXFLy+8k1a7r7Nm6cNKekkF8g2gtugh9pAUpOzuzPFRWD+XI5fUE+edW7a2ubqbg9 - lHhcHBleu/aPeoZpR2pDcXr6W3dKS+8No/dB8Q7QBlppEZVqtru8vO8TrZZuPB+E8dybNlnsMTGhnUO8 - TqFoQ6IR0I0U783Le61frx8bzMh4LP4tuAqugIHU1Ee9ev1d5C4HfJ5Npxt2y2QhnRsZ5nsk1QfEg18q - /r6CAra/vHzs64wM37/FvwwLI70oYNJoJpBXCGJ53dXVWyc1GpcLokHnQ0VFf+4OLR4MfmNeXi42drQn - M5NcDoibcnNnP9doHpasXPkxcuYKIBa3VVYetZSWem6zrI86b1QqOzA+n3gw+IbVqxW0HVexzqRWz16C - 8xcEgnOY04G5FiHCmISE6D69/rP7paWOiwUFOH3hjRhngJDO06R5gn+lqurt2zrdxE2t1lmclES/BXog - BfS4Pl5Lf8QAJdgQuC708X8O0KNJT85G8AoI+cAtAtQx/VjTK71fSFAT9P3/YgBaMGCMx/sHyjLY+hqD - P/QAAAAASUVORK5CYII= + YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAVCSURBVEhLjZVtTFNXGMcLQmdHO6AdarLSOcTxXqC3ltZN + xfEiRQu2VNAxomZUXEHxBSWK0RhNNIrJkn3YPmwftsxkxi3RzWxMGCVTgYiMsuGCgA5rKX0vXPaSLNKz + /ymtzlg2nuSX23vOc/7Pv885914ODXNRUYwpKenjrmXLRq9LJB9tFItfwXBUYPJ/YkShUP4mkdwYW7p0 + 4Epysh5DMSAiMEljyGjk9xUVfTnS3EzGL1wgt5qa/NeVys48gWAlpqPnssLHMMO8adu27SF77hyZaW0l + o5WVng9SU+slsbHxmJ4r0rlixYf3DhwgD06eJOMnThArki2HD5M2pfKGVCB4HSlhi4ypVG9B3MKeP09Y + rGVbWsjM8ePknkbj/Vosfh8pQhDF6RKJrFT8QVMTeXj0KLEiafLMGdLX3Oy/rlDczBIIUpD4TBG0pdhW + VWWlzqn4DMTZQ4fIDIz5amqIKSHhLtKUQMDpSEy8OFpbS8ZRwAKsR44QGxY4Tp8m/WhXh1LZk8nnpyI5 + UGREJiuerKy0Tp89S1iYmYEpFutm9u8nU7t3kzaFwrVdJLqM1FIg5OiWL3+1TS6/cXPnTv8jJE2ASbhx + YE/caJnz4EHShSK5fH7aTwxTNKHX26ZPnQq0hIVjFu1lGxvJNMRNubmeFB7vMwgbgBTQDedEMyJRKor0 + WLZvJ7Y9e4i9oYE49u0jLhTzQsjc0ODvVakG75eXW9ljxwKOWcyxyGXr65+Ip/F4F6FXCzKC4pEgENxc + oTDte5ms+3Z1td9eV0ecwG00Eg8EfCjm27uXTMPtNBXGPYv7gDja2y2Xe6V8PhWnztPBYvD0qAaDqxAK + 0zuys3tsW7YQ544dxA28BgPxweEUCk5hA6fKy4lv/XriU6mIB3Tn5flyBIJLWL8LUOc88Jx4KLir4uMz + OqTSniG5/LFn61biVquJm2GISywmLoGAuKKjiWvRImLm8/3tSUleZVwc3dA6kAleBPOKhyLQrj6p1DwJ + QTvEHMAJqHCAqCgykJ39lz419VvkvweywILEA2GrqTHaVCqLncd7XhzQsaHk5McmjeZ+WmJiOZbEgoWJ + +wyGg66CApcd7ucTnwAPgSUjgwxotXffXr06B0v/87USCLfB0OLcsMFtj4l5TtyJ3tN2UfFxMBoZSYZA + V06O31xR8ateqaRtmr+Id9euo46SEk84506RiDwqKfm7PyXFT8VHguL9oBuYGGbWTP/JfEVwFFvsBQVe + O58fEH6mLUuWkOHS0t9PrV3b279p06QlLY38AtE74Bb4kRagrFo1e1uvHyyQSukL8umr3lNb20zFHeHE + ExLI8Lp1f9QzTAdSG0oyM9+5W1Z2fxi9D4l3gnbQRosoFLO9FRX9n2o0dOO5IILj2bzZ6oiLC+8c4nUy + WTsSjYBupHBffv4bAzrd2GBW1hPx78A1cBWY09Mf39Hp7iF3OeBy7FrtsEciCevcyDA/IKk+KB76UnH3 + FxYqByoqxq5lZfn/Lf5VRAS5gwImtXoCeUUgntNbXb1tUq12uyEacj5UXPznnvDioeA25ufnYWNH+7Kz + yZWguCkvb/YLtfpR6cqVnyBnrgBicXtl5TFrWZl3UKn0U+eNcnknxucTDwXXsGaNjLbjG6wzqVSzl+H8 + JR6vFXNaMNciRASTlBTbr9N9/qCszHmpsBCnL7IR4wzg03maNE9wr1ZVvfuzVjtxS6NxlaSk0G+BDogB + Pa5P1tIfcUAONgavC338XwD0aNKTswm8BsI+cIsAdUw/1vRK7xcS1AR9/78chBYMGuNw/gGBHdjskDgc + +QAAAABJRU5ErkJggg== diff --git a/SCrawler.YouTube/Downloader/VideoListForm.vb b/SCrawler.YouTube/Downloader/VideoListForm.vb index c25aec2..c7957e1 100644 --- a/SCrawler.YouTube/Downloader/VideoListForm.vb +++ b/SCrawler.YouTube/Downloader/VideoListForm.vb @@ -50,7 +50,11 @@ Namespace DownloadObjects.STDownloader End If If AppMode Then - If Now.Month.ValueBetween(6, 8) Then Text = "SCrawler: Happy LGBT Pride Month! :-)" + If Now.Month.ValueBetween(6, 8) Then + Text = "SCrawler: Happy LGBT Pride Month! :-)" + ElseIf Not MyYouTubeSettings Is Nothing AndAlso Not MyYouTubeSettings.ProgramText.IsEmptyString Then + Text = MyYouTubeSettings.ProgramText + End If MyNotificator = New YTNotificator(Me) MyDownloaderSettings = MyYouTubeSettings End If @@ -64,6 +68,7 @@ Namespace DownloadObjects.STDownloader BTT_LOG.Visible = False BTT_INFO.Visible = False BTT_DONATE.Visible = False + BTT_BUG_REPORT.Visible = False End If MyProgress.Visible = False LoadData() @@ -96,7 +101,7 @@ Namespace DownloadObjects.STDownloader If c.ListExists Then c.Sort(New ContainerDateComparer) SuspendLayout() - For i% = c.Count - 1 To 0 Step -1 : ControlCreateAndAdd(c(i), True, i = 0) : Next + For i% = c.Count - 1 To 0 Step -1 : ControlCreateAndAdd(c(i), True, i = 0, True) : Next ResumeLayout(False) PerformLayout() End If @@ -121,11 +126,11 @@ Namespace DownloadObjects.STDownloader #End Region #Region "Controls" Protected Sub ControlCreateAndAdd(ByVal Container As IYouTubeMediaContainer, Optional ByVal DisableDownload As Boolean = False, - Optional ByVal PerformClick As Boolean = True) + Optional ByVal PerformClick As Boolean = True, Optional ByVal IsLoading As Boolean = False) ControlInvokeFast(TP_CONTROLS, Sub() With TP_CONTROLS .SuspendLayout() - If DisableDownload Or Not MyDownloaderSettings.DownloadAutomatically Then Container.Save() + If Not IsLoading And (DisableDownload Or Not MyDownloaderSettings.DownloadAutomatically) Then Container.Save() '.AutoScroll = True '.HorizontalScroll.Visible = False .RowStyles.Insert(0, New RowStyle(SizeType.Absolute, 60)) @@ -337,13 +342,13 @@ Namespace DownloadObjects.STDownloader MyJob.Cancel() End Sub Private Sub BTT_DELETE_Click(sender As Object, e As EventArgs) Handles BTT_DELETE.Click - RemoveControls(ControlsChecked) + RemoveControls(ControlsChecked, True) End Sub Protected Overridable Sub BTT_CLEAR_DONE_Click(sender As Object, e As EventArgs) Handles BTT_CLEAR_DONE.Click - RemoveControls(ControlsDownloaded) + RemoveControls(ControlsDownloaded, False) End Sub Protected Overridable Sub BTT_CLEAR_ALL_Click(sender As Object, e As EventArgs) Handles BTT_CLEAR_ALL.Click - RemoveControls() + RemoveControls(, False) End Sub Private Sub BTT_LOG_Click(sender As Object, e As EventArgs) Handles BTT_LOG.Click MyMainLOG_ShowForm(DesignXML,,,, AddressOf UpdateLogButton) @@ -351,20 +356,23 @@ Namespace DownloadObjects.STDownloader Friend Sub UpdateLogButton() If AppMode Then MyMainLOG_UpdateLogButton(BTT_LOG, TOOLBAR_TOP) End Sub + Private Sub BTT_BUG_REPORT_Click(sender As Object, e As EventArgs) Handles BTT_BUG_REPORT.Click + Try + With MyYouTubeSettings + Using f As New Editors.BugReporterForm(MyCache, .DesignXml, .ProgramText, My.Application.Info.Version, + True, .Self, .ProgramDescription) : f.ShowDialog() : End Using + End With + Catch + End Try + End Sub Private Sub BTT_DONATE_Click(sender As Object, e As EventArgs) Handles BTT_DONATE.Click Try : Process.Start("https://github.com/AAndyProgram/SCrawler/blob/main/HowToSupport.md") : Catch : End Try End Sub Private Sub BTT_INFO_Click(sender As Object, e As EventArgs) Handles BTT_INFO.Click - Try - MsgBoxE({$"YouTube Downloader v{My.Application.Info.Version}" & vbCr & - $"Address: https://github.com/AAndyProgram/SCrawler" & vbCr & - "Created by Greek LGBT person Andy (Gay)", - "Program information"},,,, - {"OK", New MsgBoxButton("Go to site") With {.CallBack = Sub(r, n, b) Process.Start("https://github.com/AAndyProgram/SCrawler/releases")}}) - Catch - End Try + ShowProgramInfo(MyYouTubeSettings.ProgramText.Value.IfNullOrEmpty("YouTube Downloader"), + My.Application.Info.Version, False, True, MyYouTubeSettings, True,, False, MyYouTubeSettings.ProgramDescription) End Sub - Protected Overloads Sub RemoveControls(Optional ByVal Predicate As Predicate(Of MediaItem) = Nothing) + Protected Overloads Sub RemoveControls(Optional ByVal Predicate As Predicate(Of MediaItem) = Nothing, Optional ByVal RemoveFiles As Boolean = False) ControlInvokeFast(TP_CONTROLS, Sub() With TP_CONTROLS If .Controls.Count > 0 Then @@ -379,7 +387,7 @@ Namespace DownloadObjects.STDownloader For i = rCnt.Count - 1 To 0 Step -1 cnt = .Controls(rCnt(i)) .Controls.RemoveAt(rCnt(i)) - If Not cnt.MyContainer Is Nothing Then cnt.MyContainer.Delete(False) + If Not cnt.MyContainer Is Nothing Then cnt.MyContainer.Delete(RemoveFiles) : cnt.MyContainer.Dispose() cnt.Dispose() Next End If @@ -395,19 +403,24 @@ Namespace DownloadObjects.STDownloader UpdateScrolls(Nothing, Nothing) End Sub, EDP.None) End Sub - Private Overloads Sub RemoveControls(ByVal CNT As MediaItem) + Private Overloads Sub RemoveControls(ByVal CNT As MediaItem, Optional ByVal RemoveFiles As Boolean = False) ControlInvokeFast(TP_CONTROLS, Sub() - If Not CNT Is Nothing Then TP_CONTROLS.Controls.Remove(CNT) : OffsetControls() + If Not CNT Is Nothing Then + If Not CNT.MyContainer Is Nothing Then CNT.MyContainer.Delete(RemoveFiles) : CNT.MyContainer.Dispose() + TP_CONTROLS.Controls.Remove(CNT) + OffsetControls() + CNT.Dispose() + End If End Sub, EDP.None) End Sub #End Region #Region "Media controls' handlers" Private Sub MediaControl_FileDownloaded(ByVal Sender As MediaItem, ByVal Container As IYouTubeMediaContainer) If MyDownloaderSettings.ShowNotifications Then MyNotificator.ShowNotification(Container.ToString(), Container.ThumbnailFile) - If MyDownloaderSettings.RemoveDownloadedAutomatically Then RemoveControls(Sender) + If MyDownloaderSettings.RemoveDownloadedAutomatically Then RemoveControls(Sender, False) End Sub Private Sub MediaControl_Removal(ByVal Sender As MediaItem, ByVal Container As IYouTubeMediaContainer) - RemoveControls(Sender) + RemoveControls(Sender, False) End Sub Private Sub MediaControl_DownloadAgain(ByVal Sender As MediaItem, ByVal Container As IYouTubeMediaContainer) If Not Container.URL.IsEmptyString Then BufferText = Container.URL : BTT_ADD.PerformClick() @@ -432,8 +445,6 @@ Namespace DownloadObjects.STDownloader Protected Sub AddToDownload(ByRef Item As MediaItem, ByVal RunThread As Boolean) Dim hc% = Item.MyContainer.GetHashCode If MyJob.Count = 0 OrElse Not MyJob.Items.Exists(Function(i) i.MyContainer.GetHashCode = hc) Then - 'TODELETE: YT video downloader 'Item.Pending' - 'Item.Pending = True MyJob.Add(Item) Item.AddToQueue() If RunThread Then StartDownloading() @@ -457,25 +468,39 @@ Namespace DownloadObjects.STDownloader MyJob.Start() Const nf As ANumbers.Formats = ANumbers.Formats.Number Dim t As New List(Of Task) - Dim i% + Dim i%, iAbs% Dim __item As MediaItem Dim Indexes As New List(Of Integer) + Dim IndexesToRemove As New List(Of Integer) Dim maxJobCount% = MyDownloaderSettings.MaxJobsCount If maxJobCount <= 0 Then maxJobCount = 1 MyProgress.Visible = True MyProgress.Maximum = MyJob.Count Do While MyJob.Count > 0 And Not MyJob.IsCancellationRequested i = -1 + iAbs = -1 Indexes.Clear() + IndexesToRemove.Clear() For Each __item In MyJob.Items - i += 1 - If i <= maxJobCount - 1 Then - Indexes.Add(i) - t.Add(Task.Run(Sub() __item.Download(MyJob.Token))) + iAbs += 1 + If Not __item.IsDisposed And Not If(__item.MyContainer?.DownloadState, Plugin.UserMediaStates.Unknown) = Plugin.UserMediaStates.Downloaded Then + i += 1 + If i <= maxJobCount - 1 Then + Indexes.Add(iAbs) + t.Add(Task.Run(Sub() __item.Download(MyJob.Token))) + Else + Exit For + End If Else - Exit For + IndexesToRemove.Add(iAbs) End If Next + If IndexesToRemove.Count > 0 Then + For i = IndexesToRemove.Count - 1 To 0 Step -1 + If Not MyJob.Items(IndexesToRemove(i)).IsDisposed Then MyJob.Items(IndexesToRemove(i)).Pending = False + MyJob.Items.RemoveAt(IndexesToRemove(i)) + Next + End If If t.Count > 0 Then MyProgress.Information = $"Downloading {t.Count.NumToString(nf, PNumProv)}/{MyJob.Count.NumToString(nf, PNumProv)}" MyProgress.InformationTemporary = MyProgress.Information @@ -491,6 +516,7 @@ Namespace DownloadObjects.STDownloader End If Loop Indexes.Clear() + IndexesToRemove.Clear() MyProgress.Done() MyProgress.InformationTemporary = "Download completed" Catch aoex As ArgumentOutOfRangeException diff --git a/SCrawler.YouTube/Editors/BugReporterForm.Designer.vb b/SCrawler.YouTube/Editors/BugReporterForm.Designer.vb new file mode 100644 index 0000000..5d0fffb --- /dev/null +++ b/SCrawler.YouTube/Editors/BugReporterForm.Designer.vb @@ -0,0 +1,361 @@ +' Copyright (C) 2023 Andy https://github.com/AAndyProgram +' 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 Editors + + Partial Public Class BugReporterForm : 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 TP_MAIN As System.Windows.Forms.TableLayoutPanel + Dim TP_BUTTONS As System.Windows.Forms.TableLayoutPanel + 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(BugReporterForm)) + 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() + Dim ActionButton7 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() + 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() + Me.BTT_EMAIL = New System.Windows.Forms.Button() + Me.BTT_GITHUB = New System.Windows.Forms.Button() + Me.BTT_COPY = New System.Windows.Forms.Button() + Me.BTT_CANCEL = New System.Windows.Forms.Button() + Me.BTT_ANON = New System.Windows.Forms.Button() + Me.TT_MAIN = New System.Windows.Forms.ToolTip(Me.components) + Me.TXT_DESCR = New PersonalUtilities.Forms.Controls.TextBoxExtended() + Me.TXT_URL_PROFILE = New PersonalUtilities.Forms.Controls.TextBoxExtended() + Me.TXT_URL_POST = New PersonalUtilities.Forms.Controls.TextBoxExtended() + Me.TXT_REPRODUCE = New PersonalUtilities.Forms.Controls.TextBoxExtended() + Me.TXT_EXPECT = New PersonalUtilities.Forms.Controls.TextBoxExtended() + Me.TXT_LOG = New PersonalUtilities.Forms.Controls.TextBoxExtended() + Me.TXT_FILES = New PersonalUtilities.Forms.Controls.TextBoxExtended() + TP_MAIN = New System.Windows.Forms.TableLayoutPanel() + TP_BUTTONS = New System.Windows.Forms.TableLayoutPanel() + TP_MAIN.SuspendLayout() + TP_BUTTONS.SuspendLayout() + CType(Me.TXT_DESCR, System.ComponentModel.ISupportInitialize).BeginInit() + CType(Me.TXT_URL_PROFILE, System.ComponentModel.ISupportInitialize).BeginInit() + CType(Me.TXT_URL_POST, System.ComponentModel.ISupportInitialize).BeginInit() + CType(Me.TXT_REPRODUCE, System.ComponentModel.ISupportInitialize).BeginInit() + CType(Me.TXT_EXPECT, System.ComponentModel.ISupportInitialize).BeginInit() + CType(Me.TXT_LOG, System.ComponentModel.ISupportInitialize).BeginInit() + CType(Me.TXT_FILES, System.ComponentModel.ISupportInitialize).BeginInit() + Me.SuspendLayout() + ' + 'TP_MAIN + ' + TP_MAIN.ColumnCount = 1 + TP_MAIN.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) + TP_MAIN.Controls.Add(Me.TXT_DESCR, 0, 0) + TP_MAIN.Controls.Add(Me.TXT_URL_PROFILE, 0, 1) + TP_MAIN.Controls.Add(Me.TXT_URL_POST, 0, 2) + TP_MAIN.Controls.Add(Me.TXT_REPRODUCE, 0, 3) + TP_MAIN.Controls.Add(Me.TXT_EXPECT, 0, 4) + TP_MAIN.Controls.Add(Me.TXT_LOG, 0, 5) + TP_MAIN.Controls.Add(TP_BUTTONS, 0, 7) + TP_MAIN.Controls.Add(Me.TXT_FILES, 0, 6) + 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 = 8 + TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 20.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.Absolute, 28.0!)) + TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 20.0!)) + TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 20.0!)) + TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 20.0!)) + TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 20.0!)) + TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 30.0!)) + TP_MAIN.Size = New System.Drawing.Size(584, 461) + TP_MAIN.TabIndex = 0 + ' + 'TP_BUTTONS + ' + TP_BUTTONS.ColumnCount = 6 + TP_BUTTONS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) + TP_BUTTONS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 100.0!)) + TP_BUTTONS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 100.0!)) + TP_BUTTONS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 100.0!)) + TP_BUTTONS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 100.0!)) + TP_BUTTONS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 100.0!)) + TP_BUTTONS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20.0!)) + TP_BUTTONS.Controls.Add(Me.BTT_EMAIL, 2, 0) + TP_BUTTONS.Controls.Add(Me.BTT_GITHUB, 3, 0) + TP_BUTTONS.Controls.Add(Me.BTT_COPY, 4, 0) + TP_BUTTONS.Controls.Add(Me.BTT_CANCEL, 5, 0) + TP_BUTTONS.Controls.Add(Me.BTT_ANON, 1, 0) + TP_BUTTONS.Dock = System.Windows.Forms.DockStyle.Fill + TP_BUTTONS.Location = New System.Drawing.Point(0, 431) + TP_BUTTONS.Margin = New System.Windows.Forms.Padding(0) + TP_BUTTONS.Name = "TP_BUTTONS" + TP_BUTTONS.RowCount = 1 + TP_BUTTONS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) + TP_BUTTONS.Size = New System.Drawing.Size(584, 30) + TP_BUTTONS.TabIndex = 7 + ' + 'BTT_EMAIL + ' + Me.BTT_EMAIL.Dock = System.Windows.Forms.DockStyle.Fill + Me.BTT_EMAIL.Location = New System.Drawing.Point(187, 3) + Me.BTT_EMAIL.Name = "BTT_EMAIL" + Me.BTT_EMAIL.Size = New System.Drawing.Size(94, 24) + Me.BTT_EMAIL.TabIndex = 1 + Me.BTT_EMAIL.Text = "email" + Me.TT_MAIN.SetToolTip(Me.BTT_EMAIL, "Create a message to send via email.") + Me.BTT_EMAIL.UseVisualStyleBackColor = True + ' + 'BTT_GITHUB + ' + Me.BTT_GITHUB.Dock = System.Windows.Forms.DockStyle.Fill + Me.BTT_GITHUB.Location = New System.Drawing.Point(287, 3) + Me.BTT_GITHUB.Name = "BTT_GITHUB" + Me.BTT_GITHUB.Size = New System.Drawing.Size(94, 24) + Me.BTT_GITHUB.TabIndex = 2 + Me.BTT_GITHUB.Text = "GitHub" + Me.TT_MAIN.SetToolTip(Me.BTT_GITHUB, "Create a MarkDown message to post to GitHub.") + Me.BTT_GITHUB.UseVisualStyleBackColor = True + ' + 'BTT_COPY + ' + Me.BTT_COPY.Dock = System.Windows.Forms.DockStyle.Fill + Me.BTT_COPY.Location = New System.Drawing.Point(387, 3) + Me.BTT_COPY.Name = "BTT_COPY" + Me.BTT_COPY.Size = New System.Drawing.Size(94, 24) + Me.BTT_COPY.TabIndex = 3 + Me.BTT_COPY.Text = "Copy" + Me.TT_MAIN.SetToolTip(Me.BTT_COPY, "Create a message and copy to your clipboard.") + Me.BTT_COPY.UseVisualStyleBackColor = True + ' + 'BTT_CANCEL + ' + Me.BTT_CANCEL.DialogResult = System.Windows.Forms.DialogResult.Cancel + Me.BTT_CANCEL.Dock = System.Windows.Forms.DockStyle.Fill + Me.BTT_CANCEL.Location = New System.Drawing.Point(487, 3) + Me.BTT_CANCEL.Name = "BTT_CANCEL" + Me.BTT_CANCEL.Size = New System.Drawing.Size(94, 24) + Me.BTT_CANCEL.TabIndex = 4 + Me.BTT_CANCEL.Text = "Cancel" + Me.BTT_CANCEL.UseVisualStyleBackColor = True + ' + 'BTT_ANON + ' + Me.BTT_ANON.Dock = System.Windows.Forms.DockStyle.Fill + Me.BTT_ANON.Location = New System.Drawing.Point(87, 3) + Me.BTT_ANON.Name = "BTT_ANON" + Me.BTT_ANON.Size = New System.Drawing.Size(94, 24) + Me.BTT_ANON.TabIndex = 0 + Me.BTT_ANON.Text = "Anon message" + Me.TT_MAIN.SetToolTip(Me.BTT_ANON, resources.GetString("BTT_ANON.ToolTip")) + Me.BTT_ANON.UseVisualStyleBackColor = True + ' + 'TXT_DESCR + ' + ActionButton1.BackgroundImage = CType(resources.GetObject("ActionButton1.BackgroundImage"), System.Drawing.Image) + ActionButton1.Dock = System.Windows.Forms.DockStyle.Top + ActionButton1.Name = "Clear" + ActionButton1.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear + Me.TXT_DESCR.Buttons.Add(ActionButton1) + Me.TXT_DESCR.CaptionDock = System.Windows.Forms.DockStyle.Top + Me.TXT_DESCR.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.None + Me.TXT_DESCR.CaptionVisible = False + Me.TXT_DESCR.Dock = System.Windows.Forms.DockStyle.Fill + Me.TXT_DESCR.GroupBoxed = True + Me.TXT_DESCR.GroupBoxText = "Describe the bug or write your message" + Me.TXT_DESCR.Lines = New String(-1) {} + Me.TXT_DESCR.Location = New System.Drawing.Point(3, 3) + Me.TXT_DESCR.Multiline = True + Me.TXT_DESCR.Name = "TXT_DESCR" + Me.TXT_DESCR.Size = New System.Drawing.Size(578, 69) + Me.TXT_DESCR.TabIndex = 0 + Me.TXT_DESCR.TextToolTip = "A clear and concise description of what the bug is" + Me.TXT_DESCR.TextToolTipEnabled = True + ' + 'TXT_URL_PROFILE + ' + ActionButton2.BackgroundImage = CType(resources.GetObject("ActionButton2.BackgroundImage"), System.Drawing.Image) + ActionButton2.Name = "Clear" + ActionButton2.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear + Me.TXT_URL_PROFILE.Buttons.Add(ActionButton2) + Me.TXT_URL_PROFILE.CaptionText = "Profile URL" + Me.TXT_URL_PROFILE.CaptionWidth = 75.0R + Me.TXT_URL_PROFILE.Dock = System.Windows.Forms.DockStyle.Fill + Me.TXT_URL_PROFILE.Lines = New String(-1) {} + Me.TXT_URL_PROFILE.Location = New System.Drawing.Point(3, 78) + Me.TXT_URL_PROFILE.Name = "TXT_URL_PROFILE" + Me.TXT_URL_PROFILE.Size = New System.Drawing.Size(578, 22) + Me.TXT_URL_PROFILE.TabIndex = 1 + ' + 'TXT_URL_POST + ' + ActionButton3.BackgroundImage = CType(resources.GetObject("ActionButton3.BackgroundImage"), System.Drawing.Image) + ActionButton3.Name = "Clear" + ActionButton3.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear + Me.TXT_URL_POST.Buttons.Add(ActionButton3) + Me.TXT_URL_POST.CaptionText = "Post URL" + Me.TXT_URL_POST.CaptionWidth = 75.0R + Me.TXT_URL_POST.Dock = System.Windows.Forms.DockStyle.Fill + Me.TXT_URL_POST.Lines = New String(-1) {} + Me.TXT_URL_POST.Location = New System.Drawing.Point(3, 106) + Me.TXT_URL_POST.Name = "TXT_URL_POST" + Me.TXT_URL_POST.Size = New System.Drawing.Size(578, 22) + Me.TXT_URL_POST.TabIndex = 2 + ' + 'TXT_REPRODUCE + ' + ActionButton4.BackgroundImage = CType(resources.GetObject("ActionButton4.BackgroundImage"), System.Drawing.Image) + ActionButton4.Dock = System.Windows.Forms.DockStyle.Top + ActionButton4.Name = "Clear" + ActionButton4.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear + Me.TXT_REPRODUCE.Buttons.Add(ActionButton4) + Me.TXT_REPRODUCE.CaptionDock = System.Windows.Forms.DockStyle.Top + Me.TXT_REPRODUCE.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.None + Me.TXT_REPRODUCE.CaptionVisible = False + Me.TXT_REPRODUCE.Dock = System.Windows.Forms.DockStyle.Fill + Me.TXT_REPRODUCE.GroupBoxed = True + Me.TXT_REPRODUCE.GroupBoxText = "To Reproduce" + Me.TXT_REPRODUCE.Lines = New String(-1) {} + Me.TXT_REPRODUCE.Location = New System.Drawing.Point(3, 134) + Me.TXT_REPRODUCE.Multiline = True + Me.TXT_REPRODUCE.Name = "TXT_REPRODUCE" + Me.TXT_REPRODUCE.Size = New System.Drawing.Size(578, 69) + Me.TXT_REPRODUCE.TabIndex = 3 + Me.TXT_REPRODUCE.TextToolTip = "Steps to reproduce the behavior:" & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "1. Do something" & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "2. See error" + Me.TXT_REPRODUCE.TextToolTipEnabled = True + ' + 'TXT_EXPECT + ' + ActionButton5.BackgroundImage = CType(resources.GetObject("ActionButton5.BackgroundImage"), System.Drawing.Image) + ActionButton5.Dock = System.Windows.Forms.DockStyle.Top + ActionButton5.Name = "Clear" + ActionButton5.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear + Me.TXT_EXPECT.Buttons.Add(ActionButton5) + Me.TXT_EXPECT.CaptionDock = System.Windows.Forms.DockStyle.Top + Me.TXT_EXPECT.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.None + Me.TXT_EXPECT.CaptionVisible = False + Me.TXT_EXPECT.Dock = System.Windows.Forms.DockStyle.Fill + Me.TXT_EXPECT.GroupBoxed = True + Me.TXT_EXPECT.GroupBoxText = "Expected behavior" + Me.TXT_EXPECT.Lines = New String(-1) {} + Me.TXT_EXPECT.Location = New System.Drawing.Point(3, 209) + Me.TXT_EXPECT.Multiline = True + Me.TXT_EXPECT.Name = "TXT_EXPECT" + Me.TXT_EXPECT.Size = New System.Drawing.Size(578, 69) + Me.TXT_EXPECT.TabIndex = 4 + Me.TXT_EXPECT.TextToolTip = "A clear and concise description of what you expected to happen." + Me.TXT_EXPECT.TextToolTipEnabled = True + ' + 'TXT_LOG + ' + ActionButton6.BackgroundImage = CType(resources.GetObject("ActionButton6.BackgroundImage"), System.Drawing.Image) + ActionButton6.Dock = System.Windows.Forms.DockStyle.Top + ActionButton6.Name = "Open" + ActionButton6.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open + ActionButton6.ToolTipText = "Select log files to add their text to the message" + ActionButton7.BackgroundImage = CType(resources.GetObject("ActionButton7.BackgroundImage"), System.Drawing.Image) + ActionButton7.Dock = System.Windows.Forms.DockStyle.Top + ActionButton7.Name = "Clear" + ActionButton7.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear + ActionButton7.ToolTipText = "Empty" + Me.TXT_LOG.Buttons.Add(ActionButton6) + Me.TXT_LOG.Buttons.Add(ActionButton7) + Me.TXT_LOG.CaptionDock = System.Windows.Forms.DockStyle.Top + Me.TXT_LOG.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.None + Me.TXT_LOG.CaptionVisible = False + Me.TXT_LOG.Dock = System.Windows.Forms.DockStyle.Fill + Me.TXT_LOG.GroupBoxed = True + Me.TXT_LOG.GroupBoxText = "Log data" + Me.TXT_LOG.Lines = New String(-1) {} + Me.TXT_LOG.Location = New System.Drawing.Point(3, 284) + Me.TXT_LOG.Multiline = True + Me.TXT_LOG.Name = "TXT_LOG" + Me.TXT_LOG.Size = New System.Drawing.Size(578, 69) + Me.TXT_LOG.TabIndex = 5 + ' + 'TXT_FILES + ' + ActionButton8.BackgroundImage = CType(resources.GetObject("ActionButton8.BackgroundImage"), System.Drawing.Image) + ActionButton8.Dock = System.Windows.Forms.DockStyle.Top + ActionButton8.Name = "Add" + ActionButton8.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Add + ActionButton8.ToolTipText = "Add files" + ActionButton9.BackgroundImage = CType(resources.GetObject("ActionButton9.BackgroundImage"), System.Drawing.Image) + ActionButton9.Dock = System.Windows.Forms.DockStyle.Top + ActionButton9.Name = "Clear" + ActionButton9.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear + ActionButton9.ToolTipText = "Clear files" + Me.TXT_FILES.Buttons.Add(ActionButton8) + Me.TXT_FILES.Buttons.Add(ActionButton9) + Me.TXT_FILES.CaptionDock = System.Windows.Forms.DockStyle.Top + Me.TXT_FILES.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.None + Me.TXT_FILES.CaptionVisible = False + Me.TXT_FILES.Dock = System.Windows.Forms.DockStyle.Fill + Me.TXT_FILES.GroupBoxed = True + Me.TXT_FILES.GroupBoxText = "Files" + Me.TXT_FILES.Lines = New String(-1) {} + Me.TXT_FILES.Location = New System.Drawing.Point(3, 359) + Me.TXT_FILES.Multiline = True + Me.TXT_FILES.Name = "TXT_FILES" + Me.TXT_FILES.Size = New System.Drawing.Size(578, 69) + Me.TXT_FILES.TabIndex = 6 + Me.TXT_FILES.TextBoxReadOnly = True + Me.TXT_FILES.TextToolTip = "Attach files to your message (only works with anonymous message)" + Me.TXT_FILES.TextToolTipEnabled = True + ' + 'BugReporterForm + ' + Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!) + Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font + Me.CancelButton = Me.BTT_CANCEL + Me.ClientSize = New System.Drawing.Size(584, 461) + Me.Controls.Add(TP_MAIN) + Me.KeyPreview = True + Me.MinimumSize = New System.Drawing.Size(600, 500) + Me.Name = "BugReporterForm" + Me.Text = "New message" + TP_MAIN.ResumeLayout(False) + TP_BUTTONS.ResumeLayout(False) + CType(Me.TXT_DESCR, System.ComponentModel.ISupportInitialize).EndInit() + CType(Me.TXT_URL_PROFILE, System.ComponentModel.ISupportInitialize).EndInit() + CType(Me.TXT_URL_POST, System.ComponentModel.ISupportInitialize).EndInit() + CType(Me.TXT_REPRODUCE, System.ComponentModel.ISupportInitialize).EndInit() + CType(Me.TXT_EXPECT, System.ComponentModel.ISupportInitialize).EndInit() + CType(Me.TXT_LOG, System.ComponentModel.ISupportInitialize).EndInit() + CType(Me.TXT_FILES, System.ComponentModel.ISupportInitialize).EndInit() + Me.ResumeLayout(False) + + End Sub + + Private WithEvents TXT_DESCR As PersonalUtilities.Forms.Controls.TextBoxExtended + Private WithEvents TXT_URL_PROFILE As PersonalUtilities.Forms.Controls.TextBoxExtended + Private WithEvents TXT_URL_POST As PersonalUtilities.Forms.Controls.TextBoxExtended + Private WithEvents TXT_REPRODUCE As PersonalUtilities.Forms.Controls.TextBoxExtended + Private WithEvents TXT_EXPECT As PersonalUtilities.Forms.Controls.TextBoxExtended + Private WithEvents TXT_LOG As PersonalUtilities.Forms.Controls.TextBoxExtended + Private WithEvents TT_MAIN As ToolTip + Private WithEvents BTT_EMAIL As Button + Private WithEvents BTT_GITHUB As Button + Private WithEvents BTT_COPY As Button + Private WithEvents BTT_CANCEL As Button + Private WithEvents BTT_ANON As Button + Private WithEvents TXT_FILES As PersonalUtilities.Forms.Controls.TextBoxExtended + End Class +End Namespace \ No newline at end of file diff --git a/SCrawler.YouTube/Editors/BugReporterForm.resx b/SCrawler.YouTube/Editors/BugReporterForm.resx new file mode 100644 index 0000000..4702b2e --- /dev/null +++ b/SCrawler.YouTube/Editors/BugReporterForm.resx @@ -0,0 +1,225 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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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 + cMaRN0UdBBkAAAAASUVORK5CYII= + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO + xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go + tbbB43rK5xSAQq1VYFtmeQBoqZTSreVZvgTknM8yyyjA/qodsDF9gspD2Bj6B+DH+NqzhQQAG+POMnSX + AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC + + + + False + + + 17, 17 + + + Send an anonymous message. +The developer will not be able you contact you back. +You can attach files (images, photos) to your message. +If you would like a response from the developer, response, please add your contact details (email, Discord, etc.). + + + + iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6 + JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAADmUlE + QVRIS62WWWxMURjHL220JW1HausmlFrDFKUhnUGH6bRFzJ2idImlC0Vp2mlji1A8iNhCPIjIRES8EU+W + h2oEtbSDTk3HNNM7S01VKsXjkb/vXBo3k1Ee7sMvmZzzzf//ne/+z50RAAxL1MUIG4G/YAv3HSVhF5Vw + IYNdz3LadVj9RgdTB+HQYYPHIJuE1ocSdlEJFzG+1bPRLQLinglIeCkg+XUkKvz56hnkOfQs/rmA8S9H + YEp7FDI64tAQtKhnsMapZ7zzNHsUFnbGY4VzIk70l6hnIH4wsDR7NBZ3apDrSqL5T8eFgUr1DLZ78lim + Q4N8VzK29MxEpZSBa4M16hnU+c3M9CEFpdJsVHsXos63DDcHrf9nQEXD5VymwW/5USLNwl5vJhp7dTgW + NML2pR7jbsUMS+KdMTa5Q8NQxinfBU4dRFcOyjy52OtbhwOBDTgZLKPPmTgY0ON4MBdNfSbYBupxY8Aq + G10dqMG5/nIc7ytGQ6CQRliAamkTN/g1Ai4e95Qy3iogpX0UtBRDnhRzdxq2SXOxz5eFQ70rScCEU335 + ssGxj0YS06HSm4GN3ekwdE2C1hGH1LZR0JDOJof5jwHvnIvzTa0jlooTYfktvt+fhcOBHDQFTWRgxJGP + ObAGsulZLMLWnjlY756K5c4JmNcRi6T2SGheCIihS2l5ozAo6NRhMolnUAcGV6IcwwqvFrX+JTjYuwKH + SfRAYDms/mzs9y1GFe2VSnOw1j0FejqpLN4WCX4ZufiIBwLMLxQGm12rsLQzgWKYgmLPLNTQw6ynpDSS + IBet8y+TqaVRVdFIeJrWuCcj+/0EzH43BomvIhBLI45uFiDcJ+6QwROFwa6+Amb9bGFNg6Xs9Ncd7Oy3 + Knb2eyU7/20nu9y/m136tIvEl6BC0qKoZwby3alo9JVhj7T5R7m/kJVIIityi8zyXmTiW+I10SqyIQNb + uIgNwYuuf25kFd75KPKkI49OmUWnrfYWyXv/wBb2cijhhVf6a9lGei65XclYRDd6mj0GWz2iLBJaH0rY + RSVc5Eywmhm7kuQXHX+bJlBStrh+zTi0PpSwi0q4yNFAOVvgiEcKJWUsxZn/NhT+znlofShhF5VwkRpv + MUtti4KGYjj6sYCIh5QSu4oG27stjItHU+cjeQzvkcFzFQ2KnSKLoc4FukDCXeI2GbSoaFD4ziyPxNxK + 0AUyNxOP1DOwcaG/8I+/LRB+At7psBnyDBG0AAAAAElFTkSuQmCC + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO + xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go + tbbB43rK5xSAQq1VYFtmeQBoqZTSreVZvgTknM8yyyjA/qodsDF9gspD2Bj6B+DH+NqzhQQAG+POMnSX + AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC + + + \ No newline at end of file diff --git a/SCrawler.YouTube/Editors/BugReporterForm.vb b/SCrawler.YouTube/Editors/BugReporterForm.vb new file mode 100644 index 0000000..be27f46 --- /dev/null +++ b/SCrawler.YouTube/Editors/BugReporterForm.vb @@ -0,0 +1,238 @@ +' Copyright (C) 2023 Andy https://github.com/AAndyProgram +' 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.ComponentModel +Imports PersonalUtilities.Bots +Imports PersonalUtilities.Tools +Imports PersonalUtilities.Forms +Imports PersonalUtilities.Forms.Controls.Base +Imports PersonalUtilities.Functions.XML +Imports PersonalUtilities.Functions.Messaging +Imports BStyle = PersonalUtilities.Bots.IBot.Styles +Imports ADB = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons +Namespace Editors + Public Class BugReporterForm +#Region "Declarations" + Private Const MsgTitle As String = "Bug report" + Private ReadOnly MyView As FormView + Private ReadOnly MyFieldsChecker As FieldsChecker + Private MyProgramInfo As String + Private MyProgramInfoPopulated As Boolean = False + Private ReadOnly MyProgramText As String + Private ReadOnly MyCurrentVersion As Version + Private ReadOnly MyIsYouTube As Boolean + Private ReadOnly MyEnvirData As DownloadObjects.STDownloader.IDownloaderSettings + Private ReadOnly MyAdditText As String + Private ReadOnly MyCache As CacheKeeper +#End Region +#Region "Initializer" + Public Sub New(ByVal Cache As CacheKeeper, ByVal DesignXML As EContainer, ByVal ProgramText As String, ByVal CurrentVersion As Version, ByVal IsYouTube As Boolean, + ByVal EnvirData As DownloadObjects.STDownloader.IDownloaderSettings, Optional ByVal AdditText As String = Nothing) + InitializeComponent() + MyView = New FormView(Me, DesignXML) + MyFieldsChecker = New FieldsChecker + MyCache = Cache + MyProgramText = ProgramText + MyCurrentVersion = CurrentVersion + MyIsYouTube = IsYouTube + MyEnvirData = EnvirData + MyAdditText = AdditText + Icon = ImageRenderer.GetIcon(My.Resources.MailPic_16, EDP.ReturnValue) + End Sub +#End Region +#Region "Form handlers" + Private Async Sub BugReporterForm_Load(sender As Object, e As EventArgs) Handles Me.Load + MyView.Import() + MyView.SetFormSize() + With MyFieldsChecker + .AddControl(Of String)(TXT_DESCR, TXT_DESCR.GroupBoxText) + .EndLoaderOperations() + End With + TXT_LOG.Text = MyMainLOG + Await Task.Run(Sub() + MyProgramInfo = ProgramInfo.GetProgramText(MyProgramText.IfNullOrEmpty(IIf(MyIsYouTube, "YouTube downloader", "SCrawler")), + MyCurrentVersion, MyIsYouTube, MyEnvirData, MyAdditText) + MyProgramInfoPopulated = True + End Sub) + End Sub + Private Sub BugReporterForm_Closing(sender As Object, e As CancelEventArgs) Handles Me.Closing + MyView.Dispose() + MyFieldsChecker.Dispose() + End Sub +#End Region +#Region "Message" + Private Sub WaitLoadingDone() + While Not MyProgramInfoPopulated : Threading.Thread.Sleep(100) : End While + End Sub + Private Function CreateMessage(ByVal ForGitHub As Boolean, Optional ByVal ForDiscord As Boolean = False) As Object + Try + Dim nl$ = vbNewLine.StringDup(2) + Dim data As New List(Of BotMessage) + Dim t$ = String.Empty + Dim discordAppendNl As Action = Sub() data.Add(New BotMessage(vbNewLine)) + Dim appendNewLine As Action = Sub() If ForDiscord Then data.Add(New BotMessage(nl)) Else t &= nl + Dim ghBold As Func(Of String, Object) = Function(ByVal input As String) As Object + If ForDiscord Then + Return New BotMessage(input, BStyle.Bold) + Else + Return String.Format("{1}{0}{1}", input, IIf(ForGitHub, "**", "")) + End If + End Function + Dim appendData As Action(Of Object) = Sub(ByVal input As Object) + If ForDiscord Then + discordAppendNl.Invoke + data.Add(If(TypeOf input Is BotMessage, input, New BotMessage(input.ToString))) + Else + t.StringAppendLine(input) + End If + End Sub + + appendData(ghBold("Describe the bug")) + appendData(TXT_DESCR.Text) + If Not TXT_URL_PROFILE.IsEmptyString Then appendData($"Profile URL: {TXT_URL_PROFILE.Text}") + If Not TXT_URL_POST.IsEmptyString Then appendData($"Post URL: {TXT_URL_POST.Text}") + If Not TXT_REPRODUCE.IsEmptyString Then + appendNewLine.Invoke + appendData(ghBold("To Reproduce")) + appendData(TXT_REPRODUCE.Text) + End If + If Not TXT_EXPECT.IsEmptyString Then + appendNewLine.Invoke + appendData(ghBold("Expected behavior")) + appendData(TXT_EXPECT.Text) + End If + If Not TXT_LOG.IsEmptyString Then + appendNewLine.Invoke + If ForDiscord Then + data.Add(New BotMessage(TXT_LOG.Text, BStyle.Code)) + ElseIf ForGitHub Then + appendData($"
Log data
{TXT_LOG.Text}
") + Else + appendData(ghBold("LOG")) + appendData(TXT_LOG.Text) + End If + End If + + WaitLoadingDone() + appendNewLine.Invoke + appendData(ghBold("Release information:")) + appendData(MyProgramInfo) + + Return If(ForDiscord, data, t) + Catch ex As Exception + Return ErrorsDescriber.Execute(EDP.SendToLog + EDP.ReturnValue, ex, "[BugReporterForm.CreateMessage]") + End Try + End Function + Private Function ValidateFields(Optional ByVal SimpleMode As Boolean = False) As Boolean + If MyFieldsChecker.AllParamsOK Then + Dim opts$ = String.Empty + If TXT_URL_PROFILE.IsEmptyString Then opts.StringAppend("profile URL") + If TXT_URL_POST.IsEmptyString Then opts.StringAppend("post URL") + If TXT_LOG.Text.IsEmptyString Then opts.StringAppend("LOG") + Return opts.IsEmptyString OrElse SimpleMode OrElse + MsgBoxE({$"You haven't completed the following fields: {opts}.{vbCr}Are you sure you want to skip them?", + MsgTitle}, vbExclamation,,, {"Process", "Cancel"}) = 0 + End If + Return False + End Function +#End Region +#Region "Buttons" + Private Sub BTT_ANON_Click(sender As Object, e As EventArgs) Handles BTT_ANON.Click + Try + If ValidateFields(True) Then + Dim files As List(Of SFile) = Nothing + If TXT_FILES.Lines.ListExists Then files.ListAddList(TXT_FILES.Lines, LAP.NotContainsOnly) + Dim msgs As New List(Of BotMessage) + Dim isSimple As Boolean = False + Dim aMsg$ = String.Empty + Select Case MsgBoxE(New MMessage("Do you want to send a simple message or report a bug?", MsgTitle, + {New MsgBoxButton("Nice", "Say something nice to the developer." & vbCr & + "You can also attach cat picture :-)" & vbCr & + $"The message will be sent from the '{TXT_DESCR.GroupBoxText}' field."), + New MsgBoxButton("Simple", $"The developer will only receive the message from the '{TXT_DESCR.GroupBoxText}' field."), + New MsgBoxButton("Bug report", "The developer will receive a full bug report."), + "Cancel"}, vbQuestion) With {.ButtonsPerRow = 4, .DefaultButton = 2, .CancelButton = 3}).Index + Case 0 : msgs.Add(TXT_DESCR.Text) : aMsg = $"{vbCr}Thank you very much. I'm very grateful for your messages. You are awesome!" + Case 1 : isSimple = True : msgs.Add(TXT_DESCR.Text) + Case 2 : msgs = CreateMessage(False, True) + Case Else : Exit Sub + End Select + If msgs.ListExists Then + Dim nErr As New ErrorsDescriber(EDP.None) + Using d As New DiscordBot With {.Credential = DiscordWebHook, .User = "Anonymous user"} + d.SendMessage(New BotMessage(msgs.ToArray), EDP.ThrowException) + If isSimple Then WaitLoadingDone() : d.SendMessage(MyProgramInfo, nErr) + If files.ListExists Then files.ForEach(Sub(ff) d.SendFile(BotMessage.FromFile(ff),, nErr)) + End Using + msgs.Clear() + MsgBoxE({$"Your message has been sent to the developer.{aMsg}", MsgTitle}) + End If + End If + Catch ex As Exception + MsgBoxE({"Something is wrong. Your message has not been sent to the developer.", MsgTitle}, vbCritical) + End Try + End Sub + Private Sub BTT_EMAIL_Click(sender As Object, e As EventArgs) Handles BTT_EMAIL.Click + If ValidateFields() Then + Dim msg$ = CreateMessage(False) + Dim cmd$ = "START mailto:""andyprogram@proton.me?to=andyprogram@proton.me&subject=Application%%20bug%%20report""" + BufferText = msg + MsgBoxE({"The message has been copied to your clipboard. Click OK and paste this message into the window that opens.", MsgTitle}) + Using b As New BatchExecutor + b.FileExchanger = MyCache.NewInstance(Of BatchFileExchanger) + b.Execute(cmd) + End Using + End If + End Sub + Private Sub BTT_GITHUB_Click(sender As Object, e As EventArgs) Handles BTT_GITHUB.Click + If ValidateFields() Then + Dim msg$ = CreateMessage(True) + BufferText = msg + MsgBoxE({"The message has been copied to your clipboard. Create a new issue on GitHub and paste this message.", MsgTitle}) + Try : Process.Start("https://github.com/AAndyProgram/SCrawler/issues/new?assignees=&labels=&projects=&template=custom.md&title=") : Catch : End Try + End If + End Sub + Private Sub BTT_COPY_Click(sender As Object, e As EventArgs) Handles BTT_COPY.Click + If ValidateFields() Then + Dim msg$ = CreateMessage(MsgBoxE({"Will you post this message on GitHub?", MsgTitle}, vbQuestion + vbYesNo) = vbYes) + BufferText = msg + MsgBoxE({"The message has been copied to your clipboard.", MsgTitle}) + End If + End Sub + Private Sub BTT_CANCEL_Click(sender As Object, e As EventArgs) Handles BTT_CANCEL.Click + DialogResult = DialogResult.Cancel + Close() + End Sub +#End Region +#Region "Logs" + Private Sub TXT_LOG_ActionOnButtonClick(ByVal Sender As Object, ByVal e As ActionButtonEventArgs) Handles TXT_LOG.ActionOnButtonClick + If e.DefaultButton = ADB.Open Then + Dim files As List(Of SFile) = SFile.SelectFiles("LOGs\",, "Select log files", "Log files|*.txt|All files|*.*", EDP.ReturnValue) + If files.ListExists Then + Dim t$ + For Each file As SFile In files + t = file.GetText + If Not t.IsEmptyString Then _ + TXT_LOG.Text = $"{TXT_LOG.Text}{If(TXT_LOG.Text.IsEmptyString, String.Empty, vbNewLine.StringDup(2))}{file.Name}{vbNewLine}{t}" + Next + End If + End If + End Sub + Private Sub TXT_FILES_ActionOnButtonClick(ByVal Sender As Object, ByVal e As ActionButtonEventArgs) Handles TXT_FILES.ActionOnButtonClick + Try + If e.DefaultButton = ADB.Add Then + Dim f As List(Of SFile) = SFile.SelectFiles(,, "Select files to be sent", "Images|*.jpg;*.jpeg;*.png;*.webp;*.webm;*.gif|All files|*.*", EDP.ReturnValue) + If f.ListExists Then TXT_FILES.Lines = ListAddList(Nothing, TXT_FILES.Lines.Concat(f.Select(Function(ff) ff.ToString)), + LAP.NotContainsOnly, EDP.ReturnValue).ToArray + End If + Catch ex As Exception + End Try + End Sub +#End Region + End Class +End Namespace \ No newline at end of file diff --git a/SCrawler.YouTube/MainModShared.vb b/SCrawler.YouTube/MainModShared.vb index 8c12a36..b779e6a 100644 --- a/SCrawler.YouTube/MainModShared.vb +++ b/SCrawler.YouTube/MainModShared.vb @@ -7,6 +7,8 @@ ' This program is distributed in the hope that it will be useful, ' but WITHOUT ANY WARRANTY Imports PersonalUtilities.Tools +Imports PersonalUtilities.Tools.Web +Imports PersonalUtilities.Functions.Messaging Imports SCrawler.DownloadObjects.STDownloader Public Module MainModShared Public Property BATCH As BatchExecutor @@ -36,4 +38,98 @@ Public Module MainModShared End If End Try End Sub -End Module \ No newline at end of file + Public Sub ShowProgramInfo(ByVal ProgramText As String, ByVal CurrentVersion As Version, ByVal CheckForUpdate As Boolean, ByVal Force As Boolean, + ByVal EnvirData As IDownloaderSettings, ByVal IsYouTube As Boolean, + Optional ByRef NewVersionDestination As String = Nothing, Optional ByVal ShowNewVersionNotification As Boolean = True, + Optional ByVal AdditText As String = Nothing) + Try + Dim GoToSite As New MsgBoxButton("Go to site") With {.CallBack = Sub(r, n, b) Process.Start("https://github.com/AAndyProgram/SCrawler/releases/latest")} + If CheckForUpdate AndAlso GitHub.NewVersionExists(CurrentVersion, "AAndyProgram", "SCrawler", NewVersionDestination) Then + If ShowNewVersionNotification Or Force Then + If MsgBoxE(New MMessage($"{ProgramText}: new version detected" & vbCr & + $"Current version: {CurrentVersion}" & vbCr & + $"New version: {NewVersionDestination}", + "New version", + {"OK", GoToSite, "Disable notifications"})) = 2 Then ShowNewVersionNotification = False + End If + Else + If Force Then + Dim pVer$ = $"{ProgramText} v{CurrentVersion} ({IIf(Environment.Is64BitProcess, "x64", "x86")})" + Dim eText$ = Editors.ProgramInfo.GetProgramBaseText(ProgramText, CurrentVersion, AdditText) + Dim m As New MMessage($"{pVer}" & vbCr & + "Address: https://github.com/AAndyProgram/SCrawler" & vbCr & + "Created by Greek LGBT person Andy (Gay)", + "Program information", + {"OK", + GoToSite, + New MsgBoxButton("Environment", "Show program environment") With { + .IsDialogResultButton = False, + .CallBack = Sub(r, n, b) ShowProgramEnvir(EnvirData, IsYouTube, eText)} + }) With {.DefaultButton = 0, .CancelButton = 0} + If Not AdditText.IsEmptyString Then m.Text &= $"{vbCr}{AdditText}" + m.Show() + End If + ShowNewVersionNotification = True + End If + Catch ex As Exception + End Try + End Sub + Private Sub ShowProgramEnvir(ByVal EnvirData As IDownloaderSettings, ByVal IsYouTube As Boolean, ByVal AdditCopyText As String) + Dim m As New MMessage(Editors.ProgramInfo.GetProgramEnvirText(EnvirData, IsYouTube), "Program environment", {"OK", "Copy"}) With {.Editable = True, .DefaultButton = 0, .CancelButton = 0} + If m.Text = Editors.ProgramInfo.EnvironmentNotFound Then m.Style = vbCritical + m.Text = $"{AdditCopyText}{vbCr}{m.Text}" + If m.Show() = 1 Then BufferText = m.Text + End Sub +End Module +Namespace Editors + Public NotInheritable Class ProgramInfo + Public Const EnvironmentNotFound As String = "Environment not found" + Private Sub New() + End Sub + Public Shared Function GetProgramText(ByVal ProgramText As String, ByVal CurrentVersion As Version, ByVal IsYouTube As Boolean, + ByVal EnvirData As IDownloaderSettings, Optional ByVal AdditText As String = Nothing) As String + Return GetProgramBaseText(ProgramText, CurrentVersion, AdditText) & vbNewLine & GetProgramEnvirText(EnvirData, IsYouTube) + End Function + Public Shared Function GetProgramBaseText(ByVal ProgramText As String, ByVal CurrentVersion As Version, Optional ByVal AdditText As String = Nothing) As String + Dim pVer$ = $"{ProgramText} v{CurrentVersion} ({IIf(Environment.Is64BitProcess, "x64", "x86")})" + Dim WinVer$ = String.Empty + Try : WinVer = $"OS: {My.Computer.Info.OSFullName} ({IIf(Environment.Is64BitOperatingSystem, "x64", "x86")})" : Catch : End Try + Return pVer.StringDup(1).StringAppendLine(WinVer).StringAppendLine(AdditText) + End Function + Public Shared Function GetProgramEnvirText(ByVal EnvirData As IDownloaderSettings, ByVal IsYouTube As Boolean) As String + Try + Dim output$ = String.Empty + Using b As New BatchExecutor(True) + Dim f As SFile + Dim cmd$, ff$, vText$ + + For i% = 0 To IIf(IsYouTube, 1, 3) + cmd = "--version" + Select Case i + Case 0 : f = EnvirData.ENVIR_FFMPEG : ff = "ffmpeg" : cmd = "-version" + Case 1 : f = EnvirData.ENVIR_YTDLP : ff = "yt-dlp" + Case 2 : f = EnvirData.ENVIR_GDL : ff = "gallery-dl" + Case 3 : f = EnvirData.ENVIR_CURL : ff = "cURL" + Case Else : f = Nothing : ff = Nothing : cmd = Nothing + End Select + If Not ff.IsEmptyString Then + If f.IsEmptyString Then + output.StringAppendLine($"[{ff}] NOT FOUND") + Else + b.Reset() + b.Execute($"""{f}"" {cmd}", EDP.None) + If b.OutputData.Count > 3 Then vText = b.OutputData(3) Else vText = "undefined" + output.StringAppendLine($"{ff} version: {vText}") + End If + End If + Next + + If output.IsEmptyString Then output = EnvironmentNotFound + End Using + Return output + Catch ex As Exception + Return ErrorsDescriber.Execute(EDP.SendToLog + EDP.ReturnValue, ex, "[ProgramInfo.GetProgramEnvirText]", String.Empty) + End Try + End Function + End Class +End Namespace \ No newline at end of file diff --git a/SCrawler.YouTube/My Project/AssemblyInfo.vb b/SCrawler.YouTube/My Project/AssemblyInfo.vb index ed53f87..0e64b65 100644 --- a/SCrawler.YouTube/My Project/AssemblyInfo.vb +++ b/SCrawler.YouTube/My Project/AssemblyInfo.vb @@ -32,6 +32,6 @@ Imports System.Runtime.InteropServices ' by using the '*' as shown below: ' - - + + diff --git a/SCrawler.YouTube/My Project/Resources.Designer.vb b/SCrawler.YouTube/My Project/Resources.Designer.vb index 3b601a1..e418ab9 100644 --- a/SCrawler.YouTube/My Project/Resources.Designer.vb +++ b/SCrawler.YouTube/My Project/Resources.Designer.vb @@ -130,6 +130,16 @@ Namespace My.Resources End Get End Property + ''' + ''' Looks up a localized resource of type System.Drawing.Bitmap. + ''' + Public ReadOnly Property MailPic_16() As System.Drawing.Bitmap + Get + Dim obj As Object = ResourceManager.GetObject("MailPic_16", resourceCulture) + Return CType(obj,System.Drawing.Bitmap) + End Get + End Property + ''' ''' Looks up a localized resource of type System.Drawing.Bitmap. ''' diff --git a/SCrawler.YouTube/My Project/Resources.resx b/SCrawler.YouTube/My Project/Resources.resx index 6f8c584..16d2461 100644 --- a/SCrawler.YouTube/My Project/Resources.resx +++ b/SCrawler.YouTube/My Project/Resources.resx @@ -139,6 +139,9 @@ ..\Content\Pictures\LinkPic_32.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + ..\Content\Pictures\MailPic_16.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Content\Pictures\RulerPic_32.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a diff --git a/SCrawler.YouTube/Objects/YouTubeMediaContainerBase.vb b/SCrawler.YouTube/Objects/YouTubeMediaContainerBase.vb index c63bed4..dfd08d8 100644 --- a/SCrawler.YouTube/Objects/YouTubeMediaContainerBase.vb +++ b/SCrawler.YouTube/Objects/YouTubeMediaContainerBase.vb @@ -65,6 +65,7 @@ Namespace API.YouTube.Objects End Set End Property Protected _MediaState As UMStates = UMStates.Unknown + Protected _MediaStateOnLoad As UMStates = UMStates.Unknown Public Property MediaState As UMStates Implements IYouTubeMediaContainer.MediaState, IUserMedia.DownloadState Get If _MediaState = UMStates.Unknown And HasElements Then @@ -591,7 +592,9 @@ Namespace API.YouTube.Objects Bitrate = 0 _MediaType = UMTypes.Undefined If SelectedVideoIndex >= 0 Then - cmd.StringAppend($"bv*[format_id={SelectedVideo.ID}]") + 'URGENT: 2023.3.4 -> 2023.7.6 + 'cmd.StringAppend($"bv*[format_id={SelectedVideo.ID}]") + cmd.StringAppend(SelectedVideo.ID) _Size = SelectedVideo.Size _MediaType = UMTypes.Video Height = SelectedVideo.Height @@ -602,7 +605,9 @@ Namespace API.YouTube.Objects End If If SelectedAudioIndex >= 0 Then Dim atCodec$ - cmd.StringAppend($"ba*[format_id={SelectedAudio.ID}]", "+") + 'URGENT: 2023.3.4 -> 2023.7.6 + 'cmd.StringAppend($"ba*[format_id={SelectedAudio.ID}]", "+") + cmd.StringAppend(SelectedAudio.ID, "+") If OutputAudioCodec.StringToLower = ac3 Then PostProcessing_AudioAC3 = True formats.StringAppend($"--audio-format {aac}", " ") @@ -633,7 +638,9 @@ Namespace API.YouTube.Objects subs = $"--write-subs --write-auto-subs --sub-format {OutputSubtitlesFormat.StringToLower} --sub-langs ""{subs}"" --convert-subs {OutputSubtitlesFormat.StringToLower}" End If If Not cmd.IsEmptyString Then - cmd = $"yt-dlp -f ""{cmd}""" + 'URGENT: 2023.3.4 -> 2023.7.6 + 'cmd = $"yt-dlp -f ""{cmd}""" + cmd = $"yt-dlp -f {cmd}" If Not MyYouTubeSettings.ReplaceModificationDate Then cmd &= " --no-mtime" cmd.StringAppend(formats, " ") cmd.StringAppend(subs, " ") @@ -1017,6 +1024,7 @@ Namespace API.YouTube.Objects Dim fc As SFile = x.Value(Name_CachePath).CSFileP If fc.Exists(SFO.Path, False) AndAlso SFile.GetFiles(fc, "*.json",, EDP.ReturnValue).Count > 0 Then Parse(Nothing, fc, IsMusic) XMLPopulateData(Me, x) + _MediaStateOnLoad = _MediaState _Exists = True If If(x(Name_CheckedElements)?.Count, 0) > 0 Then ApplyElementCheckedValue(x(Name_CheckedElements)) If ArrayMaxResolution <> -10 Then SetMaxResolution(ArrayMaxResolution) @@ -1031,6 +1039,9 @@ Namespace API.YouTube.Objects End Sub #End Region #Region "Save" + Protected Function NeedToSave() As Boolean + Return Not _MediaStateOnLoad = _MediaState And Not FileSettings.Exists + End Function Private Function GetThumbnails() As IEnumerable(Of SFile) If HasElements Then Return ListAddList(Of SFile)(New List(Of SFile)({ThumbnailFile}), @@ -1041,46 +1052,50 @@ Namespace API.YouTube.Objects End Function Public Overridable Sub Save() Implements IDownloadableMedia.Save Try - Dim fSettings As SFile = FileSettings - If fSettings.IsEmptyString Then fSettings = MyCacheSettings.NewFile - Dim f As SFile = fSettings + If NeedToSave() Then + Dim fSettings As SFile = FileSettings + If fSettings.IsEmptyString Then fSettings = MyCacheSettings.NewFile + Dim f As SFile = fSettings - If Not MediaState = UMStates.Downloaded Then - If CachePath.Exists(SFO.Path, False) AndAlso Not CachePath.Path.Contains(MyCacheSettings.RootDirectory.Path) Then - f = $"{f.PathWithSeparator}{f.Name}\" - If f.Exists(SFO.Path) Then - Dim files As List(Of SFile) = SFile.GetFiles(CachePath, "*.json", IO.SearchOption.AllDirectories, EDP.ReturnValue) - If files.ListExists Then - CachePath = f - Dim fd As SFile = f - fd.Extension = "json" - For Each f In files - fd.Name = f.Name - SFile.Move(f, fd) - Next - Else - If CachePath.Exists(SFO.Path, False) Then CachePath.Delete(SFO.Path, SFODelete.DeletePermanently, EDP.None) - CachePath = Nothing + If Not MediaState = UMStates.Downloaded Then + If CachePath.Exists(SFO.Path, False) AndAlso Not CachePath.Path.Contains(MyCacheSettings.RootDirectory.Path) Then + f = $"{f.PathWithSeparator}{f.Name}\" + If f.Exists(SFO.Path) Then + Dim files As List(Of SFile) = SFile.GetFiles(CachePath, "*.json", IO.SearchOption.AllDirectories, EDP.ReturnValue) + If files.ListExists Then + CachePath = f + Dim fd As SFile = f + fd.Extension = "json" + For Each f In files + fd.Name = f.Name + SFile.Move(f, fd) + Next + Else + If CachePath.Exists(SFO.Path, False) Then CachePath.Delete(SFO.Path, SFODelete.DeletePermanently, EDP.None) + CachePath = Nothing + End If End If End If + Else + If CachePath.Exists(SFO.Path, False) Then CachePath.Delete(SFO.Path, SFODelete.DeletePermanently, EDP.None) + CachePath = Nothing + If ThumbnailFile.IsEmptyString And HasElements Then + With ListAddList(Nothing, GetThumbnails, LAP.NotContainsOnly).ListWithRemove(Function(tf) tf.IsEmptyString) + If .ListExists Then _ThumbnailFile = .FirstOrDefault(Function(tf) tf.Exists) + End With + End If End If - Else - If CachePath.Exists(SFO.Path, False) Then CachePath.Delete(SFO.Path, SFODelete.DeletePermanently, EDP.None) - CachePath = Nothing - If ThumbnailFile.IsEmptyString And HasElements Then - With ListAddList(Nothing, GetThumbnails, LAP.NotContainsOnly).ListWithRemove(Function(tf) tf.IsEmptyString) - If .ListExists Then _ThumbnailFile = .FirstOrDefault(Function(tf) tf.Exists) - End With - End If - End If - Using x As New XmlFile With {.AllowSameNames = True} - fSettings.Extension = "xml" - FileSettings = fSettings - x.AddRange(ToEContainer.Elements) - x.Name = "MediaContainer" - x.Save(fSettings) - End Using + Using x As New XmlFile With {.AllowSameNames = True} + fSettings.Extension = "xml" + FileSettings = fSettings + If NeedToSave() Then + x.AddRange(ToEContainer.Elements) + x.Name = "MediaContainer" + x.Save(fSettings) + End If + End Using + End If Catch ex As Exception ErrorsDescriber.Execute(EDP.SendToLog, ex, $"YouTubeMediaContainerBase.Save({FileSettings})") End Try @@ -1244,25 +1259,27 @@ Namespace API.YouTube.Objects obj.Height = AConvert(Of Integer)(ee.Value("height"), NumberProvider, -1) obj.FPS = AConvert(Of Double)(ee.Value("fps"), NumberProvider, -1) obj.Bitrate = AConvert(Of Double)(ee.Value("tbr"), NumberProvider, -1) + obj.Protocol = ee.Value("protocol") + If Not obj.Protocol.IsEmptyString Then obj.Protocol = obj.Protocol.Split("_").FirstOrDefault nValue = AConvert(Of Double)(ee.Value("filesize"), NumberProvider, -1) If nValue > 0 Then obj.Size = (nValue / 1024).RoundVal(2) + If obj.Size <= 0 Then + nValue = AConvert(Of Double)(ee.Value("filesize_approx"), NumberProvider, -1) + If nValue > 0 Then obj.Size = (nValue / 1024).RoundVal(2) + End If + If obj.Size <= 0 And obj.Bitrate > 0 And Duration.TotalSeconds > 0 Then _ + obj.Size = (obj.Bitrate / 8 * Duration.TotalSeconds).RoundVal(2) + sValue = ee.Value("vcodec") If validCodecValue(sValue) Then obj.Type = UMTypes.Video obj.Codec = sValue.Split(".").First - If validCodecValue(ee.Value("acodec")) Then - obj.Type = av - If obj.Size <= 0 Then - nValue = AConvert(Of Double)(ee.Value("filesize_approx"), NumberProvider, -1) - If nValue > 0 Then obj.Size = (nValue / 1024).RoundVal(2) - End If - End If + If validCodecValue(ee.Value("acodec")) Then obj.Type = av Else sValue = ee.Value("acodec") If validCodecValue(sValue) Then obj.Type = UMTypes.Audio obj.Codec = sValue.Split(".").First - obj.Bitrate = AConvert(Of Double)(ee.Value("tbr"), NumberProvider, -1) Else Continue For End If @@ -1292,6 +1309,7 @@ Namespace API.YouTube.Objects Next End If End Sub + If MediaObjects.Count > 0 And Not MyYouTubeSettings.DefaultVideoIncludeNullSize Then MediaObjects.RemoveAll(Function(mo) mo.Size <= 0) If MediaObjects.Count > 0 Then DupRemover.Invoke(UMTypes.Audio) If MediaObjects.Count > 0 Then DupRemover.Invoke(UMTypes.Video) If MediaObjects.Count > 0 Then diff --git a/SCrawler.YouTube/SCrawler.YouTube.vbproj b/SCrawler.YouTube/SCrawler.YouTube.vbproj index abc229f..c8b3281 100644 --- a/SCrawler.YouTube/SCrawler.YouTube.vbproj +++ b/SCrawler.YouTube/SCrawler.YouTube.vbproj @@ -121,6 +121,7 @@ Form + @@ -155,6 +156,13 @@ + + BugReporterForm.vb + + + Form + + @@ -218,6 +226,9 @@ VideoOption.vb + + BugReporterForm.vb + PublicVbMyResourcesResXFileCodeGenerator Resources.Designer.vb @@ -317,5 +328,8 @@ + + + \ No newline at end of file diff --git a/SCrawler.YouTubeDownloader/MainFrame.Designer.vb b/SCrawler.YouTubeDownloader/MainFrame.Designer.vb index 7393dae..6f167d4 100644 --- a/SCrawler.YouTubeDownloader/MainFrame.Designer.vb +++ b/SCrawler.YouTubeDownloader/MainFrame.Designer.vb @@ -67,7 +67,6 @@ Partial Public Class MainFrame : Inherits SCrawler.DownloadObjects.STDownloader. Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!) Me.ClientSize = New System.Drawing.Size(1008, 729) Me.Name = "MainFrame" - Me.Text = "SCrawler: Happy LGBT Pride Month! :-)" Me.TRAY_CONTEXT.ResumeLayout(False) Me.ResumeLayout(False) Me.PerformLayout() diff --git a/SCrawler.YouTubeDownloader/My Project/AssemblyInfo.vb b/SCrawler.YouTubeDownloader/My Project/AssemblyInfo.vb index 3ff188f..5c5c805 100644 --- a/SCrawler.YouTubeDownloader/My Project/AssemblyInfo.vb +++ b/SCrawler.YouTubeDownloader/My Project/AssemblyInfo.vb @@ -32,6 +32,6 @@ Imports System.Runtime.InteropServices ' by using the '*' as shown below: ' - - + + diff --git a/SCrawler/API/Base/Declarations.vb b/SCrawler/API/Base/Declarations.vb index 5a181c6..8dcd51a 100644 --- a/SCrawler/API/Base/Declarations.vb +++ b/SCrawler/API/Base/Declarations.vb @@ -6,9 +6,12 @@ ' ' This program is distributed in the hope that it will be useful, ' but WITHOUT ANY WARRANTY +Imports PersonalUtilities.Forms +Imports PersonalUtilities.Functions.RegularExpressions Namespace API.Base Friend Module Declarations Friend Const UserLabelName As String = "User" + Friend Const SearchRequestLabelName As String = "Search request" Friend ReadOnly LNC As New ListAddParams(LAP.NotContainsOnly) Friend ReadOnly UnixDate32Provider As New ADateTime(ADateTime.Formats.Unix32) Friend ReadOnly UnixDate64Provider As New ADateTime(ADateTime.Formats.Unix64) @@ -16,5 +19,58 @@ Namespace API.Base Friend ReadOnly TitleHtmlConverter As Func(Of String, String) = Function(Input) SymbolsConverter.HTML.Decode(SymbolsConverter.Convert(Input, EDP.ReturnValue), EDP.ReturnValue). StringRemoveWinForbiddenSymbols().StringTrim() + Friend ReadOnly Regex_VideosThumb_OG_IMAGE As RParams = RParams.DMS("meta.property=.og.image..content=""([^""]+)""", 1, EDP.ReturnValue) + Friend Class ConcurrentDownloadsProvider : Inherits FieldsCheckerProviderBase + Public Overrides Sub Reset() + ErrorMessage = String.Empty + MyBase.Reset() + End Sub + Public Overrides 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 + Dim v% = AConvert(Of Integer)(Value, -1) + Dim defV% = Settings.MaxUsersJobsCount + If v.ValueBetween(1, defV) Then + Return Value + Else + HasError = True + If ACheck(Of Integer)(Value) Then + ErrorMessage = $"The number of concurrent downloads must be greater than 0 and equal to or less than {defV} (global limit)." + Else + TypeError = True + End If + Return Nothing + End If + End Function + End Class + Friend Class TokenRefreshIntervalProvider : Inherits FieldsCheckerProviderBase + Public Overrides Sub Reset() + ErrorMessage = String.Empty + MyBase.Reset() + End Sub + Public Overrides 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 + Dim v% = AConvert(Of Integer)(Value, -1) + If v > 0 Then + Return Value + ElseIf Not ACheck(Of Integer)(Value) Then + TypeError = True + Else + ErrorMessage = $"The value of [{Name}] field must be greater than or equal to 1" + End If + HasError = True + Return Nothing + End Function + End Class + Friend ReadOnly Property CacheDeletionError(ByVal RootPath As SFile) As ErrorsDescriber + Get + Return New ErrorsDescriber(EDP.None) With {.Action = Sub(ee, eex, msg, obj) Settings.Cache.AddPath(RootPath)} + End Get + End Property + Friend Function ValidateChangeSearchOptions(ByVal User As String, ByVal NewQuery As String, ByVal CurrentQuery As String) As Boolean + Return MsgBoxE({$"Are you sure you want to change the query for user '{User}'?{vbCr}" & + "It is highly recommended to add a new user with this query instead of changing current one." & vbCr & + $"Current query: [{CurrentQuery}]{vbCr}New query: [{NewQuery}]", + "Changing a query"}, vbExclamation,,, {"Process", "Cancel"}) = 0 + End Function End Module End Namespace \ No newline at end of file diff --git a/SCrawler/API/Base/DeclaredNames.vb b/SCrawler/API/Base/DeclaredNames.vb new file mode 100644 index 0000000..2076ab4 --- /dev/null +++ b/SCrawler/API/Base/DeclaredNames.vb @@ -0,0 +1,31 @@ +' Copyright (C) 2023 Andy https://github.com/AAndyProgram +' 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 API.Base + Friend NotInheritable Class DeclaredNames + Friend Const Header_Authorization As String = "authorization" + Friend Const Header_CSRFToken As String = "x-csrf-token" + + Friend Const ConcurrentDownloadsCaption As String = "Concurrent downloads" + Friend Const ConcurrentDownloadsToolTip As String = "The number of concurrent downloads." + Friend Const SavedPostsUserNameCaption As String = "Saved posts user" + Friend Const SavedPostsUserNameToolTip As String = "Personal profile username" + Friend Const GifsSpecialFolderCaption As String = "GIFs special folder" + Friend Const GifsSpecialFolderToolTip As String = "Put the GIFs in a special folder" & vbCr & + "This is a folder name, not an absolute path." & vbCr & + "This folder(s) will be created relative to the user's root folder." & vbCr & + "Examples:" & vbCr & "SomeFolderName" & vbCr & "SomeFolderName\SomeFolderName2" + Friend Const GifsPrefixCaption As String = "GIF prefix" + Friend Const GifsPrefixToolTip As String = "This prefix will be added to the beginning of the filename" + Friend Const GifsDownloadCaption As String = "Download GIFs" + Friend Const UseMD5ComparisonCaption As String = "Use MD5 comparison" + Friend Const UseMD5ComparisonToolTip As String = "Each image will be checked for existence using MD5" + Private Sub New() + End Sub + End Class +End Namespace \ No newline at end of file diff --git a/SCrawler/API/Base/GDLBatch.vb b/SCrawler/API/Base/GDLBatch.vb index 4899285..9b19f4a 100644 --- a/SCrawler/API/Base/GDLBatch.vb +++ b/SCrawler/API/Base/GDLBatch.vb @@ -66,12 +66,12 @@ Namespace API.Base.GDL Return urls End Function End Module - Friend Class GDLBatch : Inherits BatchExecutor + Friend Class GDLBatch : Inherits TokenBatch Friend Property TempPostsList As List(Of String) Friend Const UrlLibStart As String = "[urllib3.connectionpool][debug]" Friend Const UrlTextStart As String = UrlLibStart & " https" - Friend Sub New() - MyBase.New(True) + Friend Sub New(ByVal _Token As Threading.CancellationToken) + MyBase.New(_Token) MainProcessName = "gallery-dl" ChangeDirectory(Settings.GalleryDLFile.File) End Sub @@ -86,8 +86,9 @@ Namespace API.Base.GDL End If End Sub Protected Overridable Async Function Validate(ByVal Value As String) As Task - If Not ProcessKilled AndAlso Await Task.Run(Of Boolean)(Function() Not Value.IsEmptyString AndAlso - TempPostsList.Exists(Function(v) Value.Contains(v))) Then Kill() + If Not ProcessKilled AndAlso Await Task.Run(Of Boolean)(Function() Token.IsCancellationRequested OrElse + (Not Value.IsEmptyString AndAlso + TempPostsList.Exists(Function(v) Value.Contains(v)))) Then Kill() End Function End Class End Namespace \ No newline at end of file diff --git a/SCrawler/API/Base/IUserData.vb b/SCrawler/API/Base/IUserData.vb new file mode 100644 index 0000000..9c01523 --- /dev/null +++ b/SCrawler/API/Base/IUserData.vb @@ -0,0 +1,88 @@ +' Copyright (C) 2023 Andy https://github.com/AAndyProgram +' 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 SCrawler.Plugin.Hosts +Namespace API.Base + Friend Interface IUserData : Inherits IComparable(Of UserDataBase), IComparable, IEquatable(Of UserDataBase), IIndexable, IDisposable + Event UserUpdated(ByVal User As IUserData) + Enum EraseMode As Integer + None = 0 + Data = 1 + History = 2 + End Enum + ReadOnly Property Site As String + ReadOnly Property Name As String + Property ID As String + Property Options As String + Property FriendlyName As String + Property Description As String + Property Favorite As Boolean + Property Temporary As Boolean + Property BackColor As Color? + Property ForeColor As Color? + Sub OpenSite(Optional ByVal e As ErrorsDescriber = Nothing) + Sub DownloadData(ByVal Token As CancellationToken) + Sub DownloadSingleObject(ByVal Data As YouTube.Objects.IYouTubeMediaContainer, ByVal Token As CancellationToken) + Property ParseUserMediaOnly As Boolean + ReadOnly Property IsSubscription As Boolean +#Region "Images" + Function GetPicture() As Image + Sub SetPicture(ByVal f As SFile) +#End Region +#Region "Collection support" + ReadOnly Property IsCollection As Boolean + ReadOnly Property CollectionName As String + ReadOnly Property CollectionPath As SFile + ReadOnly Property IncludedInCollection As Boolean + ReadOnly Property UserModel As UsageModel + ReadOnly Property CollectionModel As UsageModel + ReadOnly Property IsVirtual As Boolean + ReadOnly Property Labels As List(Of String) +#End Region + Property Exists As Boolean + Property Suspended As Boolean + Property ReadyForDownload As Boolean + Property HOST As SettingsHost + Property [File] As SFile + Property FileExists As Boolean + Property DownloadedPictures(ByVal Total As Boolean) As Integer + Property DownloadedVideos(ByVal Total As Boolean) As Integer + ReadOnly Property DownloadedTotal(Optional ByVal Total As Boolean = True) As Integer + ReadOnly Property DownloadedInformation As String + Property HasError As Boolean + ReadOnly Property FitToAddParams As Boolean + ReadOnly Property Key As String + Property DownloadImages As Boolean + Property DownloadVideos As Boolean + Property DownloadMissingOnly As Boolean + Property ScriptUse As Boolean + Property ScriptData As String + Function GetLVI(ByVal Destination As ListView) As ListViewItem + Function GetLVIGroup(ByVal Destination As ListView) As ListViewGroup + Sub LoadUserInformation() + Sub UpdateUserInformation() + ''' + ''' 0 - Nothing removed
+ ''' 1 - User removed
+ ''' 2 - Collection removed
+ ''' 3 - Collection split + '''
+ Function Delete(Optional ByVal Multiple As Boolean = False, Optional ByVal CollectionValue As Integer = -1) As Integer + Function EraseData(ByVal Mode As EraseMode) As Boolean + Function MoveFiles(ByVal CollectionName As String, ByVal SpecialCollectionPath As SFile) As Boolean + Function CopyFiles(ByVal DestinationPath As SFile, Optional ByVal e As ErrorsDescriber = Nothing) As Boolean + Sub OpenFolder() + Property DownloadTopCount As Integer? + Property DownloadDateFrom As Date? + Property DownloadDateTo As Date? + Sub SetEnvironment(ByRef h As SettingsHost, ByVal u As UserInfo, ByVal _LoadUserInformation As Boolean, + Optional ByVal AttachUserInfo As Boolean = True) + ReadOnly Property Disposed As Boolean + End Interface +End Namespace \ No newline at end of file diff --git a/SCrawler/API/Base/M3U8Base.vb b/SCrawler/API/Base/M3U8Base.vb index 4ebfff5..00cf96f 100644 --- a/SCrawler/API/Base/M3U8Base.vb +++ b/SCrawler/API/Base/M3U8Base.vb @@ -43,6 +43,7 @@ Namespace API.Base If ConcatFile.Name.IsEmptyString Then ConcatFile.Name = "PlayListFile" ConcatFile.Extension = "mp4" Cache = New CacheKeeper($"{DestinationFile.PathWithSeparator}_{TempCacheFolderName}\") + Cache.CacheDeleteError = CacheDeletionError(Cache) Dim cache2 As CacheKeeper = Cache.NewInstance If cache2.RootDirectory.Exists(SFO.Path) Then Dim progressExists As Boolean = Not Progress Is Nothing diff --git a/SCrawler/API/Base/SiteSettingsBase.vb b/SCrawler/API/Base/SiteSettingsBase.vb index d20b2a7..e9692a1 100644 --- a/SCrawler/API/Base/SiteSettingsBase.vb +++ b/SCrawler/API/Base/SiteSettingsBase.vb @@ -15,6 +15,13 @@ Namespace API.Base Friend ReadOnly Property Site As String Implements ISiteSettings.Site Friend Overridable ReadOnly Property Icon As Icon Implements ISiteSettings.Icon Friend Overridable ReadOnly Property Image As Image Implements ISiteSettings.Image + Protected _AllowUserAgentUpdate As Boolean = True + Protected _SubscriptionsAllowed As Boolean = False + Friend ReadOnly Property SubscriptionsAllowed As Boolean Implements ISiteSettings.SubscriptionsAllowed + Get + Return _SubscriptionsAllowed + End Get + End Property Private Property Logger As ILogProvider = LogConnector Implements ISiteSettings.Logger Friend Overridable ReadOnly Property Responser As Responser Friend ReadOnly Property CookiesNetscapeFile As SFile @@ -62,7 +69,7 @@ Namespace API.Base Friend Overridable Sub BeginInit() Implements ISiteSettings.BeginInit End Sub Friend Overridable Sub EndInit() Implements ISiteSettings.EndInit - If Not DefaultUserAgent.IsEmptyString And Not Responser Is Nothing Then Responser.UserAgent = DefaultUserAgent + If _AllowUserAgentUpdate And Not DefaultUserAgent.IsEmptyString And Not Responser Is Nothing Then Responser.UserAgent = DefaultUserAgent If CheckNetscapeCookiesOnEndInit Then Update_SaveCookiesNetscape(, True) End Sub #End Region @@ -82,6 +89,11 @@ Namespace API.Base Friend Overridable Sub Update() Implements ISiteSettings.Update If _SiteEditorFormOpened Then If UseNetscapeCookies Then Update_SaveCookiesNetscape() + If Not Responser Is Nothing Then + With Responser.Headers + If .Count > 0 Then .ListDisposeRemove(Function(h) h.Value.IsEmptyString) + End With + End If DomainsApply() End If If Not Responser Is Nothing Then Responser.SaveSettings() @@ -105,12 +117,30 @@ Namespace API.Base #End Region #End Region #Region "Before and After Download" + ''' + ''' PRE
+ ''' DownloadStarted
+ '''
+ ''' BEFORE
+ ''' Available
+ '''
+ ''' IN
+ ''' ReadyToDownload
+ ''' BeforeStartDownload
+ ''' AfterDownload
+ '''
+ ''' AFTER
+ ''' DownloadDone + '''
Friend Overridable Sub DownloadStarted(ByVal What As Download) Implements ISiteSettings.DownloadStarted End Sub + ''' Friend Overridable Sub BeforeStartDownload(ByVal User As Object, ByVal What As Download) Implements ISiteSettings.BeforeStartDownload End Sub + ''' Friend Overridable Sub AfterDownload(ByVal User As Object, ByVal What As Download) Implements ISiteSettings.AfterDownload End Sub + ''' Friend Overridable Sub DownloadDone(ByVal What As Download) Implements ISiteSettings.DownloadDone End Sub #End Region @@ -158,13 +188,13 @@ Namespace API.Base Friend Overridable Function BaseAuthExists() As Boolean Return True End Function - ''' JOB: leave or remove ''' Return BaseAuthExists() + ''' Friend Overridable Function Available(ByVal What As Download, ByVal Silent As Boolean) As Boolean Implements ISiteSettings.Available Return BaseAuthExists() End Function - ''' 'DownloadData': before processing ''' True + ''' Friend Overridable Function ReadyToDownload(ByVal What As Download) As Boolean Implements ISiteSettings.ReadyToDownload Return True End Function diff --git a/SCrawler/API/Base/Structures.vb b/SCrawler/API/Base/Structures.vb index eaf82a4..51abe75 100644 --- a/SCrawler/API/Base/Structures.vb +++ b/SCrawler/API/Base/Structures.vb @@ -12,6 +12,13 @@ Imports PersonalUtilities.Functions.XML.Base Imports PersonalUtilities.Functions.RegularExpressions Namespace API.Base Friend Module Structures + Friend Enum SiteModes As Integer + User = 0 + Search = 1 + Tags = 2 + Categories = 3 + Pornstars = 4 + End Enum Friend Structure UserMedia : Implements IUserMedia, IEquatable(Of UserMedia), IEContainerProvider #Region "XML Names" Friend Const Name_MediaNode As String = "MediaData" @@ -182,6 +189,7 @@ Namespace API.Base End With End If + 'TODO: UserMedia.SpecialFolder SpecialFolder = e.Attribute(Name_SpecialFolder).Value If Not SpecialFolder.IsEmptyString Then upath &= $"{SpecialFolder}\" If vp.HasValue AndAlso vp.Value Then upath &= $"Video\" diff --git a/SCrawler/API/Base/TokenBatch.vb b/SCrawler/API/Base/TokenBatch.vb new file mode 100644 index 0000000..924c3a0 --- /dev/null +++ b/SCrawler/API/Base/TokenBatch.vb @@ -0,0 +1,27 @@ +' Copyright (C) 2023 Andy https://github.com/AAndyProgram +' 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.Tools +Namespace API.Base + Friend Class TokenBatch : Inherits BatchExecutor + Protected ReadOnly Token As CancellationToken + Friend Sub New(ByVal _Token As CancellationToken) + MyBase.New(True) + Token = _Token + End Sub + Protected Overrides Async Sub OutputDataReceiver(ByVal Sender As Object, ByVal e As DataReceivedEventArgs) + MyBase.OutputDataReceiver(Sender, e) + Await Task.Run(Sub() If Token.IsCancellationRequested Then Kill()) + End Sub + Protected Overrides Async Sub ErrorDataReceiver(ByVal Sender As Object, ByVal e As DataReceivedEventArgs) + MyBase.ErrorDataReceiver(Sender, e) + Await Task.Run(Sub() If Token.IsCancellationRequested Then Kill()) + End Sub + End Class +End Namespace \ No newline at end of file diff --git a/SCrawler/API/Base/UserDataBase.vb b/SCrawler/API/Base/UserDataBase.vb index 67019eb..1e856da 100644 --- a/SCrawler/API/Base/UserDataBase.vb +++ b/SCrawler/API/Base/UserDataBase.vb @@ -13,6 +13,7 @@ Imports System.ComponentModel Imports System.Runtime.CompilerServices Imports SCrawler.Plugin Imports SCrawler.Plugin.Hosts +Imports PersonalUtilities.Functions.Messaging Imports PersonalUtilities.Functions.XML Imports PersonalUtilities.Functions.XML.Objects Imports PersonalUtilities.Functions.RegularExpressions @@ -51,6 +52,28 @@ Namespace API.Base Friend Sub RemoveUpdateHandlers() UserUpdatedEventHandlers.Clear() End Sub + Private ReadOnly UserDownloadStateChangedEventHandlers As List(Of UserDownloadStateChangedEventHandler) + Friend Custom Event UserDownloadStateChanged As UserDownloadStateChangedEventHandler + AddHandler(ByVal h As UserDownloadStateChangedEventHandler) + If Not UserDownloadStateChangedEventHandlers.Contains(h) Then UserDownloadStateChangedEventHandlers.Add(h) + End AddHandler + RemoveHandler(ByVal h As UserDownloadStateChangedEventHandler) + UserDownloadStateChangedEventHandlers.Remove(h) + End RemoveHandler + RaiseEvent(ByVal User As IUserData, ByVal IsDownloading As Boolean) + Try + If UserDownloadStateChangedEventHandlers.Count > 0 Then + For i% = 0 To UserDownloadStateChangedEventHandlers.Count - 1 + Try : UserDownloadStateChangedEventHandlers(i).Invoke(User, IsDownloading) : Catch : End Try + Next + End If + Catch + End Try + End RaiseEvent + End Event + Private Sub OnUserDownloadStateChanged(ByVal IsDownloading As Boolean) + RaiseEvent UserDownloadStateChanged(Me, IsDownloading) + End Sub #End Region #Region "Collection buttons" Private _CollectionButtonsExists As Boolean = False @@ -58,6 +81,7 @@ Namespace API.Base Friend WithEvents BTT_CONTEXT_DOWN As ToolStripKeyMenuItem Friend WithEvents BTT_CONTEXT_EDIT As ToolStripMenuItem Friend WithEvents BTT_CONTEXT_DELETE As ToolStripMenuItem + Friend WithEvents BTT_CONTEXT_ERASE As ToolStripMenuItem Friend WithEvents BTT_CONTEXT_OPEN_PATH As ToolStripMenuItem Friend WithEvents BTT_CONTEXT_OPEN_SITE As ToolStripMenuItem Friend Sub CreateButtons() @@ -75,6 +99,7 @@ Namespace API.Base BTT_CONTEXT_DOWN = New ToolStripKeyMenuItem(tn, i) With {.Name = tnn("DOWN"), .Tag = Me} BTT_CONTEXT_EDIT = New ToolStripMenuItem(tn, i) With {.Name = tnn("EDIT"), .Tag = Me} BTT_CONTEXT_DELETE = New ToolStripMenuItem(tn, i) With {.Name = tnn("DELETE"), .Tag = Me} + BTT_CONTEXT_ERASE = New ToolStripMenuItem(tn, i) With {.Name = tnn("ERASE"), .Tag = Me} BTT_CONTEXT_OPEN_PATH = New ToolStripMenuItem(tn, i) With {.Name = tnn("PATH"), .Tag = Me} BTT_CONTEXT_OPEN_SITE = New ToolStripMenuItem(tn, i) With {.Name = tnn("SITE"), .Tag = Me} UpdateButtonsColor() @@ -91,7 +116,8 @@ Namespace API.Base cb = MyColor.EditBack cf = MyColor.EditFore End If - For Each b As ToolStripMenuItem In {BTT_CONTEXT_DOWN, BTT_CONTEXT_EDIT, BTT_CONTEXT_DELETE, BTT_CONTEXT_OPEN_PATH, BTT_CONTEXT_OPEN_SITE} + For Each b As ToolStripMenuItem In {BTT_CONTEXT_DOWN, BTT_CONTEXT_EDIT, BTT_CONTEXT_DELETE, BTT_CONTEXT_ERASE, + BTT_CONTEXT_OPEN_PATH, BTT_CONTEXT_OPEN_SITE} If Not b Is Nothing Then b.BackColor = cb : b.ForeColor = cf Next If _UserInformationLoaded Then _CollectionButtonsColorsSet = True @@ -111,12 +137,16 @@ Namespace API.Base Private Const Name_UserExists As String = "UserExists" Private Const Name_UserSuspended As String = "UserSuspended" Protected Const Name_FriendlyName As String = "FriendlyName" - Private Const Name_UserSiteName As String = "UserSiteName" + Protected Const Name_UserSiteName As String = "UserSiteName" Protected Const Name_UserID As String = "UserID" - Private Const Name_Description As String = "Description" + Protected Const Name_Options As String = "Options" + Protected Const Name_Description As String = "Description" Private Const Name_ParseUserMediaOnly As String = "ParseUserMediaOnly" + Private Const Name_IsSubscription As String = UserInfo.Name_IsSubscription Private Const Name_Temporary As String = "Temporary" Private Const Name_Favorite As String = "Favorite" + Private Const Name_BackColor As String = "BackColor" + Private Const Name_ForeColor As String = "ForeColor" Private Const Name_CreatedByChannel As String = "CreatedByChannel" Private Const Name_SeparateVideoFolder As String = "SeparateVideoFolder" @@ -142,7 +172,7 @@ Namespace API.Base #Region "Declarations" #Region "Host, Site, Progress" Friend Property HOST As SettingsHost Implements IUserData.HOST - Friend ReadOnly Property Site As String Implements IContentProvider.Site + Friend ReadOnly Property Site As String Implements IUserData.Site Get Return HOST.Name End Get @@ -160,7 +190,7 @@ Namespace API.Base End Property Protected Property ProgressPre As PreProgress = Nothing #End Region -#Region "User name, ID, exist, suspend" +#Region "User name, ID, exist, suspend, options" Friend User As UserInfo Friend Property IsSavedPosts As Boolean Implements IPluginContentProvider.IsSavedPosts Private _UserExists As Boolean = True @@ -190,14 +220,14 @@ Namespace API.Base Set(ByVal NewName As String) End Set End Property - Friend Overridable ReadOnly Property Name As String Implements IContentProvider.Name + Friend Overridable ReadOnly Property Name As String Implements IUserData.Name Get Return User.Name End Get End Property - Friend Overridable Property ID As String = String.Empty Implements IContentProvider.ID, IPluginContentProvider.ID + Friend Overridable Property ID As String = String.Empty Implements IUserData.ID, IPluginContentProvider.ID Protected _FriendlyName As String = String.Empty - Friend Overridable Property FriendlyName As String Implements IContentProvider.FriendlyName + Friend Overridable Property FriendlyName As String Implements IUserData.FriendlyName Get If Settings.UserSiteNameAsFriendly Then Return _FriendlyName.IfNullOrEmpty(UserSiteName) @@ -251,9 +281,15 @@ Namespace API.Base Return UserModel = UsageModel.Virtual End Get End Property + Friend Property Options As String = String.Empty Implements IUserData.Options, IPluginContentProvider.Options + Friend Overridable ReadOnly Property FeedIsUser As Boolean + Get + Return True + End Get + End Property #End Region #Region "Description" - Friend Property UserDescription As String = String.Empty Implements IContentProvider.Description, IPluginContentProvider.UserDescription + Friend Property UserDescription As String = String.Empty Implements IUserData.Description, IPluginContentProvider.UserDescription Protected _DescriptionEveryTime As Boolean = False Protected _DescriptionChecked As Boolean = False Protected Function UserDescriptionNeedToUpdate() As Boolean @@ -270,9 +306,9 @@ Namespace API.Base End If End Sub #End Region -#Region "Favorite, Temporary" +#Region "Favorite, Temporary, Colors" Protected _Favorite As Boolean = False - Friend Overridable Property Favorite As Boolean Implements IContentProvider.Favorite + Friend Overridable Property Favorite As Boolean Implements IUserData.Favorite Get Return _Favorite End Get @@ -282,7 +318,7 @@ Namespace API.Base End Set End Property Protected _Temporary As Boolean = False - Friend Overridable Property Temporary As Boolean Implements IContentProvider.Temporary + Friend Overridable Property Temporary As Boolean Implements IUserData.Temporary Get Return _Temporary End Get @@ -291,6 +327,24 @@ Namespace API.Base If _Temporary Then _Favorite = False End Set End Property + Private _BackColor As Color? = Nothing + Friend Overridable Property BackColor As Color? Implements IUserData.BackColor + Get + Return _BackColor + End Get + Set(ByVal b As Color?) + _BackColor = b + End Set + End Property + Private _ForeColor As Color? = Nothing + Friend Overridable Property ForeColor As Color? Implements IUserData.ForeColor + Get + Return _ForeColor + End Get + Set(ByVal f As Color?) + _ForeColor = f + End Set + End Property #End Region #Region "Channel" Friend Property CreatedByChannel As Boolean = False @@ -405,32 +459,106 @@ BlockNullPicture: Return _IsCollection End Get End Property - Friend Overridable Property CollectionName As String Implements IUserData.CollectionName + Friend Overridable ReadOnly Property CollectionName As String Implements IUserData.CollectionName Get Return User.CollectionName End Get - Set(ByVal NewCollection As String) - ChangeCollectionName(NewCollection, True) - End Set + End Property + Friend Overridable ReadOnly Property CollectionPath As SFile Implements IUserData.CollectionPath + Get + Return User.GetCollectionRootPath + End Get End Property Friend ReadOnly Property IncludedInCollection As Boolean Implements IUserData.IncludedInCollection Get Return User.IncludedInCollection End Get End Property - Friend Overridable Sub ChangeCollectionName(ByVal NewName As String, ByVal UpdateSettings As Boolean) - Dim u As UserInfo = User - u.CollectionName = NewName - u.UpdateUserFile() - User = u - If UpdateSettings Then Settings.UpdateUsersList(User) - End Sub Friend Overridable ReadOnly Property Labels As List(Of String) Implements IUserData.Labels + Protected ReadOnly Property LabelsString As String + Get + Return Labels.ListToString("|", EDP.ReturnValue) + End Get + End Property + Friend Overridable ReadOnly Property SpecialLabels As IEnumerable(Of String) + Get + Return New String() {} + End Get + End Property + ''' + ''' 0 add
+ ''' 1 replace
+ ''' 2 remove + '''
+ ''' true = w/special + Friend Shared Function UpdateLabelsKeepSpecial(ByVal Mode As Byte) As Boolean + Dim m As New MMessage("", "Update labels",, vbQuestion + vbYesNo) With {.DefaultButton = 0, .CancelButton = 0} + Select Case Mode + Case 0 : m.Text = "Do you want to exclude site-specific labels from adding?" + Case 1, 2 : m.Text = "Do you want to keep site-specific labels?" + Case Else : Return False + End Select + Return m.Show = vbYes + End Function + ''' + Friend Shared Sub UpdateLabels(ByVal User As UserDataBase, ByVal NewLabels As IEnumerable(Of String), ByVal Mode As Byte, ByVal KeepSpecial As Boolean) + Try + If User.IsCollection Then + With DirectCast(User, UserDataBind) + If .Count > 0 Then .Collections.ForEach(Sub(u) UpdateLabels(u, NewLabels, Mode, KeepSpecial)) + End With + Else + Dim nl As List(Of String) + If NewLabels.ListExists Then nl = NewLabels.ToList Else nl = New List(Of String) + + Dim lex As List(Of String) = User.SpecialLabels.ToList + If lex.ListExists Then + If User.Labels.Count = 0 Or Not KeepSpecial Then + lex.Clear() + Else + lex.ListDisposeRemove(Function(l) Not User.Labels.Contains(l)) + End If + End If + + Select Case Mode + Case 0 'add + If KeepSpecial Then nl.ListAddList(lex, LNC) + User.Labels.ListAddList(nl, LNC) + Case 1 'replace + If KeepSpecial Then + nl.ListAddList(lex, LNC) + Else + nl.ListWithRemove(lex) + End If + User.Labels.Clear() + User.Labels.ListAddList(nl, LNC) + Case 2 'remove + If KeepSpecial Then nl.ListWithRemove(lex) + User.Labels.ListWithRemove(nl) + End Select + + If User.Labels.Count > 0 Then User.Labels.Sort() + End If + Catch ex As Exception + ErrorsDescriber.Execute(EDP.SendToLog, ex, "[UserDataBase.UpdateLabels]") + End Try + End Sub #End Region #Region "Downloading" Protected _DataLoaded As Boolean = False Protected _DataParsed As Boolean = False Friend Property ParseUserMediaOnly As Boolean = False Implements IUserData.ParseUserMediaOnly, IPluginContentProvider.ParseUserMediaOnly + Friend Overridable ReadOnly Property IsSubscription As Boolean Implements IUserData.IsSubscription + Get + Return User.IsSubscription + End Get + End Property + Private Property IPluginContentProvider_IsSubscription As Boolean Implements IPluginContentProvider.IsSubscription + Get + Return IsSubscription + End Get + Set : End Set + End Property Friend Overridable Property ReadyForDownload As Boolean = True Implements IUserData.ReadyForDownload Friend Property DownloadImages As Boolean = True Implements IUserData.DownloadImages Friend Property DownloadVideos As Boolean = True Implements IUserData.DownloadVideos @@ -636,7 +764,7 @@ BlockNullPicture: Friend ReadOnly Property LVIKey As String Implements IUserData.Key Get If Not _IsCollection Then - Return $"{Site.ToString.ToUpper}_{Name}" + Return $"{IIf(IsSubscription, "SSSS", String.Empty)}{Site.ToString.ToUpper}_{Name}" Else Return $"CCCC_{CollectionName}" End If @@ -652,6 +780,8 @@ BlockNullPicture: Friend Overridable ReadOnly Property FitToAddParams As Boolean Implements IUserData.FitToAddParams Get With Settings + If IsSubscription And Not .MainFrameUsersShowSubscriptions Then Return False + If Not IsSubscription And Not .MainFrameUsersShowDefaults Then Return False If LastUpdated.HasValue And Not .ViewDateMode.Value = ShowingDates.Off Then Dim f As Date = If(.ViewDateFrom.HasValue, .ViewDateFrom.Value.Date, Date.MinValue.Date) Dim t As Date = If(.ViewDateTo.HasValue, .ViewDateTo.Value.Date, Date.MaxValue.Date) @@ -705,6 +835,7 @@ BlockNullPicture: _TempPostsList = New List(Of String) Labels = New List(Of String) UserUpdatedEventHandlers = New List(Of IUserData.UserUpdatedEventHandler) + UserDownloadStateChangedEventHandlers = New List(Of UserDownloadStateChangedEventHandler) If InvokeImageHandler Then MainFrameObj.ImageHandler(Me) End Sub Friend Sub SetEnvironment(ByRef h As SettingsHost, ByVal u As UserInfo, ByVal _LoadUserInformation As Boolean, @@ -753,12 +884,25 @@ BlockNullPicture: UserExists = x.Value(Name_UserExists).FromXML(Of Boolean)(True) UserSuspended = x.Value(Name_UserSuspended).FromXML(Of Boolean)(False) ID = x.Value(Name_UserID) + Options = x.Value(Name_Options) _FriendlyName = x.Value(Name_FriendlyName) UserSiteName = x.Value(Name_UserSiteName) UserDescription = x.Value(Name_Description) ParseUserMediaOnly = x.Value(Name_ParseUserMediaOnly).FromXML(Of Boolean)(False) Temporary = x.Value(Name_Temporary).FromXML(Of Boolean)(False) Favorite = x.Value(Name_Favorite).FromXML(Of Boolean)(False) + + If Not x.Value(Name_BackColor).IsEmptyString Then + BackColor = AConvert(Of Color)(x.Value(Name_BackColor), Nothing, EDP.ReturnValue) + Else + BackColor = Nothing + End If + If Not x.Value(Name_ForeColor).IsEmptyString Then + ForeColor = AConvert(Of Color)(x.Value(Name_ForeColor), Nothing, EDP.ReturnValue) + Else + ForeColor = Nothing + End If + CreatedByChannel = x.Value(Name_CreatedByChannel).FromXML(Of Boolean)(False) SeparateVideoFolder = AConvert(Of Boolean)(x.Value(Name_SeparateVideoFolder), AModes.Var, Nothing) ReadyForDownload = x.Value(Name_ReadyForDownload).FromXML(Of Boolean)(True) @@ -771,7 +915,6 @@ BlockNullPicture: ScriptUse = x.Value(Name_ScriptUse).FromXML(Of Boolean)(False) ScriptData = x.Value(Name_ScriptData) DataMerging = x.Value(Name_Merged).FromXML(Of Boolean)(False) - ChangeCollectionName(x.Value(Name_CollectionName), False) Labels.ListAddList(x.Value(Name_LabelsName).StringToList(Of String, List(Of String))("|", EDP.ReturnValue), LAP.NotContainsOnly, LAP.ClearBeforeAdd) LoadUserInformation_OptionalFields(x, True) End Using @@ -798,12 +941,18 @@ BlockNullPicture: x.Add(Name_UserExists, UserExists.BoolToInteger) x.Add(Name_UserSuspended, UserSuspended.BoolToInteger) x.Add(Name_UserID, ID) + x.Add(Name_Options, Options) x.Add(Name_FriendlyName, _FriendlyName) x.Add(Name_UserSiteName, UserSiteName) x.Add(Name_Description, UserDescription) x.Add(Name_ParseUserMediaOnly, ParseUserMediaOnly.BoolToInteger) + x.Add(Name_IsSubscription, IsSubscription.BoolToInteger) x.Add(Name_Temporary, Temporary.BoolToInteger) x.Add(Name_Favorite, Favorite.BoolToInteger) + + x.Add(Name_BackColor, CStr(AConvert(Of String)(BackColor, String.Empty, EDP.ReturnValue))) + x.Add(Name_ForeColor, CStr(AConvert(Of String)(ForeColor, String.Empty, EDP.ReturnValue))) + x.Add(Name_CreatedByChannel, CreatedByChannel.BoolToInteger) If SeparateVideoFolder.HasValue Then x.Add(Name_SeparateVideoFolder, SeparateVideoFolder.Value.BoolToInteger) @@ -820,7 +969,7 @@ BlockNullPicture: x.Add(Name_ScriptUse, ScriptUse.BoolToInteger) x.Add(Name_ScriptData, ScriptData) x.Add(Name_CollectionName, CollectionName) - x.Add(Name_LabelsName, Labels.ListToString("|", EDP.ReturnValue)) + x.Add(Name_LabelsName, LabelsString) x.Add(Name_Merged, DataMerging.BoolToInteger) LoadUserInformation_OptionalFields(x, False) @@ -867,7 +1016,7 @@ BlockNullPicture: #End Region #End Region #Region "Open site, folder" - Friend Overridable Sub OpenSite(Optional ByVal e As ErrorsDescriber = Nothing) Implements IContentProvider.OpenSite + Friend Overridable Sub OpenSite(Optional ByVal e As ErrorsDescriber = Nothing) Implements IUserData.OpenSite Try Dim URL$ = HOST.Source.GetUserUrl(Me) If Not URL.IsEmptyString Then Process.Start(URL) @@ -927,6 +1076,13 @@ BlockNullPicture: End Function #End Region #Region "Download functions and options" + Private __DOWNLOAD_IN_PROGRESS As Boolean = False + Friend ReadOnly Property DownloadInProgress As Boolean + Get + Return __DOWNLOAD_IN_PROGRESS + End Get + End Property + Friend PersonalToken As CancellationToken Protected Responser As Responser Protected UseResponserClient As Boolean = False Protected UseClientTokens As Boolean = False @@ -935,10 +1091,12 @@ BlockNullPicture: Private _DownloadInProgress As Boolean = False Private _EnvirUserExists As Boolean Private _EnvirUserSuspended As Boolean + Private _EnvirCreatedByChannel As Boolean Private _EnvirChanged As Boolean = False Private _PictureExists As Boolean Private _EnvirInvokeUserUpdated As Boolean = False Protected Sub EnvirDownloadSet() + PersonalToken = Nothing ProgressPre.Reset() UpdateDataFiles() _DownloadInProgress = True @@ -948,6 +1106,7 @@ BlockNullPicture: _ForceSaveUserInfo = False _EnvirUserExists = UserExists _EnvirUserSuspended = UserSuspended + _EnvirCreatedByChannel = CreatedByChannel _EnvirChanged = False _EnvirInvokeUserUpdated = False UserExists = True @@ -965,7 +1124,9 @@ BlockNullPicture: End Select End If End Sub - Friend Overridable Sub DownloadData(ByVal Token As CancellationToken) Implements IContentProvider.DownloadData + Friend Overridable Sub DownloadData(ByVal Token As CancellationToken) Implements IUserData.DownloadData + __DOWNLOAD_IN_PROGRESS = True + OnUserDownloadStateChanged(True) Dim Canceled As Boolean = False _ExternalCompatibilityToken = Token Try @@ -981,14 +1142,14 @@ BlockNullPicture: _TempMediaList.Clear() _TempPostsList.Clear() LatestData.Clear() - Dim __SaveData As Boolean = Not CreatedByChannel Or Not Settings.FromChannelDownloadTopUse + Dim __isChannelsSupport As Boolean = CreatedByChannel And Settings.FromChannelDownloadTopUse LoadContentInformation() If MyFilePosts.Exists Then _TempPostsList.ListAddList(File.ReadAllLines(MyFilePosts)) If _ContentList.Count > 0 Then _TempPostsList.ListAddList(_ContentList.Select(Function(u) u.Post.ID), LNC) - If Not DownloadMissingOnly Then + If Not DownloadMissingOnly Or IsSubscription Then ThrowAny(Token) DownloadDataF(Token) ProgressPre.Done() @@ -1010,22 +1171,37 @@ BlockNullPicture: ProgressPre.Done() ThrowAny(Token) - If UseMD5Comparison Then ValidateMD5(Token) : ProgressPre.Done() : ThrowAny(Token) + If UseMD5Comparison And Not IsSubscription Then ValidateMD5(Token) : ProgressPre.Done() : ThrowAny(Token) - If _TempPostsList.Count > 0 And Not DownloadMissingOnly And __SaveData Then + If _TempPostsList.Count > 0 And Not DownloadMissingOnly And Not __isChannelsSupport Then If _TempPostsList.Count > 1000 Then _TempPostsList.ListAddList(_TempPostsList.ListTake(-2, 1000, EDP.ReturnValue).ListReverse, LAP.ClearBeforeAdd) TextSaver.SaveTextToFile(_TempPostsList.ListToString(Environment.NewLine), MyFilePosts, True,, EDP.None) End If - _ContentNew.ListAddList(_TempMediaList, LAP.ClearBeforeAdd) - DownloadContent(Token) - ThrowIfDisposed() - If IncludeInTheFeed Then LatestData.ListAddList(_ContentNew.Where(_downContent), LNC) + _ContentNew.ListAddList(_TempMediaList, LAP.ClearBeforeAdd) + If IsSubscription Then + _ContentNew.ListAddList(_ContentNew.ListForEachCopy(Function(ByVal tmpC As UserMedia, ByVal ii As Integer) As UserMedia + tmpC.State = UStates.Downloaded + If tmpC.Type = UTypes.Picture Or tmpC.Type = UTypes.GIF Then + DownloadedPictures(False) += 1 + Else + DownloadedVideos(False) += 1 + End If + Return tmpC + End Function)) + Else + DownloadContent(Token) + ThrowIfDisposed() + End If + + CreatedByChannel = False + + If IncludeInTheFeed Or IsSubscription Then LatestData.ListAddList(_ContentNew.Where(_downContent), LNC) Dim mcb& = If(ContentMissingExists, _ContentList.LongCount(Function(c) MissingFinder(c)), 0) _ContentList.ListAddList(_ContentNew.Where(Function(c) _downContent(c) Or MissingFinder(c)), LNC) Dim mca& = If(ContentMissingExists, _ContentList.LongCount(Function(c) MissingFinder(c)), 0) If DownloadedTotal(False) > 0 Or _EnvirChanged Or Not mcb = mca Or _ForceSaveUserData Then - If __SaveData Then + If Not __isChannelsSupport Then LastUpdated = Now RunScript() DownloadedPictures(True) = SFile.GetFiles(MyFile.CutPath, "*.jpg|*.jpeg|*.png|*.gif|*.webm",, EDP.ReturnValue).Count @@ -1040,20 +1216,30 @@ BlockNullPicture: End If UpdateUserInformation() If _CollectionButtonsExists AndAlso _EnvirChanged Then UpdateButtonsColor() - ElseIf _ForceSaveUserInfo Then + ElseIf _ForceSaveUserInfo Or __isChannelsSupport Or Not _EnvirCreatedByChannel = CreatedByChannel Then UpdateUserInformation() End If ThrowIfDisposed() If Not _PictureExists Or _EnvirInvokeUserUpdated Then OnUserUpdated() - Catch oex As OperationCanceledException When Token.IsCancellationRequested + Catch oex As OperationCanceledException When Token.IsCancellationRequested Or PersonalToken.IsCancellationRequested MyMainLOG = $"{ToStringForLog()}: downloading canceled" Canceled = True + Catch exit_ex As ExitException + If Not exit_ex.Silent Then + If exit_ex.SimpleLogLine Then + MyMainLOG = $"{ToStringForLog()}: downloading canceled (exit) ({exit_ex.Message})" + Else + ErrorsDescriber.Execute(EDP.SendToLog, exit_ex, $"{ToStringForLog()}: downloading canceled (exit)") + End If + End If + Canceled = True Catch dex As ObjectDisposedException When Disposed Canceled = True Catch ex As Exception LogError(ex, "downloading data error") HasError = True Finally + If Not UserExists Then MyMainLOG = $"User '{ToStringForLog()}' not found on the site" If Not Responser Is Nothing Then Responser.Dispose() : Responser = Nothing If Not Canceled Then _DataParsed = True _ContentNew.Clear() @@ -1065,6 +1251,8 @@ BlockNullPicture: _ForceSaveUserData = False _ForceSaveUserInfo = False ProgressPre.Done() + __DOWNLOAD_IN_PROGRESS = False + OnUserDownloadStateChanged(False) End Try End Sub Protected Sub UpdateDataFiles() @@ -1087,6 +1275,13 @@ BlockNullPicture: End If End Sub Protected MustOverride Sub DownloadDataF(ByVal Token As CancellationToken) + Protected Function CreateCache() As CacheKeeper + Dim Cache As New CacheKeeper($"{DownloadContentDefault_GetRootDir()}\_tCache\") + Cache.CacheDeleteError = CacheDeletionError(Cache) + If Cache.RootDirectory.Exists(SFO.Path, False) Then Cache.RootDirectory.Delete(SFO.Path, SFODelete.DeletePermanently, EDP.ReturnValue) + Cache.Validate() + Return Cache + End Function #Region "DownloadSingleObject" Protected IsSingleObjectDownload As Boolean = False Friend Overridable Sub DownloadSingleObject(ByVal Data As YouTube.Objects.IYouTubeMediaContainer, ByVal Token As CancellationToken) Implements IUserData.DownloadSingleObject @@ -1124,7 +1319,12 @@ BlockNullPicture: DirectCast(Data, IDownloadableMedia).ThumbnailFile = _ContentNew(0).File ElseIf Settings.STDownloader_TakeSnapshot And Settings.FfmpegFile.Exists And Not Settings.STDownloader_RemoveDownloadedAutomatically Then Dim f As SFile = _ContentNew(0).File - Dim ff As SFile = f + Dim ff As SFile + If Settings.STDownloader_SnapshotsKeepWithFiles Then + ff = f + Else + ff = Settings.CacheSnapshots(Settings.STDownloader_SnapShotsCachePermamnent).NewFile + End If ff.Name &= "_thumb" ff.Extension = "jpg" f = Web.FFMPEG.TakeSnapshot(f, ff, Settings.FfmpegFile, TimeSpan.FromSeconds(1),,, EDP.LogMessageValue) @@ -1423,6 +1623,7 @@ BlockNullPicture: If __isVideo Then fileNumProvider.FileName = f.Name : f = SFile.IndexReindex(f,,, fileNumProvider) __interrupt = False + If IsSingleObjectDownload Then f.Exists(SFO.Path, True) If v.Type = UTypes.m3u8 And UseInternalM3U8Function Then f = DownloadM3U8(v.URL, v, f, Token) If f.IsEmptyString Then Throw New Exception("M3U8 download failed") @@ -1519,8 +1720,10 @@ BlockNullPicture: Protected Function ProcessException(ByVal ex As Exception, ByVal Token As CancellationToken, ByVal Message As String, Optional ByVal RDE As Boolean = True, Optional ByVal EObj As Object = Nothing, Optional ByVal ThrowEx As Boolean = True) As Integer - If Not ((TypeOf ex Is OperationCanceledException And Token.IsCancellationRequested) Or - (TypeOf ex Is ObjectDisposedException And Disposed)) Then + If TypeOf ex Is ExitException Then + Throw ex + ElseIf Not ((TypeOf ex Is OperationCanceledException And (Token.IsCancellationRequested Or PersonalToken.IsCancellationRequested)) Or + (TypeOf ex Is ObjectDisposedException And Disposed)) Then If RDE Then Dim v% = DownloadingException(ex, Message, True, EObj) If v = 0 Then LogError(ex, Message) : HasError = True @@ -1579,7 +1782,67 @@ BlockNullPicture: End Sub #End Region #End Region -#Region "Delete, Move, Merge, Copy" +#Region "Erase, Delete, Move, Merge, Copy" + Friend Shared Function GetEraseMode(ByVal Users As IEnumerable(Of IUserData)) As IUserData.EraseMode + Dim mode As IUserData.EraseMode = IUserData.EraseMode.None + If Users.ListExists Then + Dim m As New MMessage("The data of the following users will be erased:" & vbCr & vbCr, "Erase data", + {New MsgBoxButton("History and Data", "All files (images and videos) will be deleted; download history will be deleted."), + New MsgBoxButton("Data", "All files (images and videos) will be deleted; download history will not be affected."), + New MsgBoxButton("History", "All files (images and videos) will not be affected; download history will be deleted."), + New MsgBoxButton("Cancel") + }, MsgBoxStyle.Exclamation) With {.ButtonsPerRow = 4} + Dim collectionsCount% = Users.Count(Function(u) u.IsCollection) + m.Text &= Users.ListToStringE(vbNewLine, MainFrameObj.GetUserListProvider(collectionsCount > 0)) + m.Text &= vbCr.StringDup(2) + If collectionsCount > 0 Then + If collectionsCount = 1 And Users.Count = 1 Then + m.Text &= $"THIS USER IS A COLLECTION OF {DirectCast(Users(0), UserDataBind).Count} USERS. THE DATA WILL BE ERASED FOR ALL OF THEM." + Else + m.Text &= "ONE OR MORE USERS IN THE LIST IS A COLLECTION. THE DATA WILL BE ERASED FOR EACH USER OF EACH COLLECTION." + End If + m.Text &= vbCr.StringDup(2) + End If + m.Text &= "Are you sure you want to erase the data?" + Select Case m.Show + Case 0 : mode = IUserData.EraseMode.Data + IUserData.EraseMode.History + Case 1 : mode = IUserData.EraseMode.Data + Case 2 : mode = IUserData.EraseMode.History + End Select + End If + Return mode + End Function + Friend Overridable Function EraseData(ByVal Mode As IUserData.EraseMode) As Boolean Implements IUserData.EraseData + Try + Dim result As Boolean = False + If Not Mode = IUserData.EraseMode.None And Not DataMerging Then + Dim m() As IUserData.EraseMode = Mode.EnumExtract(Of IUserData.EraseMode) + If m.ListExists Then + Dim e As New ErrorsDescriber(EDP.ReturnValue) + If m.Contains(IUserData.EraseMode.History) Then + If MyFilePosts.Delete(SFO.File, SFODelete.DeleteToRecycleBin, e) Then result = True + If MyFileData.Delete(SFO.File, SFODelete.DeleteToRecycleBin, e) Then result = True + If result Then + _TempPostsList.Clear() + _TempMediaList.Clear() + _ContentNew.Clear() + _ContentList.Clear() + End If + End If + If m.Contains(IUserData.EraseMode.Data) Then + Dim files As List(Of SFile) = SFile.GetFiles(DownloadContentDefault_GetRootDir.CSFileP,, SearchOption.AllDirectories, e) + If files.ListExists Then files.RemoveAll(Function(f) Not f.Extension.IsEmptyString AndAlso (f.Extension = "txt" Or f.Extension = "xml")) + If files.ListExists Then files.ForEach(Sub(f) f.Delete(SFO.File, Settings.DeleteMode, e)) + LatestData.Clear() + result = True + End If + End If + End If + Return result + Catch ex As Exception + Return ErrorsDescriber.Execute(EDP.SendToLog + EDP.ReturnValue, ex, $"EraseData({CInt(Mode)}): {ToStringForLog()}", False) + End Try + End Function Friend Overridable Function Delete(Optional ByVal Multiple As Boolean = False, Optional ByVal CollectionValue As Integer = -1) As Integer Implements IUserData.Delete Dim f As SFile = SFile.GetPath(MyFile.CutPath.Path) If f.Exists(SFO.Path, False) AndAlso (User.Merged OrElse f.Delete(SFO.Path, Settings.DeleteMode)) Then @@ -1601,7 +1864,8 @@ BlockNullPicture: Try Dim f As SFile Dim v As Boolean = IsVirtual - If IncludedInCollection Then + + If IncludedInCollection And __CollectionName.IsEmptyString And __SpecialCollectionPath.IsEmptyString Then Settings.Users.Add(Me) Removed = False User.CollectionName = String.Empty @@ -1634,7 +1898,8 @@ BlockNullPicture: f.Delete(SFO.Path, Settings.DeleteMode, EDP.ThrowException) End If f.CutPath.Exists(SFO.Path) - Directory.Move(UserBefore.File.CutPath(, EDP.ThrowException).Path, f.Path) + SFile.Move(UserBefore.File.CutPath(, EDP.ThrowException), f, SFO.Path,, + SFODelete.EmptyOnly + SFODelete.DeleteToRecycleBin + SFODelete.OnCancelThrowException, EDP.ThrowException) If Not ScriptData.IsEmptyString AndAlso ScriptData.Contains(UserBefore.File.PathNoSeparator) Then _ ScriptData = ScriptData.Replace(UserBefore.File.PathNoSeparator, MyFile.PathNoSeparator) End If @@ -1772,10 +2037,11 @@ BlockNullPicture: ''' Friend Overridable Overloads Sub ThrowAny(ByVal Token As CancellationToken) Token.ThrowIfCancellationRequested() + PersonalToken.ThrowIfCancellationRequested() ThrowIfDisposed() End Sub #End Region - Protected Function ToStringForLog() As String + Friend Function ToStringForLog() As String Return $"{IIf(IncludedInCollection, $"[{CollectionName}] - ", String.Empty)}[{Site}] - {Name}" End Function Public Overrides Function ToString() As String @@ -1807,6 +2073,21 @@ BlockNullPicture: End Sub Private Sub BTT_CONTEXT_DELETE_Click(sender As Object, e As EventArgs) Handles BTT_CONTEXT_DELETE.Click End Sub + Private Sub BTT_CONTEXT_ERASE_Click(sender As Object, e As EventArgs) Handles BTT_CONTEXT_ERASE.Click + Const msgTitle$ = "Erase data" + Try + Dim m As IUserData.EraseMode = GetEraseMode({Me}) + If Not m = IUserData.EraseMode.None Then + If EraseData(m) Then + MsgBoxE({"User data has been erased.", msgTitle}) + Else + MsgBoxE({"User data has not been erased.", msgTitle}, vbExclamation) + End If + End If + Catch ex As Exception + ErrorsDescriber.Execute(EDP.LogMessageValue, ex, msgTitle) + End Try + End Sub Private Sub BTT_CONTEXT_OPEN_PATH_Click(sender As Object, e As EventArgs) Handles BTT_CONTEXT_OPEN_PATH.Click OpenFolder() End Sub @@ -1858,6 +2139,7 @@ BlockNullPicture: If Not BTT_CONTEXT_DOWN Is Nothing Then BTT_CONTEXT_DOWN.Dispose() If Not BTT_CONTEXT_EDIT Is Nothing Then BTT_CONTEXT_EDIT.Dispose() If Not BTT_CONTEXT_DELETE Is Nothing Then BTT_CONTEXT_DELETE.Dispose() + If Not BTT_CONTEXT_ERASE Is Nothing Then BTT_CONTEXT_ERASE.Dispose() If Not BTT_CONTEXT_OPEN_PATH Is Nothing Then BTT_CONTEXT_OPEN_PATH.Dispose() If Not BTT_CONTEXT_OPEN_SITE Is Nothing Then BTT_CONTEXT_OPEN_SITE.Dispose() UserUpdatedEventHandlers.Clear() @@ -1875,85 +2157,4 @@ BlockNullPicture: End Sub #End Region End Class -#Region "Base interfaces" - Friend Interface IContentProvider - ReadOnly Property Site As String - ReadOnly Property Name As String - Property ID As String - Property FriendlyName As String - Property Description As String - Property Favorite As Boolean - Property Temporary As Boolean - Sub OpenSite(Optional ByVal e As ErrorsDescriber = Nothing) - Sub DownloadData(ByVal Token As CancellationToken) - Sub DownloadSingleObject(ByVal Data As YouTube.Objects.IYouTubeMediaContainer, ByVal Token As CancellationToken) - End Interface - Friend Interface IUserData : Inherits IContentProvider, IComparable(Of UserDataBase), IComparable, IEquatable(Of UserDataBase), IIndexable, IDisposable - Event UserUpdated(ByVal User As IUserData) - Property ParseUserMediaOnly As Boolean -#Region "Images" - Function GetPicture() As Image - Sub SetPicture(ByVal f As SFile) -#End Region -#Region "Collection support" - ReadOnly Property IsCollection As Boolean - Property CollectionName As String - ReadOnly Property IncludedInCollection As Boolean - ReadOnly Property UserModel As UsageModel - ReadOnly Property CollectionModel As UsageModel - ReadOnly Property IsVirtual As Boolean - ReadOnly Property Labels As List(Of String) -#End Region - Property Exists As Boolean - Property Suspended As Boolean - Property ReadyForDownload As Boolean - Property HOST As SettingsHost - Property [File] As SFile - Property FileExists As Boolean - Property DownloadedPictures(ByVal Total As Boolean) As Integer - Property DownloadedVideos(ByVal Total As Boolean) As Integer - ReadOnly Property DownloadedTotal(Optional ByVal Total As Boolean = True) As Integer - ReadOnly Property DownloadedInformation As String - Property HasError As Boolean - ReadOnly Property FitToAddParams As Boolean - ReadOnly Property Key As String - Property DownloadImages As Boolean - Property DownloadVideos As Boolean - Property DownloadMissingOnly As Boolean - Property ScriptUse As Boolean - Property ScriptData As String - Function GetLVI(ByVal Destination As ListView) As ListViewItem - Function GetLVIGroup(ByVal Destination As ListView) As ListViewGroup - Sub LoadUserInformation() - Sub UpdateUserInformation() - ''' - ''' 0 - Nothing removed
- ''' 1 - User removed
- ''' 2 - Collection removed
- ''' 3 - Collection split - '''
- Function Delete(Optional ByVal Multiple As Boolean = False, Optional ByVal CollectionValue As Integer = -1) As Integer - Function MoveFiles(ByVal CollectionName As String, ByVal SpecialCollectionPath As SFile) As Boolean - Function CopyFiles(ByVal DestinationPath As SFile, Optional ByVal e As ErrorsDescriber = Nothing) As Boolean - Sub OpenFolder() - Property DownloadTopCount As Integer? - Property DownloadDateFrom As Date? - Property DownloadDateTo As Date? - Sub SetEnvironment(ByRef h As SettingsHost, ByVal u As UserInfo, ByVal _LoadUserInformation As Boolean, - Optional ByVal AttachUserInfo As Boolean = True) - ReadOnly Property Disposed As Boolean - End Interface - Friend Interface IChannelLimits - Property AutoGetLimits As Boolean - Property DownloadLimitCount As Integer? - Property DownloadLimitPost As String - Property DownloadLimitDate As Date? - Overloads Sub SetLimit(Optional ByVal Post As String = "", Optional ByVal Count As Integer? = Nothing, Optional ByVal [Date] As Date? = Nothing) - Overloads Sub SetLimit(ByVal Source As IChannelLimits) - End Interface - Friend Interface IChannelData : Inherits IContentProvider, IChannelLimits - Property SkipExistsUsers As Boolean - Property SaveToCache As Boolean - End Interface -#End Region End Namespace \ No newline at end of file diff --git a/SCrawler/API/Instagram/SiteSettings.vb b/SCrawler/API/Instagram/SiteSettings.vb index 0536199..7366845 100644 --- a/SCrawler/API/Instagram/SiteSettings.vb +++ b/SCrawler/API/Instagram/SiteSettings.vb @@ -74,9 +74,9 @@ Namespace API.Instagram Friend Const Header_IG_WWW_CLAIM As String = "x-ig-www-claim" Friend Const Header_CSRF_TOKEN As String = "x-csrftoken" Private Const Header_ASBD_ID As String = "X-Asbd-Id" - Private ReadOnly Header_Browser As New HttpHeader("Sec-Ch-Ua", """Google Chrome"";v=""113"", ""Chromium"";v=""113"", ""Not-A.Brand"";v=""24""") - Private ReadOnly Header_BrowserExt As New HttpHeader("Sec-Ch-Ua-Full-Version-List", """Google Chrome"";v=""113.0.5672.127"", ""Chromium"";v=""113.0.5672.127"", ""Not-A.Brand"";v=""24.0.0.0""") - Private ReadOnly Header_Platform As New HttpHeader("Sec-Ch-Ua-Platform-Version", """10.0.0""") + Private Const Header_Browser As String = "Sec-Ch-Ua" + Private Const Header_BrowserExt As String = "Sec-Ch-Ua-Full-Version-List" + Private Const Header_Platform As String = "Sec-Ch-Ua-Platform-Version" Friend ReadOnly Property HashTagged As PropertyValue @@ -108,9 +108,9 @@ Namespace API.Instagram Case NameOf(HH_ASBD_ID) : f = Header_ASBD_ID Case NameOf(HH_IG_WWW_CLAIM) : f = Header_IG_WWW_CLAIM Case NameOf(HH_CSRF_TOKEN) : f = Header_CSRF_TOKEN - Case NameOf(HH_BROWSER) : f = Header_Browser.Name - Case NameOf(HH_BROWSER_EXT) : f = Header_BrowserExt.Name - Case NameOf(HH_PLATFORM) : f = Header_Platform.Name + Case NameOf(HH_BROWSER) : f = Header_Browser + Case NameOf(HH_BROWSER_EXT) : f = Header_BrowserExt + Case NameOf(HH_PLATFORM) : f = Header_Platform Case NameOf(HH_USER_AGENT) : isUserAgent = True End Select If Not f.IsEmptyString Then @@ -219,20 +219,6 @@ Namespace API.Instagram Dim platform$ = String.Empty Dim useragent$ = String.Empty - Dim __UpdateHeader As Action(Of HttpHeader, Boolean) = Sub(ByVal h As HttpHeader, ByVal UpdateValueIfEmpty As Boolean) - With Responser.Headers - Dim i% = .IndexOf(h) - Dim hh As HttpHeader - If i >= 0 Then - hh = .Item(i) - If hh.Value.IsEmptyString And UpdateValueIfEmpty Then hh.Value = h.Value - Else - hh = h - End If - .Add(hh) - End With - End Sub - With Responser .Accept = "*/*" useragent = .UserAgent @@ -242,19 +228,13 @@ Namespace API.Instagram app_id = .Value(Header_IG_APP_ID) www_claim = .Value(Header_IG_WWW_CLAIM) asbd = .Value(Header_ASBD_ID) - browser = .Value(Header_Browser.Name) - browserExt = .Value(Header_BrowserExt.Name) - platform = .Value(Header_Platform.Name) + browser = .Value(Header_Browser) + browserExt = .Value(Header_BrowserExt) + platform = .Value(Header_Platform) End If .Add("Dnt", 1) - __UpdateHeader(Header_Browser, browser.IsEmptyString) - browser = .Value(Header_Browser.Name) - __UpdateHeader(Header_BrowserExt, browserExt.IsEmptyString) - browserExt = .Value(Header_BrowserExt.Name) .Add("Sec-Ch-Ua-Mobile", "?0") .Add("Sec-Ch-Ua-Platform", """Windows""") - __UpdateHeader(Header_Platform, platform.IsEmptyString) - platform = .Value(Header_Platform.Name) .Add("Sec-Fetch-Dest", "empty") .Add("Sec-Fetch-Mode", "cors") .Add("Sec-Fetch-Site", "same-origin") @@ -301,6 +281,7 @@ Namespace API.Instagram LastRequestsCountLabel = New PropertyValue(LastRequestsCountLabelStr.Invoke(LastRequestsCount.Value)) AddHandler LastRequestsCount.ValueChanged, Sub(sender, e) LastRequestsCountLabel.Value = LastRequestsCountLabelStr.Invoke(DirectCast(sender, XMLValue(Of Integer)).ValueF.Value) + _AllowUserAgentUpdate = False UrlPatternUser = "https://www.instagram.com/{0}/" UserRegex = RParams.DMS("[htps:/]{7,8}.*?instagram.com/([^/]+)", 1) ImageVideoContains = "instagram.com" diff --git a/SCrawler/API/Instagram/UserData.vb b/SCrawler/API/Instagram/UserData.vb index 35a38e8..d6dab24 100644 --- a/SCrawler/API/Instagram/UserData.vb +++ b/SCrawler/API/Instagram/UserData.vb @@ -26,6 +26,7 @@ Namespace API.Instagram Private Const Name_GetStories As String = "GetStories" Private Const Name_GetTagged As String = "GetTaggedData" Private Const Name_TaggedChecked As String = "TaggedChecked" + Private Const Name_NameTrue As String = "NameTrue" #End Region #Region "Declarations" Private Structure PostKV : Implements IEContainerProvider @@ -75,6 +76,13 @@ Namespace API.Instagram Friend Property GetTimeline As Boolean = True Friend Property GetStories As Boolean Friend Property GetTaggedData As Boolean + Private _NameTrue As String = String.Empty + Private ReadOnly Property NameTrue As String + Get + Return _NameTrue.IfNullOrEmpty(Name) + End Get + End Property + Private UserNameRequested As Boolean = False #End Region #Region "Exchange options" Friend Overrides Function ExchangeOptionsGet() As Object @@ -96,21 +104,25 @@ Namespace API.Instagram PostsToReparse = New List(Of PostKV) End Sub Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean) - If Loading Then - LastCursor = Container.Value(Name_LastCursor) - FirstLoadingDone = Container.Value(Name_FirstLoadingDone).FromXML(Of Boolean)(False) - GetTimeline = Container.Value(Name_GetTimeline).FromXML(Of Boolean)(CBool(MySiteSettings.GetTimeline.Value)) - GetStories = Container.Value(Name_GetStories).FromXML(Of Boolean)(CBool(MySiteSettings.GetStories.Value)) - GetTaggedData = Container.Value(Name_GetTagged).FromXML(Of Boolean)(CBool(MySiteSettings.GetTagged.Value)) - TaggedChecked = Container.Value(Name_TaggedChecked).FromXML(Of Boolean)(False) - Else - Container.Add(Name_LastCursor, LastCursor) - Container.Add(Name_FirstLoadingDone, FirstLoadingDone.BoolToInteger) - Container.Add(Name_GetTimeline, GetTimeline.BoolToInteger) - Container.Add(Name_GetStories, GetStories.BoolToInteger) - Container.Add(Name_GetTagged, GetTaggedData.BoolToInteger) - Container.Add(Name_TaggedChecked, TaggedChecked.BoolToInteger) - End If + With Container + If Loading Then + LastCursor = .Value(Name_LastCursor) + FirstLoadingDone = .Value(Name_FirstLoadingDone).FromXML(Of Boolean)(False) + GetTimeline = .Value(Name_GetTimeline).FromXML(Of Boolean)(CBool(MySiteSettings.GetTimeline.Value)) + GetStories = .Value(Name_GetStories).FromXML(Of Boolean)(CBool(MySiteSettings.GetStories.Value)) + GetTaggedData = .Value(Name_GetTagged).FromXML(Of Boolean)(CBool(MySiteSettings.GetTagged.Value)) + TaggedChecked = .Value(Name_TaggedChecked).FromXML(Of Boolean)(False) + _NameTrue = .Value(Name_NameTrue) + Else + .Add(Name_LastCursor, LastCursor) + .Add(Name_FirstLoadingDone, FirstLoadingDone.BoolToInteger) + .Add(Name_GetTimeline, GetTimeline.BoolToInteger) + .Add(Name_GetStories, GetStories.BoolToInteger) + .Add(Name_GetTagged, GetTaggedData.BoolToInteger) + .Add(Name_TaggedChecked, TaggedChecked.BoolToInteger) + .Add(Name_NameTrue, _NameTrue) + End If + End With End Sub #End Region #Region "Download data" @@ -195,6 +207,7 @@ Namespace API.Instagram End Function Private _DownloadingInProgress As Boolean = False Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken) + UserNameRequested = False Dim s As Sections = Sections.Timeline Dim errorFound As Boolean = False Try @@ -413,13 +426,13 @@ Namespace API.Instagram 'Check environment If Not IsSavedPosts Then If ID.IsEmptyString Then GetUserId() - If ID.IsEmptyString Then Throw New ArgumentException("User ID is not detected", "ID") + If ID.IsEmptyString Then Throw New Plugin.ExitException("can't get user ID") End If 'Create query Select Case Section Case Sections.Timeline - URL = $"https://www.instagram.com/api/v1/feed/user/{Name}/username/?count=50" & + URL = $"https://www.instagram.com/api/v1/feed/user/{NameTrue}/username/?count=50" & If(Cursor.IsEmptyString, String.Empty, $"&max_id={Cursor}") ENode = Nothing Case Sections.SavedPosts @@ -766,16 +779,18 @@ Namespace API.Instagram End Try End Sub #End Region -#Region "GetUserId" +#Region "GetUserId, GetUserName" Private Sub GetUserId() Dim __idFound As Boolean = False Try - Dim r$ = Responser.GetResponse($"https://i.instagram.com/api/v1/users/web_profile_info/?username={Name}",, EDP.ThrowException) + RequestsCount += 1 + Dim r$ = Responser.GetResponse($"https://i.instagram.com/api/v1/users/web_profile_info/?username={NameTrue}",, EDP.ThrowException) If Not r.IsEmptyString Then Using j As EContainer = JsonDocument.Parse(r) If Not j Is Nothing AndAlso j.Contains({"data", "user"}) Then With j({"data", "user"}) ID = .Value("id") + _ForceSaveUserData = True __idFound = True UserSiteNameUpdate(.Value("full_name")) Dim descr$ = .Value("biography") @@ -800,11 +815,43 @@ Namespace API.Instagram If Responser.StatusCode = HttpStatusCode.NotFound Or Responser.StatusCode = HttpStatusCode.BadRequest Then Throw ex Else - LogError(ex, "get Instagram user id") + LogError(ex, "get Instagram user ID") End If End If End Try End Sub + Private Function GetUserNameById() As Boolean + UserNameRequested = True + Try + If Not ID.IsEmptyString Then + RequestsCount += 1 + Dim r$ = Responser.GetResponse($"https://i.instagram.com/api/v1/users/{ID}/info/",, EDP.ReturnValue) + If Not r.IsEmptyString Then + Using j As EContainer = JsonDocument.Parse(r, EDP.ReturnValue) + If j.ListExists Then + Dim newName$ = j.Value({"user"}, "username") + If Not newName.IsEmptyString Then + Dim oldName$ = NameTrue + If Not newName = oldName Then + MyMainLOG = $"{ToStringForLog()}: username changed from '{oldName}' to '{newName}'" + _NameTrue = newName + Dim descr$ = $"Username changed from '{oldName}' to '{newName}' ({Now.ToStringDate(ADateTime.Formats.BaseDateTime)})!" + descr.StringAppendLine(UserDescription) + UserDescription = descr + _ForceSaveUserData = True + End If + Return True + End If + End If + End Using + End If + End If + Return False + Catch ex As Exception + LogError(ex, "get Instagram user name by ID") + Return False + End Try + End Function #End Region #Region "Pinned stories" Private Sub GetStoriesData(ByRef StoriesList As List(Of String), ByVal Token As CancellationToken) @@ -887,7 +934,7 @@ Namespace API.Instagram Protected Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False, Optional ByVal s As Object = Nothing) As Integer If Responser.StatusCode = HttpStatusCode.NotFound Then - UserExists = False + If Not UserNameRequested AndAlso GetUserNameById() Then Return 1 Else UserExists = False ElseIf Responser.StatusCode = HttpStatusCode.BadRequest Then HasError = True MyMainLOG = $"Instagram credentials have expired [{CInt(Responser.StatusCode)}]: {ToStringForLog()} [{s}]" @@ -906,7 +953,7 @@ Namespace API.Instagram ElseIf Responser.StatusCode = 560 Then MySiteSettings.SkipUntilNextSession = True Else - MyMainLOG = $"Instagram hash requested [{CInt(Responser.StatusCode)}]: {ToString()} [{s}]" + MyMainLOG = $"Something is wrong. Your credentials may have expired [{CInt(Responser.StatusCode)}]: {ToString()} [{s}]" DisableSection(s) If Not FromPE Then LogError(ex, Message) : HasError = True Return 0 diff --git a/SCrawler/API/Mastodon/EditorExchangeOptions.vb b/SCrawler/API/Mastodon/EditorExchangeOptions.vb index d0c24c9..6e1576f 100644 --- a/SCrawler/API/Mastodon/EditorExchangeOptions.vb +++ b/SCrawler/API/Mastodon/EditorExchangeOptions.vb @@ -9,9 +9,11 @@ Imports SCrawler.Plugin.Attributes Namespace API.Mastodon Friend Class EditorExchangeOptions : Inherits Twitter.EditorExchangeOptions + Friend Overrides Property MediaModelAllowNonUserTweets As Boolean Friend Overrides Property DownloadModelMedia As Boolean Friend Overrides Property DownloadModelProfile As Boolean Friend Overrides Property DownloadModelSearch As Boolean + Friend Overrides Property DownloadModelForceApply As Boolean Friend Sub New(ByVal s As SiteSettings) MyBase.New(s) End Sub diff --git a/SCrawler/API/Mastodon/SiteSettings.vb b/SCrawler/API/Mastodon/SiteSettings.vb index 1761c34..9026c92 100644 --- a/SCrawler/API/Mastodon/SiteSettings.vb +++ b/SCrawler/API/Mastodon/SiteSettings.vb @@ -13,7 +13,7 @@ Imports PersonalUtilities.Functions.XML Imports PersonalUtilities.Functions.RegularExpressions Imports PersonalUtilities.Tools.Web.Clients Imports PersonalUtilities.Tools.Web.Documents.JSON -Imports TS = SCrawler.API.Twitter.SiteSettings +Imports DN = SCrawler.API.Base.DeclaredNames Namespace API.Mastodon Friend Class SiteSettings : Inherits SiteSettingsBase @@ -46,8 +46,8 @@ Namespace API.Mastodon If Not PropName.IsEmptyString Then Dim f$ = String.Empty Select Case PropName - Case NameOf(Auth) : f = TS.Header_Authorization - Case NameOf(Token) : f = TS.Header_Token + Case NameOf(Auth) : f = DN.Header_Authorization + Case NameOf(Token) : f = DN.Header_CSRFToken End Select If Not f.IsEmptyString Then Responser.Headers.Remove(f) @@ -58,15 +58,15 @@ Namespace API.Mastodon End Sub #End Region #Region "Other properties" - + Friend ReadOnly Property GifsDownload As PropertyValue - + Friend ReadOnly Property GifsSpecialFolder As PropertyValue - + Friend ReadOnly Property GifsPrefix As PropertyValue Private ReadOnly Property GifStringChecker As IFormatProvider - + Friend ReadOnly Property UseMD5Comparison As PropertyValue @@ -82,13 +82,13 @@ Namespace API.Mastodon Domains.DestinationProp = SiteDomains DomainsLastUpdateDate = New PropertyValue(Now.AddYears(-1)) - Auth = New PropertyValue(Responser.Headers.Value(TS.Header_Authorization), GetType(String), Sub(v) ChangeResponserFields(NameOf(Auth), v)) - Token = New PropertyValue(Responser.Headers.Value(TS.Header_Token), GetType(String), Sub(v) ChangeResponserFields(NameOf(Token), v)) + Auth = New PropertyValue(Responser.Headers.Value(DN.Header_Authorization), GetType(String), Sub(v) ChangeResponserFields(NameOf(Auth), v)) + Token = New PropertyValue(Responser.Headers.Value(DN.Header_CSRFToken), GetType(String), Sub(v) ChangeResponserFields(NameOf(Token), v)) GifsDownload = New PropertyValue(True) GifsSpecialFolder = New PropertyValue(String.Empty, GetType(String)) GifsPrefix = New PropertyValue("GIF_") - GifStringChecker = New TS.GifStringProvider + GifStringChecker = New API.Twitter.SiteSettings.GifStringProvider UseMD5Comparison = New PropertyValue(False) MyDomain = New PropertyValue(String.Empty, GetType(String)) UserRelatedToMyDomain = New PropertyValue(False) diff --git a/SCrawler/API/Mastodon/UserData.vb b/SCrawler/API/Mastodon/UserData.vb index b9456ee..05c55b7 100644 --- a/SCrawler/API/Mastodon/UserData.vb +++ b/SCrawler/API/Mastodon/UserData.vb @@ -55,8 +55,8 @@ Namespace API.Mastodon If setDef Then MyCredentials = New Credentials With {.Domain = UserDomain, .Bearer = MySettings.Auth.Value, .Csrf = MySettings.Token.Value} End With With MyCredentials - Responser.Headers.Add(Twitter.SiteSettings.Header_Authorization, .Bearer) - Responser.Headers.Add(Twitter.SiteSettings.Header_Token, .Csrf) + Responser.Headers.Add(DeclaredNames.Header_Authorization, .Bearer) + Responser.Headers.Add(DeclaredNames.Header_CSRFToken, .Csrf) End With End Sub #End Region @@ -274,7 +274,7 @@ Namespace API.Mastodon ProcessException(ex, Token, $"ReparseMissing error [{URL}]") Finally If rList.Count > 0 Then - For i% = rList.Count - 1 To 0 Step -1 : _ContentList.RemoveAt(i) : Next + For i% = rList.Count - 1 To 0 Step -1 : _ContentList.RemoveAt(rList(i)) : Next rList.Clear() End If End Try diff --git a/SCrawler/API/OnlyFans/SiteSettings.vb b/SCrawler/API/OnlyFans/SiteSettings.vb index edf0232..90f036b 100644 --- a/SCrawler/API/OnlyFans/SiteSettings.vb +++ b/SCrawler/API/OnlyFans/SiteSettings.vb @@ -14,7 +14,7 @@ Imports PersonalUtilities.Tools.Web.Clients Imports PersonalUtilities.Tools.Web.Cookies Imports PersonalUtilities.Functions.RegularExpressions Namespace API.OnlyFans - + Friend Class SiteSettings : Inherits SiteSettingsBase #Region "Icon" Friend Overrides ReadOnly Property Icon As Icon @@ -29,6 +29,13 @@ Namespace API.OnlyFans End Property #End Region #Region "Declarations" +#Region "Options" + + Friend Property DownloadHighlights As PropertyValue + + Friend Property DownloadChatMedia As PropertyValue +#End Region +#Region "Headers" Private Const HeaderBrowser As String = "sec-ch-ua" Private Const HeaderUserID As String = "User-Id" Private Const HeaderXBC As String = "X-Bc" @@ -39,7 +46,7 @@ Namespace API.OnlyFans Private ReadOnly Property HH_X_BC As PropertyValue Private ReadOnly Property HH_APP_TOKEN As PropertyValue - + Private ReadOnly Property HH_BROWSER As PropertyValue Private ReadOnly Property UserAgent As PropertyValue @@ -59,6 +66,8 @@ Namespace API.OnlyFans Responser.UserAgent = Value End If End Sub +#End Region +#Region "Rules" Private ReadOnly Property LastDateUpdated_XML As PropertyValue Friend Property LastDateUpdated As Date Get @@ -81,6 +90,7 @@ Namespace API.OnlyFans "Change this value only if you know what you are doing."), PXML> Friend ReadOnly Property DynamicRules As PropertyValue #End Region +#End Region #Region "Initializer" Friend Sub New() MyBase.New("OnlyFans", ".onlyfans.com") @@ -110,13 +120,16 @@ Namespace API.OnlyFans UserAgent = New PropertyValue(IIf(.UserAgentExists, .UserAgent, String.Empty), GetType(String), Sub(v) UpdateHeader(NameOf(UserAgent), v)) End With + DownloadHighlights = New PropertyValue(True) + DownloadChatMedia = New PropertyValue(True) + LastDateUpdated_XML = New PropertyValue(Now.AddYears(-1), GetType(Date)) UseOldAuthRules = New PropertyValue(False) DynamicRulesUpdateInterval = New PropertyValue(60 * 24) DynamicRulesUpdateIntervalProvider = New FieldsCheckerProviderSimple(Function(v) IIf(AConvert(Of Integer)(v, 0) > 0, v, Nothing), "The value of [{0}] field must be greater than 0") DynamicRules = New PropertyValue(String.Empty, GetType(String)) - UserRegex = RParams.DMS("onlyfans.com/(\w+)", 1, EDP.ReturnValue) + UserRegex = RParams.DMS("onlyfans.com/([\w\._]+)", 1, EDP.ReturnValue) UrlPatternUser = "https://onlyfans.com/{0}" ImageVideoContains = "onlyfans.com" End Sub @@ -134,7 +147,7 @@ Namespace API.OnlyFans #End Region #Region "Download" Friend Overrides Function BaseAuthExists() As Boolean - Return Responser.CookiesExists And {HH_USER_ID, HH_X_BC, HH_APP_TOKEN, HH_BROWSER, UserAgent}.All(Function(v) ACheck(v.Value)) + Return Responser.CookiesExists And {HH_USER_ID, HH_X_BC, HH_APP_TOKEN, UserAgent}.All(Function(v) ACheck(v.Value)) End Function Friend Overrides Function ReadyToDownload(ByVal What As ISiteSettings.Download) As Boolean Return BaseAuthExists() And Not SessionAborted @@ -149,17 +162,36 @@ Namespace API.OnlyFans If Responser.Cookies.Changed Then Responser.SaveCookies() : Responser.Cookies.Changed = False End Sub #End Region -#Region "GetUserUrl, GetUserPostUrl" +#Region "GetUserUrl, GetUserPostUrl, UserOptions" Friend Overrides Function GetUserUrl(ByVal User As IPluginContentProvider) As String Return String.Format(UrlPatternUser, If(User.ID.IsEmptyString, User.Name, $"u{User.ID}")) End Function Friend Overrides Function GetUserPostUrl(ByVal User As UserDataBase, ByVal Media As UserMedia) As String If Not Media.Post.ID.IsEmptyString Then - Return String.Format("https://onlyfans.com/{0}/{1}", Media.Post.ID, If(User.ID.IsEmptyString, User.Name, $"u{User.ID}")) + Dim post$() = Media.Post.ID.Split("_") + Dim p$ = String.Empty + If post.ListExists Then + If post(0) = UserData.A_MESSAGE Then + If Not User.ID.IsEmptyString Then Return $"https://onlyfans.com/my/chats/chat/{User.ID}/" + ElseIf Not post(0) = UserData.A_HIGHLIGHT Then + p = post(0) + End If + End If + If p.IsEmptyString Then + Return GetUserUrl(User) + Else + Return String.Format("https://onlyfans.com/{0}/{1}", p, If(User.ID.IsEmptyString, User.Name, $"u{User.ID}")) + End If Else Return String.Empty End If End Function + Friend Overrides Sub UserOptions(ByRef Options As Object, ByVal OpenForm As Boolean) + If Options Is Nothing OrElse Not TypeOf Options Is UserExchangeOptions Then Options = New UserExchangeOptions(Me) + If OpenForm Then + Using f As New InternalSettingsForm(Options, Me, False) : f.ShowDialog() : End Using + End If + End Sub #End Region End Class End Namespace \ No newline at end of file diff --git a/SCrawler/API/OnlyFans/UserData.vb b/SCrawler/API/OnlyFans/UserData.vb index e5c6dc8..5e7dad5 100644 --- a/SCrawler/API/OnlyFans/UserData.vb +++ b/SCrawler/API/OnlyFans/UserData.vb @@ -19,26 +19,68 @@ Imports UTypes = SCrawler.API.Base.UserMedia.Types Imports UStates = SCrawler.API.Base.UserMedia.States Namespace API.OnlyFans Friend Class UserData : Inherits UserDataBase +#Region "XML names" + Private Const Name_MediaDownloadHighlights As String = "DownloadHighlights" + Private Const Name_MediaDownloadChatMedia As String = "DownloadChatMedia" +#End Region #Region "Declarations" Friend Property CCookie As CookieKeeper = Nothing Private Const HeaderSign As String = "Sign" Private Const HeaderTime As String = "Time" + Private ReadOnly HighlightsList As List(Of String) + Friend Property MediaDownloadHighlights As Boolean = True + Friend Property MediaDownloadChatMedia As Boolean = True Private ReadOnly Property MySettings As SiteSettings Get Return HOST.Source End Get End Property +#End Region +#Region "Load" Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean) + With Container + If Loading Then + MediaDownloadHighlights = .Value(Name_MediaDownloadHighlights).FromXML(Of Boolean)(True) + MediaDownloadChatMedia = .Value(Name_MediaDownloadChatMedia).FromXML(Of Boolean)(True) + Else + .Add(Name_MediaDownloadHighlights, MediaDownloadHighlights.BoolToInteger) + .Add(Name_MediaDownloadChatMedia, MediaDownloadChatMedia.BoolToInteger) + End If + End With + End Sub +#End Region +#Region "Exchange" + Friend Overrides Function ExchangeOptionsGet() As Object + Return New UserExchangeOptions(Me) + End Function + Friend Overrides Sub ExchangeOptionsSet(ByVal Obj As Object) + If Not Obj Is Nothing AndAlso TypeOf Obj Is UserExchangeOptions Then + With DirectCast(Obj, UserExchangeOptions) + MediaDownloadHighlights = .DownloadHighlights + MediaDownloadChatMedia = .DownloadChatMedia + End With + End If + End Sub +#End Region +#Region "Initializer" + Friend Sub New() + HighlightsList = New List(Of String) End Sub #End Region #Region "Download functions" Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken) - If Not CCookie Is Nothing Then CCookie.Dispose() - CCookie = Responser.Cookies.Copy - Responser.Cookies.Clear() - AddHandler Responser.ResponseReceived, AddressOf OnResponseReceived - UpdateCookieHeader() - DownloadData(IIf(IsSavedPosts, 0, String.Empty), Token) + If Not MySettings.SessionAborted Then + If Not CCookie Is Nothing Then CCookie.Dispose() + CCookie = Responser.Cookies.Copy + Responser.Cookies.Clear() + AddHandler Responser.ResponseReceived, AddressOf OnResponseReceived + UpdateCookieHeader() + DownloadTimeline(IIf(IsSavedPosts, 0, String.Empty), Token) + If Not IsSavedPosts Then + If MediaDownloadHighlights Then DownloadHighlights(Token) + If MediaDownloadChatMedia Then DownloadChatMedia(0, Token) + End If + End If End Sub Private Sub OnResponseReceived(ByVal Sender As Object, ByVal e As WebDataResponse) If e.CookiesExists Then @@ -49,9 +91,11 @@ Namespace API.OnlyFans Private Sub UpdateCookieHeader() Responser.Headers.Add("Cookie", CCookie.ToString(False)) End Sub + Friend Const A_HIGHLIGHT As String = "HL" + Friend Const A_MESSAGE As String = "MSG" Private Const BaseUrlPattern As String = "https://onlyfans.com{0}" - Private Overloads Sub DownloadData(ByVal Cursor As String, ByVal Token As CancellationToken) - +#Region "Download timeline" + Private Overloads Sub DownloadTimeline(ByVal Cursor As String, ByVal Token As CancellationToken) Dim url$ = String.Empty Dim _complete As Boolean = True Do @@ -122,15 +166,148 @@ Namespace API.OnlyFans If hasMore Then If IsSavedPosts Then tmpCursor = CInt(Cursor.IfNullOrEmpty(0)) + 10 - DownloadData(tmpCursor, Token) + DownloadTimeline(tmpCursor, Token) End If Catch ex As Exception - If ProcessException(ex, Token, $"data downloading error [{url}]") = 2 Then _complete = False + _complete = Not ProcessException(ex, Token, $"data downloading error [{url}]") = 2 End Try Loop While Not _complete End Sub +#End Region +#Region "Download highlights" + Private Overloads Sub DownloadHighlights(ByVal Token As CancellationToken) + HighlightsList.Clear() + DownloadHighlights(0, Token) + If HighlightsList.Count > 0 Then HighlightsList.ForEach(Sub(hl) DownloadHighlightMedia(hl, Token)) + End Sub + Private Overloads Sub DownloadHighlights(ByVal Cursor As Integer, ByVal Token As CancellationToken) + Dim url$ = String.Empty + Dim _complete As Boolean = True + Do + Try + Dim hasMore As Boolean = False + Dim path$ = $"/api2/v2/users/{ID}/stories/highlights?limit=5&offset={Cursor}" + If UpdateSignature(path) Then + url = String.Format(BaseUrlPattern, path) + ThrowAny(Token) + Dim r$ = Responser.GetResponse(url) + If Not r.IsEmptyString Then + Using j As EContainer = JsonDocument.Parse(r) + If j.ListExists Then + hasMore = j.Value("hasMore").FromXML(Of Boolean)(False) + With j("list") + If .ListExists Then + HighlightsList.AddRange(.Select(Function(e) e.Value("id"))) + Else + hasMore = False + End If + End With + End If + End Using + End If + End If + If hasMore Then DownloadHighlights(Cursor + 5, Token) + Catch ex As Exception + _complete = Not ProcessException(ex, Token, $"highlights downloading error [{url}]") = 2 + End Try + Loop While Not _complete + End Sub + Private Sub DownloadHighlightMedia(ByVal HLID As String, ByVal Token As CancellationToken) + Dim url$ = String.Empty + Dim _complete As Boolean = True + Do + Try + Dim specFolder$, postID$, postDate$ + Dim media As List(Of UserMedia) + Dim result As Boolean + Dim path$ = $"/api2/v2/stories/highlights/{HLID}" + If UpdateSignature(path) Then + url = String.Format(BaseUrlPattern, path) + ThrowAny(Token) + Dim r$ = Responser.GetResponse(url) + If Not r.IsEmptyString Then + Using j As EContainer = JsonDocument.Parse(r) + If j.ListExists Then + specFolder = j.Value("title").StringRemoveWinForbiddenSymbols.IfNullOrEmpty(HLID) + specFolder &= "*" + With j("stories") + If .ListExists Then + ProgressPre.ChangeMax(.Count) + For Each m As EContainer In .Self + ProgressPre.Perform() + postID = $"{A_HIGHLIGHT}_{HLID}_{m.Value("id")}" + postDate = m.Value("createdAt") + If Not _TempPostsList.Contains(postID) Then + _TempPostsList.Add(postID) + Else + Exit Sub + End If + result = False + media = TryCreateMedia(m, postID, postDate, result, True, specFolder) + If result Then _TempMediaList.ListAddList(media, LNC) + Next + End If + End With + End If + End Using + End If + End If + Catch ex As Exception + _complete = Not ProcessException(ex, Token, $"highlights downloading error [{url}]") = 2 + End Try + Loop While Not _complete + End Sub +#End Region +#Region "Download chat media" + Private Sub DownloadChatMedia(ByVal Cursor As Integer, ByVal Token As CancellationToken) + Dim url$ = String.Empty + Dim _complete As Boolean = True + Do + Try + Dim hasMore As Boolean = False + Dim postID$, postDate$ + Dim media As List(Of UserMedia) + Dim result As Boolean + Dim path$ = $"/api2/v2/chats/{ID}/media/?opened=1&limit=20&skip_users=all" + If Cursor > 0 Then path &= $"&offset={Cursor}" + If UpdateSignature(path) Then + url = String.Format(BaseUrlPattern, path) + ThrowAny(Token) + Dim r$ = Responser.GetResponse(url) + If Not r.IsEmptyString Then + Using j As EContainer = JsonDocument.Parse(r) + If j.ListExists Then + hasMore = j.Value("hasMore").FromXML(Of Boolean)(False) + With j("list") + If .ListExists Then + For Each m As EContainer In .Self + postID = $"{A_MESSAGE}_{m.Value("id")}" + postDate = m.Value("createdAt") + If Not _TempPostsList.Contains(postID) Then + _TempPostsList.Add(postID) + Else + Exit Sub + End If + result = False + media = TryCreateMedia(m, postID, postDate, result,, "Chats*") + If result Then _TempMediaList.ListAddList(media, LNC) + Next + End If + End With + End If + End Using + End If + End If + If hasMore Then DownloadChatMedia(Cursor + 20, Token) + Catch ex As Exception + _complete = Not ProcessException(ex, Token, $"chats downloading error [{url}]") = 2 + End Try + Loop While Not _complete + End Sub +#End Region Private Function TryCreateMedia(ByVal n As EContainer, ByVal PostID As String, Optional ByVal PostDate As String = Nothing, - Optional ByRef Result As Boolean = False) As List(Of UserMedia) + Optional ByRef Result As Boolean = False, Optional ByVal IsHL As Boolean = False, + Optional ByVal SpecFolder As String = Nothing) As List(Of UserMedia) Dim postUrl$, ext$ Dim t As UTypes Dim mList As New List(Of UserMedia) @@ -138,7 +315,11 @@ Namespace API.OnlyFans With n("media") If .ListExists Then For Each m In .Self - postUrl = m.Value({"source"}, "source").IfNullOrEmpty(m.Value("full")) + If IsHL Then + postUrl = m.Value({"files", "source"}, "url") + Else + postUrl = m.Value({"source"}, "source").IfNullOrEmpty(m.Value("full")) + End If Select Case m.Value("type") Case "photo" : t = UTypes.Picture : ext = "jpg" Case "video" : t = UTypes.Video : ext = "mp4" @@ -146,7 +327,9 @@ Namespace API.OnlyFans End Select If Not t = UTypes.Undefined And Not postUrl.IsEmptyString Then Dim media As New UserMedia(postUrl, t) With { - .Post = New UserPost(PostID, AConvert(Of Date)(PostDate, DateProvider, Nothing))} + .Post = New UserPost(PostID, AConvert(Of Date)(PostDate, DateProvider, Nothing)), + .SpecialFolder = SpecFolder + } media.File.Extension = ext Result = True mList.Add(media) @@ -157,6 +340,7 @@ Namespace API.OnlyFans Return mList End Function Private Sub GetUserID() + Const brTag$ = "
" Dim path$ = $"/api2/v2/users/{Name}" Dim url$ = String.Format(BaseUrlPattern, path) Try @@ -168,7 +352,9 @@ Namespace API.OnlyFans ID = j.Value("id") If Not ID.IsEmptyString Then _ForceSaveUserInfo = True UserSiteNameUpdate(j.Value("name")) - UserDescriptionUpdate(j.Value("about")) + Dim descr$ = j.Value("about") + If Not descr.IsEmptyString Then descr = descr.Replace(brTag, String.Empty) + UserDescriptionUpdate(descr) Dim a As Action(Of String) = Sub(ByVal address As String) If Not address.IsEmptyString Then Dim f As SFile = address @@ -232,7 +418,7 @@ Namespace API.OnlyFans ProcessException(ex, Token, $"ReparseMissing error [{URL}]") Finally If rList.Count > 0 Then - For i% = rList.Count - 1 To 0 Step -1 : _ContentList.RemoveAt(i) : Next + For i% = rList.Count - 1 To 0 Step -1 : _ContentList.RemoveAt(rList(i)) : Next rList.Clear() End If End Try @@ -347,6 +533,10 @@ Namespace API.OnlyFans ElseIf Responser.StatusCode = Net.HttpStatusCode.NotFound Then UserExists = False Return 1 + ElseIf Responser.StatusCode = Net.HttpStatusCode.GatewayTimeout Or Responser.StatusCode = 429 Then + If Responser.StatusCode = 429 Then MyMainLOG = $"[429] OnlyFans too many requests ({ToStringForLog()})" + MySettings.SessionAborted = True + Return 1 Else Return 0 End If @@ -354,7 +544,7 @@ Namespace API.OnlyFans #End Region #Region "IDisposable Support" Protected Overrides Sub Dispose(ByVal disposing As Boolean) - If Not disposedValue And disposing Then CCookie.DisposeIfReady(False) : CCookie = Nothing + If Not disposedValue And disposing Then CCookie.DisposeIfReady(False) : CCookie = Nothing : HighlightsList.Clear() MyBase.Dispose(disposing) End Sub #End Region diff --git a/SCrawler/API/OnlyFans/UserExchangeOptions.vb b/SCrawler/API/OnlyFans/UserExchangeOptions.vb new file mode 100644 index 0000000..dfe337d --- /dev/null +++ b/SCrawler/API/OnlyFans/UserExchangeOptions.vb @@ -0,0 +1,28 @@ +' Copyright (C) 2023 Andy https://github.com/AAndyProgram +' 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 SCrawler.Plugin.Attributes +Namespace API.OnlyFans + Friend Class UserExchangeOptions + + Friend Property DownloadHighlights As Boolean + + Friend Property DownloadChatMedia As Boolean + Private ReadOnly MySettings As SiteSettings + Friend Sub New(ByVal u As UserData) + DownloadHighlights = u.MediaDownloadHighlights + DownloadChatMedia = u.MediaDownloadChatMedia + MySettings = u.HOST.Source + End Sub + Friend Sub New(ByVal s As SiteSettings) + DownloadHighlights = s.DownloadHighlights.Value + DownloadChatMedia = s.DownloadChatMedia.Value + MySettings = s + End Sub + End Class +End Namespace \ No newline at end of file diff --git a/SCrawler/API/PathPlugin/UserData.vb b/SCrawler/API/PathPlugin/UserData.vb index f222cc5..45c810e 100644 --- a/SCrawler/API/PathPlugin/UserData.vb +++ b/SCrawler/API/PathPlugin/UserData.vb @@ -12,7 +12,7 @@ Namespace API.PathPlugin Private Const DOWNLOAD_ERROR As String = "The path plugin only provides user paths." Friend Overrides Property UserExists As Boolean Get - Return FileExists + Return DownloadContentDefault_GetRootDir.CSFileP.Exists(SFO.Path, False) End Get Set(ByVal e As Boolean) MyBase.UserExists = e diff --git a/SCrawler/API/Pinterest/SiteSettings.vb b/SCrawler/API/Pinterest/SiteSettings.vb index 6516d83..2b64b80 100644 --- a/SCrawler/API/Pinterest/SiteSettings.vb +++ b/SCrawler/API/Pinterest/SiteSettings.vb @@ -25,25 +25,12 @@ Namespace API.Pinterest Return My.Resources.SiteResources.PinterestPic_48 End Get End Property - Private Class ConcurrentDownloadsValidator : Inherits FieldsCheckerProviderBase - Public Overrides 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 - Dim v% = AConvert(Of Integer)(Value, -1) - Dim defV% = Settings.MaxUsersJobsCount - If v.ValueBetween(1, defV) Then - Return Value - Else - ErrorMessage = $"The number of concurrent downloads must be greater than 0 and equal to or less than {defV} (global limit)." - HasError = True - Return Nothing - End If - End Function - End Class - - Private ReadOnly Property ConcurrentDownloadsProvider As IFormatProvider - + Friend ReadOnly Property ConcurrentDownloads As PropertyValue - + + Private ReadOnly Property MyConcurrentDownloadsProvider As IFormatProvider + Friend ReadOnly Property SavedPostsUserName As PropertyValue #End Region #Region "Initializer" @@ -51,7 +38,7 @@ Namespace API.Pinterest MyBase.New("Pinterest", "pinterest.com") SavedPostsUserName = New PropertyValue(String.Empty, GetType(String)) ConcurrentDownloads = New PropertyValue(1) - ConcurrentDownloadsProvider = New ConcurrentDownloadsValidator + MyConcurrentDownloadsProvider = New ConcurrentDownloadsProvider CheckNetscapeCookiesOnEndInit = True UseNetscapeCookies = True UserRegex = RParams.DMS("https?://w{0,3}.?[^/]*?.?pinterest.com/([^/]+)/?(?(_)|([^/]*))", 0, RegexReturn.ListByMatch, EDP.ReturnValue) diff --git a/SCrawler/API/Pinterest/UserData.vb b/SCrawler/API/Pinterest/UserData.vb index 5843e3a..b494416 100644 --- a/SCrawler/API/Pinterest/UserData.vb +++ b/SCrawler/API/Pinterest/UserData.vb @@ -38,6 +38,12 @@ Namespace API.Pinterest Friend Property TrueUserName As String Friend Property TrueBoardName As String Friend Property IsUser As Boolean + Private Const BoardLabelName As String = "Board" + Friend Overrides ReadOnly Property SpecialLabels As IEnumerable(Of String) + Get + Return {UserLabelName, BoardLabelName} + End Get + End Property #End Region #Region "Load" Private Function ReconfUserName() As Boolean @@ -48,12 +54,12 @@ Namespace API.Pinterest IsUser = True If n.Length > 1 Then TrueBoardName = n(1) : IsUser = False If Not IsSavedPosts And Not IsSingleObjectDownload Then - Dim l$ = IIf(IsUser, UserLabelName, "Board") + Dim l$ = IIf(IsUser, UserLabelName, BoardLabelName) Settings.Labels.Add(l) Labels.ListAddValue(l, LNC) Labels.Sort() + Return True End If - Return True End If End If Return False @@ -66,7 +72,7 @@ Namespace API.Pinterest IsUser = .Value(Name_IsUser).FromXML(Of Boolean)(False) ReconfUserName() Else - If ReconfUserName() Then .Value(Name_LabelsName) = Labels.ListToString("|", EDP.ReturnValue) + If ReconfUserName() Then .Value(Name_LabelsName) = LabelsString .Add(Name_TrueUserName, TrueUserName) .Add(Name_TrueBoardName, TrueBoardName) .Add(Name_IsUser, IsUser.BoolToInteger) @@ -128,7 +134,7 @@ Namespace API.Pinterest Dim j As EContainer, jj As EContainer Dim rootNode$() = {"resource_response", "data"} Dim jErr As New ErrorsDescriber(EDP.SendToLog + EDP.ReturnValue) - Dim urls As List(Of String) = GetDataFromGalleryDL(URL, True) + Dim urls As List(Of String) = GetDataFromGalleryDL(URL, True, Token) If urls.ListExists Then urls.RemoveAll(Function(__url) Not __url.Contains("BoardsResource/get/")) If urls.ListExists Then ProgressPre.ChangeMax(urls.Count) @@ -177,7 +183,7 @@ Namespace API.Pinterest Dim images As List(Of Sizes) Dim imgSelector As Func(Of EContainer, Sizes) = Function(img) New Sizes(img.Value("width"), img.Value("url")) Dim fullData As Predicate(Of EContainer) = Function(e) e.Count > 5 - Dim l As List(Of String) = GetDataFromGalleryDL(Board.URL, False) + Dim l As List(Of String) = GetDataFromGalleryDL(Board.URL, False, Token) If l.ListExists Then l.RemoveAll(Function(ll) Not ll.Contains("BoardFeedResource/get/")) If l.ListExists Then ProgressPre.ChangeMax(l.Count) @@ -253,8 +259,8 @@ Namespace API.Pinterest Private Class GDLBatch : Inherits GDL.GDLBatch Private ReadOnly Property Source As UserData Private ReadOnly IsBoardsRequested As Boolean - Friend Sub New(ByRef s As UserData, ByVal IsBoardsRequested As Boolean) - MyBase.New + Friend Sub New(ByRef s As UserData, ByVal IsBoardsRequested As Boolean, ByVal _Token As CancellationToken) + MyBase.New(_Token) Source = s Me.IsBoardsRequested = IsBoardsRequested End Sub @@ -269,22 +275,24 @@ Namespace API.Pinterest Protected Overrides Async Function Validate(ByVal Value As String) As Task If IsBoardsRequested Then If ErrorOutputData.Count > 0 Then - If Await Task.Run(Of Boolean)(Function() ErrorOutputData.Exists(Function(ee) Not ee.IsEmptyString AndAlso + If Await Task.Run(Of Boolean)(Function() Token.IsCancellationRequested OrElse + ErrorOutputData.Exists(Function(ee) Not ee.IsEmptyString AndAlso ee.StartsWith(UrlTextStart))) Then Kill() End If Else - If Await Task.Run(Of Boolean)(Function() Not Value.IsEmptyString AndAlso - Source._TempPostsList.Exists(Function(v) Value.Contains(v))) Then Kill() + If Await Task.Run(Of Boolean)(Function() Token.IsCancellationRequested OrElse + (Not Value.IsEmptyString AndAlso + Source._TempPostsList.Exists(Function(v) Value.Contains(v)))) Then Kill() End If End Function End Class - Private Function GetDataFromGalleryDL(ByVal URL As String, ByVal IsBoardsRequested As Boolean) As List(Of String) + Private Function GetDataFromGalleryDL(ByVal URL As String, ByVal IsBoardsRequested As Boolean, ByVal Token As CancellationToken) As List(Of String) Dim command$ = $"gallery-dl --verbose --simulate " Try If Not URL.IsEmptyString Then If MySettings.CookiesNetscapeFile.Exists Then command &= $"--cookies ""{MySettings.CookiesNetscapeFile}"" " command &= URL - Using batch As New GDLBatch(Me, IsBoardsRequested) + Using batch As New GDLBatch(Me, IsBoardsRequested, Token) Return GetUrlsFromGalleryDl(batch, command) End Using End If diff --git a/SCrawler/API/PornHub/Declarations.vb b/SCrawler/API/PornHub/Declarations.vb index 30800e5..4cd4d60 100644 --- a/SCrawler/API/PornHub/Declarations.vb +++ b/SCrawler/API/PornHub/Declarations.vb @@ -6,6 +6,7 @@ ' ' This program is distributed in the hope that it will be useful, ' but WITHOUT ANY WARRANTY +Imports System.Text.RegularExpressions Imports PersonalUtilities.Functions.RegularExpressions Namespace API.PornHub Friend Module Declarations @@ -15,17 +16,11 @@ Namespace API.PornHub #Region "Declarations video" Friend ReadOnly RegexVideo_FlashVarsBlocks As RParams = RParams.DM("(?<=(flashvars_\['[nN]ext[vV]ideo'\]|flashvars_\d+[^ ]+? = media_\d+?);[\r\n]*?)(.+?)(?=;flashvars_\d+?)", 0, RegexReturn.List, EDP.ReturnValue) - 'TODELETE: PornHub old 'RegexVideo_FlashVarsBlock' declaration - 'Friend ReadOnly RegexVideo_FlashVarsBlock As RParams = RParams.DM("(?<=flashvars_\['[nN]ext[vV]ideo'\];[\r\n]*?)(.+?)(?=;flashvars_\d+?)", 0, EDP.ReturnValue) Friend ReadOnly RegexVideo_FlashVars_Vars As RParams = RParams.DM("var ([\w\d]{10,})=("".+?)(?=(;|\Z))", 0, RegexReturn.List) Friend ReadOnly RegexVideo_FlashVars_Compiler As RParams = RParams.DM("(?<=\*/)([\w\d\S]{10,})", 0, RegexReturn.List) Friend ReadOnly RegexVideo_FlashVars_UrlResolution As RParams = RParams.DMS("/(\d+)[^/]+\.mp4", 1, EDP.ReturnValue) - Friend ReadOnly RegexVideo_Video_All As RParams = RParams.DM("div class=""thumbnail-info-wrapper clearfix.+?[\r\n\s]*?\).)*?)(\).)*?)|)(\).)+?)(\
).)*?)(\)", + 0, RegexOptions.Singleline, RegexReturn.List, EDP.ReturnValue, UnicodeHexConverter) Friend ReadOnly RegexVideo_Video_VideoKey As RParams = RParams.DMS("viewkey=([\w\d]+)", 1, EDP.ReturnValue) Friend ReadOnly RegexVideoPageTitle As RParams = RParams.DMS("meta (property|name)=""[^:]+?:title"" content=""([^""]+)""", 2, EDP.ReturnValue) #End Region diff --git a/SCrawler/API/PornHub/SiteSettings.vb b/SCrawler/API/PornHub/SiteSettings.vb index e3d6969..67eecbf 100644 --- a/SCrawler/API/PornHub/SiteSettings.vb +++ b/SCrawler/API/PornHub/SiteSettings.vb @@ -27,6 +27,14 @@ Namespace API.PornHub End Property Friend Property DownloadUHD As PropertyValue + + Friend Property DownloadUploaded As PropertyValue + + Friend Property DownloadTagged As PropertyValue + + Friend Property DownloadPrivate As PropertyValue + + Friend Property DownloadFavorite As PropertyValue Friend ReadOnly Property DownloadGifs As PropertyValue @@ -35,7 +43,7 @@ Namespace API.PornHub ControlToolTip:="Download photo only from ModelHub. Prornstar photos hosted on PornHub itself will not be downloaded." & vbCr & "Attention! Downloading photos hosted on PornHub is a very heavy job."), PXML> Friend ReadOnly Property DownloadPhotoOnlyFromModelHub As PropertyValue - + Friend ReadOnly Property SavedPostsUserName As PropertyValue #End Region #Region "Initializer" @@ -44,14 +52,19 @@ Namespace API.PornHub With Responser : .CurlSslNoRevoke = True : .CurlInsecure = True : End With DownloadUHD = New PropertyValue(False) + DownloadUploaded = New PropertyValue(True) + DownloadTagged = New PropertyValue(False) + DownloadPrivate = New PropertyValue(False) + DownloadFavorite = New PropertyValue(False) DownloadGifsAsMp4 = New PropertyValue(True) DownloadGifs = New PropertyValue(CInt(CheckState.Indeterminate), GetType(Integer)) DownloadPhotoOnlyFromModelHub = New PropertyValue(True) SavedPostsUserName = New PropertyValue(String.Empty, GetType(String)) + _SubscriptionsAllowed = True UrlPatternUser = "https://www.pornhub.com/{0}/{1}" - UserRegex = RParams.DMS("pornhub.com/([^/]+)/([^/]+).*?", 0, RegexReturn.ListByMatch) - ImageVideoContains = "pornhub" + UserRegex = RParams.DMS("pornhub.com/(model|user[s]?|pornstar|channel[s]?)/([^/]+).*?", 0, RegexReturn.ListByMatch) + ImageVideoContains = "pornhub.com" End Sub #End Region #Region "GetInstance" @@ -67,11 +80,17 @@ Namespace API.PornHub End Function #End Region #Region "IsMyUser" + Private ReadOnly NonUserRegex As RParams = RParams.DM("(?<=pornhub.com/)((.+?)(?=[\?&]{1}page=\d+)|(.+))", 0, EDP.ReturnValue) Friend Overrides Function IsMyUser(ByVal UserURL As String) As ExchangeOptions Try - If Not UserURL.IsEmptyString Then + If Not UserURL.IsEmptyString AndAlso UserURL.ToLower.Contains("pornhub.com") Then Dim alist As List(Of String) = RegexReplace(UserURL.ToLower, UserRegex) - If alist.ListExists(3) Then Return New ExchangeOptions(Site, $"{alist(1)}_{alist(2)}") + If alist.ListExists(3) Then + Return New ExchangeOptions(Site, $"{alist(1)}_{alist(2)}") + Else + Dim opt$ = RegexReplace(UserURL, NonUserRegex) + If Not opt.IsEmptyString Then Return New ExchangeOptions(Site, opt.StringRemoveWinForbiddenSymbols) With {.Options = opt} + End If End If Return Nothing Catch ex As Exception @@ -81,7 +100,13 @@ Namespace API.PornHub #End Region #Region "GetUserUrl" Friend Overrides Function GetUserUrl(ByVal User As IPluginContentProvider) As String - With DirectCast(User, UserData) : Return String.Format(UrlPatternUser, .PersonType, .NameTrue) : End With + With DirectCast(User, UserData) + If .IsUser Then + Return String.Format(UrlPatternUser, .PersonType, .NameTrue) + Else + Return .GetNonUserUrl(0) + End If + End With End Function #End Region #Region "User options" diff --git a/SCrawler/API/PornHub/UserData.vb b/SCrawler/API/PornHub/UserData.vb index c2f21d7..15b1811 100644 --- a/SCrawler/API/PornHub/UserData.vb +++ b/SCrawler/API/PornHub/UserData.vb @@ -21,11 +21,15 @@ Namespace API.PornHub #Region "XML names" Private Const Name_PersonType As String = "PersonType" Private Const Name_NameTrue As String = "NameTrue" - Private Const Name_VideoPageModel As String = "VideoPageModel" Private Const Name_PhotoPageModel As String = "PhotoPageModel" Private Const Name_DownloadUHD As String = "DownloadUHD" + Private Const Name_DownloadUploaded As String = "DownloadUploaded" + Private Const Name_DownloadTagged As String = "DownloadTagged" + Private Const Name_DownloadPrivate As String = "DownloadPrivate" + Private Const Name_DownloadFavorite As String = "DownloadFavorite" Private Const Name_DownloadGifs As String = "DownloadGifs" Private Const Name_DownloadPhotoOnlyFromModelHub As String = "DownloadPhotoOnlyFromModelHub" + Private Const Name_IsUser As String = "IsUser" #End Region #Region "Structures" Private Structure FlashVar : Implements IRegExCreator @@ -50,18 +54,31 @@ Namespace API.PornHub Friend URL As String Friend ID As String Friend Title As String - Friend Function ToUserMedia() As UserMedia + Friend Type As VideoTypes + Friend Function ToUserMedia(Optional ByVal SpecialFolder As String = Nothing) As UserMedia Return New UserMedia(URL, UTypes.VideoPre) With { .File = If(Title.IsEmptyString, .File, New SFile($"{Title}.mp4")), - .Post = ID + .Post = ID, + .SpecialFolder = SpecialFolder } End Function Private Function CreateFromArray(ByVal ParamsArray() As String) As Object Implements IRegExCreator.CreateFromArray - If ParamsArray.ListExists Then + If ParamsArray.ListExists(4) Then URL = ParamsArray(0) ID = RegexReplace(URL, RegexVideo_Video_VideoKey) - URL = String.Format(UrlPattern, URL.TrimStart("/")) - Title = TitleHtmlConverter(ParamsArray(1)) + If ID.IsEmptyString Then + URL = String.Empty + Else + URL = String.Format(UrlPattern, URL.TrimStart("/")) + Title = TitleHtmlConverter(ParamsArray(1)) + If Not ParamsArray(2).IsEmptyString Then + Type = VideoTypes.Private + ElseIf Not ParamsArray(3).IsEmptyString Then + Type = VideoTypes.Tagged + Else + Type = VideoTypes.Uploaded + End If + End If End If Return Me End Function @@ -82,21 +99,24 @@ Namespace API.PornHub End Structure #End Region #Region "Enums" - Friend Enum VideoPageModels As Integer - [Default] = 0 - ConcatPage = 1 - Favorite = 2 - Undefined = -1 - End Enum Private Enum PhotoPageModels As Integer Undefined = 0 PornHubPage = 1 ModelHubPage = 2 End Enum + Private Enum VideoTypes + Undefined + Uploaded + [Private] + Tagged + Favorite + End Enum #End Region #Region "Constants" Private Const PersonTypeModel As String = "model" - Friend Const PersonTypeUser As String = "users" + Private Const PersonTypeUser As String = "users" + Private Const PersonTypePornstar As String = "pornstar" + Private Const PersonTypeCannel As String = "channels" #End Region #Region "Person" Friend Property PersonType As String @@ -111,11 +131,37 @@ Namespace API.PornHub End Property #End Region #Region "Advanced fields" - Friend Property VideoPageModel As VideoPageModels = VideoPageModels.Undefined + Friend Overrides ReadOnly Property FeedIsUser As Boolean + Get + Return IsUser + End Get + End Property Private Property PhotoPageModel As PhotoPageModels = PhotoPageModels.Undefined Friend Property DownloadUHD As Boolean = False + Friend Property DownloadUploaded As Boolean = True + Friend Property DownloadTagged As Boolean = False + Friend Property DownloadPrivate As Boolean = False + Friend Property DownloadFavorite As Boolean = False Friend Property DownloadGifs As Boolean Friend Property DownloadPhotoOnlyFromModelHub As Boolean = True + Friend Property IsUser As Boolean = True + Friend Property QueryString As String + Get + If IsUser Then + Return String.Empty + Else + Return GetNonUserUrl(0) + End If + End Get + Set(ByVal q As String) + UpdateUserOptions(True, q) + End Set + End Property + Friend Overrides ReadOnly Property SpecialLabels As IEnumerable(Of String) + Get + Return {SearchRequestLabelName} + End Get + End Property #End Region #Region "ExchangeOptions" Friend Overrides Function ExchangeOptionsGet() As Object @@ -125,8 +171,13 @@ Namespace API.PornHub If Not Obj Is Nothing AndAlso TypeOf Obj Is UserExchangeOptions Then With DirectCast(Obj, UserExchangeOptions) DownloadUHD = .DownloadUHD + DownloadUploaded = .DownloadUploaded + DownloadTagged = .DownloadTagged + DownloadPrivate = .DownloadPrivate + DownloadFavorite = .DownloadFavorite DownloadGifs = .DownloadGifs DownloadPhotoOnlyFromModelHub = .DownloadPhotoOnlyFromModelHub + QueryString = .QueryString End With End If End Sub @@ -136,96 +187,131 @@ Namespace API.PornHub Return DirectCast(HOST.Source, SiteSettings) End Get End Property + Private ReadOnly LastPageIDs As List(Of String) #End Region -#Region "Initializer, loader" +#Region "Initializer" Friend Sub New() + LastPageIDs = New List(Of String) UseInternalM3U8Function = True UseClientTokens = True End Sub +#End Region +#Region "Loader" + Private Function UpdateUserOptions(Optional ByVal Force As Boolean = False, Optional ByVal NewUrl As String = Nothing) As Boolean + + If Not Force OrElse (Not IsUser AndAlso Not NewUrl.IsEmptyString AndAlso MyFileSettings.Exists) Then + Dim eObj As Plugin.ExchangeOptions = Nothing + If Force Then eObj = MySettings.IsMyUser(NewUrl) + If (Force And Not eObj.UserName.IsEmptyString) Or (Not Force And Not Name.IsEmptyString And NameTrue.IsEmptyString) Then + If Not If(Force, eObj.Options, Options).IsEmptyString Then + If IsUser And Force Then + Return False + Else + IsUser = False + Options = If(Force, eObj.Options, Options) + NameTrue = Options + If Not Force Then + Settings.Labels.Add(SearchRequestLabelName) + Labels.ListAddValue(SearchRequestLabelName, LNC) + Labels.Sort() + Return True + End If + End If + Else + IsUser = True + Dim n$() = Name.Split("_") + If n.ListExists(2) Then + NameTrue = Name.Replace($"{n(0)}_", String.Empty) + PersonType = n(0) + End If + End If + End If + End If + Return False + End Function Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean) With Container - Dim SetNames As Action = Sub() - If Not Name.IsEmptyString And NameTrue.IsEmptyString Then - Dim n$() = Name.Split("_") - If n.ListExists(2) Then - NameTrue = Name.Replace($"{n(0)}_", String.Empty) - PersonType = n(0) - If (PersonType = PersonTypeModel Or PersonType = PersonTypeUser) And - VideoPageModel = VideoPageModels.Undefined Then VideoPageModel = VideoPageModels.Default - End If - End If - End Sub If Loading Then PersonType = .Value(Name_PersonType) NameTrue = .Value(Name_NameTrue) - VideoPageModel = .Value(Name_VideoPageModel).FromXML(Of Integer)(VideoPageModels.Undefined) PhotoPageModel = .Value(Name_PhotoPageModel).FromXML(Of Integer)(PhotoPageModels.Undefined) DownloadUHD = .Value(Name_DownloadUHD).FromXML(Of Boolean)(False) + DownloadUploaded = .Value(Name_DownloadUploaded).FromXML(Of Boolean)(True) + DownloadTagged = .Value(Name_DownloadTagged).FromXML(Of Boolean)(False) + DownloadPrivate = .Value(Name_DownloadPrivate).FromXML(Of Boolean)(False) + DownloadFavorite = .Value(Name_DownloadFavorite).FromXML(Of Boolean)(False) DownloadGifs = .Value(Name_DownloadGifs).FromXML(Of Integer)(False) DownloadPhotoOnlyFromModelHub = .Value(Name_DownloadPhotoOnlyFromModelHub).FromXML(Of Boolean)(True) - SetNames.Invoke() + IsUser = .Value(Name_IsUser).FromXML(Of Boolean)(True) + UpdateUserOptions() Else - SetNames.Invoke() + If UpdateUserOptions() Then .Value(Name_LabelsName) = LabelsString .Add(Name_PersonType, PersonType) .Add(Name_NameTrue, NameTrue) - .Add(Name_VideoPageModel, CInt(VideoPageModel)) .Add(Name_PhotoPageModel, CInt(PhotoPageModel)) .Add(Name_DownloadUHD, DownloadUHD.BoolToInteger) + .Add(Name_DownloadUploaded, DownloadUploaded.BoolToInteger) + .Add(Name_DownloadTagged, DownloadTagged.BoolToInteger) + .Add(Name_DownloadPrivate, DownloadPrivate.BoolToInteger) + .Add(Name_DownloadFavorite, DownloadFavorite.BoolToInteger) .Add(Name_DownloadGifs, DownloadGifs.BoolToInteger) .Add(Name_DownloadPhotoOnlyFromModelHub, DownloadPhotoOnlyFromModelHub.BoolToInteger) + .Add(Name_IsUser, IsUser.BoolToInteger) + + 'Debug.WriteLine(GetNonUserUrl(0)) + 'Debug.WriteLine(GetNonUserUrl(2)) End If End With End Sub #End Region #Region "Downloading" #Region "Download override" - Private Const DataDownloaded As Integer = -10 - Private Const DataDownloaded_NotFound As Integer = -20 Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken) Try Responser.ResetStatus() - If PersonType = PersonTypeUser Then Responser.Mode = Responser.Modes.Curl If IsSavedPosts Then - VideoPageModel = VideoPageModels.Favorite PersonType = PersonTypeUser NameTrue = MySettings.SavedPostsUserName.Value End If - Dim page% = 1 - Dim __continue As Boolean = True - Dim __videoDone As Boolean = False - Dim d% + Dim limit% = If(DownloadTopCount, -1) If DownloadVideos Then - If PersonType = PersonTypeUser Then Responser.Mode = Responser.Modes.Curl : Responser.Method = "POST" - If VideoPageModel = VideoPageModels.Undefined Then - __continue = False - d = DownloadUserVideos(page, Token) - Select Case d - Case DataDownloaded : __continue = True : page += 1 - Case 1 : VideoPageModel = VideoPageModels.ConcatPage - Case EXCEPTION_OPERATION_CANCELED : ThrowAny(Token) - Case DataDownloaded_NotFound : __videoDone = True - End Select - If Not __continue And Not __videoDone Then - d = DownloadUserVideos(page, Token) - Select Case d - Case DataDownloaded : __continue = True : page += 1 - Case 1 : VideoPageModel = VideoPageModels.Undefined - Case EXCEPTION_OPERATION_CANCELED : ThrowAny(Token) - Case DataDownloaded_NotFound : __videoDone = True - End Select + + If IsSavedPosts Or Not IsUser Or PersonType = PersonTypeUser Then + DownloadUserVideos(1, VideoTypes.Favorite, False, Token) + Else + If DownloadUploaded Then + LastPageIDs.Clear() + DownloadUserVideos(1, VideoTypes.Uploaded, False, Token) + End If + If DownloadTagged Then + LastPageIDs.Clear() + Dim lBefore% = _TempMediaList.Count + DownloadUserVideos(1, VideoTypes.Tagged, False, Token) + If PersonType = PersonTypePornstar And lBefore = _TempMediaList.Count Then + LastPageIDs.Clear() + DownloadUserVideos(1, VideoTypes.Tagged, True, Token) + End If + End If + If DownloadPrivate Then + LastPageIDs.Clear() + DownloadUserVideos(1, VideoTypes.Private, False, Token) + End If + If DownloadFavorite Then + LastPageIDs.Clear() + DownloadUserVideos(1, VideoTypes.Favorite, False, Token) End If End If - If __continue And Not __videoDone Then - Do While DownloadUserVideos(page, Token) = DataDownloaded And page < 100 : page += 1 : Loop + + If _TempMediaList.Count > 0 Then + _TempMediaList.RemoveAll(Function(m) Not m.Type = UTypes.m3u8 And Not m.Type = UTypes.VideoPre) + If limit > 0 And _TempMediaList.Count > limit Then _TempMediaList.ListAddList(_TempMediaList.ListTake(-1, limit), LAP.ClearBeforeAdd) End If - If _TempMediaList.Count > 0 Then _TempMediaList.RemoveAll(Function(m) Not m.Type = UTypes.m3u8 And Not m.Type = UTypes.VideoPre) End If - Responser.Method = "GET" - If DownloadGifs And Not IsSavedPosts Then DownloadUserGifs(Token) - If DownloadImages Then DownloadUserPhotos(Token) + If DownloadGifs And Not IsSavedPosts And Not IsSubscription And IsUser Then DownloadUserGifs(Token) + If DownloadImages And Not IsSubscription And IsUser Then DownloadUserPhotos(Token) Finally Responser.Mode = Responser.Modes.Default Responser.Method = "GET" @@ -234,72 +320,100 @@ Namespace API.PornHub End Sub #End Region #Region "Download video" - Private ReadOnly Property VideoPageType As String - Get - Select Case VideoPageModel - Case VideoPageModels.Default : Return "/videos/upload" - Case VideoPageModels.Favorite : Return "/videos/favorites/" - Case Else : Return String.Empty - End Select - End Get - End Property - Private ReadOnly Property VideoPageAppender As String - Get - Return If(PersonType = PersonTypeUser, "ajax?o=newest&page=", String.Empty) - End Get - End Property - Private Overloads Function DownloadUserVideos(ByVal Page As Integer, ByVal Token As CancellationToken) As Integer - Const VideoUrlPattern$ = "https://www.pornhub.com/{0}/{1}{2}{3}" - Const HtmlPageNotFoundVideo$ = "Error Page Not Found" + Friend Function GetNonUserUrl(ByVal Page As Integer) As String + If IsUser Then + Return String.Empty + Else + Dim url$ = $"https://www.pornhub.com/{Options}" + If Page > 1 Then + If url.Contains("?") Then + url &= $"&page={Page}" + Else + url = url.TrimEnd("/") + url &= $"?page={Page}" + End If + End If + Return url + End If + End Function + Private Sub DownloadUserVideos(ByVal Page As Integer, ByVal Type As VideoTypes, ByVal SecondMode As Boolean, ByVal Token As CancellationToken) Dim URL$ = String.Empty ProgressPre.ChangeMax(1) Try - Dim p$ - If PersonType = PersonTypeUser Then - p = Page + Dim specFolder$ = String.Empty + Dim tryNextPage As Boolean = False + Dim limit% = If(DownloadTopCount, -1) + If IsUser Then + URL = $"https://www.pornhub.com/{PersonType}/{NameTrue}" + If Type = VideoTypes.Uploaded Then + URL &= "/videos/upload" + ElseIf Type = VideoTypes.Tagged Then + If Not SecondMode Then URL &= "/videos" + specFolder = "Tagged" + ElseIf Type = VideoTypes.Private Then + URL &= "/videos/private" + specFolder = "Private" + ElseIf Type = VideoTypes.Favorite Then + URL &= "/videos/favorites" + If Not PersonType = PersonTypeUser Then specFolder = "Favorite" + Else + Throw New ArgumentException($"Type '{Type}' is not implemented in the video download function", "Type") + End If + If Page > 1 Then URL &= $"?page={Page}" Else - p = IIf(Page = 1, String.Empty, $"?page={Page}") + URL = GetNonUserUrl(Page) End If - URL = $"{String.Format(VideoUrlPattern, PersonType, NameTrue, VideoPageType, VideoPageAppender)}{p}" ThrowAny(Token) + 'Debug.WriteLine(URL) Dim r$ = Responser.GetResponse(URL) If Not r.IsEmptyString Then - If PersonType = PersonTypeUser And r.Contains(HtmlPageNotFoundVideo) Then Return DataDownloaded_NotFound - Dim l As List(Of UserVideo) = RegexFields(Of UserVideo)(r, {RegexVideo_Video_All}, {1, 2}) - Dim lw As List(Of UserVideo) = Nothing - If Not PersonType = PersonTypeUser Then lw = RegexFields(Of UserVideo)(r, {RegexVideo_Video_Wrong}, RegexVideo_Video_Wrong_Fields) + Dim l As List(Of UserVideo) = RegexFields(Of UserVideo)(r, {RegexUserVideos}, {6, 7, 3, 10}) + If l.ListExists Then l = l.ListTake(3, l.Count).ToList If l.ListExists Then - If lw.ListExists Then l.ListWithRemove(lw) + If IsUser Then + If Type = VideoTypes.Favorite Then + l.RemoveAll(Function(uv) uv.Type = VideoTypes.Private) + ElseIf Not PersonType = PersonTypeCannel Then + l.RemoveAll(Function(uv) Not uv.Type = Type) + End If + End If + If l.Count > 0 Then l.RemoveAll(Function(uv) uv.ID.IsEmptyString Or uv.URL.IsEmptyString) If l.Count > 0 Then Dim lBefore% = l.Count + Dim nonLastPageDetected As Boolean = False + Dim newLastPageIDs As New List(Of String) l.RemoveAll(Function(ByVal uv As UserVideo) As Boolean If Not _TempPostsList.Contains(uv.ID) Then _TempPostsList.Add(uv.ID) + newLastPageIDs.Add(uv.ID) Return False Else + If Not LastPageIDs.Contains(uv.ID) Then nonLastPageDetected = True + 'Debug.WriteLine($"[REMOVED]: {uv.Title}") Return True End If End Function) - If l.Count > 0 Then _TempMediaList.ListAddList(l.Select(Function(uv) uv.ToUserMedia)) - If l.Count = lBefore And l.Count > 0 Then Return DataDownloaded + 'Debug.WriteLineIf(l.Count > 0, l.Select(Function(ll) ll.Title).ListToString(vbNewLine)) + If l.Count > 0 Then _TempMediaList.ListAddList(l.Select(Function(uv) uv.ToUserMedia(specFolder))) + LastPageIDs.Clear() + If newLastPageIDs.Count > 0 Then LastPageIDs.AddRange(newLastPageIDs) : newLastPageIDs.Clear() + If l.Count > 0 AndAlso (l.Count = lBefore Or Not nonLastPageDetected) AndAlso + Not (limit > 0 And _TempMediaList.Count >= limit) Then tryNextPage = True End If End If End If - Return DataDownloaded_NotFound + + If tryNextPage Then DownloadUserVideos(Page + 1, Type, SecondMode, Token) Catch regex_ex As RegexFieldsTextBecameNullException - If PersonType = PersonTypeUser Or IsSavedPosts Then - Return DataDownloaded_NotFound - Else - Return ProcessException(regex_ex, Token, $"videos downloading error [{URL}]") - End If + If Not IsSavedPosts Then MyMainLOG = $"{ToStringForLog()}: videos not found. You may need to update your credentials." Catch ex As Exception - Return ProcessException(ex, Token, $"videos downloading error [{URL}]") + ProcessException(ex, Token, $"videos downloading error [{URL}]") Finally ProgressPre.Perform() End Try - End Function + End Sub #End Region #Region "Download GIF" Private Sub DownloadUserGifs(ByVal Token As CancellationToken) @@ -393,7 +507,7 @@ Namespace API.PornHub URL = String.Format(PhotoUrlPattern_ModelHub, NameTrue) Dim r$ = Responser.GetResponse(URL) If Not r.IsEmptyString Then - Dim l As List(Of PhotoBlock) = RegexFields(Of PhotoBlock)(r, {Regex_Photo_ModelHub_PhotoBlocks}, {1, 2}) + Dim l As List(Of PhotoBlock) = RegexFields(Of PhotoBlock)(r, {Regex_Photo_ModelHub_PhotoBlocks}, {1, 2}, EDP.ReturnValue) If l.ListExists Then l.RemoveAll(Function(ll) ll.Data.IsEmptyString) If l.ListExists Then ProgressPre.ChangeMax(l.Count) @@ -431,7 +545,7 @@ Namespace API.PornHub Dim page% Dim r$ = Responser.GetResponse(String.Format(PhotoUrlPattern_PornHub, PersonType, NameTrue)) If Not r.IsEmptyString Then - Dim l As List(Of PhotoBlock) = RegexFields(Of PhotoBlock)(r, {Regex_Photo_PornHub_PhotoBlocks}, {2, 1}) + Dim l As List(Of PhotoBlock) = RegexFields(Of PhotoBlock)(r, {Regex_Photo_PornHub_PhotoBlocks}, {2, 1}, EDP.ReturnValue) If l.ListExists Then l.RemoveAll(Function(ll) ll.AlbumID.IsEmptyString) If l.ListExists Then ProgressPre.ChangeMax(l.Count) @@ -539,17 +653,21 @@ Namespace API.PornHub End If Return False Catch ex As Exception - Return ProcessException(ex, Token, $"photos downloading error [{URL}]") + Return ProcessException(ex, Token, $"photos downloading error [{URL}]") = 1 End Try End Function #End Region #End Region #Region "ReparseVideo" Protected Overloads Overrides Sub ReparseVideo(ByVal Token As CancellationToken) - ReparseVideo(Token, False) + If IsSubscription Then + ReparseVideoSubscriptions(Token) + Else + ReparseVideo(Token, False) + End If End Sub - Protected Overloads Sub ReparseVideo(ByVal Token As CancellationToken, ByVal CreateFileName As Boolean, - Optional ByRef Data As IYouTubeMediaContainer = Nothing) + Private Overloads Sub ReparseVideo(ByVal Token As CancellationToken, ByVal CreateFileName As Boolean, + Optional ByRef Data As IYouTubeMediaContainer = Nothing) Const ERR_NEW_URL$ = "ERR_NEW_URL" Dim URL$ = String.Empty Try @@ -600,6 +718,54 @@ Namespace API.PornHub ProcessException(ex, Token, "video reparsing error", False) End Try End Sub + Private Sub ReparseVideoSubscriptions(ByVal Token As CancellationToken) + Try + If _TempMediaList.Count > 0 AndAlso _TempMediaList.Exists(Function(tm) tm.Type = UTypes.VideoPre) Then + Dim m As UserMedia + Dim r$, URL$, tmpName$, thumb$ + Dim c% = 0 + Dim rErr As New ErrorsDescriber(EDP.ReturnValue) + Progress.Maximum += _TempMediaList.Count + For i% = _TempMediaList.Count - 1 To 0 Step -1 + Progress.Perform() + If _TempMediaList(i).Type = UTypes.VideoPre Then + If Not DownloadTopCount.HasValue OrElse c <= DownloadTopCount.Value Then + m = _TempMediaList(i) + ThrowAny(Token) + Try + URL = m.URL_BASE + r = Responser.GetResponse(URL,, rErr) + If Not r.IsEmptyString Then + m.Type = UTypes.m3u8 + + thumb = RegexReplace(r, Regex_VideosThumb_OG_IMAGE) + If Not thumb.IsEmptyString Then m.URL = thumb + + tmpName = RegexReplace(r, RegexVideoPageTitle) + If Not tmpName.IsEmptyString Then + m.File.Name = TitleHtmlConverter(tmpName) + m.File.Extension = "mp4" + m.PictureOption = tmpName + End If + + _TempMediaList(i) = m + c += 1 + Else + _TempMediaList.RemoveAt(i) + End If + Catch mid_ex As Exception + _TempMediaList.RemoveAt(i) + End Try + Else + _TempMediaList.RemoveAt(i) + End If + End If + Next + End If + Catch ex As Exception + ProcessException(ex, Token, "subscriptions video reparsing error", False) + End Try + End Sub #End Region #Region "ReparseMissing" Protected Overrides Sub ReparseMissing(ByVal Token As CancellationToken) @@ -646,33 +812,6 @@ Namespace API.PornHub End Function #End Region #Region "CreateVideoURL" - 'TODELETE: PornHub old 'CreateVideoURL' function - 'Private Function CreateVideoURL(ByVal r As String) As String - ' Try - ' Dim OutStr$ = String.Empty - ' If Not r.IsEmptyString Then - ' Dim _VarBlock$ = RegexReplace(r, RegexVideo_FlashVarsBlock) - ' If Not _VarBlock.IsEmptyString Then - ' Dim vars As List(Of FlashVar) = RegexFields(Of FlashVar)(_VarBlock, {RegexVideo_FlashVars_Vars}, {1, 2}) - ' Dim compiler As List(Of String) = RegexReplace(_VarBlock, RegexVideo_FlashVars_Compiler) - ' If vars.ListExists And compiler.ListExists Then - ' Dim v$ - ' Dim i% - ' For Each var$ In compiler - ' i = vars.IndexOf(var) - ' If i >= 0 Then - ' v = vars(i).Value - ' If Not v.IsEmptyString Then OutStr &= v - ' End If - ' Next - ' End If - ' End If - ' End If - ' Return OutStr - ' Catch ex As Exception - ' Return ErrorsDescriber.Execute(EDP.SendToLog, ex, "[API.PornHub.UserData.CreateVideoURL]", String.Empty) - ' End Try - 'End Function Private Function CreateVideoURL(ByVal r As String) As String Try Dim OutStr$ = String.Empty @@ -705,8 +844,8 @@ Namespace API.PornHub End If End If - If outList.Count > 0 Then outList.RemoveAll(Function(u) u.IsEmptyString) - If outList.Count > 0 Then + If OutList.Count > 0 Then OutList.RemoveAll(Function(u) u.IsEmptyString) + If OutList.Count > 0 Then i = OutList.FindIndex(Function(u) u.Contains("urlset")) If i >= 0 Then OutStr = OutList(i) @@ -728,6 +867,9 @@ Namespace API.PornHub End If OutList.Clear() Return OutStr + Catch regex_ex As RegexFieldsTextBecameNullException + MyMainLOG = $"{ToStringForLog()}: something is wrong when parsing flashvars.{vbCr}{regex_ex.Message}" + Return String.Empty Catch ex As Exception Return ErrorsDescriber.Execute(EDP.SendToLog, ex, "[API.PornHub.UserData.CreateVideoURL]", String.Empty) End Try @@ -753,6 +895,12 @@ Namespace API.PornHub Return 0 End If End Function +#End Region +#Region "IDisposable Support" + Protected Overrides Sub Dispose(ByVal disposing As Boolean) + If Not disposedValue And disposing Then LastPageIDs.Clear() + MyBase.Dispose(disposing) + End Sub #End Region End Class End Namespace \ No newline at end of file diff --git a/SCrawler/API/PornHub/UserExchangeOptions.vb b/SCrawler/API/PornHub/UserExchangeOptions.vb index f2d1556..1a4fde3 100644 --- a/SCrawler/API/PornHub/UserExchangeOptions.vb +++ b/SCrawler/API/PornHub/UserExchangeOptions.vb @@ -8,9 +8,17 @@ ' but WITHOUT ANY WARRANTY Imports SCrawler.Plugin.Attributes Namespace API.PornHub - Friend Class UserExchangeOptions + Friend Class UserExchangeOptions : Inherits Xhamster.UserExchangeOptions Friend Property DownloadUHD As Boolean + + Friend Property DownloadUploaded As Boolean + + Friend Property DownloadTagged As Boolean + + Friend Property DownloadPrivate As Boolean + + Friend Property DownloadFavorite As Boolean Friend Property DownloadGifs As Boolean @@ -18,13 +26,22 @@ Namespace API.PornHub Private ReadOnly Property MySettings As SiteSettings Friend Sub New(ByVal u As UserData) DownloadUHD = u.DownloadUHD + DownloadUploaded = u.DownloadUploaded + DownloadTagged = u.DownloadTagged + DownloadPrivate = u.DownloadPrivate + DownloadFavorite = u.DownloadFavorite DownloadGifs = u.DownloadGifs DownloadPhotoOnlyFromModelHub = u.DownloadPhotoOnlyFromModelHub + QueryString = u.QueryString MySettings = u.HOST.Source End Sub Friend Sub New(ByVal s As SiteSettings) Dim v As CheckState = CInt(s.DownloadGifs.Value) DownloadUHD = s.DownloadUHD.Value + DownloadUploaded = s.DownloadUploaded.Value + DownloadTagged = s.DownloadTagged.Value + DownloadPrivate = s.DownloadPrivate.Value + DownloadFavorite = s.DownloadFavorite.Value DownloadGifs = Not v = CheckState.Unchecked DownloadPhotoOnlyFromModelHub = s.DownloadPhotoOnlyFromModelHub.Value MySettings = s diff --git a/SCrawler/API/Reddit/IChannelLimits.vb b/SCrawler/API/Reddit/IChannelLimits.vb new file mode 100644 index 0000000..e224ce2 --- /dev/null +++ b/SCrawler/API/Reddit/IChannelLimits.vb @@ -0,0 +1,18 @@ +' Copyright (C) 2023 Andy https://github.com/AAndyProgram +' 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 API.Reddit + Friend Interface IChannelLimits + Property AutoGetLimits As Boolean + Property DownloadLimitCount As Integer? + Property DownloadLimitPost As String + Property DownloadLimitDate As Date? + Overloads Sub SetLimit(Optional ByVal Post As String = "", Optional ByVal Count As Integer? = Nothing, Optional ByVal [Date] As Date? = Nothing) + Overloads Sub SetLimit(ByVal Source As IChannelLimits) + End Interface +End Namespace \ No newline at end of file diff --git a/SCrawler/API/Reddit/M3U8.vb b/SCrawler/API/Reddit/M3U8.vb index 0c9b331..c064beb 100644 --- a/SCrawler/API/Reddit/M3U8.vb +++ b/SCrawler/API/Reddit/M3U8.vb @@ -75,6 +75,7 @@ Namespace API.Reddit ProgressPre = New PreProgress(Progress) Me.UsePreProgress = UsePreProgress Cache = New CacheKeeper($"{OutFile.PathWithSeparator}_{Base.M3U8Base.TempCacheFolderName}\") + Cache.CacheDeleteError = Base.CacheDeletionError(Cache) CacheFiles = Cache.NewInstance End Sub #Region "Internal functions" diff --git a/SCrawler/API/Reddit/SiteSettings.vb b/SCrawler/API/Reddit/SiteSettings.vb index 8aa0bba..0c38160 100644 --- a/SCrawler/API/Reddit/SiteSettings.vb +++ b/SCrawler/API/Reddit/SiteSettings.vb @@ -9,12 +9,16 @@ Imports SCrawler.API.Base Imports SCrawler.Plugin Imports SCrawler.Plugin.Attributes +Imports PersonalUtilities.Tools.Web.Clients +Imports PersonalUtilities.Tools.Web.Documents.JSON +Imports PersonalUtilities.Functions.XML Imports PersonalUtilities.Functions.RegularExpressions Imports DownDetector = SCrawler.API.Base.DownDetector Imports Download = SCrawler.Plugin.ISiteSettings.Download Namespace API.Reddit Friend Class SiteSettings : Inherits SiteSettingsBase +#Region "Icons" Friend Overrides ReadOnly Property Icon As Icon Get Return My.Resources.SiteResources.RedditIcon_128 @@ -25,36 +29,85 @@ Namespace API.Reddit Return My.Resources.SiteResources.RedditPic_512 End Get End Property - +#End Region +#Region "Declarations" +#Region "Authorization" + + Friend ReadOnly Property AuthUserName As PropertyValue + + Friend ReadOnly Property AuthPassword As PropertyValue + + Friend ReadOnly Property ApiClientID As PropertyValue + + Friend ReadOnly Property ApiClientSecret As PropertyValue + + Friend ReadOnly Property BearerToken As PropertyValue +#Region "TokenUpdateInterval" + + Friend ReadOnly Property TokenUpdateInterval As PropertyValue + + Private ReadOnly Property TokenUpdateIntervalProvider As IFormatProvider +#End Region + Private ReadOnly Property BearerTokenDateUpdate As PropertyValue + + Friend ReadOnly Property UseTokenForTimelines As PropertyValue + + Friend ReadOnly Property UseTokenForSavedPosts As PropertyValue + + Friend ReadOnly Property UseCookiesForTimelines As PropertyValue + Friend ReadOnly Property SavedPostsUserName As PropertyValue - +#End Region +#Region "Other" + Friend ReadOnly Property UseM3U8 As PropertyValue +#End Region +#End Region +#Region "Initializer" Friend Sub New() MyBase.New(RedditSite, "reddit.com") + + Dim token$ With Responser Dim d% = .Decoders.Count .Decoders.ListAddList({SymbolsConverter.Converters.Unicode, SymbolsConverter.Converters.HTML}, LAP.NotContainsOnly) - If d <> .Decoders.Count Then .SaveSettings() + token = .Headers.Value(DeclaredNames.Header_Authorization) End With + + AuthUserName = New PropertyValue(String.Empty, GetType(String)) + AuthPassword = New PropertyValue(String.Empty, GetType(String)) + ApiClientID = New PropertyValue(String.Empty, GetType(String)) + ApiClientSecret = New PropertyValue(String.Empty, GetType(String)) + BearerToken = New PropertyValue(token, GetType(String), Sub(v) Responser.Headers.Add(DeclaredNames.Header_Authorization, v)) + TokenUpdateInterval = New PropertyValue(60 * 12) + TokenUpdateIntervalProvider = New TokenRefreshIntervalProvider + BearerTokenDateUpdate = New PropertyValue(Now.AddYears(-1)) + UseTokenForTimelines = New PropertyValue(False) + UseTokenForSavedPosts = New PropertyValue(False) + UseCookiesForTimelines = New PropertyValue(False) SavedPostsUserName = New PropertyValue(String.Empty, GetType(String)) + UseM3U8 = New PropertyValue(True) + UrlPatternUser = "https://www.reddit.com/{0}/{1}/" ImageVideoContains = "reddit.com" UserRegex = RParams.DM("[htps:/]{7,8}.*?reddit.com/([user]{1,4})/([^/]+)", 0, RegexReturn.ListByMatch, EDP.ReturnValue) End Sub +#End Region +#Region "GetInstance" Friend Overrides Function GetInstance(ByVal What As Download) As IPluginContentProvider Return New UserData End Function - Friend Const ChannelOption As String = "r" - Friend Overrides Function IsMyUser(ByVal UserURL As String) As ExchangeOptions - Dim l As List(Of String) = RegexReplace(UserURL, UserRegex) - If l.ListExists(3) Then - Dim n$ = l(2) - If Not l(1).IsEmptyString AndAlso l(1) = ChannelOption Then n &= $"@{ChannelOption}" - Return New ExchangeOptions(Site, n) - Else - Return Nothing - End If +#End Region +#Region "Available, UpdateRedGifsToken" + Friend Property SessionInterrupted As Boolean = False + Friend Overrides Function ReadyToDownload(ByVal What As Download) As Boolean + If What = Download.Main Then Return Not SessionInterrupted Else Return True End Function Friend Overrides Function Available(ByVal What As Download, ByVal Silent As Boolean) As Boolean Try @@ -72,29 +125,40 @@ Namespace API.Reddit avg.NumToString(New ANumbers With {.FormatOptions = ANumbers.Options.GroupIntegral}) & " outage reports:" & vbCr & dl.ListToString(vbCr) & vbCr & vbCr & "Do you want to continue parsing Reddit data?", "There are outage reports on Reddit"}, vbYesNo) = vbYes Then - UpdateRedGifsToken() - Return trueValue + If trueValue Then UpdateRedGifsToken() + Return trueValue AndAlso UpdateTokenIfRequired() Else Return False End If End If End If End If - UpdateRedGifsToken() - Return trueValue + If trueValue Then UpdateRedGifsToken() + Return trueValue AndAlso UpdateTokenIfRequired() Catch ex As Exception Return ErrorsDescriber.Execute(EDP.SendToLog + EDP.ReturnValue, ex, "[API.Reddit.SiteSettings.Available]", True) End Try End Function + Friend Overrides Sub DownloadDone(ByVal What As Download) + SessionInterrupted = False + MyBase.DownloadDone(What) + End Sub Private Sub UpdateRedGifsToken() DirectCast(Settings(RedGifs.RedGifsSiteKey).Source, RedGifs.SiteSettings).UpdateTokenIfRequired() End Sub - Friend Overrides Sub UserOptions(ByRef Options As Object, ByVal OpenForm As Boolean) - If Options Is Nothing OrElse Not TypeOf Options Is RedditViewExchange Then Options = New RedditViewExchange - If OpenForm Then - Using f As New RedditViewSettingsForm(Options) : f.ShowDialog() : End Using +#End Region +#Region "IsMyUser, GetUserUrl, GetUserPostUrl" + Friend Const ChannelOption As String = "r" + Friend Overrides Function IsMyUser(ByVal UserURL As String) As ExchangeOptions + Dim l As List(Of String) = RegexReplace(UserURL, UserRegex) + If l.ListExists(3) Then + Dim n$ = l(2) + If Not l(1).IsEmptyString AndAlso l(1) = ChannelOption Then n &= $"@{ChannelOption}" + Return New ExchangeOptions(Site, n) + Else + Return Nothing End If - End Sub + End Function Friend Overrides Function GetUserUrl(ByVal User As IPluginContentProvider) As String With DirectCast(User, UserData) : Return String.Format(UrlPatternUser, IIf(.IsChannel, ChannelOption, "user"), .TrueName) : End With End Function @@ -105,5 +169,90 @@ Namespace API.Reddit Return String.Empty End If End Function +#End Region +#Region "UserOptions" + Friend Overrides Sub UserOptions(ByRef Options As Object, ByVal OpenForm As Boolean) + If Options Is Nothing OrElse Not TypeOf Options Is RedditViewExchange Then Options = New RedditViewExchange + If OpenForm Then + Using f As New RedditViewSettingsForm(Options) : f.ShowDialog() : End Using + End If + End Sub +#End Region +#Region "BeginEdit, Update" + Private _OldTokenValue As String = String.Empty + Friend Overrides Sub BeginEdit() + _OldTokenValue = BearerToken.Value + MyBase.BeginEdit() + End Sub + Friend Overrides Sub Update() + If _SiteEditorFormOpened Then + Dim newTokenValue$ = BearerToken.Value + If Not newTokenValue.IsEmptyString AndAlso Not newTokenValue = _OldTokenValue Then BearerTokenDateUpdate.Value = Now + End If + MyBase.Update() + End Sub +#End Region +#Region "Token" + + Private Function TokenPropertiesChecker(ByVal p As IEnumerable(Of PropertyData)) As Boolean + If p.ListExists Then + Dim wrong As New List(Of String) + For i% = 0 To p.Count - 1 + If CStr(p(i).Value).IsEmptyString Then wrong.Add(p(i).Name) + Next + If wrong.Count > 0 Then + MsgBoxE({$"You have not completed the following fields: {wrong.ListToString}." & vbCr & + "To use OAuth authorization, all authorization fields must be filled in.", "Validate token fields"}, vbCritical) + Return False + Else + Return True + End If + End If + Return False + End Function + Private Function UpdateTokenIfRequired() As Boolean + If (CBool(UseTokenForTimelines.Value) Or CBool(UseTokenForSavedPosts.Value)) AndAlso + {AuthUserName.Value, AuthPassword.Value, ApiClientID.Value, ApiClientSecret.Value}.All(Function(v$) Not v.IsEmptyString) Then + If CDate(BearerTokenDateUpdate.Value).AddMinutes(TokenUpdateInterval.Value) <= Now Then Return UpdateToken() + End If + Return True + End Function + Private Overloads Function UpdateToken() As Boolean + Return UpdateToken(AuthUserName.Value, AuthPassword.Value, ApiClientID.Value, ApiClientSecret.Value) + End Function + + Private Overloads Function UpdateToken(ByVal UserName As String, ByVal Password As String, ByVal ClientID As String, ByVal ClientSecret As String) As Boolean + Try + Dim result As Boolean = True + If {UserName, Password, ClientID, ClientSecret}.All(Function(v) Not v.IsEmptyString) Then + result = False + Dim r$ = String.Empty + Using resp As New Responser With { + .Mode = Responser.Modes.Curl, + .Method = "POST", + .CurlArgumentsLeft = $"-d ""grant_type=password&username={UserName}&password={Password}"" --user ""{ClientID}:{ClientSecret}""" + } + r = resp.GetResponse("https://www.reddit.com/api/v1/access_token") + End Using + If Not r.IsEmptyString Then + Using j As EContainer = JsonDocument.Parse(r) + If j.ListExists Then + Dim newToken$ = j.Value("access_token") + If Not newToken.IsEmptyString Then + BearerToken.Value = $"Bearer {newToken}" + BearerTokenDateUpdate.Value = Now + Responser.SaveSettings() + result = True + End If + End If + End Using + End If + End If + Return result + Catch ex As Exception + Return ErrorsDescriber.Execute(EDP.SendToLog + EDP.ReturnValue, ex, "[Reddit.SiteSettings.UpdateToken]", False) + End Try + End Function +#End Region End Class End Namespace \ No newline at end of file diff --git a/SCrawler/API/Reddit/UserData.vb b/SCrawler/API/Reddit/UserData.vb index e8d78c8..3464cd6 100644 --- a/SCrawler/API/Reddit/UserData.vb +++ b/SCrawler/API/Reddit/UserData.vb @@ -22,7 +22,7 @@ Imports UTypes = SCrawler.API.Base.UserMedia.Types Imports CView = SCrawler.API.Reddit.IRedditView.View Imports CPeriod = SCrawler.API.Reddit.IRedditView.Period Namespace API.Reddit - Friend Class UserData : Inherits UserDataBase : Implements IChannelData, IRedditView + Friend Class UserData : Inherits UserDataBase : Implements IChannelLimits, IRedditView #Region "XML names" Private Const Name_TrueName As String = "TrueName" #End Region @@ -46,6 +46,11 @@ Namespace API.Reddit End Property Friend Property IsChannel As Boolean = False Friend Property TrueName As String = String.Empty + Friend Overrides ReadOnly Property SpecialLabels As IEnumerable(Of String) + Get + Return {CannelsLabelName, CannelsLabelName_ChannelsForm, UserLabelName} + End Get + End Property #End Region #Region "Channels Support" #Region "IChannelLimits Support" @@ -70,9 +75,9 @@ Namespace API.Reddit #End Region Friend Property ChannelInfo As Channel Private ReadOnly ChannelPostsNames As List(Of String) - Friend Property SkipExistsUsers As Boolean = False Implements IChannelData.SkipExistsUsers + Friend Property SkipExistsUsers As Boolean = False Private ReadOnly _ExistsUsersNames As List(Of String) - Friend Property SaveToCache As Boolean = False Implements IChannelData.SaveToCache + Friend Property SaveToCache As Boolean = False Friend Function GetNewChannelPosts() As IEnumerable(Of UserPost) If _ContentNew.Count > 0 Then Return (From c As UserMedia In _ContentNew Where Not c.Post.CachedFile.IsEmptyString And c.State = UStates.Downloaded @@ -127,7 +132,7 @@ Namespace API.Reddit End Sub #End Region #Region "Load and Update user info" - Private Sub UpdateNames() + Private Function UpdateNames() As Boolean If TrueName.IsEmptyString Then Dim n$() = Name.Split("@") If n.ListExists Then @@ -145,9 +150,11 @@ Namespace API.Reddit Settings.Labels.Add(l) Labels.ListAddValue(l, LNC) Labels.Sort() + Return True End If End If - End Sub + Return False + End Function Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean) With Container If Loading Then @@ -157,7 +164,7 @@ Namespace API.Reddit TrueName = .Value(Name_TrueName) UpdateNames() Else - UpdateNames() + If UpdateNames() Then .Value(Name_LabelsName) = LabelsString .Add(Name_ViewMode, CInt(ViewMode)) .Add(Name_ViewPeriod, CInt(ViewPeriod)) .Add(Name_IsChannel, IsChannel.BoolToInteger) @@ -198,6 +205,15 @@ Namespace API.Reddit End If End Sub Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken) + With MySiteSettings + If IsSavedPosts Then + If Not CBool(.UseTokenForSavedPosts.Value) Then Responser.Headers.Remove(DeclaredNames.Header_Authorization) + Else + If Not CBool(.UseCookiesForTimelines.Value) Then Responser.Cookies.Clear() + If Not CBool(.UseTokenForTimelines.Value) Then Responser.Headers.Remove(DeclaredNames.Header_Authorization) + End If + End With + _TotalPostsDownloaded = 0 If IsSavedPosts Then Responser.DecodersError = EDP.ReturnValue @@ -302,7 +318,7 @@ Namespace API.Reddit End If End Using If POST.IsEmptyString And ExistsDetected Then Exit Sub - If Not PostID.IsEmptyString And NewPostDetected Then DownloadDataUser(PostID, Token) + If Not _PostID().IsEmptyString And NewPostDetected Then DownloadDataUser(_PostID(), Token) End If _completed = True Catch ex As Exception @@ -979,8 +995,13 @@ Namespace API.Reddit UserSuspended = True ElseIf .StatusCode = HttpStatusCode.BadGateway Or .StatusCode = HttpStatusCode.ServiceUnavailable Then MyMainLOG = $"[{CInt(Responser.StatusCode)}] Reddit is currently unavailable ({ToString()})" + Throw New Plugin.ExitException With {.Silent = True} ElseIf .StatusCode = HttpStatusCode.GatewayTimeout Then Return 1 + ElseIf .StatusCode = HttpStatusCode.Unauthorized Then + MyMainLOG = $"[{CInt(Responser.StatusCode)}] Reddit credentials expired ({ToString()})" + MySiteSettings.SessionInterrupted = True + Throw New Plugin.ExitException With {.Silent = True} ElseIf .StatusCode = HttpStatusCode.InternalServerError Then If Not IsNothing(EObj) AndAlso IsNumeric(EObj) AndAlso CInt(EObj) = HttpStatusCode.InternalServerError Then Return 1 Return HttpStatusCode.InternalServerError diff --git a/SCrawler/API/Redgifs/SiteSettings.vb b/SCrawler/API/Redgifs/SiteSettings.vb index e664b28..8319762 100644 --- a/SCrawler/API/Redgifs/SiteSettings.vb +++ b/SCrawler/API/Redgifs/SiteSettings.vb @@ -28,7 +28,7 @@ Namespace API.RedGifs Return My.Resources.SiteResources.RedGifsPic_32 End Get End Property - + Friend ReadOnly Property Token As PropertyValue Private ReadOnly Property UserAgent As PropertyValue @@ -38,22 +38,6 @@ Namespace API.RedGifs Friend ReadOnly Property TokenUpdateInterval As PropertyValue - Private Class TokenIntervalProvider : Inherits FieldsCheckerProviderBase - Public Overrides 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 - TypeError = False - ErrorMessage = String.Empty - If Not ACheck(Of Integer)(Value) Then - TypeError = True - ElseIf CInt(Value) > 0 Then - Return Value - Else - ErrorMessage = $"The value of [{Name}] field must be greater than or equal to 1" - HasError = True - End If - Return Nothing - End Function - End Class Private ReadOnly Property TokenUpdateIntervalProvider As IFormatProvider #End Region @@ -64,7 +48,6 @@ Namespace API.RedGifs Dim t$ = String.Empty With Responser .Mode = Responser.Modes.WebClient - If Not .UserAgentExists Then .UserAgent = ParserUserAgent .ClientWebUseCookies = False .ClientWebUseHeaders = True t = .Headers.Value(TokenName) @@ -73,7 +56,8 @@ Namespace API.RedGifs UserAgent = New PropertyValue(Responser.UserAgent, GetType(String), Sub(v) UpdateResponse(NameOf(UserAgent), v)) TokenLastDateUpdated = New PropertyValue(Now.AddYears(-1), GetType(Date)) TokenUpdateInterval = New PropertyValue(60 * 12, GetType(Integer)) - TokenUpdateIntervalProvider = New TokenIntervalProvider + TokenUpdateIntervalProvider = New TokenRefreshIntervalProvider + _AllowUserAgentUpdate = False UrlPatternUser = "https://www.redgifs.com/users/{0}/" UserRegex = RParams.DMS("[htps:/]{7,8}.*?redgifs.com/users/([^/]+)", 1) ImageVideoContains = "redgifs" diff --git a/SCrawler/API/ThisVid/Declarations.vb b/SCrawler/API/ThisVid/Declarations.vb index a95bc94..bec2da1 100644 --- a/SCrawler/API/ThisVid/Declarations.vb +++ b/SCrawler/API/ThisVid/Declarations.vb @@ -18,5 +18,9 @@ Namespace API.ThisVid Friend ReadOnly RegExAlbumID As RParams = RParams.DMS("albumId:.'(\d+)'", 1) Friend ReadOnly RegExAlbumImagesList As RParams = RParams.DMS("""([^""]+?image\d+/?)""", 1, RegexReturn.List, EDP.ReturnValue) Friend ReadOnly RegExAlbumImageUrl As RParams = RParams.DMS("\ @@ -28,6 +30,8 @@ Namespace API.ThisVid Friend ReadOnly Property DownloadPublic As PropertyValue Friend ReadOnly Property DownloadPrivate As PropertyValue + + Friend ReadOnly Property DownloadFavourite As PropertyValue = 3 AndAlso Not data(2).IsEmptyString Then + Dim mode As SiteModes + Dim n$ = String.Empty, opt$ = String.Empty + Dim __data As Func(Of Integer, String) = Function(i) If(data.Count - 1 >= i, data(i), String.Empty) + + Select Case data(2) + Case P_Albums + Case P_Tags + mode = SiteModes.Tags + If Not __data(3).IsEmptyString Then + n = __data(3) + If Not __data(4).IsEmptyString AndAlso Not IsNumeric(__data(4)) Then opt = __data(4) + End If + Case P_Categories + mode = SiteModes.Categories + If Not __data(3).IsEmptyString Then + n = __data(3) + If Not __data(4).IsEmptyString AndAlso Not IsNumeric(__data(4)) Then opt = __data(4) + End If + Case Else + mode = SiteModes.Search + If Not __data(3).IsEmptyString AndAlso Not IsNumeric(__data(3)) Then n = __data(3) + If n.IsEmptyString AndAlso Not __data(4).IsEmptyString AndAlso Not IsNumeric(__data(4)) Then n = __data(4) + If Not n.IsEmptyString Then n = n.TrimStart("?", "q", "=") + If Not n.IsEmptyString Then + If __data(2).IsEmptyString Then + n = String.Empty + Else + opt = __data(2) + End If + End If + End Select + + opt = $"{n}@{opt}" + n = n.StringRemoveWinForbiddenSymbols + If Not n.IsEmptyString Then + n = $"{CInt(mode)}@{n}" + Return New ExchangeOptions(Site, n) With {.Options = opt} + End If + End If + End If + End If + End If + Return Nothing + End Function Friend Overrides Sub UserOptions(ByRef Options As Object, ByVal OpenForm As Boolean) If Options Is Nothing OrElse Not TypeOf Options Is UserExchangeOptions Then Options = New UserExchangeOptions(Me) If OpenForm Then diff --git a/SCrawler/API/ThisVid/UserData.vb b/SCrawler/API/ThisVid/UserData.vb index 6958ede..6c4c57d 100644 --- a/SCrawler/API/ThisVid/UserData.vb +++ b/SCrawler/API/ThisVid/UserData.vb @@ -18,7 +18,11 @@ Namespace API.ThisVid #Region "XML names" Private Const Name_DownloadPublic As String = "DownloadPublic" Private Const Name_DownloadPrivate As String = "DownloadPrivate" + Private Const Name_DownloadFavourite As String = "DownloadFavourite" Private Const Name_DifferentFolders As String = "DifferentFolders" + Private Const Name_TrueName As String = "TrueName" + Private Const Name_SiteMode As String = "SiteMode" + Private Const Name_Arguments As String = "Arguments" #End Region #Region "Structures" Private Structure Album : Implements IRegExCreator @@ -34,21 +38,127 @@ Namespace API.ThisVid End Structure #End Region #Region "Declarations" + Friend Overrides ReadOnly Property FeedIsUser As Boolean + Get + Return IsUser + End Get + End Property Friend Property DownloadPublic As Boolean = True Friend Property DownloadPrivate As Boolean = True + Friend Property DownloadFavourite As Boolean = False Friend Property DifferentFolders As Boolean = True + Friend Property TrueName As String = String.Empty + Friend Property SiteMode As SiteModes = SiteModes.User + Private Property Arguments As String = String.Empty + Friend Overrides ReadOnly Property SpecialLabels As IEnumerable(Of String) + Get + Return {SearchRequestLabelName} + End Get + End Property + Friend Property QueryString As String + Get + If SiteMode = SiteModes.User Then + Return String.Empty + Else + Return GetNonUserUrl(0) + End If + End Get + Set(ByVal q As String) + UpdateUserOptions(True, q) + End Set + End Property + Friend ReadOnly Property IsUser As Boolean + Get + Return SiteMode = SiteModes.User + End Get + End Property + Private ReadOnly Property MySettings As SiteSettings + Get + Return DirectCast(HOST.Source, SiteSettings) + End Get + End Property #End Region #Region "Loaders" + Private Function UpdateUserOptions(Optional ByVal Force As Boolean = False, Optional ByVal NewUrl As String = Nothing) As Boolean + If Not Force OrElse (Not SiteMode = SiteModes.User AndAlso Not NewUrl.IsEmptyString AndAlso MyFileSettings.Exists) Then + Dim eObj As Plugin.ExchangeOptions = Nothing + If Force Then eObj = MySettings.IsMyUser(NewUrl) + If (Force And Not eObj.UserName.IsEmptyString) Or (Not Force And TrueName.IsEmptyString) Then + Dim n$() = If(Force, eObj.UserName, Name).Split("@") + If n.ListExists(2) Then + + If Force And SiteMode = SiteModes.User Then Return False + + Dim __TrueName$, __Arguments$ + Dim __Mode As SiteModes + Dim __ForceApply As Boolean = False + Dim opt$() = If(Force, eObj.Options, Options).Split("@") + __Mode = CInt(n(0)) + If opt.Length > 1 Then + __Arguments = opt.ListTake(0, 100, EDP.ReturnValue).ListToString(String.Empty) + Else + __Arguments = String.Empty + End If + __TrueName = n(1) + + If Force AndAlso (Not TrueName = __TrueName Or Not SiteMode = __Mode) Then + If ValidateChangeSearchOptions(ToStringForLog, $"{__Mode}: {__TrueName}", $"{SiteMode}: {TrueName}") Then + __ForceApply = True + Else + Return False + End If + End If + + Arguments = __Arguments + Options = If(Force, eObj.Options, Options) + If Not Force Then + TrueName = __TrueName + SiteMode = __Mode + Settings.Labels.Add(SearchRequestLabelName) + Labels.ListAddValue(SearchRequestLabelName, LNC) + Labels.Sort() + UserSiteName = $"{SiteMode}: {TrueName}" + If FriendlyName.IsEmptyString Then FriendlyName = UserSiteName + ElseIf Force And __ForceApply Then + TrueName = __TrueName + SiteMode = __Mode + End If + Return True + Else + SiteMode = SiteModes.User + TrueName = Name + End If + End If + End If + Return False + End Function Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean) With Container If Loading Then DownloadPublic = .Value(Name_DownloadPublic).FromXML(Of Boolean)(True) DownloadPrivate = .Value(Name_DownloadPrivate).FromXML(Of Boolean)(True) + DownloadFavourite = .Value(Name_DownloadFavourite).FromXML(Of Boolean)(False) DifferentFolders = .Value(Name_DifferentFolders).FromXML(Of Boolean)(True) + TrueName = .Value(Name_TrueName) + SiteMode = .Value(Name_SiteMode).FromXML(Of Integer)(SiteModes.User) + Arguments = .Value(Name_Arguments) + UpdateUserOptions() Else + If UpdateUserOptions() Then + .Value(Name_LabelsName) = LabelsString + .Value(Name_UserSiteName) = UserSiteName + .Value(Name_FriendlyName) = FriendlyName + End If .Add(Name_DownloadPublic, DownloadPublic.BoolToInteger) .Add(Name_DownloadPrivate, DownloadPrivate.BoolToInteger) + .Add(Name_DownloadFavourite, DownloadFavourite.BoolToInteger) .Add(Name_DifferentFolders, DifferentFolders.BoolToInteger) + .Add(Name_TrueName, TrueName) + .Add(Name_SiteMode, CInt(SiteMode)) + .Add(Name_Arguments, Arguments) + + 'Debug.WriteLine(GetNonUserUrl(0)) + 'Debug.WriteLine(GetNonUserUrl(2)) End If End With End Sub @@ -60,7 +170,9 @@ Namespace API.ThisVid With DirectCast(Obj, UserExchangeOptions) DownloadPublic = .DownloadPublic DownloadPrivate = .DownloadPrivate + DownloadFavourite = .DownloadFavourite DifferentFolders = .DifferentFolders + QueryString = .QueryString End With End If End Sub @@ -111,37 +223,73 @@ Namespace API.ThisVid End Function #End Region #Region "Download functions" + Private AddedCount As Integer = 0 Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken) + AddedCount = 0 + Responser.Cookies.ChangedAllowInternalDrop = False + Responser.Cookies.Changed = False If ID.IsEmptyString Then ID = Name - If IsValid() Then + If Not IsUser OrElse IsValid() Then If IsSavedPosts Then - DownloadData(1, True, Token) + DownloadData(1, 0, Token) DownloadData_Images(Token) Else - If DownloadVideos Then - If DownloadPublic Then DownloadData(1, True, Token) - If DownloadPrivate Then DownloadData(1, False, Token) + If IsUser Then + If DownloadVideos Then + If DownloadPublic Then DownloadData(1, 0, Token) + If DownloadPrivate Then DownloadData(1, 1, Token) + If DownloadFavourite Then DownloadData(1, 2, Token) + End If + If DownloadImages And Not IsSubscription Then DownloadData_Images(Token) + Else + DownloadData(1, 0, Token) End If - If DownloadImages Then DownloadData_Images(Token) End If End If + If Responser.Cookies.Changed Then MySettings.UpdateCookies(Responser) : Responser.Cookies.Changed = False End Sub - Private Overloads Sub DownloadData(ByVal Page As Integer, ByVal IsPublic As Boolean, ByVal Token As CancellationToken) + Friend Function GetNonUserUrl(ByVal Page As Integer) As String + Dim url$ = String.Empty + Select Case SiteMode + Case SiteModes.Tags + url = $"https://thisvid.com/{SiteSettings.P_Tags}/{TrueName}/" + If Not Arguments.IsEmptyString Then url &= $"{Arguments}/" + If Page > 1 Then url &= $"{Page}/" + Case SiteModes.Categories + url = $"https://thisvid.com/{SiteSettings.P_Categories}/{TrueName}/" + If Not Arguments.IsEmptyString Then url &= $"{Arguments}/" + If Page > 1 Then url &= $"{Page}/" + Case SiteModes.Search + If Not Arguments.IsEmptyString Then + url = $"https://thisvid.com/{Arguments}/" + If Page > 1 Then url &= $"{Page}/" + url &= $"?q={TrueName}/" + End If + End Select + Return url + End Function + Private Overloads Sub DownloadData(ByVal Page As Integer, ByVal Model As Byte, ByVal Token As CancellationToken) Dim URL$ = String.Empty Try ProgressPre.ChangeMax(1) + Dim limit% = If(DownloadTopCount, -1) Dim p$ = IIf(Page = 1, String.Empty, $"{Page}/") If IsSavedPosts Then URL = $"https://thisvid.com/my_favourite_videos/{p}" + ElseIf IsUser Then + URL = $"https://thisvid.com/members/{ID}/{Interaction.Switch(Model = 0, "public", Model = 1, "private", Model = 2, "favourite")}_videos/{p}" Else - URL = $"https://thisvid.com/members/{ID}/{IIf(IsPublic, "public", "private")}_videos/{p}" + URL = GetNonUserUrl(Page) + If URL.IsEmptyString Then Throw New ArgumentNullException With {.HelpLink = 1} End If ThrowAny(Token) ProgressPre.Perform() Dim r$ = Responser.GetResponse(URL) Dim cBefore% = _TempMediaList.Count If Not r.IsEmptyString Then - Dim __SpecialFolder$ = IIf(DifferentFolders, IIf(IsPublic, "Public", "Private"), String.Empty) + Dim __SpecialFolder$ = If(DifferentFolders And Not IsSavedPosts And IsUser, + Interaction.Switch(Model = 0, "Public", Model = 1, "Private", Model = 2, "Favourite"), + String.Empty) Dim l As List(Of String) = RegexReplace(r, If(IsSavedPosts, RegExVideoListSavedPosts, RegExVideoList)) If l.ListExists Then For Each u$ In l @@ -149,6 +297,8 @@ Namespace API.ThisVid If Not _TempPostsList.Contains(u) Then _TempPostsList.Add(u) _TempMediaList.Add(New UserMedia(u) With {.Type = UserMedia.Types.VideoPre, .SpecialFolder = __SpecialFolder}) + AddedCount += 1 + If limit > 0 And AddedCount >= limit Then Exit Sub Else Exit Sub End If @@ -156,7 +306,8 @@ Namespace API.ThisVid Next End If End If - If Not cBefore = _TempMediaList.Count Then DownloadData(Page + 1, IsPublic, Token) + If Not cBefore = _TempMediaList.Count And (IsUser Or Page < 1000) Then DownloadData(Page + 1, Model, Token) + Catch aex As ArgumentNullException When aex.HelpLink = 1 Catch ex As Exception ProcessException(ex, Token, $"videos downloading error [{URL}]") End Try @@ -239,53 +390,104 @@ Namespace API.ThisVid #End Region #Region "ReparseVideo" Protected Overrides Sub ReparseVideo(ByVal Token As CancellationToken) + If IsSubscription Then + ReparseVideoSubscriptions(Token) + Else + Try + If _TempMediaList.Count > 0 Then + Dim u As UserMedia + Dim dirCmd$ = String.Empty + Dim f As SFile = Settings.YtdlpFile.File + Dim n$ + Dim cookieFile As SFile = MySettings.CookiesNetscapeFile + Dim command$ + Dim e As EContainer + ProgressPre.ChangeMax(_TempMediaList.Count) + For i% = _TempMediaList.Count - 1 To 0 Step -1 + ProgressPre.Perform() + u = _TempMediaList(i) + If u.Type = UserMedia.Types.VideoPre Then + ThrowAny(Token) + command = $"""{f}"" --verbose --dump-json " + If cookieFile.Exists Then command &= $"--no-cookies-from-browser --cookies ""{cookieFile}"" " + command &= u.URL + e = GetJson(command) + If Not e Is Nothing Then + u.URL = e.Value("url") + u.Post = New UserPost(e.Value("id"), ADateTime.ParseUnix32(e.Value("epoch"))) + If u.Post.Date.HasValue Then + Select Case CheckDatesLimit(u.Post.Date.Value, Nothing) + Case DateResult.Skip : _TempPostsList.ListAddValue(u.Post.ID, LNC) : _TempMediaList.RemoveAt(i) : Continue For + Case DateResult.Exit : Exit Sub + End Select + End If + n = TitleHtmlConverter(e.Value("title")) + If Not n.IsEmptyString Then n = n.Replace("ThisVid.com", String.Empty).StringTrim.StringTrimEnd("-").StringTrim + If n.IsEmptyString Then n = u.Post.ID + If n.IsEmptyString Then n = "VideoFile" + u.File = $"{n}.mp4" + If u.URL.IsEmptyString OrElse (Not u.Post.ID.IsEmptyString AndAlso _TempPostsList.Contains(u.Post.ID)) Then + _TempMediaList.RemoveAt(i) + Else + u.Type = UserMedia.Types.Video + _TempPostsList.Add(u.Post.ID) + _TempMediaList(i) = u + End If + e.Dispose() + End If + End If + Next + End If + Catch ex As Exception + ProcessException(ex, Token, "video reparsing error") + End Try + End If + End Sub + Private Sub ReparseVideoSubscriptions(ByVal Token As CancellationToken) Try If _TempMediaList.Count > 0 Then Dim u As UserMedia - Dim dirCmd$ = String.Empty - Dim f As SFile = Settings.YtdlpFile.File - Dim n$ - Dim cookieFile As SFile = DirectCast(HOST.Source, SiteSettings).CookiesNetscapeFile - Dim command$ - Dim e As EContainer - ProgressPre.ChangeMax(_TempMediaList.Count) + Dim n$, r$ + Dim c% = 0 + Progress.Maximum += _TempMediaList.Count For i% = _TempMediaList.Count - 1 To 0 Step -1 - ProgressPre.Perform() + Progress.Perform() u = _TempMediaList(i) If u.Type = UserMedia.Types.VideoPre Then - ThrowAny(Token) - command = $"""{f}"" --verbose --dump-json " - If cookieFile.Exists Then command &= $"--no-cookies-from-browser --cookies ""{cookieFile}"" " - command &= u.URL - e = GetJson(command) - If Not e Is Nothing Then - u.URL = e.Value("url") - u.Post = New UserPost(e.Value("id"), ADateTime.ParseUnix32(e.Value("epoch"))) - If u.Post.Date.HasValue Then - Select Case CheckDatesLimit(u.Post.Date.Value, Nothing) - Case DateResult.Skip : _TempPostsList.ListAddValue(u.Post.ID, LNC) : _TempMediaList.RemoveAt(i) : Continue For - Case DateResult.Exit : Exit Sub - End Select + If Not DownloadTopCount.HasValue OrElse c <= DownloadTopCount.Value Then + ThrowAny(Token) + r = Responser.GetResponse(u.URL,, EDP.ReturnValue) + If Not r.IsEmptyString Then + n = TitleHtmlConverter(RegexReplace(r, RegExVideoTitle)) + u.Post.ID = u.URL + If Not n.IsEmptyString Then n = n.Replace("ThisVid.com", String.Empty).StringTrim.StringTrimEnd("-").StringTrim + If n.IsEmptyString Then n = TitleHtmlConverter(u.URL.Replace("https://thisvid.com/videos/", String.Empty).StringTrim.StringTrimEnd("-").StringTrim) + If n.IsEmptyString Then n = "VideoFile" + u.File = $"{n}.mp4" + u.PictureOption = n + u.URL = RegexReplace(r, Regex_VideosThumb_OG_IMAGE) + If u.URL.IsEmptyString Then u.URL = RegexReplace(r, RegExVideosThumb1) + If u.URL.IsEmptyString Then u.URL = RegexReplace(r, RegExVideosThumb2) + If Not u.URL.IsEmptyString Then + u.URL = LinkFormatterSecure(u.URL) + u.Type = UserMedia.Types.Video + _TempPostsList.Add(u.Post.ID) + _TempMediaList(i) = u + c += 1 + Else + _TempMediaList.RemoveAt(i) + End If End If - n = TitleHtmlConverter(e.Value("title")) - If Not n.IsEmptyString Then n = n.Replace("ThisVid.com", String.Empty).StringTrim.StringTrimEnd("-").StringTrim - If n.IsEmptyString Then n = u.Post.ID - If n.IsEmptyString Then n = "VideoFile" - u.File = $"{n}.mp4" - If u.URL.IsEmptyString OrElse (Not u.Post.ID.IsEmptyString AndAlso _TempPostsList.Contains(u.Post.ID)) Then - _TempMediaList.RemoveAt(i) - Else - u.Type = UserMedia.Types.Video - _TempPostsList.Add(u.Post.ID) - _TempMediaList(i) = u - End If - e.Dispose() + Else + _TempMediaList.RemoveAt(i) End If End If Next End If Catch ex As Exception - ProcessException(ex, Token, "video reparsing error") + ProcessException(ex, Token, "subscriptions video reparsing error") + Finally + If Responser.Cookies.Changed Then MySettings.UpdateCookies(Responser) : Responser.Cookies.Changed = False End Try End Sub #End Region diff --git a/SCrawler/API/ThisVid/UserExchangeOptions.vb b/SCrawler/API/ThisVid/UserExchangeOptions.vb index 3b91793..3bf8a38 100644 --- a/SCrawler/API/ThisVid/UserExchangeOptions.vb +++ b/SCrawler/API/ThisVid/UserExchangeOptions.vb @@ -8,24 +8,29 @@ ' but WITHOUT ANY WARRANTY Imports SCrawler.Plugin.Attributes Namespace API.ThisVid - Friend Class UserExchangeOptions + Friend Class UserExchangeOptions : Inherits Xhamster.UserExchangeOptions Friend Property DownloadPublic As Boolean = True Friend Property DownloadPrivate As Boolean = True + + Friend Property DownloadFavourite As Boolean = False Friend Property DifferentFolders As Boolean = True Private ReadOnly Property MySettings As SiteSettings Friend Sub New(ByVal s As SiteSettings) DownloadPublic = s.DownloadPublic.Value DownloadPrivate = s.DownloadPrivate.Value + DownloadFavourite = s.DownloadFavourite.Value DifferentFolders = s.DifferentFolders.Value MySettings = s End Sub Friend Sub New(ByVal u As UserData) DownloadPublic = u.DownloadPublic DownloadPrivate = u.DownloadPrivate + DownloadFavourite = u.DownloadFavourite DifferentFolders = u.DifferentFolders + QueryString = u.QueryString MySettings = u.HOST.Source End Sub End Class diff --git a/SCrawler/API/TikTok/Declarations.vb b/SCrawler/API/TikTok/Declarations.vb index 2b030da..de31241 100644 --- a/SCrawler/API/TikTok/Declarations.vb +++ b/SCrawler/API/TikTok/Declarations.vb @@ -9,53 +9,8 @@ Imports PersonalUtilities.Functions.RegularExpressions Namespace API.TikTok Friend Module Declarations - Friend ReadOnly RegexEnvir As New RegexParseEnvir - Friend ReadOnly CheckDateProvider As New CustomProvider(Function(v) IIf(CType(v, Date?).HasValue, CObj(CType(v, Date?).Value), Nothing)) - Friend Class RegexParseEnvir - Private ReadOnly UrlIdRegex As RParams = RParams.DMS("http[s]?://[w\.]{0,4}tiktok.com/[^/]+?/video/(\d+)", 1, EDP.ReturnValue) - Private ReadOnly RegexItemsArrPre As RParams = RParams.DMS("ItemList"":\{""user-post"":\{""list"":\[([^\[]+)\]", 1) - Private ReadOnly RegexItemsArr As RParams = RParams.DM("\d+", 0, RegexReturn.List) - Private ReadOnly VideoPattern As New RParams(String.Empty, Nothing, 1, EDP.ReturnValue) - Private ReadOnly DatePattern As New RParams(String.Empty, Nothing, 1, EDP.ReturnValue) - Private ReadOnly UserIdFromVideo As RParams = RParams.DMS("/\?a=(\d+)", 1, EDP.ReturnValue) - Friend Function GetIDList(ByVal r As String) As List(Of String) - Try - If Not r.IsEmptyString Then - Dim l As List(Of String) = Nothing - Dim IdArr$ = RegexReplace(r, RegexItemsArrPre) - If Not IdArr.IsEmptyString Then l = RegexReplace(IdArr, RegexItemsArr) - If l.ListExists Then l.RemoveAll(Function(id) id.IsEmptyString) - Return l - End If - Return Nothing - Catch ex As Exception - Return ErrorsDescriber.Execute(EDP.SendToLog, ex, "[API.TikTok.RegexParseEnvir.GetIDList]") - End Try - End Function - Friend Function GetVideoData(ByVal r As String, ByVal ID As String, ByRef URL As String, ByRef [Date] As Date?) As Boolean - Try - [Date] = Nothing - URL = String.Empty - If Not r.IsEmptyString Then - VideoPattern.Pattern = "video"":\{""id"":""" & ID & """[^\}]+?""downloadAddr"":""([^""]+)""" - DatePattern.Pattern = """:{""id"":""" & ID & """,""desc"":.+?""createTime"":""(\d+)" - Dim u$ = RegexReplace(r, VideoPattern) - If Not u.IsEmptyString Then URL = SymbolsConverter.Unicode.Decode(u, EDP.ReturnValue) - Dim d$ = RegexReplace(r, DatePattern) - If Not d.IsEmptyString Then [Date] = ADateTime.ParseUnix32(d) - Return Not URL.IsEmptyString - End If - Return False - Catch ex As Exception - Return ErrorsDescriber.Execute(EDP.SendToLog, ex, "[API.TikTok.RegexParseEnvir.GetVideoData]", False) - End Try - End Function - Friend Function ExtractPostID(ByVal URL As String) As String - If Not URL.IsEmptyString Then Return RegexReplace(URL, UrlIdRegex) Else Return String.Empty - End Function - Friend Function ExtractUserID(ByVal VideoUrl As String) As String - If Not VideoUrl.IsEmptyString Then Return RegexReplace(VideoUrl, UserIdFromVideo) Else Return String.Empty - End Function - End Class + Friend ReadOnly SimpleDateConverter As New ADateTime("yyyyMMdd") + Friend ReadOnly RegexTagsReplacer As RParams = RParams.DM("#\w+\s?", -1, RegexReturn.Replace, + CType(Function(input$) String.Empty, Func(Of String, String)), EDP.ReturnValue) End Module End Namespace \ No newline at end of file diff --git a/SCrawler/API/TikTok/SiteSettings.vb b/SCrawler/API/TikTok/SiteSettings.vb index bf9df98..3105533 100644 --- a/SCrawler/API/TikTok/SiteSettings.vb +++ b/SCrawler/API/TikTok/SiteSettings.vb @@ -11,7 +11,7 @@ Imports SCrawler.Plugin Imports SCrawler.Plugin.Attributes Imports PersonalUtilities.Functions.RegularExpressions Namespace API.TikTok - + Friend Class SiteSettings : Inherits SiteSettingsBase Friend Overrides ReadOnly Property Icon As Icon Get @@ -23,8 +23,22 @@ Namespace API.TikTok Return My.Resources.SiteResources.TikTokPic_192 End Get End Property + + Friend Property RemoveTagsFromTitle As PropertyValue + + Friend Property TitleUseNative As PropertyValue + + Friend Property TitleUseNativeSTD As PropertyValue + + Friend Property TitleAddVideoID As PropertyValue Friend Sub New() MyBase.New("TikTok", "www.tiktok.com") + RemoveTagsFromTitle = New PropertyValue(False) + TitleUseNative = New PropertyValue(True) + TitleUseNativeSTD = New PropertyValue(False) + TitleAddVideoID = New PropertyValue(True) + UseNetscapeCookies = True UrlPatternUser = "https://www.tiktok.com/@{0}/" UserRegex = RParams.DMS("[htps:/]{7,8}.*?tiktok.com/@([^/]+)", 1) ImageVideoContains = "tiktok.com" @@ -32,12 +46,14 @@ Namespace API.TikTok Friend Overrides Function GetInstance(ByVal What As ISiteSettings.Download) As IPluginContentProvider Return New UserData End Function - Friend Overrides Function BaseAuthExists() As Boolean - Return Responser.CookiesExists - End Function Friend Overrides Function Available(ByVal What As ISiteSettings.Download, ByVal Silent As Boolean) As Boolean - 'TODO: TikTok disabled - Return False + Return Settings.YtdlpFile.Exists End Function + Friend Overrides Sub UserOptions(ByRef Options As Object, ByVal OpenForm As Boolean) + If Options Is Nothing OrElse Not TypeOf Options Is UserExchangeOptions Then Options = New UserExchangeOptions(Me) + If OpenForm Then + Using f As New InternalSettingsForm(Options, Me, False) : f.ShowDialog() : End Using + End If + End Sub End Class End Namespace \ No newline at end of file diff --git a/SCrawler/API/TikTok/UserData.vb b/SCrawler/API/TikTok/UserData.vb index 9ff3590..b6425d1 100644 --- a/SCrawler/API/TikTok/UserData.vb +++ b/SCrawler/API/TikTok/UserData.vb @@ -8,65 +8,261 @@ ' but WITHOUT ANY WARRANTY Imports System.Threading Imports SCrawler.API.Base +Imports SCrawler.API.YouTube.Objects Imports PersonalUtilities.Functions.XML Imports PersonalUtilities.Functions.RegularExpressions -Imports PersonalUtilities.Tools.Web.Clients +Imports PersonalUtilities.Tools +Imports PersonalUtilities.Tools.Web.Documents.JSON Namespace API.TikTok Friend Class UserData : Inherits UserDataBase - Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean) +#Region "XML names" + Private Const Name_RemoveTagsFromTitle As String = "RemoveTagsFromTitle" + Private Const Name_TitleUseNative As String = "TitleUseNative" + Private Const Name_TitleAddVideoID As String = "TitleAddVideoID" + Private Const Name_LastDownloadDate As String = "LastDownloadDate" +#End Region +#Region "Declarations" + Private ReadOnly Property MySettings As SiteSettings + Get + Return HOST.Source + End Get + End Property + Private ReadOnly Property RootCacheTikTok As ICacheKeeper + Get + With Settings.Cache + Dim f As SFile = $"{Settings.Cache.RootDirectory.PathWithSeparator}TikTokCache\" + If .ContainsFolder(f) Then + Return .GetInstance(f) + Else + f.Exists(SFO.Path, True) + With .NewInstance(Of BatchFileExchanger)(f) + .DeleteCacheOnDispose = False + .DeleteRootOnDispose = False + Return .Self + End With + End If + End With + End Get + End Property + Friend Property RemoveTagsFromTitle As Boolean = False + Friend Property TitleUseNative As Boolean = True + Friend Property TitleAddVideoID As Boolean = True + Private Property LastDownloadDate As Date? = Nothing +#End Region +#Region "Exchange" + Friend Overrides Function ExchangeOptionsGet() As Object + Return New UserExchangeOptions(Me) + End Function + Friend Overrides Sub ExchangeOptionsSet(ByVal Obj As Object) + If Not Obj Is Nothing AndAlso TypeOf Obj Is UserExchangeOptions Then + With DirectCast(Obj, UserExchangeOptions) + RemoveTagsFromTitle = .RemoveTagsFromTitle + TitleUseNative = .TitleUseNative + TitleAddVideoID = .TitleAddVideoID + End With + End If End Sub +#End Region +#Region "Loader" + Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean) + With Container + If Loading Then + RemoveTagsFromTitle = .Value(Name_RemoveTagsFromTitle).FromXML(Of Boolean)(False) + TitleUseNative = .Value(Name_TitleUseNative).FromXML(Of Boolean)(True) + TitleAddVideoID = .Value(Name_TitleAddVideoID).FromXML(Of Boolean)(True) + LastDownloadDate = AConvert(Of Date)(.Value(Name_LastDownloadDate), ADateTime.Formats.BaseDateTime, Nothing) + Else + .Add(Name_RemoveTagsFromTitle, RemoveTagsFromTitle.BoolToInteger) + .Add(Name_TitleUseNative, TitleUseNative.BoolToInteger) + .Add(Name_TitleAddVideoID, TitleAddVideoID.BoolToInteger) + .Add(Name_LastDownloadDate, AConvert(Of String)(LastDownloadDate, AModes.XML, ADateTime.Formats.BaseDateTime, String.Empty)) + End If + End With + End Sub +#End Region +#Region "Initializer" Friend Sub New() SeparateVideoFolder = False + UseInternalDownloadFileFunction = True End Sub +#End Region +#Region "Download functions" Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken) - Dim URL$ = String.Empty - Try - Dim PostIDs As List(Of String) - Dim PostDate As Date? = Nothing - Dim PostURL$ = String.Empty - Dim r$ - URL = $"https://www.tiktok.com/@{Name}" - r = Responser.GetResponse(URL) - PostIDs = RegexEnvir.GetIDList(r) - If PostIDs.ListExists Then - For Each __id$ In PostIDs - If Not _TempPostsList.Contains(__id) Then - _TempPostsList.Add(__id) - If RegexEnvir.GetVideoData(r, __id, PostURL, PostDate) Then - Select Case CheckDatesLimit(PostDate, CheckDateProvider) - Case DateResult.Skip : Continue For - Case DateResult.Exit : Exit Sub - End Select - If ID.IsEmptyString And Not PostURL.IsEmptyString Then ID = RegexEnvir.ExtractUserID(PostURL) - _TempMediaList.ListAddValue(MediaFromData(PostURL, __id, PostDate)) - End If - Else - Exit Sub + Dim URL$ = $"https://www.tiktok.com/@{Name}" + Using cache As CacheKeeper = CreateCache() + Try + Dim postID$, title$, postUrl$ + Dim postDate As Date? + Dim dateAfterC As Date? = Nothing + Dim dateBefore As Date? = DownloadDateTo + Dim dateAfter As Date? = DownloadDateFrom + + If _ContentList.Count > 0 Then + With (From d In _ContentList Where d.Post.Date.HasValue Select d.Post.Date.Value) + If .ListExists Then dateAfterC = .Min + End With + End If + + With {CStr(AConvert(Of String)(dateAfter, SimpleDateConverter, String.Empty)).FromXML(Of Integer)(-1), + CStr(AConvert(Of String)(dateAfterC, SimpleDateConverter, String.Empty)).FromXML(Of Integer)(-1)}.ListWithRemove(Function(d) d = -1) + If .ListExists Then dateAfter = AConvert(Of Date)(CStr(.Min), SimpleDateConverter, Nothing) + End With + + If LastDownloadDate.HasValue Then + If dateAfter.HasValue And Not DownloadDateFrom.HasValue Then + If (LastDownloadDate.Value - dateAfter.Value).TotalDays > 1 Then dateAfter = dateAfter.Value.AddDays(1) End If - Next - End If - Catch ex As Exception - ProcessException(ex, Token, $"data downloading error [{URL}]") - End Try + End If + + Using b As New TokenBatch(Token) + b.ChangeDirectory(cache) + b.Encoding = BatchExecutor.UnicodeEncoding + b.Execute(CreateYTCommand(cache.RootDirectory, URL, False, dateBefore, dateAfter)) + End Using + + ThrowAny(Token) + + Dim files As List(Of SFile) = SFile.GetFiles(cache, "*.json",, EDP.ReturnValue) + If files.ListExists Then + Dim j As EContainer + For Each file As SFile In files + j = JsonDocument.Parse(file.GetText, EDP.ReturnValue) + If j.ListExists Then + If j.Value("_type").StringToLower = "video" Then + postID = j.Value("id") + If Not _TempPostsList.Contains(postID) Then + _TempPostsList.Add(postID) + Else + Exit Sub + End If + title = j.Value("title").StringRemoveWinForbiddenSymbols + If title.IsEmptyString Or Not TitleUseNative Then + title = postID + Else + If RemoveTagsFromTitle Then title = RegexReplace(title, RegexTagsReplacer) + title = title.StringTrim + If title.IsEmptyString Then + title = postID + ElseIf TitleAddVideoID Then + title &= $" ({postID})" + End If + End If + postDate = AConvert(Of Date)(j.Value("timestamp"), UnixDate32Provider, Nothing) + If Not postDate.HasValue Then postDate = AConvert(Of Date)(j.Value("upload_date"), SimpleDateConverter, Nothing) + Select Case CheckDatesLimit(postDate, SimpleDateConverter) + Case DateResult.Skip : Continue For + Case DateResult.Exit : Exit Sub + End Select + + postUrl = j.Value("webpage_url") + If postUrl.IsEmptyString Then postUrl = $"https://www.tiktok.com/@{Name}/video/{postID}" + _TempMediaList.Add(New UserMedia(postUrl, UserMedia.Types.Video) With { + .File = $"{title}.mp4", .Post = New UserPost(postID, postDate)}) + End If + j.Dispose() + End If + Next + End If + Catch ex As Exception + ProcessException(ex, Token, $"data downloading error [{URL}]") + End Try + End Using End Sub +#End Region +#Region "ReparseMissing" + Protected Overrides Sub ReparseMissing(ByVal Token As CancellationToken) + If ContentMissingExists Then + Dim m As UserMedia + Dim i% + Dim rList As New List(Of Integer) + For i = 0 To _ContentList.Count - 1 + If _ContentList(i).State = UserMedia.States.Missing Then + m = _ContentList(i) + m.URL = m.URL_BASE + _TempMediaList.Add(m) + rList.Add(i) + End If + Next + If rList.Count > 0 Then + For i% = rList.Count - 1 To 0 Step -1 : _ContentList.RemoveAt(rList(i)) : Next + End If + End If + End Sub +#End Region +#Region "YT-DLP Support" + Private Function CreateYTCommand(ByVal Output As SFile, ByVal URL As String, ByVal IsDownload As Boolean, + Optional ByVal DateBefore As Date? = Nothing, Optional ByVal DateAfter As Date? = Nothing, + Optional ByVal PrintTitle As Boolean = False, Optional ByVal SupportOutput As Boolean = True) As String + Dim command$ = $"""{Settings.YtdlpFile}"" " + If Not IsDownload Then command &= "--write-info-json --skip-download " + If PrintTitle Then + If Not command.Contains("--skip-download") Then command &= "--skip-download " + command &= "--print title " + End If + If DateBefore.HasValue Then command &= $"--datebefore {DateBefore.Value.AddDays(1).ToStringDate(SimpleDateConverter)} " + If DateAfter.HasValue Then command &= $"--dateafter {DateAfter.Value.AddDays(-1).ToStringDate(SimpleDateConverter)} " + If MySettings.CookiesNetscapeFile.Exists Then command &= $"--no-cookies-from-browser --cookies ""{MySettings.CookiesNetscapeFile}"" " + command &= $"{URL} " + If SupportOutput Then + If IsDownload Then + command &= $"-o ""{Output}""" + Else + command &= "-o %(id)s" + End If + End If + Return command + End Function +#End Region +#Region "DownloadContent, DownloadFile" Protected Overrides Sub DownloadContent(ByVal Token As CancellationToken) DownloadContentDefault(Token) End Sub - Private Function MediaFromData(ByVal _URL As String, ByVal PostID As String, ByVal PostDate As Date?) As UserMedia - _URL = LinkFormatterSecure(RegexReplace(_URL.Replace("\", String.Empty), LinkPattern)) - Dim m As New UserMedia(_URL, UserMedia.Types.Video) With {.Post = New UserPost With {.ID = PostID}} - If Not m.URL.IsEmptyString Then m.File = $"{PostID}.mp4" - If PostDate.HasValue Then m.Post.Date = PostDate - Return m + Protected Overrides Function DownloadFile(ByVal URL As String, ByVal Media As UserMedia, ByVal DestinationFile As SFile, ByVal Token As CancellationToken) As SFile + Using b As New TokenBatch(Token) With {.FileExchanger = RootCacheTikTok} + b.Encoding = BatchExecutor.UnicodeEncoding + b.Execute(CreateYTCommand(DestinationFile, URL, True)) + End Using + Return DestinationFile End Function +#End Region +#Region "DownloadSingleObject" + Protected Overrides Sub DownloadSingleObject_GetPosts(ByVal Data As IYouTubeMediaContainer, ByVal Token As CancellationToken) + Dim f$ = String.Empty + If CBool(MySettings.TitleUseNativeSTD.Value) Then + Using b As New BatchExecutor(True) With { + .Encoding = BatchExecutor.UnicodeEncoding, + .CleanAutomaticallyViaRegEx = True, + .CleanAutomaticallyViaRegExRemoveAllCommands = True + } + b.Execute(CreateYTCommand(Nothing, Data.URL, True,,, True, False)) + b.Clean() + With b.OutputData + If .Count > 0 Then + For Each vData$ In .Self + If Not vData.Contains($": {BatchExecutor.UnicodeEncoding}") Then f = vData : Exit For + Next + End If + End With + End Using + End If + Dim m As New UserMedia(Data.URL, UserMedia.Types.Video) + If Not f.IsEmptyString Then f = TitleHtmlConverter(f) + If Not f.IsEmptyString Then + If CBool(MySettings.RemoveTagsFromTitle.Value) Then f = RegexReplace(f, RegexTagsReplacer) + f = f.StringTrim + If Not f.IsEmptyString Then + If CBool(MySettings.TitleAddVideoID.Value) Then f &= $" ({m.File.Name})" + m.File.Name = f + End If + End If + _TempMediaList.Add(m) + End Sub +#End Region +#Region "Exception" Protected Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False, Optional ByVal EObj As Object = Nothing) As Integer - If Responser.Status = Net.WebExceptionStatus.ConnectionClosed Then - UserExists = False - Return 1 - Else - Return 0 - End If + Return 0 End Function +#End Region End Class End Namespace \ No newline at end of file diff --git a/SCrawler/API/TikTok/UserExchangeOptions.vb b/SCrawler/API/TikTok/UserExchangeOptions.vb new file mode 100644 index 0000000..35b2f1f --- /dev/null +++ b/SCrawler/API/TikTok/UserExchangeOptions.vb @@ -0,0 +1,32 @@ +' Copyright (C) 2023 Andy https://github.com/AAndyProgram +' 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 SCrawler.Plugin.Attributes +Namespace API.TikTok + Friend Class UserExchangeOptions + + Friend Property RemoveTagsFromTitle As Boolean + + Friend Property TitleUseNative As Boolean + + Friend Property TitleAddVideoID As Boolean + Private ReadOnly MySettings As SiteSettings + Friend Sub New(ByVal u As UserData) + MySettings = u.HOST.Source + RemoveTagsFromTitle = u.RemoveTagsFromTitle + TitleUseNative = u.TitleUseNative + TitleAddVideoID = u.TitleAddVideoID + End Sub + Friend Sub New(ByVal s As SiteSettings) + MySettings = s + RemoveTagsFromTitle = s.RemoveTagsFromTitle.Value + TitleUseNative = s.TitleUseNative.Value + TitleAddVideoID = s.TitleAddVideoID.Value + End Sub + End Class +End Namespace diff --git a/SCrawler/API/Twitter/EditorExchangeOptions.vb b/SCrawler/API/Twitter/EditorExchangeOptions.vb index 86d5e70..60f6a78 100644 --- a/SCrawler/API/Twitter/EditorExchangeOptions.vb +++ b/SCrawler/API/Twitter/EditorExchangeOptions.vb @@ -24,6 +24,8 @@ Namespace API.Twitter ToolTip:="Existing files will be checked for duplicates and duplicates removed." & vbCr & "Works only on the first activation 'Use MD5 comparison'.", LeftOffset:=DefaultOffset)> Friend Property RemoveExistingDuplicates As Boolean = False + + Friend Overridable Property MediaModelAllowNonUserTweets As Boolean = False @@ -36,12 +38,18 @@ Namespace API.Twitter Caption:="Download model 'Search'", ToolTip:="Download the data using the 'https://twitter.com/search?q=from:UserName+include:nativeretweets' command.", LeftOffset:=DefaultOffset)> Friend Overridable Property DownloadModelSearch As Boolean = False + + Friend Overridable Property DownloadModelForceApply As Boolean = False Private ReadOnly Property MySettings As Object Friend Sub New(ByVal s As SiteSettings) GifsDownload = s.GifsDownload.Value GifsSpecialFolder = s.GifsSpecialFolder.Value GifsPrefix = s.GifsPrefix.Value UseMD5Comparison = s.UseMD5Comparison.Value + DownloadModelForceApply = s.UseAppropriateModel.Value + MediaModelAllowNonUserTweets = s.MediaModelAllowNonUserTweets.Value MySettings = s End Sub Friend Sub New(ByVal s As Mastodon.SiteSettings) @@ -57,7 +65,9 @@ Namespace API.Twitter GifsPrefix = u.GifsPrefix UseMD5Comparison = u.UseMD5Comparison RemoveExistingDuplicates = u.RemoveExistingDuplicates + MediaModelAllowNonUserTweets = u.MediaModelAllowNonUserTweets If Not TypeOf u Is Mastodon.UserData Then + DownloadModelForceApply = u.DownloadModelForceApply Dim dm As DModels() = EnumExtract(Of DModels)(u.DownloadModel) If dm.ListExists Then DownloadModelMedia = dm.Contains(DModels.Media) diff --git a/SCrawler/API/Twitter/SiteSettings.vb b/SCrawler/API/Twitter/SiteSettings.vb index f1b1cf7..d6cac57 100644 --- a/SCrawler/API/Twitter/SiteSettings.vb +++ b/SCrawler/API/Twitter/SiteSettings.vb @@ -11,50 +11,48 @@ Imports SCrawler.Plugin Imports SCrawler.Plugin.Attributes Imports PersonalUtilities.Functions.RegularExpressions Imports PersonalUtilities.Tools.Web.Clients +Imports DN = SCrawler.API.Base.DeclaredNames Namespace API.Twitter Friend Class SiteSettings : Inherits SiteSettingsBase -#Region "Token names" - Friend Const Header_Authorization As String = "authorization" - Friend Const Header_Token As String = "x-csrf-token" -#End Region -#Region "Properties constants" - Friend Const GifsSpecialFolder_Text As String = "GIFs special folder" - Friend Const GifsSpecialFolder_ToolTip As String = "Put the GIFs in a special folder" & vbCr & - "This is a folder name, not an absolute path." & vbCr & - "This folder(s) will be created relative to the user's root folder." & vbCr & - "Examples:" & vbCr & "SomeFolderName" & vbCr & "SomeFolderName\SomeFolderName2" - Friend Const GifsPrefix_Text As String = "GIF prefix" - Friend Const GifsPrefix_ToolTip As String = "This prefix will be added to the beginning of the filename" - Friend Const GifsDownload_Text As String = "Download GIFs" - Friend Const UseMD5Comparison_Text As String = "Use MD5 comparison" - Friend Const UseMD5Comparison_ToolTip As String = "Each image will be checked for existence using MD5" -#End Region #Region "Declarations" Friend Overrides ReadOnly Property Icon As Icon Get Return My.Resources.SiteResources.TwitterIcon_32 End Get End Property + Private ReadOnly _Image As Image Friend Overrides ReadOnly Property Image As Image Get - Return My.Resources.SiteResources.TwitterPic_400 + Return _Image End Get End Property - 'TODELETE: twitter headers - '#Region "Auth" - ' - ' Private ReadOnly Property Auth As PropertyValue - ' - ' Private ReadOnly Property Token As PropertyValue - '#End Region #Region "Other properties" - + + Friend ReadOnly Property UseAppropriateModel As PropertyValue +#Region "End points" + + Friend Property UseNewEndPointSearch As PropertyValue + + Friend Property UseNewEndPointProfiles As PropertyValue +#End Region +#Region "Limits" + + Friend Property AbortOnLimit As PropertyValue + + Friend Property DownloadAlreadyParsed As PropertyValue +#End Region + + Friend ReadOnly Property MediaModelAllowNonUserTweets As PropertyValue + Friend ReadOnly Property GifsDownload As PropertyValue - + Friend ReadOnly Property GifsSpecialFolder As PropertyValue - + Friend ReadOnly Property GifsPrefix As PropertyValue Private ReadOnly Property GifStringChecker As IFormatProvider @@ -76,52 +74,38 @@ Namespace API.Twitter Throw New NotImplementedException("[GetFormat] is not available in the context of [GifStringProvider]") End Function End Class - + Friend ReadOnly Property UseMD5Comparison As PropertyValue - + Friend ReadOnly Property ConcurrentDownloads As PropertyValue + + Private ReadOnly Property MyConcurrentDownloadsProvider As IFormatProvider #End Region - 'TODELETE: twitter headers - 'Private Sub ChangeResponserFields(ByVal PropName As String, ByVal Value As Object) - ' If Not PropName.IsEmptyString Then - ' Dim f$ = String.Empty - ' Select Case PropName - ' Case NameOf(Auth) : f = Header_Authorization - ' Case NameOf(Token) : f = Header_Token - ' End Select - ' If Not f.IsEmptyString Then - ' Responser.Headers.Remove(f) - ' If Not CStr(Value).IsEmptyString Then Responser.Headers.Add(f, CStr(Value)) - ' Responser.SaveSettings() - ' End If - ' End If - 'End Sub #End Region Friend Sub New() MyBase.New(TwitterSite, "twitter.com") - 'TODELETE: twitter headers - 'Dim a$ = String.Empty - 'Dim t$ = String.Empty + _Image = My.Resources.SiteResources.TwitterIcon_32.ToBitmap With Responser - 'TODELETE: twitter headers - 'a = .Headers.Value(Header_Authorization) - 't = .Headers.Value(Header_Token) .Cookies.ChangedAllowInternalDrop = False .Cookies.Changed = False End With - 'TODELETE: twitter headers - 'Auth = New PropertyValue(a, GetType(String), Sub(v) ChangeResponserFields(NameOf(Auth), v)) - 'Token = New PropertyValue(t, GetType(String), Sub(v) ChangeResponserFields(NameOf(Token), v)) - + UseAppropriateModel = New PropertyValue(True) + UseNewEndPointSearch = New PropertyValue(True) + UseNewEndPointProfiles = New PropertyValue(True) + AbortOnLimit = New PropertyValue(True) + DownloadAlreadyParsed = New PropertyValue(True) + MediaModelAllowNonUserTweets = New PropertyValue(False) GifsDownload = New PropertyValue(True) GifsSpecialFolder = New PropertyValue(String.Empty, GetType(String)) GifsPrefix = New PropertyValue("GIF_") GifStringChecker = New GifStringProvider UseMD5Comparison = New PropertyValue(False) ConcurrentDownloads = New PropertyValue(1) + MyConcurrentDownloadsProvider = New ConcurrentDownloadsProvider UserRegex = RParams.DMS("[htps:/]{7,8}.*?twitter.com/([^/]+)", 1) UrlPatternUser = "https://twitter.com/{0}" @@ -141,6 +125,11 @@ Namespace API.Twitter Friend Overrides Function Available(ByVal What As ISiteSettings.Download, ByVal Silent As Boolean) As Boolean Return Settings.GalleryDLFile.Exists And BaseAuthExists() End Function + Friend Property LIMIT_ABORT As Boolean = False + Friend Overrides Sub DownloadDone(ByVal What As ISiteSettings.Download) + LIMIT_ABORT = False + MyBase.DownloadDone(What) + End Sub Friend Overrides Sub UserOptions(ByRef Options As Object, ByVal OpenForm As Boolean) If Options Is Nothing OrElse (Not TypeOf Options Is EditorExchangeOptions OrElse Not DirectCast(Options, EditorExchangeOptions).SiteKey = TwitterSiteKey) Then _ diff --git a/SCrawler/API/Twitter/UserData.vb b/SCrawler/API/Twitter/UserData.vb index c5f295d..d456bb7 100644 --- a/SCrawler/API/Twitter/UserData.vb +++ b/SCrawler/API/Twitter/UserData.vb @@ -20,6 +20,8 @@ Namespace API.Twitter #Region "XML names" Private Const Name_FirstDownloadComplete As String = "FirstDownloadComplete" Private Const Name_DownloadModel As String = "DownloadModel" + Private Const Name_DownloadModelForceApply As String = "DownloadModelForceApply" + Private Const Name_MediaModelAllowNonUserTweets As String = "MediaModelAllowNonUserTweets" Private Const Name_GifsDownload As String = "GifsDownload" Private Const Name_GifsSpecialFolder As String = "GifsSpecialFolder" Private Const Name_GifsPrefix As String = "GifsPrefix" @@ -32,7 +34,9 @@ Namespace API.Twitter Search = 5 End Enum Private FirstDownloadComplete As Boolean = False + Friend Property DownloadModelForceApply As Boolean = False Friend Property DownloadModel As DownloadModels = DownloadModels.Undefined + Friend Property MediaModelAllowNonUserTweets As Boolean = False Friend Property GifsDownload As Boolean = True Friend Property GifsSpecialFolder As String = String.Empty Friend Property GifsPrefix As String = String.Empty @@ -64,6 +68,8 @@ Namespace API.Twitter UseMD5Comparison = .UseMD5Comparison RemoveExistingDuplicates = .RemoveExistingDuplicates DownloadModel = DownloadModels.Undefined + DownloadModelForceApply = .DownloadModelForceApply + MediaModelAllowNonUserTweets = .MediaModelAllowNonUserTweets If .DownloadModelMedia Then DownloadModel += DownloadModels.Media If .DownloadModelProfile Then DownloadModel += DownloadModels.Profile If .DownloadModelSearch Then DownloadModel += DownloadModels.Search @@ -78,6 +84,7 @@ Namespace API.Twitter Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean) With Container If Loading Then + DownloadModelForceApply = .Value(Name_DownloadModelForceApply).FromXML(Of Boolean)(False) If .Contains(Name_FirstDownloadComplete) Then FirstDownloadComplete = .Value(Name_FirstDownloadComplete).FromXML(Of Boolean)(False) DownloadModel = .Value(Name_DownloadModel).FromXML(Of Integer)(DownloadModels.Undefined) @@ -87,6 +94,7 @@ Namespace API.Twitter DownloadModel = .Value(Name_DownloadModel).FromXML(Of Integer)(DownloadModels.Undefined) Else If FirstDownloadComplete Then + DownloadModelForceApply = False If ParseUserMediaOnly Then DownloadModel = DownloadModels.Media Else @@ -107,8 +115,10 @@ Namespace API.Twitter UseMD5Comparison = .Value(Name_UseMD5Comparison).FromXML(Of Boolean)(False) RemoveExistingDuplicates = .Value(Name_RemoveExistingDuplicates).FromXML(Of Boolean)(False) StartMD5Checked = .Value(Name_StartMD5Checked).FromXML(Of Boolean)(False) + MediaModelAllowNonUserTweets = .Value(Name_MediaModelAllowNonUserTweets).FromXML(Of Boolean)(False) Else .Add(Name_FirstDownloadComplete, FirstDownloadComplete.BoolToInteger) + .Add(Name_DownloadModelForceApply, DownloadModelForceApply.BoolToInteger) .Add(Name_DownloadModel, CInt(DownloadModel)) .Add(Name_GifsDownload, GifsDownload.BoolToInteger) .Add(Name_GifsSpecialFolder, GifsSpecialFolder) @@ -116,18 +126,29 @@ Namespace API.Twitter .Add(Name_UseMD5Comparison, UseMD5Comparison.BoolToInteger) .Add(Name_RemoveExistingDuplicates, RemoveExistingDuplicates.BoolToInteger) .Add(Name_StartMD5Checked, StartMD5Checked.BoolToInteger) + .Add(Name_MediaModelAllowNonUserTweets, MediaModelAllowNonUserTweets.BoolToInteger) End If End With End Sub #End Region #Region "Download functions" + Private Function GetContainerSubnodes() As List(Of String()) + Return New List(Of String()) From { + {{"content", "itemContent", "tweet_results", "result", "legacy"}}, + {{"content", "itemContent", "tweet_results", "result", "tweet", "legacy"}} + } + End Function Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken) - If IsSavedPosts Then - If _ContentList.Count > 0 Then _DataNames.ListAddList(_ContentList.Select(Function(c) c.Post.ID), LAP.ClearBeforeAdd, LAP.NotContainsOnly) - DownloadData_SavedPosts(Token) + If MySettings.LIMIT_ABORT Then + TwitterLimitException.LogMessage(ToStringForLog, True) Else - If _ContentList.Count > 0 Then _DataNames.ListAddList(_ContentList.Select(Function(c) c.File.File), LAP.ClearBeforeAdd, LAP.NotContainsOnly) - DownloadData_Timeline(Token) + If IsSavedPosts Then + If _ContentList.Count > 0 Then _DataNames.ListAddList(_ContentList.Select(Function(c) c.Post.ID), LAP.ClearBeforeAdd, LAP.NotContainsOnly) + DownloadData_SavedPosts(Token) + Else + If _ContentList.Count > 0 Then _DataNames.ListAddList(_ContentList.Select(Function(c) c.File.File), LAP.ClearBeforeAdd, LAP.NotContainsOnly) + DownloadData_Timeline(Token) + End If End If End Sub Private Sub DownloadData_Timeline(ByVal Token As CancellationToken) @@ -139,6 +160,8 @@ Namespace API.Twitter Dim PostDate$, tmpUserId$ Dim i% Dim dirIndx% = -1 + Dim nodes As List(Of String()) = GetContainerSubnodes() + Dim node$() Dim timelineNode As Predicate(Of EContainer) = Function(ee) ee.Value("type").StringToLower = "timelineaddentries" Dim pinNode As Predicate(Of EContainer) = Function(ee) ee.Value("type").StringToLower = "timelinepinentry" Dim entriesNode As Predicate(Of EContainer) = Function(ee) ee.Name = "entries" Or ee.Name = entry @@ -150,13 +173,15 @@ Namespace API.Twitter Dim __parseContainer As Func(Of EContainer, Boolean) = Function(ByVal ee As EContainer) As Boolean - If dirIndx <= 1 Then - nn = ee({"content", "itemContent", "tweet_results", "result", "legacy"}) - Else - nn = ee + nn = Nothing + If dirIndx > 1 Then nn = ee + If Not nn.ListExists Then + For Each node In nodes + nn = ee(node) + If nn.ListExists Then Exit For + Next End If - If Not nn.ListExists Then nn = ee({"content", "itemContent", "tweet_results", "result", "tweet", "legacy"}) If nn.ListExists Then PostID = nn.Value("id_str").IfNullOrEmpty(nn.Value("id")) @@ -181,15 +206,14 @@ Namespace API.Twitter If tmpUserId.IsEmptyString Then tmpUserId = nn.ItemF({"extended_entities", "media", 0, sourceIdPredicate}).XmlIfNothingValue. IfNullOrEmpty(nn.Value("user_id")).IfNullOrEmpty(nn.Value("user_id_str")).IfNullOrEmpty("/") - If Not ParseUserMediaOnly OrElse (Not ID.IsEmptyString AndAlso tmpUserId = ID) Then ObtainMedia(nn, PostID, PostDate) + If Not ParseUserMediaOnly OrElse + (dirIndx = 0 AndAlso MediaModelAllowNonUserTweets) OrElse + (Not ID.IsEmptyString AndAlso tmpUserId = ID) Then ObtainMedia(nn, PostID, PostDate) End If Return True End Function - tCache = New CacheKeeper($"{DownloadContentDefault_GetRootDir()}\_tCache\") With { - .CacheDeleteError = New ErrorsDescriber(EDP.None) With {.Action = Sub(ee, eex, msg, obj) Settings.Cache.AddPath(tCache)}} - If tCache.RootDirectory.Exists(SFO.Path, False) Then tCache.RootDirectory.Delete(SFO.Path, SFODelete.DeletePermanently, EDP.ReturnValue) - tCache.Validate() + tCache = CreateCache() Dim dirs As List(Of SFile) = GetTimelineFromGalleryDL(tCache, Token) If dirs.ListExists Then @@ -313,7 +337,9 @@ Namespace API.Twitter End If End If End If + DownloadModelForceApply = False FirstDownloadComplete = True + Catch limit_ex As TwitterLimitException Catch ex As Exception ProcessException(ex, Token, $"data downloading error [{URL}]") Finally @@ -328,6 +354,8 @@ Namespace API.Twitter If files.ListExists Then ResetFileNameProvider(Math.Max(files.Count.ToString.Length, 3)) Dim id$ + Dim nodes As List(Of String()) = GetContainerSubnodes() + Dim node$() Dim j As EContainer, jj As EContainer Dim jErr As New ErrorsDescriber(EDP.ReturnValue) For i% = 0 To files.Count - 1 @@ -339,19 +367,24 @@ Namespace API.Twitter ProgressPre.ChangeMax(.Count) For Each jj In .Self ProgressPre.Perform() - With jj({"content", "itemContent", "tweet_results", "result", "legacy"}) - If .ListExists Then - id = .Value("id_str") - If _TempPostsList.Contains(id) Then j.Dispose() : Exit Sub Else ObtainMedia(.Self, id, .Value("created_at")) - End If - End With + For Each node In nodes + With jj(node) + If .ListExists Then + id = .Value("id_str") + If _TempPostsList.Contains(id) Then j.Dispose() : Exit Sub Else ObtainMedia(.Self, id, .Value("created_at")) + Exit For + End If + End With + Next Next End If End With j.Dispose() End If Next + nodes.Clear() End If + Catch limit_ex As TwitterLimitException Catch ex As Exception ProcessException(ex, Token, "data downloading error (Saved Posts)") End Try @@ -408,30 +441,28 @@ Namespace API.Twitter Dim f As SFile Dim m As UserMedia If w.ListExists Then - For Each n As EContainer In w - If n.Value("type") = "animated_gif" Then - With n({"video_info", "variants"}) - If .ListExists Then - With .ItemF({gifUrl}) - If .ListExists Then - url = .Value("url") - ff = UrlFile(url) - If Not ff.IsEmptyString Then - If GifsDownload And Not _DataNames.Contains(ff) Then - m = MediaFromData(url, PostID, PostDate,, State, UTypes.Video) - f = m.File - If Not f.IsEmptyString And Not GifsPrefix.IsEmptyString Then f.Name = $"{GifsPrefix}{f.Name}" : m.File = f - If Not GifsSpecialFolder.IsEmptyString Then m.SpecialFolder = $"{GifsSpecialFolder}*" - _TempMediaList.ListAddValue(m, LNC) - End If - Return True + If w.Value("type") = "animated_gif" Then + With w({"video_info", "variants"}) + If .ListExists Then + With .ItemF({gifUrl}) + If .ListExists Then + url = .Value("url") + ff = UrlFile(url) + If Not ff.IsEmptyString Then + If GifsDownload And Not _DataNames.Contains(ff) Then + m = MediaFromData(url, PostID, PostDate,, State, UTypes.Video) + f = m.File + If Not f.IsEmptyString And Not GifsPrefix.IsEmptyString Then f.Name = $"{GifsPrefix}{f.Name}" : m.File = f + If Not GifsSpecialFolder.IsEmptyString Then m.SpecialFolder = $"{GifsSpecialFolder}*" + _TempMediaList.ListAddValue(m, LNC) End If + Return True End If - End With - End If - End With - End If - Next + End If + End With + End If + End With + End If End If Return False Catch ex As Exception @@ -460,13 +491,22 @@ Namespace API.Twitter End Function #End Region #Region "Gallery-DL Support" + Private Class TwitterLimitException : Inherits Exception + Friend Sub New(ByVal User As String, ByVal Skipped As Boolean) + LogMessage(User, Skipped) + End Sub + Friend Shared Sub LogMessage(ByVal User As String, ByVal Skipped As Boolean) + MyMainLOG = $"{User}: twitter limit reached.{IIf(Skipped, "Data has not been downloaded", String.Empty)}" + End Sub + End Class Private Class TwitterGDL : Inherits GDL.GDLBatch - Private Property Token As CancellationToken - Friend Sub New(ByVal Dir As SFile, ByVal _Token As CancellationToken) - MyBase.New + Private ReadOnly KillOnLimit As Boolean + Friend LimitReached As Boolean = False + Friend Sub New(ByVal Dir As SFile, ByVal _Token As CancellationToken, ByVal _KillOnLimit As Boolean) + MyBase.New(_Token) Commands.Clear() If Not Dir.IsEmptyString Then ChangeDirectory(Dir) - Token = _Token + KillOnLimit = _KillOnLimit End Sub Protected Overrides Async Function Validate(ByVal Value As String) As Task If Not ProcessKilled AndAlso Await Task.Run(Function() Token.IsCancellationRequested OrElse IdExists(Value)) Then Kill() @@ -482,14 +522,27 @@ Namespace API.Twitter End Try Return False End Function + Protected Overrides Async Sub ErrorDataReceiver(ByVal Sender As Object, ByVal e As DataReceivedEventArgs) + Await Task.Run(Sub() CheckForLimit(e.Data)) + End Sub + Private Sub CheckForLimit(ByVal Value As String) + If Token.IsCancellationRequested Or (KillOnLimit AndAlso Not ProcessKilled AndAlso + Not Value.IsEmptyString AndAlso Value.ToLower.Contains("for rate limit reset")) Then + LimitReached = True + Kill() + End If + End Sub End Class Private Function GetDataFromGalleryDL(ByVal URL As String, ByVal Cache As CacheKeeper, ByVal UseTempPostList As Boolean, Optional ByVal Token As CancellationToken = Nothing) As SFile - Dim command$ = $"""{Settings.GalleryDLFile}"" --verbose --no-download --no-skip --cookies ""{MySettings.CookiesNetscapeFile}"" --write-pages " + Dim command$ = String.Empty Try + Dim conf As SFile = GdlCreateConf(Cache.NewPath) + command = $"""{Settings.GalleryDLFile}"" --verbose --no-download --no-skip --config ""{conf}"" --write-pages " + command &= GdlGetIdFilterString() Dim dir As SFile = Cache.NewPath If dir.Exists(SFO.Path,, EDP.ThrowException) Then - Using batch As New TwitterGDL(dir, Token) + Using batch As New TwitterGDL(dir, Token, MySettings.AbortOnLimit.Value) If UseTempPostList Then batch.TempPostsList = _TempPostsList command &= GdlGetIdFilterString() @@ -499,10 +552,22 @@ Namespace API.Twitter 'Debug.WriteLine(command) '#End If batch.Execute(command) + If batch.LimitReached Then + If CBool(MySettings.DownloadAlreadyParsed.Value) And + SFile.GetFiles(dir, "*.txt", IO.SearchOption.AllDirectories, EDP.ReturnValue).Count > 0 Then + MySettings.LIMIT_ABORT = True + Return dir + Else + Throw New TwitterLimitException(ToStringForLog, False) + End If + End If End Using Return dir End If Return Nothing + Catch limit_ex As TwitterLimitException + MySettings.LIMIT_ABORT = True + Throw limit_ex Catch ex As Exception Return ErrorsDescriber.Execute(EDP.SendToLog, ex, $"{ToStringForLog()}: GetDataFromGalleryDL({command})") End Try @@ -511,20 +576,23 @@ Namespace API.Twitter Dim command$ = String.Empty Try Dim confCache As CacheKeeper = Cache.NewInstance(Of BatchFileExchanger) - Dim conf As SFile = $"{confCache.RootDirectory.PathWithSeparator}TwitterGdlConfig.conf" - Dim confText$ = "{""extractor"":{""cookies"": """ & MySettings.CookiesNetscapeFile.ToString.Replace("\", "/") & - """,""cookies-update"": false,""twitter"":{""cards"": false,""conversations"": true,""pinned"": false,""quoted"": false,""replies"": true,""retweets"": true,""strategy"": null,""text-tweets"": false,""twitpic"": false,""unique"": true,""users"": ""timeline"",""videos"": true}}}" - If conf.Exists(SFO.Path, True, EDP.ThrowException) Then TextSaver.SaveTextToFile(confText, conf) - If Not conf.Exists Then Throw New IO.FileNotFoundException("Can't find Twitter GDL config file", conf) + Dim conf As SFile = GdlCreateConf(confCache.RootDirectory) + + If DownloadModel = DownloadModels.Undefined And Not FirstDownloadComplete And DownloadModelForceApply Then + If ParseUserMediaOnly Then + DownloadModel = DownloadModels.Media + Else + DownloadModel = DownloadModels.Media + DownloadModels.Profile + DownloadModels.Search + End If + End If Dim outList As New List(Of SFile) Dim rootDir As CacheKeeper = Cache.NewInstance Dim dir As SFile Dim dm As List(Of DownloadModels) = EnumExtract(Of DownloadModels)(DownloadModel).ListIfNothing Dim process As Boolean - Dim bProcess As Boolean = DownloadModel = DownloadModels.Undefined Or Not FirstDownloadComplete - Using tgdl As New TwitterGDL(Nothing, Token) With { + Using tgdl As New TwitterGDL(Nothing, Token, MySettings.AbortOnLimit.Value) With { .TempPostsList = _TempPostsList, .AutoClear = True, .AutoReset = True, @@ -541,22 +609,36 @@ Namespace API.Twitter command = $"""{Settings.GalleryDLFile}"" --verbose --no-download --no-skip --config ""{conf}"" --write-pages " command &= GdlGetIdFilterString() Select Case i - Case 0 : command &= $"https://twitter.com/{Name}/media" : process = bProcess Or dm.Contains(DownloadModels.Media) - Case 1 : command &= $"https://twitter.com/{Name}" : process = bProcess Or dm.Contains(DownloadModels.Profile) - Case 2 : command &= $"https://twitter.com/search?q=from:{Name}+include:nativeretweets" : process = bProcess Or dm.Contains(DownloadModels.Search) + Case 0 : command &= $"https://twitter.com/{Name}/media" : process = dm.Contains(DownloadModels.Media) + Case 1 : command &= $"https://twitter.com/{Name}" : process = dm.Contains(DownloadModels.Profile) + Case 2 : command &= $"-o search-endpoint=graphql https://twitter.com/search?q=from:{Name}+include:nativeretweets" : process = dm.Contains(DownloadModels.Search) Case Else : process = False End Select '#If DEBUG Then 'Debug.WriteLine(command) '#End If ThrowAny(Token) - If process Then tgdl.Execute(command) + If process Then + tgdl.Execute(command) + If tgdl.LimitReached Then + If CBool(MySettings.DownloadAlreadyParsed.Value) And + SFile.GetFiles(rootDir, "*.txt", IO.SearchOption.AllDirectories, EDP.ReturnValue).Count > 0 Then + MySettings.LIMIT_ABORT = True + Exit For + Else + Throw New TwitterLimitException(ToStringForLog, False) + End If + End If + End If ThrowAny(Token) Next End Using dm.Clear() Return outList + Catch limit_ex As TwitterLimitException + MySettings.LIMIT_ABORT = True + Throw limit_ex Catch ex As Exception ProcessException(ex, Token, $"{ToStringForLog()}: GetTimelineFromGalleryDL({command})") Return Nothing @@ -565,6 +647,20 @@ Namespace API.Twitter Private Function GdlGetIdFilterString() As String Return If(_TempPostsList.Count > 0, $"--filter ""int(tweet_id) > {_TempPostsList.Last} or abort()"" ", String.Empty) End Function + Private Function GdlCreateConf(ByVal Path As SFile) As SFile + Try + Dim conf As SFile = $"{Path.PathWithSeparator}TwitterGdlConfig.conf" + Dim confText$ = "{""extractor"":{""cookies"": """ & MySettings.CookiesNetscapeFile.ToString.Replace("\", "/") & + """,""cookies-update"": false,""twitter"":{""tweet-endpoint"": ""detail"",""cards"": false,""conversations"": true,""pinned"": false,""quoted"": false,""replies"": true,""retweets"": true,""strategy"": null,""text-tweets"": false,""twitpic"": false,""unique"": true,""users"": ""timeline"",""videos"": true}}}" + If conf.Exists(SFO.Path, True, EDP.ThrowException) Then TextSaver.SaveTextToFile(confText, conf) + If Not conf.Exists Then Throw New IO.FileNotFoundException("Can't find Twitter GDL config file", conf) + Return conf + Catch file_ex As IO.FileNotFoundException + Throw file_ex + Catch ex As Exception + Return ErrorsDescriber.Execute(EDP.SendToLog, ex, "gallery-dl configuration file creating error", New SFile) + End Try + End Function #End Region #Region "ReparseMissing" Protected Overrides Sub ReparseMissing(ByVal Token As CancellationToken) @@ -576,7 +672,9 @@ Namespace API.Twitter If ContentMissingExists Then Dim m As UserMedia Dim PostDate$ - Dim j As EContainer + Dim nodes As List(Of String()) = GetContainerSubnodes() + Dim node$() + Dim j As EContainer, n As EContainer Dim f As SFile Dim i%, ii% Dim files As List(Of SFile) @@ -585,6 +683,7 @@ Namespace API.Twitter cache = Settings.Cache Else cache = New CacheKeeper(DownloadContentDefault_GetRootDir.CSFilePS) + cache.CacheDeleteError = CacheDeletionError(cache) End If ProgressPre.ChangeMax(_ContentList.Count) For i = 0 To _ContentList.Count - 1 @@ -598,7 +697,7 @@ Namespace API.Twitter Else URL = String.Format(SinglePostPattern, Name, m.Post.ID) End If - f = GetDataFromGalleryDL(URL, cache, Favorite, Token) + f = GetDataFromGalleryDL(URL, cache, False, Token) If Not f.IsEmptyString Then files = SFile.GetFiles(f, "*.txt") If files.ListExists Then @@ -606,13 +705,20 @@ Namespace API.Twitter f = RenameGdlFile(files(ii), ii) j = JsonDocument.Parse(f.GetText) If Not j Is Nothing Then - With j.ItemF({"data", 0, "instructions", 0, "entries", 0, - "content", "itemContent", "tweet_results", "result", "legacy"}) + With j.ItemF({"data", 0, "instructions", 0, "entries"}) If .ListExists Then - PostDate = String.Empty - If .Contains("created_at") Then PostDate = .Value("created_at") Else PostDate = String.Empty - ObtainMedia(.Self, m.Post.ID, PostDate, UStates.Missing) - rList.Add(i) + For Each n In .Self + For Each node In nodes + With n(node) + If .ListExists Then + PostDate = String.Empty + If .Contains("created_at") Then PostDate = .Value("created_at") Else PostDate = String.Empty + ObtainMedia(.Self, m.Post.ID, PostDate, UStates.Missing) + rList.ListAddValue(i, LNC) + End If + End With + Next + Next End If End With j.Dispose() @@ -630,7 +736,7 @@ Namespace API.Twitter Finally If Not cache Is Nothing And Not IsSingleObjectDownload Then cache.Dispose() If rList.Count > 0 Then - For i% = rList.Count - 1 To 0 Step -1 : _ContentList.RemoveAt(i) : Next + For i% = rList.Count - 1 To 0 Step -1 : _ContentList.RemoveAt(rList(i)) : Next rList.Clear() End If End Try diff --git a/SCrawler/API/UserDataBind.vb b/SCrawler/API/UserDataBind.vb index 86d3b2a..9fbaf4a 100644 --- a/SCrawler/API/UserDataBind.vb +++ b/SCrawler/API/UserDataBind.vb @@ -33,7 +33,7 @@ Namespace API End Property Friend Property CurrentlyEdited As Boolean = False Private _CollectionName As String = String.Empty - Friend Overrides Property CollectionName As String + Friend Overrides ReadOnly Property CollectionName As String Get If Count > 0 Then Return Collections(0).CollectionName @@ -41,14 +41,29 @@ Namespace API Return _CollectionName End If End Get - Set(ByVal NewName As String) - ChangeCollectionName(NewName, True) - End Set End Property - Friend Overrides Sub ChangeCollectionName(ByVal NewName As String, ByVal UpdateSettings As Boolean) - _CollectionName = NewName - If Count > 0 Then Collections.ForEach(Sub(c) c.CollectionName = NewName) + Friend Sub ChangeVirtualCollectionName(ByVal NewName As String) + If Count > 0 And Not NewName.IsEmptyString Then + Dim u As UserInfo + For Each user As UserDataBase In Collections + u = user.User + u.CollectionName = NewName + u.UpdateUserFile() + user.User = u + Settings.UpdateUsersList(u) + Next + End If End Sub + Private _CollectionPath As SFile = Nothing + Friend Overrides ReadOnly Property CollectionPath As SFile + Get + If Count > 0 And Not IsVirtual Then + Dim _RealUser As UserDataBase = GetRealUser() + If Not _RealUser Is Nothing Then Return _RealUser.User.SpecialCollectionPath + End If + Return _CollectionPath + End Get + End Property Friend Overrides ReadOnly Property Name As String Get Return CollectionName @@ -185,6 +200,43 @@ Namespace API UpdateUserInformation() End Set End Property + Friend Overrides Property BackColor As Color? + Get + If Count > 0 Then + With Collections.Select(Function(u) u.BackColor) + If .All(Function(c) c.HasValue) Then + Dim cc As Color = Collections(0).BackColor.Value + If .All(Function(c) c.Value = cc) Then Return cc + End If + End With + End If + Return Nothing + End Get + Set(ByVal b As Color?) + If Count > 0 Then Collections.ForEach(Sub(c) c.BackColor = b) + End Set + End Property + Friend Overrides Property ForeColor As Color? + Get + If Count > 0 Then + With Collections.Select(Function(u) u.ForeColor) + If .All(Function(c) c.HasValue) Then + Dim cc As Color = Collections(0).ForeColor.Value + If .All(Function(c) c.Value = cc) Then Return cc + End If + End With + End If + Return Nothing + End Get + Set(ByVal f As Color?) + If Count > 0 Then Collections.ForEach(Sub(c) c.ForeColor = f) + End Set + End Property + Friend Overrides ReadOnly Property IsSubscription As Boolean + Get + Return Count > 0 AndAlso Collections.All(Function(u) u.IsSubscription) + End Get + End Property Friend Overrides Property ReadyForDownload As Boolean Get Return Count > 0 AndAlso Collections(0).ReadyForDownload @@ -202,6 +254,16 @@ Namespace API End If End Get End Property + Friend Overrides ReadOnly Property SpecialLabels As IEnumerable(Of String) + Get + If Count > 0 Then + With Collections.SelectMany(Function(u As UserDataBase) u.SpecialLabels) + If .ListExists Then Return .Distinct + End With + End If + Return New String() {} + End Get + End Property Friend Overrides Function GetUserInformation() As String Dim OutStr$ = String.Empty If IsVirtual Then OutStr = "This is a virtual collection." @@ -268,6 +330,15 @@ Namespace API End If End Get End Property + Friend ReadOnly Property ContextErase As ToolStripMenuItem() + Get + If Count > 0 Then + Return Collections.Select(Function(c) DirectCast(c, UserDataBase).BTT_CONTEXT_ERASE).ToArray + Else + Return New ToolStripMenuItem() {} + End If + End Get + End Property Friend ReadOnly Property ContextPath As ToolStripMenuItem() Get If Count > 0 Then @@ -293,9 +364,10 @@ Namespace API _IsCollection = True Collections = New List(Of IUserData) End Sub - Friend Sub New(ByVal _Name As String) + Friend Sub New(ByVal _Name As String, Optional ByVal _Path As SFile = Nothing) Me.New - CollectionName = _Name + _CollectionName = _Name + _CollectionPath = _Path End Sub #End Region #Region "Load, Update" @@ -384,18 +456,13 @@ Namespace API Catch End Try End Sub - Friend Function GetRealUserFile() As SFile + Friend Function GetRealUser() As IUserData Dim i% = -1 If Count > 0 Then i = Collections.FindIndex(RealUser) - If i >= 0 Then Return Collections(i).File Else Return Nothing + Return If(i >= 0, Collections(i), Nothing) End Function - Friend Function GetRealUserSpecialCollectionPath() - Dim _SpecialCollectionPath As SFile = Nothing - If Count > 0 And Not IsVirtual Then - Dim _RealUser As UserDataBase = Collections.Find(RealUser) - If Not _RealUser Is Nothing Then _SpecialCollectionPath = _RealUser.User.SpecialCollectionPath - End If - Return _SpecialCollectionPath + Friend Function GetRealUserFile() As SFile + Return If(GetRealUser()?.File, New SFile) End Function #End Region #Region "ICollection Support" @@ -431,7 +498,7 @@ Namespace API ''' Friend Overloads Sub Add(ByVal _Item As IUserData) Implements ICollection(Of IUserData).Add With _Item - If .MoveFiles(CollectionName, GetRealUserSpecialCollectionPath()) Then + If .MoveFiles(CollectionName, CollectionPath) Then If Not _Item.IsVirtual And DataMerging Then DirectCast(.Self, UserDataBase).MergeData() Collections.Add(_Item) With Collections.Last @@ -480,12 +547,7 @@ Namespace API End Try End Sub Private Sub ConsolidateLabels() - If Count > 1 Then - Dim l As New List(Of String) - Dim lp As New ListAddParams(LAP.ClearBeforeAdd) - l.ListAddList(Collections.SelectMany(Function(c) c.Labels), LNC) - Collections.ForEach(Sub(c) c.Labels.ListAddList(l, lp)) - End If + UpdateLabels(Me, ListAddList(Nothing, Labels.ListWithRemove(SpecialLabels)), 1, True) End Sub Private Sub ConsolidateScripts() If Count > 1 AndAlso ScriptUse Then Collections.ForEach(Sub(c) c.ScriptUse = True) @@ -520,7 +582,14 @@ Namespace API End If End Sub #End Region -#Region "Remove, Delete" +#Region "Erase, Remove, Delete" + Friend Overrides Function EraseData(ByVal Mode As IUserData.EraseMode) As Boolean + If Count > 0 Then + Return Collections.All(Function(u) u.EraseData(Mode)) + Else + Return True + End If + End Function Friend Function Remove(ByVal _Item As IUserData) As Boolean Implements ICollection(Of IUserData).Remove If DataMerging Then MsgBoxE($"Collection [{CollectionName}] data is already merged" & vbCr & diff --git a/SCrawler/API/XVIDEOS/Declarations.vb b/SCrawler/API/XVIDEOS/Declarations.vb index f174492..3113c34 100644 --- a/SCrawler/API/XVIDEOS/Declarations.vb +++ b/SCrawler/API/XVIDEOS/Declarations.vb @@ -18,5 +18,8 @@ Namespace API.XVIDEOS Friend ReadOnly Regex_M3U8_Appender As RParams = RParams.DM("(.+)(?=/.+?\.m3u8.*?)", 0) Friend ReadOnly Regex_SavedVideosPlaylist As RParams = RParams.DM("\
+ Friend Class SiteSettings : Inherits SiteSettingsBase #Region "Declarations" Friend Overrides ReadOnly Property Icon As Icon @@ -44,6 +44,8 @@ Namespace API.XVIDEOS Domains.DestinationProp = SiteDomains DownloadUHD = New PropertyValue(False) SavedVideosPlaylist = New PropertyValue(String.Empty, GetType(String)) + + _SubscriptionsAllowed = True UrlPatternUser = "https://xvideos.com/{0}" End Sub Friend Overrides Sub EndInit() @@ -81,21 +83,19 @@ Namespace API.XVIDEOS End Function #End Region #Region "User: get, check" - Friend Function GetUserUrlPart(ByVal User As UserData) As String - Dim __user$ = User.Name.Split("_").FirstOrDefault - __user &= $"/{User.Name.Replace($"{__user}_", String.Empty)}" - Return __user - End Function Friend Overrides Function GetUserUrl(ByVal User As IPluginContentProvider) As String - Return String.Format(UrlPatternUser, GetUserUrlPart(User)) + Return DirectCast(User, UserData).GetUserUrl(0) End Function #End Region #Region "IsMyUser, IsMyImageVideo" Private Const UserRegexDefault As String = "/(profiles|[\w]*?[-]{0,1}channels)/([^/]+)(\Z|.*?)" Private Const URD As String = ".*?{0}{1}" + Private ReadOnly AbstractRegex As RParams = RParams.DM("[^/]+", 0, RegexReturn.List, EDP.ReturnValue) + Private ReadOnly SearchRegex As RParams = RParams.DMS("\?k=([^&]+)&?((.*)(&p=\d+)|(.*))", 0, RegexReturn.ListByMatch, EDP.ReturnValue) Friend Overrides Function IsMyUser(ByVal UserURL As String) As ExchangeOptions If Not UserURL.IsEmptyString Then - If Domains.Count > 0 Then + UserURL = UserURL.ToLower + If Domains.Count > 0 AndAlso Domains.Domains.Exists(Function(d) UserURL.Contains(d)) Then Dim uName$, uOpt$, fStr$ Dim uErr As New ErrorsDescriber(EDP.ReturnValue) For i% = 0 To Domains.Count - 1 @@ -103,9 +103,41 @@ Namespace API.XVIDEOS uName = RegexReplace(UserURL, RParams.DMS(fStr, 2, uErr)) If Not uName.IsEmptyString Then uOpt = RegexReplace(UserURL, RParams.DMS(fStr, 1)) - If Not uOpt.IsEmptyString Then Return New ExchangeOptions(Site, $"{uOpt}_{uName}") + If Not uOpt.IsEmptyString Then Return New ExchangeOptions(Site, $"{uOpt}@{uName}") End If Next + + Dim absList As List(Of String) = RegexReplace(UserURL, AbstractRegex) + If absList.ListExists(3) AndAlso Not absList(2).IsEmptyString Then + If absList(2) = "c" Then + If absList.Count > 3 AndAlso Not absList.Last.IsEmptyString AndAlso IsNumeric(absList.Last) Then absList.RemoveAt(absList.Count - 1) + If absList.Count > 3 Then + uName = $"{CInt(SiteModes.Categories)}@{absList.Last}" + uOpt = $"{absList.Last}@" + absList.RemoveAt(absList.Count - 1) + If absList.Count > 3 Then uOpt &= absList.ListTake(2, absList.Count).ListToString("/") + Return New ExchangeOptions(Site, uName) With {.Options = uOpt} + End If + ElseIf absList(2) = "tags" And absList.Count >= 4 Then + If Not absList.Last.IsEmptyString AndAlso IsNumeric(absList.Last) Then absList.RemoveAt(absList.Count - 1) + If absList.Count > 3 Then + uOpt = String.Empty + uName = absList.Last + absList.RemoveAt(absList.Count - 1) + If absList.Count > 3 Then uOpt = absList.ListTake(2, 100, EDP.ReturnValue).ListToString("/").StringTrimStart("/").StringTrimEnd("/") + uOpt = $"{uName}@{uOpt}" + uName = $"{CInt(SiteModes.Tags)}@{uName.StringRemoveWinForbiddenSymbols}" + Return New ExchangeOptions(Site, uName) With {.Options = uOpt} + End If + ElseIf absList.Count = 3 And Not absList(2).IsEmptyString Then + absList = RegexReplace(absList(2), SearchRegex) + If absList.ListExists(6) AndAlso Not absList(1).IsEmptyString Then + uName = $"{CInt(SiteModes.Search)}@{absList(1).StringRemoveWinForbiddenSymbols}" + uOpt = $"{absList(1)}@{absList(3).IfNullOrEmpty(absList(5))}" + Return New ExchangeOptions(Site, uName) With {.Options = uOpt} + End If + End If + End If End If End If Return Nothing @@ -116,6 +148,14 @@ Namespace API.XVIDEOS End If Return Nothing End Function +#End Region +#Region "UserOptions" + Friend Overrides Sub UserOptions(ByRef Options As Object, ByVal OpenForm As Boolean) + If Options Is Nothing OrElse Not TypeOf Options Is UserExchangeOptions Then Options = New UserExchangeOptions + If OpenForm Then + Using f As New InternalSettingsForm(Options, Me, False) : f.ShowDialog() : End Using + End If + End Sub #End Region End Class End Namespace \ No newline at end of file diff --git a/SCrawler/API/XVIDEOS/UserData.vb b/SCrawler/API/XVIDEOS/UserData.vb index b1354e8..13e520a 100644 --- a/SCrawler/API/XVIDEOS/UserData.vb +++ b/SCrawler/API/XVIDEOS/UserData.vb @@ -16,6 +16,13 @@ Imports PersonalUtilities.Tools.Web.Documents.JSON Imports UTypes = SCrawler.API.Base.UserMedia.Types Namespace API.XVIDEOS Friend Class UserData : Inherits UserDataBase +#Region "XML names" + Private Const Name_SiteMode As String = "SiteMode" + Private Const Name_TrueName As String = "TrueName" + Private Const Name_Arguments As String = "Arguments" + Private Const Name_PersonType As String = "PersonType" +#End Region +#Region "Structures" Private Structure PlayListVideo : Implements IRegExCreator Friend ID As String Friend URL As String @@ -33,23 +40,171 @@ Namespace API.XVIDEOS Return New UserMedia(URL, UTypes.VideoPre) With {.Object = Me, .PictureOption = Title, .Post = ID} End Function End Structure +#End Region +#Region "Declarations" + Friend Overrides ReadOnly Property FeedIsUser As Boolean + Get + Return SiteMode = SiteModes.User + End Get + End Property + Private Property SiteMode As SiteModes = SiteModes.User + Private Property TrueName As String = String.Empty + Private Property Arguments As String = String.Empty + Private Property PersonType As String = String.Empty + Friend Overrides ReadOnly Property SpecialLabels As IEnumerable(Of String) + Get + Return {SearchRequestLabelName} + End Get + End Property + Friend Property QueryString As String + Get + If SiteMode = SiteModes.User Then + Return String.Empty + Else + Return GetUserUrl(0) + End If + End Get + Set(ByVal q As String) + UpdateUserOptions(True, q) + End Set + End Property Private ReadOnly Property MySettings As SiteSettings Get Return DirectCast(HOST.Source, SiteSettings) End Get End Property - Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean) +#End Region +#Region "Load" + Friend Overrides Function ExchangeOptionsGet() As Object + Return New UserExchangeOptions(Me) + End Function + Friend Overrides Sub ExchangeOptionsSet(ByVal Obj As Object) + If Not Obj Is Nothing AndAlso TypeOf Obj Is UserExchangeOptions Then QueryString = DirectCast(Obj, UserExchangeOptions).QueryString End Sub + Private Function UpdateUserOptions(Optional ByVal Force As Boolean = False, Optional ByVal NewUrl As String = Nothing) As Boolean + If Not Force OrElse (Not SiteMode = SiteModes.User AndAlso Not NewUrl.IsEmptyString AndAlso MyFileSettings.Exists) Then + Dim eObj As Plugin.ExchangeOptions = Nothing + If Force Then eObj = MySettings.IsMyUser(NewUrl) + If (Force And Not eObj.UserName.IsEmptyString) Or (Not Force And TrueName.IsEmptyString) Then + Dim n$() = If(Force, eObj.UserName, Name).Split("@") + If n.ListExists(2) Then + Dim opt$ = If(Force, eObj.Options, Options) + If opt.IsEmptyString AndAlso Not IsNumeric(n(0)) Then + If Not Force Then + PersonType = n(0) + TrueName = If(Force, eObj.UserName, Name).Replace($"{PersonType}@", String.Empty) + End If + ElseIf Not opt.IsEmptyString Then + Dim n2$() = opt.Split("@") + Dim __SiteMode As SiteModes = CInt(n(0)) + Dim __TrueName$ = n2.FirstOrDefault + Dim __Arguments$ = opt.Replace($"{__TrueName}@", String.Empty) + Dim __ForceApply As Boolean = False + + If Force AndAlso (Not TrueName = __TrueName Or Not SiteMode = __SiteMode) Then + If ValidateChangeSearchOptions(ToStringForLog, $"{__SiteMode}: {__TrueName}", $"{SiteMode}: {TrueName}") Then + __ForceApply = True + Else + Return False + End If + End If + + Arguments = __Arguments + Options = opt + If Not Force Then + SiteMode = __SiteMode + TrueName = __TrueName + UserSiteName = $"{SiteMode}: {TrueName}" + If FriendlyName.IsEmptyString Then FriendlyName = UserSiteName + Settings.Labels.Add(SearchRequestLabelName) + Labels.ListAddValue(SearchRequestLabelName, LNC) + Labels.Sort() + ElseIf Force And __ForceApply Then + SiteMode = __SiteMode + TrueName = __TrueName + End If + + Return True + End If + End If + End If + End If + Return False + End Function + Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean) + With Container + If Loading Then + SiteMode = .Value(Name_SiteMode).FromXML(Of Integer)(SiteModes.User) + TrueName = .Value(Name_TrueName) + Arguments = .Value(Name_Arguments) + PersonType = .Value(Name_PersonType) + If PersonType.IsEmptyString And TrueName.IsEmptyString And Not Name.IsEmptyString Then + If Not Name.Contains("@") Then + Dim n$() = Name.Split("_") + PersonType = n(0) + TrueName = Name.Replace($"{PersonType}_", String.Empty) + End If + End If + UpdateUserOptions() + Else + If UpdateUserOptions() Then + .Value(Name_LabelsName) = LabelsString + .Value(Name_UserSiteName) = UserSiteName + .Value(Name_FriendlyName) = FriendlyName + End If + .Add(Name_SiteMode, CInt(SiteMode)) + .Add(Name_TrueName, TrueName) + .Add(Name_Arguments, Arguments) + .Add(Name_PersonType, PersonType) + + 'Debug.WriteLine(GetUserUrl(0)) + 'Debug.WriteLine(GetUserUrl(2)) + End If + End With + End Sub +#End Region +#Region "Initializer" Friend Sub New() SeparateVideoFolder = False UseInternalM3U8Function = True UseClientTokens = True End Sub +#End Region + Friend Function GetUserUrl(ByVal Page As Integer) As String + Dim url$ = String.Empty + If SiteMode = SiteModes.User Then + url = $"https://xvideos.com/{PersonType}/{TrueName}" + ElseIf SiteMode = SiteModes.Categories Then + url = "https://xvideos.com/c/" + If Not Arguments.IsEmptyString Then url &= $"{Arguments}/" + url &= TrueName + If Page > 1 Then url &= $"/{Page - 1}" + ElseIf SiteMode = SiteModes.Tags Then + url = "https://www.xvideos.com/tags/" + If Not Arguments.IsEmptyString Then url &= $"{Arguments}/" + url &= $"{TrueName}/" + If Page > 1 Then url &= Page - 1 + ElseIf SiteMode = SiteModes.Search Then + url = $"https://www.xvideos.com/?k={TrueName}" + If Not Arguments.IsEmptyString Then url &= $"&{Arguments}" + If Page > 1 Then url &= $"&p={Page - 1}" + End If + Return url + End Function + Private Sub Wait429(ByVal Round As Integer) + If (Round Mod 5) = 0 Then + Thread.Sleep(5000 + (Round / 5).RoundDown) + Else + Thread.Sleep(1000) + End If + End Sub Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken) If Not Settings.UseM3U8 Then MyMainLOG = $"{ToStringForLog()}: File [ffmpeg.exe] not found" : Exit Sub If IsSavedPosts Then If Not ACheck(MySettings.SavedVideosPlaylist.Value) Then Throw New ArgumentNullException("SavedVideosPlaylist", "Playlist of saved videos cannot be null") DownloadSavedVideos(Token) + ElseIf Not SiteMode = SiteModes.User Then + DownloadSavedVideos(Token) Else DownloadUserVideo(Token) End If @@ -59,11 +214,11 @@ Namespace API.XVIDEOS Dim isQuickies As Boolean = False Try Dim NextPage%, d% + Dim round% = 0 Dim limit% = If(DownloadTopCount, -1) Dim r$, n$ Dim j As EContainer = Nothing Dim jj As EContainer - Dim user$ = MySettings.GetUserUrlPart(Me) Dim p As UserMedia Dim EnvirSet As Boolean = False @@ -74,9 +229,12 @@ Namespace API.XVIDEOS d = 0 n = IIf(i = 0, "u", "url") Do + round += 1 + Wait429(round) ThrowAny(Token) If i = 0 Then - URL = $"https://www.xvideos.com/{user}/videos/new/{If(NextPage = 0, String.Empty, NextPage)}" + URL = GetUserUrl(0) + URL &= $"/videos/new/{If(NextPage = 0, String.Empty, NextPage)}" Else 'Quickies URL = $"https://www.xvideos.com/quickies-api/profilevideos/all/none/N/{ID}/{NextPage}" isQuickies = True @@ -95,10 +253,7 @@ Namespace API.XVIDEOS NextPage += 1 For Each jj In .Self ProgressPre.Perform() - p = New UserMedia With { - .Post = jj.Value("id"), - .URL = $"https://www.xvideos.com/{jj.Value(n).StringTrimStart("/")}" - } + p = New UserMedia($"https://www.xvideos.com/{jj.Value(n).StringTrimStart("/")}") With {.Post = jj.Value("id")} If Not p.Post.ID.IsEmptyString And Not jj.Value(n).IsEmptyString Then If Not _TempPostsList.Contains(p.Post.ID) Then _TempPostsList.Add(p.Post.ID) @@ -124,10 +279,19 @@ Namespace API.XVIDEOS If Not j Is Nothing Then j.Dispose() + If limit > 0 And _TempMediaList.Count >= limit Then _TempMediaList.ListAddList(_TempMediaList.ListTake(-1, limit), LAP.ClearBeforeAdd) If _TempMediaList.Count > 0 Then - ProgressPre.ChangeMax(_TempMediaList.Count) + If IsSubscription Then + Progress.Maximum += _TempMediaList.Count + Else + ProgressPre.ChangeMax(_TempMediaList.Count) + End If For i% = 0 To _TempMediaList.Count - 1 - ProgressPre.Perform() + If IsSubscription Then + Progress.Perform() + Else + ProgressPre.Perform() + End If ThrowAny(Token) _TempMediaList(i) = GetVideoData(_TempMediaList(i)) Next @@ -140,25 +304,40 @@ Namespace API.XVIDEOS End Try End Sub Private Sub GetUserID() - Dim r$ = Responser.GetResponse($"https://www.xvideos.com/{MySettings.GetUserUrlPart(Me)}",, EDP.ReturnValue) + Dim r$ = Responser.GetResponse(GetUserUrl(0),, EDP.ReturnValue) If Not r.IsEmptyString Then ID = RegexReplace(r, RParams.DMS("""id_user"":(\d+)", 1, EDP.ReturnValue)) End Sub Private Sub DownloadSavedVideos(ByVal Token As CancellationToken) Dim URL$ = MySettings.SavedVideosPlaylist.Value Try - Dim NextPage% = 0 + Dim NextPage% = IIf(SiteMode = SiteModes.User, -1, 0) + Dim startPage% = NextPage Dim __continue As Boolean = True Dim r$ + Dim round% = 0 Dim data As List(Of PlayListVideo) - Dim i% + Dim cBefore% + + Dim limit% = If(DownloadTopCount, -1) Do + round += 1 + Wait429(round) ThrowAny(Token) - URL = $"{MySettings.SavedVideosPlaylist.Value}{If(NextPage = 0, String.Empty, $"/{NextPage}")}" + NextPage += 1 + cBefore = _TempMediaList.Count + + If SiteMode = SiteModes.User Then + URL = $"{MySettings.SavedVideosPlaylist.Value}{If(NextPage = 0, String.Empty, $"/{NextPage}")}" + Else + URL = GetUserUrl(NextPage) + End If + r = Responser.GetResponse(URL,, EDP.ReturnValue) + If Responser.HasError Then If Responser.StatusCode = Net.HttpStatusCode.NotFound Then - If NextPage = 0 Then - MyMainLOG = $"XVIDEOS saved video playlist {URL} not found." + If NextPage = startPage Then + If SiteMode = SiteModes.User Then MyMainLOG = $"XVIDEOS saved video playlist {URL} not found." Exit Sub Else Exit Do @@ -167,26 +346,32 @@ Namespace API.XVIDEOS Throw New Exception(Responser.ErrorText, Responser.ErrorException) End If End If - NextPage += 1 + If Not r.IsEmptyString Then data = RegexFields(Of PlayListVideo)(r, {Regex_SavedVideosPlaylist}, {1, 2, 3}, EDP.ReturnValue) If data.ListExists Then If data.RemoveAll(Function(d) _TempPostsList.Contains(d.ID)) > 0 Then __continue = False If data.ListExists Then _TempPostsList.ListAddList(data.Select(Function(d) d.ID), LNC) - i = _TempMediaList.Count _TempMediaList.ListAddList(data.Select(Function(d) d.ToUserMedia()), LNC) - If _TempMediaList.Count = i Or Not __continue Then Exit Do Else Continue Do End If End If End If - Exit Do - Loop While NextPage < 100 And __continue + Loop While NextPage < 100 And __continue And _TempMediaList.Count > cBefore And (limit < 0 Or _TempMediaList.Count < limit) + If limit > 0 And _TempMediaList.Count >= limit Then _TempMediaList.ListAddList(_TempMediaList.ListTake(-1, limit), LAP.ClearBeforeAdd) If _TempMediaList.Count > 0 Then - ProgressPre.ChangeMax(_TempMediaList.Count) + If SiteMode = SiteModes.User Then + ProgressPre.ChangeMax(_TempMediaList.Count) + Else + Progress.Maximum += _TempMediaList.Count + End If For i% = 0 To _TempMediaList.Count - 1 - ProgressPre.Perform() + If SiteMode = SiteModes.User Then + ProgressPre.Perform() + Else + Progress.Perform() + End If ThrowAny(Token) _TempMediaList(i) = GetVideoData(_TempMediaList(i)) Next @@ -205,31 +390,50 @@ Namespace API.XVIDEOS If Not NewUrl.IsEmptyString Then Dim appender$ = RegexReplace(NewUrl, Regex_M3U8_Appender) Dim t$ = If(Media.PictureOption.IsEmptyString, RegexReplace(r, Regex_VideoTitle), Media.PictureOption) - r = Responser.GetResponse(NewUrl) - If Not r.IsEmptyString Then - Dim ls As List(Of Sizes) = RegexFields(Of Sizes)(r, {Regex_M3U8_Reparse}, {1, 2}) - If ls.ListExists And Not MySettings.DownloadUHD.Value Then ls.RemoveAll(Function(v) Not v.Value.ValueBetween(1, 1080)) - If ls.ListExists Then - ls.Sort() - NewUrl = $"{appender}/{ls(0).Data.StringTrimStart("/")}" - ls.Clear() - Dim pID$ = Media.Post.ID - If pID.IsEmptyString Then pID = RegexReplace(r, Regex_VideoID) - If pID.IsEmptyString Then pID = "0" - - t = t.StringRemoveWinForbiddenSymbols.StringTrim - If t.IsEmptyString Then - t = pID + If IsSubscription Then + Dim thumb$ = RegexReplace(r, Regex_VideoThumbBig) + If thumb.IsEmptyString Then thumb = RegexReplace(r, Regex_VideoThumbSmall) + If thumb.IsEmptyString Then thumb = RegexReplace(r, Regex_VideosThumb_OG_IMAGE) + If Not thumb.IsEmptyString Then + Media.URL = thumb + If Not t.IsEmptyString Then + Media.PictureOption = t + Media.File = $"{t}.mp4" Else - If t.Length > 100 Then t = Left(t, 100) + Media.PictureOption = "Video" + Media.File = "Video.mp4" End If - If Not NewUrl.IsEmptyString Then - Return New UserMedia(NewUrl, UTypes.m3u8) With { - .Post = pID, - .URL_BASE = Media.URL, - .File = $"{t}.mp4", - .PictureOption = appender - } + Return Media + Else + Return Nothing + End If + Else + r = Responser.GetResponse(NewUrl) + If Not r.IsEmptyString Then + Dim ls As List(Of Sizes) = RegexFields(Of Sizes)(r, {Regex_M3U8_Reparse}, {1, 2}) + If ls.ListExists And Not MySettings.DownloadUHD.Value Then ls.RemoveAll(Function(v) Not v.Value.ValueBetween(1, 1080)) + If ls.ListExists Then + ls.Sort() + NewUrl = $"{appender}/{ls(0).Data.StringTrimStart("/")}" + ls.Clear() + Dim pID$ = Media.Post.ID + If pID.IsEmptyString Then pID = RegexReplace(r, Regex_VideoID) + If pID.IsEmptyString Then pID = "0" + + t = t.StringRemoveWinForbiddenSymbols.StringTrim + If t.IsEmptyString Then + t = pID + Else + If t.Length > 100 Then t = Left(t, 100) + End If + If Not NewUrl.IsEmptyString Then + Return New UserMedia(NewUrl, UTypes.m3u8) With { + .Post = pID, + .URL_BASE = Media.URL, + .File = $"{t}.mp4", + .PictureOption = appender + } + End If End If End If End If diff --git a/SCrawler/API/XVIDEOS/UserExchangeOptions.vb b/SCrawler/API/XVIDEOS/UserExchangeOptions.vb new file mode 100644 index 0000000..593fefc --- /dev/null +++ b/SCrawler/API/XVIDEOS/UserExchangeOptions.vb @@ -0,0 +1,17 @@ +' Copyright (C) 2023 Andy https://github.com/AAndyProgram +' 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 API.XVIDEOS + Friend Class UserExchangeOptions : Inherits Xhamster.UserExchangeOptions + Friend Sub New() + End Sub + Friend Sub New(ByVal u As UserData) + QueryString = u.QueryString + End Sub + End Class +End Namespace \ No newline at end of file diff --git a/SCrawler/API/Xhamster/SiteSettings.vb b/SCrawler/API/Xhamster/SiteSettings.vb index 39b1b5e..df33387 100644 --- a/SCrawler/API/Xhamster/SiteSettings.vb +++ b/SCrawler/API/Xhamster/SiteSettings.vb @@ -12,7 +12,7 @@ Imports SCrawler.Plugin.Attributes Imports PersonalUtilities.Tools.Web.Clients Imports PersonalUtilities.Functions.RegularExpressions Namespace API.Xhamster - + Friend Class SiteSettings : Inherits SiteSettingsBase #Region "Declarations" Friend Overrides ReadOnly Property Icon As Icon @@ -39,7 +39,7 @@ Namespace API.Xhamster Domains.DestinationProp = SiteDomains DownloadUHD = New PropertyValue(False) - + _SubscriptionsAllowed = True UrlPatternUser = "https://xhamster.com/{0}/{1}" UserRegex = RParams.DMS($"/({UserOption}|{ChannelOption})/([^/]+)(\Z|.*)", 0, RegexReturn.ListByMatch) ImageVideoContains = "xhamster" @@ -77,18 +77,60 @@ Namespace API.Xhamster End If End Function Friend Overrides Function GetUserUrl(ByVal User As IPluginContentProvider) As String - With DirectCast(User, UserData) : Return String.Format(UrlPatternUser, IIf(.IsChannel, ChannelOption, UserOption), .TrueName) : End With + With DirectCast(User, UserData) + If Not .SiteMode = SiteModes.User Then + Return .GetNonUserUrl(0) + Else + Return String.Format(UrlPatternUser, IIf(.IsChannel, ChannelOption, UserOption), .TrueName) + End If + End With End Function #Region "IsMyUser, IsMyImageVideo" Friend Const ChannelOption As String = "channels" Private Const UserOption As String = "users" + Friend Const P_Search As String = "search" + Friend Const P_Tags As String = "tags" + Friend Const P_Categories As String = "categories" + Friend Const P_Pornstars = "pornstars" + Private ReadOnly NonUsersRegex As RParams = RParams.DM("https?://[^/]+/((gay)/|(shemale)/|)(pornstars|tags|categories|search)/([^/\?]+)[/\?]?(.*)", 0, + RegexReturn.ListByMatch, EDP.ReturnValue) + Private ReadOnly PageRemover_1 As RParams = RParams.DM("[\?&]?[Pp]age=\d+", 0, RegexReturn.Replace, EDP.ReturnValue, + CType(Function(input) String.Empty, Func(Of String, String))) + Private ReadOnly PageRemover_2 As RParams = RParams.DM("/\d+\?", 0, RegexReturn.Replace, EDP.ReturnValue, + CType(Function(input) "?", Func(Of String, String))) Friend Overrides Function IsMyUser(ByVal UserURL As String) As ExchangeOptions If Not UserURL.IsEmptyString AndAlso Domains.Domains.Count > 0 AndAlso Domains.Domains.Exists(Function(d) UserURL.ToLower.Contains(d.ToLower)) Then + Dim n$, opt$ Dim data As List(Of String) = RegexReplace(UserURL, UserRegex) If data.ListExists(3) AndAlso Not data(2).IsEmptyString Then - Dim n$ = data(2) + n = data(2) If Not data(1).IsEmptyString AndAlso data(1) = ChannelOption Then n &= $"@{data(1)}" Return New ExchangeOptions(Site, n) + Else + data = RegexReplace(UserURL, NonUsersRegex) + If data.ListExists(7) AndAlso Not data(5).IsEmptyString Then + n = data(5).StringRemoveWinForbiddenSymbols + If Not n.IsEmptyString And Not data(4).IsEmptyString Then + Dim mode As SiteModes + Select Case data(4) + Case P_Search : mode = SiteModes.Search + Case P_Tags : mode = SiteModes.Tags + Case P_Categories : mode = SiteModes.Categories + Case P_Pornstars : mode = SiteModes.Pornstars + Case Else : Return Nothing + End Select + n = $"{CInt(mode)}@{n}" + Dim tmpOpt$ = data(6) + + If Not tmpOpt.IsEmptyString Then + tmpOpt = RegexReplace(tmpOpt, PageRemover_1) + tmpOpt = RegexReplace(tmpOpt, PageRemover_2) + End If + 'mode@gay@tags@arguments@query + opt = $"{CInt(mode)}@{data(2)}@{data(4)}@{tmpOpt}@{data(5)}" + Return New ExchangeOptions(Site, n) With {.Options = opt} + End If + End If End If End If Return Nothing @@ -99,6 +141,14 @@ Namespace API.Xhamster End If Return Nothing End Function +#End Region +#Region "UserOptions" + Friend Overrides Sub UserOptions(ByRef Options As Object, ByVal OpenForm As Boolean) + If Options Is Nothing OrElse Not TypeOf Options Is UserExchangeOptions Then Options = New UserExchangeOptions + If OpenForm Then + Using f As New InternalSettingsForm(Options, Me, False) : f.ShowDialog() : End Using + End If + End Sub #End Region End Class End Namespace \ No newline at end of file diff --git a/SCrawler/API/Xhamster/UserData.vb b/SCrawler/API/Xhamster/UserData.vb index d5c8b5a..73102c1 100644 --- a/SCrawler/API/Xhamster/UserData.vb +++ b/SCrawler/API/Xhamster/UserData.vb @@ -19,10 +19,38 @@ Namespace API.Xhamster Friend Class UserData : Inherits UserDataBase #Region "XML names" Private Const Name_TrueName As String = "TrueName" + Private Const Name_Gender As String = "Gender" + Private Const Name_SiteMode As String = "SiteMode" + Private Const Name_Arguments As String = "Arguments" #End Region #Region "Declarations" + Friend Overrides ReadOnly Property FeedIsUser As Boolean + Get + Return SiteMode = SiteModes.User + End Get + End Property Friend Property IsChannel As Boolean = False Friend Property TrueName As String = String.Empty + Friend Property Gender As String = String.Empty + Friend Property SiteMode As SiteModes = SiteModes.User + Friend Property Arguments As String = String.Empty + Friend Overrides ReadOnly Property SpecialLabels As IEnumerable(Of String) + Get + Return {SearchRequestLabelName} + End Get + End Property + Friend Property QueryString As String + Get + If SiteMode = SiteModes.User Then + Return String.Empty + Else + Return GetNonUserUrl(0) + End If + End Get + Set(ByVal q As String) + UpdateUserOptions(True, q) + End Set + End Property Private ReadOnly Property MySettings As SiteSettings Get Return DirectCast(HOST.Source, SiteSettings) @@ -32,35 +60,106 @@ Namespace API.Xhamster Friend IsPhoto As Boolean End Structure Private ReadOnly _TempPhotoData As List(Of UserMedia) + Private Function UpdateUserOptions(Optional ByVal Force As Boolean = False, Optional ByVal NewUrl As String = Nothing) As Boolean + If Not Force OrElse (Not SiteMode = SiteModes.User AndAlso Not NewUrl.IsEmptyString AndAlso MyFileSettings.Exists) Then + Dim eObj As Plugin.ExchangeOptions = Nothing + If Force Then eObj = MySettings.IsMyUser(NewUrl) + If (Force And Not eObj.UserName.IsEmptyString) Or (Not Force And TrueName.IsEmptyString) Then + Dim n$() = If(Force, eObj.UserName, Name).Split("@") + If n.ListExists Then + If n.Length = 2 And If(Force, eObj.Options, Options).IsEmptyString Then + If Force Then Return False + TrueName = n(0) + IsChannel = True + ElseIf IsChannel Then + If Force Then Return False + TrueName = Name + ElseIf Not If(Force, eObj.Options, Options).IsEmptyString Then + Dim __TrueName$, __Arguments$, __Gender$ + Dim __Mode As SiteModes + Dim __ForceApply As Boolean = False + Dim n2 As List(Of String) = If(Force, eObj.Options, Options).Split("@").ListIfNothing + If n2.ListExists Then + IsChannel = False + __Mode = CInt(n2(0)) + __Gender = n2(1) + __Arguments = n2(3) + __TrueName = n2.ListTake(3, 100, EDP.ReturnValue).ListToString(String.Empty) + + If Force AndAlso (Not TrueName = __TrueName Or Not SiteMode = __Mode Or Not Gender = __Gender) Then + If ValidateChangeSearchOptions(ToStringForLog, + $"{__Mode}{IIf(__Gender.IsEmptyString, String.Empty, $" ({__Gender})")}: {__TrueName}", + $"{SiteMode}{IIf(Gender.IsEmptyString, String.Empty, $" ({Gender})")}: {TrueName}") Then + __ForceApply = True + Else + Return False + End If + End If + + Arguments = __Arguments + Options = If(Force, eObj.Options, Options) + If Not Force Then + TrueName = __TrueName + SiteMode = __Mode + Gender = __Gender + + UserSiteName = $"{SiteMode}: {TrueName}" + If FriendlyName.IsEmptyString Then FriendlyName = UserSiteName + Settings.Labels.Add(SearchRequestLabelName) + Labels.ListAddValue(SearchRequestLabelName, LNC) + Labels.Sort() + ElseIf Force And __ForceApply Then + TrueName = __TrueName + SiteMode = __Mode + Gender = __Gender + End If + + Return True + Else + If Force Then Return False + UserExists = False + End If + Else + If Force Then Return False + TrueName = n(0) + End If + End If + End If + End If + Return False + End Function Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean) - Dim setNames As Action = Sub() - If TrueName.IsEmptyString Then - Dim n$() = Name.Split("@") - If n.ListExists Then - If n.Length = 2 Then - TrueName = n(0) - IsChannel = True - ElseIf IsChannel Then - TrueName = Name - Else - TrueName = n(0) - End If - End If - End If - End Sub With Container If Loading Then IsChannel = .Value(Name_IsChannel).FromXML(Of Boolean)(False) TrueName = .Value(Name_TrueName) - setNames.Invoke + Gender = .Value(Name_Gender) + SiteMode = .Value(Name_SiteMode).FromXML(Of Integer)(SiteModes.User) + Arguments = .Value(Name_Arguments) + UpdateUserOptions() Else - setNames.Invoke + If UpdateUserOptions() Then + .Value(Name_LabelsName) = LabelsString + .Value(Name_UserSiteName) = UserSiteName + .Value(Name_FriendlyName) = FriendlyName + End If .Add(Name_IsChannel, IsChannel.BoolToInteger) .Add(Name_TrueName, TrueName) - setNames.Invoke + .Add(Name_Gender, Gender) + .Add(Name_SiteMode, CInt(SiteMode)) + .Add(Name_Arguments, Arguments) + + 'Debug.WriteLine(GetNonUserUrl(0)) + 'Debug.WriteLine(GetNonUserUrl(2)) End If End With End Sub + Friend Overrides Function ExchangeOptionsGet() As Object + Return New UserExchangeOptions(Me) + End Function + Friend Overrides Sub ExchangeOptionsSet(ByVal Obj As Object) + If Not Obj Is Nothing AndAlso TypeOf Obj Is UserExchangeOptions Then QueryString = DirectCast(Obj, UserExchangeOptions).QueryString + End Sub #End Region #Region "Initializer" Friend Sub New() @@ -69,11 +168,56 @@ Namespace API.Xhamster _TempPhotoData = New List(Of UserMedia) End Sub #End Region -#Region "Download base functions" +#Region "Download functions" + Friend Function GetNonUserUrl(ByVal Page As Integer) As String + If SiteMode = SiteModes.User Then + Return String.Empty + Else + Dim url$ = "https://xhamster.com/" + If Not Gender.IsEmptyString Then url &= $"{Gender}/" + Select Case SiteMode + Case SiteModes.Tags : url &= SiteSettings.P_Tags + Case SiteModes.Categories : url &= SiteSettings.P_Categories + Case SiteModes.Search : url &= SiteSettings.P_Search + Case SiteModes.Pornstars : url &= SiteSettings.P_Pornstars + Case Else : Return String.Empty + End Select + url &= $"/{TrueName}" + + Dim args$ = Arguments + If Page > 1 Then + If args.IsEmptyString Then + If SiteMode = SiteModes.Search Then + args = $"?page={Page}" + Else + args = $"/{Page}" + End If + Else + If SiteMode = SiteModes.Search Then + args = $"?{args}&page={Page}" + Else + If args.Contains("?") Then + args = $"/{args.Replace("?", $"/{Page}?")}" + Else + args = $"/{args.StringTrimEnd("/")}/{Page}" + End If + End If + End If + Else + If Not args.IsEmptyString Then args = $"{IIf(SiteMode = SiteModes.Search, "?", "/")}{args}" + End If + + url &= args + + Return url + End If + End Function + Private SearchPostsCount As Integer = 0 Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken) _TempPhotoData.Clear() + SearchPostsCount = 0 If DownloadVideos Then DownloadData(1, True, Token) - If Not IsChannel And DownloadImages Then + If Not IsChannel And DownloadImages And Not IsSubscription Then DownloadData(1, False, Token) ReparsePhoto(Token) End If @@ -85,19 +229,35 @@ Namespace API.Xhamster Dim Type As UTypes = IIf(IsVideo, UTypes.VideoPre, UTypes.Picture) Dim mPages$ = IIf(IsVideo, "maxVideoPages", "maxPhotoPages") Dim listNode$() + Dim containerNodes As New List(Of String()) Dim skipped As Boolean = False + Dim limit% = If(DownloadTopCount, -1) Dim cBefore% = _TempMediaList.Count Dim m As UserMedia + Dim checkLimit As Func(Of Boolean) = Function() limit > 0 And SearchPostsCount >= limit And IsVideo If IsSavedPosts Then URL = $"https://xhamster.com/my/favorites/{IIf(IsVideo, "videos", "photos-and-galleries")}{IIf(Page = 1, String.Empty, $"/{Page}")}" - listNode = If(IsVideo, {"favoriteVideoListComponent", "models"}, {"favoritesGalleriesAndPhotosCollection"}) + containerNodes.Add(If(IsVideo, {"favoriteVideoListComponent", "models"}, {"favoritesGalleriesAndPhotosCollection"})) ElseIf IsChannel Then URL = $"https://xhamster.com/channels/{TrueName}/newest{IIf(Page = 1, String.Empty, $"/{Page}")}" - listNode = {"trendingVideoListComponent", "models"} + containerNodes.Add({"trendingVideoListComponent", "models"}) + containerNodes.Add({"pagesCategoryComponent", "trendingVideoListProps", "models"}) + ElseIf SiteMode = SiteModes.Search Then + URL = GetNonUserUrl(Page) + containerNodes.Add({"searchResult", "models"}) + ElseIf SiteMode = SiteModes.Tags Or SiteMode = SiteModes.Categories Or SiteMode = SiteModes.Pornstars Then + URL = GetNonUserUrl(Page) + If SiteMode = SiteModes.Pornstars Then + containerNodes.Add({"trendingVideoListComponent", "models"}) + containerNodes.Add({"pagesCategoryComponent", "trendingVideoListProps", "models"}) + Else + containerNodes.Add({"pagesCategoryComponent", "trendingVideoListProps", "models"}) + containerNodes.Add({"trendingVideoListComponent", "models"}) + End If Else URL = $"https://xhamster.com/users/{TrueName}/{IIf(IsVideo, "videos", "photos")}{IIf(Page = 1, String.Empty, $"/{Page}")}" - listNode = {If(IsVideo, "userVideoCollection", "userGalleriesCollection")} + containerNodes.Add({If(IsVideo, "userVideoCollection", "userGalleriesCollection")}) End If ThrowAny(Token) @@ -111,47 +271,54 @@ Namespace API.Xhamster MaxPage = j.Value(mPages).FromXML(Of Integer)(-1) - With j(listNode) - If .ListExists Then - ProgressPre.ChangeMax(.Count) - For Each e As EContainer In .Self - ProgressPre.Perform() - m = ExtractMedia(e, Type) - If Not m.URL.IsEmptyString Then - If m.File.IsEmptyString Then Continue For + For Each listNode In containerNodes + With j(listNode) + If .ListExists Then + ProgressPre.ChangeMax(.Count) + For Each e As EContainer In .Self + ProgressPre.Perform() + m = ExtractMedia(e, Type) + If Not m.URL.IsEmptyString Then + If m.File.IsEmptyString Then Continue For - If m.Post.Date.HasValue Then - Select Case CheckDatesLimit(m.Post.Date.Value, Nothing) - Case DateResult.Skip : skipped = True : Continue For - Case DateResult.Exit : Exit Sub - End Select - End If + If m.Post.Date.HasValue Then + Select Case CheckDatesLimit(m.Post.Date.Value, Nothing) + Case DateResult.Skip : skipped = True : Continue For + Case DateResult.Exit : Exit Sub + End Select + End If - If IsVideo AndAlso Not _TempPostsList.Contains(m.Post.ID) Then - _TempPostsList.Add(m.Post.ID) - _TempMediaList.ListAddValue(m, LNC) - ElseIf Not IsVideo Then - If DirectCast(m.Object, ExchObj).IsPhoto Then - If Not m.Post.ID.IsEmptyString AndAlso Not _TempPostsList.Contains(m.Post.ID) Then - _TempPostsList.Add(m.Post.ID) - _TempMediaList.ListAddValue(m, LNC) + If IsVideo AndAlso Not _TempPostsList.Contains(m.Post.ID) Then + _TempPostsList.Add(m.Post.ID) + _TempMediaList.ListAddValue(m, LNC) + SearchPostsCount += 1 + If checkLimit.Invoke Then Exit Sub + ElseIf Not IsVideo Then + If DirectCast(m.Object, ExchObj).IsPhoto Then + If Not m.Post.ID.IsEmptyString AndAlso Not _TempPostsList.Contains(m.Post.ID) Then + _TempPostsList.Add(m.Post.ID) + _TempMediaList.ListAddValue(m, LNC) + End If + Else + _TempPhotoData.ListAddValue(m, LNC) End If Else - _TempPhotoData.ListAddValue(m, LNC) + Exit Sub End If - Else - Exit Sub End If - End If - Next - End If - End With + Next + Exit For + End If + End With + Next End If End Using End If + containerNodes.Clear() + If (Not _TempMediaList.Count = cBefore Or skipped) And - (IsChannel Or (MaxPage > 0 And Page < MaxPage)) Then DownloadData(Page + 1, IsVideo, Token) + (IsChannel Or (MaxPage > 0 And Page < MaxPage) Or (Not SiteMode = SiteModes.User And Page < 1000)) Then DownloadData(Page + 1, IsVideo, Token) Catch ex As Exception ProcessException(ex, Token, $"data downloading error [{URL}]") End Try @@ -159,30 +326,64 @@ Namespace API.Xhamster #End Region #Region "Reparse video, photo" Protected Overrides Sub ReparseVideo(ByVal Token As CancellationToken) - Dim URL$ = String.Empty + If IsSubscription Then + ReparseVideoSubscriptions(Token) + Else + Try + If _TempMediaList.Count > 0 AndAlso _TempMediaList.Exists(Function(tm) tm.Type = UTypes.VideoPre) Then + Dim m As UserMedia, m2 As UserMedia + ProgressPre.ChangeMax(_TempMediaList.Count) + For i% = _TempMediaList.Count - 1 To 0 Step -1 + ProgressPre.Perform() + If _TempMediaList(i).Type = UTypes.VideoPre Then + m = _TempMediaList(i) + If Not m.URL_BASE.IsEmptyString Then + m2 = Nothing + If GetM3U8(m2, m.URL_BASE) Then + m2.URL_BASE = m.URL_BASE + _TempMediaList(i) = m2 + Else + m.State = UserMedia.States.Missing + _TempMediaList(i) = m + End If + End If + End If + Next + End If + Catch ex As Exception + ProcessException(ex, Token, "video reparsing error", False) + End Try + End If + End Sub + Private Sub ReparseVideoSubscriptions(ByVal Token As CancellationToken) Try If _TempMediaList.Count > 0 AndAlso _TempMediaList.Exists(Function(tm) tm.Type = UTypes.VideoPre) Then Dim m As UserMedia, m2 As UserMedia - ProgressPre.ChangeMax(_TempMediaList.Count) + Dim c% = 0 + Progress.Maximum += _TempMediaList.Count For i% = _TempMediaList.Count - 1 To 0 Step -1 - ProgressPre.Perform() + Progress.Perform() If _TempMediaList(i).Type = UTypes.VideoPre Then - m = _TempMediaList(i) - If Not m.URL_BASE.IsEmptyString Then - m2 = Nothing - If GetM3U8(m2, m.URL_BASE) Then - m2.URL_BASE = m.URL_BASE - _TempMediaList(i) = m2 - Else - m.State = UserMedia.States.Missing - _TempMediaList(i) = m + If Not DownloadTopCount.HasValue OrElse c <= DownloadTopCount.Value Then + m = _TempMediaList(i) + If Not m.URL_BASE.IsEmptyString Then + m2 = Nothing + If GetM3U8(m2, m.URL_BASE) Then + m2.URL_BASE = m.URL_BASE + _TempMediaList(i) = m2 + c += 1 + Else + _TempMediaList.RemoveAt(i) + End If End If + Else + _TempMediaList.RemoveAt(i) End If End If Next End If Catch ex As Exception - ProcessException(ex, Token, "video reparsing error", False) + ProcessException(ex, Token, "subscriptions video reparsing error", False) End Try End Sub Private Overloads Sub ReparsePhoto(ByVal Token As CancellationToken) @@ -277,7 +478,16 @@ Namespace API.Xhamster If j.ListExists Then m = ExtractMedia(j("videoModel"), UTypes.VideoPre) m.URL_BASE = URL - Return GetM3U8(m, j) + If IsSubscription Then + With j("videoModel") + If .ListExists Then + m.URL = .Value("thumbURL").IfNullOrEmpty(.Value("previewThumbURL")) + Return Not m.URL.IsEmptyString + End If + End With + Else + Return GetM3U8(m, j) + End If End If End Using End If diff --git a/SCrawler/API/Xhamster/UserExchangeOptions.vb b/SCrawler/API/Xhamster/UserExchangeOptions.vb new file mode 100644 index 0000000..39ae828 --- /dev/null +++ b/SCrawler/API/Xhamster/UserExchangeOptions.vb @@ -0,0 +1,21 @@ +' Copyright (C) 2023 Andy https://github.com/AAndyProgram +' 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 SCrawler.Plugin.Attributes +Namespace API.Xhamster + Friend Class UserExchangeOptions + + Friend Property QueryString As String + Friend Sub New() + End Sub + Friend Sub New(ByVal u As UserData) + QueryString = u.QueryString + End Sub + End Class +End Namespace \ No newline at end of file diff --git a/SCrawler/API/YouTube/SiteSettings.vb b/SCrawler/API/YouTube/SiteSettings.vb index 7f9f69d..9163f25 100644 --- a/SCrawler/API/YouTube/SiteSettings.vb +++ b/SCrawler/API/YouTube/SiteSettings.vb @@ -41,6 +41,7 @@ Namespace API.YouTube DownloadShorts = New PropertyValue(False) DownloadPlaylists = New PropertyValue(False) UseCookies = New PropertyValue(False) + _SubscriptionsAllowed = True End Sub #End Region #Region "GetInstance" diff --git a/SCrawler/API/YouTube/UserData.vb b/SCrawler/API/YouTube/UserData.vb index dea39bd..7a7ffc8 100644 --- a/SCrawler/API/YouTube/UserData.vb +++ b/SCrawler/API/YouTube/UserData.vb @@ -113,6 +113,7 @@ Namespace API.YouTube Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken) Dim pr As New YTPreProgress(ProgressPre) Try + If IsSubscription And IsMusic Then Exit Sub Dim container As IYouTubeMediaContainer = Nothing Dim list As New List(Of IYouTubeMediaContainer) Dim url$ = String.Empty @@ -191,8 +192,9 @@ Namespace API.YouTube If Settings.UserSiteNameUpdateEveryTime Or UserSiteName.IsEmptyString Then UserSiteName = .UserTitle If FriendlyName.IsEmptyString Then FriendlyName = UserSiteName End With - _TempMediaList.AddRange(list.Select(Function(c) New UserMedia(c))) + _TempMediaList.AddRange(list.Select(Function(c) New UserMedia(c) With {.URL = If(IsSubscription, c.ThumbnailUrlMedia, .URL)})) _TempPostsList.ListAddList(_TempMediaList.Select(Function(m) m.Post.ID), LNC) + If IsSubscription Then _TempMediaList.RemoveAll(Function(m) m.URL.IsEmptyString) list.Clear() End If Catch ex As Exception diff --git a/SCrawler/Channels/ChannelViewForm.vb b/SCrawler/Channels/ChannelViewForm.vb index 134d478..6c03476 100644 --- a/SCrawler/Channels/ChannelViewForm.vb +++ b/SCrawler/Channels/ChannelViewForm.vb @@ -336,7 +336,7 @@ Friend Class ChannelViewForm : Implements IChannelLimits End With Settings.Users.Add(tmpUser) With Settings.Users.Last - .Labels.Add(UserData.CannelsLabelName_ChannelsForm) + .Labels.ListAddList({UserData.CannelsLabelName_ChannelsForm, LabelsKeeper.NoParsedUser}) .UpdateUserInformation() If Settings.FromChannelCopyImageToUser And Not f.IsEmptyString And Not .File.IsEmptyString Then _ CopyFile(ListAddValue(Nothing, New ChannelsCollection.ChannelImage(cn, f)).ListAddList(Settings.Channels.GetUserFiles(.Name), c), .File) diff --git a/SCrawler/Content/Pictures/BrushToolPic_16.png b/SCrawler/Content/Pictures/BrushToolPic_16.png new file mode 100644 index 0000000..301dba0 Binary files /dev/null and b/SCrawler/Content/Pictures/BrushToolPic_16.png differ diff --git a/SCrawler/Content/Pictures/SitePictures/TwitterPic_400.png b/SCrawler/Content/Pictures/SitePictures/TwitterPic_400.png deleted file mode 100644 index 24efa51..0000000 Binary files a/SCrawler/Content/Pictures/SitePictures/TwitterPic_400.png and /dev/null differ diff --git a/SCrawler/Download/Automation/AutoDownloader.vb b/SCrawler/Download/Automation/AutoDownloader.vb index a0cd6b8..9630b0a 100644 --- a/SCrawler/Download/Automation/AutoDownloader.vb +++ b/SCrawler/Download/Automation/AutoDownloader.vb @@ -80,7 +80,7 @@ Namespace DownloadObjects If Not AutoDownloaderSource Is Nothing And Settings.ProcessNotification(SettingsCLS.NotificationObjects.AutoDownloader) Then If AutoDownloaderSource.ShowNotifications Then If Not User Is Nothing Then - Dim Text$ = $"{User.Site} - {User.Name}{vbNewLine}" & + Dim Text$ = $"{IIf(User.IsSubscription, "[Subscription] ", String.Empty)}{User.Site} - {User.Name}{vbNewLine}" & $"Downloaded: {User.DownloadedPictures(False)} images, {User.DownloadedVideos(False)} videos" Dim Title$ If Not User.CollectionName.IsEmptyString Then @@ -96,7 +96,7 @@ Namespace DownloadObjects Dim uifKey$ = String.Empty If AutoDownloaderSource.ShowPictureUser Then uPic = DirectCast(User, UserDataBase).GetUserPictureToastAddress If AutoDownloaderSource.ShowPictureUser AndAlso uPic.Exists Then Notify.Images = {New ToastImage(uPic)} - If AutoDownloaderSource.ShowPictureDownloaded And User.DownloadedPictures(False) > 0 Then + If AutoDownloaderSource.ShowPictureDownloaded And User.DownloadedPictures(False) > 0 And Not User.IsSubscription Then uif = DirectCast(User, UserDataBase).GetLastImageAddress uif_orig = uif If uif.Exists Then @@ -129,7 +129,7 @@ Namespace DownloadObjects ErrorsDescriber.Execute(EDP.SendToLog, ex, "[AutoDownloader.NotifiedUser.ShowNotification]") If Not User Is Nothing Then MainFrameObj.ShowNotification(SettingsCLS.NotificationObjects.AutoDownloader, - User.ToString & vbNewLine & + If(User.IsSubscription, "[Subscription] ", String.Empty) & User.ToString & vbNewLine & $"Downloaded: {User.DownloadedPictures(False)} images, {User.DownloadedVideos(False)} videos" & If(User.HasError, vbNewLine & "With errors", String.Empty)) End If @@ -142,7 +142,11 @@ Namespace DownloadObjects ElseIf Key = _Key Then Return True ElseIf KeyFolder = _Key Then - User.OpenFolder() + If User.IsSubscription Then + Return True + Else + User.OpenFolder() + End If ElseIf KeySite = _Key Then User.OpenSite() ElseIf Images.ContainsKey(_Key) Then @@ -216,6 +220,7 @@ Namespace DownloadObjects Private ReadOnly LastDownloadDateXML As Date? = Nothing Private _LastDownloadDate As Date = Now.AddYears(-1) Private _LastDownloadDateChanged As Boolean = False + Private _LastDownloadDateSkip As Date? = Nothing Friend Property LastDownloadDate As Date Get Return _LastDownloadDate @@ -227,10 +232,11 @@ Namespace DownloadObjects End Property Private ReadOnly Property NextExecutionDate As Date Get + Dim lds As Date = If(_LastDownloadDateSkip, Date.MinValue) If _PauseValue.HasValue Then - Return {LastDownloadDate.AddMinutes(Timer), _StartTime.AddMinutes(StartupDelay), _PauseValue.Value}.Max + Return {LastDownloadDate.AddMinutes(Timer), _StartTime.AddMinutes(StartupDelay), _PauseValue.Value, lds}.Max Else - Return {LastDownloadDate.AddMinutes(Timer), _StartTime.AddMinutes(StartupDelay)}.Max + Return {LastDownloadDate.AddMinutes(Timer), _StartTime.AddMinutes(StartupDelay), lds}.Max End If End Get End Property @@ -411,21 +417,33 @@ Namespace DownloadObjects Friend Sub [Stop]() If Working Then _StopRequested = True End Sub - Friend Sub Skip() + Friend Overloads Sub Skip() If LastDownloadDate.AddMinutes(Timer) <= Now Then - LastDownloadDate = Now.AddMinutes(Timer) + _LastDownloadDateSkip = Now.AddMinutes(Timer) Else - LastDownloadDate = LastDownloadDate.AddMinutes(Timer) + _LastDownloadDateSkip = LastDownloadDate.AddMinutes(Timer) End If End Sub + Friend Overloads Sub Skip(ByVal Minutes As Integer) + _LastDownloadDateSkip = If(_LastDownloadDateSkip, Now).AddMinutes(Minutes) + End Sub + Friend Overloads Sub Skip(ByVal ToDate As Date) + _LastDownloadDateSkip = ToDate + End Sub + Friend Sub SkipReset() + _LastDownloadDateSkip = Nothing + End Sub + Friend Sub ForceStart() + _ForceStartRequested = True + End Sub + Private _ForceStartRequested As Boolean = False Private _SpecialDelayUse As Boolean = False Private _SpecialDelayTime As Date? = Nothing Private Sub Checker() Try Dim _StartDownload As Boolean While (Not _StopRequested Or Downloader.Working) And Not Mode = Modes.None - If LastDownloadDate.AddMinutes(Timer) < Now And _StartTime.AddMinutes(StartupDelay) < Now And - Not IsPaused And Not _StopRequested And Not Mode = Modes.None Then + If ((NextExecutionDate < Now And Not IsPaused) Or _ForceStartRequested) And Not _StopRequested And Not Mode = Modes.None Then If Downloader.Working Then _SpecialDelayUse = True Else @@ -434,9 +452,7 @@ Namespace DownloadObjects _SpecialDelayUse = False _SpecialDelayTime = Nothing _StartDownload = False - If Settings.Automation.Count = 1 Then - _StartDownload = True - ElseIf Index = -1 Then + If Settings.Automation.Count = 1 Or _ForceStartRequested Or Index = -1 Then _StartDownload = True Else _StartDownload = NextExecutionDate.AddMilliseconds(1000 * (Index + 1)).Ticks <= Now.Ticks @@ -467,6 +483,7 @@ Namespace DownloadObjects Dim GName$ Dim i% Dim DownloadedUsersCount% = 0 + Dim DownloadedSubscriptionsCount% = 0 Dim simple As Boolean = ShowSimpleNotification And ShowNotifications Dim notify As Action = Sub() Try @@ -476,7 +493,11 @@ Namespace DownloadObjects With .Item(indx) If Keys.Contains(.Key) Then If simple Then - DownloadedUsersCount += 1 + If .IsSubscription Then + DownloadedSubscriptionsCount += 1 + Else + DownloadedUsersCount += 1 + End If Else ShowNotification(.Self) End If @@ -501,9 +522,27 @@ Namespace DownloadObjects End If End Function Dim CheckSites As Predicate(Of IUserData) = Function(u) SitesExcluded.Count = 0 OrElse Not SitesExcluded.Contains(u.Site) - users.ListAddList(Settings.GetUsers(Function(u) UserExistsPredicate(u) And CheckLabels.Invoke(u) And CheckSites.Invoke(u))) + Dim ExistsPredicate As Predicate(Of IUserData) + If Subscriptions Then + If SubscriptionsOnly Then + ExistsPredicate = UserExistsSubscriptionsPredicate + Else + ExistsPredicate = UserExistsPredicate + End If + Else + ExistsPredicate = UserExistsNonSubscriptionsPredicate + End If + users.ListAddList(Settings.GetUsers(Function(u) ExistsPredicate(u) And CheckLabels.Invoke(u) And CheckSites.Invoke(u))) + If UsersCount <> 0 And users.Count > 0 Then + users = users.ListTake(If(UsersCount > 0, -1, -2), Math.Abs(UsersCount)) + If UsersCount < 0 Then users = users.ListReverse + End If Case Modes.Default - Using g As New GroupParameters : users.ListAddList(DownloadGroup.GetUsers(g, True)) : End Using + Using g As New GroupParameters + g.LabelsExcluded.ListAddList(LabelsExcluded) + g.SitesExcluded.ListAddList(SitesExcluded) + 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 @@ -522,9 +561,13 @@ Namespace DownloadObjects While .Working Or .Count > 0 : notify.Invoke() : Thread.Sleep(200) : End While .AutoDownloaderWorking = False notify.Invoke - If simple And DownloadedUsersCount > 0 Then _ - MainFrameObj.ShowNotification(SettingsCLS.NotificationObjects.AutoDownloader, - $"{DownloadedUsersCount} user(s) downloaded with scheduler plan '{Name}'") + If simple And DownloadedUsersCount + DownloadedSubscriptionsCount > 0 Then + Dim msg$ = String.Empty + If DownloadedUsersCount > 0 Then msg = $"{DownloadedUsersCount} user(s) " + If DownloadedSubscriptionsCount > 0 Then msg &= $"{IIf(DownloadedUsersCount > 0, "and ", String.Empty)}{DownloadedSubscriptionsCount} subscription(s) " + msg &= $"downloaded with scheduler plan '{Name}'" + MainFrameObj.ShowNotification(SettingsCLS.NotificationObjects.AutoDownloader, msg) + End If End With End If Catch ex As Exception @@ -534,6 +577,8 @@ Namespace DownloadObjects LastDownloadDate = Now Update() _Downloading = False + _ForceStartRequested = False + _LastDownloadDateSkip = Nothing End Try End Sub Private Sub ShowNotification(ByVal u As IUserData) diff --git a/SCrawler/Download/Automation/AutoDownloaderEditorForm.Designer.vb b/SCrawler/Download/Automation/AutoDownloaderEditorForm.Designer.vb index 88633df..5190e4c 100644 --- a/SCrawler/Download/Automation/AutoDownloaderEditorForm.Designer.vb +++ b/SCrawler/Download/Automation/AutoDownloaderEditorForm.Designer.vb @@ -66,13 +66,13 @@ Namespace DownloadObjects 'CONTAINER_MAIN.ContentPanel ' CONTAINER_MAIN.ContentPanel.Controls.Add(Me.DEF_GROUP) - CONTAINER_MAIN.ContentPanel.Size = New System.Drawing.Size(476, 308) + CONTAINER_MAIN.ContentPanel.Size = New System.Drawing.Size(476, 363) 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, 333) + CONTAINER_MAIN.Size = New System.Drawing.Size(476, 388) CONTAINER_MAIN.TabIndex = 0 CONTAINER_MAIN.TopToolStripPanelVisible = False ' @@ -81,20 +81,22 @@ Namespace DownloadObjects Me.DEF_GROUP.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.[Single] Me.DEF_GROUP.ColumnCount = 1 Me.DEF_GROUP.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) - Me.DEF_GROUP.Controls.Add(Me.TXT_GROUPS, 0, 6) Me.DEF_GROUP.Controls.Add(TP_MODE, 0, 0) - Me.DEF_GROUP.Controls.Add(Me.TXT_TIMER, 0, 8) - Me.DEF_GROUP.Controls.Add(Me.LBL_LAST_TIME_UP, 0, 10) - Me.DEF_GROUP.Controls.Add(Me.NUM_DELAY, 0, 9) - Me.DEF_GROUP.Controls.Add(TP_NOTIFY, 0, 7) + Me.DEF_GROUP.Controls.Add(Me.TXT_GROUPS, 0, 8) + Me.DEF_GROUP.Controls.Add(TP_NOTIFY, 0, 9) + Me.DEF_GROUP.Controls.Add(Me.TXT_TIMER, 0, 10) + Me.DEF_GROUP.Controls.Add(Me.NUM_DELAY, 0, 11) + Me.DEF_GROUP.Controls.Add(Me.LBL_LAST_TIME_UP, 0, 12) 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 = 12 + Me.DEF_GROUP.RowCount = 14 Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) + Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) + Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) @@ -104,7 +106,7 @@ Namespace DownloadObjects Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20.0!)) - Me.DEF_GROUP.Size = New System.Drawing.Size(476, 308) + Me.DEF_GROUP.Size = New System.Drawing.Size(476, 363) Me.DEF_GROUP.TabIndex = 0 ' 'TXT_GROUPS @@ -118,7 +120,7 @@ Namespace DownloadObjects Me.TXT_GROUPS.CaptionText = "Groups" Me.TXT_GROUPS.CaptionWidth = 50.0R Me.TXT_GROUPS.Dock = System.Windows.Forms.DockStyle.Fill - Me.TXT_GROUPS.Location = New System.Drawing.Point(4, 169) + Me.TXT_GROUPS.Location = New System.Drawing.Point(4, 195) Me.TXT_GROUPS.Name = "TXT_GROUPS" Me.TXT_GROUPS.Size = New System.Drawing.Size(468, 22) Me.TXT_GROUPS.TabIndex = 1 @@ -222,7 +224,7 @@ Namespace DownloadObjects Me.TXT_TIMER.CaptionToolTipText = "Timer (in minutes)" Me.TXT_TIMER.CaptionWidth = 50.0R Me.TXT_TIMER.Dock = System.Windows.Forms.DockStyle.Fill - Me.TXT_TIMER.Location = New System.Drawing.Point(4, 227) + Me.TXT_TIMER.Location = New System.Drawing.Point(4, 282) Me.TXT_TIMER.Name = "TXT_TIMER" Me.TXT_TIMER.Size = New System.Drawing.Size(468, 22) Me.TXT_TIMER.TabIndex = 3 @@ -232,7 +234,7 @@ Namespace DownloadObjects Me.LBL_LAST_TIME_UP.AutoSize = True Me.LBL_LAST_TIME_UP.Dock = System.Windows.Forms.DockStyle.Fill Me.LBL_LAST_TIME_UP.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, CType(204, Byte)) - Me.LBL_LAST_TIME_UP.Location = New System.Drawing.Point(4, 282) + Me.LBL_LAST_TIME_UP.Location = New System.Drawing.Point(4, 337) Me.LBL_LAST_TIME_UP.Name = "LBL_LAST_TIME_UP" Me.LBL_LAST_TIME_UP.Size = New System.Drawing.Size(468, 25) Me.LBL_LAST_TIME_UP.TabIndex = 5 @@ -251,7 +253,7 @@ Namespace DownloadObjects Me.NUM_DELAY.ClearTextByButtonClear = False Me.NUM_DELAY.ControlMode = PersonalUtilities.Forms.Controls.TextBoxExtended.ControlModes.NumericUpDown Me.NUM_DELAY.Dock = System.Windows.Forms.DockStyle.Fill - Me.NUM_DELAY.Location = New System.Drawing.Point(4, 256) + Me.NUM_DELAY.Location = New System.Drawing.Point(4, 311) Me.NUM_DELAY.Name = "NUM_DELAY" Me.NUM_DELAY.NumberMaximum = New Decimal(New Integer() {1440, 0, 0, 0}) Me.NUM_DELAY.NumberUpDownAlign = System.Windows.Forms.LeftRightAlignment.Left @@ -271,7 +273,7 @@ Namespace DownloadObjects TP_NOTIFY.Controls.Add(Me.CH_SHOW_PIC_USER, 3, 0) TP_NOTIFY.Controls.Add(Me.CH_NOTIFY_SIMPLE, 1, 0) TP_NOTIFY.Dock = System.Windows.Forms.DockStyle.Fill - TP_NOTIFY.Location = New System.Drawing.Point(1, 195) + TP_NOTIFY.Location = New System.Drawing.Point(1, 250) TP_NOTIFY.Margin = New System.Windows.Forms.Padding(0) TP_NOTIFY.Name = "TP_NOTIFY" TP_NOTIFY.RowCount = 1 @@ -331,15 +333,15 @@ Namespace DownloadObjects ' Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!) Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font - Me.ClientSize = New System.Drawing.Size(476, 333) + Me.ClientSize = New System.Drawing.Size(476, 388) Me.Controls.Add(CONTAINER_MAIN) Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle Me.Icon = Global.SCrawler.My.Resources.Resources.ArrowDownIcon_Blue_24 Me.KeyPreview = True Me.MaximizeBox = False - Me.MaximumSize = New System.Drawing.Size(492, 372) + Me.MaximumSize = New System.Drawing.Size(492, 427) Me.MinimizeBox = False - Me.MinimumSize = New System.Drawing.Size(492, 372) + Me.MinimumSize = New System.Drawing.Size(492, 427) Me.Name = "AutoDownloaderEditorForm" Me.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide Me.Text = "AutoDownloader settings" diff --git a/SCrawler/Download/Automation/AutoDownloaderEditorForm.vb b/SCrawler/Download/Automation/AutoDownloaderEditorForm.vb index d5c771c..be19b9f 100644 --- a/SCrawler/Download/Automation/AutoDownloaderEditorForm.vb +++ b/SCrawler/Download/Automation/AutoDownloaderEditorForm.vb @@ -114,7 +114,7 @@ Namespace DownloadObjects Private Sub ChangeEnabled() Handles OPT_DISABLED.CheckedChanged, OPT_ALL.CheckedChanged, OPT_DEFAULT.CheckedChanged, OPT_SPEC.CheckedChanged, OPT_GROUP.CheckedChanged, CH_NOTIFY.CheckedChanged, CH_NOTIFY_SIMPLE.CheckedChanged - DEF_GROUP.Enabled(OPT_ALL.Checked Or OPT_DEFAULT.Checked Or OPT_SPEC.Checked) = OPT_SPEC.Checked + DEF_GROUP.Enabled(OPT_ALL.Checked Or OPT_DEFAULT.Checked Or OPT_SPEC.Checked, OPT_ALL.Checked) = OPT_SPEC.Checked TXT_GROUPS.Enabled = OPT_GROUP.Checked TXT_TIMER.Enabled = Not OPT_DISABLED.Checked NUM_DELAY.Enabled = Not OPT_DISABLED.Checked diff --git a/SCrawler/Download/Automation/SchedulerEditorForm.vb b/SCrawler/Download/Automation/SchedulerEditorForm.vb index 485c58e..55d79f4 100644 --- a/SCrawler/Download/Automation/SchedulerEditorForm.vb +++ b/SCrawler/Download/Automation/SchedulerEditorForm.vb @@ -12,8 +12,13 @@ Namespace DownloadObjects Friend Class SchedulerEditorForm #Region "Declarations" Private WithEvents MyDefs As DefaultFormOptions - Private WithEvents BTT_SKIP As ToolStripButton + Private ReadOnly MENU_SKIP As ToolStripDropDownButton + Private WithEvents BTT_SKIP As ToolStripMenuItem + Private WithEvents BTT_SKIP_MIN As ToolStripMenuItem + Private WithEvents BTT_SKIP_DATE As ToolStripMenuItem + Private WithEvents BTT_SKIP_RESET As ToolStripMenuItem Private WithEvents BTT_START As ToolStripButton + Private WithEvents BTT_START_FORCE As ToolStripButton Private WithEvents BTT_PAUSE As ToolStripDropDownButton Private WithEvents PauseArr As AutoDownloaderPauseButtons #End Region @@ -21,18 +26,52 @@ Namespace DownloadObjects Friend Sub New() InitializeComponent() MyDefs = New DefaultFormOptions(Me, Settings.Design) - BTT_SKIP = New ToolStripButton With { + MENU_SKIP = New ToolStripDropDownButton With { .Text = "Skip", - .ToolTipText = "Skip next run", + .ToolTipText = String.Empty, + .AutoToolTip = False, + .DisplayStyle = ToolStripItemDisplayStyle.Text + } + BTT_SKIP = New ToolStripMenuItem With { + .Text = "Skip", + .ToolTipText = "Delay for the number of minutes configured in the task", .AutoToolTip = True, .DisplayStyle = ToolStripItemDisplayStyle.Text } + BTT_SKIP_MIN = New ToolStripMenuItem With { + .Text = "Delay for minutes", + .ToolTipText = "Delay for a specific number of minutes", + .AutoToolTip = True, + .DisplayStyle = ToolStripItemDisplayStyle.Text, + .Tag = "m" + } + BTT_SKIP_DATE = New ToolStripMenuItem With { + .Text = "Delay by date/time", + .ToolTipText = String.Empty, + .AutoToolTip = False, + .DisplayStyle = ToolStripItemDisplayStyle.Text, + .Tag = "d" + } + BTT_SKIP_RESET = New ToolStripMenuItem With { + .Text = "Delay reset", + .ToolTipText = "Reset the delay you set earlier", + .AutoToolTip = True, + .DisplayStyle = ToolStripItemDisplayStyle.Text, + .Tag = "r" + } + MENU_SKIP.DropDownItems.AddRange({BTT_SKIP, BTT_SKIP_MIN, BTT_SKIP_DATE, New ToolStripSeparator, BTT_SKIP_RESET}) BTT_START = New ToolStripButton With { .Text = "Start", .Image = My.Resources.StartPic_Green_16, .ToolTipText = "Run selected plan", .AutoToolTip = True } + BTT_START_FORCE = New ToolStripButton With { + .Text = "Start (force)", + .ToolTipText = "Force start of the current task", + .AutoToolTip = True, + .Image = My.Resources.StartPic_Green_16 + } BTT_PAUSE = New ToolStripDropDownButton With { .Text = "Pause", .Image = My.Resources.Pause_Blue_16, @@ -47,7 +86,7 @@ Namespace DownloadObjects Private Sub SchedulerEditorForm_Load(sender As Object, e As EventArgs) Handles Me.Load With MyDefs .MyViewInitialize() - .AddEditToolbarPlus({BTT_START, BTT_SKIP, BTT_PAUSE}) + .AddEditToolbarPlus({New ToolStripSeparator, BTT_START, BTT_START_FORCE, MENU_SKIP, BTT_PAUSE}) PauseArr.AddButtons(BTT_PAUSE, .MyEditToolbar.ToolStrip) Refill() .EndLoaderOperations(False) @@ -138,10 +177,38 @@ Namespace DownloadObjects Refill() End If End Sub - Private Sub BTT_SKIP_Click(sender As Object, e As EventArgs) Handles BTT_SKIP.Click + Private Sub BTT_START_FORCE_Click(sender As Object, e As EventArgs) Handles BTT_START_FORCE.Click If _LatestSelected.ValueBetween(0, LIST_PLANS.Items.Count - 1) Then - Settings.Automation(_LatestSelected).Skip() - Refill() + With Settings.Automation(_LatestSelected) + If .Working Then .ForceStart() : Refill() + End With + End If + End Sub + Private Sub BTT_SKIP_Click(ByVal Sender As ToolStripMenuItem, ByVal e As EventArgs) Handles BTT_SKIP.Click, BTT_SKIP_MIN.Click, BTT_SKIP_DATE.Click, BTT_SKIP_RESET.Click + If _LatestSelected.ValueBetween(0, LIST_PLANS.Items.Count - 1) Then + Dim mode$ = AConvert(Of String)(Sender.Tag, String.Empty) + Select Case mode + Case String.Empty + Settings.Automation(_LatestSelected).Skip() + Refill() + Case "m" + Dim mins% = AConvert(Of Integer)(InputBoxE("Enter a number of minutes you want to delay:", Sender.Text, 60), -1) + If mins > 0 Then Settings.Automation(_LatestSelected).Skip(mins) : Refill() + Case "d" + Dim d As Date? = Nothing + Using f As New DateTimeSelectionForm(DateTimeSelectionForm.Modes.Date + + DateTimeSelectionForm.Modes.Time + + DateTimeSelectionForm.Modes.Start, Settings.Design) With { + .MyDateStart = Now.AddMinutes(60) + } + f.ShowDialog() + If f.DialogResult = DialogResult.OK Then d = f.MyDateStart + End Using + If d.HasValue Then Settings.Automation(_LatestSelected).Skip(d.Value) : Refill() + Case "r" + Settings.Automation(_LatestSelected).SkipReset() + Refill() + End Select End If End Sub Private Sub PauseArr_Updating() Handles PauseArr.Updating diff --git a/SCrawler/Download/DownloadedInfoForm.Designer.vb b/SCrawler/Download/DownloadedInfoForm.Designer.vb index cbbeaf2..0fba052 100644 --- a/SCrawler/Download/DownloadedInfoForm.Designer.vb +++ b/SCrawler/Download/DownloadedInfoForm.Designer.vb @@ -24,11 +24,14 @@ Namespace DownloadObjects Private Sub InitializeComponent() Dim SEP_1 As System.Windows.Forms.ToolStripSeparator Dim SEP_2 As System.Windows.Forms.ToolStripSeparator + Dim MENU_VIEW_SEP_1 As System.Windows.Forms.ToolStripSeparator Dim resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(DownloadedInfoForm)) Me.ToolbarTOP = New System.Windows.Forms.ToolStrip() Me.MENU_VIEW = New System.Windows.Forms.ToolStripDropDownButton() Me.MENU_VIEW_SESSION = New System.Windows.Forms.ToolStripMenuItem() Me.MENU_VIEW_ALL = New System.Windows.Forms.ToolStripMenuItem() + Me.OPT_DEFAULT = New System.Windows.Forms.ToolStripMenuItem() + Me.OPT_SUBSCRIPTIONS = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_REFRESH = New System.Windows.Forms.ToolStripButton() Me.BTT_UP = New System.Windows.Forms.ToolStripButton() Me.BTT_DOWN = New System.Windows.Forms.ToolStripButton() @@ -38,6 +41,7 @@ Namespace DownloadObjects Me.LIST_DOWN = New System.Windows.Forms.ListBox() SEP_1 = New System.Windows.Forms.ToolStripSeparator() SEP_2 = New System.Windows.Forms.ToolStripSeparator() + MENU_VIEW_SEP_1 = New System.Windows.Forms.ToolStripSeparator() Me.ToolbarTOP.SuspendLayout() Me.SuspendLayout() ' @@ -51,6 +55,11 @@ Namespace DownloadObjects SEP_2.Name = "SEP_2" SEP_2.Size = New System.Drawing.Size(6, 25) ' + 'MENU_VIEW_SEP_1 + ' + MENU_VIEW_SEP_1.Name = "MENU_VIEW_SEP_1" + MENU_VIEW_SEP_1.Size = New System.Drawing.Size(211, 6) + ' 'ToolbarTOP ' Me.ToolbarTOP.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden @@ -63,18 +72,18 @@ Namespace DownloadObjects 'MENU_VIEW ' Me.MENU_VIEW.AutoToolTip = False - Me.MENU_VIEW.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text - Me.MENU_VIEW.DropDownItems.AddRange(New System.Windows.Forms.ToolStripItem() {Me.MENU_VIEW_SESSION, Me.MENU_VIEW_ALL}) + Me.MENU_VIEW.DropDownItems.AddRange(New System.Windows.Forms.ToolStripItem() {Me.MENU_VIEW_SESSION, Me.MENU_VIEW_ALL, MENU_VIEW_SEP_1, Me.OPT_DEFAULT, Me.OPT_SUBSCRIPTIONS}) + Me.MENU_VIEW.Image = CType(resources.GetObject("MENU_VIEW.Image"), System.Drawing.Image) Me.MENU_VIEW.ImageTransparentColor = System.Drawing.Color.Magenta Me.MENU_VIEW.Name = "MENU_VIEW" - Me.MENU_VIEW.Size = New System.Drawing.Size(45, 22) + Me.MENU_VIEW.Size = New System.Drawing.Size(61, 22) Me.MENU_VIEW.Text = "View" ' 'MENU_VIEW_SESSION ' Me.MENU_VIEW_SESSION.AutoToolTip = True Me.MENU_VIEW_SESSION.Name = "MENU_VIEW_SESSION" - Me.MENU_VIEW_SESSION.Size = New System.Drawing.Size(180, 22) + Me.MENU_VIEW_SESSION.Size = New System.Drawing.Size(214, 22) Me.MENU_VIEW_SESSION.Text = "Session" Me.MENU_VIEW_SESSION.ToolTipText = "Show downloaded users by this session" ' @@ -82,10 +91,26 @@ Namespace DownloadObjects ' Me.MENU_VIEW_ALL.AutoToolTip = True Me.MENU_VIEW_ALL.Name = "MENU_VIEW_ALL" - Me.MENU_VIEW_ALL.Size = New System.Drawing.Size(180, 22) + Me.MENU_VIEW_ALL.Size = New System.Drawing.Size(214, 22) Me.MENU_VIEW_ALL.Text = "All" Me.MENU_VIEW_ALL.ToolTipText = "Show all users (sorted by latest download)" ' + 'OPT_DEFAULT + ' + Me.OPT_DEFAULT.AutoToolTip = True + Me.OPT_DEFAULT.Name = "OPT_DEFAULT" + Me.OPT_DEFAULT.Size = New System.Drawing.Size(214, 22) + Me.OPT_DEFAULT.Text = "Downloaded users" + Me.OPT_DEFAULT.ToolTipText = "Show downloaded users" + ' + 'OPT_SUBSCRIPTIONS + ' + Me.OPT_SUBSCRIPTIONS.AutoToolTip = True + Me.OPT_SUBSCRIPTIONS.Name = "OPT_SUBSCRIPTIONS" + Me.OPT_SUBSCRIPTIONS.Size = New System.Drawing.Size(214, 22) + Me.OPT_SUBSCRIPTIONS.Text = "Downloaded subscriptions" + Me.OPT_SUBSCRIPTIONS.ToolTipText = "Show downloaded subscriptions" + ' 'BTT_REFRESH ' Me.BTT_REFRESH.Image = Global.SCrawler.My.Resources.Resources.RefreshPic_24 @@ -179,5 +204,7 @@ Namespace DownloadObjects Private WithEvents BTT_FIND As ToolStripButton Private WithEvents BTT_UP As ToolStripButton Private WithEvents BTT_DOWN As ToolStripButton + Private WithEvents OPT_DEFAULT As ToolStripMenuItem + Private WithEvents OPT_SUBSCRIPTIONS As ToolStripMenuItem End Class End Namespace \ No newline at end of file diff --git a/SCrawler/Download/DownloadedInfoForm.resx b/SCrawler/Download/DownloadedInfoForm.resx index 51b1704..ed9625a 100644 --- a/SCrawler/Download/DownloadedInfoForm.resx +++ b/SCrawler/Download/DownloadedInfoForm.resx @@ -123,10 +123,21 @@ False + + False + 17, 17 + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABkSURBVDhPY6AKyO86WFDQfeg/iIYKkQZAmkNbnvyXta76 + DxViYGFi+Y8PQ5VBAMhmkGYgJs8FAw9GA5EKILFiWUFixfL/IBoqRBoAafYsOvpf0jiTvEAE2QzSLGmU + MeQCkYEBAD3tUdo+/cEPAAAAAElFTkSuQmCC + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 diff --git a/SCrawler/Download/DownloadedInfoForm.vb b/SCrawler/Download/DownloadedInfoForm.vb index 5423f04..8442ced 100644 --- a/SCrawler/Download/DownloadedInfoForm.vb +++ b/SCrawler/Download/DownloadedInfoForm.vb @@ -16,7 +16,6 @@ Namespace DownloadObjects #End Region #Region "Declarations" Private MyView As FormView - Private ReadOnly LParams As New ListAddParams(LAP.IgnoreICopier) With {.Error = EDP.None} Private Opened As Boolean = False Friend ReadOnly Property ReadyToOpen As Boolean Get @@ -35,12 +34,29 @@ Namespace DownloadObjects Settings.InfoViewMode.Value = CInt(SMode) End Set End Property - Private ReadOnly _TempUsersList As List(Of IUserData) + Private ReadOnly _UsersListSession As List(Of IUserData) + Private ReadOnly _UsersListAll As List(Of IUserData) + Private ReadOnly Property Current As List(Of IUserData) + Get + Return If(ViewMode = ViewModes.All, _UsersListAll, _UsersListSession) + End Get + End Property + Private Overloads ReadOnly Property SelectedUser As IUserData + Get + If ViewMode = ViewModes.All Then + If _LatestSelected.ValueBetween(0, _UsersListAll.Count - 1) Then Return _UsersListAll(_LatestSelected) + Else + If _LatestSelected.ValueBetween(0, _UsersListSession.Count - 1) Then Return _UsersListSession(_LatestSelected) + End If + Return Nothing + End Get + End Property #End Region #Region "Initializer" Public Sub New() InitializeComponent() - _TempUsersList = New List(Of IUserData) + _UsersListSession = New List(Of IUserData) + _UsersListAll = New List(Of IUserData) If Settings.InfoViewMode.Value = CInt(ViewModes.All) Then MENU_VIEW_SESSION.Checked = False MENU_VIEW_ALL.Checked = True @@ -48,6 +64,8 @@ Namespace DownloadObjects MENU_VIEW_SESSION.Checked = True MENU_VIEW_ALL.Checked = False End If + OPT_DEFAULT.Checked = Settings.InfoViewDefault + OPT_SUBSCRIPTIONS.Checked = Not Settings.InfoViewDefault Settings.InfoViewMode.Value = ViewMode RefillList() End Sub @@ -56,8 +74,8 @@ Namespace DownloadObjects Private Sub DownloadedInfoForm_Load(sender As Object, e As EventArgs) Handles Me.Load Try If MyView Is Nothing Then - MyView = New FormView(Me) - MyView.Import(Settings.Design) + MyView = New FormView(Me, Settings.Design) + MyView.Import() MyView.SetFormSize() End If BTT_CLEAR.Visible = ViewMode = ViewModes.Session @@ -72,8 +90,9 @@ Namespace DownloadObjects Hide() End Sub Private Sub DownloadedInfoForm_Disposed(sender As Object, e As EventArgs) Handles Me.Disposed - If Not MyView Is Nothing Then MyView.Dispose(Settings.Design) - _TempUsersList.Clear() + MyView.DisposeIfReady() + _UsersListSession.Clear() + _UsersListAll.Clear() End Sub Private Sub DownloadedInfoForm_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown Dim b As Boolean = True @@ -98,25 +117,36 @@ Namespace DownloadObjects End Class Private Sub RefillList() Handles BTT_REFRESH.Click Try - _TempUsersList.Clear() Dim lClear As Action = Sub() LIST_DOWN.Items.Clear() If LIST_DOWN.InvokeRequired Then LIST_DOWN.Invoke(lClear) Else lClear.Invoke If ViewMode = ViewModes.Session Then - _TempUsersList.ListAddList(Downloader.Downloaded, LParams) + With Downloader.Downloaded + If .Count > 0 Then + With .Select(Function(u) Settings.GetUser(u, False)).Reverse + If _UsersListSession.Count > 0 Then _UsersListSession.ListWithRemove(.Self) + If _UsersListSession.Count > 0 Then + _UsersListSession.InsertRange(0, .Self) + Else + _UsersListSession.AddRange(.Self) + End If + End With + End If + End With Else - _TempUsersList.ListAddList(Settings.Users.SelectMany(Of IUserData) _ - (Function(u) If(u.IsCollection, DirectCast(u, API.UserDataBind).Collections, {u})), LParams) + _UsersListAll.ListAddList(Settings.GetUsers(Function(u) True), LAP.ClearBeforeAdd) + If _UsersListAll.Count > 0 Then _UsersListAll.Sort(New UsersDateOrder) End If - If _TempUsersList.Count > 0 Then - _TempUsersList.Sort(New UsersDateOrder) - For Each user As IUserData In _TempUsersList + Dim isDefault As Boolean = OPT_DEFAULT.Checked + If Current.Count > 0 Then Current.RemoveAll(Function(u) u.IsSubscription = isDefault) + If Current.Count > 0 Then + For Each user As IUserData In Current If LIST_DOWN.InvokeRequired Then LIST_DOWN.Invoke(Sub() LIST_DOWN.Items.Add(user.DownloadedInformation)) Else LIST_DOWN.Items.Add(user.DownloadedInformation) End If Next - If _LatestSelected >= 0 AndAlso _LatestSelected <= LIST_DOWN.Items.Count - 1 Then + If _LatestSelected.ValueBetween(0, LIST_DOWN.Items.Count - 1) Then Dim aSel As Action = Sub() LIST_DOWN.SelectedIndex = _LatestSelected If LIST_DOWN.InvokeRequired Then LIST_DOWN.Invoke(aSel) Else aSel.Invoke Else @@ -125,6 +155,7 @@ Namespace DownloadObjects Else _LatestSelected = -1 End If + Catch ies As InvalidOperationException Catch ex As Exception ErrorsDescriber.Execute(EDP.SendToLog, ex, "[DownloadedInfoForm.RefillList]") Finally @@ -133,34 +164,40 @@ Namespace DownloadObjects End Sub #End Region #Region "Toolbar controls" - Private Sub MENU_VIEW_SESSION_Click(sender As Object, e As EventArgs) Handles MENU_VIEW_SESSION.Click - MENU_VIEW_SESSION.Checked = True - MENU_VIEW_ALL.Checked = False - ViewMode = ViewModes.Session - BTT_CLEAR.Visible = True - RefillList() + Private Sub MENU_VIEW_Click(ByVal Sender As ToolStripMenuItem, ByVal e As EventArgs) Handles MENU_VIEW_SESSION.Click, MENU_VIEW_ALL.Click + Dim __refill As Boolean = False + Dim clicked As ToolStripMenuItem = Sender + Dim other As ToolStripMenuItem = If(Sender Is MENU_VIEW_SESSION, MENU_VIEW_ALL, MENU_VIEW_SESSION) + If other.Checked Then + clicked.Checked = True + other.Checked = False + __refill = True + Else + clicked.Checked = False + End If + ViewMode = IIf(MENU_VIEW_SESSION.Checked, ViewModes.Session, ViewModes.All) + ControlInvokeFast(ToolbarTOP, BTT_CLEAR, Sub() BTT_CLEAR.Visible = ViewMode = ViewModes.Session) + If __refill Then RefillList() End Sub - Private Sub MENU_VIEW_ALL_Click(sender As Object, e As EventArgs) Handles MENU_VIEW_ALL.Click - MENU_VIEW_SESSION.Checked = False - MENU_VIEW_ALL.Checked = True - ViewMode = ViewModes.All - BTT_CLEAR.Visible = False - RefillList() + Private Sub OPT_Click(ByVal Sender As ToolStripMenuItem, ByVal e As EventArgs) Handles OPT_DEFAULT.Click, OPT_SUBSCRIPTIONS.Click + Dim __refill As Boolean = False + Dim clicked As ToolStripMenuItem = Sender + Dim other As ToolStripMenuItem = If(Sender Is OPT_DEFAULT, OPT_SUBSCRIPTIONS, OPT_DEFAULT) + If other.Checked Then + clicked.Checked = True + other.Checked = False + __refill = True + Else + clicked.Checked = False + End If + Settings.InfoViewDefault.Value = OPT_DEFAULT.Checked + If __refill Then RefillList() End Sub Private Sub BTT_FIND_Click(sender As Object, e As EventArgs) Handles BTT_FIND.Click - Try - If _LatestSelected.ValueBetween(0, LIST_DOWN.Items.Count - 1) AndAlso _LatestSelected.ValueBetween(0, Downloader.Downloaded.Count - 1) Then - Dim u As IUserData = Settings.GetUser(_TempUsersList(_LatestSelected), True) - If Not u Is Nothing Then RaiseEvent UserFind(u.Key) - End If - Catch ex As Exception - End Try + Try : RaiseEvent UserFind(If(Settings.GetUser(SelectedUser, True)?.Key, String.Empty)) : Catch : End Try End Sub Private Sub BTT_CLEAR_Click(sender As Object, e As EventArgs) Handles BTT_CLEAR.Click - If LIST_DOWN.Items.Count > 0 Then - Downloader.Downloaded.Clear() - RefillList() - End If + If LIST_DOWN.Items.Count > 0 Then Downloader.Downloaded.Clear() : RefillList() End Sub #End Region #Region "List handlers" @@ -171,8 +208,8 @@ Namespace DownloadObjects End Sub Private Sub LIST_DOWN_MouseDoubleClick(sender As Object, e As MouseEventArgs) Handles LIST_DOWN.MouseDoubleClick Try - If _LatestSelected.ValueBetween(0, _TempUsersList.Count - 1) AndAlso - Not DirectCast(_TempUsersList(_LatestSelected), UserDataBase).Disposed Then _TempUsersList(_LatestSelected).OpenFolder() + Dim u As IUserData = SelectedUser + If Not If(u?.Disposed, True) Then u.OpenFolder() Catch End Try End Sub @@ -191,17 +228,12 @@ Namespace DownloadObjects u = _LatestSelected > 0 d = _LatestSelected < .Items.Count - 1 End If - Dim a As Action = Sub() - BTT_UP.Enabled = u - BTT_DOWN.Enabled = d - End Sub - If ToolbarTOP.InvokeRequired Then ToolbarTOP.Invoke(a) Else a.Invoke - a = Nothing - If Offset.HasValue AndAlso .Items.Count > 0 AndAlso - (_LatestSelected + Offset.Value).ValueBetween(0, .Items.Count - 1) Then a = Sub() .SelectedIndex = _LatestSelected + Offset.Value - If Not a Is Nothing Then - If LIST_DOWN.InvokeRequired Then LIST_DOWN.Invoke(a) Else a.Invoke - End If + ControlInvokeFast(ToolbarTOP, BTT_UP, Sub() + BTT_UP.Enabled = u + BTT_DOWN.Enabled = d + End Sub, EDP.None) + If Offset.HasValue AndAlso .Items.Count > 0 AndAlso (_LatestSelected + Offset.Value).ValueBetween(0, .Items.Count - 1) Then _ + ControlInvokeFast(LIST_DOWN, Sub() .SelectedIndex = _LatestSelected + Offset.Value, EDP.None) End With End Sub #End Region diff --git a/SCrawler/Download/Feed/DownloadFeedForm.Designer.vb b/SCrawler/Download/Feed/DownloadFeedForm.Designer.vb index 2ae226f..053b185 100644 --- a/SCrawler/Download/Feed/DownloadFeedForm.Designer.vb +++ b/SCrawler/Download/Feed/DownloadFeedForm.Designer.vb @@ -23,15 +23,25 @@ Namespace DownloadObjects Private Sub InitializeComponent() Dim SEP_1 As System.Windows.Forms.ToolStripSeparator + Dim SEP_2 As System.Windows.Forms.ToolStripSeparator + Dim MENU_VIEW As System.Windows.Forms.ToolStripDropDownButton + Dim resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(DownloadFeedForm)) + Me.OPT_DEFAULT = New System.Windows.Forms.ToolStripMenuItem() + Me.OPT_SUBSCRIPTIONS = New System.Windows.Forms.ToolStripMenuItem() Me.ToolbarTOP = New System.Windows.Forms.ToolStrip() Me.MENU_LOAD_SESSION = New System.Windows.Forms.ToolStripDropDownButton() Me.BTT_LOAD_SESSION_LAST = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_LOAD_SESSION_CHOOSE = New System.Windows.Forms.ToolStripMenuItem() Me.SEP_0 = New System.Windows.Forms.ToolStripSeparator() + Me.MENU_DOWN = New System.Windows.Forms.ToolStripDropDownButton() + Me.BTT_DOWN_ALL = New System.Windows.Forms.ToolStripMenuItem() + Me.BTT_DOWN_SELECTED = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_REFRESH = New System.Windows.Forms.ToolStripButton() Me.BTT_CLEAR = New System.Windows.Forms.ToolStripButton() Me.TP_DATA = New System.Windows.Forms.TableLayoutPanel() SEP_1 = New System.Windows.Forms.ToolStripSeparator() + SEP_2 = New System.Windows.Forms.ToolStripSeparator() + MENU_VIEW = New System.Windows.Forms.ToolStripDropDownButton() Me.ToolbarTOP.SuspendLayout() Me.SuspendLayout() ' @@ -40,10 +50,37 @@ Namespace DownloadObjects SEP_1.Name = "SEP_1" SEP_1.Size = New System.Drawing.Size(6, 25) ' + 'SEP_2 + ' + SEP_2.Name = "SEP_2" + SEP_2.Size = New System.Drawing.Size(6, 25) + ' + 'MENU_VIEW + ' + MENU_VIEW.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image + MENU_VIEW.DropDownItems.AddRange(New System.Windows.Forms.ToolStripItem() {Me.OPT_DEFAULT, Me.OPT_SUBSCRIPTIONS}) + MENU_VIEW.Image = CType(resources.GetObject("MENU_VIEW.Image"), System.Drawing.Image) + MENU_VIEW.ImageTransparentColor = System.Drawing.Color.Magenta + MENU_VIEW.Name = "MENU_VIEW" + MENU_VIEW.Size = New System.Drawing.Size(29, 22) + MENU_VIEW.Text = "View" + ' + 'OPT_DEFAULT + ' + Me.OPT_DEFAULT.Name = "OPT_DEFAULT" + Me.OPT_DEFAULT.Size = New System.Drawing.Size(145, 22) + Me.OPT_DEFAULT.Text = "Downloads" + ' + 'OPT_SUBSCRIPTIONS + ' + Me.OPT_SUBSCRIPTIONS.Name = "OPT_SUBSCRIPTIONS" + Me.OPT_SUBSCRIPTIONS.Size = New System.Drawing.Size(145, 22) + Me.OPT_SUBSCRIPTIONS.Text = "Subscriptions" + ' 'ToolbarTOP ' Me.ToolbarTOP.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden - Me.ToolbarTOP.Items.AddRange(New System.Windows.Forms.ToolStripItem() {Me.MENU_LOAD_SESSION, Me.SEP_0, Me.BTT_REFRESH, Me.BTT_CLEAR, SEP_1}) + Me.ToolbarTOP.Items.AddRange(New System.Windows.Forms.ToolStripItem() {Me.MENU_LOAD_SESSION, Me.SEP_0, MENU_VIEW, SEP_1, Me.MENU_DOWN, Me.BTT_REFRESH, Me.BTT_CLEAR, SEP_2}) Me.ToolbarTOP.Location = New System.Drawing.Point(0, 0) Me.ToolbarTOP.Name = "ToolbarTOP" Me.ToolbarTOP.Size = New System.Drawing.Size(484, 25) @@ -78,6 +115,33 @@ Namespace DownloadObjects Me.SEP_0.Name = "SEP_0" Me.SEP_0.Size = New System.Drawing.Size(6, 25) ' + 'MENU_DOWN + ' + Me.MENU_DOWN.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image + Me.MENU_DOWN.DropDownItems.AddRange(New System.Windows.Forms.ToolStripItem() {Me.BTT_DOWN_ALL, Me.BTT_DOWN_SELECTED}) + Me.MENU_DOWN.Image = Global.SCrawler.My.Resources.Resources.StartPic_Green_16 + Me.MENU_DOWN.ImageTransparentColor = System.Drawing.Color.Magenta + Me.MENU_DOWN.Name = "MENU_DOWN" + Me.MENU_DOWN.Size = New System.Drawing.Size(29, 22) + Me.MENU_DOWN.Text = "Download" + Me.MENU_DOWN.Visible = False + ' + 'BTT_DOWN_ALL + ' + Me.BTT_DOWN_ALL.Image = Global.SCrawler.My.Resources.Resources.StartPic_Green_16 + Me.BTT_DOWN_ALL.Name = "BTT_DOWN_ALL" + Me.BTT_DOWN_ALL.Size = New System.Drawing.Size(180, 22) + Me.BTT_DOWN_ALL.Tag = "a" + Me.BTT_DOWN_ALL.Text = "Download ALL" + ' + 'BTT_DOWN_SELECTED + ' + Me.BTT_DOWN_SELECTED.Image = Global.SCrawler.My.Resources.Resources.StartPic_Green_16 + Me.BTT_DOWN_SELECTED.Name = "BTT_DOWN_SELECTED" + Me.BTT_DOWN_SELECTED.Size = New System.Drawing.Size(180, 22) + Me.BTT_DOWN_SELECTED.Tag = "s" + Me.BTT_DOWN_SELECTED.Text = "Download selected" + ' 'BTT_REFRESH ' Me.BTT_REFRESH.Image = Global.SCrawler.My.Resources.Resources.RefreshPic_24 @@ -141,14 +205,18 @@ Namespace DownloadObjects Me.PerformLayout() End Sub - - Private WithEvents ToolbarTOP As ToolStrip - Private WithEvents TP_DATA As TableLayoutPanel Private WithEvents BTT_REFRESH As ToolStripButton Private WithEvents BTT_CLEAR As ToolStripButton Private WithEvents MENU_LOAD_SESSION As ToolStripDropDownButton Private WithEvents BTT_LOAD_SESSION_LAST As ToolStripMenuItem Private WithEvents BTT_LOAD_SESSION_CHOOSE As ToolStripMenuItem Private WithEvents SEP_0 As ToolStripSeparator + Private WithEvents ToolbarTOP As ToolStrip + Private WithEvents TP_DATA As TableLayoutPanel + Private WithEvents OPT_DEFAULT As ToolStripMenuItem + Private WithEvents OPT_SUBSCRIPTIONS As ToolStripMenuItem + Private WithEvents MENU_DOWN As ToolStripDropDownButton + Private WithEvents BTT_DOWN_ALL As ToolStripMenuItem + Private WithEvents BTT_DOWN_SELECTED As ToolStripMenuItem End Class End Namespace \ No newline at end of file diff --git a/SCrawler/Download/Feed/DownloadFeedForm.resx b/SCrawler/Download/Feed/DownloadFeedForm.resx index d0947eb..913b26b 100644 --- a/SCrawler/Download/Feed/DownloadFeedForm.resx +++ b/SCrawler/Download/Feed/DownloadFeedForm.resx @@ -120,6 +120,21 @@ False + + False + + + False + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABkSURBVDhPY6AKyO86WFDQfeg/iIYKkQZAmkNbnvyXta76 + DxViYGFi+Y8PQ5VBAMhmkGYgJs8FAw9GA5EKILFiWUFixfL/IBoqRBoAafYsOvpf0jiTvEAE2QzSLGmU + MeQCkYEBAD3tUdo+/cEPAAAAAElFTkSuQmCC + + 17, 17 diff --git a/SCrawler/Download/Feed/DownloadFeedForm.vb b/SCrawler/Download/Feed/DownloadFeedForm.vb index 2ed3b68..01a90bf 100644 --- a/SCrawler/Download/Feed/DownloadFeedForm.vb +++ b/SCrawler/Download/Feed/DownloadFeedForm.vb @@ -23,10 +23,17 @@ Namespace DownloadObjects Private DataRows As Integer = 10 Private DataColumns As Integer = 1 Private FeedEndless As Boolean = False - Private ReadOnly FileNotExist As New FPredicate(Of UserMediaD)(Function(d) Not d.Data.File.Exists) + Private ReadOnly FilterSubscriptions As New FPredicate(Of UserMediaD)(Function(d) If(d.User?.IsSubscription, False)) + Private ReadOnly FilterUsers As New FPredicate(Of UserMediaD)(Function(d) Not FilterSubscriptions.Invoke(d)) + Private ReadOnly FileNotExist As New FPredicate(Of UserMediaD)(Function(d) Not d.Data.File.Exists And Not FilterSubscriptions.Invoke(d)) Private BttRefreshToolTipText As String = "Refresh data list" Private CenterImage As Boolean = False Private NumberOfVisibleImages As Integer = 1 + Private ReadOnly Property IsSubscription As Boolean + Get + Return OPT_SUBSCRIPTIONS.Checked + End Get + End Property #End Region #Region "Initializer" Friend Sub New() @@ -56,6 +63,15 @@ Namespace DownloadObjects .AddThisToolbar() End With ToolbarTOP.Items.AddRange({New ToolStripSeparator, BTT_DELETE_SELECTED}) + With Settings + If .FeedOpenLastMode Then + If .FeedLastModeSubscriptions Then OPT_SUBSCRIPTIONS.Checked = True Else OPT_DEFAULT.Checked = True + Else + OPT_DEFAULT.Checked = True + Settings.FeedLastModeSubscriptions.Value = False + End If + End With + MENU_DOWN.Visible = OPT_SUBSCRIPTIONS.Checked UpdateSettings() RefillList() .EndLoaderOperations(False) @@ -72,7 +88,15 @@ Namespace DownloadObjects DataList.Clear() End Sub Private Sub DownloadFeedForm_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown - If e.KeyCode = Keys.F5 Then RefillList() : e.Handled = True + Dim b As Boolean = True + If e.KeyCode = Keys.F5 Then + RefillList() + ElseIf e.Control And e.KeyCode = Keys.G Then + MyRange.GoToF() + Else + b = False + End If + If b Then e.Handled = True End Sub #End Region #Region "Settings" @@ -143,8 +167,11 @@ Namespace DownloadObjects Private Sub RefillList(Optional ByVal RefillDataList As Boolean = True) DataPopulated = False If RefillDataList Then - Try : Downloader.Files.RemoveAll(FileNotExist) : Catch : End Try - DataList.ListAddList(Downloader.Files, LAP.ClearBeforeAdd, LAP.NotContainsOnly) + If Not IsSubscription Then + Try : Downloader.Files.RemoveAll(FileNotExist) : Catch : End Try + End If + DataList.Clear() + DataList.ListAddList(Downloader.Files.Where(If(IsSubscription, FilterSubscriptions, FilterUsers)), LAP.NotContainsOnly) End If MyRange.Source = DataList ControlInvokeFast(ToolbarTOP, BTT_REFRESH, Sub() BTT_REFRESH.ToolTipText = BttRefreshToolTipText) @@ -173,6 +200,14 @@ Namespace DownloadObjects Dim m As New MMessage("Saved sessions not selected", "Sessions",, vbExclamation) Dim x As XmlFile Dim lcr As New ListAddParams(LAP.NotContainsOnly + LAP.IgnoreICopier) + Dim __clearList As Action = Sub() + If IsSubscription Then + DataList.RemoveAll(FilterUsers) + Else + DataList.RemoveAll(FilterSubscriptions) + DataList.RemoveAll(FileNotExist) + End If + End Sub If Not GetLast AndAlso f.Exists(SFO.Path, False) Then fList = SFile.GetFiles(f, "*.xml",, EDP.ReturnValue) If Not GetLast AndAlso fList.ListExists Then Using chooser As New SimpleListForm(Of SFile)(fList, Settings.Design) With { @@ -192,7 +227,7 @@ Namespace DownloadObjects If x.Count > 0 Then DataList.ListAddList(x, lcr) x.Dispose() Next - DataList.RemoveAll(FileNotExist) + __clearList.Invoke RefillList(False) Else MsgBoxE(m) @@ -206,6 +241,7 @@ Namespace DownloadObjects x.LoadData() If x.Count > 0 Then DataList.Clear() : DataList.ListAddList(x, lcr) x.Dispose() + __clearList.Invoke RefillList(False) Else m.Text = "Saved sessions not found" @@ -216,6 +252,49 @@ Namespace DownloadObjects ErrorsDescriber.Execute(EDP.SendToLog, ex, $"[DownloadObjects.DownloadFeedForm.SessionChooser({GetLast})]") End Try End Sub + Private Sub OPT_Click(ByVal Sender As ToolStripMenuItem, ByVal e As EventArgs) Handles OPT_DEFAULT.Click, OPT_SUBSCRIPTIONS.Click + Dim __refill As Boolean = False + ControlInvokeFast(ToolbarTOP, Sender, + Sub() + Dim clicked As ToolStripMenuItem = Sender + Dim other As ToolStripMenuItem = If(Sender Is OPT_DEFAULT, OPT_SUBSCRIPTIONS, OPT_DEFAULT) + If other.Checked Then + __refill = True + clicked.Checked = True + other.Checked = False + Else + clicked.Checked = False + End If + Settings.FeedLastModeSubscriptions.Value = OPT_SUBSCRIPTIONS.Checked + MENU_DOWN.Visible = OPT_SUBSCRIPTIONS.Checked + End Sub, EDP.None) + If __refill Then RefillList() + End Sub +#End Region +#Region "Download" + Private Sub FeedMedia_Download(ByVal Sender As Object, ByVal e As EventArgs) Handles BTT_DOWN_ALL.Click, BTT_DOWN_SELECTED.Click + Try + Dim urls As New List(Of String) + If TypeOf Sender Is FeedMedia Then + urls.Add(DirectCast(Sender, FeedMedia).Post.URL_BASE) + ElseIf TypeOf Sender Is ToolStripMenuItem Then + Dim all As Boolean = CStr(AConvert(Of String)(DirectCast(Sender, ToolStripMenuItem).Tag, String.Empty)).StringToLower = "a" + ControlInvokeFast(TP_DATA, Sub() + urls.ListAddList((From m As FeedMedia In TP_DATA.Controls + Where m.Checked Or all + Select m.Post.URL_BASE).ListIfNothing) + TP_DATA.Controls.Cast(Of FeedMedia).ToList.ForEach(Sub(cnt) cnt.Checked = False) + End Sub) + End If + If urls.Count > 0 Then + VideoDownloader.FormShow + VideoDownloader.ADD_URLS_EXTERNAL(urls) + urls.Clear() + End If + Catch ex As Exception + ErrorsDescriber.Execute(EDP.LogMessageValue, ex, "Download subscription media") + End Try + End Sub #End Region #Region "Delete" Private Sub BTT_DELETE_SELECTED_Click(sender As Object, e As EventArgs) Handles BTT_DELETE_SELECTED.Click @@ -370,10 +449,11 @@ Namespace DownloadObjects RefillInProgress = True AllowTopScroll = False ScrollSuspended = True + Dim __isSubscriptions As Boolean = IsSubscription Dim d As List(Of UserMediaD) = MyRange.Current Dim d2 As List(Of UserMediaD) Dim i% - If d.ListExists And d.All(FileNotExist) Then + If d.ListExists AndAlso Not IsSubscription AndAlso d.All(FileNotExist) Then i = Sender.CurrentIndex Sender.HandlersSuspended = True RefillList() @@ -390,14 +470,20 @@ Namespace DownloadObjects ClearTable() If Sender.CurrentIndex > 0 And FeedEndless Then d2 = DirectCast(MyRange.Switcher, RangeSwitcher(Of UserMediaD)).Item(Sender.CurrentIndex - 1). - Where(Function(md) Not FileNotExist.Predicate(md)).ListTake(-2, DataColumns, EDP.ReturnValue).ListIfNothing + Where(Function(md) __isSubscriptions OrElse Not FileNotExist.Predicate(md)).ListTake(-2, DataColumns, EDP.ReturnValue).ListIfNothing If d2.Count > 0 Then d.InsertRange(0, d2) : d2.Clear() End If Dim w% = GetWidth() Dim h% = GetHeight() Dim p As New TPCELL(DataRows, DataColumns) Dim fmList As New List(Of FeedMedia) - d.ForEach(Sub(de) fmList.Add(New FeedMedia(de, w, h, AddressOf FeedMedia_MediaDeleted))) + d.ForEach(Sub(ByVal de As UserMediaD) + fmList.Add(New FeedMedia(de, w, h)) + With fmList.Last + AddHandler .MediaDeleted, AddressOf FeedMedia_MediaDeleted + AddHandler .MediaDownload, AddressOf FeedMedia_Download + End With + End Sub) If fmList.Count > 0 Then fmList.ListDisposeRemoveAll(Function(fm) fm Is Nothing OrElse fm.HasError) If fmList.Count > 0 Then For i = 0 To fmList.Count - 1 @@ -448,6 +534,7 @@ Namespace DownloadObjects End Function Private Sub DownloadFeedForm_ResizeEnd(sender As Object, e As EventArgs) Handles Me.ResizeEnd ResizeGrid() + UpdateButton() End Sub Private Sub DownloadFeedForm_SizeChanged(sender As Object, e As EventArgs) Handles Me.SizeChanged If Not LastWinState = WindowState And Not If(MyDefs?.Initializing, True) Then LastWinState = WindowState : ResizeGrid() @@ -480,6 +567,11 @@ Namespace DownloadObjects End With End Sub) End Sub + Private Sub UpdateButton() + ControlInvokeFast(ToolbarTOP, MENU_DOWN, Sub() MENU_DOWN.DisplayStyle = IIf(Width >= 540, + ToolStripItemDisplayStyle.ImageAndText, + ToolStripItemDisplayStyle.Image), EDP.None) + End Sub #End Region #Region "Scroll" Private AllowTopScroll As Boolean = False diff --git a/SCrawler/Download/Feed/FeedMedia.Designer.vb b/SCrawler/Download/Feed/FeedMedia.Designer.vb index 458d764..d9dca91 100644 --- a/SCrawler/Download/Feed/FeedMedia.Designer.vb +++ b/SCrawler/Download/Feed/FeedMedia.Designer.vb @@ -25,25 +25,29 @@ Namespace DownloadObjects Me.components = New System.ComponentModel.Container() Dim CONTEXT_SEP_1 As System.Windows.Forms.ToolStripSeparator Dim CONTEXT_SEP_2 As System.Windows.Forms.ToolStripSeparator - Dim CONTEXT_SEP_3 As System.Windows.Forms.ToolStripSeparator Dim TP_LBL As System.Windows.Forms.TableLayoutPanel Me.CH_CHECKED = New System.Windows.Forms.CheckBox() Me.LBL_INFO = New System.Windows.Forms.Label() Me.CONTEXT_DATA = New System.Windows.Forms.ContextMenuStrip(Me.components) + Me.BTT_CONTEXT_DOWN = New System.Windows.Forms.ToolStripMenuItem() + Me.CONTEXT_SEP_0 = New System.Windows.Forms.ToolStripSeparator() Me.BTT_CONTEXT_OPEN_MEDIA = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_CONTEXT_OPEN_USER = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_CONTEXT_OPEN_USER_URL = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_CONTEXT_OPEN_USER_POST = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_CONTEXT_FIND_USER = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_CONTEXT_INFO = New System.Windows.Forms.ToolStripMenuItem() + Me.CONTEXT_SEP_3 = New System.Windows.Forms.ToolStripSeparator() Me.BTT_CONTEXT_DELETE = New System.Windows.Forms.ToolStripMenuItem() + Me.ICON_SITE = New System.Windows.Forms.PictureBox() Me.TP_MAIN = New System.Windows.Forms.TableLayoutPanel() + Me.LBL_TITLE = New System.Windows.Forms.Label() CONTEXT_SEP_1 = New System.Windows.Forms.ToolStripSeparator() CONTEXT_SEP_2 = New System.Windows.Forms.ToolStripSeparator() - CONTEXT_SEP_3 = New System.Windows.Forms.ToolStripSeparator() TP_LBL = New System.Windows.Forms.TableLayoutPanel() TP_LBL.SuspendLayout() Me.CONTEXT_DATA.SuspendLayout() + CType(Me.ICON_SITE, System.ComponentModel.ISupportInitialize).BeginInit() Me.TP_MAIN.SuspendLayout() Me.SuspendLayout() ' @@ -57,25 +61,21 @@ Namespace DownloadObjects CONTEXT_SEP_2.Name = "CONTEXT_SEP_2" CONTEXT_SEP_2.Size = New System.Drawing.Size(134, 6) ' - 'CONTEXT_SEP_3 - ' - CONTEXT_SEP_3.Name = "CONTEXT_SEP_3" - CONTEXT_SEP_3.Size = New System.Drawing.Size(134, 6) - ' 'TP_LBL ' - TP_LBL.ColumnCount = 2 + TP_LBL.ColumnCount = 3 + TP_LBL.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) TP_LBL.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) TP_LBL.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) TP_LBL.Controls.Add(Me.CH_CHECKED, 0, 0) - TP_LBL.Controls.Add(Me.LBL_INFO, 1, 0) + TP_LBL.Controls.Add(Me.LBL_INFO, 2, 0) + TP_LBL.Controls.Add(Me.ICON_SITE, 1, 0) TP_LBL.Dock = System.Windows.Forms.DockStyle.Fill TP_LBL.Location = New System.Drawing.Point(0, 0) TP_LBL.Margin = New System.Windows.Forms.Padding(0) TP_LBL.Name = "TP_LBL" TP_LBL.RowCount = 1 TP_LBL.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) - TP_LBL.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) TP_LBL.Size = New System.Drawing.Size(146, 25) TP_LBL.TabIndex = 0 ' @@ -95,17 +95,31 @@ Namespace DownloadObjects Me.LBL_INFO.AutoSize = True Me.LBL_INFO.ContextMenuStrip = Me.CONTEXT_DATA Me.LBL_INFO.Dock = System.Windows.Forms.DockStyle.Fill - Me.LBL_INFO.Location = New System.Drawing.Point(28, 0) + Me.LBL_INFO.Location = New System.Drawing.Point(53, 0) Me.LBL_INFO.Name = "LBL_INFO" - Me.LBL_INFO.Size = New System.Drawing.Size(115, 25) + Me.LBL_INFO.Size = New System.Drawing.Size(90, 25) Me.LBL_INFO.TabIndex = 1 Me.LBL_INFO.TextAlign = System.Drawing.ContentAlignment.MiddleLeft ' 'CONTEXT_DATA ' - Me.CONTEXT_DATA.Items.AddRange(New System.Windows.Forms.ToolStripItem() {Me.BTT_CONTEXT_OPEN_MEDIA, Me.BTT_CONTEXT_OPEN_USER, CONTEXT_SEP_1, Me.BTT_CONTEXT_OPEN_USER_URL, Me.BTT_CONTEXT_OPEN_USER_POST, CONTEXT_SEP_2, Me.BTT_CONTEXT_FIND_USER, Me.BTT_CONTEXT_INFO, CONTEXT_SEP_3, Me.BTT_CONTEXT_DELETE}) + Me.CONTEXT_DATA.Items.AddRange(New System.Windows.Forms.ToolStripItem() {Me.BTT_CONTEXT_DOWN, Me.CONTEXT_SEP_0, Me.BTT_CONTEXT_OPEN_MEDIA, Me.BTT_CONTEXT_OPEN_USER, CONTEXT_SEP_1, Me.BTT_CONTEXT_OPEN_USER_URL, Me.BTT_CONTEXT_OPEN_USER_POST, CONTEXT_SEP_2, Me.BTT_CONTEXT_FIND_USER, Me.BTT_CONTEXT_INFO, Me.CONTEXT_SEP_3, Me.BTT_CONTEXT_DELETE}) Me.CONTEXT_DATA.Name = "CONTEXT_PIC" - Me.CONTEXT_DATA.Size = New System.Drawing.Size(138, 176) + Me.CONTEXT_DATA.Size = New System.Drawing.Size(138, 204) + ' + 'BTT_CONTEXT_DOWN + ' + Me.BTT_CONTEXT_DOWN.Image = Global.SCrawler.My.Resources.Resources.StartPic_Green_16 + Me.BTT_CONTEXT_DOWN.Name = "BTT_CONTEXT_DOWN" + Me.BTT_CONTEXT_DOWN.Size = New System.Drawing.Size(137, 22) + Me.BTT_CONTEXT_DOWN.Text = "Download" + Me.BTT_CONTEXT_DOWN.Visible = False + ' + 'CONTEXT_SEP_0 + ' + Me.CONTEXT_SEP_0.Name = "CONTEXT_SEP_0" + Me.CONTEXT_SEP_0.Size = New System.Drawing.Size(134, 6) + Me.CONTEXT_SEP_0.Visible = False ' 'BTT_CONTEXT_OPEN_MEDIA ' @@ -149,6 +163,11 @@ Namespace DownloadObjects Me.BTT_CONTEXT_INFO.Size = New System.Drawing.Size(137, 22) Me.BTT_CONTEXT_INFO.Text = "Information" ' + 'CONTEXT_SEP_3 + ' + Me.CONTEXT_SEP_3.Name = "CONTEXT_SEP_3" + Me.CONTEXT_SEP_3.Size = New System.Drawing.Size(134, 6) + ' 'BTT_CONTEXT_DELETE ' Me.BTT_CONTEXT_DELETE.Image = Global.SCrawler.My.Resources.Resources.DeletePic_24 @@ -156,22 +175,43 @@ Namespace DownloadObjects Me.BTT_CONTEXT_DELETE.Size = New System.Drawing.Size(137, 22) Me.BTT_CONTEXT_DELETE.Text = "Delete" ' + 'ICON_SITE + ' + Me.ICON_SITE.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom + Me.ICON_SITE.Dock = System.Windows.Forms.DockStyle.Fill + Me.ICON_SITE.Location = New System.Drawing.Point(28, 3) + Me.ICON_SITE.Name = "ICON_SITE" + Me.ICON_SITE.Size = New System.Drawing.Size(19, 19) + Me.ICON_SITE.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom + Me.ICON_SITE.TabIndex = 2 + Me.ICON_SITE.TabStop = False + ' 'TP_MAIN ' Me.TP_MAIN.ColumnCount = 1 Me.TP_MAIN.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) - Me.TP_MAIN.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20.0!)) Me.TP_MAIN.Controls.Add(TP_LBL, 0, 0) + Me.TP_MAIN.Controls.Add(Me.LBL_TITLE, 0, 1) Me.TP_MAIN.Dock = System.Windows.Forms.DockStyle.Fill Me.TP_MAIN.Location = New System.Drawing.Point(0, 0) Me.TP_MAIN.Margin = New System.Windows.Forms.Padding(0) Me.TP_MAIN.Name = "TP_MAIN" - Me.TP_MAIN.RowCount = 2 + Me.TP_MAIN.RowCount = 3 + Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) Me.TP_MAIN.Size = New System.Drawing.Size(146, 146) Me.TP_MAIN.TabIndex = 0 ' + 'LBL_TITLE + ' + Me.LBL_TITLE.AutoSize = True + Me.LBL_TITLE.Dock = System.Windows.Forms.DockStyle.Fill + Me.LBL_TITLE.Location = New System.Drawing.Point(3, 25) + Me.LBL_TITLE.Name = "LBL_TITLE" + Me.LBL_TITLE.Size = New System.Drawing.Size(140, 25) + Me.LBL_TITLE.TabIndex = 1 + ' 'FeedMedia ' Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!) @@ -186,7 +226,9 @@ Namespace DownloadObjects TP_LBL.ResumeLayout(False) TP_LBL.PerformLayout() Me.CONTEXT_DATA.ResumeLayout(False) + CType(Me.ICON_SITE, System.ComponentModel.ISupportInitialize).EndInit() Me.TP_MAIN.ResumeLayout(False) + Me.TP_MAIN.PerformLayout() Me.ResumeLayout(False) End Sub @@ -202,5 +244,10 @@ Namespace DownloadObjects Private WithEvents CH_CHECKED As CheckBox Private WithEvents LBL_INFO As Label Private WithEvents BTT_CONTEXT_INFO As ToolStripMenuItem + Private WithEvents ICON_SITE As PictureBox + Private WithEvents CONTEXT_SEP_3 As ToolStripSeparator + Private WithEvents BTT_CONTEXT_DOWN As ToolStripMenuItem + Private WithEvents CONTEXT_SEP_0 As ToolStripSeparator + Private WithEvents LBL_TITLE As Label End Class End Namespace \ No newline at end of file diff --git a/SCrawler/Download/Feed/FeedMedia.resx b/SCrawler/Download/Feed/FeedMedia.resx index 6a1dbbc..d0a3aa8 100644 --- a/SCrawler/Download/Feed/FeedMedia.resx +++ b/SCrawler/Download/Feed/FeedMedia.resx @@ -123,9 +123,6 @@ False - - False - False diff --git a/SCrawler/Download/Feed/FeedMedia.vb b/SCrawler/Download/Feed/FeedMedia.vb index 45c2c80..7606931 100644 --- a/SCrawler/Download/Feed/FeedMedia.vb +++ b/SCrawler/Download/Feed/FeedMedia.vb @@ -16,6 +16,7 @@ Namespace DownloadObjects Public Class FeedMedia #Region "Events" Friend Event MediaDeleted(ByVal Sender As Object) + Friend Event MediaDownload As EventHandler #End Region #Region "Declarations" Private Const VideoHeight As Integer = 450 @@ -55,17 +56,22 @@ Namespace DownloadObjects End Property Private ReadOnly Property ObjectsPaddingHeight As Integer Get - Return TP_MAIN.RowStyles(0).Height + PaddingE.GetOf({TP_MAIN}).Vertical(2) + Return TP_MAIN.RowStyles(0).Height + TP_MAIN.RowStyles(1).Height + PaddingE.GetOf({TP_MAIN}).Vertical(3) End Get End Property Private ReadOnly UserKey As String - Private ReadOnly Post As UserMedia - Friend ReadOnly Property Checked As Boolean + Friend ReadOnly Post As UserMedia + Private ReadOnly Media As UserMediaD + Friend Property Checked As Boolean Get Return CH_CHECKED.Checked End Get + Set(ByVal c As Boolean) + If Not CH_CHECKED.Checked = c Then ControlInvokeFast(CH_CHECKED, Sub() CH_CHECKED.Checked = c) + End Set End Property Friend ReadOnly Property Information As String + Private ReadOnly Property IsSubscription As Boolean = False Private Function GetImageResize(ByVal Width As Integer, ByVal Height As Integer) As Size If Height > 0 Then Dim h% = Height = ObjectsPaddingHeight @@ -101,46 +107,123 @@ Namespace DownloadObjects End If End Sub Private Sub ApplyColors() - If Settings.FeedBackColor.Exists Then - BackColor = Settings.FeedBackColor - LBL_INFO.BackColor = Settings.FeedBackColor + Dim b As Color? = Nothing, f As Color? = Nothing + If Not Media.User Is Nothing Then + If Media.User.BackColor.HasValue Then b = Media.User.BackColor + If Media.User.ForeColor.HasValue Then f = Media.User.ForeColor End If - If Settings.FeedForeColor.Exists Then - ForeColor = Settings.FeedForeColor - LBL_INFO.ForeColor = Settings.FeedForeColor + If Not b.HasValue And Settings.FeedBackColor.Exists Then b = Settings.FeedBackColor.Value + If Not f.HasValue And Settings.FeedForeColor.Exists Then f = Settings.FeedForeColor.Value + If b.HasValue Then + BackColor = b.Value + LBL_INFO.BackColor = b.Value + If Not LBL_TITLE.IsDisposed Then LBL_TITLE.BackColor = b.Value + End If + If f.HasValue Then + ForeColor = f.Value + LBL_INFO.ForeColor = f.Value + If Not LBL_TITLE.IsDisposed Then LBL_TITLE.ForeColor = f.Value End If End Sub #End Region +#Region "Converter" + Private Const ExtWebp As String = "webp" + Private Const ExtJpg As String = "jpg" + Private Function ConvertWebp(ByVal file As SFile, Optional ByVal NewCacheDir As Boolean = False) As SFile + If file.Extension = ExtWebp Then + If Settings.FfmpegFile.Exists Then + Dim dir As SFile + If NewCacheDir Then dir = Settings.Cache.NewPath Else dir = Settings.Cache + Dim f As SFile = file + f.Path = dir.Path + f.Extension = ExtJpg + Using imgBatch As New BatchExecutor + With imgBatch + .ChangeDirectory(dir) + .Execute($"""{Settings.FfmpegFile}"" -i ""{file}"" ""{f}""") + End With + End Using + If f.Exists Then Return f + End If + Else + Return file + End If + Return Nothing + End Function +#End Region #Region "Initializers" Public Sub New() InitializeComponent() End Sub - Friend Sub New(ByVal Media As UserMediaD, ByVal Width As Integer, ByVal Height As Integer, ByVal Handler As MediaDeletedEventHandler) + Friend Sub New(ByVal Media As UserMediaD, ByVal Width As Integer, ByVal Height As Integer) Try InitializeComponent() - File = Media.Data.File - If Not File.Exists And Media.Data.Type = UserMedia.Types.Video Then File.Path = $"{File.Path.CSFilePS}Video" - If Not File.Exists Then + Me.Media = Media + IsSubscription = If(Media.User?.IsSubscription, False) + + If IsSubscription Then + LBL_TITLE.Text = Media.Data.PictureOption.IfNullOrEmpty(Media.Data.File.Name) + If LBL_TITLE.Text.IsEmptyString Then + TP_MAIN.Controls.Remove(LBL_TITLE) + LBL_TITLE.Dispose() + TP_MAIN.RowStyles(1).Height = 0 + End If + + BTT_CONTEXT_DOWN.Visible = True + CONTEXT_SEP_0.Visible = True + BTT_CONTEXT_OPEN_USER.Visible = False + CONTEXT_SEP_3.Visible = False + BTT_CONTEXT_DELETE.Visible = False + + If Not Media.Data.URL.IsEmptyString Then + Dim ext$ = Media.Data.URL.CSFile.Extension + Dim imgFile As New SFile With {.Path = Settings.Cache.RootDirectory.Path} + With Media.User + imgFile.Name = $"{IIf(.IncludedInCollection, "{.CollectionName}", String.Empty)}{ .Site}{ .Name}_" + imgFile.Name &= (CLng(Media.Data.URL.GetHashCode) + CLng(Media.Data.File.GetHashCode)).ToString + imgFile.Extension = ExtJpg + If Not imgFile.Exists AndAlso Not ext.IsEmptyString AndAlso ext.ToLower = ExtWebp Then imgFile.Extension = ExtWebp + End With + If Not imgFile.Exists Then + Settings.Cache.Validate() + GetWebFile(Media.Data.URL, imgFile, EDP.None) + If imgFile.Exists Then File = ConvertWebp(imgFile) + Else + File = imgFile + End If + End If + Else + TP_MAIN.Controls.Remove(LBL_TITLE) + LBL_TITLE.Dispose() + TP_MAIN.RowStyles(1).Height = 0 + File = Media.Data.File + If Not File.Exists And Media.Data.Type = UserMedia.Types.Video Then File.Path = $"{File.Path.CSFilePS}Video" + End If + + If Not File.Exists And Not IsSubscription Then If Not Media.Data.SpecialFolder.IsEmptyString Then File.Path = $"{File.Path.CSFilePS}{Media.Data.SpecialFolder}".CSFileP If Not File.Exists And Media.Data.Type = UserMedia.Types.Video Then File.Path = $"{File.Path.CSFilePS}Video" End If End If + If File.Exists Then Information = $"Type: {Media.Data.Type}" Information.StringAppendLine($"File: {File.File}") Information.StringAppendLine($"Address: {File}") Information.StringAppendLine($"Downloaded: {Media.Date.ToStringDate(ADateTime.Formats.BaseDateTime)}") If Media.Data.Post.Date.HasValue Then Information.StringAppendLine($"Posted: {Media.Data.Post.Date.Value.ToStringDate(ADateTime.Formats.BaseDateTime)}") - Dim infoType As UserMedia.Types = Media.Data.Type + Dim infoType As UserMedia.Types = If(IsSubscription, UserMedia.Types.Picture, Media.Data.Type) Dim h% Dim s As Size Post = Media.Data - Select Case Media.Data.Type + Select Case infoType Case UserMedia.Types.Picture, UserMedia.Types.GIF - MyImage = New ImageRenderer(File) + Dim tmpMediaFile As SFile = ConvertWebp(File, True) + If tmpMediaFile.IsEmptyString Then Throw New ArgumentNullException With {.HelpLink = 1} + MyImage = New ImageRenderer(tmpMediaFile) Dim a As AnchorStyles = AnchorStyles.Top + If(Height > 0, 0, AnchorStyles.Left) s = GetImageResize(Width, Height) h = s.Height @@ -158,14 +241,14 @@ Namespace DownloadObjects .Padding = New Padding(0), .ContextMenuStrip = CONTEXT_DATA } - TP_MAIN.Controls.Add(MyPicture, 0, 1) + TP_MAIN.Controls.Add(MyPicture, 0, 2) BTT_CONTEXT_OPEN_MEDIA.Text &= " picture" BTT_CONTEXT_DELETE.Text &= " picture" Case UserMedia.Types.Video, UserMedia.Types.m3u8 infoType = UserMedia.Types.Video MyVideo = New FeedVideo(File) With {.Tag = File, .Dock = DockStyle.Fill, .ContextMenuStrip = CONTEXT_DATA} If MyVideo.HasError Then HasError = True - TP_MAIN.Controls.Add(MyVideo, 0, 1) + TP_MAIN.Controls.Add(MyVideo, 0, 2) BTT_CONTEXT_OPEN_MEDIA.Text &= " video" BTT_CONTEXT_DELETE.Text &= " video" h = VideoHeight @@ -181,22 +264,33 @@ Namespace DownloadObjects If .IncludedInCollection Then Information.StringAppendLine($"User collection: { .CollectionName}") Information.StringAppendLine($"User site: { .Site}") Information.StringAppendLine($"User name: {IIf(Not .FriendlyName.IsEmptyString And Not .IncludedInCollection, .FriendlyName, .Name)}") - If .Site = API.Instagram.InstagramSite Then BTT_CONTEXT_OPEN_USER_POST.Visible = False If .IncludedInCollection Then info &= $"[{ .CollectionName}]: " - info &= $"{ .Site} - {IIf(Not .FriendlyName.IsEmptyString And Not .IncludedInCollection, .FriendlyName, .Name)}" + If Settings.FeedShowFriendlyNames Or Not DirectCast(.Self, UserDataBase).FeedIsUser Then + info &= $"{ .Site} - { .FriendlyName.IfNullOrEmpty(.Name)}" + Else + info &= $"{ .Site} - {IIf(Not .FriendlyName.IsEmptyString And Not .IncludedInCollection, .FriendlyName, .Name)}" + End If End With End If If Settings.FeedAddSessionToCaption Then info = $"[{Media.Session}] {info}" If Settings.FeedAddDateToCaption Then info &= $" ({Media.Date.ToStringDate(ADateTime.Formats.BaseDateTime)})" LBL_INFO.Text = info + If Not Media.User Is Nothing AndAlso Not Media.User.HOST Is Nothing Then + With Media.User.HOST.Source + If Not .Image Is Nothing Then + ICON_SITE.Image = .Image + ElseIf Not .Icon Is Nothing Then + ICON_SITE.Image = .Icon.ToBitmap + End If + End With + End If s = New Size(Width, h + ObjectsPaddingHeight) Size = s MinimumSize = s MaximumSize = s ApplyColors() - If Not Handler Is Nothing Then AddHandler Me.MediaDeleted, Handler Else Throw New ArgumentNullException With {.HelpLink = 1} End If @@ -222,11 +316,16 @@ Namespace DownloadObjects If e.Button = MouseButtons.Left Then ControlInvoke(CH_CHECKED, Sub() CH_CHECKED.Checked = Not CH_CHECKED.Checked) End Sub Private Sub LBL_INFO_DoubleClick(sender As Object, e As EventArgs) Handles LBL_INFO.DoubleClick - If Not UserKey.IsEmptyString Then + If Not UserKey.IsEmptyString And Not IsSubscription Then Dim u As IUserData = Settings.GetUser(UserKey) If Not u Is Nothing Then u.OpenFolder() End If End Sub + Private Sub LBL_TITLE_MouseDoubleClick(sender As Object, e As MouseEventArgs) Handles LBL_TITLE.MouseDoubleClick + If Not Post.URL_BASE.IsEmptyString Then + Try : Process.Start(Post.URL_BASE) : Catch : End Try + End If + End Sub #End Region #Region "Picture / Video objects" Private Sub MyPicture_DoubleClick(sender As Object, e As EventArgs) Handles MyPicture.DoubleClick @@ -234,6 +333,9 @@ Namespace DownloadObjects End Sub #End Region #Region "Context" + Private Sub BTT_CONTEXT_DOWN_Click(sender As Object, e As EventArgs) Handles BTT_CONTEXT_DOWN.Click + RaiseEvent MediaDownload(Me, EventArgs.Empty) + End Sub Private Sub BTT_CONTEXT_OPEN_MEDIA_Click(sender As Object, e As EventArgs) Handles BTT_CONTEXT_OPEN_MEDIA.Click File.Open() End Sub @@ -251,15 +353,18 @@ Namespace DownloadObjects End Sub Private Sub BTT_CONTEXT_OPEN_USER_POST_Click(sender As Object, e As EventArgs) Handles BTT_CONTEXT_OPEN_USER_POST.Click Try - If Not UserKey.IsEmptyString And Not Post.Post.ID.IsEmptyString Then - Dim u As IUserData = Settings.GetUser(UserKey) - If Not u Is Nothing Then - Dim url$ = UserDataBase.GetPostUrl(u, Post) - If Not url.IsEmptyString Then - Try : Process.Start(url) : Catch : End Try - End If + Dim url$ = String.Empty + If IsSubscription Then + url = Post.URL_BASE + Else + If Not UserKey.IsEmptyString And Not Post.Post.ID.IsEmptyString Then + Dim u As IUserData = Settings.GetUser(UserKey) + If Not u Is Nothing Then url = UserDataBase.GetPostUrl(u, Post) End If End If + If Not url.IsEmptyString Then + Try : Process.Start(url) : Catch : End Try + End If Catch ex As Exception ErrorsDescriber.Execute(EDP.LogMessageValue, ex, $"[FeedMedia.OpenPost({UserKey}, {Post.Post.ID})]") End Try diff --git a/SCrawler/Download/Groups/DownloadGroup.vb b/SCrawler/Download/Groups/DownloadGroup.vb index 081566b..45f1fa3 100644 --- a/SCrawler/Download/Groups/DownloadGroup.vb +++ b/SCrawler/Download/Groups/DownloadGroup.vb @@ -144,6 +144,17 @@ Namespace DownloadObjects.Groups (.Temporary = CheckState.Indeterminate Or user.Temporary = CBool(.Temporary)) And (.Favorite = CheckState.Indeterminate Or (user.Favorite = CBool(.Favorite))) And (Not UseReadyOption Or .ReadyForDownloadIgnore Or user.ReadyForDownload = .ReadyForDownload) And user.Exists + Dim CheckSubscription As Predicate(Of IUserData) = Function(ByVal user As IUserData) As Boolean + If .Subscriptions Then + If .SubscriptionsOnly Then + Return user.IsSubscription = True + Else + Return True + End If + Else + Return user.IsSubscription = False + End If + End Function Dim CheckLabelsExcluded As Predicate(Of IUserData) = Function(ByVal user As IUserData) As Boolean If .LabelsExcluded.Count = 0 Then Return True @@ -165,7 +176,16 @@ Namespace DownloadObjects.Groups Dim CheckSites As Predicate(Of IUserData) = Function(user) _ (.Sites.Count = 0 OrElse .Sites.Contains(user.Site)) AndAlso (.SitesExcluded.Count = 0 OrElse Not .SitesExcluded.Contains(user.Site)) - Return Settings.GetUsers(Function(user) CheckLabels.Invoke(user) AndAlso CheckSites.Invoke(user) AndAlso CheckParams.Invoke(user)) + Dim users As IEnumerable(Of IUserData) = + Settings.GetUsers(Function(user) CheckLabels.Invoke(user) AndAlso CheckSites.Invoke(user) AndAlso + CheckParams.Invoke(user) AndAlso CheckSubscription.Invoke(user)) + If .UsersCount = 0 Or Not users.ListExists Then + Return users + Else + users = users.ListTake(If(.UsersCount > 0, -1, -2), Math.Abs(.UsersCount)) + If .UsersCount < 0 Then users = users.ListReverse + Return users + End If End With Else Return Nothing diff --git a/SCrawler/Download/Groups/GroupDefaults.vb b/SCrawler/Download/Groups/GroupDefaults.vb index a5a99b4..3f433a8 100644 --- a/SCrawler/Download/Groups/GroupDefaults.vb +++ b/SCrawler/Download/Groups/GroupDefaults.vb @@ -13,10 +13,14 @@ Namespace DownloadObjects.Groups Public Class GroupDefaults : Inherits TableLayoutPanel Private ReadOnly TP_1 As TableLayoutPanel Private ReadOnly TP_2 As TableLayoutPanel + Private ReadOnly TP_3 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 ReadOnly CH_SUBSCRIPTIONS As CheckBox + Private ReadOnly CH_SUBSCRIPTIONS_ONLY As CheckBox + Private WithEvents NUM_USERS_COUNT As TextBoxExtended Private WithEvents TXT_LABELS As TextBoxExtended Private WithEvents TXT_SITES As TextBoxExtended Friend WithEvents TXT_NAME As TextBoxExtended @@ -48,6 +52,32 @@ Namespace DownloadObjects.Groups 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) + + CH_SUBSCRIPTIONS = New CheckBox With {.Text = "Subscriptions", .Name = "CH_SUBSCRIPTIONS", .Checked = False, .Dock = DockStyle.Fill} + CH_SUBSCRIPTIONS_ONLY = New CheckBox With {.Text = "Subscriptions only", .Name = "CH_SUBSCRIPTIONS_ONLY", .Checked = False, .Dock = DockStyle.Fill} + TP_3 = New TableLayoutPanel With {.CellBorderStyle = TableLayoutPanelCellBorderStyle.Single, .Margin = New Padding(0), .Dock = DockStyle.Fill} + FillTP(TP_3, CH_SUBSCRIPTIONS, CH_SUBSCRIPTIONS_ONLY) + + NUM_USERS_COUNT = New TextBoxExtended + With NUM_USERS_COUNT + .BeginInit() + .CaptionText = "Users" + .CaptionToolTipText = "The number of users that to be downloaded." & vbCr & + "The number is 0 = all users." & vbCr & + "Number greater than 0 = number of users from the beginning to the end of the list." & vbCr & + "Number less than 0 = number of users from end to the beginning of the list." + .CaptionToolTipEnabled = True + .CaptionWidth = 50 + .ControlMode = TextBoxExtended.ControlModes.NumericUpDown + .NumberMinimum = Integer.MinValue + .NumberMaximum = Integer.MaxValue + .NumberUpDownAlign = LeftRightAlignment.Left + .Dock = DockStyle.Fill + .Buttons.Add(New ActionButton(ADB.Clear) With {.ToolTipText = "Reset value"}) + .ClearTextByButtonClear = False + .Value = 0 + .EndInit() + End With End Sub Private Sub InitTextBox(ByRef TXT As TextBoxExtended, ByVal Caption As String, ByVal Buttons As ActionButton()) TXT = New TextBoxExtended @@ -76,6 +106,9 @@ Namespace DownloadObjects.Groups CH_FAV.Dispose() CH_READY_FOR_DOWN.Dispose() CH_READY_FOR_DOWN_IGNORE.Dispose() + CH_SUBSCRIPTIONS.Dispose() + CH_SUBSCRIPTIONS_ONLY.Dispose() + NUM_USERS_COUNT.Dispose() TXT_LABELS.Dispose() With TP_1 .Controls.Clear() @@ -89,6 +122,12 @@ Namespace DownloadObjects.Groups .ColumnStyles.Clear() .Dispose() End With + With TP_3 + .Controls.Clear() + .RowStyles.Clear() + .ColumnStyles.Clear() + .Dispose() + End With End Sub Protected Overrides Sub InitLayout() MyBase.InitLayout() @@ -98,11 +137,13 @@ Namespace DownloadObjects.Groups CellBorderStyle = TableLayoutPanelCellBorderStyle.Single ColumnCount = 1 ColumnStyles.Add(New ColumnStyle(SizeType.Percent, 100)) - RowCount = 7 + RowCount = 9 RowStyles.Add(New RowStyle(SizeType.Absolute, 25)) RowStyles.Add(New RowStyle(SizeType.Absolute, 28)) RowStyles.Add(New RowStyle(SizeType.Absolute, 25)) RowStyles.Add(New RowStyle(SizeType.Absolute, 25)) + RowStyles.Add(New RowStyle(SizeType.Absolute, 25)) + RowStyles.Add(New RowStyle(SizeType.Absolute, 28)) RowStyles.Add(New RowStyle(SizeType.Absolute, 28)) RowStyles.Add(New RowStyle(SizeType.Absolute, 28)) RowStyles.Add(New RowStyle(SizeType.Percent, 100)) @@ -110,8 +151,13 @@ Namespace DownloadObjects.Groups Controls.Add(TXT_NAME, 0, 1) Controls.Add(TP_1, 0, 2) Controls.Add(TP_2, 0, 3) - Controls.Add(TXT_LABELS, 0, 4) - Controls.Add(TXT_SITES, 0, 5) + Controls.Add(TP_3, 0, 4) + Controls.Add(NUM_USERS_COUNT, 0, 5) + Controls.Add(TXT_LABELS, 0, 6) + Controls.Add(TXT_SITES, 0, 7) + End Sub + Private Sub NUM_USERS_COUNT_ActionOnButtonClick(ByVal Sender As ActionButton, ByVal e As ActionButtonEventArgs) Handles NUM_USERS_COUNT.ActionOnButtonClick + If Sender.DefaultButton = ADB.Clear Then NUM_USERS_COUNT.Value = 0 End Sub Private Sub TXT_LABELS_ActionOnButtonClick(ByVal Sender As ActionButton, ByVal e As ActionButtonEventArgs) Handles TXT_LABELS.ActionOnButtonClick Select Case Sender.DefaultButton @@ -163,6 +209,9 @@ Namespace DownloadObjects.Groups .Favorite = CH_FAV.CheckState .ReadyForDownload = CH_READY_FOR_DOWN.Checked .ReadyForDownloadIgnore = CH_READY_FOR_DOWN_IGNORE.Checked + .Subscriptions = CH_SUBSCRIPTIONS.Checked + .SubscriptionsOnly = CH_SUBSCRIPTIONS_ONLY.Checked + .UsersCount = NUM_USERS_COUNT.Value .Labels.Clear() .Labels.ListAddList(Labels) .LabelsExcluded.Clear() @@ -182,6 +231,9 @@ Namespace DownloadObjects.Groups CH_FAV.CheckState = .Favorite CH_READY_FOR_DOWN.Checked = .ReadyForDownload CH_READY_FOR_DOWN_IGNORE.Checked = .ReadyForDownloadIgnore + CH_SUBSCRIPTIONS.Checked = .Subscriptions + CH_SUBSCRIPTIONS_ONLY.Checked = .SubscriptionsOnly + NUM_USERS_COUNT.Value = .UsersCount Labels.ListAddList(.Labels) LabelsExcluded.ListAddList(.LabelsExcluded) @@ -195,7 +247,8 @@ Namespace DownloadObjects.Groups End Sub Private _Enabled As Boolean = True Private _JustExcludeOptions As Boolean = False - Friend Overloads Property Enabled(Optional ByVal LeaveExcludeOptions As Boolean = False) As Boolean + Friend Overloads Property Enabled(Optional ByVal LeaveExcludeOptions As Boolean = False, + Optional ByVal LeaveSubscriptionsAndUsersCount As Boolean = False) As Boolean Get Return _Enabled End Get @@ -204,6 +257,8 @@ Namespace DownloadObjects.Groups _JustExcludeOptions = False TP_1.Enabled = e TP_2.Enabled = e + TP_3.Enabled = e Or LeaveSubscriptionsAndUsersCount + NUM_USERS_COUNT.Enabled = e Or LeaveSubscriptionsAndUsersCount If e Then TXT_LABELS.Enabled = True TXT_SITES.Enabled = True diff --git a/SCrawler/Download/Groups/GroupEditorForm.Designer.vb b/SCrawler/Download/Groups/GroupEditorForm.Designer.vb index 9cc09f8..a27c27f 100644 --- a/SCrawler/Download/Groups/GroupEditorForm.Designer.vb +++ b/SCrawler/Download/Groups/GroupEditorForm.Designer.vb @@ -35,13 +35,13 @@ Namespace DownloadObjects.Groups 'CONTAINER_MAIN.ContentPanel ' CONTAINER_MAIN.ContentPanel.Controls.Add(Me.DEFS_GROUP) - CONTAINER_MAIN.ContentPanel.Size = New System.Drawing.Size(476, 141) + CONTAINER_MAIN.ContentPanel.Size = New System.Drawing.Size(476, 196) 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, 166) + CONTAINER_MAIN.Size = New System.Drawing.Size(476, 221) CONTAINER_MAIN.TabIndex = 0 CONTAINER_MAIN.TopToolStripPanelVisible = False ' @@ -53,30 +53,32 @@ Namespace DownloadObjects.Groups Me.DEFS_GROUP.Dock = System.Windows.Forms.DockStyle.Fill Me.DEFS_GROUP.Location = New System.Drawing.Point(0, 0) Me.DEFS_GROUP.Name = "DEFS_GROUP" - Me.DEFS_GROUP.RowCount = 7 + Me.DEFS_GROUP.RowCount = 9 Me.DEFS_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 0!)) Me.DEFS_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) Me.DEFS_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) Me.DEFS_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) + Me.DEFS_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) + Me.DEFS_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) Me.DEFS_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) Me.DEFS_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.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, 141) + Me.DEFS_GROUP.Size = New System.Drawing.Size(476, 196) Me.DEFS_GROUP.TabIndex = 0 ' 'GroupEditorForm ' Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!) Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font - Me.ClientSize = New System.Drawing.Size(476, 166) + Me.ClientSize = New System.Drawing.Size(476, 221) Me.Controls.Add(CONTAINER_MAIN) Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle Me.Icon = Global.SCrawler.My.Resources.Resources.GroupByIcon_16 Me.KeyPreview = True Me.MaximizeBox = False - Me.MaximumSize = New System.Drawing.Size(492, 205) + Me.MaximumSize = New System.Drawing.Size(492, 260) Me.MinimizeBox = False - Me.MinimumSize = New System.Drawing.Size(492, 205) + Me.MinimumSize = New System.Drawing.Size(492, 260) Me.Name = "GroupEditorForm" Me.ShowInTaskbar = False Me.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide diff --git a/SCrawler/Download/Groups/GroupParameters.vb b/SCrawler/Download/Groups/GroupParameters.vb index 1d77172..6451ee9 100644 --- a/SCrawler/Download/Groups/GroupParameters.vb +++ b/SCrawler/Download/Groups/GroupParameters.vb @@ -18,6 +18,9 @@ Namespace DownloadObjects.Groups Property Favorite As CheckState Property ReadyForDownload As Boolean Property ReadyForDownloadIgnore As Boolean + Property Subscriptions As Boolean + Property SubscriptionsOnly As Boolean + Property UsersCount As Integer End Interface Friend Class GroupParameters : Implements IGroup, IDisposable Protected Const Name_Name As String = "Name" @@ -25,6 +28,9 @@ Namespace DownloadObjects.Groups Protected Const Name_Favorite As String = "Favorite" Protected Const Name_ReadyForDownload As String = "RFD" Protected Const Name_ReadyForDownloadIgnore As String = "RFDI" + Protected Const Name_Subscriptions As String = "Subscriptions" + Protected Const Name_SubscriptionsOnly As String = "SubscriptionsOnly" + Protected Const Name_UsersCount As String = "UsersCount" Protected Const Name_Labels As String = "Labels" Protected Const Name_Labels_Excluded As String = "LabelsExcluded" Protected Const Name_Sites As String = "Sites" @@ -38,6 +44,9 @@ Namespace DownloadObjects.Groups 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 Property Subscriptions As Boolean = False Implements IGroup.Subscriptions + Friend Property SubscriptionsOnly As Boolean = False Implements IGroup.SubscriptionsOnly + Friend Property UsersCount As Integer = 0 Implements IGroup.UsersCount Friend Sub New() Labels = New List(Of String) LabelsExcluded = New List(Of String) @@ -50,6 +59,9 @@ Namespace DownloadObjects.Groups Favorite = e.Value(Name_Favorite).FromXML(Of Integer)(CInt(CheckState.Indeterminate)) ReadyForDownload = e.Value(Name_ReadyForDownload).FromXML(Of Boolean)(True) ReadyForDownloadIgnore = e.Value(Name_ReadyForDownloadIgnore).FromXML(Of Boolean)(False) + Subscriptions = e.Value(Name_Subscriptions).FromXML(Of Boolean)(False) + SubscriptionsOnly = e.Value(Name_SubscriptionsOnly).FromXML(Of Boolean)(False) + UsersCount = e.Value(Name_UsersCount).FromXML(Of Integer)(0) Dim l As New ListAddParams(LAP.NotContainsOnly) If Not e.Value(Name_Labels).IsEmptyString Then Labels.ListAddList(e.Value(Name_Labels).Split("|"), l) @@ -63,6 +75,9 @@ Namespace DownloadObjects.Groups New EContainer(Name_Favorite, CInt(Favorite)), New EContainer(Name_ReadyForDownload, ReadyForDownload.BoolToInteger), New EContainer(Name_ReadyForDownloadIgnore, ReadyForDownloadIgnore.BoolToInteger), + New EContainer(Name_Subscriptions, Subscriptions.BoolToInteger), + New EContainer(Name_SubscriptionsOnly, SubscriptionsOnly.BoolToInteger), + New EContainer(Name_UsersCount, UsersCount), New EContainer(Name_Labels, Labels.ListToString("|")), New EContainer(Name_Labels_Excluded, LabelsExcluded.ListToString("|")), New EContainer(Name_Sites, Sites.ListToString("|")), diff --git a/SCrawler/Download/MissingPostsForm.vb b/SCrawler/Download/MissingPostsForm.vb index a09e926..be0d017 100644 --- a/SCrawler/Download/MissingPostsForm.vb +++ b/SCrawler/Download/MissingPostsForm.vb @@ -11,11 +11,13 @@ Imports SCrawler.API.Base Imports PersonalUtilities.Forms Imports PersonalUtilities.Forms.Toolbars Imports PersonalUtilities.Functions.Messaging +Imports ECI = PersonalUtilities.Forms.Toolbars.EditToolbar.ControlItem Namespace DownloadObjects Friend Class MissingPostsForm #Region "Declarations" Private WithEvents MyDefs As DefaultFormOptions Private ReadOnly MUsers As List(Of IUserData) + Private WithEvents BTT_DELETE_ALL As ToolStripButton Private WithEvents BTT_DOWN_ALL As ToolStripButton Private WithEvents BTT_INFO As ToolStripButton #End Region @@ -24,6 +26,13 @@ Namespace DownloadObjects InitializeComponent() MUsers = New List(Of IUserData) MyDefs = New DefaultFormOptions(Me, Settings.Design) + BTT_DELETE_ALL = New ToolStripButton With { + .Text = "Delete ALL", + .ToolTipText = String.Empty, + .AutoToolTip = False, + .Image = PersonalUtilities.My.Resources.DeletePic_Red_24, + .DisplayStyle = ToolStripItemDisplayStyle.ImageAndText + } BTT_DOWN_ALL = New ToolStripButton With { .Text = "Download ALL", .ToolTipText = String.Empty, @@ -44,7 +53,7 @@ Namespace DownloadObjects Private Sub MissingPostsForm_Load(sender As Object, e As EventArgs) Handles Me.Load With MyDefs .MyViewInitialize() - .AddEditToolbarPlus({EditToolbar.ControlItem.Separator, BTT_DOWN_ALL, BTT_INFO}) + .AddEditToolbar({ECI.Update, ECI.Separator, ECI.Delete, BTT_DELETE_ALL, ECI.Separator, BTT_DOWN_ALL, BTT_INFO}) .EndLoaderOperations(False) End With RefillList() @@ -258,11 +267,17 @@ Namespace DownloadObjects ErrorsDescriber.Execute(EDP.SendToLog, ex, $"[DownloadObjects.MissingPostsForm.FindUser]") End Try End Sub - Private Sub DeletePost() Handles MyDefs.ButtonDeleteClickE, BTT_DELETE.Click + Private Sub DeletePost(ByVal Sender As Object, ByVal e As EventArgs) Handles MyDefs.ButtonDeleteClickE, BTT_DELETE.Click, BTT_DELETE_ALL.Click Const MsgTitle$ = "Remove missing posts" Dim UsersToUpdate As New List(Of UserDataBase) Try - Dim data As List(Of ListViewItem) = LIST_DATA.SelectedItems.ToObjectsList.ListCast(Of ListViewItem) + Dim data As List(Of ListViewItem) + Dim isAll As Boolean = Sender Is BTT_DELETE_ALL + If isAll Then + data = LIST_DATA.Items.ToObjectsList.ListCast(Of ListViewItem) + Else + data = LIST_DATA.SelectedItems.ToObjectsList.ListCast(Of ListViewItem) + End If If data.ListExists Then Dim lp As New ListAddParams(LAP.NotContainsOnly) Dim usersCount% = ListAddList(Nothing, data.Select(Function(d) d.Group.Header), LAP.NotContainsOnly).ListIfNothing.Count @@ -288,7 +303,7 @@ Namespace DownloadObjects MsgBoxE({"Operation canceled", MsgTitle}) End If Else - MsgBoxE({"No selected posts", MsgTitle}) + MsgBoxE({IIf(isAll, "No posts found to delete", "No selected posts"), MsgTitle}) End If Catch ex As Exception ErrorsDescriber.Execute(EDP.SendToLog, ex, "[DownloadObjects.MissingPostsForm.DeletePost]") diff --git a/SCrawler/Download/STDownloader/DownloaderUrlForm.Designer.vb b/SCrawler/Download/STDownloader/DownloaderUrlForm.Designer.vb index a61a221..8047e66 100644 --- a/SCrawler/Download/STDownloader/DownloaderUrlForm.Designer.vb +++ b/SCrawler/Download/STDownloader/DownloaderUrlForm.Designer.vb @@ -28,8 +28,12 @@ Namespace DownloadObjects.STDownloader Dim resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(DownloaderUrlForm)) 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 ListColumn1 As PersonalUtilities.Forms.Controls.Base.ListColumn = New PersonalUtilities.Forms.Controls.Base.ListColumn() + Dim ListColumn2 As PersonalUtilities.Forms.Controls.Base.ListColumn = New PersonalUtilities.Forms.Controls.Base.ListColumn() Me.TXT_URL = New PersonalUtilities.Forms.Controls.TextBoxExtended() - Me.TXT_PATH = New PersonalUtilities.Forms.Controls.TextBoxExtended() + Me.TXT_PATH = New PersonalUtilities.Forms.Controls.ComboBoxExtended() CONTAINER_MAIN = New System.Windows.Forms.ToolStripContainer() TP_MAIN = New System.Windows.Forms.TableLayoutPanel() CONTAINER_MAIN.ContentPanel.SuspendLayout() @@ -92,20 +96,44 @@ Namespace DownloadObjects.STDownloader ActionButton2.BackgroundImage = CType(resources.GetObject("ActionButton2.BackgroundImage"), System.Drawing.Image) ActionButton2.Name = "Open" ActionButton2.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open + ActionButton2.ToolTipText = "Choose a new location (Ctrl+O)" ActionButton3.BackgroundImage = CType(resources.GetObject("ActionButton3.BackgroundImage"), System.Drawing.Image) - ActionButton3.Name = "Clear" - ActionButton3.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear + ActionButton3.Name = "Add" + ActionButton3.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Add + ActionButton3.ToolTipText = "Choose a new location and add it to the list (Alt+O)" + ActionButton4.BackgroundImage = CType(resources.GetObject("ActionButton4.BackgroundImage"), System.Drawing.Image) + ActionButton4.Name = "Clear" + ActionButton4.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear + ActionButton5.BackgroundImage = CType(resources.GetObject("ActionButton5.BackgroundImage"), System.Drawing.Image) + ActionButton5.Name = "ArrowDown" + ActionButton5.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.ArrowDown Me.TXT_PATH.Buttons.Add(ActionButton2) Me.TXT_PATH.Buttons.Add(ActionButton3) + Me.TXT_PATH.Buttons.Add(ActionButton4) + Me.TXT_PATH.Buttons.Add(ActionButton5) + Me.TXT_PATH.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.Label Me.TXT_PATH.CaptionText = "Output" Me.TXT_PATH.CaptionToolTipEnabled = True Me.TXT_PATH.CaptionToolTipText = "Output path" + Me.TXT_PATH.CaptionVisible = True Me.TXT_PATH.CaptionWidth = 40.0R + ListColumn1.Name = "COL_NAME" + ListColumn1.Text = "Name" + ListColumn1.Width = -1 + ListColumn2.DisplayMember = True + ListColumn2.Name = "COL_VALUE" + ListColumn2.Text = "Value" + ListColumn2.ValueMember = True + ListColumn2.Visible = False + Me.TXT_PATH.Columns.Add(ListColumn1) + Me.TXT_PATH.Columns.Add(ListColumn2) Me.TXT_PATH.Dock = System.Windows.Forms.DockStyle.Fill + Me.TXT_PATH.ListAutoCompleteMode = PersonalUtilities.Forms.Controls.ComboBoxExtended.AutoCompleteModes.Disabled Me.TXT_PATH.Location = New System.Drawing.Point(4, 33) Me.TXT_PATH.Name = "TXT_PATH" Me.TXT_PATH.Size = New System.Drawing.Size(476, 22) Me.TXT_PATH.TabIndex = 1 + Me.TXT_PATH.TextBoxBorderStyle = System.Windows.Forms.BorderStyle.FixedSingle ' 'DownloaderUrlForm ' @@ -134,6 +162,6 @@ Namespace DownloadObjects.STDownloader End Sub Private WithEvents TXT_URL As PersonalUtilities.Forms.Controls.TextBoxExtended - Private WithEvents TXT_PATH As PersonalUtilities.Forms.Controls.TextBoxExtended + Private WithEvents TXT_PATH As PersonalUtilities.Forms.Controls.ComboBoxExtended End Class End Namespace \ No newline at end of file diff --git a/SCrawler/Download/STDownloader/DownloaderUrlForm.resx b/SCrawler/Download/STDownloader/DownloaderUrlForm.resx index f385b3a..299ec6d 100644 --- a/SCrawler/Download/STDownloader/DownloaderUrlForm.resx +++ b/SCrawler/Download/STDownloader/DownloaderUrlForm.resx @@ -144,11 +144,123 @@ + + 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 + AAAASUVORK5CYII= \ No newline at end of file diff --git a/SCrawler/Download/STDownloader/DownloaderUrlForm.vb b/SCrawler/Download/STDownloader/DownloaderUrlForm.vb index 2a405cc..dcd4b49 100644 --- a/SCrawler/Download/STDownloader/DownloaderUrlForm.vb +++ b/SCrawler/Download/STDownloader/DownloaderUrlForm.vb @@ -8,6 +8,7 @@ ' but WITHOUT ANY WARRANTY Imports PersonalUtilities.Forms Imports PersonalUtilities.Forms.Controls.Base +Imports ADB = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons Namespace DownloadObjects.STDownloader Friend Class DownloaderUrlForm Private WithEvents MyDefs As DefaultFormOptions @@ -22,6 +23,7 @@ Namespace DownloadObjects.STDownloader .MyViewInitialize(True) .AddOkCancelToolbar() TXT_URL.Text = URL + Settings.DownloadLocations.PopulateComboBox(TXT_PATH) TXT_PATH.Text = Settings.LatestSavingPath.Value If TXT_PATH.Text.IsEmptyString Then TXT_PATH.Text = Application.StartupPath.CSFileP.PathWithSeparator .MyFieldsChecker = New FieldsChecker @@ -31,8 +33,20 @@ Namespace DownloadObjects.STDownloader .EndLoaderOperations() End With .EndLoaderOperations() + .MyOkCancel.EnableOK = True End With End Sub + Private Sub DownloaderUrlForm_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown + Dim b As Boolean = True + If e.KeyCode = Keys.O And e.Control Then + Settings.DownloadLocations.ChooseNewLocation(TXT_PATH, False, Settings.STDownloader_OutputPathAskForName) + ElseIf e.KeyCode = Keys.O And e.Alt Then + Settings.DownloadLocations.ChooseNewLocation(TXT_PATH, True, Settings.STDownloader_OutputPathAskForName) + Else + b = False + End If + If b Then e.Handled = True + End Sub Private Sub MyDefs_ButtonOkClick(ByVal Sender As Object, ByVal e As KeyHandleEventArgs) Handles MyDefs.ButtonOkClick If MyDefs.MyFieldsChecker.AllParamsOK Then URL = TXT_URL.Text @@ -41,10 +55,8 @@ Namespace DownloadObjects.STDownloader End If End Sub Private Sub TXT_PATH_ActionOnButtonClick(ByVal Sender As ActionButton, ByVal e As ActionButtonEventArgs) Handles TXT_PATH.ActionOnButtonClick - If Sender.DefaultButton = ActionButton.DefaultButtons.Open Then - Dim f As SFile = SFile.SelectPath(TXT_PATH.Text.CSFileP, "Select output directory", EDP.ReturnValue) - If Not f.IsEmptyString Then TXT_PATH.Text = f.PathWithSeparator - End If + If Sender.DefaultButton = ADB.Open Or Sender.DefaultButton = ADB.Add Then _ + Settings.DownloadLocations.ChooseNewLocation(TXT_PATH, Sender.DefaultButton = ADB.Add, Settings.STDownloader_OutputPathAskForName) End Sub End Class End Namespace \ No newline at end of file diff --git a/SCrawler/Download/STDownloader/DownloaderUrlsArrForm.Designer.vb b/SCrawler/Download/STDownloader/DownloaderUrlsArrForm.Designer.vb index ffc7698..babbd6f 100644 --- a/SCrawler/Download/STDownloader/DownloaderUrlsArrForm.Designer.vb +++ b/SCrawler/Download/STDownloader/DownloaderUrlsArrForm.Designer.vb @@ -27,8 +27,12 @@ Namespace DownloadObjects.STDownloader 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(DownloaderUrlsArrForm)) 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 ListColumn1 As PersonalUtilities.Forms.Controls.Base.ListColumn = New PersonalUtilities.Forms.Controls.Base.ListColumn() + Dim ListColumn2 As PersonalUtilities.Forms.Controls.Base.ListColumn = New PersonalUtilities.Forms.Controls.Base.ListColumn() Dim FRM_URLS As System.Windows.Forms.GroupBox - Me.TXT_OUTPUT = New PersonalUtilities.Forms.Controls.TextBoxExtended() + Me.TXT_OUTPUT = New PersonalUtilities.Forms.Controls.ComboBoxExtended() Me.TXT_URLS = New System.Windows.Forms.RichTextBox() CONTAINER_MAIN = New System.Windows.Forms.ToolStripContainer() TP_MAIN = New System.Windows.Forms.TableLayoutPanel() @@ -77,18 +81,40 @@ Namespace DownloadObjects.STDownloader ActionButton1.BackgroundImage = CType(resources.GetObject("ActionButton1.BackgroundImage"), System.Drawing.Image) ActionButton1.Name = "Open" ActionButton1.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open + ActionButton1.ToolTipText = "Choose a new location (Ctrl+O)" ActionButton2.BackgroundImage = CType(resources.GetObject("ActionButton2.BackgroundImage"), System.Drawing.Image) - ActionButton2.Name = "Clear" - ActionButton2.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear + ActionButton2.Name = "Add" + ActionButton2.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Add + ActionButton2.ToolTipText = "Choose a new location and add it to the list (Alt+O)" + ActionButton3.BackgroundImage = CType(resources.GetObject("ActionButton3.BackgroundImage"), System.Drawing.Image) + ActionButton3.Name = "Clear" + ActionButton3.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear + ActionButton4.BackgroundImage = CType(resources.GetObject("ActionButton4.BackgroundImage"), System.Drawing.Image) + ActionButton4.Name = "ArrowDown" + ActionButton4.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.ArrowDown Me.TXT_OUTPUT.Buttons.Add(ActionButton1) Me.TXT_OUTPUT.Buttons.Add(ActionButton2) + Me.TXT_OUTPUT.Buttons.Add(ActionButton3) + Me.TXT_OUTPUT.Buttons.Add(ActionButton4) Me.TXT_OUTPUT.CaptionText = "Output path" Me.TXT_OUTPUT.CaptionWidth = 70.0R + ListColumn1.Name = "COL_NAME" + ListColumn1.Text = "Name" + ListColumn1.Width = -1 + ListColumn2.DisplayMember = True + ListColumn2.Name = "COL_VALUE" + ListColumn2.Text = "Value" + ListColumn2.ValueMember = True + ListColumn2.Visible = False + Me.TXT_OUTPUT.Columns.Add(ListColumn1) + Me.TXT_OUTPUT.Columns.Add(ListColumn2) Me.TXT_OUTPUT.Dock = System.Windows.Forms.DockStyle.Fill + Me.TXT_OUTPUT.ListAutoCompleteMode = PersonalUtilities.Forms.Controls.ComboBoxExtended.AutoCompleteModes.Disabled Me.TXT_OUTPUT.Location = New System.Drawing.Point(3, 3) Me.TXT_OUTPUT.Name = "TXT_OUTPUT" Me.TXT_OUTPUT.Size = New System.Drawing.Size(378, 22) Me.TXT_OUTPUT.TabIndex = 0 + Me.TXT_OUTPUT.TextBoxBorderStyle = System.Windows.Forms.BorderStyle.FixedSingle ' 'FRM_URLS ' @@ -117,9 +143,10 @@ Namespace DownloadObjects.STDownloader Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font Me.ClientSize = New System.Drawing.Size(384, 261) Me.Controls.Add(CONTAINER_MAIN) + Me.Icon = Global.SCrawler.My.Resources.Resources.ArrowDownIcon_Blue_24 + Me.KeyPreview = True Me.MinimumSize = New System.Drawing.Size(400, 300) Me.Name = "DownloaderUrlsArrForm" - Me.Icon = Global.SCrawler.My.Resources.ArrowDownIcon_Blue_24 Me.ShowInTaskbar = False Me.Text = "Urls array" CONTAINER_MAIN.ContentPanel.ResumeLayout(False) @@ -129,8 +156,9 @@ Namespace DownloadObjects.STDownloader CType(Me.TXT_OUTPUT, System.ComponentModel.ISupportInitialize).EndInit() FRM_URLS.ResumeLayout(False) Me.ResumeLayout(False) + End Sub - Private WithEvents TXT_OUTPUT As PersonalUtilities.Forms.Controls.TextBoxExtended + Private WithEvents TXT_OUTPUT As PersonalUtilities.Forms.Controls.ComboBoxExtended Private WithEvents TXT_URLS As RichTextBox End Class End Namespace \ No newline at end of file diff --git a/SCrawler/Download/STDownloader/DownloaderUrlsArrForm.resx b/SCrawler/Download/STDownloader/DownloaderUrlsArrForm.resx index fbe3f6b..04fd3f1 100644 --- a/SCrawler/Download/STDownloader/DownloaderUrlsArrForm.resx +++ b/SCrawler/Download/STDownloader/DownloaderUrlsArrForm.resx @@ -136,11 +136,123 @@ + + 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 + AAAASUVORK5CYII= diff --git a/SCrawler/Download/STDownloader/DownloaderUrlsArrForm.vb b/SCrawler/Download/STDownloader/DownloaderUrlsArrForm.vb index 500b26a..b3e5597 100644 --- a/SCrawler/Download/STDownloader/DownloaderUrlsArrForm.vb +++ b/SCrawler/Download/STDownloader/DownloaderUrlsArrForm.vb @@ -8,6 +8,7 @@ ' but WITHOUT ANY WARRANTY Imports PersonalUtilities.Forms Imports PersonalUtilities.Forms.Controls.Base +Imports ADB = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons Namespace DownloadObjects.STDownloader Friend Class DownloaderUrlsArrForm Private WithEvents MyDefs As DefaultFormOptions @@ -25,14 +26,16 @@ Namespace DownloadObjects.STDownloader End If End Get End Property - Friend Sub New() + Friend Sub New(ByVal InitialList As IEnumerable(Of String)) InitializeComponent() MyDefs = New DefaultFormOptions(Me, Settings.Design) + If InitialList.ListExists Then TXT_URLS.Text = InitialList.ListToString(vbNewLine) End Sub Private Sub MyForm_Load(sender As Object, e As EventArgs) Handles Me.Load With MyDefs .MyViewInitialize() .AddOkCancelToolbar() + Settings.DownloadLocations.PopulateComboBox(TXT_OUTPUT) TXT_OUTPUT.Text = Settings.LatestSavingPath.Value.PathWithSeparator If TXT_OUTPUT.Text.IsEmptyString Then TXT_OUTPUT.Text = Application.StartupPath.CSFileP.PathWithSeparator .MyFieldsChecker = New FieldsChecker @@ -41,17 +44,26 @@ Namespace DownloadObjects.STDownloader .EndLoaderOperations() End With .EndLoaderOperations() + .MyOkCancel.EnableOK = True End With End Sub + Private Sub DownloaderUrlsArrForm_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown + Dim b As Boolean = True + If e.KeyCode = Keys.O And e.Control Then + Settings.DownloadLocations.ChooseNewLocation(TXT_OUTPUT, False, Settings.STDownloader_OutputPathAskForName) + ElseIf e.KeyCode = Keys.O And e.Alt Then + Settings.DownloadLocations.ChooseNewLocation(TXT_OUTPUT, True, Settings.STDownloader_OutputPathAskForName) + Else + b = False + End If + If b Then e.Handled = True + End Sub Private Sub MyDefs_ButtonOkClick(ByVal Sender As Object, ByVal e As KeyHandleEventArgs) Handles MyDefs.ButtonOkClick If MyDefs.MyFieldsChecker.AllParamsOK Then MyDefs.CloseForm() End Sub Private Sub TXT_OUTPUT_ActionOnButtonClick(ByVal Sender As Object, ByVal e As ActionButtonEventArgs) Handles TXT_OUTPUT.ActionOnButtonClick - If e.DefaultButton = ActionButton.DefaultButtons.Open Then - Dim f As SFile = TXT_OUTPUT.Text.CSFileP - f = SFile.SelectPath(f, "Select a folder for files", EDP.ReturnValue) - If Not f.IsEmptyString Then TXT_OUTPUT.Text = f.PathWithSeparator - End If + If Sender.DefaultButton = ADB.Open Or Sender.DefaultButton = ADB.Add Then _ + Settings.DownloadLocations.ChooseNewLocation(TXT_OUTPUT, Sender.DefaultButton = ADB.Add, Settings.STDownloader_OutputPathAskForName) End Sub End Class End Namespace \ No newline at end of file diff --git a/SCrawler/Download/STDownloader/VideoDownloaderForm.vb b/SCrawler/Download/STDownloader/VideoDownloaderForm.vb index 813c339..8045573 100644 --- a/SCrawler/Download/STDownloader/VideoDownloaderForm.vb +++ b/SCrawler/Download/STDownloader/VideoDownloaderForm.vb @@ -13,20 +13,33 @@ Imports PersonalUtilities.Forms.Controls.KeyClick Namespace DownloadObjects.STDownloader Friend Class VideoDownloaderForm Private WithEvents BTT_ADD_URLS_ARR As ToolStripMenuItemKeyClick + Private WithEvents BTT_ADD_URLS_EXTERNAL As ToolStripMenuItemKeyClick Private Const UrlsArrTag As String = "URLS_ARR" + Private Const TAG_EXTERNAL As String = "EXTERNAL" Private ReadOnly ControlNonYT As New FPredicate(Of MediaItem)(Function(i) Not i.MyContainer.SiteKey = API.YouTube.YouTubeSiteKey) Private ReadOnly ControlsDownloadedNonYT As New FPredicate(Of MediaItem)(Function(i) i.MyContainer.MediaState = Plugin.UserMediaStates.Downloaded And ControlNonYT.Invoke(i)) + Private ReadOnly Property ExternalUrlsTemp As List(Of String) Public Sub New() InitializeComponent() + ExternalUrlsTemp = New List(Of String) AppMode = False Icon = My.Resources.ArrowDownIcon_Blue_24 BTT_ADD_PLS_ARR.Text = $"YouTube: {BTT_ADD_PLS_ARR.Text}" BTT_ADD_NO_SHORTS.Text = $"YouTube: {BTT_ADD_NO_SHORTS.Text}" BTT_ADD_SHORTS_ONLY.Text = $"YouTube: {BTT_ADD_SHORTS_ONLY.Text}" BTT_ADD_URLS_ARR = New ToolStripMenuItemKeyClick("Add an array of URLs", PersonalUtilities.My.Resources.PlusPic_Green_24) With {.Tag = UrlsArrTag} + BTT_ADD_URLS_EXTERNAL = New ToolStripMenuItemKeyClick With {.Tag = TAG_EXTERNAL} MENU_ADD.DropDownItems.Insert(1, BTT_ADD_URLS_ARR) Text = "Video downloader" End Sub + Protected Overrides Sub VideoListForm_Disposed(sender As Object, e As EventArgs) + ExternalUrlsTemp.Clear() + MyBase.VideoListForm_Disposed(sender, e) + End Sub + Friend Sub ADD_URLS_EXTERNAL(ByVal UrlsList As IEnumerable(Of String)) + ExternalUrlsTemp.ListAddList(UrlsList, LAP.ClearBeforeAdd, LAP.NotContainsOnly) + If ExternalUrlsTemp.Count > 0 Then BTT_ADD_URLS_EXTERNAL.PerformClick() + End Sub Protected Overrides Function LoadData_GetFiles() As List(Of IYouTubeMediaContainer) Try Dim l As List(Of IYouTubeMediaContainer) = Nothing @@ -43,17 +56,19 @@ Namespace DownloadObjects.STDownloader Return ErrorsDescriber.Execute(EDP.SendToLog, ex, "VideoListForm.LoadData_GetFiles", New List(Of IYouTubeMediaContainer)) End Try End Function - Protected Overrides Sub BTT_ADD_KeyClick(ByVal Sender As ToolStripMenuItemKeyClick, ByVal e As KeyClickEventArgs) Handles BTT_ADD_URLS_ARR.KeyClick + Protected Overrides Sub BTT_ADD_KeyClick(ByVal Sender As ToolStripMenuItemKeyClick, ByVal e As KeyClickEventArgs) Handles BTT_ADD_URLS_ARR.KeyClick, + BTT_ADD_URLS_EXTERNAL.KeyClick Dim __tag$ = UniversalFunctions.IfNullOrEmpty(Of Object)(Sender.Tag, String.Empty) - If Not __tag = "a" And Not __tag = UrlsArrTag Then + If Not __tag = "a" And Not __tag = UrlsArrTag And Not __tag = TAG_EXTERNAL Then MyBase.BTT_ADD_KeyClick(Sender, e) Else Dim url$ = String.Empty Try - url = BufferText + Dim isExternal As Boolean = __tag = TAG_EXTERNAL + If Not isExternal Then url = BufferText Dim disableDown As Boolean = e.Shift Dim output As SFile = Settings.LatestSavingPath - Dim isArr As Boolean = __tag = UrlsArrTag + Dim isArr As Boolean = (__tag = UrlsArrTag Or (isExternal And ExternalUrlsTemp.Count > 1)) Dim formOpened As Boolean = False Dim media As IYouTubeMediaContainer Dim formValues As Func(Of DialogResult) = Function() As DialogResult @@ -67,6 +82,8 @@ Namespace DownloadObjects.STDownloader Settings.LatestSavingPath.Value = output If Settings.STDownloader_UpdateYouTubeOutputPath Then _ API.YouTube.MyYouTubeSettings.OutputPath.Value = output + If Settings.STDownloader_OutputPathAutoAddPaths Then _ + Settings.DownloadLocations.Add(output, False) Return DialogResult.OK Else Return DialogResult.Cancel @@ -103,22 +120,26 @@ Namespace DownloadObjects.STDownloader If output.IsEmptyString Then output = API.YouTube.MyYouTubeSettings.OutputPath If isArr Then - Dim urls As List(Of String) + Dim urls As List(Of String) = Nothing Dim cntAdded As Boolean = False - Using fa As New DownloaderUrlsArrForm + If isExternal Then urls = New List(Of String)(ExternalUrlsTemp) + Using fa As New DownloaderUrlsArrForm(urls) fa.ShowDialog() If fa.DialogResult = DialogResult.OK Then urls = fa.URLs.ToList output = fa.OutputPath + If Settings.STDownloader_UpdateYouTubeOutputPath Then API.YouTube.MyYouTubeSettings.OutputPath.Value = output + If Settings.STDownloader_OutputPathAutoAddPaths Then Settings.DownloadLocations.Add(output, False) Else Exit Sub End If End Using If urls.ListExists Then urls.ListForEach(Function(uu, ii) uu.StringTrim,, False) - urls.RemoveAll(Function(uu) url.IsEmptyString OrElse Not url.StartsWith("http") OrElse Not canProcessUrl(uu, False)) + urls.RemoveAll(Function(uu) uu.IsEmptyString OrElse Not uu.StartsWith("http") OrElse Not canProcessUrl(uu, False)) End If If urls.ListExists Then + output.Exists(SFO.Path, True) For Each url In urls If Not TryYouTube.Invoke Then media = FindSource(url, output) @@ -131,6 +152,7 @@ Namespace DownloadObjects.STDownloader MsgBoxE({"There are no valid URLs in the list", "Add URLs array"}, vbCritical) End If Else + If isExternal Then url = ExternalUrlsTemp.FirstOrDefault If formValues.Invoke = DialogResult.Cancel Then Exit Sub If canProcessUrl(url, True) Then If TryYouTube.Invoke Then Exit Sub @@ -153,11 +175,14 @@ Namespace DownloadObjects.STDownloader If media Is Nothing Then MsgBoxE({$"The URL you entered is not recognized by existing plugins.{vbCr}{url}", "Download video"}, vbCritical) Else + output.Exists(SFO.Path, True) ControlCreateAndAdd(media, disableDown) End If End If Catch ex As Exception ErrorsDescriber.Execute(EDP.LogMessageValue, ex, $"Error when trying to download video from URL: [{url}]") + Finally + ExternalUrlsTemp.Clear() End Try End If End Sub @@ -181,14 +206,14 @@ Namespace DownloadObjects.STDownloader If Settings.STDownloader_RemoveYTVideosOnClear Then MyBase.BTT_CLEAR_DONE_Click(sender, e) Else - RemoveControls(ControlsDownloadedNonYT) + RemoveControls(ControlsDownloadedNonYT, False) End If End Sub Protected Overrides Sub BTT_CLEAR_ALL_Click(sender As Object, e As EventArgs) If Settings.STDownloader_RemoveYTVideosOnClear Then MyBase.BTT_CLEAR_ALL_Click(sender, e) Else - RemoveControls(ControlNonYT) + RemoveControls(ControlNonYT, False) End If End Sub Protected Overrides Sub MyJob_Finished(ByVal Sender As Object, ByVal e As EventArgs) diff --git a/SCrawler/Download/TDownloader.vb b/SCrawler/Download/TDownloader.vb index 6976014..c941801 100644 --- a/SCrawler/Download/TDownloader.vb +++ b/SCrawler/Download/TDownloader.vb @@ -22,6 +22,7 @@ Namespace DownloadObjects Friend Event SendNotification As NotificationEventHandler Friend Event Reconfigured() Friend Event FeedFilesChanged(ByVal Added As Boolean) + Friend Event UserDownloadStateChanged As UserDownloadStateChangedEventHandler #End Region #Region "Declarations" #Region "Files" @@ -124,6 +125,8 @@ Namespace DownloadObjects End Try End Sub #End Region + Friend ReadOnly Property ActiveDownloading As List(Of IUserData) + Friend Property QueueFormOpening As Boolean = False Friend ReadOnly Property Downloaded As List(Of IUserData) Private ReadOnly NProv As IFormatProvider #End Region @@ -272,6 +275,7 @@ Namespace DownloadObjects #Region "Initializer" Friend Sub New() Files = New List(Of UserMediaD) + ActiveDownloading = New List(Of IUserData) Downloaded = New List(Of IUserData) NProv = New ANumbers With {.FormatOptions = ANumbers.Options.GroupIntegral} Pool = New List(Of Job) @@ -406,6 +410,9 @@ Namespace DownloadObjects Dim Keys As New List(Of String) Dim h As Boolean = False Dim host As SettingsHost = Nothing + Dim waitQueueForm As Action = Sub() + While QueueFormOpening : Thread.Sleep(100) : End While + End Sub For Each _Item As IUserData In _Job.Items If Not _Item.Disposed Then Keys.Add(_Item.Key) @@ -413,8 +420,11 @@ Namespace DownloadObjects If host.Source.ReadyToDownload(Download.Main) Then host.BeforeStartDownload(_Item, Download.Main) _Job.ThrowIfCancellationRequested() + waitQueueForm.Invoke DirectCast(_Item, UserDataBase).Progress = _Job.Progress t.Add(Task.Run(Sub() _Item.DownloadData(Token))) + ActiveDownloading.Add(_Item) + RaiseEvent UserDownloadStateChanged(_Item, True) i += 1 If i >= limit Then Exit For End If @@ -431,11 +441,14 @@ Namespace DownloadObjects Dim dcc As Boolean = False If Keys.Count > 0 Then For Each k$ In Keys + waitQueueForm.Invoke i = _Job.Items.FindIndex(Function(ii) ii.Key = k) If i >= 0 Then With _Job.Items(i) If DirectCast(.Self, UserDataBase).ContentMissingExists Then MissingPostsDetected = True - host.AfterDownload(_Job.Items(i), Download.Main) + If ActiveDownloading.Count > 0 AndAlso ActiveDownloading.Contains(.Self) Then ActiveDownloading.Remove(.Self) + RaiseEvent UserDownloadStateChanged(.Self, False) + host.AfterDownload(.Self, Download.Main) If Not .Disposed AndAlso Not .IsCollection AndAlso .DownloadedTotal(False) > 0 Then If Not Downloaded.Contains(.Self) Then Downloaded.Add(Settings.GetUser(.Self)) With DirectCast(.Self, UserDataBase) @@ -518,6 +531,7 @@ Namespace DownloadObjects [Stop]() Pool.ListClearDispose Files.Clear() + ActiveDownloading.Clear() Downloaded.Clear() End If disposedValue = True diff --git a/SCrawler/Download/UserDownloadQueueForm.Designer.vb b/SCrawler/Download/UserDownloadQueueForm.Designer.vb new file mode 100644 index 0000000..3148764 --- /dev/null +++ b/SCrawler/Download/UserDownloadQueueForm.Designer.vb @@ -0,0 +1,66 @@ +' Copyright (C) 2023 Andy https://github.com/AAndyProgram +' 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 UserDownloadQueueForm : 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() + Dim TOOLBAR_BOTTOM As System.Windows.Forms.StatusStrip + Me.LIST_QUEUE = New System.Windows.Forms.ListBox() + TOOLBAR_BOTTOM = New System.Windows.Forms.StatusStrip() + Me.SuspendLayout() + ' + 'TOOLBAR_BOTTOM + ' + TOOLBAR_BOTTOM.Location = New System.Drawing.Point(0, 189) + TOOLBAR_BOTTOM.Name = "TOOLBAR_BOTTOM" + TOOLBAR_BOTTOM.Size = New System.Drawing.Size(284, 22) + TOOLBAR_BOTTOM.TabIndex = 1 + TOOLBAR_BOTTOM.Text = "StatusStrip1" + ' + 'LIST_QUEUE + ' + Me.LIST_QUEUE.Dock = System.Windows.Forms.DockStyle.Fill + Me.LIST_QUEUE.FormattingEnabled = True + Me.LIST_QUEUE.Location = New System.Drawing.Point(0, 0) + Me.LIST_QUEUE.Name = "LIST_QUEUE" + Me.LIST_QUEUE.Size = New System.Drawing.Size(284, 189) + Me.LIST_QUEUE.TabIndex = 0 + ' + 'UserDownloadQueueForm + ' + Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!) + Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font + Me.ClientSize = New System.Drawing.Size(284, 211) + Me.Controls.Add(Me.LIST_QUEUE) + Me.Controls.Add(TOOLBAR_BOTTOM) + Me.Icon = Global.SCrawler.My.Resources.Resources.ArrowDownIcon_Blue_24 + Me.KeyPreview = True + Me.MinimumSize = New System.Drawing.Size(300, 250) + Me.Name = "UserDownloadQueueForm" + Me.Text = "User download queue" + Me.ResumeLayout(False) + Me.PerformLayout() + + End Sub + + Private WithEvents LIST_QUEUE As ListBox + End Class +End Namespace \ No newline at end of file diff --git a/SCrawler/Download/UserDownloadQueueForm.resx b/SCrawler/Download/UserDownloadQueueForm.resx new file mode 100644 index 0000000..c9c1d89 --- /dev/null +++ b/SCrawler/Download/UserDownloadQueueForm.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + 17, 17 + + \ No newline at end of file diff --git a/SCrawler/Download/UserDownloadQueueForm.vb b/SCrawler/Download/UserDownloadQueueForm.vb new file mode 100644 index 0000000..ca4ed96 --- /dev/null +++ b/SCrawler/Download/UserDownloadQueueForm.vb @@ -0,0 +1,180 @@ +' Copyright (C) 2023 Andy https://github.com/AAndyProgram +' 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 System.ComponentModel +Imports SCrawler.API.Base +Imports PersonalUtilities.Forms +Namespace DownloadObjects + Friend Class UserDownloadQueueForm + Private ReadOnly MyVew As FormView + Private ReadOnly Tokens As List(Of CancellationTokenSource) + Private Structure ListUser + Friend ReadOnly User As UserDataBase + Friend IsDownloading As Boolean + Private ReadOnly _UserString As String + Private ReadOnly Property UserString As String + Get + Return $"[{IIf(IsDownloading, "-", "+")}] {_UserString}" + End Get + End Property + Friend ReadOnly Key As String + Friend Sub New(ByVal _User As IUserData) + User = _User + Key = _User.Key + IsDownloading = True + _UserString = DirectCast(User, UserDataBase).ToStringForLog() + If Not User.FriendlyName.IsEmptyString Then _UserString &= $" ({User.FriendlyName})" + End Sub + Public Shared Widening Operator CType(ByVal _User As UserDataBase) As ListUser + Return New ListUser(_User) + End Operator + Public Shared Widening Operator CType(ByVal _User As ListUser) As String + Return _User.ToString + End Operator + Public Overrides Function ToString() As String + Return UserString + End Function + Public Overrides Function Equals(ByVal Obj As Object) As Boolean + Try : Return Not IsNothing(Obj) AndAlso TypeOf Obj Is ListUser AndAlso Key.Equals(CType(Obj, ListUser).Key) : Catch : End Try + Return False + End Function + End Structure + Public Sub New() + InitializeComponent() + MyVew = New FormView(Me, Settings.Design) + Tokens = New List(Of CancellationTokenSource) + End Sub + Private Sub UserDownloadQueueForm_Load(sender As Object, e As EventArgs) Handles Me.Load + Try + MyVew.Import() + MyVew.SetFormSize() + With Downloader + .QueueFormOpening = True + If .ActiveDownloading.Count > 0 Then + For Each user As UserDataBase In .ActiveDownloading + ApplyHandlers(user, user.DownloadInProgress) + LIST_QUEUE.Items.Add(New ListUser(user)) + Next + End If + AddHandler .UserDownloadStateChanged, AddressOf Downloader_UserDownloadStateChanged + AddHandler .Downloading, AddressOf Downloader_Downloading + .QueueFormOpening = False + End With + Catch aoutex As ArgumentOutOfRangeException + Catch iex As IndexOutOfRangeException + Catch ex As Exception + ErrorsDescriber.Execute(EDP.SendToLog + EDP.ShowMainMsg, ex, "Error when opening user download queue form") + Finally + Downloader.QueueFormOpening = False + End Try + End Sub + Private Sub UserDownloadQueueForm_Closing(sender As Object, e As CancelEventArgs) Handles Me.Closing + e.Cancel = True + Hide() + End Sub + Private Sub UserDownloadQueueForm_Disposed(sender As Object, e As EventArgs) Handles Me.Disposed + MyVew.Dispose() + Tokens.ListClearDispose + End Sub + Private Sub UserDownloadQueueForm_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown + Dim b As Boolean = True + If e.KeyCode = Keys.Delete Then + CancelUserDownload() + ElseIf e.KeyCode = Keys.F And e.Control Then + FindUser() + Else + b = False + End If + If b Then e.Handled = True + End Sub + Private Sub Downloader_Downloading(ByVal Value As Boolean) + ControlInvokeFast(LIST_QUEUE, Sub() If Not Value Then LIST_QUEUE.Items.Clear() : Tokens.ListClearDispose, EDP.None) + End Sub + Private Async Sub Downloader_UserDownloadStateChanged(ByVal User As IUserData, ByVal IsDownloading As Boolean) + Await Task.Run(Sub() + Try + ControlInvokeFast(LIST_QUEUE, Sub() + Dim u As New ListUser(User) + ApplyHandlers(User, IsDownloading) + If IsDownloading Then + LIST_QUEUE.Items.Add(u) + Else + LIST_QUEUE.Items.Remove(u) + End If + LIST_QUEUE.Refresh() + End Sub) + Catch ex As Exception + End Try + End Sub) + End Sub + Private Async Sub User_UserDownloadStateChanged(ByVal User As IUserData, ByVal IsDownloading As Boolean) + Await Task.Run(Sub() + Try + ControlInvokeFast(LIST_QUEUE, + Sub() + Dim lu As New ListUser(User) + Dim i% = LIST_QUEUE.Items.IndexOf(lu) + If i >= 0 Then + lu = LIST_QUEUE.Items(i) + If Not lu.User Is Nothing And Not lu.IsDownloading = IsDownloading Then + lu.IsDownloading = IsDownloading + LIST_QUEUE.Items(i) = lu + LIST_QUEUE.Refresh() + End If + End If + End Sub) + Catch + End Try + End Sub) + End Sub + Private Sub ApplyHandlers(ByVal User As IUserData, ByVal IsDownloading As Boolean) + Try + If Not User Is Nothing Then + With DirectCast(User, UserDataBase) + If IsDownloading Then + AddHandler .UserDownloadStateChanged, AddressOf User_UserDownloadStateChanged + Else + RemoveHandler .UserDownloadStateChanged, AddressOf User_UserDownloadStateChanged + End If + End With + End If + Catch + End Try + End Sub + Private Sub CancelUserDownload() + Const msgTitle$ = "Stop user download" + Try + Dim lu As ListUser = GetUserSelectedUser() + If Not lu.User Is Nothing AndAlso + MsgBoxE({$"Are you sure you want to stop downloading the following user?{vbCr}{lu}", msgTitle}, vbExclamation + vbYesNo) = vbYes Then + Dim token As New CancellationTokenSource + lu.User.PersonalToken = token.Token + token.Cancel() + Tokens.Add(token) + MsgBoxE({"Cancel user download processed.", msgTitle}) + End If + Catch ex As Exception + End Try + End Sub + Private Sub FindUser() + Try + MainFrameObj.FocusUser(GetUserSelectedUser().Key, True) + Catch ex As Exception + End Try + End Sub + Private Function GetUserSelectedUser() As ListUser + Dim lu As ListUser = Nothing + ControlInvokeFast(LIST_QUEUE, Sub() + Dim sIndx% = LIST_QUEUE.SelectedIndex + If sIndx >= 0 Then lu = LIST_QUEUE.Items(sIndx) + End Sub) + Return lu + End Function + End Class +End Namespace \ No newline at end of file diff --git a/SCrawler/Editors/CollectionEditorForm.Designer.vb b/SCrawler/Editors/CollectionEditorForm.Designer.vb index c091daa..5035e9a 100644 --- a/SCrawler/Editors/CollectionEditorForm.Designer.vb +++ b/SCrawler/Editors/CollectionEditorForm.Designer.vb @@ -26,6 +26,7 @@ Namespace Editors 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(CollectionEditorForm)) 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() Me.CMB_COLLECTIONS = New PersonalUtilities.Forms.Controls.ComboBoxExtended() CONTAINER_MAIN = New System.Windows.Forms.ToolStripContainer() CONTAINER_MAIN.ContentPanel.SuspendLayout() @@ -56,11 +57,17 @@ Namespace Editors ActionButton1.Name = "Add" ActionButton1.ToolTipText = "Add new collection" ActionButton2.BackgroundImage = CType(resources.GetObject("ActionButton2.BackgroundImage"), System.Drawing.Image) - ActionButton2.Name = "ArrowDown" - ActionButton2.Visible = False + ActionButton2.Name = "Open" + ActionButton2.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open + ActionButton2.ToolTipText = "Choose a different destination for the new collection (Ctrl+O)" + ActionButton3.BackgroundImage = CType(resources.GetObject("ActionButton3.BackgroundImage"), System.Drawing.Image) + ActionButton3.Name = "ArrowDown" + ActionButton3.Visible = False Me.CMB_COLLECTIONS.Buttons.Add(ActionButton1) Me.CMB_COLLECTIONS.Buttons.Add(ActionButton2) + Me.CMB_COLLECTIONS.Buttons.Add(ActionButton3) Me.CMB_COLLECTIONS.Dock = System.Windows.Forms.DockStyle.Fill + Me.CMB_COLLECTIONS.Lines = New String(-1) {} Me.CMB_COLLECTIONS.ListDropDownStyle = PersonalUtilities.Forms.Controls.ComboBoxExtended.ListMode.Simple Me.CMB_COLLECTIONS.Location = New System.Drawing.Point(2, 0) Me.CMB_COLLECTIONS.Name = "CMB_COLLECTIONS" diff --git a/SCrawler/Editors/CollectionEditorForm.resx b/SCrawler/Editors/CollectionEditorForm.resx index c30579c..669ae0e 100644 --- a/SCrawler/Editors/CollectionEditorForm.resx +++ b/SCrawler/Editors/CollectionEditorForm.resx @@ -144,6 +144,17 @@ + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO + wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP + WQwhyWIyJIUW5NqyPb7oCVtIlhVTwYf8nv7/t2zJagel9KmqKsIACYL9RjI8UHz5zshougZr/AEvbxEP + aZCDBY3VslixaJvX3wzkkDiOwbZtDRGA5vdNAg+TL27qgmt5XkBG/gTdAG7Gt+3PP9oOaEGFCVEC6rp+ + 5g9MfM/c5e4OsEZMZkQEtGL5H2DdZ5JRArDwPA+iKII0TfkC9vroC9j5vq8JTWw3WzWgLMtZGIaa0MR8 + vlAD8PYlSaIJTTiOowY0p0Bc19XEJo6HE59FAPuMzyAINKGJ1XLFZxHALtMrnkBXOIQIIIQ8YvF/KrgB + cMaRN0UdBBkAAAAASUVORK5CYII= + + + iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAABGdBTUEAALGPC/xhBQAAE65JREFUeF7t 3X2sJWddB/DdLi2lQG2hdOHuvfM887J7Cxca4ELTQMDWKigIFpBAEAgi9g+CJpJo9Q8NJhgBiYZIYspL diff --git a/SCrawler/Editors/CollectionEditorForm.vb b/SCrawler/Editors/CollectionEditorForm.vb index cbe0d35..450f7e4 100644 --- a/SCrawler/Editors/CollectionEditorForm.vb +++ b/SCrawler/Editors/CollectionEditorForm.vb @@ -8,15 +8,24 @@ ' but WITHOUT ANY WARRANTY Imports PersonalUtilities.Forms Imports PersonalUtilities.Forms.Controls.Base +Imports ADB = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons Namespace Editors Friend Class CollectionEditorForm Private WithEvents MyDefs As DefaultFormOptions Private ReadOnly Collections As List(Of String) - Friend Property [Collection] As String = String.Empty + Friend Property MyCollection As String = String.Empty + Private _MyCollectionSpecialPath As SFile = Nothing + Friend ReadOnly Property MyCollectionSpecialPath As SFile + Get + Return _MyCollectionSpecialPath + End Get + End Property Friend Sub New() InitializeComponent() MyDefs = New DefaultFormOptions(Me, Settings.Design) Collections = New List(Of String) + Icon = PersonalUtilities.Tools.ImageRenderer.GetIcon(My.Resources.DBPic_32, EDP.ReturnValue) + If Not Icon Is Nothing Then ShowIcon = True End Sub Private Sub CollectionEditorForm_Load(sender As Object, e As EventArgs) Handles Me.Load Try @@ -27,7 +36,7 @@ Namespace Editors Dim ecol As List(Of String) = ListAddList(Nothing, (From c In Settings.Users Where c.IsCollection Select c.CollectionName), LAP.NotContainsOnly) If ecol.ListExists Then ecol.Sort() : Collections.ListAddList(ecol, LAP.NotContainsOnly) : ecol.Clear() If Collections.Count > 0 Then CMB_COLLECTIONS.Items.AddRange(Collections.Select(Function(c) New ListItem(c))) - If Not Collection.IsEmptyString And Collections.Contains(Collection) Then CMB_COLLECTIONS.SelectedIndex = Collections.IndexOf(Collection) + If Not MyCollection.IsEmptyString And Collections.Contains(MyCollection) Then CMB_COLLECTIONS.SelectedIndex = Collections.IndexOf(MyCollection) .DelegateClosingChecker = False .EndLoaderOperations() End With @@ -39,29 +48,41 @@ Namespace Editors Collections.Clear() End Sub Private Sub CollectionEditorForm_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown - If e.KeyCode = Keys.Insert Then AddNewCollection() : e.Handled = True Else e.Handled = False + If e.KeyCode = Keys.Insert Or (e.KeyCode = Keys.O And e.Control) Then AddNewCollection(Not e.KeyCode = Keys.Insert) : e.Handled = True End Sub Private Sub MyDefs_ButtonOkClick() Handles MyDefs.ButtonOkClick If CMB_COLLECTIONS.SelectedIndex >= 0 Then - Collection = CMB_COLLECTIONS.Value.ToString + MyCollection = CMB_COLLECTIONS.Value.ToString With Settings.LastCollections - If .Contains(Collection) Then .Remove(Collection) - If .Count = 0 Then .Add(Collection) Else .Insert(0, Collection) + If .Contains(MyCollection) Then .Remove(MyCollection) + If .Count = 0 Then .Add(MyCollection) Else .Insert(0, MyCollection) End With MyDefs.CloseForm() Else MsgBoxE("Collection not selected", MsgBoxStyle.Exclamation) End If End Sub - Private Sub CMB_COLLECTIONS_ActionOnButtonClick(ByVal Sender As ActionButton, ByVal e As EventArgs) Handles CMB_COLLECTIONS.ActionOnButtonClick - If Sender.DefaultButton = ActionButton.DefaultButtons.Add Then AddNewCollection() + Private Sub CMB_COLLECTIONS_ActionOnButtonClick(ByVal Sender As ActionButton, ByVal e As ActionButtonEventArgs) Handles CMB_COLLECTIONS.ActionOnButtonClick + If e.DefaultButton = ADB.Add Or e.DefaultButton = ADB.Open Then AddNewCollection(e.DefaultButton = ADB.Open) End Sub Private Sub CMB_COLLECTIONS_ActionOnListDoubleClick(ByVal Sender As Object, ByVal e As EventArgs, ByVal Item As ListViewItem) Handles CMB_COLLECTIONS.ActionOnListDoubleClick Item.Selected = True MyDefs_ButtonOkClick() End Sub - Private Sub AddNewCollection() - Dim c$ = InputBoxE("Enter new collection name:", "Collection name") + Private Sub AddNewCollection(ByVal OpenMode As Boolean) + Dim c$ = String.Empty + If OpenMode Then + Using f As New GlobalLocationsChooserForm With {.MyIsCollectionSelector = True} + f.ShowDialog() + If f.DialogResult = DialogResult.OK Then + c = f.MyCollectionName + _MyCollectionSpecialPath = $"{f.MyDestination.Path.CSFilePS}{c}\" + End If + End Using + Else + _MyCollectionSpecialPath = Nothing + c = InputBoxE("Enter new collection name:", "Collection name") + End If If Not c.IsEmptyString Then If Not Collections.Contains(c) Then Collections.Add(c) @@ -69,7 +90,11 @@ Namespace Editors CMB_COLLECTIONS.SelectedIndex = CMB_COLLECTIONS.Count - 1 Else Dim i% = Collections.IndexOf(c) - If i >= 0 Then CMB_COLLECTIONS.SelectedIndex = i + If i >= 0 Then + CMB_COLLECTIONS.SelectedIndex = i + _MyCollectionSpecialPath = Settings.UsersList.FirstOrDefault(Function(u) u.CollectionName = c).SpecialCollectionPath + MsgBoxE({$"The '{c}' collection already exists", "Add a new collection"}, vbExclamation) + End If End If End If End Sub diff --git a/SCrawler/Editors/ColorPicker.vb b/SCrawler/Editors/ColorPicker.vb index 6d1226f..a318dc3 100644 --- a/SCrawler/Editors/ColorPicker.vb +++ b/SCrawler/Editors/ColorPicker.vb @@ -92,10 +92,18 @@ Namespace Editors ForeColorImpl = Nothing End If End Sub + Friend Sub ColorsSetUser(ByVal b As Color?, ByVal f As Color?) + BackColorImpl = b + ForeColorImpl = f + End Sub Friend Sub ColorsGet(ByRef b As XMLValue(Of Color), ByRef f As XMLValue(Of Color)) If BackColorImpl.HasValue Then b.Value = BackColorImpl.Value Else b.ValueF = Nothing If ForeColorImpl.HasValue Then f.Value = ForeColorImpl.Value Else f.ValueF = Nothing End Sub + Friend Sub ColorsGetUser(ByRef b As Color?, ByRef f As Color?) + b = BackColorImpl + f = ForeColorImpl + End Sub #End Region #Region "Buttons handlers" Private Sub COLOR_BUTTONS_Click(ByVal Sender As Button, ByVal e As EventArgs) Handles BTT_COLORS_BACK.Click, diff --git a/SCrawler/Editors/GlobalLocationsChooserForm.Designer.vb b/SCrawler/Editors/GlobalLocationsChooserForm.Designer.vb new file mode 100644 index 0000000..19f2b07 --- /dev/null +++ b/SCrawler/Editors/GlobalLocationsChooserForm.Designer.vb @@ -0,0 +1,227 @@ +' Copyright (C) 2023 Andy https://github.com/AAndyProgram +' 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 Editors + + Partial Friend Class GlobalLocationsChooserForm : 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() + 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(GlobalLocationsChooserForm)) + 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 TP_LOCATIONS_USER As System.Windows.Forms.TableLayoutPanel + Dim ActionButton4 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() + Me.TP_MAIN = New System.Windows.Forms.TableLayoutPanel() + Me.CMB_LOCATIONS = New PersonalUtilities.Forms.Controls.ComboBoxExtended() + Me.FRM_LOCATIONS = New System.Windows.Forms.GroupBox() + Me.OPT_LOCATION_1 = New System.Windows.Forms.RadioButton() + Me.OPT_LOCATION_2 = New System.Windows.Forms.RadioButton() + Me.OPT_LOCATION_3 = New System.Windows.Forms.RadioButton() + Me.TXT_COL_NAME = New PersonalUtilities.Forms.Controls.TextBoxExtended() + CONTAINER_MAIN = New System.Windows.Forms.ToolStripContainer() + TP_LOCATIONS_USER = New System.Windows.Forms.TableLayoutPanel() + CONTAINER_MAIN.ContentPanel.SuspendLayout() + CONTAINER_MAIN.SuspendLayout() + Me.TP_MAIN.SuspendLayout() + CType(Me.CMB_LOCATIONS, System.ComponentModel.ISupportInitialize).BeginInit() + Me.FRM_LOCATIONS.SuspendLayout() + TP_LOCATIONS_USER.SuspendLayout() + CType(Me.TXT_COL_NAME, System.ComponentModel.ISupportInitialize).BeginInit() + Me.SuspendLayout() + ' + 'CONTAINER_MAIN + ' + ' + 'CONTAINER_MAIN.ContentPanel + ' + CONTAINER_MAIN.ContentPanel.Controls.Add(Me.TP_MAIN) + CONTAINER_MAIN.ContentPanel.Size = New System.Drawing.Size(584, 251) + 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(584, 251) + CONTAINER_MAIN.TabIndex = 0 + CONTAINER_MAIN.TopToolStripPanelVisible = False + ' + 'TP_MAIN + ' + Me.TP_MAIN.ColumnCount = 1 + Me.TP_MAIN.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) + Me.TP_MAIN.Controls.Add(Me.CMB_LOCATIONS, 0, 0) + Me.TP_MAIN.Controls.Add(Me.FRM_LOCATIONS, 0, 2) + Me.TP_MAIN.Controls.Add(Me.TXT_COL_NAME, 0, 1) + Me.TP_MAIN.Dock = System.Windows.Forms.DockStyle.Fill + Me.TP_MAIN.Location = New System.Drawing.Point(0, 0) + Me.TP_MAIN.Name = "TP_MAIN" + Me.TP_MAIN.RowCount = 3 + Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) + Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) + Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) + Me.TP_MAIN.Size = New System.Drawing.Size(584, 251) + Me.TP_MAIN.TabIndex = 0 + ' + 'CMB_LOCATIONS + ' + ActionButton1.BackgroundImage = CType(resources.GetObject("ActionButton1.BackgroundImage"), System.Drawing.Image) + ActionButton1.Name = "Open" + ActionButton1.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open + ActionButton1.ToolTipText = "Choose a new location (Ctrl+O)" + ActionButton2.BackgroundImage = CType(resources.GetObject("ActionButton2.BackgroundImage"), System.Drawing.Image) + ActionButton2.Name = "Clear" + ActionButton2.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear + ActionButton3.BackgroundImage = CType(resources.GetObject("ActionButton3.BackgroundImage"), System.Drawing.Image) + ActionButton3.Name = "ArrowDown" + ActionButton3.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.ArrowDown + Me.CMB_LOCATIONS.Buttons.Add(ActionButton1) + Me.CMB_LOCATIONS.Buttons.Add(ActionButton2) + Me.CMB_LOCATIONS.Buttons.Add(ActionButton3) + Me.CMB_LOCATIONS.CaptionChecked = True + Me.CMB_LOCATIONS.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.CheckBox + Me.CMB_LOCATIONS.CaptionText = "Global path" + Me.CMB_LOCATIONS.CaptionToolTipEnabled = True + Me.CMB_LOCATIONS.CaptionToolTipText = "If checked, the path will be added to the global paths" + Me.CMB_LOCATIONS.CaptionVisible = True + Me.CMB_LOCATIONS.ChangeControlsEnableOnCheckedChange = False + Me.CMB_LOCATIONS.Dock = System.Windows.Forms.DockStyle.Fill + Me.CMB_LOCATIONS.Lines = New String(-1) {} + Me.CMB_LOCATIONS.Location = New System.Drawing.Point(3, 3) + Me.CMB_LOCATIONS.Name = "CMB_LOCATIONS" + Me.CMB_LOCATIONS.Size = New System.Drawing.Size(578, 22) + Me.CMB_LOCATIONS.TabIndex = 0 + Me.CMB_LOCATIONS.TextBoxBorderStyle = System.Windows.Forms.BorderStyle.FixedSingle + ' + 'FRM_LOCATIONS + ' + Me.FRM_LOCATIONS.Controls.Add(TP_LOCATIONS_USER) + Me.FRM_LOCATIONS.Dock = System.Windows.Forms.DockStyle.Fill + Me.FRM_LOCATIONS.Location = New System.Drawing.Point(3, 59) + Me.FRM_LOCATIONS.Name = "FRM_LOCATIONS" + Me.FRM_LOCATIONS.Size = New System.Drawing.Size(578, 189) + Me.FRM_LOCATIONS.TabIndex = 2 + Me.FRM_LOCATIONS.TabStop = False + Me.FRM_LOCATIONS.Text = "Locations" + ' + 'TP_LOCATIONS_USER + ' + TP_LOCATIONS_USER.ColumnCount = 1 + TP_LOCATIONS_USER.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) + TP_LOCATIONS_USER.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20.0!)) + TP_LOCATIONS_USER.Controls.Add(Me.OPT_LOCATION_1, 0, 0) + TP_LOCATIONS_USER.Controls.Add(Me.OPT_LOCATION_2, 0, 1) + TP_LOCATIONS_USER.Controls.Add(Me.OPT_LOCATION_3, 0, 2) + TP_LOCATIONS_USER.Dock = System.Windows.Forms.DockStyle.Fill + TP_LOCATIONS_USER.Location = New System.Drawing.Point(3, 16) + TP_LOCATIONS_USER.Margin = New System.Windows.Forms.Padding(0) + TP_LOCATIONS_USER.Name = "TP_LOCATIONS_USER" + TP_LOCATIONS_USER.RowCount = 3 + TP_LOCATIONS_USER.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33333!)) + TP_LOCATIONS_USER.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33333!)) + TP_LOCATIONS_USER.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33333!)) + TP_LOCATIONS_USER.Size = New System.Drawing.Size(572, 170) + TP_LOCATIONS_USER.TabIndex = 0 + ' + 'OPT_LOCATION_1 + ' + Me.OPT_LOCATION_1.AutoSize = True + Me.OPT_LOCATION_1.Dock = System.Windows.Forms.DockStyle.Fill + Me.OPT_LOCATION_1.Location = New System.Drawing.Point(3, 3) + Me.OPT_LOCATION_1.Name = "OPT_LOCATION_1" + Me.OPT_LOCATION_1.Size = New System.Drawing.Size(566, 50) + Me.OPT_LOCATION_1.TabIndex = 0 + Me.OPT_LOCATION_1.TabStop = True + Me.OPT_LOCATION_1.Text = "Location user 1" & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "Location collection 1" + Me.OPT_LOCATION_1.UseVisualStyleBackColor = True + ' + 'OPT_LOCATION_2 + ' + Me.OPT_LOCATION_2.AutoSize = True + Me.OPT_LOCATION_2.Dock = System.Windows.Forms.DockStyle.Fill + Me.OPT_LOCATION_2.Location = New System.Drawing.Point(3, 59) + Me.OPT_LOCATION_2.Name = "OPT_LOCATION_2" + Me.OPT_LOCATION_2.Size = New System.Drawing.Size(566, 50) + Me.OPT_LOCATION_2.TabIndex = 1 + Me.OPT_LOCATION_2.TabStop = True + Me.OPT_LOCATION_2.Text = "Location user 2" & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "Location collection 2" + Me.OPT_LOCATION_2.UseVisualStyleBackColor = True + ' + 'OPT_LOCATION_3 + ' + Me.OPT_LOCATION_3.AutoSize = True + Me.OPT_LOCATION_3.Dock = System.Windows.Forms.DockStyle.Fill + Me.OPT_LOCATION_3.Location = New System.Drawing.Point(3, 115) + Me.OPT_LOCATION_3.Name = "OPT_LOCATION_3" + Me.OPT_LOCATION_3.Size = New System.Drawing.Size(566, 52) + Me.OPT_LOCATION_3.TabIndex = 2 + Me.OPT_LOCATION_3.TabStop = True + Me.OPT_LOCATION_3.Text = "Location user 3" & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "Location collection 3" + Me.OPT_LOCATION_3.UseVisualStyleBackColor = True + ' + 'TXT_COL_NAME + ' + ActionButton4.BackgroundImage = CType(resources.GetObject("ActionButton4.BackgroundImage"), System.Drawing.Image) + ActionButton4.Name = "Clear" + ActionButton4.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear + Me.TXT_COL_NAME.Buttons.Add(ActionButton4) + Me.TXT_COL_NAME.CaptionText = "Collection name" + Me.TXT_COL_NAME.CaptionToolTipEnabled = True + Me.TXT_COL_NAME.CaptionToolTipText = "Collection folder to be created in the destination" + Me.TXT_COL_NAME.Dock = System.Windows.Forms.DockStyle.Fill + Me.TXT_COL_NAME.Lines = New String(-1) {} + Me.TXT_COL_NAME.Location = New System.Drawing.Point(3, 31) + Me.TXT_COL_NAME.Name = "TXT_COL_NAME" + Me.TXT_COL_NAME.PlaceholderEnabled = True + Me.TXT_COL_NAME.PlaceholderText = "Enter collection name here..." + Me.TXT_COL_NAME.Size = New System.Drawing.Size(578, 22) + Me.TXT_COL_NAME.TabIndex = 1 + ' + 'GlobalLocationsChooserForm + ' + Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!) + Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font + Me.ClientSize = New System.Drawing.Size(584, 251) + Me.Controls.Add(CONTAINER_MAIN) + Me.KeyPreview = True + Me.MinimumSize = New System.Drawing.Size(600, 290) + Me.Name = "GlobalLocationsChooserForm" + Me.Text = "Choose a new location" + CONTAINER_MAIN.ContentPanel.ResumeLayout(False) + CONTAINER_MAIN.ResumeLayout(False) + CONTAINER_MAIN.PerformLayout() + Me.TP_MAIN.ResumeLayout(False) + CType(Me.CMB_LOCATIONS, System.ComponentModel.ISupportInitialize).EndInit() + Me.FRM_LOCATIONS.ResumeLayout(False) + TP_LOCATIONS_USER.ResumeLayout(False) + TP_LOCATIONS_USER.PerformLayout() + CType(Me.TXT_COL_NAME, System.ComponentModel.ISupportInitialize).EndInit() + Me.ResumeLayout(False) + + End Sub + Private WithEvents CMB_LOCATIONS As PersonalUtilities.Forms.Controls.ComboBoxExtended + Private WithEvents OPT_LOCATION_1 As RadioButton + Private WithEvents OPT_LOCATION_2 As RadioButton + Private WithEvents OPT_LOCATION_3 As RadioButton + Private WithEvents FRM_LOCATIONS As GroupBox + Private WithEvents TP_MAIN As TableLayoutPanel + Private WithEvents TXT_COL_NAME As PersonalUtilities.Forms.Controls.TextBoxExtended + End Class +End Namespace \ No newline at end of file diff --git a/SCrawler/Editors/GlobalLocationsChooserForm.resx b/SCrawler/Editors/GlobalLocationsChooserForm.resx new file mode 100644 index 0000000..6b21a20 --- /dev/null +++ b/SCrawler/Editors/GlobalLocationsChooserForm.resx @@ -0,0 +1,244 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO + wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP + WQwhyWIyJIUW5NqyPb7oCVtIlhVTwYf8nv7/t2zJagel9KmqKsIACYL9RjI8UHz5zshougZr/AEvbxEP + aZCDBY3VslixaJvX3wzkkDiOwbZtDRGA5vdNAg+TL27qgmt5XkBG/gTdAG7Gt+3PP9oOaEGFCVEC6rp+ + 5g9MfM/c5e4OsEZMZkQEtGL5H2DdZ5JRArDwPA+iKII0TfkC9vroC9j5vq8JTWw3WzWgLMtZGIaa0MR8 + vlAD8PYlSaIJTTiOowY0p0Bc19XEJo6HE59FAPuMzyAINKGJ1XLFZxHALtMrnkBXOIQIIIQ8YvF/KrgB + cMaRN0UdBBkAAAAASUVORK5CYII= + + + + + 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 + AAAASUVORK5CYII= + + + + False + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO + xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go + tbbB43rK5xSAQq1VYFtmeQBoqZTSreVZvgTknM8yyyjA/qodsDF9gspD2Bj6B+DH+NqzhQQAG+POMnSX + AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC + + + \ No newline at end of file diff --git a/SCrawler/Editors/GlobalLocationsChooserForm.vb b/SCrawler/Editors/GlobalLocationsChooserForm.vb new file mode 100644 index 0000000..9dc6d53 --- /dev/null +++ b/SCrawler/Editors/GlobalLocationsChooserForm.vb @@ -0,0 +1,217 @@ +' Copyright (C) 2023 Andy https://github.com/AAndyProgram +' 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 SCrawler.API.Base +Imports SCrawler.DownloadObjects.STDownloader +Imports PersonalUtilities.Forms +Imports PersonalUtilities.Tools +Imports PersonalUtilities.Forms.Controls.Base +Imports ADB = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons +Namespace Editors + Friend Class GlobalLocationsChooserForm +#Region "Statics" + Friend Shared ReadOnly Property ModelHandler(ByVal Model As PathCreationModel) As PathMoverHandler + Get + Dim pattern$ + Select Case Model + Case PathCreationModel.Path : pattern = "{0}\" + Case PathCreationModel.Path_UserName : pattern = "{0}\{2}\" + Case PathCreationModel.Path_UserSite_UserName : pattern = "{0}\{1}\{2}\" + Case PathCreationModel.Collection : pattern = UserInfo.CollectionUserPathPattern + Case Else : Return Nothing + End Select + Return New PathMoverHandler(Function(u, d) String.Format(pattern, d.PathNoSeparator, u.Site, u.Name).CSFileP) + End Get + End Property + Friend Shared Function GetModelByLocation(ByVal Locations As SFile) As Integer + If Settings.GlobalLocations.Count > 0 Then + Dim i% = Settings.GlobalLocations.IndexOf(Locations, True) + If i >= 0 Then Return Settings.GlobalLocations(i).Model + End If + Return -1 + End Function +#End Region +#Region "Declarations" + Private WithEvents MyDefs As DefaultFormOptions + Friend ReadOnly Property MyModelHandler As PathMoverHandler + Get + Select Case True + Case OPT_LOCATION_1.Checked : Return ModelHandler(If(MyIsCollectionSelector, PathCreationModel.Collection, PathCreationModel.Path)) + Case OPT_LOCATION_2.Checked : Return ModelHandler(PathCreationModel.Path_UserName) + Case OPT_LOCATION_3.Checked : Return ModelHandler(PathCreationModel.Path_UserSite_UserName) + Case Else : Return Nothing + End Select + End Get + End Property + Friend ReadOnly Property MyModel As PathCreationModel + Get + Select Case True + Case OPT_LOCATION_1.Checked : Return If(MyIsCollectionSelector, PathCreationModel.Collection, PathCreationModel.Path) + Case OPT_LOCATION_2.Checked : Return PathCreationModel.Path_UserName + Case OPT_LOCATION_3.Checked : Return PathCreationModel.Path_UserSite_UserName + Case Else : Return PathCreationModel.Path_UserSite_UserName + End Select + End Get + End Property + Friend ReadOnly Property MyDestination As DownloadLocation + Get + Return New DownloadLocation With {.Path = CMB_LOCATIONS.Text.CSFileP, .Model = MyModel} + End Get + End Property + Friend Property MyIsMultipleUsers As Boolean = False + Friend Property MyInitialLocation As SFile + Private _MyNonMyltipleUser As IUserData + Private _UserSite As String = String.Empty + Private _UserName As String = String.Empty + Friend Property MyNonMyltipleUser As IUserData + Get + Return _MyNonMyltipleUser + End Get + Set(ByVal u As IUserData) + _MyNonMyltipleUser = u + If Not u Is Nothing And Not u.IsCollection Then + _UserSite = u.Site + _UserName = u.Name + End If + End Set + End Property + Friend Property MyIsCollectionSelector As Boolean = False + Friend Property MyCollectionName As String + Get + Return TXT_COL_NAME.Text + End Get + Set(ByVal NewName As String) + TXT_COL_NAME.Text = NewName + End Set + End Property +#End Region +#Region "Initializer" + Friend Sub New() + InitializeComponent() + MyDefs = New DefaultFormOptions(Me, Settings.Design) + Icon = ImageRenderer.GetIcon(My.Resources.FolderPic_32, EDP.ReturnValue) + If Icon Is Nothing Then ShowIcon = False + End Sub +#End Region +#Region "Form handlers" + Private Sub GlobalLocationsChooserForm_Load(sender As Object, e As EventArgs) Handles Me.Load + With MyDefs + .MyViewInitialize() + .AddOkCancelToolbar() + If Not MyIsCollectionSelector Then + TP_MAIN.Controls.Remove(TXT_COL_NAME) + TP_MAIN.RowStyles(1).Height = 0 + End If + Settings.GlobalLocations.PopulateComboBox(CMB_LOCATIONS) + If MyIsCollectionSelector Then + FRM_LOCATIONS.Enabled = False + OPT_LOCATION_1.Checked = True + Else + OPT_LOCATION_3.Checked = True + End If + .MyFieldsChecker = New FieldsChecker + With .MyFieldsCheckerE + .AddControl(Of String)(CMB_LOCATIONS, "Location") + If MyIsCollectionSelector Then .AddControl(Of String)(TXT_COL_NAME, TXT_COL_NAME.CaptionText) + .EndLoaderOperations() + End With + .EndLoaderOperations() + UpdateOptions(False) + End With + End Sub + Private Sub GlobalLocationsChooserForm_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown + If e.KeyCode = Keys.O And e.Control Then + CMB_LOCATIONS.Button(ADB.Open).PerformClick() + e.Handled = True + End If + End Sub +#End Region +#Region "Ok, Cancel" + Private Sub MyDefs_ButtonOkClick(ByVal Sender As Object, ByVal e As KeyHandleEventArgs) Handles MyDefs.ButtonOkClick + If MyDefs.MyFieldsChecker.AllParamsOK Then + If CMB_LOCATIONS.Checked Then _ + Settings.GlobalLocations.Add(New DownloadLocation With {.Path = CMB_LOCATIONS.Text.CSFileP, .Model = MyModel}, + Settings.STDownloader_OutputPathAskForName) + MyDefs.CloseForm() + End If + End Sub +#End Region +#Region "Controls" + Private Sub TXT_COL_NAME_ActionOnTextChanged(sender As Object, e As EventArgs) Handles TXT_COL_NAME.ActionOnTextChanged + UpdateOptions(True, False, False) + End Sub + Private Sub CMB_LOCATIONS_ActionOnButtonClick(ByVal Sender As ActionButton, ByVal e As ActionButtonEventArgs) Handles CMB_LOCATIONS.ActionOnButtonClick + If e.DefaultButton = ADB.Open Or e.DefaultButton = ADB.Add Then + Dim t$ = "Select a new destination for " + If MyIsMultipleUsers Then + t &= "multiple users" + Else + t &= $"{IIf(If(MyNonMyltipleUser?.IsCollection, False), "collection", "user")}" + If Not MyNonMyltipleUser Is Nothing Then t &= $" [{MyNonMyltipleUser}]" + End If + + Dim f As SFile = SFile.SelectPath(MyInitialLocation, t) + If Not f.IsEmptyString Then + _SuspendUpdate = True + CMB_LOCATIONS.Text = f.PathWithSeparator + _SuspendUpdate = False + UpdateOptions(True) + End If + End If + End Sub + Private Sub CMB_LOCATIONS_ActionSelectedItemBeforeChanged(ByVal Sender As Object, ByVal e As EventArgs, ByVal Item As ListViewItem) Handles CMB_LOCATIONS.ActionSelectedItemBeforeChanged + _SuspendUpdate = True + End Sub + Private Sub CMB_LOCATIONS_ActionSelectedItemChanged(ByVal Sender As Object, ByVal e As EventArgs, ByVal Item As ListViewItem) Handles CMB_LOCATIONS.ActionSelectedItemChanged + Dim i% = CMB_LOCATIONS.SelectedIndex + If i.ValueBetween(0, Settings.DownloadLocations.Count - 1) Then + Select Case Settings.DownloadLocations(i).Model + Case PathCreationModel.Path, PathCreationModel.Collection : OPT_LOCATION_1.Checked = True + Case PathCreationModel.Path_UserName : OPT_LOCATION_2.Checked = True + Case PathCreationModel.Path_UserSite_UserName : OPT_LOCATION_3.Checked = True + End Select + End If + _SuspendUpdate = False + UpdateOptions(False, False, False) + End Sub + Private Sub CMB_LOCATIONS_ActionOnCheckedChange(ByVal Sender As Object, ByVal e As EventArgs, ByVal Checked As Boolean) Handles CMB_LOCATIONS.ActionOnCheckedChange + If Not MyDefs.Initializing Then CMB_LOCATIONS.CaptionText = IIf(Checked, "Global path", "Path") + End Sub + Private Sub CMB_LOCATIONS_ActionOnTextChanged(ByVal Sender As Object, ByVal e As EventArgs) Handles CMB_LOCATIONS.ActionOnTextChanged + UpdateOptions(False) + End Sub + Private _SuspendUpdate As Boolean = False + Private Sub UpdateOptions(ByVal ResetColName As Boolean, Optional ByVal UpdateLocation As Boolean = True, Optional ByVal UpdateColName As Boolean = True) + Const uSiteDef$ = "[UserSite]" + Const uNameDef$ = "[UserName]" + If Not _SuspendUpdate Then + _SuspendUpdate = True + Dim loc As SFile = SFile.GetPath(CMB_LOCATIONS.Text) + If Not loc.IsEmptyString Then + If MyIsCollectionSelector And ResetColName Then + If UpdateColName Then TXT_COL_NAME.Text = loc.Segments.LastOrDefault + If UpdateLocation Then loc = loc.CutPath + CMB_LOCATIONS.Text = loc.PathWithSeparator + End If + Dim cName$ = TXT_COL_NAME.Text + If Not cName.IsEmptyString Then cName &= "\" + Dim p$ = loc.PathWithSeparator + Dim uSite$ = If(MyIsMultipleUsers, uSiteDef, _UserSite.IfNullOrEmpty(uSiteDef)) + Dim uName$ = If(MyIsMultipleUsers, uNameDef, _UserName.IfNullOrEmpty(uNameDef)) + OPT_LOCATION_1.Text = p & vbCr & $"{p}{cName}{uSite}_{uName}\" + OPT_LOCATION_2.Text = $"{p}{uName}\" + OPT_LOCATION_3.Text = $"{p}{uSite}\{uName}\" + Else + With {OPT_LOCATION_1, OPT_LOCATION_2, OPT_LOCATION_3}.ToList : .ForEach(Sub(opt) opt.Text = String.Empty) : End With + End If + _SuspendUpdate = False + End If + End Sub +#End Region + End Class +End Namespace \ No newline at end of file diff --git a/SCrawler/Editors/GlobalSettingsForm.Designer.vb b/SCrawler/Editors/GlobalSettingsForm.Designer.vb index 8adcac7..3b40d7c 100644 --- a/SCrawler/Editors/GlobalSettingsForm.Designer.vb +++ b/SCrawler/Editors/GlobalSettingsForm.Designer.vb @@ -36,6 +36,8 @@ Namespace Editors 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 TP_FILE_NAME As System.Windows.Forms.TableLayoutPanel Dim TP_FILE_PATTERNS As System.Windows.Forms.TableLayoutPanel Dim LBL_DATE_POS As System.Windows.Forms.Label @@ -48,14 +50,14 @@ Namespace Editors Dim TP_CHANNELS As System.Windows.Forms.TableLayoutPanel Dim TAB_BEHAVIOR As System.Windows.Forms.TabPage Dim TP_BEHAVIOR As System.Windows.Forms.TableLayoutPanel - 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 ActionButton13 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() + Dim ActionButton14 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 ActionButton13 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() - Dim ActionButton14 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() + Dim ActionButton15 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() + Dim ActionButton16 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() Dim TP_MISSING_DATA As System.Windows.Forms.TableLayoutPanel Dim TAB_FEED As System.Windows.Forms.TabPage Dim TP_FEED As System.Windows.Forms.TableLayoutPanel @@ -63,8 +65,6 @@ Namespace Editors Dim TAB_NOTIFY As System.Windows.Forms.TabPage Dim TP_NOTIFY_MAIN As System.Windows.Forms.TableLayoutPanel Dim TP_ENVIR As System.Windows.Forms.TableLayoutPanel - Dim ActionButton15 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() - Dim ActionButton16 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() Dim ActionButton17 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() Dim ActionButton18 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() Dim ActionButton19 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() @@ -73,9 +73,11 @@ Namespace Editors Dim ActionButton22 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() Dim ActionButton23 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() Dim ActionButton24 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() + Dim ActionButton25 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() + Dim ActionButton26 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() Dim TAB_STD As System.Windows.Forms.TabPage Dim TP_STD As System.Windows.Forms.TableLayoutPanel - Dim ActionButton25 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() + Dim ActionButton27 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() Dim ListColumn1 As PersonalUtilities.Forms.Controls.Base.ListColumn = New PersonalUtilities.Forms.Controls.Base.ListColumn() Dim ListColumn2 As PersonalUtilities.Forms.Controls.Base.ListColumn = New PersonalUtilities.Forms.Controls.Base.ListColumn() Me.TXT_GLOBAL_PATH = New PersonalUtilities.Forms.Controls.TextBoxExtended() @@ -91,6 +93,9 @@ Namespace Editors Me.TXT_USER_AGENT = New PersonalUtilities.Forms.Controls.TextBoxExtended() Me.TXT_USER_LIST_IMAGE = New PersonalUtilities.Forms.Controls.TextBoxExtended() Me.COLORS_USERLIST = New SCrawler.Editors.ColorPicker() + Me.COLORS_SUBSCRIPTIONS = New SCrawler.Editors.ColorPicker() + Me.TXT_PRG_TITLE = New PersonalUtilities.Forms.Controls.TextBoxExtended() + Me.TXT_PRG_DESCR = New PersonalUtilities.Forms.Controls.TextBoxExtended() Me.OPT_FILE_NAME_REPLACE = New System.Windows.Forms.RadioButton() Me.OPT_FILE_NAME_ADD_DATE = New System.Windows.Forms.RadioButton() Me.CH_FILE_NAME_CHANGE = New System.Windows.Forms.CheckBox() @@ -124,6 +129,12 @@ Namespace Editors Me.CH_STD_EVERY = New System.Windows.Forms.CheckBox() Me.CH_STD_YT_LOAD = New System.Windows.Forms.CheckBox() Me.CH_STD_YT_REMOVE = New System.Windows.Forms.CheckBox() + Me.CH_FEED_OPEN_LAST_MODE = New System.Windows.Forms.CheckBox() + Me.CH_STD_YT_OUTPUT_ASK_NAME = New System.Windows.Forms.CheckBox() + Me.CH_STD_YT_OUTPUT_AUTO_ADD = New System.Windows.Forms.CheckBox() + Me.BTT_RESET_DOWNLOAD_LOCATIONS = New System.Windows.Forms.Button() + Me.CH_STD_SNAP_KEEP_WITH_FILES = New System.Windows.Forms.CheckBox() + Me.CH_STD_SNAP_CACHE_PERMANENT = New System.Windows.Forms.CheckBox() Me.TXT_CHANNELS_ROWS = New PersonalUtilities.Forms.Controls.TextBoxExtended() Me.TXT_CHANNELS_COLUMNS = New PersonalUtilities.Forms.Controls.TextBoxExtended() Me.CH_DOWN_IMAGES_NATIVE = New System.Windows.Forms.CheckBox() @@ -138,6 +149,7 @@ Namespace Editors Me.TXT_SCRIPT = New PersonalUtilities.Forms.Controls.TextBoxExtended() Me.TXT_DOWN_COMPLETE_SCRIPT = New PersonalUtilities.Forms.Controls.TextBoxExtended() Me.CH_UNAME_UP = New System.Windows.Forms.CheckBox() + Me.CH_UICON_UP = New System.Windows.Forms.CheckBox() Me.TXT_FEED_ROWS = New PersonalUtilities.Forms.Controls.TextBoxExtended() Me.TXT_FEED_COLUMNS = New PersonalUtilities.Forms.Controls.TextBoxExtended() Me.CH_FEED_ENDLESS = New System.Windows.Forms.CheckBox() @@ -159,7 +171,7 @@ Namespace Editors Me.TAB_MAIN = New System.Windows.Forms.TabControl() Me.TAB_ENVIR = New System.Windows.Forms.TabPage() Me.CONTAINER_MAIN = New System.Windows.Forms.ToolStripContainer() - Me.CH_UICON_UP = New System.Windows.Forms.CheckBox() + Me.CH_FEED_SHOW_FRIENDLY = 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() @@ -198,6 +210,8 @@ Namespace Editors CType(Me.TXT_IMGUR_CLIENT_ID, System.ComponentModel.ISupportInitialize).BeginInit() CType(Me.TXT_USER_AGENT, System.ComponentModel.ISupportInitialize).BeginInit() CType(Me.TXT_USER_LIST_IMAGE, System.ComponentModel.ISupportInitialize).BeginInit() + CType(Me.TXT_PRG_TITLE, System.ComponentModel.ISupportInitialize).BeginInit() + CType(Me.TXT_PRG_DESCR, System.ComponentModel.ISupportInitialize).BeginInit() TP_FILE_NAME.SuspendLayout() TP_FILE_PATTERNS.SuspendLayout() TP_CHANNELS_IMGS.SuspendLayout() @@ -255,16 +269,19 @@ Namespace Editors TP_BASIS.Controls.Add(Me.TXT_MAX_JOBS_USERS, 0, 3) TP_BASIS.Controls.Add(Me.TXT_MAX_JOBS_CHANNELS, 0, 4) TP_BASIS.Controls.Add(Me.CH_CHECK_VER_START, 0, 5) - TP_BASIS.Controls.Add(Me.TXT_IMGUR_CLIENT_ID, 0, 7) - TP_BASIS.Controls.Add(Me.CH_SHOW_GROUPS, 0, 10) - TP_BASIS.Controls.Add(Me.CH_USERS_GROUPING, 0, 11) - TP_BASIS.Controls.Add(Me.TXT_USER_AGENT, 0, 6) - TP_BASIS.Controls.Add(Me.TXT_USER_LIST_IMAGE, 0, 8) - TP_BASIS.Controls.Add(Me.COLORS_USERLIST, 0, 9) + TP_BASIS.Controls.Add(Me.TXT_IMGUR_CLIENT_ID, 0, 9) + TP_BASIS.Controls.Add(Me.CH_SHOW_GROUPS, 0, 13) + TP_BASIS.Controls.Add(Me.CH_USERS_GROUPING, 0, 14) + TP_BASIS.Controls.Add(Me.TXT_USER_AGENT, 0, 8) + TP_BASIS.Controls.Add(Me.TXT_USER_LIST_IMAGE, 0, 10) + TP_BASIS.Controls.Add(Me.COLORS_USERLIST, 0, 11) + TP_BASIS.Controls.Add(Me.COLORS_SUBSCRIPTIONS, 0, 12) + TP_BASIS.Controls.Add(Me.TXT_PRG_TITLE, 0, 6) + TP_BASIS.Controls.Add(Me.TXT_PRG_DESCR, 0, 7) TP_BASIS.Dock = System.Windows.Forms.DockStyle.Fill TP_BASIS.Location = New System.Drawing.Point(3, 3) TP_BASIS.Name = "TP_BASIS" - TP_BASIS.RowCount = 13 + TP_BASIS.RowCount = 16 TP_BASIS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) TP_BASIS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) TP_BASIS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) @@ -274,12 +291,14 @@ Namespace Editors TP_BASIS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) TP_BASIS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) TP_BASIS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) + TP_BASIS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) + TP_BASIS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.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.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.Absolute, 25.0!)) TP_BASIS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) - TP_BASIS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20.0!)) - TP_BASIS.Size = New System.Drawing.Size(570, 362) + TP_BASIS.Size = New System.Drawing.Size(570, 445) TP_BASIS.TabIndex = 0 ' 'TXT_GLOBAL_PATH @@ -294,6 +313,7 @@ Namespace Editors Me.TXT_GLOBAL_PATH.CaptionToolTipEnabled = True Me.TXT_GLOBAL_PATH.CaptionToolTipText = "Root path for storing users' data" Me.TXT_GLOBAL_PATH.Dock = System.Windows.Forms.DockStyle.Fill + Me.TXT_GLOBAL_PATH.Lines = New String(-1) {} Me.TXT_GLOBAL_PATH.Location = New System.Drawing.Point(4, 4) Me.TXT_GLOBAL_PATH.Name = "TXT_GLOBAL_PATH" Me.TXT_GLOBAL_PATH.Size = New System.Drawing.Size(562, 22) @@ -323,6 +343,7 @@ Namespace Editors Me.TXT_IMAGE_LARGE.CaptionToolTipText = "Maximum large image size by height" Me.TXT_IMAGE_LARGE.ControlMode = PersonalUtilities.Forms.Controls.TextBoxExtended.ControlModes.NumericUpDown Me.TXT_IMAGE_LARGE.Dock = System.Windows.Forms.DockStyle.Fill + Me.TXT_IMAGE_LARGE.Lines = New String(-1) {} Me.TXT_IMAGE_LARGE.Location = New System.Drawing.Point(3, 3) Me.TXT_IMAGE_LARGE.Name = "TXT_IMAGE_LARGE" Me.TXT_IMAGE_LARGE.NumberMaximum = New Decimal(New Integer() {256, 0, 0, 0}) @@ -339,6 +360,7 @@ Namespace Editors Me.TXT_IMAGE_SMALL.CaptionToolTipText = "Maximum small image size by height" Me.TXT_IMAGE_SMALL.ControlMode = PersonalUtilities.Forms.Controls.TextBoxExtended.ControlModes.NumericUpDown Me.TXT_IMAGE_SMALL.Dock = System.Windows.Forms.DockStyle.Fill + Me.TXT_IMAGE_SMALL.Lines = New String(-1) {} Me.TXT_IMAGE_SMALL.Location = New System.Drawing.Point(287, 3) Me.TXT_IMAGE_SMALL.Name = "TXT_IMAGE_SMALL" Me.TXT_IMAGE_SMALL.NumberMaximum = New Decimal(New Integer() {256, 0, 0, 0}) @@ -357,6 +379,7 @@ Namespace Editors Me.TXT_COLLECTIONS_PATH.CaptionToolTipEnabled = True Me.TXT_COLLECTIONS_PATH.CaptionToolTipText = "Set collections folder name (name only)" Me.TXT_COLLECTIONS_PATH.Dock = System.Windows.Forms.DockStyle.Fill + Me.TXT_COLLECTIONS_PATH.Lines = New String(-1) {} Me.TXT_COLLECTIONS_PATH.Location = New System.Drawing.Point(4, 62) Me.TXT_COLLECTIONS_PATH.Name = "TXT_COLLECTIONS_PATH" Me.TXT_COLLECTIONS_PATH.Size = New System.Drawing.Size(562, 22) @@ -373,6 +396,7 @@ Namespace Editors Me.TXT_MAX_JOBS_USERS.CaptionWidth = 50.0R Me.TXT_MAX_JOBS_USERS.ControlMode = PersonalUtilities.Forms.Controls.TextBoxExtended.ControlModes.NumericUpDown Me.TXT_MAX_JOBS_USERS.Dock = System.Windows.Forms.DockStyle.Fill + Me.TXT_MAX_JOBS_USERS.Lines = New String(-1) {} Me.TXT_MAX_JOBS_USERS.Location = New System.Drawing.Point(4, 91) Me.TXT_MAX_JOBS_USERS.Name = "TXT_MAX_JOBS_USERS" Me.TXT_MAX_JOBS_USERS.NumberMinimum = New Decimal(New Integer() {1, 0, 0, 0}) @@ -392,6 +416,7 @@ Namespace Editors Me.TXT_MAX_JOBS_CHANNELS.CaptionWidth = 50.0R Me.TXT_MAX_JOBS_CHANNELS.ControlMode = PersonalUtilities.Forms.Controls.TextBoxExtended.ControlModes.NumericUpDown Me.TXT_MAX_JOBS_CHANNELS.Dock = System.Windows.Forms.DockStyle.Fill + Me.TXT_MAX_JOBS_CHANNELS.Lines = New String(-1) {} Me.TXT_MAX_JOBS_CHANNELS.Location = New System.Drawing.Point(4, 120) Me.TXT_MAX_JOBS_CHANNELS.Name = "TXT_MAX_JOBS_CHANNELS" Me.TXT_MAX_JOBS_CHANNELS.NumberMinimum = New Decimal(New Integer() {1, 0, 0, 0}) @@ -418,19 +443,20 @@ Namespace Editors Me.TXT_IMGUR_CLIENT_ID.Buttons.Add(ActionButton6) Me.TXT_IMGUR_CLIENT_ID.CaptionText = "Imgur Client ID" Me.TXT_IMGUR_CLIENT_ID.Dock = System.Windows.Forms.DockStyle.Fill - Me.TXT_IMGUR_CLIENT_ID.Location = New System.Drawing.Point(4, 204) + Me.TXT_IMGUR_CLIENT_ID.Lines = New String(-1) {} + Me.TXT_IMGUR_CLIENT_ID.Location = New System.Drawing.Point(4, 262) Me.TXT_IMGUR_CLIENT_ID.Name = "TXT_IMGUR_CLIENT_ID" Me.TXT_IMGUR_CLIENT_ID.Size = New System.Drawing.Size(562, 22) - Me.TXT_IMGUR_CLIENT_ID.TabIndex = 7 + Me.TXT_IMGUR_CLIENT_ID.TabIndex = 9 ' 'CH_SHOW_GROUPS ' Me.CH_SHOW_GROUPS.AutoSize = True Me.CH_SHOW_GROUPS.Dock = System.Windows.Forms.DockStyle.Fill - Me.CH_SHOW_GROUPS.Location = New System.Drawing.Point(4, 288) + Me.CH_SHOW_GROUPS.Location = New System.Drawing.Point(4, 372) Me.CH_SHOW_GROUPS.Name = "CH_SHOW_GROUPS" Me.CH_SHOW_GROUPS.Size = New System.Drawing.Size(562, 19) - Me.CH_SHOW_GROUPS.TabIndex = 10 + Me.CH_SHOW_GROUPS.TabIndex = 13 Me.CH_SHOW_GROUPS.Text = "Show groups" TT_MAIN.SetToolTip(Me.CH_SHOW_GROUPS, "Grouping users by site") Me.CH_SHOW_GROUPS.UseVisualStyleBackColor = True @@ -439,10 +465,10 @@ Namespace Editors ' Me.CH_USERS_GROUPING.AutoSize = True Me.CH_USERS_GROUPING.Dock = System.Windows.Forms.DockStyle.Fill - Me.CH_USERS_GROUPING.Location = New System.Drawing.Point(4, 314) + Me.CH_USERS_GROUPING.Location = New System.Drawing.Point(4, 398) Me.CH_USERS_GROUPING.Name = "CH_USERS_GROUPING" Me.CH_USERS_GROUPING.Size = New System.Drawing.Size(562, 19) - Me.CH_USERS_GROUPING.TabIndex = 11 + Me.CH_USERS_GROUPING.TabIndex = 14 Me.CH_USERS_GROUPING.Text = "Use user grouping" TT_MAIN.SetToolTip(Me.CH_USERS_GROUPING, "Group users by groups and/or labels") Me.CH_USERS_GROUPING.UseVisualStyleBackColor = True @@ -461,10 +487,11 @@ Namespace Editors Me.TXT_USER_AGENT.CaptionToolTipEnabled = True Me.TXT_USER_AGENT.CaptionToolTipText = "Default user agent to use in requests" Me.TXT_USER_AGENT.Dock = System.Windows.Forms.DockStyle.Fill - Me.TXT_USER_AGENT.Location = New System.Drawing.Point(4, 175) + Me.TXT_USER_AGENT.Lines = New String(-1) {} + Me.TXT_USER_AGENT.Location = New System.Drawing.Point(4, 233) Me.TXT_USER_AGENT.Name = "TXT_USER_AGENT" Me.TXT_USER_AGENT.Size = New System.Drawing.Size(562, 22) - Me.TXT_USER_AGENT.TabIndex = 6 + Me.TXT_USER_AGENT.TabIndex = 8 ' 'TXT_USER_LIST_IMAGE ' @@ -480,10 +507,11 @@ Namespace Editors Me.TXT_USER_LIST_IMAGE.CaptionToolTipEnabled = True Me.TXT_USER_LIST_IMAGE.CaptionToolTipText = "Background image for user list" Me.TXT_USER_LIST_IMAGE.Dock = System.Windows.Forms.DockStyle.Fill - Me.TXT_USER_LIST_IMAGE.Location = New System.Drawing.Point(4, 233) + Me.TXT_USER_LIST_IMAGE.Lines = New String(-1) {} + Me.TXT_USER_LIST_IMAGE.Location = New System.Drawing.Point(4, 291) Me.TXT_USER_LIST_IMAGE.Name = "TXT_USER_LIST_IMAGE" Me.TXT_USER_LIST_IMAGE.Size = New System.Drawing.Size(562, 22) - Me.TXT_USER_LIST_IMAGE.TabIndex = 8 + Me.TXT_USER_LIST_IMAGE.TabIndex = 10 ' 'COLORS_USERLIST ' @@ -491,12 +519,57 @@ Namespace Editors Me.COLORS_USERLIST.CaptionText = "Userlist colors" Me.COLORS_USERLIST.CaptionWidth = 103 Me.COLORS_USERLIST.Dock = System.Windows.Forms.DockStyle.Fill - Me.COLORS_USERLIST.Location = New System.Drawing.Point(1, 259) + Me.COLORS_USERLIST.Location = New System.Drawing.Point(1, 317) Me.COLORS_USERLIST.Margin = New System.Windows.Forms.Padding(0) Me.COLORS_USERLIST.Name = "COLORS_USERLIST" Me.COLORS_USERLIST.Padding = New System.Windows.Forms.Padding(0, 0, 2, 0) Me.COLORS_USERLIST.Size = New System.Drawing.Size(568, 25) - Me.COLORS_USERLIST.TabIndex = 9 + Me.COLORS_USERLIST.TabIndex = 11 + ' + 'COLORS_SUBSCRIPTIONS + ' + Me.COLORS_SUBSCRIPTIONS.ButtonsMargin = New System.Windows.Forms.Padding(1, 2, 1, 2) + Me.COLORS_SUBSCRIPTIONS.CaptionText = "Subscriptions color" + Me.COLORS_SUBSCRIPTIONS.CaptionWidth = 103 + Me.COLORS_SUBSCRIPTIONS.Dock = System.Windows.Forms.DockStyle.Fill + Me.COLORS_SUBSCRIPTIONS.Location = New System.Drawing.Point(1, 343) + Me.COLORS_SUBSCRIPTIONS.Margin = New System.Windows.Forms.Padding(0) + Me.COLORS_SUBSCRIPTIONS.Name = "COLORS_SUBSCRIPTIONS" + Me.COLORS_SUBSCRIPTIONS.Padding = New System.Windows.Forms.Padding(0, 0, 2, 0) + Me.COLORS_SUBSCRIPTIONS.Size = New System.Drawing.Size(568, 25) + Me.COLORS_SUBSCRIPTIONS.TabIndex = 12 + ' + 'TXT_PRG_TITLE + ' + ActionButton11.BackgroundImage = CType(resources.GetObject("ActionButton11.BackgroundImage"), System.Drawing.Image) + ActionButton11.Name = "Clear" + ActionButton11.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear + Me.TXT_PRG_TITLE.Buttons.Add(ActionButton11) + Me.TXT_PRG_TITLE.CaptionText = "Program title" + Me.TXT_PRG_TITLE.CaptionToolTipEnabled = True + Me.TXT_PRG_TITLE.CaptionToolTipText = "Change the title of the main window if you need to" + Me.TXT_PRG_TITLE.Dock = System.Windows.Forms.DockStyle.Fill + Me.TXT_PRG_TITLE.Lines = New String(-1) {} + Me.TXT_PRG_TITLE.Location = New System.Drawing.Point(4, 175) + Me.TXT_PRG_TITLE.Name = "TXT_PRG_TITLE" + Me.TXT_PRG_TITLE.Size = New System.Drawing.Size(562, 22) + Me.TXT_PRG_TITLE.TabIndex = 6 + ' + 'TXT_PRG_DESCR + ' + ActionButton12.BackgroundImage = CType(resources.GetObject("ActionButton12.BackgroundImage"), System.Drawing.Image) + ActionButton12.Name = "Clear" + ActionButton12.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear + Me.TXT_PRG_DESCR.Buttons.Add(ActionButton12) + Me.TXT_PRG_DESCR.CaptionText = "Program description" + Me.TXT_PRG_DESCR.CaptionToolTipEnabled = True + Me.TXT_PRG_DESCR.CaptionToolTipText = "Add some additional info to the program info if you need" + Me.TXT_PRG_DESCR.Dock = System.Windows.Forms.DockStyle.Fill + Me.TXT_PRG_DESCR.Lines = New String(-1) {} + Me.TXT_PRG_DESCR.Location = New System.Drawing.Point(4, 204) + Me.TXT_PRG_DESCR.Name = "TXT_PRG_DESCR" + Me.TXT_PRG_DESCR.Size = New System.Drawing.Size(562, 22) + Me.TXT_PRG_DESCR.TabIndex = 7 ' 'TP_FILE_NAME ' @@ -930,11 +1003,11 @@ Namespace Editors ' Me.CH_STD_YT_LOAD.AutoSize = True Me.CH_STD_YT_LOAD.Dock = System.Windows.Forms.DockStyle.Fill - Me.CH_STD_YT_LOAD.Location = New System.Drawing.Point(4, 166) + Me.CH_STD_YT_LOAD.Location = New System.Drawing.Point(4, 218) Me.CH_STD_YT_LOAD.Name = "CH_STD_YT_LOAD" Me.CH_STD_YT_LOAD.Padding = New System.Windows.Forms.Padding(100, 0, 0, 0) Me.CH_STD_YT_LOAD.Size = New System.Drawing.Size(568, 19) - Me.CH_STD_YT_LOAD.TabIndex = 6 + Me.CH_STD_YT_LOAD.TabIndex = 8 Me.CH_STD_YT_LOAD.Text = "Load downloaded YouTube videos to the form" TT_MAIN.SetToolTip(Me.CH_STD_YT_LOAD, "If checked, downloaded YouTube videos will be loaded to the form. Otherwise, all " & "downloaded data will be loaded to the form except YouTube data.") @@ -944,16 +1017,92 @@ Namespace Editors ' Me.CH_STD_YT_REMOVE.AutoSize = True Me.CH_STD_YT_REMOVE.Dock = System.Windows.Forms.DockStyle.Fill - Me.CH_STD_YT_REMOVE.Location = New System.Drawing.Point(4, 192) + Me.CH_STD_YT_REMOVE.Location = New System.Drawing.Point(4, 244) Me.CH_STD_YT_REMOVE.Name = "CH_STD_YT_REMOVE" Me.CH_STD_YT_REMOVE.Padding = New System.Windows.Forms.Padding(100, 0, 0, 0) Me.CH_STD_YT_REMOVE.Size = New System.Drawing.Size(568, 19) - Me.CH_STD_YT_REMOVE.TabIndex = 7 + Me.CH_STD_YT_REMOVE.TabIndex = 9 Me.CH_STD_YT_REMOVE.Text = "Clear YouTube videos when clearing the list" TT_MAIN.SetToolTip(Me.CH_STD_YT_REMOVE, "If checked, YouTube videos will also be removed from the list. This action will a" & "lso affect the standalone 'YouTubeDownloader' app.") Me.CH_STD_YT_REMOVE.UseVisualStyleBackColor = True ' + 'CH_FEED_OPEN_LAST_MODE + ' + Me.CH_FEED_OPEN_LAST_MODE.AutoSize = True + Me.CH_FEED_OPEN_LAST_MODE.Dock = System.Windows.Forms.DockStyle.Fill + Me.CH_FEED_OPEN_LAST_MODE.Location = New System.Drawing.Point(4, 192) + Me.CH_FEED_OPEN_LAST_MODE.Name = "CH_FEED_OPEN_LAST_MODE" + Me.CH_FEED_OPEN_LAST_MODE.Size = New System.Drawing.Size(568, 19) + Me.CH_FEED_OPEN_LAST_MODE.TabIndex = 7 + Me.CH_FEED_OPEN_LAST_MODE.Text = "Open last mode (users or subscriptions)" + TT_MAIN.SetToolTip(Me.CH_FEED_OPEN_LAST_MODE, "If disabled, the user mode will be used when initializing the feed.") + Me.CH_FEED_OPEN_LAST_MODE.UseVisualStyleBackColor = True + ' + 'CH_STD_YT_OUTPUT_ASK_NAME + ' + Me.CH_STD_YT_OUTPUT_ASK_NAME.AutoSize = True + Me.CH_STD_YT_OUTPUT_ASK_NAME.Dock = System.Windows.Forms.DockStyle.Fill + Me.CH_STD_YT_OUTPUT_ASK_NAME.Location = New System.Drawing.Point(4, 270) + Me.CH_STD_YT_OUTPUT_ASK_NAME.Name = "CH_STD_YT_OUTPUT_ASK_NAME" + Me.CH_STD_YT_OUTPUT_ASK_NAME.Padding = New System.Windows.Forms.Padding(100, 0, 0, 0) + Me.CH_STD_YT_OUTPUT_ASK_NAME.Size = New System.Drawing.Size(568, 19) + Me.CH_STD_YT_OUTPUT_ASK_NAME.TabIndex = 10 + Me.CH_STD_YT_OUTPUT_ASK_NAME.Text = "Output path: ask for a name" + TT_MAIN.SetToolTip(Me.CH_STD_YT_OUTPUT_ASK_NAME, "Ask for a name when adding a new output path to the list.") + Me.CH_STD_YT_OUTPUT_ASK_NAME.UseVisualStyleBackColor = True + ' + 'CH_STD_YT_OUTPUT_AUTO_ADD + ' + Me.CH_STD_YT_OUTPUT_AUTO_ADD.AutoSize = True + Me.CH_STD_YT_OUTPUT_AUTO_ADD.Dock = System.Windows.Forms.DockStyle.Fill + Me.CH_STD_YT_OUTPUT_AUTO_ADD.Location = New System.Drawing.Point(4, 296) + Me.CH_STD_YT_OUTPUT_AUTO_ADD.Name = "CH_STD_YT_OUTPUT_AUTO_ADD" + Me.CH_STD_YT_OUTPUT_AUTO_ADD.Padding = New System.Windows.Forms.Padding(100, 0, 0, 0) + Me.CH_STD_YT_OUTPUT_AUTO_ADD.Size = New System.Drawing.Size(568, 19) + Me.CH_STD_YT_OUTPUT_AUTO_ADD.TabIndex = 11 + Me.CH_STD_YT_OUTPUT_AUTO_ADD.Text = "Output path: auto add" + TT_MAIN.SetToolTip(Me.CH_STD_YT_OUTPUT_AUTO_ADD, "Add new paths to the list automatically.") + Me.CH_STD_YT_OUTPUT_AUTO_ADD.UseVisualStyleBackColor = True + ' + 'BTT_RESET_DOWNLOAD_LOCATIONS + ' + Me.BTT_RESET_DOWNLOAD_LOCATIONS.Dock = System.Windows.Forms.DockStyle.Right + Me.BTT_RESET_DOWNLOAD_LOCATIONS.Location = New System.Drawing.Point(382, 322) + Me.BTT_RESET_DOWNLOAD_LOCATIONS.Name = "BTT_RESET_DOWNLOAD_LOCATIONS" + Me.BTT_RESET_DOWNLOAD_LOCATIONS.Size = New System.Drawing.Size(190, 22) + Me.BTT_RESET_DOWNLOAD_LOCATIONS.TabIndex = 12 + Me.BTT_RESET_DOWNLOAD_LOCATIONS.Text = "Reset download locations" + TT_MAIN.SetToolTip(Me.BTT_RESET_DOWNLOAD_LOCATIONS, "All saved download locations will be deleted") + Me.BTT_RESET_DOWNLOAD_LOCATIONS.UseVisualStyleBackColor = True + ' + 'CH_STD_SNAP_KEEP_WITH_FILES + ' + Me.CH_STD_SNAP_KEEP_WITH_FILES.AutoSize = True + Me.CH_STD_SNAP_KEEP_WITH_FILES.Dock = System.Windows.Forms.DockStyle.Fill + Me.CH_STD_SNAP_KEEP_WITH_FILES.Location = New System.Drawing.Point(4, 140) + Me.CH_STD_SNAP_KEEP_WITH_FILES.Name = "CH_STD_SNAP_KEEP_WITH_FILES" + Me.CH_STD_SNAP_KEEP_WITH_FILES.Padding = New System.Windows.Forms.Padding(100, 0, 0, 0) + Me.CH_STD_SNAP_KEEP_WITH_FILES.Size = New System.Drawing.Size(568, 19) + Me.CH_STD_SNAP_KEEP_WITH_FILES.TabIndex = 5 + Me.CH_STD_SNAP_KEEP_WITH_FILES.Text = "Keep video thumbnail with files" + TT_MAIN.SetToolTip(Me.CH_STD_SNAP_KEEP_WITH_FILES, "Only works with 'Create video thumbnail'.") + Me.CH_STD_SNAP_KEEP_WITH_FILES.UseVisualStyleBackColor = True + ' + 'CH_STD_SNAP_CACHE_PERMANENT + ' + Me.CH_STD_SNAP_CACHE_PERMANENT.AutoSize = True + Me.CH_STD_SNAP_CACHE_PERMANENT.Dock = System.Windows.Forms.DockStyle.Fill + Me.CH_STD_SNAP_CACHE_PERMANENT.Location = New System.Drawing.Point(4, 166) + Me.CH_STD_SNAP_CACHE_PERMANENT.Name = "CH_STD_SNAP_CACHE_PERMANENT" + Me.CH_STD_SNAP_CACHE_PERMANENT.Padding = New System.Windows.Forms.Padding(100, 0, 0, 0) + Me.CH_STD_SNAP_CACHE_PERMANENT.Size = New System.Drawing.Size(568, 19) + Me.CH_STD_SNAP_CACHE_PERMANENT.TabIndex = 6 + Me.CH_STD_SNAP_CACHE_PERMANENT.Text = "Leave the thumbnails cache" + TT_MAIN.SetToolTip(Me.CH_STD_SNAP_CACHE_PERMANENT, "If disabled, video thumbnails will be deleted after SCrawler closes." & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "Only works " & + "with 'Create video thumbnail' and 'Keep video thumbnail with files'.") + Me.CH_STD_SNAP_CACHE_PERMANENT.UseVisualStyleBackColor = True + ' 'TP_CHANNELS_IMGS ' TP_CHANNELS_IMGS.ColumnCount = 2 @@ -978,6 +1127,7 @@ Namespace Editors Me.TXT_CHANNELS_ROWS.CaptionToolTipText = "How many lines of images should be shown in the channels form" Me.TXT_CHANNELS_ROWS.ControlMode = PersonalUtilities.Forms.Controls.TextBoxExtended.ControlModes.NumericUpDown Me.TXT_CHANNELS_ROWS.Dock = System.Windows.Forms.DockStyle.Fill + Me.TXT_CHANNELS_ROWS.Lines = New String(-1) {} Me.TXT_CHANNELS_ROWS.Location = New System.Drawing.Point(3, 3) Me.TXT_CHANNELS_ROWS.Name = "TXT_CHANNELS_ROWS" Me.TXT_CHANNELS_ROWS.Size = New System.Drawing.Size(278, 22) @@ -992,6 +1142,7 @@ Namespace Editors Me.TXT_CHANNELS_COLUMNS.CaptionToolTipText = "How many columns of images should be shown in the channels form" Me.TXT_CHANNELS_COLUMNS.ControlMode = PersonalUtilities.Forms.Controls.TextBoxExtended.ControlModes.NumericUpDown Me.TXT_CHANNELS_COLUMNS.Dock = System.Windows.Forms.DockStyle.Fill + Me.TXT_CHANNELS_COLUMNS.Lines = New String(-1) {} Me.TXT_CHANNELS_COLUMNS.Location = New System.Drawing.Point(287, 3) Me.TXT_CHANNELS_COLUMNS.Name = "TXT_CHANNELS_COLUMNS" Me.TXT_CHANNELS_COLUMNS.Size = New System.Drawing.Size(278, 22) @@ -1005,7 +1156,7 @@ Namespace Editors 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, 368) + TAB_BASIS.Size = New System.Drawing.Size(576, 451) TAB_BASIS.TabIndex = 0 TAB_BASIS.Text = "Basis" ' @@ -1015,7 +1166,7 @@ Namespace Editors TAB_DEFAULTS.Location = New System.Drawing.Point(4, 22) TAB_DEFAULTS.Name = "TAB_DEFAULTS" TAB_DEFAULTS.Padding = New System.Windows.Forms.Padding(3) - TAB_DEFAULTS.Size = New System.Drawing.Size(576, 368) + TAB_DEFAULTS.Size = New System.Drawing.Size(576, 451) TAB_DEFAULTS.TabIndex = 1 TAB_DEFAULTS.Text = "Defaults" ' @@ -1041,7 +1192,7 @@ Namespace Editors TP_DEFS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) TP_DEFS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) TP_DEFS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) - TP_DEFS.Size = New System.Drawing.Size(570, 362) + TP_DEFS.Size = New System.Drawing.Size(570, 445) TP_DEFS.TabIndex = 0 ' 'CH_DOWN_IMAGES_NATIVE @@ -1061,7 +1212,7 @@ Namespace Editors TAB_DEFS_CHANNELS.Location = New System.Drawing.Point(4, 22) TAB_DEFS_CHANNELS.Name = "TAB_DEFS_CHANNELS" TAB_DEFS_CHANNELS.Padding = New System.Windows.Forms.Padding(3) - TAB_DEFS_CHANNELS.Size = New System.Drawing.Size(576, 368) + TAB_DEFS_CHANNELS.Size = New System.Drawing.Size(576, 451) TAB_DEFS_CHANNELS.TabIndex = 4 TAB_DEFS_CHANNELS.Text = "Channels" ' @@ -1085,7 +1236,7 @@ Namespace Editors TP_CHANNELS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) TP_CHANNELS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) TP_CHANNELS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) - TP_CHANNELS.Size = New System.Drawing.Size(570, 362) + TP_CHANNELS.Size = New System.Drawing.Size(570, 445) TP_CHANNELS.TabIndex = 0 ' 'TXT_CHANNEL_USER_POST_LIMIT @@ -1099,6 +1250,7 @@ Namespace Editors Me.TXT_CHANNEL_USER_POST_LIMIT.CaptionWidth = 50.0R Me.TXT_CHANNEL_USER_POST_LIMIT.ControlMode = PersonalUtilities.Forms.Controls.TextBoxExtended.ControlModes.NumericUpDown Me.TXT_CHANNEL_USER_POST_LIMIT.Dock = System.Windows.Forms.DockStyle.Fill + Me.TXT_CHANNEL_USER_POST_LIMIT.Lines = New String(-1) {} Me.TXT_CHANNEL_USER_POST_LIMIT.Location = New System.Drawing.Point(4, 33) Me.TXT_CHANNEL_USER_POST_LIMIT.Name = "TXT_CHANNEL_USER_POST_LIMIT" Me.TXT_CHANNEL_USER_POST_LIMIT.NumberMaximum = New Decimal(New Integer() {1000, 0, 0, 0}) @@ -1113,7 +1265,7 @@ Namespace Editors TAB_BEHAVIOR.Controls.Add(TP_BEHAVIOR) TAB_BEHAVIOR.Location = New System.Drawing.Point(4, 22) TAB_BEHAVIOR.Name = "TAB_BEHAVIOR" - TAB_BEHAVIOR.Size = New System.Drawing.Size(576, 368) + TAB_BEHAVIOR.Size = New System.Drawing.Size(576, 451) TAB_BEHAVIOR.TabIndex = 5 TAB_BEHAVIOR.Text = "Behavior" ' @@ -1144,23 +1296,24 @@ Namespace Editors TP_BEHAVIOR.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) TP_BEHAVIOR.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) TP_BEHAVIOR.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20.0!)) - TP_BEHAVIOR.Size = New System.Drawing.Size(576, 368) + TP_BEHAVIOR.Size = New System.Drawing.Size(576, 451) TP_BEHAVIOR.TabIndex = 0 ' 'TXT_FOLDER_CMD ' Me.TXT_FOLDER_CMD.AutoShowClearButton = True - ActionButton11.BackgroundImage = CType(resources.GetObject("ActionButton11.BackgroundImage"), System.Drawing.Image) - ActionButton11.Enabled = False - ActionButton11.Name = "Clear" - ActionButton11.Visible = False - Me.TXT_FOLDER_CMD.Buttons.Add(ActionButton11) + ActionButton13.BackgroundImage = CType(resources.GetObject("ActionButton13.BackgroundImage"), System.Drawing.Image) + ActionButton13.Enabled = False + ActionButton13.Name = "Clear" + ActionButton13.Visible = False + Me.TXT_FOLDER_CMD.Buttons.Add(ActionButton13) Me.TXT_FOLDER_CMD.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.CheckBox Me.TXT_FOLDER_CMD.CaptionText = "Folder cmd" Me.TXT_FOLDER_CMD.CaptionToolTipEnabled = True Me.TXT_FOLDER_CMD.CaptionToolTipText = "The command to open a folder." Me.TXT_FOLDER_CMD.Dock = System.Windows.Forms.DockStyle.Fill Me.TXT_FOLDER_CMD.LeaveDefaultButtons = True + Me.TXT_FOLDER_CMD.Lines = New String(-1) {} Me.TXT_FOLDER_CMD.Location = New System.Drawing.Point(4, 160) Me.TXT_FOLDER_CMD.Name = "TXT_FOLDER_CMD" Me.TXT_FOLDER_CMD.PlaceholderEnabled = True @@ -1193,17 +1346,18 @@ Namespace Editors 'TXT_CLOSE_SCRIPT ' Me.TXT_CLOSE_SCRIPT.AutoShowClearButton = True - ActionButton12.BackgroundImage = CType(resources.GetObject("ActionButton12.BackgroundImage"), System.Drawing.Image) - ActionButton12.Enabled = False - ActionButton12.Name = "Clear" - ActionButton12.Visible = False - Me.TXT_CLOSE_SCRIPT.Buttons.Add(ActionButton12) + ActionButton14.BackgroundImage = CType(resources.GetObject("ActionButton14.BackgroundImage"), System.Drawing.Image) + ActionButton14.Enabled = False + ActionButton14.Name = "Clear" + ActionButton14.Visible = False + Me.TXT_CLOSE_SCRIPT.Buttons.Add(ActionButton14) Me.TXT_CLOSE_SCRIPT.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.CheckBox Me.TXT_CLOSE_SCRIPT.CaptionText = "Close cmd" Me.TXT_CLOSE_SCRIPT.CaptionToolTipEnabled = True Me.TXT_CLOSE_SCRIPT.CaptionToolTipText = "This command will be executed when SCrawler is closed" Me.TXT_CLOSE_SCRIPT.Dock = System.Windows.Forms.DockStyle.Fill Me.TXT_CLOSE_SCRIPT.LeaveDefaultButtons = True + Me.TXT_CLOSE_SCRIPT.Lines = New String(-1) {} Me.TXT_CLOSE_SCRIPT.Location = New System.Drawing.Point(4, 189) Me.TXT_CLOSE_SCRIPT.Name = "TXT_CLOSE_SCRIPT" Me.TXT_CLOSE_SCRIPT.PlaceholderEnabled = True @@ -1285,7 +1439,7 @@ Namespace Editors TAB_DOWN.Controls.Add(TP_DOWNLOADING) TAB_DOWN.Location = New System.Drawing.Point(4, 22) TAB_DOWN.Name = "TAB_DOWN" - TAB_DOWN.Size = New System.Drawing.Size(576, 368) + TAB_DOWN.Size = New System.Drawing.Size(576, 451) TAB_DOWN.TabIndex = 6 TAB_DOWN.Text = "Downloading" ' @@ -1317,17 +1471,17 @@ Namespace Editors TP_DOWNLOADING.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) TP_DOWNLOADING.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) TP_DOWNLOADING.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) - TP_DOWNLOADING.Size = New System.Drawing.Size(576, 368) + TP_DOWNLOADING.Size = New System.Drawing.Size(576, 451) TP_DOWNLOADING.TabIndex = 1 ' 'TXT_SCRIPT ' - ActionButton13.BackgroundImage = CType(resources.GetObject("ActionButton13.BackgroundImage"), System.Drawing.Image) - ActionButton13.Name = "Open" - ActionButton14.BackgroundImage = CType(resources.GetObject("ActionButton14.BackgroundImage"), System.Drawing.Image) - ActionButton14.Name = "Clear" - Me.TXT_SCRIPT.Buttons.Add(ActionButton13) - Me.TXT_SCRIPT.Buttons.Add(ActionButton14) + ActionButton15.BackgroundImage = CType(resources.GetObject("ActionButton15.BackgroundImage"), System.Drawing.Image) + ActionButton15.Name = "Open" + ActionButton16.BackgroundImage = CType(resources.GetObject("ActionButton16.BackgroundImage"), System.Drawing.Image) + ActionButton16.Name = "Clear" + Me.TXT_SCRIPT.Buttons.Add(ActionButton15) + Me.TXT_SCRIPT.Buttons.Add(ActionButton16) Me.TXT_SCRIPT.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.CheckBox Me.TXT_SCRIPT.CaptionText = "Script" Me.TXT_SCRIPT.CaptionToolTipEnabled = True @@ -1336,6 +1490,7 @@ Namespace Editors Me.TXT_SCRIPT.CaptionWidth = 120.0R Me.TXT_SCRIPT.ChangeControlsEnableOnCheckedChange = False Me.TXT_SCRIPT.Dock = System.Windows.Forms.DockStyle.Fill + Me.TXT_SCRIPT.Lines = New String(-1) {} Me.TXT_SCRIPT.Location = New System.Drawing.Point(4, 144) Me.TXT_SCRIPT.Name = "TXT_SCRIPT" Me.TXT_SCRIPT.PlaceholderEnabled = True @@ -1351,6 +1506,7 @@ Namespace Editors Me.TXT_DOWN_COMPLETE_SCRIPT.CaptionToolTipText = "This command will be executed after all downloads are completed" Me.TXT_DOWN_COMPLETE_SCRIPT.CaptionWidth = 120.0R Me.TXT_DOWN_COMPLETE_SCRIPT.Dock = System.Windows.Forms.DockStyle.Fill + Me.TXT_DOWN_COMPLETE_SCRIPT.Lines = New String(-1) {} Me.TXT_DOWN_COMPLETE_SCRIPT.Location = New System.Drawing.Point(4, 173) Me.TXT_DOWN_COMPLETE_SCRIPT.Name = "TXT_DOWN_COMPLETE_SCRIPT" Me.TXT_DOWN_COMPLETE_SCRIPT.PlaceholderEnabled = True @@ -1387,12 +1543,23 @@ Namespace Editors Me.CH_UNAME_UP.Text = "Update user site name every time" Me.CH_UNAME_UP.UseVisualStyleBackColor = True ' + 'CH_UICON_UP + ' + Me.CH_UICON_UP.AutoSize = True + Me.CH_UICON_UP.Dock = System.Windows.Forms.DockStyle.Fill + Me.CH_UICON_UP.Location = New System.Drawing.Point(4, 56) + Me.CH_UICON_UP.Name = "CH_UICON_UP" + Me.CH_UICON_UP.Size = New System.Drawing.Size(568, 19) + Me.CH_UICON_UP.TabIndex = 2 + Me.CH_UICON_UP.Text = "Update user icon and banner every time (where supported)" + Me.CH_UICON_UP.UseVisualStyleBackColor = True + ' 'TAB_FEED ' TAB_FEED.Controls.Add(TP_FEED) TAB_FEED.Location = New System.Drawing.Point(4, 22) TAB_FEED.Name = "TAB_FEED" - TAB_FEED.Size = New System.Drawing.Size(576, 368) + TAB_FEED.Size = New System.Drawing.Size(576, 451) TAB_FEED.TabIndex = 7 TAB_FEED.Text = "Feed" ' @@ -1408,10 +1575,12 @@ Namespace Editors TP_FEED.Controls.Add(Me.CH_FEED_STORE_SESSION_DATA, 0, 6) TP_FEED.Controls.Add(Me.TXT_FEED_CENTER_IMAGE, 0, 1) TP_FEED.Controls.Add(Me.COLORS_FEED, 0, 2) + TP_FEED.Controls.Add(Me.CH_FEED_OPEN_LAST_MODE, 0, 7) + TP_FEED.Controls.Add(Me.CH_FEED_SHOW_FRIENDLY, 0, 8) TP_FEED.Dock = System.Windows.Forms.DockStyle.Fill TP_FEED.Location = New System.Drawing.Point(0, 0) TP_FEED.Name = "TP_FEED" - TP_FEED.RowCount = 8 + TP_FEED.RowCount = 10 TP_FEED.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) TP_FEED.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) TP_FEED.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) @@ -1419,9 +1588,10 @@ Namespace Editors TP_FEED.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) TP_FEED.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) TP_FEED.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) + TP_FEED.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) + TP_FEED.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) TP_FEED.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) - TP_FEED.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20.0!)) - TP_FEED.Size = New System.Drawing.Size(576, 368) + TP_FEED.Size = New System.Drawing.Size(576, 451) TP_FEED.TabIndex = 0 ' 'TP_FEED_IMG_COUNT @@ -1448,6 +1618,7 @@ Namespace Editors Me.TXT_FEED_ROWS.CaptionToolTipText = "How many lines of images should be shown in the feed form" Me.TXT_FEED_ROWS.ControlMode = PersonalUtilities.Forms.Controls.TextBoxExtended.ControlModes.NumericUpDown Me.TXT_FEED_ROWS.Dock = System.Windows.Forms.DockStyle.Fill + Me.TXT_FEED_ROWS.Lines = New String(-1) {} Me.TXT_FEED_ROWS.Location = New System.Drawing.Point(3, 3) Me.TXT_FEED_ROWS.Name = "TXT_FEED_ROWS" Me.TXT_FEED_ROWS.NumberMaximum = New Decimal(New Integer() {50, 0, 0, 0}) @@ -1464,6 +1635,7 @@ Namespace Editors Me.TXT_FEED_COLUMNS.CaptionToolTipText = "How many columns of images should be shown in the feed form" Me.TXT_FEED_COLUMNS.ControlMode = PersonalUtilities.Forms.Controls.TextBoxExtended.ControlModes.NumericUpDown Me.TXT_FEED_COLUMNS.Dock = System.Windows.Forms.DockStyle.Fill + Me.TXT_FEED_COLUMNS.Lines = New String(-1) {} Me.TXT_FEED_COLUMNS.Location = New System.Drawing.Point(290, 3) Me.TXT_FEED_COLUMNS.Name = "TXT_FEED_COLUMNS" Me.TXT_FEED_COLUMNS.NumberMaximum = New Decimal(New Integer() {20, 0, 0, 0}) @@ -1517,6 +1689,7 @@ Namespace Editors Me.TXT_FEED_CENTER_IMAGE.CaptionWidth = 50.0R Me.TXT_FEED_CENTER_IMAGE.ControlMode = PersonalUtilities.Forms.Controls.TextBoxExtended.ControlModes.NumericUpDown Me.TXT_FEED_CENTER_IMAGE.Dock = System.Windows.Forms.DockStyle.Fill + Me.TXT_FEED_CENTER_IMAGE.Lines = New String(-1) {} Me.TXT_FEED_CENTER_IMAGE.Location = New System.Drawing.Point(4, 33) Me.TXT_FEED_CENTER_IMAGE.Margin = New System.Windows.Forms.Padding(3, 3, 2, 3) Me.TXT_FEED_CENTER_IMAGE.Name = "TXT_FEED_CENTER_IMAGE" @@ -1543,7 +1716,7 @@ Namespace Editors TAB_NOTIFY.Controls.Add(TP_NOTIFY_MAIN) TAB_NOTIFY.Location = New System.Drawing.Point(4, 22) TAB_NOTIFY.Name = "TAB_NOTIFY" - TAB_NOTIFY.Size = New System.Drawing.Size(576, 368) + TAB_NOTIFY.Size = New System.Drawing.Size(576, 451) TAB_NOTIFY.TabIndex = 8 TAB_NOTIFY.Text = "Notifications" ' @@ -1573,7 +1746,7 @@ Namespace Editors TP_NOTIFY_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) TP_NOTIFY_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) TP_NOTIFY_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) - TP_NOTIFY_MAIN.Size = New System.Drawing.Size(576, 368) + TP_NOTIFY_MAIN.Size = New System.Drawing.Size(576, 451) TP_NOTIFY_MAIN.TabIndex = 0 ' 'TP_ENVIR @@ -1596,24 +1769,25 @@ Namespace Editors TP_ENVIR.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) TP_ENVIR.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) TP_ENVIR.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) - TP_ENVIR.Size = New System.Drawing.Size(576, 368) + TP_ENVIR.Size = New System.Drawing.Size(576, 451) TP_ENVIR.TabIndex = 0 ' 'TXT_YTDLP ' - ActionButton15.BackgroundImage = CType(resources.GetObject("ActionButton15.BackgroundImage"), System.Drawing.Image) - ActionButton15.Name = "Open" - ActionButton15.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open - ActionButton16.BackgroundImage = CType(resources.GetObject("ActionButton16.BackgroundImage"), System.Drawing.Image) - ActionButton16.Name = "Clear" - ActionButton16.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear - Me.TXT_YTDLP.Buttons.Add(ActionButton15) - Me.TXT_YTDLP.Buttons.Add(ActionButton16) + ActionButton17.BackgroundImage = CType(resources.GetObject("ActionButton17.BackgroundImage"), System.Drawing.Image) + ActionButton17.Name = "Open" + ActionButton17.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open + ActionButton18.BackgroundImage = CType(resources.GetObject("ActionButton18.BackgroundImage"), System.Drawing.Image) + ActionButton18.Name = "Clear" + ActionButton18.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear + Me.TXT_YTDLP.Buttons.Add(ActionButton17) + Me.TXT_YTDLP.Buttons.Add(ActionButton18) Me.TXT_YTDLP.CaptionText = "yt-dlp" Me.TXT_YTDLP.CaptionToolTipEnabled = True Me.TXT_YTDLP.CaptionToolTipText = "Path to yt-dlp.exe file" Me.TXT_YTDLP.CaptionWidth = 80.0R Me.TXT_YTDLP.Dock = System.Windows.Forms.DockStyle.Fill + Me.TXT_YTDLP.Lines = New String(-1) {} Me.TXT_YTDLP.Location = New System.Drawing.Point(4, 62) Me.TXT_YTDLP.Name = "TXT_YTDLP" Me.TXT_YTDLP.Size = New System.Drawing.Size(568, 22) @@ -1622,19 +1796,20 @@ Namespace Editors ' 'TXT_FFMPEG ' - ActionButton17.BackgroundImage = CType(resources.GetObject("ActionButton17.BackgroundImage"), System.Drawing.Image) - ActionButton17.Name = "Open" - ActionButton17.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open - ActionButton18.BackgroundImage = CType(resources.GetObject("ActionButton18.BackgroundImage"), System.Drawing.Image) - ActionButton18.Name = "Clear" - ActionButton18.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear - Me.TXT_FFMPEG.Buttons.Add(ActionButton17) - Me.TXT_FFMPEG.Buttons.Add(ActionButton18) + ActionButton19.BackgroundImage = CType(resources.GetObject("ActionButton19.BackgroundImage"), System.Drawing.Image) + ActionButton19.Name = "Open" + ActionButton19.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open + ActionButton20.BackgroundImage = CType(resources.GetObject("ActionButton20.BackgroundImage"), System.Drawing.Image) + ActionButton20.Name = "Clear" + ActionButton20.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear + Me.TXT_FFMPEG.Buttons.Add(ActionButton19) + Me.TXT_FFMPEG.Buttons.Add(ActionButton20) Me.TXT_FFMPEG.CaptionText = "ffmpeg" Me.TXT_FFMPEG.CaptionToolTipEnabled = True Me.TXT_FFMPEG.CaptionToolTipText = "Path to ffmpeg.exe file" Me.TXT_FFMPEG.CaptionWidth = 80.0R Me.TXT_FFMPEG.Dock = System.Windows.Forms.DockStyle.Fill + Me.TXT_FFMPEG.Lines = New String(-1) {} Me.TXT_FFMPEG.Location = New System.Drawing.Point(4, 4) Me.TXT_FFMPEG.Name = "TXT_FFMPEG" Me.TXT_FFMPEG.Size = New System.Drawing.Size(568, 22) @@ -1643,19 +1818,20 @@ Namespace Editors ' 'TXT_CURL ' - ActionButton19.BackgroundImage = CType(resources.GetObject("ActionButton19.BackgroundImage"), System.Drawing.Image) - ActionButton19.Name = "Open" - ActionButton19.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open - ActionButton20.BackgroundImage = CType(resources.GetObject("ActionButton20.BackgroundImage"), System.Drawing.Image) - ActionButton20.Name = "Clear" - ActionButton20.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear - Me.TXT_CURL.Buttons.Add(ActionButton19) - Me.TXT_CURL.Buttons.Add(ActionButton20) + ActionButton21.BackgroundImage = CType(resources.GetObject("ActionButton21.BackgroundImage"), System.Drawing.Image) + ActionButton21.Name = "Open" + ActionButton21.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open + ActionButton22.BackgroundImage = CType(resources.GetObject("ActionButton22.BackgroundImage"), System.Drawing.Image) + ActionButton22.Name = "Clear" + ActionButton22.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear + Me.TXT_CURL.Buttons.Add(ActionButton21) + Me.TXT_CURL.Buttons.Add(ActionButton22) Me.TXT_CURL.CaptionText = "cURL" Me.TXT_CURL.CaptionToolTipEnabled = True Me.TXT_CURL.CaptionToolTipText = "Path to curl.exe file" Me.TXT_CURL.CaptionWidth = 80.0R Me.TXT_CURL.Dock = System.Windows.Forms.DockStyle.Fill + Me.TXT_CURL.Lines = New String(-1) {} Me.TXT_CURL.Location = New System.Drawing.Point(4, 33) Me.TXT_CURL.Name = "TXT_CURL" Me.TXT_CURL.Size = New System.Drawing.Size(568, 22) @@ -1664,18 +1840,19 @@ Namespace Editors ' 'TXT_GALLERYDL ' - ActionButton21.BackgroundImage = CType(resources.GetObject("ActionButton21.BackgroundImage"), System.Drawing.Image) - ActionButton21.Name = "Open" - ActionButton21.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open - ActionButton22.BackgroundImage = CType(resources.GetObject("ActionButton22.BackgroundImage"), System.Drawing.Image) - ActionButton22.Name = "Clear" - ActionButton22.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear - Me.TXT_GALLERYDL.Buttons.Add(ActionButton21) - Me.TXT_GALLERYDL.Buttons.Add(ActionButton22) + ActionButton23.BackgroundImage = CType(resources.GetObject("ActionButton23.BackgroundImage"), System.Drawing.Image) + ActionButton23.Name = "Open" + ActionButton23.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open + ActionButton24.BackgroundImage = CType(resources.GetObject("ActionButton24.BackgroundImage"), System.Drawing.Image) + ActionButton24.Name = "Clear" + ActionButton24.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear + Me.TXT_GALLERYDL.Buttons.Add(ActionButton23) + Me.TXT_GALLERYDL.Buttons.Add(ActionButton24) Me.TXT_GALLERYDL.CaptionText = "gallery-dl" Me.TXT_GALLERYDL.CaptionToolTipText = "Path to gallery-dl.exe file" Me.TXT_GALLERYDL.CaptionWidth = 80.0R Me.TXT_GALLERYDL.Dock = System.Windows.Forms.DockStyle.Fill + Me.TXT_GALLERYDL.Lines = New String(-1) {} Me.TXT_GALLERYDL.Location = New System.Drawing.Point(4, 91) Me.TXT_GALLERYDL.Name = "TXT_GALLERYDL" Me.TXT_GALLERYDL.Size = New System.Drawing.Size(568, 22) @@ -1684,19 +1861,20 @@ Namespace Editors ' 'TXT_CMD_ENCODING ' - ActionButton23.BackgroundImage = CType(resources.GetObject("ActionButton23.BackgroundImage"), System.Drawing.Image) - ActionButton23.Name = "Refresh" - ActionButton23.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Refresh - ActionButton24.BackgroundImage = CType(resources.GetObject("ActionButton24.BackgroundImage"), System.Drawing.Image) - ActionButton24.Name = "Clear" - ActionButton24.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear - Me.TXT_CMD_ENCODING.Buttons.Add(ActionButton23) - Me.TXT_CMD_ENCODING.Buttons.Add(ActionButton24) + ActionButton25.BackgroundImage = CType(resources.GetObject("ActionButton25.BackgroundImage"), System.Drawing.Image) + ActionButton25.Name = "Refresh" + ActionButton25.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Refresh + ActionButton26.BackgroundImage = CType(resources.GetObject("ActionButton26.BackgroundImage"), System.Drawing.Image) + ActionButton26.Name = "Clear" + ActionButton26.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear + Me.TXT_CMD_ENCODING.Buttons.Add(ActionButton25) + Me.TXT_CMD_ENCODING.Buttons.Add(ActionButton26) Me.TXT_CMD_ENCODING.CaptionText = "CMD Encoding" Me.TXT_CMD_ENCODING.CaptionToolTipEnabled = True Me.TXT_CMD_ENCODING.CaptionToolTipText = "Command line encoding" Me.TXT_CMD_ENCODING.CaptionWidth = 80.0R Me.TXT_CMD_ENCODING.Dock = System.Windows.Forms.DockStyle.Fill + Me.TXT_CMD_ENCODING.Lines = New String(-1) {} Me.TXT_CMD_ENCODING.Location = New System.Drawing.Point(4, 120) Me.TXT_CMD_ENCODING.Name = "TXT_CMD_ENCODING" Me.TXT_CMD_ENCODING.Size = New System.Drawing.Size(568, 22) @@ -1707,7 +1885,7 @@ Namespace Editors TAB_STD.Controls.Add(TP_STD) TAB_STD.Location = New System.Drawing.Point(4, 22) TAB_STD.Name = "TAB_STD" - TAB_STD.Size = New System.Drawing.Size(576, 368) + TAB_STD.Size = New System.Drawing.Size(576, 451) TAB_STD.TabIndex = 10 TAB_STD.Text = "Downloader" ' @@ -1721,13 +1899,18 @@ Namespace Editors TP_STD.Controls.Add(Me.CH_STD_AUTO_REMOVE, 0, 2) TP_STD.Controls.Add(Me.CMB_STD_OPEN_DBL, 0, 3) TP_STD.Controls.Add(Me.CH_STD_TAKESNAP, 0, 4) - TP_STD.Controls.Add(Me.CH_STD_UPDATE_YT_PATH, 0, 5) - TP_STD.Controls.Add(Me.CH_STD_YT_LOAD, 0, 6) - TP_STD.Controls.Add(Me.CH_STD_YT_REMOVE, 0, 7) + TP_STD.Controls.Add(Me.CH_STD_UPDATE_YT_PATH, 0, 7) + TP_STD.Controls.Add(Me.CH_STD_YT_LOAD, 0, 8) + TP_STD.Controls.Add(Me.CH_STD_YT_REMOVE, 0, 9) + TP_STD.Controls.Add(Me.CH_STD_YT_OUTPUT_ASK_NAME, 0, 10) + TP_STD.Controls.Add(Me.CH_STD_YT_OUTPUT_AUTO_ADD, 0, 11) + TP_STD.Controls.Add(Me.BTT_RESET_DOWNLOAD_LOCATIONS, 0, 12) + TP_STD.Controls.Add(Me.CH_STD_SNAP_KEEP_WITH_FILES, 0, 5) + TP_STD.Controls.Add(Me.CH_STD_SNAP_CACHE_PERMANENT, 0, 6) TP_STD.Dock = System.Windows.Forms.DockStyle.Fill TP_STD.Location = New System.Drawing.Point(0, 0) TP_STD.Name = "TP_STD" - TP_STD.RowCount = 9 + TP_STD.RowCount = 14 TP_STD.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) TP_STD.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) TP_STD.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) @@ -1736,8 +1919,13 @@ Namespace Editors TP_STD.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) TP_STD.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) TP_STD.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) + TP_STD.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) + TP_STD.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) + TP_STD.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) + TP_STD.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) + TP_STD.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) TP_STD.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) - TP_STD.Size = New System.Drawing.Size(576, 368) + TP_STD.Size = New System.Drawing.Size(576, 451) TP_STD.TabIndex = 0 ' 'TXT_STD_MAX_JOBS_COUNT @@ -1747,6 +1935,7 @@ Namespace Editors Me.TXT_STD_MAX_JOBS_COUNT.CaptionToolTipText = "Maximum number of jobs" Me.TXT_STD_MAX_JOBS_COUNT.ControlMode = PersonalUtilities.Forms.Controls.TextBoxExtended.ControlModes.NumericUpDown Me.TXT_STD_MAX_JOBS_COUNT.Dock = System.Windows.Forms.DockStyle.Fill + Me.TXT_STD_MAX_JOBS_COUNT.Lines = New String(-1) {} Me.TXT_STD_MAX_JOBS_COUNT.Location = New System.Drawing.Point(4, 4) Me.TXT_STD_MAX_JOBS_COUNT.Name = "TXT_STD_MAX_JOBS_COUNT" Me.TXT_STD_MAX_JOBS_COUNT.NumberMaximum = New Decimal(New Integer() {10, 0, 0, 0}) @@ -1782,10 +1971,10 @@ Namespace Editors ' 'CMB_STD_OPEN_DBL ' - ActionButton25.BackgroundImage = CType(resources.GetObject("ActionButton25.BackgroundImage"), System.Drawing.Image) - ActionButton25.Name = "ArrowDown" - ActionButton25.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.ArrowDown - Me.CMB_STD_OPEN_DBL.Buttons.Add(ActionButton25) + ActionButton27.BackgroundImage = CType(resources.GetObject("ActionButton27.BackgroundImage"), System.Drawing.Image) + ActionButton27.Name = "ArrowDown" + ActionButton27.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.ArrowDown + Me.CMB_STD_OPEN_DBL.Buttons.Add(ActionButton27) Me.CMB_STD_OPEN_DBL.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.Label Me.CMB_STD_OPEN_DBL.CaptionText = "DoubleClick opens" Me.CMB_STD_OPEN_DBL.CaptionToolTipEnabled = True @@ -1801,6 +1990,7 @@ Namespace Editors Me.CMB_STD_OPEN_DBL.Columns.Add(ListColumn1) Me.CMB_STD_OPEN_DBL.Columns.Add(ListColumn2) Me.CMB_STD_OPEN_DBL.Dock = System.Windows.Forms.DockStyle.Fill + Me.CMB_STD_OPEN_DBL.Lines = New String(-1) {} Me.CMB_STD_OPEN_DBL.Location = New System.Drawing.Point(4, 85) Me.CMB_STD_OPEN_DBL.Name = "CMB_STD_OPEN_DBL" Me.CMB_STD_OPEN_DBL.Size = New System.Drawing.Size(568, 22) @@ -1823,11 +2013,11 @@ Namespace Editors ' Me.CH_STD_UPDATE_YT_PATH.AutoSize = True Me.CH_STD_UPDATE_YT_PATH.Dock = System.Windows.Forms.DockStyle.Fill - Me.CH_STD_UPDATE_YT_PATH.Location = New System.Drawing.Point(4, 140) + Me.CH_STD_UPDATE_YT_PATH.Location = New System.Drawing.Point(4, 192) Me.CH_STD_UPDATE_YT_PATH.Name = "CH_STD_UPDATE_YT_PATH" Me.CH_STD_UPDATE_YT_PATH.Padding = New System.Windows.Forms.Padding(100, 0, 0, 0) Me.CH_STD_UPDATE_YT_PATH.Size = New System.Drawing.Size(568, 19) - Me.CH_STD_UPDATE_YT_PATH.TabIndex = 5 + Me.CH_STD_UPDATE_YT_PATH.TabIndex = 7 Me.CH_STD_UPDATE_YT_PATH.Text = "Update the YouTube output path when you change the output path." Me.CH_STD_UPDATE_YT_PATH.UseVisualStyleBackColor = True ' @@ -1846,7 +2036,7 @@ Namespace Editors Me.TAB_MAIN.Location = New System.Drawing.Point(0, 0) Me.TAB_MAIN.Name = "TAB_MAIN" Me.TAB_MAIN.SelectedIndex = 0 - Me.TAB_MAIN.Size = New System.Drawing.Size(584, 394) + Me.TAB_MAIN.Size = New System.Drawing.Size(584, 477) Me.TAB_MAIN.TabIndex = 1 ' 'TAB_ENVIR @@ -1854,7 +2044,7 @@ Namespace Editors Me.TAB_ENVIR.Controls.Add(TP_ENVIR) Me.TAB_ENVIR.Location = New System.Drawing.Point(4, 22) Me.TAB_ENVIR.Name = "TAB_ENVIR" - Me.TAB_ENVIR.Size = New System.Drawing.Size(576, 368) + Me.TAB_ENVIR.Size = New System.Drawing.Size(576, 451) Me.TAB_ENVIR.TabIndex = 9 Me.TAB_ENVIR.Text = "Environment" ' @@ -1864,40 +2054,40 @@ Namespace Editors 'CONTAINER_MAIN.ContentPanel ' Me.CONTAINER_MAIN.ContentPanel.Controls.Add(Me.TAB_MAIN) - Me.CONTAINER_MAIN.ContentPanel.Size = New System.Drawing.Size(584, 394) + Me.CONTAINER_MAIN.ContentPanel.Size = New System.Drawing.Size(584, 477) Me.CONTAINER_MAIN.Dock = System.Windows.Forms.DockStyle.Fill Me.CONTAINER_MAIN.LeftToolStripPanelVisible = False Me.CONTAINER_MAIN.Location = New System.Drawing.Point(0, 0) Me.CONTAINER_MAIN.Name = "CONTAINER_MAIN" Me.CONTAINER_MAIN.RightToolStripPanelVisible = False - Me.CONTAINER_MAIN.Size = New System.Drawing.Size(584, 394) + Me.CONTAINER_MAIN.Size = New System.Drawing.Size(584, 477) Me.CONTAINER_MAIN.TabIndex = 0 Me.CONTAINER_MAIN.TopToolStripPanelVisible = False ' - 'CH_UICON_UP + 'CH_FEED_SHOW_FRIENDLY ' - Me.CH_UICON_UP.AutoSize = True - Me.CH_UICON_UP.Dock = System.Windows.Forms.DockStyle.Fill - Me.CH_UICON_UP.Location = New System.Drawing.Point(4, 56) - Me.CH_UICON_UP.Name = "CH_UICON_UP" - Me.CH_UICON_UP.Size = New System.Drawing.Size(568, 19) - Me.CH_UICON_UP.TabIndex = 2 - Me.CH_UICON_UP.Text = "Update user icon and banner every time (where supported)" - Me.CH_UICON_UP.UseVisualStyleBackColor = True + Me.CH_FEED_SHOW_FRIENDLY.AutoSize = True + Me.CH_FEED_SHOW_FRIENDLY.Dock = System.Windows.Forms.DockStyle.Fill + Me.CH_FEED_SHOW_FRIENDLY.Location = New System.Drawing.Point(4, 218) + Me.CH_FEED_SHOW_FRIENDLY.Name = "CH_FEED_SHOW_FRIENDLY" + Me.CH_FEED_SHOW_FRIENDLY.Size = New System.Drawing.Size(568, 19) + Me.CH_FEED_SHOW_FRIENDLY.TabIndex = 8 + Me.CH_FEED_SHOW_FRIENDLY.Text = "Show friendly names instead of usernames" + Me.CH_FEED_SHOW_FRIENDLY.UseVisualStyleBackColor = True ' 'GlobalSettingsForm ' Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!) Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font - Me.ClientSize = New System.Drawing.Size(584, 394) + Me.ClientSize = New System.Drawing.Size(584, 477) Me.Controls.Add(Me.CONTAINER_MAIN) Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle Me.Icon = Global.SCrawler.My.Resources.Resources.SettingsIcon_48 Me.KeyPreview = True Me.MaximizeBox = False - Me.MaximumSize = New System.Drawing.Size(600, 433) + Me.MaximumSize = New System.Drawing.Size(600, 516) Me.MinimizeBox = False - Me.MinimumSize = New System.Drawing.Size(600, 433) + Me.MinimumSize = New System.Drawing.Size(600, 516) Me.Name = "GlobalSettingsForm" Me.ShowInTaskbar = False Me.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide @@ -1914,6 +2104,8 @@ Namespace Editors CType(Me.TXT_IMGUR_CLIENT_ID, System.ComponentModel.ISupportInitialize).EndInit() CType(Me.TXT_USER_AGENT, System.ComponentModel.ISupportInitialize).EndInit() CType(Me.TXT_USER_LIST_IMAGE, System.ComponentModel.ISupportInitialize).EndInit() + CType(Me.TXT_PRG_TITLE, System.ComponentModel.ISupportInitialize).EndInit() + CType(Me.TXT_PRG_DESCR, System.ComponentModel.ISupportInitialize).EndInit() TP_FILE_NAME.ResumeLayout(False) TP_FILE_NAME.PerformLayout() TP_FILE_PATTERNS.ResumeLayout(False) @@ -2056,5 +2248,15 @@ Namespace Editors Private WithEvents CH_STD_YT_LOAD As CheckBox Private WithEvents CH_STD_YT_REMOVE As CheckBox Private WithEvents CH_UICON_UP As CheckBox + Private WithEvents COLORS_SUBSCRIPTIONS As ColorPicker + Private WithEvents CH_FEED_OPEN_LAST_MODE As CheckBox + Private WithEvents CH_STD_YT_OUTPUT_ASK_NAME As CheckBox + Private WithEvents CH_STD_YT_OUTPUT_AUTO_ADD As CheckBox + Private WithEvents BTT_RESET_DOWNLOAD_LOCATIONS As Button + Private WithEvents CH_STD_SNAP_KEEP_WITH_FILES As CheckBox + Private WithEvents CH_STD_SNAP_CACHE_PERMANENT As CheckBox + Private WithEvents TXT_PRG_TITLE As PersonalUtilities.Forms.Controls.TextBoxExtended + Private WithEvents TXT_PRG_DESCR As PersonalUtilities.Forms.Controls.TextBoxExtended + Private WithEvents CH_FEED_SHOW_FRIENDLY As CheckBox End Class End Namespace \ No newline at end of file diff --git a/SCrawler/Editors/GlobalSettingsForm.resx b/SCrawler/Editors/GlobalSettingsForm.resx index 1ff4215..847ff0a 100644 --- a/SCrawler/Editors/GlobalSettingsForm.resx +++ b/SCrawler/Editors/GlobalSettingsForm.resx @@ -238,6 +238,22 @@ xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go tbbB43rK5xSAQq1VYFtmeQBoqZTSreVZvgTknM8yyyjA/qodsDF9gspD2Bj6B+DH+NqzhQQAG+POMnSX AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO + xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go + tbbB43rK5xSAQq1VYFtmeQBoqZTSreVZvgTknM8yyyjA/qodsDF9gspD2Bj6B+DH+NqzhQQAG+POMnSX + AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO + xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go + tbbB43rK5xSAQq1VYFtmeQBoqZTSreVZvgTknM8yyyjA/qodsDF9gspD2Bj6B+DH+NqzhQQAG+POMnSX + AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC @@ -288,7 +304,7 @@ You can find more detailed information about the missing posts in the form that False - + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go @@ -296,7 +312,7 @@ You can find more detailed information about the missing posts in the form that AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC - + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go @@ -316,7 +332,7 @@ You can find more detailed information about the missing posts in the form that False - + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP @@ -327,7 +343,7 @@ You can find more detailed information about the missing posts in the form that cMaRN0UdBBkAAAAASUVORK5CYII= - + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go @@ -356,25 +372,6 @@ You can find more detailed information about the missing posts in the form that False - - - iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO - wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP - WQwhyWIyJIUW5NqyPb7oCVtIlhVTwYf8nv7/t2zJagel9KmqKsIACYL9RjI8UHz5zshougZr/AEvbxEP - aZCDBY3VslixaJvX3wzkkDiOwbZtDRGA5vdNAg+TL27qgmt5XkBG/gTdAG7Gt+3PP9oOaEGFCVEC6rp+ - 5g9MfM/c5e4OsEZMZkQEtGL5H2DdZ5JRArDwPA+iKII0TfkC9vroC9j5vq8JTWw3WzWgLMtZGIaa0MR8 - vlAD8PYlSaIJTTiOowY0p0Bc19XEJo6HE59FAPuMzyAINKGJ1XLFZxHALtMrnkBXOIQIIIQ8YvF/KrgB - cMaRN0UdBBkAAAAASUVORK5CYII= - - - - - iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO - xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go - tbbB43rK5xSAQq1VYFtmeQBoqZTSreVZvgTknM8yyyjA/qodsDF9gspD2Bj6B+DH+NqzhQQAG+POMnSX - AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC - - iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO @@ -433,6 +430,25 @@ You can find more detailed information about the missing posts in the form that + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO + wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP + WQwhyWIyJIUW5NqyPb7oCVtIlhVTwYf8nv7/t2zJagel9KmqKsIACYL9RjI8UHz5zshougZr/AEvbxEP + aZCDBY3VslixaJvX3wzkkDiOwbZtDRGA5vdNAg+TL27qgmt5XkBG/gTdAG7Gt+3PP9oOaEGFCVEC6rp+ + 5g9MfM/c5e4OsEZMZkQEtGL5H2DdZ5JRArDwPA+iKII0TfkC9vroC9j5vq8JTWw3WzWgLMtZGIaa0MR8 + vlAD8PYlSaIJTTiOowY0p0Bc19XEJo6HE59FAPuMzyAINKGJ1XLFZxHALtMrnkBXOIQIIIQ8YvF/KrgB + cMaRN0UdBBkAAAAASUVORK5CYII= + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO + xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go + tbbB43rK5xSAQq1VYFtmeQBoqZTSreVZvgTknM8yyyjA/qodsDF9gspD2Bj6B+DH+NqzhQQAG+POMnSX + AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6 JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAACOElE @@ -448,7 +464,7 @@ You can find more detailed information about the missing posts in the form that VnR1MIwhwMTCyqEQ37qEmZVDFF0OE/9nAACtFF4Ey6OP+wAAAABJRU5ErkJggg== - + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go @@ -462,7 +478,7 @@ You can find more detailed information about the missing posts in the form that False - + iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAABGdBTUEAALGPC/xhBQAAE65JREFUeF7t 3X2sJWddB/DdLi2lQG2hdOHuvfM887J7Cxca4ELTQMDWKigIFpBAEAgi9g+CJpJo9Q8NJhgBiYZIYspL diff --git a/SCrawler/Editors/GlobalSettingsForm.vb b/SCrawler/Editors/GlobalSettingsForm.vb index 34d205c..d49e5e4 100644 --- a/SCrawler/Editors/GlobalSettingsForm.vb +++ b/SCrawler/Editors/GlobalSettingsForm.vb @@ -33,10 +33,14 @@ Namespace Editors TXT_MAX_JOBS_USERS.Value = .MaxUsersJobsCount.Value TXT_MAX_JOBS_CHANNELS.Value = .ChannelsMaxJobsCount.Value CH_CHECK_VER_START.Checked = .CheckUpdatesAtStart + TXT_PRG_TITLE.Text = .ProgramText + TXT_PRG_DESCR.Text = .ProgramDescription TXT_USER_AGENT.Text = .UserAgent TXT_IMGUR_CLIENT_ID.Text = .ImgurClientID TXT_USER_LIST_IMAGE.Text = .UserListImage.Value COLORS_USERLIST.ColorsSet(.UserListBackColor, .UserListForeColor, SystemColors.Window, SystemColors.WindowText) + COLORS_SUBSCRIPTIONS.ColorsSet(.MainFrameUsersSubscriptionsColorBack, .MainFrameUsersSubscriptionsColorFore, + SystemColors.Window, SystemColors.WindowText) CH_SHOW_GROUPS.Checked = .ShowGroups CH_USERS_GROUPING.Checked = .UseGrouping 'Environment @@ -83,9 +87,13 @@ Namespace Editors CMB_STD_OPEN_DBL.EndUpdate(True) CMB_STD_OPEN_DBL.SelectedIndex = [Enum].GetValues(GetType(StdDblClck)).ToObjectsList(Of StdDblClck).ToList.IndexOf(.STDownloader_OnItemDoubleClick.Value) CH_STD_TAKESNAP.Checked = .STDownloader_TakeSnapshot + CH_STD_SNAP_KEEP_WITH_FILES.Checked = .STDownloader_SnapshotsKeepWithFiles + CH_STD_SNAP_CACHE_PERMANENT.Checked = .STDownloader_SnapShotsCachePermamnent CH_STD_UPDATE_YT_PATH.Checked = .STDownloader_UpdateYouTubeOutputPath CH_STD_YT_LOAD.Checked = .STDownloader_LoadYTVideos CH_STD_YT_REMOVE.Checked = .STDownloader_RemoveYTVideosOnClear + CH_STD_YT_OUTPUT_ASK_NAME.Checked = .STDownloader_OutputPathAskForName + CH_STD_YT_OUTPUT_AUTO_ADD.Checked = .STDownloader_OutputPathAutoAddPaths 'Downloading CH_UDESCR_UP.Checked = .UpdateUserDescriptionEveryTime CH_UNAME_UP.Checked = .UserSiteNameUpdateEveryTime @@ -125,6 +133,8 @@ Namespace Editors CH_FEED_ADD_SESSION.Checked = .FeedAddSessionToCaption CH_FEED_ADD_DATE.Checked = .FeedAddDateToCaption CH_FEED_STORE_SESSION_DATA.Checked = .FeedStoreSessionsData + CH_FEED_OPEN_LAST_MODE.Checked = .FeedOpenLastMode + CH_FEED_SHOW_FRIENDLY.Checked = .FeedShowFriendlyNames End With .MyFieldsChecker = New FieldsChecker With .MyFieldsCheckerE @@ -191,11 +201,14 @@ Namespace Editors .MaxUsersJobsCount.Value = CInt(TXT_MAX_JOBS_USERS.Value) .ChannelsMaxJobsCount.Value = TXT_MAX_JOBS_CHANNELS.Value .CheckUpdatesAtStart.Value = CH_CHECK_VER_START.Checked + .ProgramText.Value = TXT_PRG_TITLE.Text + .ProgramDescription.Value = TXT_PRG_DESCR.Text .UserAgent.Value = TXT_USER_AGENT.Text DefaultUserAgent = TXT_USER_AGENT.Text .ImgurClientID.Value = TXT_IMGUR_CLIENT_ID.Text .UserListImage.Value = TXT_USER_LIST_IMAGE.Text COLORS_USERLIST.ColorsGet(.UserListBackColor, .UserListForeColor) + COLORS_SUBSCRIPTIONS.ColorsGet(.MainFrameUsersSubscriptionsColorBack, .MainFrameUsersSubscriptionsColorFore) .ShowGroups.Value = CH_SHOW_GROUPS.Checked .UseGrouping.Value = CH_USERS_GROUPING.Checked 'Environment @@ -239,9 +252,13 @@ Namespace Editors .STDownloader_RemoveDownloadedAutomatically.Value = CH_STD_AUTO_REMOVE.Checked .STDownloader_OnItemDoubleClick.Value = CInt(CMB_STD_OPEN_DBL.Value) .STDownloader_TakeSnapshot.Value = CH_STD_TAKESNAP.Checked + .STDownloader_SnapshotsKeepWithFiles.Value = CH_STD_SNAP_KEEP_WITH_FILES.Checked + .STDownloader_SnapShotsCachePermamnent.Value = CH_STD_SNAP_CACHE_PERMANENT.Checked .STDownloader_UpdateYouTubeOutputPath.Value = CH_STD_UPDATE_YT_PATH.Checked .STDownloader_LoadYTVideos.Value = CH_STD_YT_LOAD.Checked .STDownloader_RemoveYTVideosOnClear.Value = CH_STD_YT_REMOVE.Checked + .STDownloader_OutputPathAskForName.Value = CH_STD_YT_OUTPUT_ASK_NAME.Checked + .STDownloader_OutputPathAutoAddPaths.Value = CH_STD_YT_OUTPUT_AUTO_ADD.Checked 'Downloading .UpdateUserDescriptionEveryTime.Value = CH_UDESCR_UP.Checked .UserSiteNameUpdateEveryTime.Value = CH_UNAME_UP.Checked @@ -282,6 +299,8 @@ Namespace Editors .FeedAddSessionToCaption.Value = CH_FEED_ADD_SESSION.Checked .FeedAddDateToCaption.Value = CH_FEED_ADD_DATE.Checked .FeedStoreSessionsData.Value = CH_FEED_STORE_SESSION_DATA.Checked + .FeedOpenLastMode.Value = CH_FEED_OPEN_LAST_MODE.Checked + .FeedShowFriendlyNames.Value = CH_FEED_SHOW_FRIENDLY.Checked FeedParametersChanged = .FeedDataRows.ChangesDetected Or .FeedDataColumns.ChangesDetected Or .FeedEndless.ChangesDetected Or .FeedStoreSessionsData.ChangesDetected Or .FeedBackColor.ChangesDetected Or .FeedForeColor.ChangesDetected Or @@ -364,5 +383,18 @@ Namespace Editors Private Sub TXT_CMD_ENCODING_ActionOnButtonClick(ByVal Sender As ActionButton, ByVal e As ActionButtonEventArgs) Handles TXT_CMD_ENCODING.ActionOnButtonClick If Sender.DefaultButton = ADB.Refresh Then TXT_CMD_ENCODING.Text = SettingsCLS.DefaultCmdEncoding End Sub + Private Sub BTT_RESET_DOWNLOAD_LOCATIONS_Click(sender As Object, e As EventArgs) Handles BTT_RESET_DOWNLOAD_LOCATIONS.Click + Try + Const msgTitle$ = "Reset download locations" + If Settings.DownloadLocations.Count = 0 Then + MsgBoxE({"There are no saved download locations.", msgTitle}) + ElseIf MsgBoxE({$"Are you sure you want to delete all ({Settings.DownloadLocations.Count}) download locations?", msgTitle}, + vbExclamation + vbYesNo) = vbYes Then + Settings.DownloadLocations.Clear() + MsgBoxE({"All download locations deleted.", msgTitle}) + End If + Catch + End Try + End Sub End Class End Namespace \ No newline at end of file diff --git a/SCrawler/Editors/UserCreatorForm.Designer.vb b/SCrawler/Editors/UserCreatorForm.Designer.vb index 1d427e9..84340b4 100644 --- a/SCrawler/Editors/UserCreatorForm.Designer.vb +++ b/SCrawler/Editors/UserCreatorForm.Designer.vb @@ -36,6 +36,7 @@ Namespace Editors Dim ActionButton6 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() Dim ActionButton7 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() 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() Me.CH_PARSE_USER_MEDIA = New System.Windows.Forms.CheckBox() Me.CH_READY_FOR_DOWN = New System.Windows.Forms.CheckBox() Me.BTT_OTHER_SETTINGS = New System.Windows.Forms.Button() @@ -56,8 +57,9 @@ Namespace Editors Me.TP_DOWN_IMG_VID = New System.Windows.Forms.TableLayoutPanel() Me.CH_DOWN_IMAGES = New System.Windows.Forms.CheckBox() Me.CH_DOWN_VIDEOS = New System.Windows.Forms.CheckBox() - Me.TXT_SPEC_FOLDER = New PersonalUtilities.Forms.Controls.TextBoxExtended() + Me.TXT_SPEC_FOLDER = New PersonalUtilities.Forms.Controls.ComboBoxExtended() Me.TXT_SCRIPT = New PersonalUtilities.Forms.Controls.TextBoxExtended() + Me.COLOR_USER = New SCrawler.Editors.ColorPicker() TT_MAIN = New System.Windows.Forms.ToolTip(Me.components) CONTAINER_MAIN = New System.Windows.Forms.ToolStripContainer() CONTAINER_MAIN.ContentPanel.SuspendLayout() @@ -105,10 +107,10 @@ Namespace Editors 'BTT_OTHER_SETTINGS ' Me.BTT_OTHER_SETTINGS.Dock = System.Windows.Forms.DockStyle.Fill - Me.BTT_OTHER_SETTINGS.Location = New System.Drawing.Point(1, 1) + Me.BTT_OTHER_SETTINGS.Location = New System.Drawing.Point(353, 1) Me.BTT_OTHER_SETTINGS.Margin = New System.Windows.Forms.Padding(1) Me.BTT_OTHER_SETTINGS.Name = "BTT_OTHER_SETTINGS" - Me.BTT_OTHER_SETTINGS.Size = New System.Drawing.Size(101, 26) + Me.BTT_OTHER_SETTINGS.Size = New System.Drawing.Size(98, 26) Me.BTT_OTHER_SETTINGS.TabIndex = 1 Me.BTT_OTHER_SETTINGS.Text = "Options (F2)" TT_MAIN.SetToolTip(Me.BTT_OTHER_SETTINGS, "Other settings") @@ -139,17 +141,18 @@ Namespace Editors Me.TP_MAIN.Controls.Add(Me.TP_SITE, 0, 3) Me.TP_MAIN.Controls.Add(Me.TP_TEMP_FAV, 0, 4) Me.TP_MAIN.Controls.Add(Me.TP_READY_USERMEDIA, 0, 6) - Me.TP_MAIN.Controls.Add(Me.TXT_DESCR, 0, 10) + Me.TP_MAIN.Controls.Add(Me.TXT_DESCR, 0, 11) Me.TP_MAIN.Controls.Add(Me.TXT_USER_FRIENDLY, 0, 1) Me.TP_MAIN.Controls.Add(Me.TP_ADD_BY_LIST, 0, 7) Me.TP_MAIN.Controls.Add(Me.TXT_LABELS, 0, 8) Me.TP_MAIN.Controls.Add(Me.TP_DOWN_IMG_VID, 0, 5) Me.TP_MAIN.Controls.Add(Me.TXT_SPEC_FOLDER, 0, 2) - Me.TP_MAIN.Controls.Add(Me.TXT_SCRIPT, 0, 9) + Me.TP_MAIN.Controls.Add(Me.TXT_SCRIPT, 0, 10) + Me.TP_MAIN.Controls.Add(Me.COLOR_USER, 0, 9) Me.TP_MAIN.Dock = System.Windows.Forms.DockStyle.Fill Me.TP_MAIN.Location = New System.Drawing.Point(0, 0) Me.TP_MAIN.Name = "TP_MAIN" - Me.TP_MAIN.RowCount = 11 + Me.TP_MAIN.RowCount = 12 Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) @@ -160,6 +163,7 @@ Namespace Editors Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26.0!)) Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26.0!)) + Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26.0!)) Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) Me.TP_MAIN.Size = New System.Drawing.Size(454, 461) Me.TP_MAIN.TabIndex = 0 @@ -167,7 +171,10 @@ Namespace Editors 'TXT_USER ' Me.TXT_USER.CaptionText = "User name" + Me.TXT_USER.CaptionToolTipEnabled = True + Me.TXT_USER.CaptionToolTipText = "You must enter the user's URL in this field." Me.TXT_USER.Dock = System.Windows.Forms.DockStyle.Fill + Me.TXT_USER.Lines = New String(-1) {} Me.TXT_USER.Location = New System.Drawing.Point(4, 4) Me.TXT_USER.Name = "TXT_USER" Me.TXT_USER.PlaceholderEnabled = True @@ -178,11 +185,11 @@ Namespace Editors 'TP_SITE ' Me.TP_SITE.ColumnCount = 2 - Me.TP_SITE.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 103.0!)) Me.TP_SITE.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) + Me.TP_SITE.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 100.0!)) Me.TP_SITE.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20.0!)) - Me.TP_SITE.Controls.Add(Me.CMB_SITE, 1, 0) - Me.TP_SITE.Controls.Add(Me.BTT_OTHER_SETTINGS, 0, 0) + Me.TP_SITE.Controls.Add(Me.CMB_SITE, 0, 0) + Me.TP_SITE.Controls.Add(Me.BTT_OTHER_SETTINGS, 1, 0) Me.TP_SITE.Dock = System.Windows.Forms.DockStyle.Fill Me.TP_SITE.Location = New System.Drawing.Point(1, 88) Me.TP_SITE.Margin = New System.Windows.Forms.Padding(0) @@ -197,6 +204,16 @@ Namespace Editors ActionButton1.BackgroundImage = CType(resources.GetObject("ActionButton1.BackgroundImage"), System.Drawing.Image) ActionButton1.Name = "ArrowDown" Me.CMB_SITE.Buttons.Add(ActionButton1) + Me.CMB_SITE.CaptionCheckAlign = System.Drawing.ContentAlignment.MiddleLeft + Me.CMB_SITE.CaptionMargin = New System.Windows.Forms.Padding(4, 3, 3, 3) + Me.CMB_SITE.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.CheckBox + Me.CMB_SITE.CaptionText = "Subscription" + Me.CMB_SITE.CaptionTextAlign = System.Drawing.ContentAlignment.MiddleLeft + Me.CMB_SITE.CaptionToolTipEnabled = True + Me.CMB_SITE.CaptionToolTipText = resources.GetString("CMB_SITE.CaptionToolTipText") + Me.CMB_SITE.CaptionVisible = True + Me.CMB_SITE.CaptionWidth = 103.0R + Me.CMB_SITE.ChangeControlsEnableOnCheckedChange = False ListColumn1.Name = "_COL_KEY" ListColumn1.Text = "Key" ListColumn1.ValueMember = True @@ -208,10 +225,11 @@ Namespace Editors Me.CMB_SITE.Columns.Add(ListColumn1) Me.CMB_SITE.Columns.Add(ListColumn2) Me.CMB_SITE.Dock = System.Windows.Forms.DockStyle.Fill - Me.CMB_SITE.Location = New System.Drawing.Point(103, 3) + Me.CMB_SITE.Lines = New String(-1) {} + Me.CMB_SITE.Location = New System.Drawing.Point(0, 3) Me.CMB_SITE.Margin = New System.Windows.Forms.Padding(0, 3, 3, 3) Me.CMB_SITE.Name = "CMB_SITE" - Me.CMB_SITE.Size = New System.Drawing.Size(346, 22) + Me.CMB_SITE.Size = New System.Drawing.Size(349, 22) Me.CMB_SITE.TabIndex = 0 Me.CMB_SITE.TextBoxBorderStyle = System.Windows.Forms.BorderStyle.FixedSingle ' @@ -285,16 +303,18 @@ Namespace Editors Me.TXT_DESCR.Dock = System.Windows.Forms.DockStyle.Fill Me.TXT_DESCR.GroupBoxed = True Me.TXT_DESCR.GroupBoxText = "Description" - Me.TXT_DESCR.Location = New System.Drawing.Point(4, 290) + Me.TXT_DESCR.Lines = New String(-1) {} + Me.TXT_DESCR.Location = New System.Drawing.Point(4, 317) Me.TXT_DESCR.Multiline = True Me.TXT_DESCR.Name = "TXT_DESCR" - Me.TXT_DESCR.Size = New System.Drawing.Size(446, 167) - Me.TXT_DESCR.TabIndex = 10 + Me.TXT_DESCR.Size = New System.Drawing.Size(446, 140) + Me.TXT_DESCR.TabIndex = 11 ' 'TXT_USER_FRIENDLY ' Me.TXT_USER_FRIENDLY.CaptionText = "Friendly name" Me.TXT_USER_FRIENDLY.Dock = System.Windows.Forms.DockStyle.Fill + Me.TXT_USER_FRIENDLY.Lines = New String(-1) {} Me.TXT_USER_FRIENDLY.Location = New System.Drawing.Point(4, 33) Me.TXT_USER_FRIENDLY.Name = "TXT_USER_FRIENDLY" Me.TXT_USER_FRIENDLY.Size = New System.Drawing.Size(446, 22) @@ -351,6 +371,7 @@ Namespace Editors Me.TXT_LABELS.CaptionText = "Labels" Me.TXT_LABELS.CaptionWidth = 50.0R Me.TXT_LABELS.Dock = System.Windows.Forms.DockStyle.Fill + Me.TXT_LABELS.Lines = New String(-1) {} Me.TXT_LABELS.Location = New System.Drawing.Point(4, 235) Me.TXT_LABELS.Margin = New System.Windows.Forms.Padding(3, 2, 3, 3) Me.TXT_LABELS.Name = "TXT_LABELS" @@ -406,38 +427,59 @@ Namespace Editors ActionButton6.BackgroundImage = CType(resources.GetObject("ActionButton6.BackgroundImage"), System.Drawing.Image) ActionButton6.Name = "Clear" ActionButton6.ToolTipText = "Clear" + ActionButton7.BackgroundImage = CType(resources.GetObject("ActionButton7.BackgroundImage"), System.Drawing.Image) + ActionButton7.Name = "ArrowDown" + ActionButton7.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.ArrowDown Me.TXT_SPEC_FOLDER.Buttons.Add(ActionButton5) Me.TXT_SPEC_FOLDER.Buttons.Add(ActionButton6) + Me.TXT_SPEC_FOLDER.Buttons.Add(ActionButton7) + Me.TXT_SPEC_FOLDER.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.Label Me.TXT_SPEC_FOLDER.CaptionText = "Special path" + Me.TXT_SPEC_FOLDER.CaptionVisible = True Me.TXT_SPEC_FOLDER.Dock = System.Windows.Forms.DockStyle.Fill + Me.TXT_SPEC_FOLDER.Lines = New String(-1) {} Me.TXT_SPEC_FOLDER.Location = New System.Drawing.Point(4, 62) Me.TXT_SPEC_FOLDER.Name = "TXT_SPEC_FOLDER" Me.TXT_SPEC_FOLDER.Size = New System.Drawing.Size(446, 22) Me.TXT_SPEC_FOLDER.TabIndex = 2 + Me.TXT_SPEC_FOLDER.TextBoxBorderStyle = System.Windows.Forms.BorderStyle.FixedSingle ' 'TXT_SCRIPT ' - ActionButton7.BackgroundImage = CType(resources.GetObject("ActionButton7.BackgroundImage"), System.Drawing.Image) - ActionButton7.Enabled = False - ActionButton7.Name = "Open" ActionButton8.BackgroundImage = CType(resources.GetObject("ActionButton8.BackgroundImage"), System.Drawing.Image) ActionButton8.Enabled = False - ActionButton8.Name = "Clear" - Me.TXT_SCRIPT.Buttons.Add(ActionButton7) + ActionButton8.Name = "Open" + ActionButton9.BackgroundImage = CType(resources.GetObject("ActionButton9.BackgroundImage"), System.Drawing.Image) + ActionButton9.Enabled = False + ActionButton9.Name = "Clear" Me.TXT_SCRIPT.Buttons.Add(ActionButton8) + Me.TXT_SCRIPT.Buttons.Add(ActionButton9) Me.TXT_SCRIPT.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.CheckBox Me.TXT_SCRIPT.CaptionText = "Script" Me.TXT_SCRIPT.CaptionToolTipEnabled = True Me.TXT_SCRIPT.CaptionToolTipText = "Execute script after downloading this user" Me.TXT_SCRIPT.CaptionWidth = 65.0R Me.TXT_SCRIPT.Dock = System.Windows.Forms.DockStyle.Fill - Me.TXT_SCRIPT.Location = New System.Drawing.Point(4, 262) + Me.TXT_SCRIPT.Lines = New String(-1) {} + Me.TXT_SCRIPT.Location = New System.Drawing.Point(4, 289) Me.TXT_SCRIPT.Margin = New System.Windows.Forms.Padding(3, 2, 3, 3) Me.TXT_SCRIPT.Name = "TXT_SCRIPT" Me.TXT_SCRIPT.PlaceholderEnabled = True Me.TXT_SCRIPT.PlaceholderText = "Leave blank to use the default script..." Me.TXT_SCRIPT.Size = New System.Drawing.Size(446, 22) - Me.TXT_SCRIPT.TabIndex = 9 + Me.TXT_SCRIPT.TabIndex = 10 + ' + 'COLOR_USER + ' + Me.COLOR_USER.ButtonsMargin = New System.Windows.Forms.Padding(1) + Me.COLOR_USER.CaptionText = "Color" + Me.COLOR_USER.CaptionWidth = 55 + Me.COLOR_USER.Dock = System.Windows.Forms.DockStyle.Fill + Me.COLOR_USER.Location = New System.Drawing.Point(2, 261) + Me.COLOR_USER.Margin = New System.Windows.Forms.Padding(1, 1, 2, 1) + Me.COLOR_USER.Name = "COLOR_USER" + Me.COLOR_USER.Size = New System.Drawing.Size(449, 24) + Me.COLOR_USER.TabIndex = 9 ' 'UserCreatorForm ' @@ -492,7 +534,7 @@ Namespace Editors Private WithEvents TXT_LABELS As PersonalUtilities.Forms.Controls.TextBoxExtended Private WithEvents CH_DOWN_IMAGES As CheckBox Private WithEvents CH_DOWN_VIDEOS As CheckBox - Private WithEvents TXT_SPEC_FOLDER As PersonalUtilities.Forms.Controls.TextBoxExtended + Private WithEvents TXT_SPEC_FOLDER As PersonalUtilities.Forms.Controls.ComboBoxExtended Private WithEvents CMB_SITE As PersonalUtilities.Forms.Controls.ComboBoxExtended Private WithEvents BTT_OTHER_SETTINGS As Button Private WithEvents TXT_SCRIPT As PersonalUtilities.Forms.Controls.TextBoxExtended @@ -501,5 +543,6 @@ Namespace Editors Private WithEvents TP_TEMP_FAV As TableLayoutPanel Private WithEvents TP_READY_USERMEDIA As TableLayoutPanel Private WithEvents TP_DOWN_IMG_VID As TableLayoutPanel + Private WithEvents COLOR_USER As ColorPicker End Class End Namespace \ No newline at end of file diff --git a/SCrawler/Editors/UserCreatorForm.resx b/SCrawler/Editors/UserCreatorForm.resx index 7e231d4..6c89bed 100644 --- a/SCrawler/Editors/UserCreatorForm.resx +++ b/SCrawler/Editors/UserCreatorForm.resx @@ -216,6 +216,12 @@ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6LEtW/4flgYiLD1qeX0A AAAASUVORK5CYII= + + + Create a subscription instead of a user. +This mode means that files will not be downloaded. Instead, a video preview (screenshot) will be loaded. You can choose what to download, open a post, etc. +The download goes through a standalone downloader. +You can see downloaded subscriptions in the feed. @@ -264,6 +270,96 @@ + + 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 + AAAASUVORK5CYII= + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP @@ -274,7 +370,7 @@ cMaRN0UdBBkAAAAASUVORK5CYII= - + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go diff --git a/SCrawler/Editors/UserCreatorForm.vb b/SCrawler/Editors/UserCreatorForm.vb index 0b2ac0c..656e3b0 100644 --- a/SCrawler/Editors/UserCreatorForm.vb +++ b/SCrawler/Editors/UserCreatorForm.vb @@ -80,8 +80,26 @@ Namespace Editors Return TXT_SCRIPT.Text End Get End Property + Friend ReadOnly Property IsSubscription As Boolean + Get + Return CMB_SITE.Checked + End Get + End Property + Private _UserBackColor As Color? = Nothing + Friend ReadOnly Property UserBackColor As Color? + Get + Return _UserBackColor + End Get + End Property + Private _UserForeColor As Color? = Nothing + Friend ReadOnly Property UserForeColor As Color? + Get + Return _UserForeColor + End Get + End Property Private FriendlyNameIsSiteName As Boolean = False Private FriendlyNameChanged As Boolean = False + Friend Property Options As String = String.Empty #End Region #Region "Exchange, Path, Labels" Friend Property MyExchangeOptions As Object = Nothing @@ -99,7 +117,9 @@ Namespace Editors End If End Get End Property + Private SpecialPathHandler As PathMoverHandler = Nothing Friend ReadOnly Property UserLabels As List(Of String) + Private LabelsIncludeSpecial As Boolean = False #End Region #Region "Initializers" ''' Create new user @@ -146,6 +166,7 @@ Namespace Editors .MyViewInitialize(True) .AddOkCancelToolbar() CH_AUTO_DETECT_SITE.Enabled = False + Settings.GlobalLocations.PopulateComboBox(TXT_SPEC_FOLDER) With CMB_SITE .BeginUpdate() .Items.AddRange(Settings.Plugins.Select(Function(p) New ListItem({p.Key, p.Name}))) @@ -155,6 +176,9 @@ Namespace Editors Dim NameFieldProvider As IFormatProvider = Nothing If UserIsCollection Then + CMB_SITE.CaptionEnabled = False + CMB_SITE.Checked = False + Icon = If(ImageRenderer.GetIcon(My.Resources.DBPic_32, EDP.ReturnValue), Icon) Text = $"Collection: {UserInstance.CollectionName}" @@ -163,6 +187,7 @@ Namespace Editors TXT_USER.Buttons.AddRange({ADB.Refresh, ADB.Clear}) TXT_USER.Buttons.UpdateButtonsPositions() TXT_SPEC_FOLDER.Buttons.Clear() + TXT_SPEC_FOLDER.Buttons.LeaveDefaultButtons = False TXT_SPEC_FOLDER.TextBoxReadOnly = True TXT_SPEC_FOLDER.Buttons.UpdateButtonsPositions() @@ -177,6 +202,7 @@ Namespace Editors .Add(New RowStyle(SizeType.Absolute, 28)) .Add(New RowStyle(SizeType.Absolute, 28)) .Add(New RowStyle(SizeType.Absolute, 26)) + .Add(New RowStyle(SizeType.Absolute, 26)) .Add(New RowStyle(SizeType.Percent, 100)) End With .RowCount = .RowStyles.Count @@ -187,7 +213,8 @@ Namespace Editors .Add(TP_DOWN_IMG_VID, 0, 3) .Add(TP_READY_USERMEDIA, 0, 4) .Add(TXT_LABELS, 0, 5) - .Add(TXT_DESCR, 0, 6) + .Add(COLOR_USER, 0, 6) + .Add(TXT_DESCR, 0, 7) End With .Refresh() .Update() @@ -214,9 +241,12 @@ Namespace Editors CH_DOWN_VIDEOS.CheckState = state(.Item(0).DownloadVideos, Function(p, v) p.DownloadVideos = v) CH_READY_FOR_DOWN.CheckState = state(.Item(0).ReadyForDownload, Function(p, v) p.ReadyForDownload = v) CH_PARSE_USER_MEDIA.CheckState = state(.Item(0).ParseUserMediaOnly, Function(p, v) p.ParseUserMediaOnly = v) + _UserBackColor = .BackColor + _UserForeColor = .ForeColor + COLOR_USER.ColorsSetUser(.BackColor, .ForeColor) TXT_DESCR.Text = .GetUserInformation.StringFormatLines - UserLabels.ListAddList(.Labels) - If UserLabels.ListExists Then TXT_LABELS.Text = UserLabels.ListToString + UpdateSpecificLabels(True) + TXT_LABELS.Buttons.Insert(0, New ActionButton(ADB.Refresh) With {.ToolTipText = "Show/hide site-specific labels"}) End With NameFieldProvider = New CollectionNameFieldProvider @@ -235,14 +265,16 @@ Namespace Editors TXT_SPEC_FOLDER.Text = User.SpecialPath Dim i% = Settings.Plugins.FindIndex(Function(p) p.Key = User.Plugin) If i >= 0 Then CMB_SITE.SelectedIndex = i + CMB_SITE.Checked = User.IsSubscription SetParamsBySite() - CMB_SITE.Enabled = False If Not UserInstance Is Nothing Then + CMB_SITE.Enabled = False Text = $"User: {UserInstance.Name}" If Not UserInstance.FriendlyName.IsEmptyString Then Text &= $" ({UserInstance.FriendlyName})" TXT_USER.Enabled = False TXT_SPEC_FOLDER.TextBoxReadOnly = True TXT_SPEC_FOLDER.Buttons.Clear() + TXT_SPEC_FOLDER.Buttons.LeaveDefaultButtons = False TXT_SPEC_FOLDER.Buttons.UpdateButtonsPositions() With UserInstance If .HOST.Key = PathPlugin.PluginKey Then TXT_SPEC_FOLDER.Enabled = False @@ -263,6 +295,7 @@ Namespace Editors CH_READY_FOR_DOWN.Checked = .ReadyForDownload CH_DOWN_IMAGES.Checked = .DownloadImages CH_DOWN_VIDEOS.Checked = .DownloadVideos + COLOR_USER.ColorsSetUser(.BackColor, .ForeColor) TXT_SCRIPT.Checked = .ScriptUse TXT_SCRIPT.Text = .ScriptData TXT_DESCR.Text = .Description.StringFormatLines @@ -312,6 +345,7 @@ Namespace Editors #End Region #Region "Ok, Cancel" Private Sub MyDef_ButtonOkClick(ByVal Sender As Object, ByVal e As KeyHandleEventArgs) Handles MyDef.ButtonOkClick + Const msgTitle$ = "Create user" If UserIsCollection Then If MyDef.MyFieldsChecker.AllParamsOK Then With UserInstance @@ -321,7 +355,23 @@ Namespace Editors If Not CH_DOWN_VIDEOS.CheckState = CheckState.Indeterminate Then .DownloadVideos = CH_DOWN_VIDEOS.Checked If Not CH_READY_FOR_DOWN.CheckState = CheckState.Indeterminate Then .ReadyForDownload = CH_READY_FOR_DOWN.Checked If Not CH_PARSE_USER_MEDIA.CheckState = CheckState.Indeterminate Then .ParseUserMediaOnly = CH_PARSE_USER_MEDIA.Checked - DirectCast(UserInstance, UserDataBind).Collections.ForEach(Sub(u) u.Labels.ListAddList(UserLabels, LAP.ClearBeforeAdd, LAP.NotContainsOnly)) + + Dim __ubc As Color? = Nothing, __ufc As Color? = Nothing + COLOR_USER.ColorsGetUser(__ubc, __ufc) + Dim __cConv As Func(Of Color?, String) = + Function(__inputColor) If(__inputColor.HasValue, CStr(AConvert(Of String)(__inputColor.Value, String.Empty)), String.Empty) + If Not __cConv(UserBackColor) = __cConv(__ubc) Or Not __cConv(UserForeColor) = __cConv(__ufc) Then + If MsgBoxE({"Are you sure you want to apply the new colors to all users in the collection?", msgTitle}, vbYesNo + vbExclamation) = vbYes Then + .BackColor = __ubc + .ForeColor = __ufc + End If + End If + + If Not .Labels.ListEquals(UserLabels) Then _ + UserDataBase.UpdateLabels(.Self, UserLabels, 1, + Not DirectCast(.Self, UserDataBase).SpecialLabels.ListExists OrElse + UserDataBase.UpdateLabelsKeepSpecial(1)) + CollectionName = TXT_USER.Text .UpdateUserInformation() End With @@ -332,63 +382,74 @@ Namespace Editors If MyDef.MyFieldsChecker.AllParamsOK Then Dim s As SettingsHost = GetSiteByCheckers() If Not s Is Nothing Then - Dim tmpUser As UserInfo = User.Clone - With tmpUser - .Name = TXT_USER.Text - .SpecialPath = SpecialPath(s) - .Site = s.Name - .Plugin = s.Key - .UpdateUserFile() - End With - User = tmpUser - Dim ScriptText$ = TXT_SCRIPT.Text - If Not ScriptText.IsEmptyString Then - Dim f As SFile = ScriptText - If Not SFile.IsDirectory(ScriptText) And Not UserInstance Is Nothing Then - With DirectCast(UserInstance, UserDataBase) : f.Path = .MyFile.Path : End With - End If - TXT_SCRIPT.Text = f - End If - If Not UserInstance Is Nothing Then - With DirectCast(UserInstance, UserDataBase) - .User = User - Dim setFriendly As Boolean = True - If FriendlyNameIsSiteName Then - If Not FriendlyNameChanged Then - setFriendly = False - Else - setFriendly = MsgBoxE({"Are you sure you want to set the site name as the friendly name?" & vbCr & - $"Friendly name: { .FriendlyNameOrig}" & vbCr & - $"Site name: { .UserSiteName}" & vbCr & - $"Your choice: {TXT_USER_FRIENDLY.Text}", "Friendly name change"}, vbExclamation,,, - {"Confirm", New Messaging.MsgBoxButton("Decline", "Friendly name will not be changed")}) = 0 - End If - End If - If setFriendly Then .FriendlyName = TXT_USER_FRIENDLY.Text - .Favorite = CH_FAV.Checked - .Temporary = CH_TEMP.Checked - .ReadyForDownload = CH_READY_FOR_DOWN.Checked - .DownloadImages = CH_DOWN_IMAGES.Checked - .DownloadVideos = CH_DOWN_VIDEOS.Checked - .UserDescription = TXT_DESCR.Text - If Not MyExchangeOptions Is Nothing Then .ExchangeOptionsSet(MyExchangeOptions) - Dim l As New ListAddParams(LAP.NotContainsOnly + LAP.ClearBeforeAdd) - If .IsCollection Then - With DirectCast(UserInstance, UserDataBind) - If .Count > 0 Then .Collections.ForEach(Sub(c) c.Labels.ListAddList(UserLabels, l)) - End With - Else - .Labels.ListAddList(UserLabels, LAP.NotContainsOnly, LAP.ClearBeforeAdd) - End If - .ParseUserMediaOnly = CH_PARSE_USER_MEDIA.Checked - .ScriptUse = TXT_SCRIPT.Checked - .ScriptData = TXT_SCRIPT.Text - .UpdateUserInformation() + If IsSubscription And Not s.Source.SubscriptionsAllowed Then + MsgBoxE({$"Subscription mode for site [{s.Name}] is not allowed", msgTitle}, vbCritical) + Exit Sub + Else + COLOR_USER.ColorsGetUser(_UserBackColor, _UserForeColor) + Dim tmpUser As UserInfo = User + With tmpUser + .Name = TXT_USER.Text + .Site = s.Name + .Plugin = s.Key + .IsSubscription = IsSubscription + Dim sp As SFile = SpecialPath(s) + If Not sp.IsEmptyString AndAlso Not SpecialPathHandler Is Nothing And UserInstance Is Nothing Then _ + sp = SpecialPathHandler.Invoke(.Self, sp) + .SpecialPath = sp + .UpdateUserFile() End With + User = tmpUser + Dim ScriptText$ = TXT_SCRIPT.Text + If Not ScriptText.IsEmptyString Then + Dim f As SFile = ScriptText + If Not SFile.IsDirectory(ScriptText) And Not UserInstance Is Nothing Then + With DirectCast(UserInstance, UserDataBase) : f.Path = .MyFile.Path : End With + End If + TXT_SCRIPT.Text = f + End If + If Not UserInstance Is Nothing Then + With DirectCast(UserInstance, UserDataBase) + .User = User + Dim setFriendly As Boolean = True + If FriendlyNameIsSiteName Then + If Not FriendlyNameChanged Then + setFriendly = False + Else + setFriendly = MsgBoxE({"Are you sure you want to set the site name as the friendly name?" & vbCr & + $"Friendly name: { .FriendlyNameOrig}" & vbCr & + $"Site name: { .UserSiteName}" & vbCr & + $"Your choice: {TXT_USER_FRIENDLY.Text}", "Friendly name change"}, vbExclamation,,, + {"Confirm", New Messaging.MsgBoxButton("Decline", "Friendly name will not be changed")}) = 0 + End If + End If + If setFriendly Then .FriendlyName = TXT_USER_FRIENDLY.Text + .Favorite = CH_FAV.Checked + .Temporary = CH_TEMP.Checked + .ReadyForDownload = CH_READY_FOR_DOWN.Checked + .DownloadImages = CH_DOWN_IMAGES.Checked + .DownloadVideos = CH_DOWN_VIDEOS.Checked + COLOR_USER.ColorsGetUser(.BackColor, .ForeColor) + .UserDescription = TXT_DESCR.Text + If Not MyExchangeOptions Is Nothing Then .ExchangeOptionsSet(MyExchangeOptions) + Dim l As New ListAddParams(LAP.NotContainsOnly + LAP.ClearBeforeAdd) + If .IsCollection Then + With DirectCast(UserInstance, UserDataBind) + If .Count > 0 Then .Collections.ForEach(Sub(c) c.Labels.ListAddList(UserLabels, l)) + End With + Else + .Labels.ListAddList(UserLabels, LAP.NotContainsOnly, LAP.ClearBeforeAdd) + End If + .ParseUserMediaOnly = CH_PARSE_USER_MEDIA.Checked + .ScriptUse = TXT_SCRIPT.Checked + .ScriptData = TXT_SCRIPT.Text + .UpdateUserInformation() + End With + End If + GoTo CloseForm End If - GoTo CloseForm Else - MsgBoxE("User site not selected", MsgBoxStyle.Exclamation) + MsgBoxE({"User site not selected", msgTitle}, MsgBoxStyle.Exclamation) End If End If Else @@ -409,6 +470,7 @@ CloseForm: Try If Not _TextChangeInvoked And Not UserIsCollection Then _TextChangeInvoked = True + Options = String.Empty If Not CH_ADD_BY_LIST.Checked Then Dim s As ExchangeOptions = GetSiteByText(TXT_USER.Text) Dim found As Boolean = False @@ -421,6 +483,7 @@ CloseForm: End If CMB_SITE.SelectedIndex = i TXT_USER.Text = s.UserName + Options = s.Options found = True End If End If @@ -479,10 +542,27 @@ CloseForm: End Sub Private Sub TXT_SPEC_FOLDER_ActionOnButtonClick(ByVal Sender As ActionButton, ByVal e As EventArgs) Handles TXT_SPEC_FOLDER.ActionOnButtonClick If Sender.DefaultButton = ADB.Open Then - Dim f As SFile = Nothing - If Not TXT_SPEC_FOLDER.Text.IsEmptyString Then f = $"{TXT_SPEC_FOLDER.Text}\" - f = SFile.SelectPath(f) - If Not f.IsEmptyString Then TXT_SPEC_FOLDER.Text = f.PathWithSeparator + Using ff As New GlobalLocationsChooserForm With {.MyInitialLocation = TXT_SPEC_FOLDER.Text} + ff.ShowDialog() + If ff.DialogResult = DialogResult.OK And Not ff.MyDestination.Path.IsEmptyString Then + Settings.GlobalLocations.PopulateComboBox(TXT_SPEC_FOLDER) + Dim i% = Settings.GlobalLocations.IndexOf(ff.MyDestination) + If i.ValueBetween(0, TXT_SPEC_FOLDER.Count - 1) Then TXT_SPEC_FOLDER.SelectedIndex = i + TXT_SPEC_FOLDER.Text = ff.MyDestination.Path + SpecialPathHandler = ff.MyModelHandler + End If + End Using + End If + End Sub + Private Sub TXT_SPEC_FOLDER_ActionOnTextChanged(sender As Object, e As EventArgs) Handles TXT_SPEC_FOLDER.ActionOnTextChanged + SpecialPathHandler = Nothing + End Sub + Private Sub TXT_SPEC_FOLDER_ActionSelectedItemChanged(ByVal Sender As Object, ByVal e As EventArgs, ByVal Item As ListViewItem) Handles TXT_SPEC_FOLDER.ActionSelectedItemChanged + Dim i% = TXT_SPEC_FOLDER.SelectedIndex + If i.ValueBetween(0, Settings.GlobalLocations.Count - 1) Then + SpecialPathHandler = GlobalLocationsChooserForm.ModelHandler(Settings.GlobalLocations(i).Model) + Else + SpecialPathHandler = Nothing End If End Sub Private Sub CH_TEMP_CheckedChanged(sender As Object, e As EventArgs) Handles CH_TEMP.CheckedChanged @@ -517,8 +597,21 @@ CloseForm: Select Case Sender.DefaultButton Case ADB.Open : ChangeLabels() Case ADB.Clear : UserLabels.Clear() + Case ADB.Refresh : UpdateSpecificLabels(False) End Select End Sub + Private Sub UpdateSpecificLabels(ByVal IsInit As Boolean) + If DirectCast(UserInstance, UserDataBase).SpecialLabels.ListExists Then + If Not IsInit Then LabelsIncludeSpecial = Not LabelsIncludeSpecial + UserLabelName.Clone() + UserLabels.ListAddList(UserInstance.Labels, LAP.NotContainsOnly) + If Not LabelsIncludeSpecial Then UserLabels.ListWithRemove(DirectCast(UserInstance, UserDataBase).SpecialLabels) + If UserLabels.Count > 0 Then UserLabels.Sort() + TXT_LABELS.Text = UserLabels.ListToString + Else + If Not IsInit Then MsgBoxE({"Users in this collection do not have site-specific labels", "Change labels view"}, vbExclamation) + End If + End Sub Private Sub TXT_SCRIPT_ActionOnButtonClick(ByVal Sender As ActionButton, ByVal e As EventArgs) Handles TXT_SCRIPT.ActionOnButtonClick SettingsCLS.ScriptTextBoxButtonClick(TXT_SCRIPT, Sender) End Sub @@ -535,17 +628,27 @@ CloseForm: If u.ListExists Then Dim NonIdentified As New List(Of String) Dim UsersForCreate As New List(Of UserInfo) + Dim UsersForCreate_Options As New List(Of String) Dim BannedUsers() As String = Nothing Dim uu$ Dim ulabels As List(Of String) = ListAddList(Nothing, UserLabels).ListAddValue(LabelsKeeper.NoParsedUser, LAP.NotContainsOnly) Dim tmpUser As UserInfo Dim s As SettingsHost = GetSiteByCheckers() - Dim sObj As ExchangeOptions + Dim sObj As ExchangeOptions = Nothing Dim Added% = 0 Dim Skipped% = 0 Dim uid% - Dim sf As Func(Of SettingsHost, String) = Function(__s) SpecialPath(__s).PathWithSeparator - Dim __sf As Func(Of String, SettingsHost, SFile) = Function(Input, __s) IIf(sf(__s).IsEmptyString, Nothing, New SFile($"{sf(__s)}{Input}\")) + Dim __getUserSpecialPath As Func(Of UserInfo, SettingsHost, SFile) = + Function(ByVal __user As UserInfo, ByVal __s As SettingsHost) As SFile + Dim sp As SFile = SpecialPath(__s).PathWithSeparator + If sp.IsEmptyString Then + Return Nothing + ElseIf Not SpecialPathHandler Is Nothing Then + Return SpecialPathHandler.Invoke(__user, sp) + Else + Return $"{sp}{__user.Name}\" + End If + End Function Settings.Labels.Add(LabelsKeeper.NoParsedUser) @@ -559,15 +662,19 @@ CloseForm: Else s = Nothing End If + ElseIf i = 0 Then + sObj = GetSiteByText(uu) End If - If Not s Is Nothing Then - tmpUser = New UserInfo(uu, s) With {.SpecialPath = __sf(uu, s)} + If Not s Is Nothing AndAlso (Not IsSubscription OrElse s.Source.SubscriptionsAllowed) Then + tmpUser = New UserInfo(uu, s) + tmpUser.SpecialPath = __getUserSpecialPath(tmpUser, s) tmpUser.UpdateUserFile() uid = -1 If Settings.UsersList.Count > 0 Then uid = Settings.UsersList.IndexOf(tmpUser) If uid < 0 And Not UsersForCreate.Contains(tmpUser) Then UsersForCreate.Add(tmpUser) + UsersForCreate_Options.Add(sObj.Options) Else Skipped += 1 End If @@ -585,6 +692,7 @@ CloseForm: If StartIndex = -1 Then StartIndex = Settings.Users.Count Settings.Users.Add(UserDataBase.GetInstance(tmpUser, False)) With Settings.Users.Last + .Options = sObj.Options .FriendlyName = TXT_USER_FRIENDLY.Text .Favorite = CH_FAV.Checked .Temporary = CH_TEMP.Checked diff --git a/SCrawler/GlobalSuppressions.vb b/SCrawler/GlobalSuppressions.vb index 03e7648..94960e5 100644 --- a/SCrawler/GlobalSuppressions.vb +++ b/SCrawler/GlobalSuppressions.vb @@ -5,4 +5,5 @@ Imports System.Diagnostics.CodeAnalysis - \ No newline at end of file + + \ No newline at end of file diff --git a/SCrawler/ListImagesLoader.vb b/SCrawler/ListImagesLoader.vb index 4c3effb..e5d65ef 100644 --- a/SCrawler/ListImagesLoader.vb +++ b/SCrawler/ListImagesLoader.vb @@ -199,9 +199,12 @@ Friend Class ListImagesLoader ElseIf CheckUserCollection(User) Then .BackColor = Color.LightSkyBlue .ForeColor = Color.MidnightBlue + ElseIf User.IsSubscription Then + .BackColor = If(User.BackColor, Settings.MainFrameUsersSubscriptionsColorBack.Value) + .ForeColor = If(User.ForeColor, Settings.MainFrameUsersSubscriptionsColorFore.Value) Else - .BackColor = Settings.UserListBackColorF - .ForeColor = Settings.UserListForeColorF + .BackColor = If(User.BackColor, Settings.UserListBackColorF) + .ForeColor = If(User.ForeColor, Settings.UserListForeColorF) End If End With Return LVI diff --git a/SCrawler/MainFrame.Designer.vb b/SCrawler/MainFrame.Designer.vb index a36f9d3..07e02cc 100644 --- a/SCrawler/MainFrame.Designer.vb +++ b/SCrawler/MainFrame.Designer.vb @@ -32,18 +32,22 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form Dim CONTEXT_SEP_4 As System.Windows.Forms.ToolStripSeparator Dim CONTEXT_SEP_5 As System.Windows.Forms.ToolStripSeparator Dim SEP_4 As System.Windows.Forms.ToolStripSeparator - Dim MENU_VIEW_SEP_1 As System.Windows.Forms.ToolStripSeparator - Dim MENU_VIEW_SEP_3 As System.Windows.Forms.ToolStripSeparator Dim MENU_VIEW_SEP_2 As System.Windows.Forms.ToolStripSeparator + Dim MENU_VIEW_SEP_4 As System.Windows.Forms.ToolStripSeparator + Dim MENU_VIEW_SEP_3 As System.Windows.Forms.ToolStripSeparator Dim TRAY_SEP_1 As System.Windows.Forms.ToolStripSeparator Dim MENU_DOWN_ALL_SEP_1 As System.Windows.Forms.ToolStripSeparator + Dim TRAY_SEP_2 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 TRAY_SEP_2 As System.Windows.Forms.ToolStripSeparator Dim MENU_DOWN_ALL_SEP_4 As System.Windows.Forms.ToolStripSeparator - Dim MENU_DOWN_ALL_SEP_5 As System.Windows.Forms.ToolStripSeparator - Dim MENU_DOWN_ALL_SEP_6 As System.Windows.Forms.ToolStripSeparator + Dim MENU_INFO As System.Windows.Forms.ToolStripDropDownButton + Dim MENU_VIEW_SEP_1 As System.Windows.Forms.ToolStripSeparator Dim resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(MainFrame)) + Me.MENU_INFO_SHOW_INFO = New System.Windows.Forms.ToolStripMenuItem() + Me.MENU_INFO_SHOW_QUEUE = New System.Windows.Forms.ToolStripMenuItem() + Me.MENU_INFO_SHOW_MISSING = New System.Windows.Forms.ToolStripMenuItem() + Me.MENU_INFO_SHOW_USER_METRICS = New System.Windows.Forms.ToolStripMenuItem() Me.MENU_SETTINGS = New System.Windows.Forms.ToolStripDropDownButton() Me.BTT_SETTINGS = New System.Windows.Forms.ToolStripMenuItem() Me.Toolbar_TOP = New System.Windows.Forms.ToolStrip() @@ -51,16 +55,21 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form Me.BTT_EDIT_USER = New System.Windows.Forms.ToolStripButton() Me.BTT_DELETE_USER = New System.Windows.Forms.ToolStripButton() Me.BTT_REFRESH = New System.Windows.Forms.ToolStripButton() - Me.BTT_SHOW_INFO = New PersonalUtilities.Forms.Controls.KeyClick.ToolStripButtonKeyClick() Me.BTT_FEED = New System.Windows.Forms.ToolStripButton() Me.BTT_CHANNELS = New System.Windows.Forms.ToolStripButton() Me.BTT_DOWN_SAVED = New System.Windows.Forms.ToolStripButton() Me.MENU_DOWN_ALL = New System.Windows.Forms.ToolStripDropDownButton() Me.BTT_DOWN_SELECTED = New SCrawler.ToolStripKeyMenuItem() + Me.MENU_D_DOWN_ALL = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_DOWN_ALL = New SCrawler.ToolStripKeyMenuItem() + Me.BTT_DOWN_ALL_SUBSCR = New SCrawler.ToolStripKeyMenuItem() Me.BTT_DOWN_SITE = New SCrawler.ToolStripKeyMenuItem() + Me.BTT_DOWN_SITE_SUBSCR = New SCrawler.ToolStripKeyMenuItem() + Me.MENU_D_DOWN_ALL_SITE = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_DOWN_ALL_FULL = New SCrawler.ToolStripKeyMenuItem() + Me.BTT_DOWN_ALL_FULL_SUBSCR = New SCrawler.ToolStripKeyMenuItem() Me.BTT_DOWN_SITE_FULL = New SCrawler.ToolStripKeyMenuItem() + Me.BTT_DOWN_SITE_FULL_SUBSCR = New SCrawler.ToolStripKeyMenuItem() Me.BTT_DOWN_VIDEO = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_ADD_NEW_GROUP = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_SILENT_MODE = New System.Windows.Forms.ToolStripMenuItem() @@ -72,6 +81,8 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form Me.BTT_VIEW_SMALL = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_VIEW_LIST = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_VIEW_DETAILS = New System.Windows.Forms.ToolStripMenuItem() + Me.BTT_MODE_SHOW_USERS = New System.Windows.Forms.ToolStripMenuItem() + Me.BTT_MODE_SHOW_SUBSCRIPTIONS = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_SITE_ALL = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_SITE_SPECIFIC = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_SHOW_ALL = New System.Windows.Forms.ToolStripMenuItem() @@ -90,6 +101,7 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form Me.BTT_LOG = New System.Windows.Forms.ToolStripButton() Me.BTT_VERSION_INFO = New System.Windows.Forms.ToolStripButton() Me.BTT_DONATE = New System.Windows.Forms.ToolStripButton() + Me.BTT_BUG_REPORT = New System.Windows.Forms.ToolStripButton() Me.Toolbar_BOTTOM = New System.Windows.Forms.StatusStrip() Me.BTT_PR_INFO = New System.Windows.Forms.ToolStripStatusLabel() Me.PR_PRE = New System.Windows.Forms.ToolStripProgressBar() @@ -104,11 +116,12 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form Me.BTT_CONTEXT_DOWN_DATE_LIMIT = New SCrawler.ToolStripKeyMenuItem() Me.BTT_CONTEXT_EDIT = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_CONTEXT_DELETE = New System.Windows.Forms.ToolStripMenuItem() + Me.BTT_CONTEXT_ERASE = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_CONTEXT_COPY_TO_FOLDER = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_CONTEXT_FAV = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_CONTEXT_TEMP = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_CONTEXT_READY = New System.Windows.Forms.ToolStripMenuItem() - Me.BTT_CONTEXT_GROUPS = New System.Windows.Forms.ToolStripMenuItem() + Me.BTT_CONTEXT_GROUPS = New PersonalUtilities.Forms.Controls.KeyClick.ToolStripMenuItemKeyClick() Me.BTT_CONTEXT_SCRIPT = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_CONTEXT_ADD_TO_COL = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_CONTEXT_COL_MERGE = New System.Windows.Forms.ToolStripMenuItem() @@ -137,17 +150,17 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form CONTEXT_SEP_4 = New System.Windows.Forms.ToolStripSeparator() CONTEXT_SEP_5 = New System.Windows.Forms.ToolStripSeparator() SEP_4 = New System.Windows.Forms.ToolStripSeparator() - MENU_VIEW_SEP_1 = New System.Windows.Forms.ToolStripSeparator() - MENU_VIEW_SEP_3 = New System.Windows.Forms.ToolStripSeparator() MENU_VIEW_SEP_2 = New System.Windows.Forms.ToolStripSeparator() + MENU_VIEW_SEP_4 = New System.Windows.Forms.ToolStripSeparator() + MENU_VIEW_SEP_3 = New System.Windows.Forms.ToolStripSeparator() TRAY_SEP_1 = New System.Windows.Forms.ToolStripSeparator() MENU_DOWN_ALL_SEP_1 = New System.Windows.Forms.ToolStripSeparator() + TRAY_SEP_2 = 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() - TRAY_SEP_2 = New System.Windows.Forms.ToolStripSeparator() MENU_DOWN_ALL_SEP_4 = New System.Windows.Forms.ToolStripSeparator() - MENU_DOWN_ALL_SEP_5 = New System.Windows.Forms.ToolStripSeparator() - MENU_DOWN_ALL_SEP_6 = New System.Windows.Forms.ToolStripSeparator() + MENU_INFO = New System.Windows.Forms.ToolStripDropDownButton() + MENU_VIEW_SEP_1 = New System.Windows.Forms.ToolStripSeparator() Me.Toolbar_TOP.SuspendLayout() Me.Toolbar_BOTTOM.SuspendLayout() Me.USER_CONTEXT.SuspendLayout() @@ -204,21 +217,21 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form SEP_4.Name = "SEP_4" SEP_4.Size = New System.Drawing.Size(6, 25) ' - 'MENU_VIEW_SEP_1 + 'MENU_VIEW_SEP_2 ' - MENU_VIEW_SEP_1.Name = "MENU_VIEW_SEP_1" - MENU_VIEW_SEP_1.Size = New System.Drawing.Size(228, 6) + MENU_VIEW_SEP_2.Name = "MENU_VIEW_SEP_2" + MENU_VIEW_SEP_2.Size = New System.Drawing.Size(228, 6) + ' + 'MENU_VIEW_SEP_4 + ' + MENU_VIEW_SEP_4.Name = "MENU_VIEW_SEP_4" + MENU_VIEW_SEP_4.Size = New System.Drawing.Size(228, 6) ' 'MENU_VIEW_SEP_3 ' MENU_VIEW_SEP_3.Name = "MENU_VIEW_SEP_3" MENU_VIEW_SEP_3.Size = New System.Drawing.Size(228, 6) ' - 'MENU_VIEW_SEP_2 - ' - MENU_VIEW_SEP_2.Name = "MENU_VIEW_SEP_2" - MENU_VIEW_SEP_2.Size = New System.Drawing.Size(228, 6) - ' 'TRAY_SEP_1 ' TRAY_SEP_1.Name = "TRAY_SEP_1" @@ -227,37 +240,78 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form 'MENU_DOWN_ALL_SEP_1 ' MENU_DOWN_ALL_SEP_1.Name = "MENU_DOWN_ALL_SEP_1" - MENU_DOWN_ALL_SEP_1.Size = New System.Drawing.Size(228, 6) - ' - 'MENU_DOWN_ALL_SEP_2 - ' - 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_DOWN_ALL_SEP_1.Size = New System.Drawing.Size(218, 6) ' 'TRAY_SEP_2 ' TRAY_SEP_2.Name = "TRAY_SEP_2" TRAY_SEP_2.Size = New System.Drawing.Size(167, 6) ' + 'MENU_DOWN_ALL_SEP_2 + ' + MENU_DOWN_ALL_SEP_2.Name = "MENU_DOWN_ALL_SEP_2" + MENU_DOWN_ALL_SEP_2.Size = New System.Drawing.Size(218, 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(218, 6) + ' 'MENU_DOWN_ALL_SEP_4 ' MENU_DOWN_ALL_SEP_4.Name = "MENU_DOWN_ALL_SEP_4" - MENU_DOWN_ALL_SEP_4.Size = New System.Drawing.Size(228, 6) + MENU_DOWN_ALL_SEP_4.Size = New System.Drawing.Size(218, 6) ' - 'MENU_DOWN_ALL_SEP_5 + 'MENU_INFO ' - MENU_DOWN_ALL_SEP_5.Name = "MENU_DOWN_ALL_SEP_5" - MENU_DOWN_ALL_SEP_5.Size = New System.Drawing.Size(228, 6) + MENU_INFO.AutoToolTip = False + MENU_INFO.DropDownItems.AddRange(New System.Windows.Forms.ToolStripItem() {Me.MENU_INFO_SHOW_INFO, Me.MENU_INFO_SHOW_QUEUE, Me.MENU_INFO_SHOW_MISSING, Me.MENU_INFO_SHOW_USER_METRICS}) + MENU_INFO.Image = Global.SCrawler.My.Resources.Resources.InfoPic_32 + MENU_INFO.ImageTransparentColor = System.Drawing.Color.Magenta + MENU_INFO.Name = "MENU_INFO" + MENU_INFO.Size = New System.Drawing.Size(57, 22) + MENU_INFO.Text = "Info" ' - 'MENU_DOWN_ALL_SEP_6 + 'MENU_INFO_SHOW_INFO ' - MENU_DOWN_ALL_SEP_6.Name = "MENU_DOWN_ALL_SEP_6" - MENU_DOWN_ALL_SEP_6.Size = New System.Drawing.Size(228, 6) + Me.MENU_INFO_SHOW_INFO.AutoToolTip = True + Me.MENU_INFO_SHOW_INFO.Image = Global.SCrawler.My.Resources.Resources.InfoPic_32 + Me.MENU_INFO_SHOW_INFO.ImageTransparentColor = System.Drawing.Color.Magenta + Me.MENU_INFO_SHOW_INFO.Name = "MENU_INFO_SHOW_INFO" + Me.MENU_INFO_SHOW_INFO.Size = New System.Drawing.Size(212, 22) + Me.MENU_INFO_SHOW_INFO.Text = "Info (download summary)" + Me.MENU_INFO_SHOW_INFO.ToolTipText = "Open the 'Info' form (show download summary)." + ' + 'MENU_INFO_SHOW_QUEUE + ' + Me.MENU_INFO_SHOW_QUEUE.Image = Global.SCrawler.My.Resources.Resources.InfoPic_32 + Me.MENU_INFO_SHOW_QUEUE.Name = "MENU_INFO_SHOW_QUEUE" + Me.MENU_INFO_SHOW_QUEUE.Size = New System.Drawing.Size(212, 22) + Me.MENU_INFO_SHOW_QUEUE.Text = "Users download queue" + ' + 'MENU_INFO_SHOW_MISSING + ' + Me.MENU_INFO_SHOW_MISSING.AutoToolTip = True + Me.MENU_INFO_SHOW_MISSING.Image = Global.SCrawler.My.Resources.Resources.InfoPic_32 + Me.MENU_INFO_SHOW_MISSING.Name = "MENU_INFO_SHOW_MISSING" + Me.MENU_INFO_SHOW_MISSING.Size = New System.Drawing.Size(212, 22) + Me.MENU_INFO_SHOW_MISSING.Text = "Missing posts" + Me.MENU_INFO_SHOW_MISSING.ToolTipText = "Open the 'Missing' form (show information about missing posts)." + ' + 'MENU_INFO_SHOW_USER_METRICS + ' + Me.MENU_INFO_SHOW_USER_METRICS.AutoToolTip = True + Me.MENU_INFO_SHOW_USER_METRICS.Image = Global.SCrawler.My.Resources.Resources.InfoPic_32 + Me.MENU_INFO_SHOW_USER_METRICS.Name = "MENU_INFO_SHOW_USER_METRICS" + Me.MENU_INFO_SHOW_USER_METRICS.Size = New System.Drawing.Size(212, 22) + Me.MENU_INFO_SHOW_USER_METRICS.Text = "User metrics" + Me.MENU_INFO_SHOW_USER_METRICS.ToolTipText = "Open the ""User metrics' form (show information about the user's metrics (such as " & + "size, number of files, etc.))." + ' + 'MENU_VIEW_SEP_1 + ' + MENU_VIEW_SEP_1.Name = "MENU_VIEW_SEP_1" + MENU_VIEW_SEP_1.Size = New System.Drawing.Size(228, 6) ' 'MENU_SETTINGS ' @@ -279,7 +333,7 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form 'Toolbar_TOP ' Me.Toolbar_TOP.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden - Me.Toolbar_TOP.Items.AddRange(New System.Windows.Forms.ToolStripItem() {Me.MENU_SETTINGS, SEP_1, Me.BTT_ADD_USER, Me.BTT_EDIT_USER, Me.BTT_DELETE_USER, Me.BTT_REFRESH, Me.BTT_SHOW_INFO, Me.BTT_FEED, Me.BTT_CHANNELS, Me.BTT_DOWN_SAVED, SEP_2, Me.MENU_DOWN_ALL, Me.BTT_DOWN_STOP, SEP_3, Me.MENU_VIEW, SEP_4, Me.BTT_LOG, Me.BTT_VERSION_INFO, Me.BTT_DONATE}) + Me.Toolbar_TOP.Items.AddRange(New System.Windows.Forms.ToolStripItem() {Me.MENU_SETTINGS, SEP_1, Me.BTT_ADD_USER, Me.BTT_EDIT_USER, Me.BTT_DELETE_USER, Me.BTT_REFRESH, MENU_INFO, Me.BTT_FEED, Me.BTT_CHANNELS, Me.BTT_DOWN_SAVED, SEP_2, Me.MENU_DOWN_ALL, Me.BTT_DOWN_STOP, SEP_3, Me.MENU_VIEW, SEP_4, Me.BTT_LOG, Me.BTT_VERSION_INFO, Me.BTT_DONATE, Me.BTT_BUG_REPORT}) Me.Toolbar_TOP.Location = New System.Drawing.Point(0, 0) Me.Toolbar_TOP.Name = "Toolbar_TOP" Me.Toolbar_TOP.Size = New System.Drawing.Size(934, 25) @@ -322,15 +376,6 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form Me.BTT_REFRESH.Text = "Refresh" Me.BTT_REFRESH.ToolTipText = "Refresh user list" ' - 'BTT_SHOW_INFO - ' - Me.BTT_SHOW_INFO.Image = Global.SCrawler.My.Resources.Resources.InfoPic_32 - Me.BTT_SHOW_INFO.ImageTransparentColor = System.Drawing.Color.Magenta - Me.BTT_SHOW_INFO.Name = "BTT_SHOW_INFO" - Me.BTT_SHOW_INFO.Size = New System.Drawing.Size(48, 22) - Me.BTT_SHOW_INFO.Text = "Info" - Me.BTT_SHOW_INFO.ToolTipText = resources.GetString("BTT_SHOW_INFO.ToolTipText") - ' 'BTT_FEED ' Me.BTT_FEED.Image = Global.SCrawler.My.Resources.Resources.RSSPic_512 @@ -361,7 +406,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_SELECTED, MENU_DOWN_ALL_SEP_1, Me.BTT_DOWN_ALL, Me.BTT_DOWN_SITE, MENU_DOWN_ALL_SEP_2, Me.BTT_DOWN_ALL_FULL, Me.BTT_DOWN_SITE_FULL, MENU_DOWN_ALL_SEP_3, Me.BTT_DOWN_VIDEO, MENU_DOWN_ALL_SEP_4, Me.BTT_ADD_NEW_GROUP, MENU_DOWN_ALL_SEP_5, Me.BTT_SILENT_MODE, MENU_DOWN_ALL_SEP_6, Me.BTT_DOWN_AUTOMATION, Me.BTT_DOWN_AUTOMATION_PAUSE}) + Me.MENU_DOWN_ALL.DropDownItems.AddRange(New System.Windows.Forms.ToolStripItem() {Me.BTT_DOWN_SELECTED, Me.MENU_D_DOWN_ALL, Me.MENU_D_DOWN_ALL_SITE, MENU_DOWN_ALL_SEP_1, Me.BTT_DOWN_VIDEO, MENU_DOWN_ALL_SEP_2, Me.BTT_ADD_NEW_GROUP, MENU_DOWN_ALL_SEP_3, Me.BTT_SILENT_MODE, MENU_DOWN_ALL_SEP_4, Me.BTT_DOWN_AUTOMATION, Me.BTT_DOWN_AUTOMATION_PAUSE}) Me.MENU_DOWN_ALL.Image = Global.SCrawler.My.Resources.Resources.StartPic_Green_16 Me.MENU_DOWN_ALL.ImageTransparentColor = System.Drawing.Color.Magenta Me.MENU_DOWN_ALL.Name = "MENU_DOWN_ALL" @@ -374,49 +419,100 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form Me.BTT_DOWN_SELECTED.Image = Global.SCrawler.My.Resources.Resources.StartPic_Green_16 Me.BTT_DOWN_SELECTED.ImageTransparentColor = System.Drawing.Color.Magenta Me.BTT_DOWN_SELECTED.Name = "BTT_DOWN_SELECTED" - Me.BTT_DOWN_SELECTED.Size = New System.Drawing.Size(231, 22) + Me.BTT_DOWN_SELECTED.Size = New System.Drawing.Size(221, 22) Me.BTT_DOWN_SELECTED.Text = "Download selected (F5)" - Me.BTT_DOWN_SELECTED.ToolTipText = "Download selected user." & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "F5: download, include in the feed." & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "Ctrl+F5: download, e" & - "xclude from feed." + Me.BTT_DOWN_SELECTED.ToolTipText = "Download selected user." & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "F5: download, include in the feed." + ' + 'MENU_D_DOWN_ALL + ' + Me.MENU_D_DOWN_ALL.DropDownItems.AddRange(New System.Windows.Forms.ToolStripItem() {Me.BTT_DOWN_ALL, Me.BTT_DOWN_ALL_SUBSCR, Me.BTT_DOWN_SITE, Me.BTT_DOWN_SITE_SUBSCR}) + Me.MENU_D_DOWN_ALL.Image = Global.SCrawler.My.Resources.Resources.StartPic_Green_16 + Me.MENU_D_DOWN_ALL.Name = "MENU_D_DOWN_ALL" + Me.MENU_D_DOWN_ALL.Size = New System.Drawing.Size(221, 22) + Me.MENU_D_DOWN_ALL.Text = "Download all (F6)" ' 'BTT_DOWN_ALL ' Me.BTT_DOWN_ALL.AutoToolTip = True Me.BTT_DOWN_ALL.Image = Global.SCrawler.My.Resources.Resources.StartPic_Green_16 Me.BTT_DOWN_ALL.Name = "BTT_DOWN_ALL" - Me.BTT_DOWN_ALL.Size = New System.Drawing.Size(231, 22) + Me.BTT_DOWN_ALL.Size = New System.Drawing.Size(237, 22) Me.BTT_DOWN_ALL.Text = "Download all (F6)" - Me.BTT_DOWN_ALL.ToolTipText = "Download all users marked 'Ready for download' from all sites." & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "F5: download, inc" & - "lude in the feed." & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "Ctrl+F5: download, exclude from feed." + Me.BTT_DOWN_ALL.ToolTipText = "Download all users marked 'Ready for download' from all sites." & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "F6: download, inc" & + "lude in the feed." + ' + 'BTT_DOWN_ALL_SUBSCR + ' + Me.BTT_DOWN_ALL_SUBSCR.AutoToolTip = True + Me.BTT_DOWN_ALL_SUBSCR.Image = Global.SCrawler.My.Resources.Resources.StartPic_Green_16 + Me.BTT_DOWN_ALL_SUBSCR.Name = "BTT_DOWN_ALL_SUBSCR" + Me.BTT_DOWN_ALL_SUBSCR.Size = New System.Drawing.Size(237, 22) + Me.BTT_DOWN_ALL_SUBSCR.Text = "Download all subscriptions" + Me.BTT_DOWN_ALL_SUBSCR.ToolTipText = "Download all subscriptions marked 'Ready for download' from all sites." ' 'BTT_DOWN_SITE ' Me.BTT_DOWN_SITE.AutoToolTip = True Me.BTT_DOWN_SITE.Image = Global.SCrawler.My.Resources.Resources.StartPic_Green_16 Me.BTT_DOWN_SITE.Name = "BTT_DOWN_SITE" - Me.BTT_DOWN_SITE.Size = New System.Drawing.Size(231, 22) + Me.BTT_DOWN_SITE.Size = New System.Drawing.Size(237, 22) Me.BTT_DOWN_SITE.Text = "Download all site users" Me.BTT_DOWN_SITE.ToolTipText = "Download all users marked 'Ready for download' from specific sites." ' + 'BTT_DOWN_SITE_SUBSCR + ' + Me.BTT_DOWN_SITE_SUBSCR.AutoToolTip = True + Me.BTT_DOWN_SITE_SUBSCR.Image = Global.SCrawler.My.Resources.Resources.StartPic_Green_16 + Me.BTT_DOWN_SITE_SUBSCR.Name = "BTT_DOWN_SITE_SUBSCR" + Me.BTT_DOWN_SITE_SUBSCR.Size = New System.Drawing.Size(237, 22) + Me.BTT_DOWN_SITE_SUBSCR.Text = "Download all site subscriptions" + Me.BTT_DOWN_SITE_SUBSCR.ToolTipText = "Download all subscriptions marked 'Ready for download' from specific sites." + ' + 'MENU_D_DOWN_ALL_SITE + ' + Me.MENU_D_DOWN_ALL_SITE.DropDownItems.AddRange(New System.Windows.Forms.ToolStripItem() {Me.BTT_DOWN_ALL_FULL, Me.BTT_DOWN_ALL_FULL_SUBSCR, Me.BTT_DOWN_SITE_FULL, Me.BTT_DOWN_SITE_FULL_SUBSCR}) + Me.MENU_D_DOWN_ALL_SITE.Image = Global.SCrawler.My.Resources.Resources.StartPic_Green_16 + Me.MENU_D_DOWN_ALL_SITE.Name = "MENU_D_DOWN_ALL_SITE" + Me.MENU_D_DOWN_ALL_SITE.Size = New System.Drawing.Size(221, 22) + Me.MENU_D_DOWN_ALL_SITE.Text = "Download all [FULL]" + ' 'BTT_DOWN_ALL_FULL ' Me.BTT_DOWN_ALL_FULL.AutoToolTip = True Me.BTT_DOWN_ALL_FULL.Image = Global.SCrawler.My.Resources.Resources.StartPic_Green_16 Me.BTT_DOWN_ALL_FULL.Name = "BTT_DOWN_ALL_FULL" - Me.BTT_DOWN_ALL_FULL.Size = New System.Drawing.Size(231, 22) + Me.BTT_DOWN_ALL_FULL.Size = New System.Drawing.Size(274, 22) Me.BTT_DOWN_ALL_FULL.Text = "Download all [FULL]" Me.BTT_DOWN_ALL_FULL.ToolTipText = "Download all users from all sites. The 'Ready for download' option will be ignore" & "d." + ' + 'BTT_DOWN_ALL_FULL_SUBSCR + ' + Me.BTT_DOWN_ALL_FULL_SUBSCR.AutoToolTip = True + Me.BTT_DOWN_ALL_FULL_SUBSCR.Image = Global.SCrawler.My.Resources.Resources.StartPic_Green_16 + Me.BTT_DOWN_ALL_FULL_SUBSCR.Name = "BTT_DOWN_ALL_FULL_SUBSCR" + Me.BTT_DOWN_ALL_FULL_SUBSCR.Size = New System.Drawing.Size(274, 22) + Me.BTT_DOWN_ALL_FULL_SUBSCR.Text = "Download all subscriptions [FULL]" + Me.BTT_DOWN_ALL_FULL_SUBSCR.ToolTipText = "Download all subscriptions from all sites. The 'Ready for download' option will b" & + "e ignored." ' 'BTT_DOWN_SITE_FULL ' Me.BTT_DOWN_SITE_FULL.AutoToolTip = True Me.BTT_DOWN_SITE_FULL.Image = Global.SCrawler.My.Resources.Resources.StartPic_Green_16 Me.BTT_DOWN_SITE_FULL.Name = "BTT_DOWN_SITE_FULL" - Me.BTT_DOWN_SITE_FULL.Size = New System.Drawing.Size(231, 22) + Me.BTT_DOWN_SITE_FULL.Size = New System.Drawing.Size(274, 22) Me.BTT_DOWN_SITE_FULL.Text = "Download all site users [FULL]" - Me.BTT_DOWN_SITE_FULL.ToolTipText = "Download all users from specific sites. The 'Ready for download' option will be i" & - "gnored." + Me.BTT_DOWN_SITE_FULL.ToolTipText = resources.GetString("BTT_DOWN_SITE_FULL.ToolTipText") + ' + 'BTT_DOWN_SITE_FULL_SUBSCR + ' + Me.BTT_DOWN_SITE_FULL_SUBSCR.AutoToolTip = True + Me.BTT_DOWN_SITE_FULL_SUBSCR.Image = Global.SCrawler.My.Resources.Resources.StartPic_Green_16 + Me.BTT_DOWN_SITE_FULL_SUBSCR.Name = "BTT_DOWN_SITE_FULL_SUBSCR" + Me.BTT_DOWN_SITE_FULL_SUBSCR.Size = New System.Drawing.Size(274, 22) + Me.BTT_DOWN_SITE_FULL_SUBSCR.Text = "Download all site subscriptions [FULL]" + Me.BTT_DOWN_SITE_FULL_SUBSCR.ToolTipText = resources.GetString("BTT_DOWN_SITE_FULL_SUBSCR.ToolTipText") ' 'BTT_DOWN_VIDEO ' @@ -424,7 +520,7 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form Me.BTT_DOWN_VIDEO.Image = Global.SCrawler.My.Resources.Resources.ArrowDownPic_Blue_24 Me.BTT_DOWN_VIDEO.ImageTransparentColor = System.Drawing.Color.Magenta Me.BTT_DOWN_VIDEO.Name = "BTT_DOWN_VIDEO" - Me.BTT_DOWN_VIDEO.Size = New System.Drawing.Size(231, 22) + Me.BTT_DOWN_VIDEO.Size = New System.Drawing.Size(221, 22) Me.BTT_DOWN_VIDEO.Text = "Standalone downloader" Me.BTT_DOWN_VIDEO.ToolTipText = "Download video by URL" ' @@ -432,7 +528,7 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form ' Me.BTT_ADD_NEW_GROUP.Image = Global.SCrawler.My.Resources.Resources.PlusPic_24 Me.BTT_ADD_NEW_GROUP.Name = "BTT_ADD_NEW_GROUP" - Me.BTT_ADD_NEW_GROUP.Size = New System.Drawing.Size(231, 22) + Me.BTT_ADD_NEW_GROUP.Size = New System.Drawing.Size(221, 22) Me.BTT_ADD_NEW_GROUP.Text = "Add a new download group" ' 'BTT_SILENT_MODE @@ -440,7 +536,7 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form Me.BTT_SILENT_MODE.AutoToolTip = True Me.BTT_SILENT_MODE.Image = Global.SCrawler.My.Resources.Resources.MessagePic_16 Me.BTT_SILENT_MODE.Name = "BTT_SILENT_MODE" - Me.BTT_SILENT_MODE.Size = New System.Drawing.Size(231, 22) + Me.BTT_SILENT_MODE.Size = New System.Drawing.Size(221, 22) Me.BTT_SILENT_MODE.Text = "Silent mode" Me.BTT_SILENT_MODE.ToolTipText = "Turn off notifications temporarily." & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "This setting is not stored in the settings f" & "ile. It is valid until you turn it off or close the program." & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) @@ -449,14 +545,14 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form ' Me.BTT_DOWN_AUTOMATION.Image = Global.SCrawler.My.Resources.Resources.ScriptPic_32 Me.BTT_DOWN_AUTOMATION.Name = "BTT_DOWN_AUTOMATION" - Me.BTT_DOWN_AUTOMATION.Size = New System.Drawing.Size(231, 22) + Me.BTT_DOWN_AUTOMATION.Size = New System.Drawing.Size(221, 22) Me.BTT_DOWN_AUTOMATION.Text = "Automation" ' 'BTT_DOWN_AUTOMATION_PAUSE ' Me.BTT_DOWN_AUTOMATION_PAUSE.Image = Global.SCrawler.My.Resources.Resources.Pause_Blue_16 Me.BTT_DOWN_AUTOMATION_PAUSE.Name = "BTT_DOWN_AUTOMATION_PAUSE" - Me.BTT_DOWN_AUTOMATION_PAUSE.Size = New System.Drawing.Size(231, 22) + Me.BTT_DOWN_AUTOMATION_PAUSE.Size = New System.Drawing.Size(221, 22) Me.BTT_DOWN_AUTOMATION_PAUSE.Text = "Pause automation" ' 'BTT_DOWN_STOP @@ -472,7 +568,7 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form 'MENU_VIEW ' Me.MENU_VIEW.AutoToolTip = False - Me.MENU_VIEW.DropDownItems.AddRange(New System.Windows.Forms.ToolStripItem() {Me.BTT_VIEW_LARGE, Me.BTT_VIEW_SMALL, Me.BTT_VIEW_LIST, Me.BTT_VIEW_DETAILS, MENU_VIEW_SEP_1, Me.BTT_SITE_ALL, Me.BTT_SITE_SPECIFIC, MENU_VIEW_SEP_2, Me.BTT_SHOW_ALL, Me.BTT_SHOW_REGULAR, Me.BTT_SHOW_TEMP, Me.BTT_SHOW_FAV, Me.BTT_SHOW_DELETED, Me.BTT_SHOW_SUSPENDED, Me.BTT_SHOW_LABELS, Me.BTT_SHOW_NO_LABELS, Me.BTT_SHOW_EXCLUDED_LABELS, Me.BTT_SHOW_EXCLUDED_LABELS_IGNORE, Me.BTT_SHOW_SHOW_GROUPS, MENU_VIEW_SEP_3, Me.BTT_SHOW_LIMIT_DATES_NOT, Me.BTT_SHOW_LIMIT_DATES_IN}) + Me.MENU_VIEW.DropDownItems.AddRange(New System.Windows.Forms.ToolStripItem() {Me.BTT_VIEW_LARGE, Me.BTT_VIEW_SMALL, Me.BTT_VIEW_LIST, Me.BTT_VIEW_DETAILS, MENU_VIEW_SEP_1, Me.BTT_MODE_SHOW_USERS, Me.BTT_MODE_SHOW_SUBSCRIPTIONS, MENU_VIEW_SEP_2, Me.BTT_SITE_ALL, Me.BTT_SITE_SPECIFIC, MENU_VIEW_SEP_3, Me.BTT_SHOW_ALL, Me.BTT_SHOW_REGULAR, Me.BTT_SHOW_TEMP, Me.BTT_SHOW_FAV, Me.BTT_SHOW_DELETED, Me.BTT_SHOW_SUSPENDED, Me.BTT_SHOW_LABELS, Me.BTT_SHOW_NO_LABELS, Me.BTT_SHOW_EXCLUDED_LABELS, Me.BTT_SHOW_EXCLUDED_LABELS_IGNORE, Me.BTT_SHOW_SHOW_GROUPS, MENU_VIEW_SEP_4, Me.BTT_SHOW_LIMIT_DATES_NOT, Me.BTT_SHOW_LIMIT_DATES_IN}) Me.MENU_VIEW.Image = CType(resources.GetObject("MENU_VIEW.Image"), System.Drawing.Image) Me.MENU_VIEW.ImageTransparentColor = System.Drawing.Color.Magenta Me.MENU_VIEW.Name = "MENU_VIEW" @@ -503,6 +599,20 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form Me.BTT_VIEW_DETAILS.Size = New System.Drawing.Size(231, 22) Me.BTT_VIEW_DETAILS.Text = "Details" ' + 'BTT_MODE_SHOW_USERS + ' + Me.BTT_MODE_SHOW_USERS.CheckOnClick = True + Me.BTT_MODE_SHOW_USERS.Name = "BTT_MODE_SHOW_USERS" + Me.BTT_MODE_SHOW_USERS.Size = New System.Drawing.Size(231, 22) + Me.BTT_MODE_SHOW_USERS.Text = "Show users" + ' + 'BTT_MODE_SHOW_SUBSCRIPTIONS + ' + Me.BTT_MODE_SHOW_SUBSCRIPTIONS.CheckOnClick = True + Me.BTT_MODE_SHOW_SUBSCRIPTIONS.Name = "BTT_MODE_SHOW_SUBSCRIPTIONS" + Me.BTT_MODE_SHOW_SUBSCRIPTIONS.Size = New System.Drawing.Size(231, 22) + Me.BTT_MODE_SHOW_SUBSCRIPTIONS.Text = "Show subscriptions" + ' 'BTT_SITE_ALL ' Me.BTT_SITE_ALL.Name = "BTT_SITE_ALL" @@ -632,6 +742,16 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form Me.BTT_DONATE.Text = "Donate" Me.BTT_DONATE.ToolTipText = "Support" ' + 'BTT_BUG_REPORT + ' + Me.BTT_BUG_REPORT.Alignment = System.Windows.Forms.ToolStripItemAlignment.Right + Me.BTT_BUG_REPORT.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image + Me.BTT_BUG_REPORT.Image = CType(resources.GetObject("BTT_BUG_REPORT.Image"), System.Drawing.Image) + Me.BTT_BUG_REPORT.ImageTransparentColor = System.Drawing.Color.Magenta + Me.BTT_BUG_REPORT.Name = "BTT_BUG_REPORT" + Me.BTT_BUG_REPORT.Size = New System.Drawing.Size(23, 22) + Me.BTT_BUG_REPORT.Text = "Bug report" + ' 'Toolbar_BOTTOM ' Me.Toolbar_BOTTOM.Items.AddRange(New System.Windows.Forms.ToolStripItem() {Me.BTT_PR_INFO, Me.PR_PRE, Me.PR_MAIN, Me.LBL_JOBS_COUNT, Me.LBL_STATUS}) @@ -691,9 +811,9 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form ' 'USER_CONTEXT ' - Me.USER_CONTEXT.Items.AddRange(New System.Windows.Forms.ToolStripItem() {Me.BTT_CONTEXT_DOWN, Me.BTT_CONTEXT_DOWN_LIMITED, Me.BTT_CONTEXT_DOWN_DATE_LIMIT, Me.BTT_CONTEXT_EDIT, Me.BTT_CONTEXT_DELETE, Me.BTT_CONTEXT_COPY_TO_FOLDER, CONTEXT_SEP_1, Me.BTT_CONTEXT_FAV, Me.BTT_CONTEXT_TEMP, Me.BTT_CONTEXT_READY, Me.BTT_CONTEXT_GROUPS, Me.BTT_CONTEXT_SCRIPT, Me.BTT_CONTEXT_ADD_TO_COL, Me.BTT_CONTEXT_COL_MERGE, Me.BTT_CONTEXT_CHANGE_FOLDER, CONTEXT_SEP_2, Me.BTT_CHANGE_IMAGE, CONTEXT_SEP_3, Me.BTT_CONTEXT_OPEN_PATH, CONTEXT_SEP_4, Me.BTT_CONTEXT_OPEN_SITE, CONTEXT_SEP_5, Me.BTT_CONTEXT_INFO}) + Me.USER_CONTEXT.Items.AddRange(New System.Windows.Forms.ToolStripItem() {Me.BTT_CONTEXT_DOWN, Me.BTT_CONTEXT_DOWN_LIMITED, Me.BTT_CONTEXT_DOWN_DATE_LIMIT, Me.BTT_CONTEXT_EDIT, Me.BTT_CONTEXT_DELETE, Me.BTT_CONTEXT_ERASE, Me.BTT_CONTEXT_COPY_TO_FOLDER, CONTEXT_SEP_1, Me.BTT_CONTEXT_FAV, Me.BTT_CONTEXT_TEMP, Me.BTT_CONTEXT_READY, Me.BTT_CONTEXT_GROUPS, Me.BTT_CONTEXT_SCRIPT, Me.BTT_CONTEXT_ADD_TO_COL, Me.BTT_CONTEXT_COL_MERGE, Me.BTT_CONTEXT_CHANGE_FOLDER, CONTEXT_SEP_2, Me.BTT_CHANGE_IMAGE, CONTEXT_SEP_3, Me.BTT_CONTEXT_OPEN_PATH, CONTEXT_SEP_4, Me.BTT_CONTEXT_OPEN_SITE, CONTEXT_SEP_5, Me.BTT_CONTEXT_INFO}) Me.USER_CONTEXT.Name = "USER_CONTEXT" - Me.USER_CONTEXT.Size = New System.Drawing.Size(222, 430) + Me.USER_CONTEXT.Size = New System.Drawing.Size(222, 452) ' 'BTT_CONTEXT_DOWN ' @@ -734,6 +854,13 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form Me.BTT_CONTEXT_DELETE.Size = New System.Drawing.Size(221, 22) Me.BTT_CONTEXT_DELETE.Text = "Delete user / collection" ' + 'BTT_CONTEXT_ERASE + ' + Me.BTT_CONTEXT_ERASE.Image = Global.SCrawler.My.Resources.Resources.BrushToolPic_16 + Me.BTT_CONTEXT_ERASE.Name = "BTT_CONTEXT_ERASE" + Me.BTT_CONTEXT_ERASE.Size = New System.Drawing.Size(221, 22) + Me.BTT_CONTEXT_ERASE.Text = "Erase data" + ' 'BTT_CONTEXT_COPY_TO_FOLDER ' Me.BTT_CONTEXT_COPY_TO_FOLDER.Image = Global.SCrawler.My.Resources.Resources.PastePic_32 @@ -757,16 +884,19 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form ' 'BTT_CONTEXT_READY ' + Me.BTT_CONTEXT_READY.Image = Global.SCrawler.My.Resources.Resources.OkPic_32 Me.BTT_CONTEXT_READY.Name = "BTT_CONTEXT_READY" Me.BTT_CONTEXT_READY.Size = New System.Drawing.Size(221, 22) Me.BTT_CONTEXT_READY.Text = "Change ready for download" ' 'BTT_CONTEXT_GROUPS ' + Me.BTT_CONTEXT_GROUPS.AutoToolTip = True Me.BTT_CONTEXT_GROUPS.Image = Global.SCrawler.My.Resources.Resources.TagPic_24 Me.BTT_CONTEXT_GROUPS.Name = "BTT_CONTEXT_GROUPS" Me.BTT_CONTEXT_GROUPS.Size = New System.Drawing.Size(221, 22) Me.BTT_CONTEXT_GROUPS.Text = "Change labels" + Me.BTT_CONTEXT_GROUPS.ToolTipText = "Change user labels." & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "Ctrl+click to include site-specific labels." ' 'BTT_CONTEXT_SCRIPT ' @@ -784,6 +914,7 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form ' 'BTT_CONTEXT_COL_MERGE ' + Me.BTT_CONTEXT_COL_MERGE.Image = Global.SCrawler.My.Resources.Resources.DBPic_32 Me.BTT_CONTEXT_COL_MERGE.Name = "BTT_CONTEXT_COL_MERGE" Me.BTT_CONTEXT_COL_MERGE.Size = New System.Drawing.Size(221, 22) Me.BTT_CONTEXT_COL_MERGE.Text = "Merge collection files" @@ -791,6 +922,7 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form 'BTT_CONTEXT_CHANGE_FOLDER ' Me.BTT_CONTEXT_CHANGE_FOLDER.AutoToolTip = True + Me.BTT_CONTEXT_CHANGE_FOLDER.Image = Global.SCrawler.My.Resources.Resources.FolderPic_32 Me.BTT_CONTEXT_CHANGE_FOLDER.Name = "BTT_CONTEXT_CHANGE_FOLDER" Me.BTT_CONTEXT_CHANGE_FOLDER.Size = New System.Drawing.Size(221, 22) Me.BTT_CONTEXT_CHANGE_FOLDER.Text = "Change folder" @@ -954,7 +1086,7 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form Private WithEvents BTT_CONTEXT_COL_MERGE As ToolStripMenuItem Private WithEvents LBL_JOBS_COUNT As ToolStripStatusLabel Private WithEvents BTT_DOWN_VIDEO As ToolStripMenuItem - Private WithEvents BTT_SHOW_INFO As PersonalUtilities.Forms.Controls.KeyClick.ToolStripButtonKeyClick + Private WithEvents MENU_INFO_SHOW_INFO As ToolStripMenuItem Private WithEvents BTT_CHANNELS As ToolStripButton Private WithEvents LIST_PROFILES As ListView Private WithEvents MENU_VIEW As ToolStripDropDownButton @@ -967,7 +1099,7 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form Private WithEvents BTT_SHOW_LABELS As ToolStripMenuItem Private WithEvents BTT_SHOW_NO_LABELS As ToolStripMenuItem Private WithEvents BTT_EDIT_USER As ToolStripButton - Private WithEvents BTT_CONTEXT_GROUPS As ToolStripMenuItem + Private WithEvents BTT_CONTEXT_GROUPS As PersonalUtilities.Forms.Controls.KeyClick.ToolStripMenuItemKeyClick Private WithEvents BTT_VERSION_INFO As ToolStripButton Private WithEvents BTT_CONTEXT_DOWN_LIMITED As ToolStripKeyMenuItem Private WithEvents BTT_CONTEXT_READY As ToolStripMenuItem @@ -1013,4 +1145,17 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form Private WithEvents BTT_TRAY_CHANNELS As ToolStripMenuItem Private WithEvents BTT_TRAY_DOWNLOADER As ToolStripMenuItem Private WithEvents PR_PRE As ToolStripProgressBar + Private WithEvents BTT_CONTEXT_ERASE As ToolStripMenuItem + Private WithEvents MENU_INFO_SHOW_MISSING As ToolStripMenuItem + Private WithEvents MENU_INFO_SHOW_USER_METRICS As ToolStripMenuItem + Private WithEvents BTT_MODE_SHOW_USERS As ToolStripMenuItem + Private WithEvents BTT_MODE_SHOW_SUBSCRIPTIONS As ToolStripMenuItem + Private WithEvents MENU_D_DOWN_ALL As ToolStripMenuItem + Private WithEvents MENU_D_DOWN_ALL_SITE As ToolStripMenuItem + Private WithEvents BTT_DOWN_ALL_SUBSCR As ToolStripKeyMenuItem + Private WithEvents BTT_DOWN_SITE_SUBSCR As ToolStripKeyMenuItem + Private WithEvents BTT_DOWN_ALL_FULL_SUBSCR As ToolStripKeyMenuItem + Private WithEvents BTT_DOWN_SITE_FULL_SUBSCR As ToolStripKeyMenuItem + Private WithEvents BTT_BUG_REPORT As ToolStripButton + Private WithEvents MENU_INFO_SHOW_QUEUE As ToolStripMenuItem End Class \ No newline at end of file diff --git a/SCrawler/MainFrame.resx b/SCrawler/MainFrame.resx index 56e777a..64790e9 100644 --- a/SCrawler/MainFrame.resx +++ b/SCrawler/MainFrame.resx @@ -147,46 +147,51 @@ False - + + False + + False False - - False - False False + + False + False False - - False - False - + False - + False 132, 17 - - Left-click: open the 'Info' form (show download summary). -Right click: open the 'Missing' form (show information about missing posts). -Ctrl+Shift+Click: open the "User metrics' form (show information about the user's metrics (such as size, number of files, etc.)). + + Download all users from specific sites. The 'Ready for download' option will be ignored. +Shift+Click to download, including non-existent users. +Ctrl+Shift+Click to download, excluding from the feed, including non-existent users. + + + Download all subscriptions from specific sites. The 'Ready for download' option will be ignored. +Shift+Click to download, including non-existent users. +Ctrl+Shift+Click to download, excluding from the feed, including non-existent users. @@ -207,6 +212,21 @@ Ctrl+Shift+Click: open the "User metrics' form (show information about the user' 9k7wdgtW4wRqSHlCP7y2AjWmbMB7Y7DzqgZdz2iF9zrxCDXq2oU9uLz31+tgAcHahhp1DSFY9pGhRl29 CFYXxrMoQ7BmsZfFPkoRpHWow+56hX26BWkRatR1gRIEaQLvUMMpOyhCkBpxBzWcMoOgLUMNm0vUIWj2 ebaJF7jj5+hGTiqE/f+bxDRGUIt8LIp+AC/GHt3tQnwvAAAAAElFTkSuQmCC + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG + YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9 + 0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw + bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc + VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9 + c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32 + Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo + mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+ + kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D + TgDQASA1MVpwzwAAAABJRU5ErkJggg== diff --git a/SCrawler/MainFrame.vb b/SCrawler/MainFrame.vb index 74cf8e0..68fd7f4 100644 --- a/SCrawler/MainFrame.vb +++ b/SCrawler/MainFrame.vb @@ -25,6 +25,7 @@ Public Class MainFrame Friend MyChannels As ChannelViewForm Friend MySavedPosts As DownloadSavedPostsForm Private MyMissingPosts As MissingPostsForm + Private DownloadQueue As UserDownloadQueueForm Private MyFeed As DownloadFeedForm Private MySearch As UserSearchForm Private MyUserMetrics As UsersInfoForm = Nothing @@ -44,11 +45,16 @@ Public Class MainFrame End With BTT_IMPORT_USERS = New ToolStripMenuItem With {.Text = "Import", .Image = My.Resources.UsersIcon_32.ToBitmap} MENU_SETTINGS.DropDownItems.AddRange({New ToolStripSeparator, BTT_IMPORT_USERS}) + BTT_BUG_REPORT.Image = My.Resources.MailPic_16 End Sub #End Region #Region "Form handlers" Private Async Sub MainFrame_Load(sender As Object, e As EventArgs) Handles Me.Load - If Now.Month.ValueBetween(6, 8) Then Text = "SCrawler: Happy LGBT Pride Month! :-)" + If Now.Month.ValueBetween(6, 8) Then + Text = "SCrawler: Happy LGBT Pride Month! :-)" + ElseIf Not Settings.ProgramText.IsEmptyString Then + Text = Settings.ProgramText + End If Settings.DeleteCachePath() MainFrameObj = New MainFrameObjects(Me) MainFrameObj.ChangeCloseVisible() @@ -94,6 +100,8 @@ Public Class MainFrame UpdateLabelsGroups() SetShowButtonsCheckers(.ShowingMode.Value) CheckVersion(False) + BTT_MODE_SHOW_USERS.Checked = .MainFrameUsersShowDefaults + BTT_MODE_SHOW_SUBSCRIPTIONS.Checked = .MainFrameUsersShowSubscriptions BTT_SITE_ALL.Checked = .SelectedSites.Count = 0 BTT_SITE_SPECIFIC.Checked = .SelectedSites.Count > 0 BTT_SHOW_LIMIT_DATES_NOT.Tag = ShowingDates.Not @@ -151,6 +159,7 @@ Public Class MainFrame Downloader.Dispose() MyProgressForm.Dispose() InfoForm.Dispose() + DownloadQueue.DisposeIfReady() MyMissingPosts.DisposeIfReady() MyFeed.DisposeIfReady() MainFrameObj.ClearNotifications() @@ -360,6 +369,7 @@ CloseResume: Settings.Users.Add(UserDataBase.GetInstance(f.User)) With Settings.Users.Last If Not .FileExists Then + .Options = f.Options .Favorite = f.UserFavorite .Temporary = f.UserTemporary .ParseUserMediaOnly = f.UserMediaOnly @@ -367,6 +377,8 @@ CloseResume: .DownloadImages = f.DownloadImages .DownloadVideos = f.DownloadVideos .FriendlyName = f.UserFriendly + .BackColor = f.UserBackColor + .ForeColor = f.UserForeColor .Description = f.UserDescr .ScriptUse = f.ScriptUse .ScriptData = f.ScriptData @@ -403,18 +415,19 @@ CloseResume: End Sub #End Region #Region "Info, Feed, Channels, Saved posts" - Private Sub BTT_SHOW_INFO_KeyClick(ByVal Sender As Object, ByVal e As Controls.KeyClick.KeyClickEventArgs) Handles BTT_SHOW_INFO.KeyClick - If e.MouseButton = MouseButtons.Right Then - If MyMissingPosts Is Nothing Then MyMissingPosts = New MissingPostsForm - If MyMissingPosts.Visible Then MyMissingPosts.BringToFront() Else MyMissingPosts.Show() - ElseIf e.MouseButton = MouseButtons.Left Then - If e.Control And e.Shift Then - If MyUserMetrics Is Nothing Then MyUserMetrics = New UsersInfoForm - MyUserMetrics.FormShowS - Else - InfoForm.FormShow() - End If - End If + Private Sub MENU_INFO_SHOW_INFO_Click(sender As Object, e As EventArgs) Handles MENU_INFO_SHOW_INFO.Click + InfoForm.FormShow() + End Sub + Private Sub MENU_INFO_SHOW_QUEUE_Click(sender As Object, e As EventArgs) Handles MENU_INFO_SHOW_QUEUE.Click + DownloadQueue.FormShow(EDP.LogMessageValue) + End Sub + Private Sub MENU_INFO_SHOW_MISSING_Click(sender As Object, e As EventArgs) Handles MENU_INFO_SHOW_MISSING.Click + If MyMissingPosts Is Nothing Then MyMissingPosts = New MissingPostsForm + If MyMissingPosts.Visible Then MyMissingPosts.BringToFront() Else MyMissingPosts.Show() + End Sub + Private Sub MENU_INFO_SHOW_USER_METRICS_Click(sender As Object, e As EventArgs) Handles MENU_INFO_SHOW_USER_METRICS.Click + If MyUserMetrics Is Nothing Then MyUserMetrics = New UsersInfoForm + MyUserMetrics.FormShowS End Sub Private Sub ShowFeed() Handles BTT_FEED.Click, BTT_TRAY_FEED_SHOW.Click If MyFeed Is Nothing Then MyFeed = New DownloadFeedForm : AddHandler Downloader.FeedFilesChanged, AddressOf MyFeed.Downloader_FilesChanged @@ -439,22 +452,39 @@ CloseResume: End Sub #End Region #Region "Download" - Private Sub BTT_DOWN_SELECTED_KeyClick(sender As Object, e As MyKeyEventArgs) Handles BTT_DOWN_SELECTED.KeyClick + Private Sub BTT_DOWN_SELECTED_KeyClick(ByVal Sender As Object, ByVal e As MyKeyEventArgs) Handles BTT_DOWN_SELECTED.KeyClick DownloadSelectedUser(DownUserLimits.None, e.IncludeInTheFeed) End Sub - Private Sub BTT_DOWN_ALL_KeyClick(sender As Object, e As MyKeyEventArgs) Handles BTT_DOWN_ALL.KeyClick - Downloader.AddRange(Settings.GetUsers(Function(u) u.ReadyForDownload And u.Exists), e.IncludeInTheFeed) +#Region "Down all" + Private Sub BTT_DOWN_ALL_KeyClick(ByVal Sender As Object, ByVal e As MyKeyEventArgs) Handles BTT_DOWN_ALL.KeyClick + Downloader.AddRange(Settings.GetUsers(Function(u) u.ReadyForDownload And UserExistsNonSubscriptionsPredicate.Invoke(u)), e.IncludeInTheFeed) End Sub - Private Sub BTT_DOWN_SITE_KeyClick(sender As Object, e As MyKeyEventArgs) Handles BTT_DOWN_SITE.KeyClick - DownloadSiteFull(True, e.IncludeInTheFeed) + Private Sub BTT_DOWN_ALL_SUBSCR_KeyClick(ByVal Sender As Object, ByVal e As MyKeyEventArgs) Handles BTT_DOWN_ALL_SUBSCR.KeyClick + Downloader.AddRange(Settings.GetUsers(Function(u) u.ReadyForDownload And UserExistsSubscriptionsPredicate.Invoke(u)), e.IncludeInTheFeed) End Sub - Private Sub BTT_DOWN_ALL_FULL_KeyClick(sender As Object, e As MyKeyEventArgs) Handles BTT_DOWN_ALL_FULL.KeyClick - Downloader.AddRange(Settings.GetUsers(UserExistsPredicate), e.IncludeInTheFeed) + Private Sub BTT_DOWN_SITE_KeyClick(ByVal Sender As Object, ByVal e As MyKeyEventArgs) Handles BTT_DOWN_SITE.KeyClick + DownloadSiteFull(True, e.IncludeInTheFeed, False) End Sub - Private Sub BTT_DOWN_SITE_FULL_KeyClick(sender As Object, e As MyKeyEventArgs) Handles BTT_DOWN_SITE_FULL.KeyClick - DownloadSiteFull(False, e.IncludeInTheFeed, e.Shift) + Private Sub BTT_DOWN_SITE_SUBSCR_KeyClick(ByVal Sender As Object, ByVal e As MyKeyEventArgs) Handles BTT_DOWN_SITE_SUBSCR.KeyClick + DownloadSiteFull(True, e.IncludeInTheFeed, True) End Sub - Private Sub DownloadSiteFull(ByVal ReadyForDownloadOnly As Boolean, ByVal IncludeInTheFeed As Boolean, Optional ByVal IgnoreExists As Boolean = False) +#End Region +#Region "Down full" + Private Sub BTT_DOWN_ALL_FULL_KeyClick(ByVal Sender As Object, ByVal e As MyKeyEventArgs) Handles BTT_DOWN_ALL_FULL.KeyClick + Downloader.AddRange(Settings.GetUsers(UserExistsNonSubscriptionsPredicate), e.IncludeInTheFeed) + End Sub + Private Sub BTT_DOWN_ALL_FULL_SUBSCR_KeyClick(ByVal Sender As Object, ByVal e As MyKeyEventArgs) Handles BTT_DOWN_ALL_FULL_SUBSCR.KeyClick + Downloader.AddRange(Settings.GetUsers(UserExistsSubscriptionsPredicate), e.IncludeInTheFeed) + End Sub + Private Sub BTT_DOWN_SITE_FULL_KeyClick(ByVal Sender As Object, ByVal e As MyKeyEventArgs) Handles BTT_DOWN_SITE_FULL.KeyClick + DownloadSiteFull(False, e.IncludeInTheFeed, False, e.Shift) + End Sub + Private Sub BTT_DOWN_SITE_FULL_SUBSCR_KeyClick(ByVal Sender As Object, ByVal e As MyKeyEventArgs) Handles BTT_DOWN_SITE_FULL_SUBSCR.KeyClick + DownloadSiteFull(False, e.IncludeInTheFeed, True, e.Shift) + End Sub +#End Region + Private Sub DownloadSiteFull(ByVal ReadyForDownloadOnly As Boolean, ByVal IncludeInTheFeed As Boolean, + ByVal Subscription As Boolean, Optional ByVal IgnoreExists As Boolean = False) Using f As New SiteSelectionForm(Settings.LatestDownloadedSites.ValuesList) f.ShowDialog() If f.DialogResult = DialogResult.OK Then @@ -463,6 +493,7 @@ CloseResume: Settings.LatestDownloadedSites.Update() If f.SelectedSites.Count > 0 Then Downloader.AddRange(Settings.GetUsers(Function(u) f.SelectedSites.Contains(u.Site) And (u.Exists Or IgnoreExists) And + u.IsSubscription = Subscription And (Not ReadyForDownloadOnly Or u.ReadyForDownload)), IncludeInTheFeed) End If End If @@ -521,7 +552,7 @@ CloseResume: End Sub #End Region #Region "View" -#Region "1 - view mode" +#Region "1 - view mode list" Private Sub BTT_VIEW_LARGE_Click(sender As Object, e As EventArgs) Handles BTT_VIEW_LARGE.Click ApplyViewPattern(ViewModes.IconLarge) End Sub @@ -558,7 +589,17 @@ CloseResume: End If End Sub #End Region -#Region "2 - view site" +#Region "2 - view mode users" + Private Sub BTT_MODE_SHOW_USERS_Click(sender As Object, e As EventArgs) Handles BTT_MODE_SHOW_USERS.Click + Settings.MainFrameUsersShowDefaults.Value = BTT_MODE_SHOW_USERS.Checked + RefillList() + End Sub + Private Sub BTT_MODE_SHOW_SUBSCRIPTIONS_Click(sender As Object, e As EventArgs) Handles BTT_MODE_SHOW_SUBSCRIPTIONS.Click + Settings.MainFrameUsersShowSubscriptions.Value = BTT_MODE_SHOW_SUBSCRIPTIONS.Checked + RefillList() + End Sub +#End Region +#Region "3 - view site" Private Sub BTT_SITE_ALL_Click(sender As Object, e As EventArgs) Handles BTT_SITE_ALL.Click Settings.SelectedSites.Clear() Settings.SelectedSites.Update() @@ -580,7 +621,7 @@ CloseResume: End Using End Sub #End Region -#Region "3 - view filters" +#Region "4 - view filters" Private Sub BTT_SHOW_ALL_Click(sender As Object, e As EventArgs) Handles BTT_SHOW_ALL.Click SetShowButtonsCheckers(ShowingModes.All) End Sub @@ -665,7 +706,7 @@ CloseResume: End Using End Function #End Region -#Region "4 - view dates" +#Region "5 - view dates" Private Sub BTT_SHOW_LIMIT_DATES_NOT_IN_Click(ByVal Sender As ToolStripMenuItem, ByVal e As EventArgs) Handles BTT_SHOW_LIMIT_DATES_NOT.Click, BTT_SHOW_LIMIT_DATES_IN.Click Dim r As Boolean = False @@ -717,6 +758,15 @@ CloseResume: Private Sub BTT_DONATE_Click(sender As Object, e As EventArgs) Handles BTT_DONATE.Click Try : Process.Start("https://github.com/AAndyProgram/SCrawler/blob/main/HowToSupport.md") : Catch : End Try End Sub + Private Sub BTT_BUG_REPORT_Click(sender As Object, e As EventArgs) Handles BTT_BUG_REPORT.Click + Try + With Settings + Using f As New BugReporterForm(.Cache, .Design, .ProgramText, My.Application.Info.Version, + False, .Self, .ProgramDescription) : f.ShowDialog() : End Using + End With + Catch + End Try + End Sub #End Region #Region "List handlers" Private _LatestSelected As Integer = -1 @@ -753,6 +803,32 @@ CloseResume: Private Sub BTT_CONTEXT_DELETE_Click(sender As Object, e As EventArgs) Handles BTT_CONTEXT_DELETE.Click DeleteSelectedUser() End Sub + Private Sub BTT_CONTEXT_ERASE_Click(sender As Object, e As EventArgs) Handles BTT_CONTEXT_ERASE.Click + Const msgTitle$ = "Erase data" + Try + Dim users As List(Of IUserData) = GetSelectedUserArray() + If users.ListExists Then + Dim m As IUserData.EraseMode = UserDataBase.GetEraseMode(users) + If Not m = IUserData.EraseMode.None Then + Dim nd As New List(Of IUserData) + For Each user As IUserData In users + If Not user.EraseData(m) Then nd.Add(user) + Next + If nd.Count = 0 Then + MsgBoxE({"All user data has been erased.", msgTitle}) + Else + MsgBoxE(New MMessage("The data of the following users has not been erased:" & + vbCr.StringDup(2) & nd.ListToStringE(vbCr, GetUserListProvider(True)), msgTitle,, + MsgBoxStyle.Exclamation) With {.Editable = True}) + End If + End If + Else + MsgBoxE({"No user selected", msgTitle}, vbExclamation) + End If + Catch ex As Exception + ErrorsDescriber.Execute(EDP.LogMessageValue, ex, msgTitle) + End Try + End Sub Private Sub BTT_CONTEXT_COPY_TO_FOLDER_Click(sender As Object, e As EventArgs) Handles BTT_CONTEXT_COPY_TO_FOLDER.Click CopyUserData() End Sub @@ -784,19 +860,24 @@ CloseResume: End Sub) End If End Sub - Private Sub BTT_CONTEXT_GROUPS_Click(sender As Object, e As EventArgs) Handles BTT_CONTEXT_GROUPS.Click + Private Sub BTT_CONTEXT_GROUPS_Click(ByVal Sender As Object, ByVal e As Controls.KeyClick.KeyClickEventArgs) Handles BTT_CONTEXT_GROUPS.KeyClick Const MsgTitle$ = "Label change" Try Dim users As List(Of IUserData) = GetSelectedUserArray() If users.ListExists Then Dim l As List(Of String) = ListAddList(Nothing, users.SelectMany(Function(u) u.Labels), LAP.NotContainsOnly) + Dim lex As List(Of String) = ListAddList(Nothing, users.SelectMany(Function(u As UserDataBase) u.SpecialLabels), LNC) + Dim initialCount% = l.Count + Dim isOneUser As Boolean = users.Count = 1 AndAlso Not users(0).IsCollection + Dim inclSpec As Boolean = (e.Control And (users.Count > 1 Or (users.Count = 1 And users(0).IsCollection))) Or isOneUser + If Not inclSpec Then l.ListWithRemove(lex) Using f As New LabelsForm(l) With {.WithDeleteButton = l.Count > 0} f.ShowDialog() If f.DialogResult = DialogResult.OK Then Dim labels As List(Of String) = f.LabelsList - Dim lp As New ListAddParams(LAP.NotContainsOnly) - Dim a As Action(Of IUserData) = Sub(u) u.Labels.ListAddList(labels, lp) Dim cMsg As New MMessage("Operation canceled", MsgTitle) + Dim upMode As Byte + Dim keepSpecial As Boolean = True If labels.ListExists Then Select Case MsgBoxE(New MMessage($"What do you want to do with the selected labels?{vbCr}Selected labels:{vbCr}{labels.ListToString(vbCr)}", MsgTitle, @@ -806,27 +887,25 @@ CloseResume: New MsgBoxButton("Remove", "These labels will be removed from the existing ones"), "Cancel" }, vbExclamation) With {.ButtonsPerRow = 2}).Index - Case 0 : lp.ClearBeforeAdd = True - Case 1 : lp.ClearBeforeAdd = False - Case 2 : a = Sub(u) u.Labels.ListDisposeRemove(labels) + Case 0 : upMode = 1 + Case 1 : upMode = 0 + Case 2 : upMode = 2 Case Else : cMsg.Show() : Exit Sub End Select Else If MsgBoxE({"Are you sure you want to remove all labels?", MsgTitle}, vbExclamation + vbYesNo) = vbYes Then - a = Sub(u) u.Labels.Clear() + upMode = 1 Else cMsg.Show() Exit Sub End If End If + + If lex.ListExists AndAlso Not isOneUser AndAlso (l.ListContains(lex) Or (Not inclSpec And Not l.Count = initialCount)) Then _ + keepSpecial = UserDataBase.UpdateLabelsKeepSpecial(upMode) + users.ForEach(Sub(ByVal u As IUserData) - If u.IsCollection Then - With DirectCast(u, UserDataBind) - If .Count > 0 Then .Collections.ForEach(a) - End With - Else - a.Invoke(u) - End If + UserDataBase.UpdateLabels(u, labels, upMode, keepSpecial) u.UpdateUserInformation() End Sub) End If @@ -881,6 +960,8 @@ CloseResume: Dim _col_user As Predicate(Of IUserData) = Function(u) u.IsCollection Dim userCollection As UserDataBind = users.Find(_col_user) Dim _col_name$ = String.Empty + Dim _col_dest As SFile = Nothing + Dim allUsersIsSubscriptions As Boolean Dim userProvider As IFormatProvider = GetUserListProvider(False) If Not userCollection Is Nothing Then i = users.LongCount(Function(u) _col_user(u)) @@ -895,17 +976,23 @@ CloseResume: If _col_name.IsEmptyString Then Using f As New CollectionEditorForm f.ShowDialog() - If f.DialogResult = DialogResult.OK Then _col_name = f.Collection + If f.DialogResult = DialogResult.OK Then + _col_name = f.MyCollection + _col_dest = f.MyCollectionSpecialPath + End If End Using End If If _col_name.IsEmptyString Then MsgBoxE({"The destination collection has not been selected.", MsgTitle}, vbExclamation) Else + With (From u In users Where Not u.IsCollection Select u.IsSubscription) + allUsersIsSubscriptions = .ListExists AndAlso .All(Function(u) u) + End With With Settings userCollection = .Users.Find(Function(u) u.IsCollection And u.CollectionName = _col_name) Dim Added As Boolean = userCollection Is Nothing If Added Then - .Users.Add(New UserDataBind(_col_name)) + .Users.Add(New UserDataBind(_col_name, _col_dest)) MainFrameObj.CollectionHandler(DirectCast(.Users.Last, UserDataBind)) userCollection = .Users.Last End If @@ -915,10 +1002,18 @@ CloseResume: Dim __ModelAskForDecision As Boolean = False If Not Added Then __modelCollection = userCollection.CollectionModel If Added Then - __ModelAskForDecision = True + If allUsersIsSubscriptions Then + __modelUser = UsageModel.Virtual + __modelCollection = UsageModel.Virtual + Else + __ModelAskForDecision = True + End If ElseIf userCollection.CollectionModel = UsageModel.Virtual Then __modelUser = UsageModel.Virtual __modelCollection = UsageModel.Virtual + ElseIf allUsersIsSubscriptions Then + __modelCollection = userCollection.CollectionModel + __modelUser = UsageModel.Virtual Else __ModelAskForDecision = True End If @@ -966,7 +1061,11 @@ CloseResume: For Each user As UserDataBase In users If Not user.IsCollection Then Try - user.User.UserModel = IIf(user.HOST.Key = PathPlugin.PluginKey, UsageModel.Virtual, __modelUser) + If user.IsSubscription Then + user.User.UserModel = UsageModel.Virtual + Else + user.User.UserModel = IIf(user.HOST.Key = PathPlugin.PluginKey, UsageModel.Virtual, __modelUser) + End If user.User.CollectionModel = __modelCollection userCollection.Add(user) RemoveUserFromList(user) @@ -1027,23 +1126,84 @@ CloseResume: End If End Sub Private Sub BTT_CONTEXT_CHANGE_FOLDER_Click(sender As Object, e As EventArgs) Handles BTT_CONTEXT_CHANGE_FOLDER.Click + ChangeUserDestination(GetSelectedUserArray(), True) + End Sub + Private Function ChangeUserDestination(ByVal users As IEnumerable(Of IUserData), ByVal InitialInvoke As Boolean, + Optional ByVal NewUsersLocation As STDownloader.DownloadLocation? = Nothing) As Boolean Const MsgTitle$ = "Change user folder" + Dim automationPaused As Boolean = Not Settings.Automation.Pause = PauseModes.Disabled Try - If Downloader.Working Then - MsgBoxE({"Some users are currently downloading." & vbCr & - "You cannot change paths while downloading." & vbCr & - "Wait until the download is complete.", MsgTitle}, vbCritical) - Exit Sub - Else - Downloader.Suspended = True - End If - Dim users As List(Of IUserData) = GetSelectedUserArray() + Dim msgShowing As New ErrorsDescriber(If(InitialInvoke, EDP.ShowMainMsg, EDP.None)) If users.ListExists Then + If Downloader.Working Then + MsgBoxE({"Some users are currently downloading." & vbCr & + "You cannot change paths while downloading." & vbCr & + "Wait until the download is complete.", MsgTitle}, vbCritical) + Return False + Else + If InitialInvoke Then + Downloader.Suspended = True + If Not automationPaused Then Settings.Automation.Pause = PauseModes.Unlimited + End If + End If + + Dim locationChooser As GlobalLocationsChooserForm + Dim newLoc As STDownloader.DownloadLocation + If users.Count > 1 Then + Dim multiUserMsgTxt$ = "You have selected multiple users to change their destinations." & vbCr & + "It is highly recommended to change the destination for one user at a time." + If users.ListExists(Function(u) u.IsCollection And Not u.IsVirtual) Then _ + multiUserMsgTxt &= vbCr & vbCr & "A collection was also found in your selection." & vbCr & + "The collection movement model is always the only one, regardless of the path model you choose." + multiUserMsgTxt &= vbCr & vbCr & $"Selected users:{vbCr}{users.ListToStringE(vbCr, GetUserListProvider(True))}." + + Select Case MsgBoxE({multiUserMsgTxt, MsgTitle}, vbExclamation,,, + {New MsgBoxButton("Process", "Change the destination for all the users you selected"), + New MsgBoxButton("First only", "Process only the first user in the selection"), + "Cancel"}).Index + Case 0 + locationChooser = New GlobalLocationsChooserForm With { + .MyIsMultipleUsers = True, + .MyNonMyltipleUser = If(users.FirstOrDefault(Function(u) Not u.IsCollection), users(0)), + .MyIsCollectionSelector = users.All(Function(u) u.IsCollection) + } + With locationChooser + .ShowDialog() + If .DialogResult = DialogResult.OK Then + newLoc = .MyDestination + .Dispose() + Else + .Dispose() + ShowOperationCanceledMsg(MsgTitle) + Return False + End If + End With + With users.Where(Function(u) Not ChangeUserDestination({u}, False, newLoc)) + If .ListExists Then + If .Count = users.Count Then + MsgBoxE({"None of the users' destinations have been changed!", MsgTitle}, vbCritical) + Return False + Else + MsgBoxE({$"The following users' destinations have not been changed:{vbCr}" & + users.ListToStringE(vbCr, GetUserListProvider(True)), MsgTitle}, vbCritical) + Return True + End If + Else + MsgBoxE({"Users' data has been moved", MsgTitle}) + Return True + End If + End With + Case 1 : users = New List(Of IUserData) From {users.First} + Case Else : ShowOperationCanceledMsg(MsgTitle) : Return False + End Select + End If + If users.Count = 1 Then Dim CutOption% = 1 Dim _IsCollection As Boolean = False Dim CurrDir As SFile Dim colName$ = String.Empty + Dim pathHandler As PathMoverHandler With users(0) If .IsCollection Then _IsCollection = True @@ -1054,50 +1214,74 @@ CloseResume: MsgBoxE({"This is a virtual collection." & vbCr & "The virtual collection path cannot be changed." & vbCr & "To change the paths of users included in a virtual collection, " & - "you must split the collection and then change the user paths.", MsgTitle}, vbCritical) - Exit Sub + "you must split the collection and then change the user paths.", MsgTitle}, vbCritical, msgShowing) + Return False Else CurrDir = .GetRealUserFile If CurrDir.IsEmptyString Then - MsgBoxE({"Non-virtual users not found", MsgTitle}, vbCritical) - Exit Sub + MsgBoxE({"Non-virtual users not found", MsgTitle}, vbCritical, msgShowing) + Return False End If CurrDir = CurrDir.CutPath(IIf(.DataMerging, 3, 2)) - colName = CurrDir.Segments.LastOrDefault + colName = .CollectionName Dim vu As IEnumerable(Of IUserData) = .Where(Function(vuu) vuu.UserModel = UsageModel.Virtual Or vuu.HOST.Key = PathPlugin.PluginKey) If vu.ListExists Then - If MsgBoxE({"This collection contains virtual users and/or paths." & vbCr & - "If you continue, the virtual user paths will not be changed." & vbCr & - "The following users have been added to the collection in virtual mode:" & vbCr & - vu.ListToStringE(vbCr, GetUserListProvider(False)), MsgTitle}, - vbExclamation,,, {"Continue", "Cancel"}) = 1 Then MsgBoxE({"Operation canceled", MsgTitle}) : Exit Sub + If InitialInvoke AndAlso MsgBoxE({"This collection contains virtual users and/or paths." & vbCr & + "If you continue, the virtual user paths will not be changed." & vbCr & + "The following users have been added to the collection in virtual mode:" & vbCr & + vu.ListToStringE(vbCr, GetUserListProvider(False)), MsgTitle}, + vbExclamation,,, {"Continue", "Cancel"}) = 1 Then ShowOperationCanceledMsg(MsgTitle) : Return False End If End If End With ElseIf .HOST.Key = PathPlugin.PluginKey Then - MsgBoxE({"This is the path (not user). The paths cannot be changed.", MsgTitle}, vbCritical) - Exit Sub + MsgBoxE({"This is the path (not user). The paths cannot be changed.", MsgTitle}, vbCritical, msgShowing) + Return False Else CurrDir = .Self.File.CutPath(1) End If - Dim NewDest As SFile = SFile.SelectPath(CurrDir, $"Select a new destination for {IIf(_IsCollection, "collection", "user")} [{ .Self}]") - Dim NewDest2 As SFile + If NewUsersLocation.HasValue Then + newLoc = NewUsersLocation.Value + Else + locationChooser = New GlobalLocationsChooserForm With {.MyInitialLocation = CurrDir} + locationChooser.MyNonMyltipleUser = .Self() + If _IsCollection Then + locationChooser.MyIsCollectionSelector = True + locationChooser.MyCollectionName = colName + End If + With locationChooser + .ShowDialog() + If .DialogResult = DialogResult.OK Then + newLoc = .MyDestination + colName = .MyCollectionName + .Dispose() + Else + .Dispose() + If InitialInvoke Then ShowOperationCanceledMsg(MsgTitle) + Return False + End If + End With + End If + + If .IsCollection Then + pathHandler = GlobalLocationsChooserForm.ModelHandler(PathCreationModel.Collection) + Else + pathHandler = GlobalLocationsChooserForm.ModelHandler(newLoc.Model) + End If + + Dim NewDest As SFile + If .IsCollection Then + If Not InitialInvoke Then + NewDest = $"{newLoc.Path.CSFilePS}{SettingsCLS.CollectionsFolderName}\{ .CollectionName}\" + Else + NewDest = $"{newLoc.Path.CSFilePS}{ .CollectionName}\" + End If + Else + NewDest = pathHandler.Invoke(DirectCast(.Self, UserDataBase).User, newLoc.Path.CSFileP) + End If If Not NewDest.IsEmptyString Then - NewDest = $"{NewDest.PathWithSeparator}{colName}\" - NewDest2 = $"{NewDest.PathWithSeparator}{CurrDir.Segments.LastOrDefault().StringAppend("\", String.Empty)}" - Dim choice% = MsgBoxE(New MMessage($"You are changing the user's [{ .Self}] destination" & vbCr & - $"Current destination: {CurrDir.PathNoSeparator}" & vbCr & - $"New destination [1]: {NewDest.PathNoSeparator}" & vbCr & - $"New destination [2]: {NewDest2.PathWithSeparator}", - MsgTitle, - {New MsgBoxButton("Confirm [1] (Enter)", "Move the data to the destination [1]."), - New MsgBoxButton("Confirm [2]", "Move the data to the destination [2].") With {.KeyCode = Keys.D2}, - "Cancel"}, - MsgBoxStyle.Exclamation) With {.AppendKeyCode = False}) - If choice < 2 Then - If choice = 1 Then NewDest = NewDest2 - If Not NewDest.IsEmptyString AndAlso + If Not NewDest.IsEmptyString AndAlso (Not NewDest.Exists(SFO.Path, False) OrElse ( SFile.GetFiles(NewDest,, IO.SearchOption.AllDirectories, EDP.ThrowException).ListIfNothing.Count = 0 AndAlso @@ -1105,54 +1289,92 @@ CloseResume: Not NewDest.Exists(SFO.Path, False) ) ) Then - If SFile.Move(CurrDir, NewDest, SFO.Path,,, EDP.ShowMainMsg + EDP.ReturnValue) Then - Dim ApplyChanges As Action(Of IUserData) = Sub(ByVal __user As IUserData) - With DirectCast(__user, UserDataBase) - Dim u As UserInfo = .User - Settings.UsersList.Remove(u) - If _IsCollection Then + If SFile.Move(CurrDir, NewDest, SFO.Path,,, EDP.ReturnValue + If(InitialInvoke, EDP.ShowMainMsg, 0)) Then + Dim colRootDef As SFile = Settings.CollectionsPathF + Dim __UserSpecialPathsEquals As Func(Of UserInfo, Boolean, Boolean) = + Function(ByVal __user As UserInfo, ByVal __isCol As Boolean) As Boolean + Dim u1 As UserInfo = __user + Dim u2 As UserInfo = __user + If __isCol Then + u1.CollectionName = colName + u1.SpecialPath = Nothing + u1.SpecialCollectionPath = Nothing + u2.CollectionName = colName + u2.SpecialPath = Nothing + u2.SpecialCollectionPath = NewDest + Else + u1.CollectionName = String.Empty + u1.SpecialPath = Nothing + u1.SpecialCollectionPath = Nothing + u2.CollectionName = String.Empty + u2.SpecialPath = NewDest + u2.SpecialCollectionPath = Nothing + End If + u1.UpdateUserFile() + u2.UpdateUserFile() + Return u1.File = u2.File + End Function + Dim ApplyChanges As Action(Of IUserData) = Sub(ByVal __user As IUserData) + With DirectCast(__user, UserDataBase) + Dim u As UserInfo = .User + Settings.UsersList.Remove(u) + If _IsCollection Then + u.CollectionName = colName + If Not __UserSpecialPathsEquals(u, True) Then u.SpecialCollectionPath = NewDest Else - u.SpecialPath = NewDest + u.SpecialCollectionPath = Nothing End If - u.UpdateUserFile() - Settings.UsersList.Add(u) - .User = u - .UpdateUserInformation() - End With - End Sub - If .Self.IsCollection Then - With DirectCast(.Self, UserDataBind) - For Each user In .Collections : ApplyChanges(user) : Next - End With - Else - ApplyChanges(.Self) - End If - Settings.UpdateUsersList() - MsgBoxE({"User data has been moved", MsgTitle}) + u.SpecialPath = Nothing + Else + u.CollectionName = String.Empty + If Not __UserSpecialPathsEquals(u, False) Then + u.SpecialPath = NewDest + Else + u.SpecialPath = Nothing + End If + u.SpecialCollectionPath = Nothing + End If + u.UpdateUserFile() + Settings.UsersList.Add(u) + .User = u + .UpdateUserInformation() + End With + End Sub + If .IsCollection Then + With DirectCast(.Self, UserDataBind) + For Each user In .Collections : ApplyChanges(user) : Next + End With + Else + ApplyChanges(.Self) End If - Else - MsgBoxE({$"Unable to move user data to new destination [{NewDest}]{vbCr}Operation canceled", MsgTitle}, MsgBoxStyle.Critical) + Settings.UpdateUsersList() + MsgBoxE({"User data has been moved", MsgTitle},, msgShowing) + Return True End If Else - MsgBoxE({"Operation canceled", MsgTitle}) + MsgBoxE({$"Unable to move user data to new destination [{NewDest}]{vbCr}Operation canceled", MsgTitle}, MsgBoxStyle.Critical, msgShowing) End If Else - MsgBoxE({$"You have not entered a new destination{vbCr}Operation canceled", MsgTitle}, MsgBoxStyle.Exclamation) + MsgBoxE({$"You have not entered a new destination{vbCr}Operation canceled", MsgTitle}, MsgBoxStyle.Exclamation, msgShowing) End If End With Else - MsgBoxE({"You have selected multiple users. You can change the folder only for one user!", MsgTitle}, MsgBoxStyle.Critical) + MsgBoxE({"You have selected multiple users. You can change the folder only for one user!", MsgTitle}, MsgBoxStyle.Critical, msgShowing) End If Else - MsgBoxE({"No one user selected", MsgTitle}, MsgBoxStyle.Exclamation) + MsgBoxE({"No one user selected", MsgTitle}, MsgBoxStyle.Exclamation, msgShowing) End If + Return False Catch ex As Exception - ErrorsDescriber.Execute(EDP.ShowAllMsg, ex, "Error while moving user") + Return ErrorsDescriber.Execute(EDP.ReturnValue + If(InitialInvoke, EDP.ShowAllMsg, EDP.SendToLog), ex, "Error while moving user", False) Finally - Downloader.Suspended = False + If InitialInvoke Then + Downloader.Suspended = False + If Not automationPaused Then Settings.Automation.Pause = PauseModes.Disabled + End If End Try - End Sub + End Function #End Region #Region "3 - change image" Private Sub BTT_CHANGE_IMAGE_Click(sender As Object, e As EventArgs) Handles BTT_CHANGE_IMAGE.Click @@ -1193,6 +1415,7 @@ CloseResume: BTT_CONTEXT_DOWN.DropDownItems.AddRange(.ContextDown) BTT_CONTEXT_EDIT.DropDownItems.AddRange(.ContextEdit) BTT_CONTEXT_DELETE.DropDownItems.AddRange(.ContextDelete) + BTT_CONTEXT_ERASE.DropDownItems.AddRange(.ContextErase) BTT_CONTEXT_OPEN_PATH.DropDownItems.AddRange(.ContextPath) BTT_CONTEXT_OPEN_SITE.DropDownItems.AddRange(.ContextSite) End With @@ -1201,6 +1424,7 @@ CloseResume: BTT_CONTEXT_DOWN.DropDownItems.Clear() BTT_CONTEXT_EDIT.DropDownItems.Clear() BTT_CONTEXT_DELETE.DropDownItems.Clear() + BTT_CONTEXT_ERASE.DropDownItems.Clear() BTT_CONTEXT_OPEN_PATH.DropDownItems.Clear() BTT_CONTEXT_OPEN_SITE.DropDownItems.Clear() End If @@ -1248,7 +1472,7 @@ CloseResume: #Region "Operation providers" Private OperationsUserListProvider As IFormatProvider = Nothing Private OperationsUserListProviderCollections As IFormatProvider = Nothing - Private Function GetUserListProvider(ByVal WithCollections As Boolean) As IFormatProvider + Friend Function GetUserListProvider(ByVal WithCollections As Boolean) As IFormatProvider If WithCollections Then If OperationsUserListProviderCollections Is Nothing Then _ OperationsUserListProviderCollections = New CustomProvider(Function(v, d, p, n, ee) @@ -1393,46 +1617,46 @@ ResumeDownloadingOperation: f.ShowDialog() If f.DialogResult = DialogResult.OK Then Dim NeedToUpdate As Boolean = True - If user.IsCollection Then - If user.IsCollection And Not user.CollectionName = f.CollectionName Then - If Not user.IsVirtual AndAlso Downloader.Working Then - MsgBoxE({"Some users are currently downloading." & vbCr & + If user.IsCollection And Not user.CollectionName = f.CollectionName Then + If Not user.IsVirtual AndAlso Downloader.Working Then + MsgBoxE({"Some users are currently downloading." & vbCr & "You cannot change collection name while downloading." & vbCr & "Wait until the download is complete.", MsgTitle}, vbCritical) - Exit Sub - Else - If Not user.IsVirtual Then - Dim colFile As SFile = DirectCast(user, UserDataBind).GetRealUserFile - If Not colFile.IsEmptyString Then - colFile = colFile.CutPath(IIf(DirectCast(user, UserDataBind).DataMerging, 1, 2)) - If Not colFile.IsEmptyString Then - Dim nf As SFile = $"{colFile.CutPath(1).PathWithSeparator}{f.CollectionName}".CSFilePS - If Not SFile.Rename(colFile, New SFile With {.Path = f.CollectionName}, SFO.Path, - New ErrorsDescriber(True, False, False, New SFile)).IsEmptyString Then - RemoveUserFromList(user) - Dim __user As UserInfo - For Each ColUser As UserDataBase In DirectCast(user, UserDataBind).Collections - __user = ColUser.User - Settings.UsersList.Remove(__user) - __user.CollectionName = f.CollectionName - If Not __user.SpecialCollectionPath.IsEmptyString Then __user.SpecialCollectionPath = nf - __user.UpdateUserFile() - ColUser.User = __user - Settings.UsersList.Add(__user) - Next - user.UpdateUserInformation() - UserListUpdate(user, True) - NeedToUpdate = False - End If + Exit Sub + Else + If Not user.IsVirtual Then + Dim rUser As UserDataBase = DirectCast(user, UserDataBind).GetRealUser + If Not rUser Is Nothing Then + Dim colPathCurr As SFile = rUser.User.GetCollectionRootPath + Dim colPathNew As SFile = SFile.GetPath(colPathCurr.CutPath.PathWithSeparator & f.CollectionName) + If Not colPathCurr.Exists(SFO.Path, False) Then + MsgBoxE({"Original location of collection not found. Operation canceled.", MsgTitle}, vbCritical) + ElseIf colPathNew.Exists(SFO.Path, False) Then + MsgBoxE({"The new collection location already exists. Operation canceled.", MsgTitle}, vbCritical) + Else + If Not SFile.Rename(colPathCurr, colPathNew, SFO.Path, New ErrorsDescriber(True, False, False, New SFile)).IsEmptyString Then + RemoveUserFromList(user) + Dim __user As UserInfo + For Each ColUser As UserDataBase In DirectCast(user, UserDataBind).Collections + __user = ColUser.User + Settings.UsersList.Remove(__user) + __user.CollectionName = f.CollectionName + If Not __user.SpecialCollectionPath.IsEmptyString Then __user.SpecialCollectionPath = colPathNew + __user.UpdateUserFile() + ColUser.User = __user + Settings.UsersList.Add(__user) + Next + user.UpdateUserInformation() + UserListUpdate(user, True) + NeedToUpdate = False End If End If - Else - RemoveUserFromList(user) - user.CollectionName = f.CollectionName - user.UpdateUserInformation() - UserListUpdate(user, True) - NeedToUpdate = False End If + Else + RemoveUserFromList(user) + DirectCast(user, UserDataBind).ChangeVirtualCollectionName(f.CollectionName) + UserListUpdate(user, True) + NeedToUpdate = False End If End If End If diff --git a/SCrawler/MainFrameObjects.vb b/SCrawler/MainFrameObjects.vb index 7c6c7ce..6b4ffa7 100644 --- a/SCrawler/MainFrameObjects.vb +++ b/SCrawler/MainFrameObjects.vb @@ -62,6 +62,9 @@ Friend Class MainFrameObjects : Implements INotificator Friend Sub UpdateLogButton() MyMainLOG_UpdateLogButton(MF.BTT_LOG, MF.Toolbar_TOP) End Sub + Friend Function GetUserListProvider(ByVal WithCollections As Boolean) As IFormatProvider + Return MF.GetUserListProvider(WithCollections) + End Function #End Region #Region "Notifications" Private Sub INotificator_ShowNotification(ByVal Text As String, ByVal Image As SFile) Implements INotificator.ShowNotification diff --git a/SCrawler/MainMod.vb b/SCrawler/MainMod.vb index 875cc00..0729dbf 100644 --- a/SCrawler/MainMod.vb +++ b/SCrawler/MainMod.vb @@ -9,9 +9,7 @@ Imports System.Runtime.CompilerServices Imports PersonalUtilities.Functions.XML.Objects Imports PersonalUtilities.Functions.RegularExpressions -Imports PersonalUtilities.Forms.Toolbars Imports PersonalUtilities.Tools -Imports PersonalUtilities.Tools.Web Imports SCrawler.API.Base Imports SCrawler.Plugin.Hosts Imports SCrawler.DownloadObjects @@ -21,6 +19,8 @@ Friend Module MainMod 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 Sender As SettingsCLS.NotificationObjects, ByVal Message As String) + Friend Delegate Sub UserDownloadStateChangedEventHandler(ByVal User As IUserData, ByVal IsDownloading As Boolean) + Friend Delegate Function PathMoverHandler(ByVal User As UserInfo, ByVal DestinationPattern As SFile) As SFile Friend Const LVI_TempOption As String = "Temp" Friend Const LVI_FavOption As String = "Favorite" Friend Const LVI_CollectionOption As String = "Collection" @@ -68,6 +68,14 @@ Friend Module MainMod [Default] = 0 Virtual = 1 End Enum + Friend Enum PathCreationModel As Integer + Undefined = -1 + Path = 1 + Path_UserName = 2 + Path_UserSite_UserName = 3 + DefaultUser = Path_UserSite_UserName + Collection = 4 + End Enum Friend Downloader As TDownloader Friend InfoForm As DownloadedInfoForm Friend VideoDownloader As STDownloader.VideoDownloaderForm @@ -77,6 +85,8 @@ Friend Module MainMod Friend ReadOnly DateTimeDefaultProvider As New ADateTime(ADateTime.Formats.BaseDateTime) Friend ReadOnly FeedVideoLengthProvider As New ADateTime("hh\:mm\:ss") With {.TimeParseMode = ADateTime.TimeModes.TimeSpan} Friend ReadOnly UserExistsPredicate As New FPredicate(Of IUserData)(Function(u) u.Exists) + Friend ReadOnly UserExistsSubscriptionsPredicate As New FPredicate(Of IUserData)(Function(u) u.Exists And u.IsSubscription) + Friend ReadOnly UserExistsNonSubscriptionsPredicate As New FPredicate(Of IUserData)(Function(u) u.Exists And Not u.IsSubscription) Friend ReadOnly LogConnector As New LogHost Friend DefaultUserAgent As String = String.Empty #Region "File name operations" @@ -91,6 +101,11 @@ Friend Module MainMod End Class #End Region Friend Property MainProgress As MyProgressExt + Friend Sub ShowOperationCanceledMsg(Optional ByVal MsgTitle As String = Nothing) + Dim m As New MMessage("Operation canceled") + If Not MsgTitle.IsEmptyString Then m.Title = MsgTitle + m.Show() + End Sub Friend Function GetLviGroupName(ByVal Host As SettingsHost, ByVal IsCollection As Boolean) As ListViewGroup() Dim l As New List(Of ListViewGroup) Dim t$ @@ -125,8 +140,11 @@ Friend Module MainMod End If End Function Friend Sub CheckVersion(ByVal Force As Boolean) - If Settings.CheckUpdatesAtStart Or Force Then _ - GitHub.DefaultVersionChecker(My.Application.Info.Version, "AAndyProgram", "SCrawler", - Settings.LatestVersion.Value, Settings.ShowNewVersionNotification.Value, Force) + With Settings + If .CheckUpdatesAtStart Or Force Then + ShowProgramInfo(.ProgramText.Value.IfNullOrEmpty("SCrawler"), My.Application.Info.Version, True, Force, .Self, False, + .LatestVersion.Value, .ShowNewVersionNotification.Value, .ProgramDescription) + End If + End With End Sub End Module \ No newline at end of file diff --git a/SCrawler/My Project/AssemblyInfo.vb b/SCrawler/My Project/AssemblyInfo.vb index a6c5e50..835ef06 100644 --- a/SCrawler/My Project/AssemblyInfo.vb +++ b/SCrawler/My Project/AssemblyInfo.vb @@ -32,6 +32,6 @@ Imports System.Runtime.InteropServices ' by using the '*' as shown below: ' - - + + diff --git a/SCrawler/My Project/Resources.Designer.vb b/SCrawler/My Project/Resources.Designer.vb index 0953ba6..60a588e 100644 --- a/SCrawler/My Project/Resources.Designer.vb +++ b/SCrawler/My Project/Resources.Designer.vb @@ -110,6 +110,16 @@ Namespace My.Resources End Get End Property + ''' + ''' Looks up a localized resource of type System.Drawing.Bitmap. + ''' + Friend ReadOnly Property BrushToolPic_16() As System.Drawing.Bitmap + Get + Dim obj As Object = ResourceManager.GetObject("BrushToolPic_16", resourceCulture) + Return CType(obj,System.Drawing.Bitmap) + End Get + End Property + ''' ''' Looks up a localized resource of type System.Drawing.Bitmap. ''' diff --git a/SCrawler/My Project/Resources.resx b/SCrawler/My Project/Resources.resx index 6e7f93e..a3b77c7 100644 --- a/SCrawler/My Project/Resources.resx +++ b/SCrawler/My Project/Resources.resx @@ -217,4 +217,7 @@ ..\Content\Pictures\TagPic_24.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + ..\Content\Pictures\BrushToolPic_16.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + \ No newline at end of file diff --git a/SCrawler/PluginsEnvironment/Hosts/DownloadableMediaHost.vb b/SCrawler/PluginsEnvironment/Hosts/DownloadableMediaHost.vb index 778393a..0f57803 100644 --- a/SCrawler/PluginsEnvironment/Hosts/DownloadableMediaHost.vb +++ b/SCrawler/PluginsEnvironment/Hosts/DownloadableMediaHost.vb @@ -145,11 +145,13 @@ Namespace Plugin.Hosts f.Extension = "xml" FileSettings = f End If - Using x As New XmlFile With {.AllowSameNames = True} - x.AddRange(ToEContainer.Elements) - x.Name = "MediaContainer" - x.Save(FileSettings) - End Using + If NeedToSave() Then + Using x As New XmlFile With {.AllowSameNames = True} + x.AddRange(ToEContainer.Elements) + x.Name = "MediaContainer" + x.Save(FileSettings) + End Using + End If End Sub Public Overrides Function GetHashCode() As Integer Return URL.GetHashCode diff --git a/SCrawler/PluginsEnvironment/Hosts/PropertyValueHost.vb b/SCrawler/PluginsEnvironment/Hosts/PropertyValueHost.vb index cff7a9e..286e2ba 100644 --- a/SCrawler/PluginsEnvironment/Hosts/PropertyValueHost.vb +++ b/SCrawler/PluginsEnvironment/Hosts/PropertyValueHost.vb @@ -16,9 +16,15 @@ Imports PersonalUtilities.Forms.Controls.Base Imports ADB = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons Namespace Plugin.Hosts Friend Class PropertyValueHost : Implements IPropertyValue, IComparable(Of PropertyValueHost) - Friend Const LeftOffsetDefault As Integer = 100 - Friend Event OnPropertyUpdateRequested(ByVal Sender As PropertyValueHost) +#Region "Events" Private Event ValueChanged As IPropertyValue.ValueChangedEventHandler Implements IPropertyValue.ValueChanged +#End Region +#Region "Declarations" + Private ReadOnly Keeper As SettingsHost + Protected Source As Object 'ReadOnly + Protected Member As MemberInfo + Friend ReadOnly Options As PropertyOption + Friend Overridable ReadOnly Property Name As String Protected _Type As Type Friend Overridable Property [Type] As Type Implements IPropertyValue.Type Get @@ -28,6 +34,11 @@ Namespace Plugin.Hosts _Type = t End Set End Property + Friend ReadOnly IsTaskCounter As Boolean + Friend ReadOnly Exists As Boolean = False +#Region "XML" + Private ReadOnly _XmlName As String +#End Region #Region "Control" Friend Property Control As Control Protected ControlNumber As Integer = -1 @@ -40,6 +51,20 @@ Namespace Plugin.Hosts End If End Get End Property + Friend Const LeftOffsetDefault As Integer = 100 + Protected _LeftOffset As Integer? = Nothing + Friend Overridable Property LeftOffset As Integer + Get + If _LeftOffset.HasValue Then + Return _LeftOffset + Else + Return If(Options?.LeftOffset, LeftOffsetDefault) + End If + End Get + Set(ByVal NewOffset As Integer) + _LeftOffset = NewOffset + End Set + End Property Protected Overridable ReadOnly Property Control_ThreeStates As Boolean Get Return Options.ThreeStates @@ -91,7 +116,11 @@ Namespace Plugin.Hosts With DirectCast(Control, TextBoxExtended) .CaptionText = Control_Caption .CaptionToolTipEnabled = Not Control_ToolTip.IsEmptyString - .CaptionWidth = LeftOffset + If LeftOffset > 0 Then + .CaptionWidth = LeftOffset + Else + Using l As New Label : .CaptionWidth = .CaptionText.MeasureTextDefault(l.Font).Width : End Using + End If If Not Control_ToolTip.IsEmptyString Then .CaptionToolTipText = Control_ToolTip : .CaptionToolTipEnabled = True .Text = CStr(AConvert(Of String)(Value, String.Empty)) With .Buttons @@ -114,9 +143,19 @@ Namespace Plugin.Hosts Private Sub TextBoxClick(ByVal Sender As ActionButton, ByVal e As EventArgs) Try If Sender.DefaultButton = ADB.Refresh AndAlso Not Source Is Nothing AndAlso Not UpdateMethod Is Nothing Then - If CBool(UpdateMethod.Invoke(Source, Nothing)) Then - RaiseEvent OnPropertyUpdateRequested(Me) + Dim args As Object = Nothing + Dim i% + If UpdateMethodArguments.ListExists Then + Dim a As New List(Of String) + For Each arg$ In UpdateMethodArguments + i = Keeper.PropList.FindIndex(Function(p) Not p Is Me And p.Name = arg) + If i >= 0 Then a.Add(AConvert(Of String)(Keeper.PropList(i).GetControlValue(), String.Empty)) Else a.Add(String.Empty) + Next + If a.Count > 0 Then args = a.ToArray + End If + If CBool(UpdateMethod.Invoke(Source, args)) Then DirectCast(Control, TextBoxExtended).Text = CStr(AConvert(Of String)(Value, String.Empty)) + If Dependents.Count > 0 Then Dependents.ForEach(Sub(d) d.UpdateDependence()) End If End If Catch ex As Exception @@ -158,29 +197,7 @@ Namespace Plugin.Hosts Return Nothing End If End Function - Private Sub UpdateProviderPropertyName() - If ProviderValueIsPropertyProvider Then DirectCast(ProviderValue, IPropertyProvider).PropertyName = Name - End Sub #End Region -#Region "Compatibility" - Protected Source As Object 'ReadOnly - Protected Member As MemberInfo - Friend Overridable ReadOnly Property Name As String - Private ReadOnly _XmlName As String - Friend ReadOnly Options As PropertyOption - Protected _LeftOffset As Integer? = Nothing - Friend Overridable Property LeftOffset As Integer - Get - If _LeftOffset.HasValue Then - Return _LeftOffset - Else - Return If(Options?.LeftOffset, LeftOffsetDefault) - End If - End Get - Set(ByVal NewOffset As Integer) - _LeftOffset = NewOffset - End Set - End Property #Region "Providers" Friend Property ProviderFieldsChecker As IFormatProvider Private Property ProviderValue As IFormatProvider @@ -195,27 +212,37 @@ Namespace Plugin.Hosts ProviderValueInteraction = Instance.Interaction End If End Sub + Private Sub UpdateProviderPropertyName() + If ProviderValueIsPropertyProvider Then DirectCast(ProviderValue, IPropertyProvider).PropertyName = Name + End Sub #End Region +#Region "Updaters, Checkers" Friend PropertiesChecking As String() Friend PropertiesCheckingMethod As MethodInfo Private UpdateMethod As MethodInfo - Private _UpdateDependencies As String() = Nothing - Friend ReadOnly Property UpdateDependencies As String() - Get - Return _UpdateDependencies - End Get - End Property - Friend Sub SetUpdateMethod(ByVal m As MethodInfo, ByVal Dependencies As String()) + Private UpdateMethodArguments As String() + Friend Sub SetUpdateMethod(ByVal m As MethodInfo, ByVal _UpdateMethodArguments As String()) UpdateMethod = m - _UpdateDependencies = Dependencies + UpdateMethodArguments = _UpdateMethodArguments End Sub - Friend ReadOnly IsTaskCounter As Boolean #End Region - Friend ReadOnly Exists As Boolean = False +#Region "Dependents" + Private ReadOnly DependentNames As New List(Of String) + Private ReadOnly Dependents As New List(Of PropertyValueHost) + Private Sub UpdateDependence() + If TypeOf Control Is CheckBox Then + DirectCast(Control, CheckBox).Checked = CBool(AConvert(Of Boolean)(Value, False)) + Else + DirectCast(Control, TextBoxExtended).Text = CStr(AConvert(Of String)(Value, String.Empty)) + End If + End Sub +#End Region +#End Region #Region "Initializer" Protected Sub New() End Sub - Friend Sub New(ByRef PropertySource As Object, ByVal Member As MemberInfo) + Friend Sub New(ByRef Keeper As SettingsHost, ByRef PropertySource As Object, ByVal Member As MemberInfo) + Me.Keeper = Keeper Source = PropertySource Name = Member.Name @@ -229,6 +256,7 @@ Namespace Plugin.Hosts IsTaskCounter = Not Member.GetCustomAttribute(Of TaskCounter)() Is Nothing _XmlName = If(Member.GetCustomAttribute(Of PXML)()?.ElementName, String.Empty) If Not _XmlName.IsEmptyString Then XValue = CreateXMLValueInstance([Type], True) + DependentNames.ListAddList(Member.GetCustomAttribute(Of DependentFields)?.Fields, LAP.NotContainsOnly) Exists = True End If End Sub @@ -239,6 +267,13 @@ Namespace Plugin.Hosts Value(False) = XValue.Value End If End Sub + Friend Sub SetDependents(ByVal Props As List(Of PropertyValueHost)) + If DependentNames.Count > 0 And Props.Count > 0 Then + For Each prop As PropertyValueHost In Props + If DependentNames.Contains(prop.Name) Then Dependents.Add(prop) + Next + End If + End Sub #End Region #Region "Value" Protected ReadOnly Property ExternalValue As PropertyValue diff --git a/SCrawler/PluginsEnvironment/Hosts/SettingsHost.vb b/SCrawler/PluginsEnvironment/Hosts/SettingsHost.vb index 9ab651c..d40d84e 100644 --- a/SCrawler/PluginsEnvironment/Hosts/SettingsHost.vb +++ b/SCrawler/PluginsEnvironment/Hosts/SettingsHost.vb @@ -131,17 +131,6 @@ Namespace Plugin.Hosts End Set End Property Friend ReadOnly Property GetUserMediaOnly As XMLValue(Of Boolean) -#End Region -#Region "Host internal functions" - Private Sub PropHost_OnPropertyUpdateRequested(ByVal Sender As PropertyValueHost) - If Sender.UpdateDependencies.ListExists Then - Settings.BeginUpdate() - For Each p As PropertyValueHost In PropList - If Sender.UpdateDependencies.Contains(p.Name) Then p.UpdateValueByControl() - Next - Settings.EndUpdate() - End If - End Sub #End Region Friend Sub New(ByVal Plugin As ISiteSettings, ByRef _XML As XmlFile, ByVal GlobalPath As SFile, ByRef _Temp As XMLValue(Of Boolean), ByRef _Imgs As XMLValue(Of Boolean), ByRef _Vids As XMLValue(Of Boolean)) @@ -188,10 +177,11 @@ Namespace Plugin.Hosts Dim Updaters As New List(Of MemberInfo) Dim Providers As New List(Of MemberInfo) Dim PropCheckers As New List(Of MemberInfo) + Dim m As MemberInfo For Each m In Members If m.MemberType = MemberTypes.Property Then - PropList.Add(New PropertyValueHost(Source, m)) + PropList.Add(New PropertyValueHost(Me, Source, m)) With DirectCast(m, PropertyInfo) If .PropertyType Is GetType(Responser) AndAlso m.GetCustomAttribute(Of DoNotUse)() Is Nothing Then _ResponserGetMethod = .GetMethod End With @@ -218,7 +208,7 @@ Namespace Plugin.Hosts For Each m In Updaters up = m.GetCustomAttribute(Of PropertyUpdater)() i = PropList.FindIndex(Function(p) p.Name = up.Name) - If i >= 0 Then PropList(i).SetUpdateMethod(DirectCast(m, MethodInfo), up.Dependencies) + If i >= 0 Then PropList(i).SetUpdateMethod(DirectCast(m, MethodInfo), up.Arguments) Next Updaters.Clear() End If @@ -252,6 +242,7 @@ Namespace Plugin.Hosts Next PropCheckers.Clear() End If + PropList.ForEach(Sub(p) p.SetDependents(PropList)) End If _Path = New XMLValue(Of SFile)("Path",, _XML, n, New XMLToFilePathProvider) @@ -277,7 +268,6 @@ Namespace Plugin.Hosts For Each p As PropertyValueHost In PropList p.SetXmlEnvironment(_XML, n) p.LeftOffset = MaxOffset - AddHandler p.OnPropertyUpdateRequested, AddressOf PropHost_OnPropertyUpdateRequested Next End If diff --git a/SCrawler/PluginsEnvironment/Hosts/UserDataHost.vb b/SCrawler/PluginsEnvironment/Hosts/UserDataHost.vb index a5ba1fa..f051bdc 100644 --- a/SCrawler/PluginsEnvironment/Hosts/UserDataHost.vb +++ b/SCrawler/PluginsEnvironment/Hosts/UserDataHost.vb @@ -48,11 +48,13 @@ Namespace Plugin.Hosts .LogProvider = LogConnector .Name = Name .ID = ID + .Options = Options .ParseUserMediaOnly = ParseUserMediaOnly .UserDescription = UserDescription .UserExists = .UserExists .UserSuspended = UserSuspended .IsSavedPosts = IsSavedPosts + .IsSubscription = IsSubscription .SeparateVideoFolder = SeparateVideoFolderF .DataPath = MyFile.CutPath.PathNoSeparator .PostsNumberLimit = DownloadTopCount diff --git a/SCrawler/SCrawler.vbproj b/SCrawler/SCrawler.vbproj index ba8e50d..c983650 100644 --- a/SCrawler/SCrawler.vbproj +++ b/SCrawler/SCrawler.vbproj @@ -113,11 +113,11 @@ My Project\app.manifest - - ..\packages\LibVLCSharp.3.6.6\lib\net40\LibVLCSharp.dll + + ..\packages\LibVLCSharp.3.7.0\lib\net40\LibVLCSharp.dll - - ..\packages\LibVLCSharp.WinForms.3.6.6\lib\net40\LibVLCSharp.WinForms.dll + + ..\packages\LibVLCSharp.WinForms.3.7.0\lib\net40\LibVLCSharp.WinForms.dll ..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll @@ -166,12 +166,15 @@ Form + + + @@ -191,6 +194,7 @@ + @@ -202,6 +206,7 @@ + RedditViewSettingsForm.vb @@ -216,15 +221,18 @@ + + + @@ -307,6 +315,12 @@ Form + + UserDownloadQueueForm.vb + + + Form + ColorPicker.vb @@ -314,6 +328,12 @@ UserControl + + GlobalLocationsChooserForm.vb + + + Form + UsersInfoForm.vb @@ -514,12 +534,18 @@ VideoDownloaderForm.vb + + UserDownloadQueueForm.vb + CollectionEditorForm.vb ColorPicker.vb + + GlobalLocationsChooserForm.vb + GlobalSettingsForm.vb @@ -618,7 +644,6 @@ - @@ -670,14 +695,17 @@ + + + - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - + + \ No newline at end of file diff --git a/SCrawler/SettingsCLS.vb b/SCrawler/SettingsCLS.vb index baddf2e..65d6663 100644 --- a/SCrawler/SettingsCLS.vb +++ b/SCrawler/SettingsCLS.vb @@ -26,6 +26,7 @@ Friend Class SettingsCLS : Implements IDownloaderSettings, IDisposable Private Const SitesValuesSeparator As String = "," Friend Const CookieEncryptKey As String = "SCrawlerCookiesEncryptKeyword" Private Const EnvironmentPath As String = "Environment\" + Friend Const CollectionsFolderName As String = "Collections" Friend Const DefaultCmdEncoding As Integer = BatchExecutor.UnicodeEncoding Friend ReadOnly Design As XmlFile Private ReadOnly MyXML As XmlFile @@ -83,8 +84,41 @@ Friend Class SettingsCLS : Implements IDownloaderSettings, IDisposable Friend ReadOnly Property YtdlpFile As ProgramFile Friend ReadOnly Property GalleryDLFile As ProgramFile Friend ReadOnly Property CurlFile As ProgramFile + Private ReadOnly Property ENVIR_FFMPEG As SFile Implements IDownloaderSettings.ENVIR_FFMPEG + Get + Return FfmpegFile + End Get + End Property + Private ReadOnly Property ENVIR_YTDLP As SFile Implements IDownloaderSettings.ENVIR_YTDLP + Get + Return YtdlpFile + End Get + End Property + Private ReadOnly Property ENVIR_GDL As SFile Implements IDownloaderSettings.ENVIR_GDL + Get + Return GalleryDLFile + End Get + End Property + Private ReadOnly Property ENVIR_CURL As SFile Implements IDownloaderSettings.ENVIR_CURL + Get + Return CurlFile + End Get + End Property #End Region Friend ReadOnly Property Cache As CacheKeeper + Private _CacheSnapshots As CacheKeeper = Nothing + Friend ReadOnly Property CacheSnapshots(ByVal Permanent As Boolean) As CacheKeeper + Get + If Permanent Then + If _CacheSnapshots Is Nothing Then _CacheSnapshots = New CacheKeeper("_CacheSnapshots\") With {.DeleteCacheOnDispose = False, .DeleteRootOnDispose = False} + Return _CacheSnapshots + Else + Dim dir As SFile = $"{Cache.RootDirectory.PathWithSeparator}Snapshots\" + Cache.AddPath(dir) + Return Cache.GetInstance(dir) + End If + End Get + End Property Friend ReadOnly Plugins As List(Of PluginHost) Friend ReadOnly Property Users As List(Of IUserData) Friend ReadOnly Property UsersList As List(Of UserInfo) @@ -92,6 +126,8 @@ Friend Class SettingsCLS : Implements IDownloaderSettings, IDisposable Friend ReadOnly Property Labels As LabelsKeeper Friend ReadOnly Property Groups As Groups.DownloadGroupCollection Friend ReadOnly Property LastCollections As List(Of String) + Friend ReadOnly Property DownloadLocations As STDownloader.DownloadLocationsCollection + Friend ReadOnly Property GlobalLocations As STDownloader.DownloadLocationsCollection Friend Property Automation As Scheduler Friend ReadOnly Property BlackList As List(Of UserBan) Private ReadOnly BlackListFile As SFile = $"{SettingsFolderName}\BlackList.txt" @@ -108,6 +144,8 @@ Friend Class SettingsCLS : Implements IDownloaderSettings, IDisposable BlackList = New List(Of UserBan) Plugins = New List(Of PluginHost) LastCollections = New List(Of String) + GlobalLocations = New STDownloader.DownloadLocationsCollection + GlobalLocations.Load(True,, $"{SettingsFolderName}\GlobalLocations.xml") Dim n() As String = {"MediaEnvironment"} @@ -130,7 +168,7 @@ Friend Class SettingsCLS : Implements IDownloaderSettings, IDisposable LastCopyPath = New XMLValue(Of SFile)("LastCopyPath",, MyXML,, New XMLToFilePathProvider) SeparateVideoFolder = New XMLValue(Of Boolean)("SeparateVideoFolder", True, MyXML) - CollectionsPath = New XMLValue(Of String)("CollectionsPath", "Collections", MyXML) + CollectionsPath = New XMLValue(Of String)("CollectionsPath", CollectionsFolderName, MyXML) UserAgent = New XMLValue(Of String)("UserAgent",, MyXML) If Not UserAgent.IsEmptyString Then DefaultUserAgent = UserAgent @@ -162,6 +200,10 @@ Friend Class SettingsCLS : Implements IDownloaderSettings, IDisposable DefaultDownloadImages, DefaultDownloadVideos) If tmpPluginList.ListExists Then Plugins.AddRange(tmpPluginList) + MainFrameUsersShowDefaults = New XMLValue(Of Boolean)("UsersShowDefaults", True, MyXML) + MainFrameUsersShowSubscriptions = New XMLValue(Of Boolean)("UsersShowSubscriptions", True, MyXML) + MainFrameUsersSubscriptionsColorBack = New XMLValue(Of Color)("UsersSubscriptionsColorBack", MyColor.OkBack, MyXML) + MainFrameUsersSubscriptionsColorFore = New XMLValue(Of Color)("UsersSubscriptionsColorFore", MyColor.OkFore, MyXML) FastProfilesLoading = New XMLValue(Of Boolean)("FastProfilesLoading", True, MyXML) MaxLargeImageHeight = New XMLValue(Of Integer)("MaxLargeImageHeight", 150, MyXML) MaxSmallImageHeight = New XMLValue(Of Integer)("MaxSmallImageHeight", 15, MyXML) @@ -176,6 +218,7 @@ Friend Class SettingsCLS : Implements IDownloaderSettings, IDisposable ClosingCommand = New XMLValueAttribute(Of String, Boolean)("ClosingCommand", "Use",,, MyXML) AddHandler ClosingCommand.ValueChanged, Sub(s, ev) MainFrameObj?.ChangeCloseVisible() InfoViewMode = New XMLValue(Of Integer)("InfoViewMode", DownloadedInfoForm.ViewModes.Session, MyXML) + InfoViewDefault = New XMLValue(Of Boolean)("InfoViewDefault", True, MyXML) ViewMode = New XMLValue(Of Integer)("ViewMode", ViewModes.IconLarge, MyXML) ShowingMode = New XMLValue(Of Integer)("ShowingMode", ShowingModes.All, MyXML) ShowGroupsInsteadLabels = New XMLValue(Of Boolean)("ShowGroupsInsteadLabels", False, MyXML) @@ -217,8 +260,15 @@ Friend Class SettingsCLS : Implements IDownloaderSettings, IDisposable STDownloader_RemoveDownloadedAutomatically = New XMLValue(Of Boolean)("RemoveDownloadedAutomatically", False, MyXML, n) STDownloader_OnItemDoubleClick = New XMLValue(Of DoubleClickBehavior)("OnItemDoubleClick", DoubleClickBehavior.Folder, MyXML, n) STDownloader_TakeSnapshot = New XMLValue(Of Boolean)("TakeSnapshot", True, MyXML, n) + STDownloader_SnapshotsKeepWithFiles = New XMLValue(Of Boolean)("SnapshotsKeepWithFiles", True, MyXML, n) + STDownloader_SnapShotsCachePermamnent = New XMLValue(Of Boolean)("SnapShotsCachePermamnent", False, MyXML, n) STDownloader_RemoveYTVideosOnClear = New XMLValue(Of Boolean)("RemoveYouTubeVideosOnClear", False, MyXML, n) STDownloader_LoadYTVideos = New XMLValue(Of Boolean)("LoadYouTubeVideos", False, MyXML, n) + STDownloader_OutputPathUseYT = New XMLValue(Of Boolean)("OutputPathUseYT", False, MyXML, n) + STDownloader_OutputPathAskForName = New XMLValue(Of Boolean)("OutputPathAskForName", True, MyXML, n) + STDownloader_OutputPathAutoAddPaths = New XMLValue(Of Boolean)("OutputPathAutoAddPaths", True, MyXML, n) + DownloadLocations = New STDownloader.DownloadLocationsCollection + DownloadLocations.Load(False, STDownloader_OutputPathUseYT) n = {"Feed"} FeedDataColumns = New XMLValue(Of Integer)("DataColumns", 1, MyXML, n) @@ -232,6 +282,9 @@ Friend Class SettingsCLS : Implements IDownloaderSettings, IDisposable FeedBackColor.SetExtended("FeedColorBack",, MyXML, n) FeedForeColor = New XMLValue(Of Color) FeedForeColor.SetExtended("FeedColorFore",, MyXML, n) + FeedOpenLastMode = New XMLValue(Of Boolean)("OpenLastMode", False, MyXML, n) + FeedLastModeSubscriptions = New XMLValue(Of Boolean)("LastModeSubscriptions", False, MyXML, n) + FeedShowFriendlyNames = New XMLValue(Of Boolean)("ShowFriendlyNames", True, MyXML, n) n = {"Users"} FromChannelDownloadTop = New XMLValue(Of Integer)("FromChannelDownloadTop", 10, MyXML, n) @@ -264,6 +317,8 @@ Friend Class SettingsCLS : Implements IDownloaderSettings, IDisposable ShowNotificationsSTDownloader = New XMLValue(Of Boolean)("STDownloader", True, MyXML, n) ShowNotificationsSTDownloaderEveryDownload = New XMLValue(Of Boolean)("STDownloaderEveryDownload", True, MyXML, n) + ProgramText = New XMLValue(Of String)("ProgramText",, MyXML) + ProgramDescription = New XMLValue(Of String)("ProgramDescription",, MyXML) ExitConfirm = New XMLValue(Of Boolean)("ExitConfirm", True, MyXML) CloseToTray = New XMLValue(Of Boolean)("CloseToTray", True, MyXML) OpenFolderInOtherProgram = New XMLValueUse(Of String)("OpenFolderInOtherProgram",,, MyXML) @@ -707,8 +762,23 @@ Friend Class SettingsCLS : Implements IDownloaderSettings, IDisposable End Get End Property Friend ReadOnly Property STDownloader_TakeSnapshot As XMLValue(Of Boolean) + Friend ReadOnly Property STDownloader_SnapshotsKeepWithFiles As XMLValue(Of Boolean) + Friend ReadOnly Property STDownloader_SnapShotsCachePermamnent As XMLValue(Of Boolean) Friend ReadOnly Property STDownloader_RemoveYTVideosOnClear As XMLValue(Of Boolean) Friend ReadOnly Property STDownloader_LoadYTVideos As XMLValue(Of Boolean) + Friend ReadOnly Property STDownloader_OutputPathUseYT As XMLValue(Of Boolean) + Friend ReadOnly Property STDownloader_OutputPathAskForName As XMLValue(Of Boolean) + Private ReadOnly Property IDownloaderSettings_OutputPathAskForName As Boolean Implements IDownloaderSettings.OutputPathAskForName + Get + Return STDownloader_OutputPathAskForName + End Get + End Property + Friend ReadOnly Property STDownloader_OutputPathAutoAddPaths As XMLValue(Of Boolean) + Private ReadOnly Property IDownloaderSettings_OutputPathAutoAddPaths As Boolean Implements IDownloaderSettings.OutputPathAutoAddPaths + Get + Return STDownloader_OutputPathAutoAddPaths + End Get + End Property #End Region #Region "User metrics" Friend ReadOnly Property UMetrics_What As XMLValue(Of Integer) @@ -730,6 +800,10 @@ Friend Class SettingsCLS : Implements IDownloaderSettings, IDisposable #End Region #End Region #Region "View" + Friend ReadOnly Property MainFrameUsersShowDefaults As XMLValue(Of Boolean) + Friend ReadOnly Property MainFrameUsersShowSubscriptions As XMLValue(Of Boolean) + Friend ReadOnly Property MainFrameUsersSubscriptionsColorBack As XMLValue(Of Color) + Friend ReadOnly Property MainFrameUsersSubscriptionsColorFore As XMLValue(Of Color) Friend ReadOnly Property FastProfilesLoading As XMLValue(Of Boolean) Friend ReadOnly Property MaxLargeImageHeight As XMLValue(Of Integer) Friend ReadOnly Property MaxSmallImageHeight As XMLValue(Of Integer) @@ -751,6 +825,7 @@ Friend Class SettingsCLS : Implements IDownloaderSettings, IDisposable Friend ReadOnly Property DownloadsCompleteCommand As XMLValueAttribute(Of String, Boolean) Friend ReadOnly Property ClosingCommand As XMLValueAttribute(Of String, Boolean) Friend ReadOnly Property InfoViewMode As XMLValue(Of Integer) + Friend ReadOnly Property InfoViewDefault As XMLValue(Of Boolean) Friend ReadOnly Property ViewMode As XMLValue(Of Integer) Friend ReadOnly Property ViewModeIsPicture As Boolean Get @@ -812,6 +887,9 @@ Friend Class SettingsCLS : Implements IDownloaderSettings, IDisposable Friend ReadOnly Property FeedStoreSessionsData As XMLValue(Of Boolean) Friend ReadOnly Property FeedBackColor As XMLValue(Of Color) Friend ReadOnly Property FeedForeColor As XMLValue(Of Color) + Friend ReadOnly Property FeedOpenLastMode As XMLValue(Of Boolean) + Friend ReadOnly Property FeedLastModeSubscriptions As XMLValue(Of Boolean) + Friend ReadOnly Property FeedShowFriendlyNames As XMLValue(Of Boolean) #End Region #Region "New version properties" Friend ReadOnly Property CheckUpdatesAtStart As XMLValue(Of Boolean) @@ -854,6 +932,8 @@ Friend Class SettingsCLS : Implements IDownloaderSettings, IDisposable Friend ReadOnly Property ShowNotificationsSTDownloaderEveryDownload As XMLValue(Of Boolean) #End Region #Region "Other program properties" + Friend ReadOnly Property ProgramText As XMLValue(Of String) + Friend ReadOnly Property ProgramDescription As XMLValue(Of String) Friend ReadOnly Property ExitConfirm As XMLValue(Of Boolean) Friend ReadOnly Property CloseToTray As XMLValue(Of Boolean) Friend ReadOnly Property OpenFolderInOtherProgram As XMLValueUse(Of String) diff --git a/SCrawler/SiteResources.Designer.vb b/SCrawler/SiteResources.Designer.vb index dff6ac0..2322380 100644 --- a/SCrawler/SiteResources.Designer.vb +++ b/SCrawler/SiteResources.Designer.vb @@ -274,16 +274,6 @@ Namespace My.Resources End Get End Property - ''' - ''' Looks up a localized resource of type System.Drawing.Bitmap. - ''' - Friend Shared ReadOnly Property TwitterPic_400() As System.Drawing.Bitmap - Get - Dim obj As Object = ResourceManager.GetObject("TwitterPic_400", resourceCulture) - Return CType(obj,System.Drawing.Bitmap) - End Get - End Property - ''' ''' Looks up a localized resource of type System.Drawing.Icon similar to (Icon). ''' diff --git a/SCrawler/SiteResources.resx b/SCrawler/SiteResources.resx index fc91260..93beb33 100644 --- a/SCrawler/SiteResources.resx +++ b/SCrawler/SiteResources.resx @@ -181,9 +181,6 @@ Content\Icons\SiteIcons\TwitterIcon_32.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - Content\Pictures\SitePictures\TwitterPic_400.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - Content\Icons\SiteIcons\XhamsterIcon_32.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a diff --git a/SCrawler/UserFinder.vb b/SCrawler/UserFinder.vb index bca903e..8eac2f2 100644 --- a/SCrawler/UserFinder.vb +++ b/SCrawler/UserFinder.vb @@ -83,7 +83,8 @@ Friend Class UserFinder : Implements IDisposable .SpecialCollectionPath = x.Value(UserInfo.Name_SpecialCollectionPath), .UserModel = x.Value(UserInfo.Name_Model_User).FromXML(Of Integer)(UsageModel.Default), .CollectionModel = x.Value(UserInfo.Name_Model_Collection).FromXML(Of Integer)(UsageModel.Default), - .CollectionName = x.Value(UserInfo.Name_Collection) + .CollectionName = x.Value(UserInfo.Name_Collection), + .IsSubscription = x.Value(UserInfo.Name_IsSubscription).FromXML(Of Boolean)(False) } u.Merged = x.Value(UserInfo.Name_Merged).FromXML(Of Boolean)(False) FoundUsers.Add(u) diff --git a/SCrawler/UserInfo.vb b/SCrawler/UserInfo.vb index 639c6c3..223daaa 100644 --- a/SCrawler/UserInfo.vb +++ b/SCrawler/UserInfo.vb @@ -12,11 +12,12 @@ Imports SCrawler.Plugin.Hosts Imports PersonalUtilities.Functions.XML Imports PersonalUtilities.Functions.XML.Base Partial Friend Module MainMod - Friend Structure UserInfo : Implements IComparable(Of UserInfo), IEquatable(Of UserInfo), ICloneable, IEContainerProvider + Friend Structure UserInfo : Implements IComparable(Of UserInfo), IEquatable(Of UserInfo), IEContainerProvider #Region "XML Names" Friend Const Name_UserNode As String = "User" Friend Const Name_Site As String = "Site" Friend Const Name_Plugin As String = "Plugin" + Friend Const Name_IsSubscription As String = "IsSubscription" Friend Const Name_Collection As String = "Collection" Friend Const Name_Model_User As String = "ModelUser" Friend Const Name_Model_Collection As String = "ModelCollection" @@ -30,6 +31,7 @@ Partial Friend Module MainMod Friend Site As String Friend Plugin As String Friend File As SFile + Friend IsSubscription As Boolean Friend SpecialPath As SFile Friend SpecialCollectionPath As SFile Friend Merged As Boolean @@ -65,6 +67,7 @@ Partial Friend Module MainMod Name = x.Value Site = x.Attribute(Name_Site).Value Plugin = x.Attribute(Name_Plugin).Value + IsSubscription = x.Attribute(Name_IsSubscription).Value.FromXML(Of Boolean)(False) CollectionName = x.Attribute(Name_Collection).Value CollectionModel = x.Attribute(Name_Model_Collection).Value.FromXML(Of Integer)(UsageModel.Default) UserModel = x.Attribute(Name_Model_User).Value.FromXML(Of Integer)(UsageModel.Default) @@ -101,6 +104,7 @@ Partial Friend Module MainMod End Function #End Region #Region "FilePath" + Friend Const CollectionUserPathPattern As String = "{0}\{1}_{2}\" Friend Sub UpdateUserFile() File = New SFile With { .Separator = "\", @@ -109,23 +113,39 @@ Partial Friend Module MainMod .Name = $"{UserDataBase.UserFileAppender}_{Site}_{Name}" } End Sub + Friend Function GetCollectionRootPath() As SFile + If IncludedInCollection And Not IsVirtual Then + Dim ColPath$ = If(SpecialCollectionPath.IsEmptyString, Settings.CollectionsPathF, SpecialCollectionPath).PathNoSeparator + If SpecialCollectionPath.IsEmptyString Then ColPath &= $"\{CollectionName}" + Return ColPath.CSFileP + Else + Return Nothing + End If + End Function Private Function GetFilePathByParams() As String If [Protected] Then Return String.Empty - Dim ColPath$ = If(SpecialCollectionPath.IsEmptyString, Settings.CollectionsPathF, SpecialCollectionPath).PathNoSeparator - If SpecialCollectionPath.IsEmptyString Then ColPath &= $"\{CollectionName}" - If Not SpecialPath.IsEmptyString Then - Return $"{SpecialPath.PathWithSeparator}{SettingsFolderName}" - ElseIf Merged And IncludedInCollection Then - Return $"{ColPath}\{SettingsFolderName}" - Else - If IncludedInCollection And Not IsVirtual Then - Return $"{ColPath}\{Site}_{Name}\{SettingsFolderName}" - ElseIf Not Settings(Plugin) Is Nothing Then - Return $"{Settings(Plugin).Path.PathNoSeparator}\{Name}\{SettingsFolderName}" + If IsSubscription Then + If Not Settings(Plugin) Is Nothing Then + Return $"{Application.StartupPath.CSFilePSN}\{SettingsFolderName}\Subscriptions\{Settings(Plugin).Key}\{Name}\{SettingsFolderName}" Else - Dim s$ = Site.ToLower - Dim i% = Settings.Plugins.FindIndex(Function(p) p.Name.ToLower = s) - If i >= 0 Then Return $"{Settings.Plugins(i).Settings.Path.PathNoSeparator}\{Name}\{SettingsFolderName}" Else Return String.Empty + Return String.Empty + End If + Else + Dim ColPath$ = GetCollectionRootPath().PathNoSeparator + If Not SpecialPath.IsEmptyString Then + Return $"{SpecialPath.PathWithSeparator}{SettingsFolderName}" + ElseIf Merged And IncludedInCollection Then + Return $"{ColPath}\{SettingsFolderName}" + Else + If IncludedInCollection And Not IsVirtual Then + Return $"{String.Format(CollectionUserPathPattern, ColPath, Site, Name)}{SettingsFolderName}" + ElseIf Not Settings(Plugin) Is Nothing Then + Return $"{Settings(Plugin).Path.PathNoSeparator}\{Name}\{SettingsFolderName}" + Else + Dim s$ = Site.ToLower + Dim i% = Settings.Plugins.FindIndex(Function(p) p.Name.ToLower = s) + If i >= 0 Then Return $"{Settings.Plugins(i).Settings.Path.PathNoSeparator}\{Name}\{SettingsFolderName}" Else Return String.Empty + End If End If End If End Function @@ -134,6 +154,7 @@ Partial Friend Module MainMod Friend Function ToEContainer(Optional ByVal e As ErrorsDescriber = Nothing) As EContainer Implements IEContainerProvider.ToEContainer Return New EContainer(Name_UserNode, Name, {New EAttribute(Name_Site, Site), New EAttribute(Name_Plugin, Plugin), + New EAttribute(Name_IsSubscription, IsSubscription.BoolToInteger), New EAttribute(Name_Collection, CollectionName), New EAttribute(Name_Model_User, CInt(UserModel)), New EAttribute(Name_Model_Collection, CInt(CollectionModel)), @@ -155,27 +176,11 @@ Partial Friend Module MainMod #Region "IEquatable Support" Friend Overloads Function Equals(ByVal Other As UserInfo) As Boolean Implements IEquatable(Of UserInfo).Equals Return Site.StringToLower = Other.Site.StringToLower And Name.StringToLower = Other.Name.StringToLower And - (Not Plugin = PathPlugin.PluginKey Or SpecialPath = Other.SpecialPath) + IsSubscription = Other.IsSubscription And (Not Plugin = PathPlugin.PluginKey Or SpecialPath = Other.SpecialPath) End Function Public Overloads Overrides Function Equals(ByVal Obj As Object) As Boolean Return Equals(DirectCast(Obj, UserInfo)) End Function -#End Region -#Region "ICloneable Support" - Friend Function Clone() As Object Implements ICloneable.Clone - Return New UserInfo With { - .Name = Name, - .Site = Site, - .Plugin = Plugin, - .File = File, - .SpecialPath = SpecialPath, - .Merged = Merged, - .CollectionName = CollectionName, - .CollectionModel = CollectionModel, - .UserModel = UserModel, - .[Protected] = [Protected] - } - End Function #End Region End Structure End Module \ No newline at end of file diff --git a/SCrawler/UserSearchForm.vb b/SCrawler/UserSearchForm.vb index 8dc50cc..ea18085 100644 --- a/SCrawler/UserSearchForm.vb +++ b/SCrawler/UserSearchForm.vb @@ -24,14 +24,14 @@ Friend Class UserSearchForm Friend Sub New(ByVal User As IUserData, ByVal Mode As Modes) Key = User.Key IsCollection = User.IsCollection - Text = $"[{IIf(IsCollection, "C", "U")}] " + Me.Mode = Mode + Text = $"[{Mode.ToString.First.ToString.ToUpper}] [{IIf(IsCollection, "C", "U")}] " If IsCollection Then Text &= $"[{User.CollectionName}] " Else If User.IncludedInCollection Then Text &= $"[{User.CollectionName}] " Text &= $"[{User.Site}] [{User.Name}]" End If - Me.Mode = Mode End Sub Private Function CompareTo(ByVal Other As SearchResult) As Integer Implements IComparable(Of SearchResult).CompareTo If CInt(Mode).CompareTo(CInt(Other.Mode)) = 0 Then @@ -41,7 +41,9 @@ Friend Class UserSearchForm End If End Function Public Overrides Function Equals(ByVal Obj As Object) As Boolean - With DirectCast(Obj, SearchResult) : Return Key = .Key And Mode = .Mode : End With + 'TODO: [UserSearchForm]: updated equal function + With DirectCast(Obj, SearchResult) : Return Key = .Key : End With + 'With DirectCast(Obj, SearchResult) : Return Key = .Key And Mode = .Mode : End With End Function End Structure Public Sub New() @@ -125,7 +127,7 @@ Friend Class UserSearchForm If .Count > 0 Then For i = 0 To .Count - 1 With .Item(i) - If Not __isUrl AndAlso __name AndAlso .Self.Name.ToLower = t Then Results.ListAddValue(New SearchResult(.Self, SearchResult.Modes.Name), RLP) + If Not __isUrl AndAlso __name AndAlso .Self.Name.ToLower.Contains(t) Then Results.ListAddValue(New SearchResult(.Self, SearchResult.Modes.Name), RLP) _addValue(.Self, SearchResult.Modes.URL, _p_url) _addValue(.Self, SearchResult.Modes.Description, _p_descr) _addValue(.Self, SearchResult.Modes.Label, _p_labels) diff --git a/SCrawler/packages.config b/SCrawler/packages.config index ac4353d..1e554cb 100644 --- a/SCrawler/packages.config +++ b/SCrawler/packages.config @@ -1,10 +1,10 @@  - - + + - + \ No newline at end of file