diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
index 6e4b3df..4229ef1 100644
--- a/.github/FUNDING.yml
+++ b/.github/FUNDING.yml
@@ -10,4 +10,4 @@ liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
-custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
+custom: ['https://blockchair.com/bitcoin/address/BC1Q0NH839FT5TA44DD7L7RLR97XDQAG9V8D6N7XET']
diff --git a/.github/ISSUE_TEMPLATE/plugin_add.md b/.github/ISSUE_TEMPLATE/plugin_add.md
new file mode 100644
index 0000000..38fdeaa
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/plugin_add.md
@@ -0,0 +1,12 @@
+---
+name: Add plugin
+about: Add plugin to plugin list
+title: "[NEW PLUGIN]"
+labels: 'New Plugin'
+assignees: ''
+
+---
+
+Plugin address:
+Plugin name:
+Plugin site:
\ No newline at end of file
diff --git a/Changelog.md b/Changelog.md
index 4db5e71..ead1c06 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,3 +1,43 @@
+# 3.0.0.0
+
+**Attention! This version of the program makes changes user data file (Users.xml). Once you start using this version, you will not be able to use previous versions of the program. Therefore, it is highly recommended to archive the program settings folder and archive the users' data files (you can use the [```ArchiveSCrawlerUsersDataFiles.bat```](Tools/ArchiveSCrawlerUsersDataFiles.bat) tool to archive the data files of all users).**
+
+- Added
+ - **PLUGINS SUPPORT**
+ - **Gfycat** site support
+ - Description of Twitter and Reddit user profiles
+ - Filter users by profile status "Suspended"
+ - Filter users by profile status "Deleted"
+ - Filter profiles that haven't downloaded new data since specific date
+ - Collections that contain non-existent profiles will be marked in blue
+ - Ability to find and activate a user in the main window from the Info form
+ - Ability to copy user images from all channels you have when adding a user from a channel
+ - Reddit default option "Get user media only" if now also used when creating new users from channels
+ - Ability to update user description every time
+ - ```Enter``` hotkey in the download info form to open the user's folder
+ - ```Enter``` hotkey in the main window to open the user's folder
+ - Channel statistics are supplemented by "existing users"
+ - ```Up``` and ```Down``` navigation buttons in the Info form
+ - ```Find``` button on the Info form to find the user in the main window
+ - "Details" view mode
+ - Fast loading of profiles in the main window. **Be careful with this setting. Fast loading leads to the highest CPU usage.**
+ - Reddit availability check with DownDetector
+ - Ability to [open folders with a specific program](https://github.com/AAndyProgram/SCrawler/wiki/Settings#folder-command)
+ - (Request #16) Ability to remove a user from the collection without deletion
+ - (Request #17) **Instagram Tagged** photos downloading
+ - (Request #17) **Instagram Stories** downloading
+ - Deleting data to recycle bin
+- Updated
+ - "List" view mode
+- Fixed
+ - Twitter reloads existing media
+ - Reddit saved posts downloader downloads all posts every time
+ - Minor bug that caused Instagram tasks timers to run longer
+ - A library error that in some cases leads to a fatal program error
+ - (Issue #16) Cannot delete a user that is in the collection.
+
+At the requests of some users, I added [screenshots](ProgramScreenshots) of the program and added screenshots to [ReadMe](README.md) and the [guide](https://github.com/AAndyProgram/SCrawler/wiki).
+
# 2.0.0.4
**Removed compatibility of program settings with version 1.0.0.4 and lower.**
diff --git a/FAQ.md b/FAQ.md
index 94d578e..797df1b 100644
--- a/FAQ.md
+++ b/FAQ.md
@@ -8,42 +8,60 @@ Any other questions I will keep in this file.
----
-Q: **HOW TO SETUP COOKIES**
+#### Q: **HOW TO SETUP COOKIES**
A: https://github.com/AAndyProgram/SCrawler/wiki/Settings#how-to-set-up-cookies
----
-Q: **I can't copy cookies.**
+#### Q: **I can't copy cookies.**
A: Use the mouse. Don't use ```Ctrl``` + ```A```!
----
-Q: **Does this program have GUI or CLI.**
+#### Q: **Does this program have GUI or CLI.**
A: This is a GUI program.
----
-Q: **Will CLI be added in the future?**
+#### Q: **Will CLI be added in the future?**
A: I do not think so.
----
-Q: **I want to add "...." site. How to request.**
+#### Q: **I want to add "...." site. How to request.**
-A: https://github.com/AAndyProgram/SCrawler/blob/main/CONTRIBUTING.md#how-to-request-a-new-site
+A: How to request a new site you can read [here](CONTRIBUTING.md#how-to-request-a-new-site)
----
-Q: **Twitter/Instagram download failed.**
+#### Q: **Twitter/Instagram download failed.**
A: Check your credentials. Both of these sites require cookies. Check your [Twitter tokens](https://github.com/AAndyProgram/SCrawler/wiki/Settings#how-to-find-twitter-tokens) and [Instagram settings](https://github.com/AAndyProgram/SCrawler/wiki/Settings#instagram-settings). If all settings are set, but nothing works, go to [create a new issue](https://github.com/AAndyProgram/SCrawler/issues). Don't forget to attach the LOG.
----
-Q: **Does the program remember the last download and check for new posts, downloading only new posts? Or does the program download the entire profile every time?**
+#### Q: **Does the program remember the last download and check for new posts, downloading only new posts? Or does the program download the entire profile every time?**
-A: The program stored posts IDs in users' folders. For the first time, the program downloads the entire profile. All subsequent times the program will check for new posts and download **only new posts**!
\ No newline at end of file
+A: The program stored posts IDs in users' folders. For the first time, the program downloads the entire profile. All subsequent times the program will check for new posts and download **only new posts**!
+
+----
+
+#### Q: **How to redownload all data**
+
+A: Double-click on the user you want to redownload. In the opened window open folder setting. Delete the files ending with ```_Data.xml``` and ```_Posts.txt```. Download this user again.
+
+----
+
+#### Q: **How to remove the label**
+
+A: There is no functionality to remove an individual label. You can open the ```Labels.txt``` file in the program settings folder and delete any label you want. You also can delete this file (```Labels.txt```). In this case, when the program starts, the list of labels list will be updated with only existing labels (from the user data files).
+
+----
+
+#### Q: **How to remove a user from the blacklist**
+
+A: Just add that user back to the program. In the dialog box that opens, click on the ```Add and remove from blacklist```` button.
\ No newline at end of file
diff --git a/HowToSupport.md b/HowToSupport.md
new file mode 100644
index 0000000..6089cb4
--- /dev/null
+++ b/HowToSupport.md
@@ -0,0 +1,11 @@
+Your support is very valuable to me. Any support is greatly appreciated. Your support encourages me to make new features, update the program, add new sites, etc.
+
+You can support the program by:
+ - **Bitcoin**: bitcoin:BC1Q0NH839FT5TA44DD7L7RLR97XDQAG9V8D6N7XET
+ - :heavy_dollar_sign: making donaion making donations on this site: https://ko-fi.com/andyprogram
+ - :repeat: make a post about my program on your profile (Reddit, Twitter, Instagram and any other social networks)
+ - :speech_balloon: tell your friends about the program
+ - :heart: like the program on this site: https://alternativeto.net/software/scrawler/about/
+ - suggest my program as an alternative ([on this site](https://alternativeto.net/software/scrawler/about/)) to any program you have used before
+
+I would be very grateful for any support! :blush:
\ No newline at end of file
diff --git a/Plugins.md b/Plugins.md
new file mode 100644
index 0000000..4649f13
--- /dev/null
+++ b/Plugins.md
@@ -0,0 +1 @@
+List of available plugins
\ No newline at end of file
diff --git a/ProgramScreenshots/Channels.png b/ProgramScreenshots/Channels.png
new file mode 100644
index 0000000..95b09d9
Binary files /dev/null and b/ProgramScreenshots/Channels.png differ
diff --git a/ProgramScreenshots/ChannelsStats.png b/ProgramScreenshots/ChannelsStats.png
new file mode 100644
index 0000000..ac3300f
Binary files /dev/null and b/ProgramScreenshots/ChannelsStats.png differ
diff --git a/ProgramScreenshots/ChannelsStats2.png b/ProgramScreenshots/ChannelsStats2.png
new file mode 100644
index 0000000..cd774cd
Binary files /dev/null and b/ProgramScreenshots/ChannelsStats2.png differ
diff --git a/ProgramScreenshots/CreateUser.png b/ProgramScreenshots/CreateUser.png
new file mode 100644
index 0000000..637177a
Binary files /dev/null and b/ProgramScreenshots/CreateUser.png differ
diff --git a/ProgramScreenshots/CreateUserChannel.png b/ProgramScreenshots/CreateUserChannel.png
new file mode 100644
index 0000000..7d255eb
Binary files /dev/null and b/ProgramScreenshots/CreateUserChannel.png differ
diff --git a/ProgramScreenshots/CreateUserClear.png b/ProgramScreenshots/CreateUserClear.png
new file mode 100644
index 0000000..7a8b2da
Binary files /dev/null and b/ProgramScreenshots/CreateUserClear.png differ
diff --git a/ProgramScreenshots/DownloadInfo.png b/ProgramScreenshots/DownloadInfo.png
new file mode 100644
index 0000000..d685b1a
Binary files /dev/null and b/ProgramScreenshots/DownloadInfo.png differ
diff --git a/ProgramScreenshots/Labels.png b/ProgramScreenshots/Labels.png
new file mode 100644
index 0000000..3fc072a
Binary files /dev/null and b/ProgramScreenshots/Labels.png differ
diff --git a/ProgramScreenshots/MainContext.png b/ProgramScreenshots/MainContext.png
new file mode 100644
index 0000000..9253865
Binary files /dev/null and b/ProgramScreenshots/MainContext.png differ
diff --git a/ProgramScreenshots/MainContext2.png b/ProgramScreenshots/MainContext2.png
new file mode 100644
index 0000000..20439a4
Binary files /dev/null and b/ProgramScreenshots/MainContext2.png differ
diff --git a/ProgramScreenshots/MainWindow.png b/ProgramScreenshots/MainWindow.png
new file mode 100644
index 0000000..685229c
Binary files /dev/null and b/ProgramScreenshots/MainWindow.png differ
diff --git a/ProgramScreenshots/MainWindow2.png b/ProgramScreenshots/MainWindow2.png
new file mode 100644
index 0000000..7f23803
Binary files /dev/null and b/ProgramScreenshots/MainWindow2.png differ
diff --git a/ProgramScreenshots/SavedPosts.png b/ProgramScreenshots/SavedPosts.png
new file mode 100644
index 0000000..81d12ce
Binary files /dev/null and b/ProgramScreenshots/SavedPosts.png differ
diff --git a/ProgramScreenshots/SeparateVideoDownloader.png b/ProgramScreenshots/SeparateVideoDownloader.png
new file mode 100644
index 0000000..4d3e5f8
Binary files /dev/null and b/ProgramScreenshots/SeparateVideoDownloader.png differ
diff --git a/ProgramScreenshots/SettingDefaults.png b/ProgramScreenshots/SettingDefaults.png
new file mode 100644
index 0000000..11d3388
Binary files /dev/null and b/ProgramScreenshots/SettingDefaults.png differ
diff --git a/ProgramScreenshots/SettingsBasis.png b/ProgramScreenshots/SettingsBasis.png
new file mode 100644
index 0000000..4c62877
Binary files /dev/null and b/ProgramScreenshots/SettingsBasis.png differ
diff --git a/ProgramScreenshots/SettingsChannels.png b/ProgramScreenshots/SettingsChannels.png
new file mode 100644
index 0000000..403a7f8
Binary files /dev/null and b/ProgramScreenshots/SettingsChannels.png differ
diff --git a/ProgramScreenshots/SettingsInstagram.png b/ProgramScreenshots/SettingsInstagram.png
new file mode 100644
index 0000000..a83fc3b
Binary files /dev/null and b/ProgramScreenshots/SettingsInstagram.png differ
diff --git a/ProgramScreenshots/SettingsReddit.png b/ProgramScreenshots/SettingsReddit.png
new file mode 100644
index 0000000..bf63aa9
Binary files /dev/null and b/ProgramScreenshots/SettingsReddit.png differ
diff --git a/ProgramScreenshots/SettingsTwitter.png b/ProgramScreenshots/SettingsTwitter.png
new file mode 100644
index 0000000..55a1b68
Binary files /dev/null and b/ProgramScreenshots/SettingsTwitter.png differ
diff --git a/ProgramsComparison.md b/ProgramsComparison.md
new file mode 100644
index 0000000..214697d
--- /dev/null
+++ b/ProgramsComparison.md
@@ -0,0 +1,69 @@
+# 4K Stogram
+
+https://www.4kdownload.com/products/product-stogram
+
+| Option | SCrawler | 4K Stogram |
+| ---- | ---- | ---- |
+| User managament | **Advanced** | Primitive |
+| Labeling users | **Yes** | No |
+| Filtering | **Yes** | No |
+| Collections | **Yes** | No |
+| Specific user folders | **Yes** | No |
+| Favorite / Temporary user options | **Yes** | No |
+| Plugins support | **Yes** | No |
+| Download posts by username | Yes | Yes |
+| Download posts by hashtag | No | **Yes** |
+| Download posts by location | No | **Yes** |
+| Save Private Instagram Content with Permission| Yes | Yes |
+| Download Instagram Stories and Highlights | Yes | Yes |
+| See Others Instagram Feed As Your Own | No | **Yes** |
+| Download Instagram Video Posts | Yes | Yes |
+| Backup Your Instagram Account | Yes | Yes |
+| Save Instagram Posts by Date | No (only limited download) | **Yes** |
+| Download Instagram Saved Posts | Yes | Yes |
+| Download Instagram Tagged Posts | Yes | Yes |
+| Export and import subscriptions | No | **Yes** |
+| **Paid** | **No** | Yes |
+| **Free options** | **The program is completely free** | Only **ONE** profile downloading and only **200 posts** per day |
+| Permitted Commercial Use | **Yes** | Starting from 43.56 EUR |
+| Automatic Subscriptions Update | **Free** | Paid (43.56 EUR) |
+| Posts and Captions Export | No | Paid (43.56 EUR) |
+| Advertisements free | **No ADs at all for free** | Paid (14.52) |
+| Operating Systems | Windows 7+ | Windows 7+, MacOS 10.13+, Ubuntu x64 |
+| Select want content type to download | **Yes** | No |
+| Instagram support | Yes | Yes |
+| Twitter support | **Yes** | No |
+| Reddit support | **Yes** | No |
+| Other sites support | **Yes** | No |
+| Still supported | Yes | Yes |
+
+# RipMeApp
+
+https://github.com/RipMeApp/ripme
+
+| Option | SCrawler | 4K Stogram |
+| ---- | ---- | ---- |
+| User managament | **Advanced** | No |
+| Labeling users | **Yes** | No |
+| Filtering | **Yes** | No |
+| Collections | **Yes** | No |
+| Specific user folders | **Yes** | No |
+| Favorite / Temporary user options | **Yes** | No |
+| Plugins support | **Yes** | No |
+| Download posts by username | Yes | Yes |
+| Download posts by hashtag | No | No |
+| Download posts by location | No | No |
+| Save Private Instagram Content with Permission| Yes | Yes |
+| Download Instagram Stories | Yes | Yes |
+| Download Instagram Video Posts | Yes | Yes |
+| Backup Your Instagram Account | Yes | Yes |
+| Download Instagram Saved Posts | **Yes** | No |
+| Download Instagram Tagged Posts | **Yes** | No |
+| Export and import subscriptions | No | No |
+| **Paid** | **No** | **No** |
+| **Free options** | The program is completely free | The program is completely free, but site limits are not declared |
+| Operating Systems | Windows 7+ | Windows, MacOS, Linux |
+| Select want content type to download | Yes | Yes |
+| Suported sites | 3 internal and any site using plugins | 86+ sites (declared) |
+| Other sites support | **Yes** | No |
+| Still supported | **Yes** | **No (last release date May 4, 2021)** |
\ No newline at end of file
diff --git a/README.md b/README.md
index b78c49f..f29be03 100644
--- a/README.md
+++ b/README.md
@@ -4,13 +4,19 @@
[](https://github.com/AAndyProgram/SCrawler/)
[](FAQ.md)
[](https://github.com/AAndyProgram/SCrawler/wiki)
+[](HowToSupport.md)
-A program to download photo and video from Reddit, Twitter, Instagram, [etc](#supported-sites).
+A program to download photo and video from [any site](#supported-sites) (e.g. Reddit, Twitter, Instagram).
-Do you like this program? Consider adding to my coffee fund by making a donation to show your support. :)
+Do you like this program? Consider adding to my coffee fund by making a donation to show your support. :blush:
[](https://ko-fi.com/andyprogram)
+**Bitcoin**: bitcoin:BC1Q0NH839FT5TA44DD7L7RLR97XDQAG9V8D6N7XET
+
+
+
+
# What can program do:
- Download pictures and videos from users' profiles and subreddits:
- Reddit images;
@@ -19,20 +25,33 @@ Do you like this program? Consider adding to my coffee fund by making a donation
- Reddit hosted videos (downloading Reddit hosted video is going through ffmpeg (**ffmpeg only works with the x64 program**));
- Twitter images and videos;
- Instagram images and videos.
+ - Imgur images, galleries and videos
+ - Gfycat videos
+ - [Other](#supported-sites) supported sites
- Parse [channel and view data](https://github.com/AAndyProgram/SCrawler/wiki/Channels).
- Download [saved Reddit and Instagram posts](https://github.com/AAndyProgram/SCrawler/wiki/Home#saved-posts).
- Add users from parsed channel.
+- **Advanced user management.**
- Labeling users.
+- Adding users to favorites and temporary.
- Filter exists users by label or group.
- Selection of media types you want to download (images only, videos only, both)
+- Download a special video, image or gallery
+- Making collections (grouping users into collections)
+- Specifying a user folder (for downloading data to another location)
+- Changing user icons
+- Changing view modes
+- ...and many others...
# Supported sites
-- Reddit
-- Twitter
-- Instagram
+- **Reddit**
+- **Twitter**
+- **Instagram**
- RedGifs
- Imgur
+- Gfycat
+- [Other sites](Plugins.md)
# How does it works:
@@ -50,28 +69,42 @@ You can read about Instagram restrictions [here](https://github.com/AAndyProgram
## How to request a new site
-Read [here](https://github.com/AAndyProgram/SCrawler/blob/main/CONTRIBUTING.md#how-to-request-a-new-site) about
+Read [here](CONTRIBUTING.md#how-to-request-a-new-site) about
-# Requirements:
+# Requirements
- Windows 7, 8, 9, 10, 11 with NET Framework 4.6.1 or higher
- Authorization cookies and tokens for Twitter (if you want to download data from Twitter)
- Authorization cookies Instagram (if you want to download data from Instagram)
- ffmpeg library for downloading videos hosted on Reddit (you can download it from the [official repo](https://github.com/GyanD/codexffmpeg/releases/tag/2021-01-12-git-ca21cb1e36) or [from my first release](https://github.com/AAndyProgram/SCrawler/releases/download/1.0.0.0/ffmpeg.zip)). **ffmpeg only works with the x64 version of the program.**
-- **Don't put program in the ```Program Files``` system folder (this is portable program and program settings are stored in the program folder)**
-- **Just unzip the program archive to any folder, copy the file ```ffmpeg.exe``` into it and enjoy. :)**
+
+# Guide
+
+**Full guide you can find [here](https://github.com/AAndyProgram/SCrawler/wiki)**
+
+# Installation
+
+**Just unzip the program archive to any folder, copy the file ```ffmpeg.exe``` into it and enjoy.** :blush:
+
+**Don't put program in the ```Program Files``` system folder (this is portable program and program settings are stored in the program folder)**
+
+# Updating
+
+Just download [latest](https://github.com/AAndyProgram/SCrawler/releases/latest) version and unpack it into the program folder. **Before starting a new version, I recommend making a backup copy of the program settings folder.**
# How to build from source
1. Delete the "PersonalUtilities" project from the solution.
-2. Add the latest version of the "PersonalUtilities.dll" library (from the [latest release](https://github.com/AAndyProgram/SCrawler/releases/latest)).
-3. Import PersonalUtilities.Functions for the whole project.
+1. Add the latest version of the "PersonalUtilities.dll" library (from the [latest release](https://github.com/AAndyProgram/SCrawler/releases/latest)).
+1. Import PersonalUtilities.Functions for the whole project.
-# Updating
+# How to make a plugin
-Just download [latest](https://github.com/AAndyProgram/SCrawler/releases/latest) version and unpack it into the program folder. Before starting a new version, I recommend making a backup copy of the program settings folder.
+Read about how to make plugin [here](https://github.com/AAndyProgram/SCrawler/wiki/Plugins).
+# How to support
+Read more about how to support the program [here](HowToSupport.md).
# Settings and usage
@@ -92,16 +125,16 @@ You can add users by patterns:
Read more about adding users and subreddits [here](https://github.com/AAndyProgram/SCrawler/wiki/Users)
-# Guide
+
-**Full guide you can find [here](https://github.com/AAndyProgram/SCrawler/wiki)**
-
-## Using program as just video downloader
+# Using program as just video downloader
Create a shortcut for the program. Open shortcut properties. In the ```Shortcut``` tab, in the ```Target``` field, just add the letter ```v``` at the end across the space.
Example: ```D:\Programs\SCrawler\SCrawler.exe v```
+
+
# Contact me
[](https://matrix.to/#/@andyprogram:matrix.org)
diff --git a/SCrawler.PluginProvider/.editorconfig b/SCrawler.PluginProvider/.editorconfig
new file mode 100644
index 0000000..ea98747
--- /dev/null
+++ b/SCrawler.PluginProvider/.editorconfig
@@ -0,0 +1,3 @@
+[*.vb]
+# Modifier preferences
+file_header_template = Copyright (C) 2022 Andy\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program. If not, see
\ No newline at end of file
diff --git a/SCrawler.PluginProvider/Attributes/Attributes.vb b/SCrawler.PluginProvider/Attributes/Attributes.vb
new file mode 100644
index 0000000..f1a2174
--- /dev/null
+++ b/SCrawler.PluginProvider/Attributes/Attributes.vb
@@ -0,0 +1,153 @@
+' Copyright (C) 2022 Andy
+' This program is free software: you can redistribute it and/or modify
+' it under the terms of the GNU General Public License as published by
+' the Free Software Foundation, either version 3 of the License, or
+' (at your option) any later version.
+'
+' This program is distributed in the hope that it will be useful,
+' but WITHOUT ANY WARRANTY
+Imports System.Runtime.CompilerServices
+Namespace Plugin.Attributes
+ ''' Create a control for a property
+ Public NotInheritable Class PropertyOption : Inherits Attribute
+ ''' Property name
+ Public ReadOnly Property Name As String
+ ''' Property value type
+ Public Property [Type] As Type
+ Private _ControlText As String
+ ''' This text will be displayed on the control information.
Default: equals the name (property name)
+ Public Property ControlText As String
+ Get
+ Return If(String.IsNullOrEmpty(_ControlText), Name, _ControlText)
+ End Get
+ Set(ByVal NewText As String)
+ _ControlText = NewText
+ End Set
+ End Property
+ ''' This tooltip will be displayed on the control.
Default:
+ Public Property ControlToolTip As String
+ ''' CheckBox ThreeStates mode
+ Public Property ThreeStates As Boolean = False
+ ''' Property allows null values
+ Public Property AllowNull As Boolean = True
+ ''' Offset the control from the left border of the form.
Default: 100
+ Public Property LeftOffset As Integer = 100
+ ''' This is an authorization property
+ Public Property IsAuth As Boolean = False
+ ''' Initialize a new property option attribute
+ ''' Property name
+ Public Sub New( Optional ByVal PropertyName As String = Nothing)
+ Name = PropertyName
+ End Sub
+ End Class
+ ''' Store property value in settings XML file
+ Public NotInheritable Class PXML : Inherits Attribute
+ Public ReadOnly ElementName As String
+ ''' Initialize a new XML attribute
+ ''' XML element name
+ Public Sub New( Optional ByVal XMLElementName As String = Nothing)
+ ElementName = XMLElementName
+ End Sub
+ End Class
+ ''' Special property updater
+ Public NotInheritable Class PropertyUpdater : Inherits Attribute
+ Public ReadOnly Name As String
+ Public ReadOnly Dependencies 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())
+ Name = UpdatingPropertyName
+ Dependencies = Dependent
+ End Sub
+ End Class
+ ''' Plugin key
+ Public NotInheritable Class Manifest : Inherits Attribute
+ Public ReadOnly GUID As String
+ ''' Initialize a new Manifest attribute
+ ''' Plugin key
+ Public Sub New(ByVal ClassGuid As String)
+ GUID = ClassGuid
+ End Sub
+ End Class
+ ''' Special form attribute for settings forms and user creator form
+ Public NotInheritable Class SpecialForm : Inherits Attribute
+ Public ReadOnly SettingsForm As Boolean
+ ''' Initialize a new SpecialForm attribute
+ '''
+ ''' - for setting form
+ ''' - for user creator form
+ '''
+ Public Sub New(ByVal IsSettingsForm As Boolean)
+ SettingsForm = IsSettingsForm
+ End Sub
+ End Class
+ ''' Property provider
+ Public NotInheritable Class Provider : Inherits Attribute
+ Public ReadOnly Name As String
+ '''
+ ''' - form field validation provider. Must return null if the value is invalid.
+ ''' - only for conversion
+ '''
+ Public FieldsChecker As Boolean = False
+ ''' Initialize a new Provider attribute. is only allowed
+ ''' The name of the property for which this provider is used
+ Public Sub New(ByVal PropertyName As String)
+ Name = PropertyName
+ End Sub
+ End Class
+ ''' Sort attribute for settings form
+ Public NotInheritable Class ControlNumber : Inherits Attribute
+ Public ReadOnly PropertyNumber As String
+ ''' Initialize a new sort attribute instance for the settings form
+ ''' Object position number in the settings form
+ Public Sub New(ByVal Number As Integer)
+ PropertyNumber = Number
+ End Sub
+ End Class
+ ''' Attribute for properties values validation methods
+ Public NotInheritable Class PropertiesDataChecker : Inherits Attribute
+ Public ReadOnly ComparableNames As String()
+ ''' Initialize a new PropertiesDataChecker attribute.
+ ''' Array of the property names
+ Public Sub New(ByVal Names As String())
+ ComparableNames = Names
+ End Sub
+ End Class
+ ''' This attribute specifies that users should be downloaded on a separate thread.
+ Public NotInheritable Class SeparatedTasks : Inherits Attribute
+ Public ReadOnly TasksCount As Integer
+ ''' Initialize a new SeparatedTasks attribute.
+ '''
+ ''' Predefined task counter.
+ ''' will take precedence if it is defined.
+ '''
+ Public Sub New(Optional ByVal JobsCount As Integer = -1)
+ TasksCount = JobsCount
+ End Sub
+ End Class
+ ''' A property attribute that specifies how many users should be downloaded at the same time in one thread
+ Public NotInheritable Class TaskCounter : Inherits Attribute
+ End Class
+ ''' This attribute indicates that the plugin has a SavedPosts environment
+ Public NotInheritable Class SavedPosts : Inherits Attribute
+ End Class
+ ''' This is an attribute of the UserData instance. Specifies that the default internal SCrawler downloader should be used.
+ Public NotInheritable Class UseInternalDownloader : Inherits Attribute
+ End Class
+ ''' GitHub plugin info
+ Public NotInheritable Class Github : Inherits Attribute
+ Public ReadOnly UserName As String
+ Public ReadOnly Repository As String
+ ''' Initialize a new Github attribute.
+ ''' Developer GitHub username
+ ''' Plugin repository name
+ Public Sub New(ByVal Name As String, ByVal RepoName As String)
+ UserName = Name
+ Repository = RepoName
+ End Sub
+ End Class
+End Namespace
\ No newline at end of file
diff --git a/SCrawler.PluginProvider/Interfaces/IPluginContentProvider.vb b/SCrawler.PluginProvider/Interfaces/IPluginContentProvider.vb
new file mode 100644
index 0000000..badd7d8
--- /dev/null
+++ b/SCrawler.PluginProvider/Interfaces/IPluginContentProvider.vb
@@ -0,0 +1,36 @@
+' Copyright (C) 2022 Andy
+' This program is free software: you can redistribute it and/or modify
+' it under the terms of the GNU General Public License as published by
+' the Free Software Foundation, either version 3 of the License, or
+' (at your option) any later version.
+'
+' This program is distributed in the hope that it will be useful,
+' but WITHOUT ANY WARRANTY
+Namespace Plugin
+ Public Interface IPluginContentProvider
+ Event ProgressChanged(ByVal Count As Integer)
+ Event TotalCountChanged(ByVal Count As Integer)
+ Property Thrower As IThrower
+ Property LogProvider As ILogProvider
+ Property Settings As ISiteSettings
+ Property Name As String
+ Property ID As String
+ Property ParseUserMediaOnly As Boolean
+ Property UserDescription As String
+ Property ExistingContentList As List(Of PluginUserMedia)
+ Property TempPostsList As List(Of String)
+ Property TempMediaList As List(Of IPluginUserMedia)
+ Property UserExists As Boolean
+ Property UserSuspended As Boolean
+ Property IsSavedPosts As Boolean
+ Property SeparateVideoFolder As Boolean
+ Property DataPath As String
+ Property PostsNumberLimit As Integer?
+ Function ExchangeOptionsGet() As Object
+ Sub ExchangeOptionsSet(ByVal Obj As Object)
+ Sub XmlFieldsSet(ByVal Fields As List(Of KeyValuePair(Of String, String)))
+ Function XmlFieldsGet() As List(Of KeyValuePair(Of String, String))
+ Sub GetMedia()
+ Sub Download()
+ End Interface
+End Namespace
\ No newline at end of file
diff --git a/SCrawler.PluginProvider/Interfaces/ISiteSettings.vb b/SCrawler.PluginProvider/Interfaces/ISiteSettings.vb
new file mode 100644
index 0000000..34ed75c
--- /dev/null
+++ b/SCrawler.PluginProvider/Interfaces/ISiteSettings.vb
@@ -0,0 +1,47 @@
+' Copyright (C) 2022 Andy
+' This program is free software: you can redistribute it and/or modify
+' it under the terms of the GNU General Public License as published by
+' the Free Software Foundation, either version 3 of the License, or
+' (at your option) any later version.
+'
+' This program is distributed in the hope that it will be useful,
+' but WITHOUT ANY WARRANTY
+Imports System.Drawing
+Namespace Plugin
+ Public Interface ISiteSettings
+ Enum Download As Integer
+ Main = 0
+ SavedPosts = 1
+ Channel = 2
+ End Enum
+ ReadOnly Property Icon As Icon
+ ReadOnly Property Image As Image
+ ReadOnly Property Site As String
+ Function GetUserUrl(ByVal UserName As String, ByVal Channel As Boolean) As String
+ Function IsMyUser(ByVal UserURL As String) As ExchangeOptions
+ Function IsMyImageVideo(ByVal URL As String) As ExchangeOptions
+ Function GetSpecialData(ByVal URL As String) As IEnumerable(Of IPluginUserMedia)
+ Function GetInstance(ByVal What As Download) As IPluginContentProvider
+#Region "XML Support"
+ Sub Load(ByVal XMLValues As IEnumerable(Of KeyValuePair(Of String, String)))
+#End Region
+#Region "Initialization"
+ Sub BeginInit()
+ Sub EndInit()
+ Sub BeginUpdate()
+ Sub EndUpdate()
+#End Region
+#Region "Site availability"
+ Function Available(ByVal What As Download) As Boolean
+ Function ReadyToDownload(ByVal What As Download) As Boolean
+#End Region
+#Region "Downloading"
+ Sub DownloadStarted(ByVal What As Download)
+ Sub BeforeStartDownload(ByVal User As Object, ByVal What As Download)
+ Sub AfterDownload(ByVal User As Object, ByVal What As Download)
+ Sub DownloadDone(ByVal What As Download)
+#End Region
+ Sub OpenSettingsForm()
+ Sub UserOptions(ByRef Options As Object, ByVal OpenForm As Boolean)
+ End Interface
+End Namespace
\ No newline at end of file
diff --git a/SCrawler.PluginProvider/My Project/Application.Designer.vb b/SCrawler.PluginProvider/My Project/Application.Designer.vb
new file mode 100644
index 0000000..88dd01c
--- /dev/null
+++ b/SCrawler.PluginProvider/My Project/Application.Designer.vb
@@ -0,0 +1,13 @@
+'------------------------------------------------------------------------------
+'
+' This code was generated by a tool.
+' Runtime Version:4.0.30319.42000
+'
+' Changes to this file may cause incorrect behavior and will be lost if
+' the code is regenerated.
+'
+'------------------------------------------------------------------------------
+
+Option Strict On
+Option Explicit On
+
diff --git a/SCrawler.PluginProvider/My Project/Application.myapp b/SCrawler.PluginProvider/My Project/Application.myapp
new file mode 100644
index 0000000..758895d
--- /dev/null
+++ b/SCrawler.PluginProvider/My Project/Application.myapp
@@ -0,0 +1,10 @@
+
+
+ false
+ false
+ 0
+ true
+ 0
+ 1
+ true
+
diff --git a/SCrawler.PluginProvider/My Project/AssemblyInfo.vb b/SCrawler.PluginProvider/My Project/AssemblyInfo.vb
new file mode 100644
index 0000000..765a1b1
--- /dev/null
+++ b/SCrawler.PluginProvider/My Project/AssemblyInfo.vb
@@ -0,0 +1,37 @@
+Imports System.Resources
+Imports System
+Imports System.Reflection
+Imports System.Runtime.InteropServices
+
+' General Information about an assembly is controlled through the following
+' set of attributes. Change these attribute values to modify the information
+' associated with an assembly.
+
+' Review the values of the assembly attributes
+
+
+
+
+
+
+
+
+
+
+'The following GUID is for the ID of the typelib if this project is exposed to COM
+
+
+' Version information for an assembly consists of the following four values:
+'
+' Major Version
+' Minor Version
+' Build Number
+' Revision
+'
+' You can specify all the values or you can default the Build and Revision Numbers
+' by using the '*' as shown below:
+'
+
+
+
+
diff --git a/SCrawler.PluginProvider/My Project/Resources.Designer.vb b/SCrawler.PluginProvider/My Project/Resources.Designer.vb
new file mode 100644
index 0000000..3d40d22
--- /dev/null
+++ b/SCrawler.PluginProvider/My Project/Resources.Designer.vb
@@ -0,0 +1,63 @@
+'------------------------------------------------------------------------------
+'
+' This code was generated by a tool.
+' Runtime Version:4.0.30319.42000
+'
+' Changes to this file may cause incorrect behavior and will be lost if
+' the code is regenerated.
+'
+'------------------------------------------------------------------------------
+
+Option Strict On
+Option Explicit On
+
+Imports System
+
+Namespace My.Resources
+
+ 'This class was auto-generated by the StronglyTypedResourceBuilder
+ 'class via a tool like ResGen or Visual Studio.
+ 'To add or remove a member, edit your .ResX file then rerun ResGen
+ 'with the /str option, or rebuild your VS project.
+ '''
+ ''' A strongly-typed resource class, for looking up localized strings, etc.
+ '''
+ _
+ Friend Module Resources
+
+ Private resourceMan As Global.System.Resources.ResourceManager
+
+ Private resourceCulture As Global.System.Globalization.CultureInfo
+
+ '''
+ ''' Returns the cached ResourceManager instance used by this class.
+ '''
+ _
+ Friend ReadOnly Property ResourceManager() As Global.System.Resources.ResourceManager
+ Get
+ If Object.ReferenceEquals(resourceMan, Nothing) Then
+ Dim temp As Global.System.Resources.ResourceManager = New Global.System.Resources.ResourceManager("SCrawler.Resources", GetType(Resources).Assembly)
+ resourceMan = temp
+ End If
+ Return resourceMan
+ End Get
+ End Property
+
+ '''
+ ''' Overrides the current thread's CurrentUICulture property for all
+ ''' resource lookups using this strongly typed resource class.
+ '''
+ _
+ Friend Property Culture() As Global.System.Globalization.CultureInfo
+ Get
+ Return resourceCulture
+ End Get
+ Set
+ resourceCulture = value
+ End Set
+ End Property
+ End Module
+End Namespace
diff --git a/SCrawler.PluginProvider/My Project/Resources.resx b/SCrawler.PluginProvider/My Project/Resources.resx
new file mode 100644
index 0000000..af7dbeb
--- /dev/null
+++ b/SCrawler.PluginProvider/My Project/Resources.resx
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/SCrawler.PluginProvider/My Project/Settings.Designer.vb b/SCrawler.PluginProvider/My Project/Settings.Designer.vb
new file mode 100644
index 0000000..fcfd812
--- /dev/null
+++ b/SCrawler.PluginProvider/My Project/Settings.Designer.vb
@@ -0,0 +1,73 @@
+'------------------------------------------------------------------------------
+'
+' This code was generated by a tool.
+' Runtime Version:4.0.30319.42000
+'
+' Changes to this file may cause incorrect behavior and will be lost if
+' the code is regenerated.
+'
+'------------------------------------------------------------------------------
+
+Option Strict On
+Option Explicit On
+
+
+Namespace My
+
+ _
+ Partial Friend NotInheritable Class MySettings
+ Inherits Global.System.Configuration.ApplicationSettingsBase
+
+ Private Shared defaultInstance As MySettings = CType(Global.System.Configuration.ApplicationSettingsBase.Synchronized(New MySettings()),MySettings)
+
+#Region "My.Settings Auto-Save Functionality"
+#If _MyType = "WindowsForms" Then
+ Private Shared addedHandler As Boolean
+
+ Private Shared addedHandlerLockObject As New Object
+
+ _
+ Private Shared Sub AutoSaveSettings(sender As Global.System.Object, e As Global.System.EventArgs)
+ If My.Application.SaveMySettingsOnExit Then
+ My.Settings.Save()
+ End If
+ End Sub
+#End If
+#End Region
+
+ Public Shared ReadOnly Property [Default]() As MySettings
+ Get
+
+#If _MyType = "WindowsForms" Then
+ If Not addedHandler Then
+ SyncLock addedHandlerLockObject
+ If Not addedHandler Then
+ AddHandler My.Application.Shutdown, AddressOf AutoSaveSettings
+ addedHandler = True
+ End If
+ End SyncLock
+ End If
+#End If
+ Return defaultInstance
+ End Get
+ End Property
+ End Class
+End Namespace
+
+Namespace My
+
+ _
+ Friend Module MySettingsProperty
+
+ _
+ Friend ReadOnly Property Settings() As Global.SCrawler.My.MySettings
+ Get
+ Return Global.SCrawler.My.MySettings.Default
+ End Get
+ End Property
+ End Module
+End Namespace
diff --git a/SCrawler.PluginProvider/My Project/Settings.settings b/SCrawler.PluginProvider/My Project/Settings.settings
new file mode 100644
index 0000000..85b890b
--- /dev/null
+++ b/SCrawler.PluginProvider/My Project/Settings.settings
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/SCrawler.PluginProvider/ObjectInterfaces/ILogProvider.vb b/SCrawler.PluginProvider/ObjectInterfaces/ILogProvider.vb
new file mode 100644
index 0000000..8a5ac57
--- /dev/null
+++ b/SCrawler.PluginProvider/ObjectInterfaces/ILogProvider.vb
@@ -0,0 +1,16 @@
+' Copyright (C) 2022 Andy
+' This program is free software: you can redistribute it and/or modify
+' it under the terms of the GNU General Public License as published by
+' the Free Software Foundation, either version 3 of the License, or
+' (at your option) any later version.
+'
+' This program is distributed in the hope that it will be useful,
+' but WITHOUT ANY WARRANTY
+Namespace Plugin
+ Public Interface ILogProvider
+ Overloads Sub Add(ByVal Message As String)
+ Overloads Sub Add(ByVal ex As Exception, ByVal Message As String,
+ Optional ByVal ShowMainMsg As Boolean = False, Optional ByVal ShowErrorMsg As Boolean = False,
+ Optional ByVal SendInLog As Boolean = True)
+ End Interface
+End Namespace
\ No newline at end of file
diff --git a/SCrawler.PluginProvider/ObjectInterfaces/IPluginUserMedia.vb b/SCrawler.PluginProvider/ObjectInterfaces/IPluginUserMedia.vb
new file mode 100644
index 0000000..23954db
--- /dev/null
+++ b/SCrawler.PluginProvider/ObjectInterfaces/IPluginUserMedia.vb
@@ -0,0 +1,40 @@
+' Copyright (C) 2022 Andy
+' This program is free software: you can redistribute it and/or modify
+' it under the terms of the GNU General Public License as published by
+' the Free Software Foundation, either version 3 of the License, or
+' (at your option) any later version.
+'
+' This program is distributed in the hope that it will be useful,
+' but WITHOUT ANY WARRANTY
+Namespace Plugin
+ Public Structure PluginUserMedia : Implements IPluginUserMedia
+ Public Property ContentType As Integer Implements IPluginUserMedia.ContentType
+ Public Property URL As String Implements IPluginUserMedia.URL
+ Public Property MD5 As String Implements IPluginUserMedia.MD5
+ Public Property File As String Implements IPluginUserMedia.File
+ Public Property DownloadState As Integer Implements IPluginUserMedia.DownloadState
+ Public Property PostID As String Implements IPluginUserMedia.PostID
+ Public Property PostDate As Date? Implements IPluginUserMedia.PostDate
+ Public Property SpecialFolder As String Implements IPluginUserMedia.SpecialFolder
+ End Structure
+ Public Interface IPluginUserMedia
+ Enum Types As Integer
+ Undefined = 0
+ [Picture] = 1
+ [Video] = 2
+ [Text] = 3
+ VideoPre = 10
+ GIF = 50
+ m3u8 = 100
+ End Enum
+ Enum States As Integer : Unknown = 0 : Tried = 1 : Downloaded = 2 : Skipped = 3 : End Enum
+ Property ContentType As Integer
+ Property URL As String
+ Property MD5 As String
+ Property File As String
+ Property DownloadState As Integer
+ Property PostID As String
+ Property PostDate As Date?
+ Property SpecialFolder As String
+ End Interface
+End Namespace
\ No newline at end of file
diff --git a/SCrawler.PluginProvider/ObjectInterfaces/IThrower.vb b/SCrawler.PluginProvider/ObjectInterfaces/IThrower.vb
new file mode 100644
index 0000000..f0053f5
--- /dev/null
+++ b/SCrawler.PluginProvider/ObjectInterfaces/IThrower.vb
@@ -0,0 +1,13 @@
+' Copyright (C) 2022 Andy
+' This program is free software: you can redistribute it and/or modify
+' it under the terms of the GNU General Public License as published by
+' the Free Software Foundation, either version 3 of the License, or
+' (at your option) any later version.
+'
+' This program is distributed in the hope that it will be useful,
+' but WITHOUT ANY WARRANTY
+Namespace Plugin
+ Public Interface IThrower
+ Sub ThrowAny()
+ End Interface
+End Namespace
\ No newline at end of file
diff --git a/SCrawler.PluginProvider/Objects/ExchangeOptions.vb b/SCrawler.PluginProvider/Objects/ExchangeOptions.vb
new file mode 100644
index 0000000..f548c07
--- /dev/null
+++ b/SCrawler.PluginProvider/Objects/ExchangeOptions.vb
@@ -0,0 +1,25 @@
+' Copyright (C) 2022 Andy
+' This program is free software: you can redistribute it and/or modify
+' it under the terms of the GNU General Public License as published by
+' the Free Software Foundation, either version 3 of the License, or
+' (at your option) any later version.
+'
+' This program is distributed in the hope that it will be useful,
+' but WITHOUT ANY WARRANTY
+Namespace Plugin
+ Public Structure ExchangeOptions
+ Public UserName As String
+ Public SiteName As String
+ Public HostKey As String
+ Public IsChannel As Boolean
+ Public Exists As Boolean
+ Public Sub New(ByVal Site As String, ByVal _Name As String)
+ UserName = _Name
+ SiteName = Site
+ End Sub
+ Public Sub New(ByVal Site As String, ByVal _Name As String, ByVal _IsChannel As Boolean)
+ Me.New(Site, _Name)
+ IsChannel = _IsChannel
+ End Sub
+ End Structure
+End Namespace
\ No newline at end of file
diff --git a/SCrawler.PluginProvider/Objects/PropertyData.vb b/SCrawler.PluginProvider/Objects/PropertyData.vb
new file mode 100644
index 0000000..4e333f5
--- /dev/null
+++ b/SCrawler.PluginProvider/Objects/PropertyData.vb
@@ -0,0 +1,18 @@
+' Copyright (C) 2022 Andy
+' This program is free software: you can redistribute it and/or modify
+' it under the terms of the GNU General Public License as published by
+' the Free Software Foundation, either version 3 of the License, or
+' (at your option) any later version.
+'
+' This program is distributed in the hope that it will be useful,
+' but WITHOUT ANY WARRANTY
+Namespace Plugin
+ Public Structure PropertyData
+ Public ReadOnly Name As String
+ Public ReadOnly Value As Object
+ Public Sub New(ByVal _Name As String, ByVal _Value As Object)
+ Name = _Name
+ Value = _Value
+ End Sub
+ End Structure
+End Namespace
\ No newline at end of file
diff --git a/SCrawler.PluginProvider/Objects/PropertyValue.vb b/SCrawler.PluginProvider/Objects/PropertyValue.vb
new file mode 100644
index 0000000..c2e5c36
--- /dev/null
+++ b/SCrawler.PluginProvider/Objects/PropertyValue.vb
@@ -0,0 +1,58 @@
+' Copyright (C) 2022 Andy
+' This program is free software: you can redistribute it and/or modify
+' it under the terms of the GNU General Public License as published by
+' the Free Software Foundation, either version 3 of the License, or
+' (at your option) any later version.
+'
+' This program is distributed in the hope that it will be useful,
+' but WITHOUT ANY WARRANTY
+Namespace Plugin
+ Public NotInheritable Class PropertyValue : Implements IPropertyValue
+ Public Event ValueChanged As IPropertyValue.ValueChangedEventHandler Implements IPropertyValue.ValueChanged
+ Public Property [Type] As Type Implements IPropertyValue.Type
+ Public Property OnChangeFunction As IPropertyValue.ValueChangedEventHandler
+ '''
+ '''
+ Public Sub New(ByVal InitValue As Object)
+ _Value = InitValue
+ If IsNothing(InitValue) Then
+ Throw New ArgumentNullException("InitValue", "InitValue cannot be null")
+ Else
+ [Type] = _Value.GetType
+ End If
+ End Sub
+ '''
+ Public Sub New(ByVal InitValue As Object, ByVal T As Type)
+ _Value = InitValue
+ [Type] = T
+ End Sub
+ ''' New property value instance
+ ''' Initialization value
+ ''' Value type
+ ''' CallBack function on value change
+ Public Sub New(ByVal InitValue As Object, ByVal T As Type, ByRef RFunction As IPropertyValue.ValueChangedEventHandler)
+ Me.New(InitValue, T)
+ OnChangeFunction = RFunction
+ End Sub
+ Private _Value As Object
+ Public Property Value As Object Implements IPropertyValue.Value
+ Get
+ Return _Value
+ End Get
+ Set(ByVal NewValue As Object)
+ _Value = NewValue
+ If Not OnChangeFunction Is Nothing Then OnChangeFunction.Invoke(Value)
+ RaiseEvent ValueChanged(_Value)
+ End Set
+ End Property
+ End Class
+ Public Interface IPropertyValue
+ ''' Event for internal exchange
+ ''' New value
+ Event ValueChanged(ByVal Value As Object)
+ ''' Value type
+ Property [Type] As Type
+ ''' Property value
+ Property Value As Object
+ End Interface
+End Namespace
\ No newline at end of file
diff --git a/SCrawler.PluginProvider/SCrawler.PluginProvider.vbproj b/SCrawler.PluginProvider/SCrawler.PluginProvider.vbproj
new file mode 100644
index 0000000..c1cd504
--- /dev/null
+++ b/SCrawler.PluginProvider/SCrawler.PluginProvider.vbproj
@@ -0,0 +1,151 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {D4650F6B-5A54-44B6-999B-6C675B7116B1}
+ Library
+ SCrawler
+ SCrawler.PluginProvider
+ 512
+ Windows
+ v4.6.1
+ true
+
+
+ true
+ full
+ true
+ true
+ bin\Debug\
+
+
+ 42016,41999,42017,42018,42019,42032,42036,42020,42021,42022
+
+
+ pdbonly
+ false
+ true
+ true
+ bin\Release\
+
+
+ 42016,41999,42017,42018,42019,42032,42036,42020,42021,42022
+
+
+ On
+
+
+ Binary
+
+
+ Off
+
+
+ On
+
+
+ true
+ true
+ true
+ bin\x64\Debug\
+ 42016,41999,42017,42018,42019,42032,42036,42020,42021,42022
+ full
+ x64
+
+
+ true
+ bin\x64\Release\
+ true
+ 42016,41999,42017,42018,42019,42032,42036,42020,42021,42022
+ pdbonly
+ x64
+
+
+ true
+ true
+ true
+ bin\x86\Debug\
+ 42016,41999,42017,42018,42019,42032,42036,42020,42021,42022
+ full
+ x86
+
+
+ true
+ bin\x86\Release\
+ true
+ 42016,41999,42017,42018,42019,42032,42036,42020,42021,42022
+ pdbonly
+ x86
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ Application.myapp
+ True
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ Settings.settings
+ True
+
+
+
+
+
+
+ VbMyResourcesResXFileCodeGenerator
+ Resources.Designer.vb
+ My.Resources
+ Designer
+
+
+
+
+
+ MyApplicationCodeGenerator
+ Application.Designer.vb
+
+
+ SettingsSingleFileGenerator
+ My
+ Settings.Designer.vb
+
+
+
+
\ No newline at end of file
diff --git a/SCrawler.sln b/SCrawler.sln
index b95a325..4b07324 100644
--- a/SCrawler.sln
+++ b/SCrawler.sln
@@ -15,6 +15,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
ToDo.txt = ToDo.txt
EndProjectSection
EndProject
+Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "SCrawler.PluginProvider", "SCrawler.PluginProvider\SCrawler.PluginProvider.vbproj", "{D4650F6B-5A54-44B6-999B-6C675B7116B1}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -49,6 +51,18 @@ Global
{8405896B-2685-4916-BC93-1FB514C323A9}.Release|x64.Build.0 = Release|x64
{8405896B-2685-4916-BC93-1FB514C323A9}.Release|x86.ActiveCfg = Release|x86
{8405896B-2685-4916-BC93-1FB514C323A9}.Release|x86.Build.0 = Release|x86
+ {D4650F6B-5A54-44B6-999B-6C675B7116B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D4650F6B-5A54-44B6-999B-6C675B7116B1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D4650F6B-5A54-44B6-999B-6C675B7116B1}.Debug|x64.ActiveCfg = Debug|x64
+ {D4650F6B-5A54-44B6-999B-6C675B7116B1}.Debug|x64.Build.0 = Debug|x64
+ {D4650F6B-5A54-44B6-999B-6C675B7116B1}.Debug|x86.ActiveCfg = Debug|x86
+ {D4650F6B-5A54-44B6-999B-6C675B7116B1}.Debug|x86.Build.0 = Debug|x86
+ {D4650F6B-5A54-44B6-999B-6C675B7116B1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D4650F6B-5A54-44B6-999B-6C675B7116B1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D4650F6B-5A54-44B6-999B-6C675B7116B1}.Release|x64.ActiveCfg = Release|x64
+ {D4650F6B-5A54-44B6-999B-6C675B7116B1}.Release|x64.Build.0 = Release|x64
+ {D4650F6B-5A54-44B6-999B-6C675B7116B1}.Release|x86.ActiveCfg = Release|x86
+ {D4650F6B-5A54-44B6-999B-6C675B7116B1}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/SCrawler/API/Base/DownDetector.vb b/SCrawler/API/Base/DownDetector.vb
new file mode 100644
index 0000000..d3ed796
--- /dev/null
+++ b/SCrawler/API/Base/DownDetector.vb
@@ -0,0 +1,60 @@
+' Copyright (C) 2022 Andy
+' This program is free software: you can redistribute it and/or modify
+' it under the terms of the GNU General Public License as published by
+' the Free Software Foundation, either version 3 of the License, or
+' (at your option) any later version.
+'
+' This program is distributed in the hope that it will be useful,
+' but WITHOUT ANY WARRANTY
+Imports System.Net
+Imports PersonalUtilities.Functions.RegularExpressions
+Namespace API.Base
+ Friend NotInheritable Class DownDetector
+ Private Shared ReadOnly Property Params As New RParams("x:.'([\S]+?)',.y:.(\d+)", -1, Nothing, RegexReturn.List)
+ Private Sub New()
+ End Sub
+ Friend Structure Data : Implements IRegExCreator, IComparable(Of Data)
+ Friend [Date] As Date
+ Friend Value As Integer
+ Friend Function CreateFromArray(ByVal ParamsArray() As String) As Object Implements IRegExCreator.CreateFromArray
+ If ParamsArray.ListExists Then
+ Try : [Date] = Date.Parse(ParamsArray(0)) : Catch : End Try
+ If ParamsArray.Length > 1 Then Value = AConvert(Of Integer)(ParamsArray(1), 0)
+ End If
+ Return Me
+ End Function
+ Public Overrides Function ToString() As String
+ Return $"{AConvert(Of String)([Date], ADateTime.Formats.BaseDateTime, String.Empty)} [{Value}]"
+ End Function
+ Friend Function CompareTo(ByVal Other As Data) As Integer Implements IComparable(Of Data).CompareTo
+ Return [Date].CompareTo(Other.Date) * -1
+ End Function
+ End Structure
+ Friend Shared Function GetData(ByVal Site As String) As List(Of Data)
+ Try
+ Dim l As List(Of Data) = Nothing
+ Using w As New WebClient
+ Dim r$ = w.DownloadString($"https://downdetector.co.uk/status/{Site}/")
+ If Not r.IsEmptyString Then
+ l = FNF.RegexFields(Of Data)(r, {Params}, {1, 2})
+ If l.ListExists(2) Then
+ Dim lDate As Date = l(0).Date
+ Dim i%
+ Dim indx% = -1
+ For i = 1 To l.Count - 1
+ If l(i).Date < lDate Then indx = i : Exit For Else lDate = l(i).Date
+ Next
+ If indx >= 0 Then
+ For i = indx To 0 Step -1 : l.RemoveAt(i) : Next
+ End If
+ l.Sort()
+ End If
+ End If
+ End Using
+ Return l
+ Catch ex As Exception
+ Return ErrorsDescriber.Execute(EDP.SendInLog + EDP.ReturnValue, ex, $"[DownDetector.GetData({Site})]")
+ End Try
+ End Function
+ End Class
+End Namespace
\ No newline at end of file
diff --git a/SCrawler/API/Base/ProfileSaved.vb b/SCrawler/API/Base/ProfileSaved.vb
new file mode 100644
index 0000000..842c0ac
--- /dev/null
+++ b/SCrawler/API/Base/ProfileSaved.vb
@@ -0,0 +1,61 @@
+' Copyright (C) 2022 Andy
+' This program is free software: you can redistribute it and/or modify
+' it under the terms of the GNU General Public License as published by
+' the Free Software Foundation, either version 3 of the License, or
+' (at your option) any later version.
+'
+' This program is distributed in the hope that it will be useful,
+' but WITHOUT ANY WARRANTY
+Imports SCrawler.Plugin.Hosts
+Imports System.Threading
+Imports PersonalUtilities.Forms.Toolbars
+Imports PDownload = SCrawler.Plugin.ISiteSettings.Download
+Namespace API.Base
+ Friend NotInheritable Class ProfileSaved
+ Private ReadOnly Property HOST As SettingsHost
+ Private ReadOnly Property Progress As MyProgress
+ Friend Sub New(ByRef h As SettingsHost, ByRef Bar As MyProgress)
+ HOST = h
+ Progress = Bar
+ End Sub
+ Friend Sub Download(ByVal Token As CancellationToken)
+ Try
+ If HOST.Source.ReadyToDownload(PDownload.SavedPosts) Then
+ If HOST.Available(PDownload.SavedPosts) Then
+ HOST.DownloadStarted(PDownload.SavedPosts)
+ Dim u As New UserInfo With {.Plugin = HOST.Key, .Site = HOST.Name, .SpecialPath = HOST.SavedPostsPath}
+ Using user As IUserData = HOST.GetInstance(PDownload.SavedPosts, Nothing, False, False)
+ If Not user Is Nothing AndAlso (Not user.Name.IsEmptyString Or Not HOST.IsMyClass) Then
+ u.Name = user.Name
+ With DirectCast(user, UserDataBase).User
+ u.IsChannel = .IsChannel
+ u.UpdateUserFile()
+ End With
+ With DirectCast(user, UserDataBase)
+ .User = u
+ .LoadUserInformation()
+ .IsSavedPosts = True
+ .Progress = Progress
+ If Not .FileExists Then .UpdateUserInformation()
+ End With
+ HOST.BeforeStartDownload(user, PDownload.SavedPosts)
+ user.DownloadData(Token)
+ Progress.InformationTemporary = $"Images: {user.DownloadedPictures(False)}; Videos: {user.DownloadedVideos(False)}"
+ HOST.AfterDownload(user, PDownload.SavedPosts)
+ End If
+ End Using
+ Else
+ Progress.InformationTemporary = $"Host [{HOST.Name}] is unavailable"
+ End If
+ Else
+ Progress.InformationTemporary = $"Host [{HOST.Name}] is nor ready"
+ End If
+ Catch ex As Exception
+ Progress.InformationTemporary = $"{HOST.Name} downloading error"
+ ErrorsDescriber.Execute(EDP.SendInLog, ex, $"[API.Base.ProfileSaved.Download({HOST.Key})]")
+ Finally
+ HOST.DownloadDone(PDownload.SavedPosts)
+ End Try
+ End Sub
+ End Class
+End Namespace
\ No newline at end of file
diff --git a/SCrawler/API/Base/SiteSettings.vb b/SCrawler/API/Base/SiteSettings.vb
deleted file mode 100644
index c7badfd..0000000
--- a/SCrawler/API/Base/SiteSettings.vb
+++ /dev/null
@@ -1,239 +0,0 @@
-' Copyright (C) 2022 Andy
-' This program is free software: you can redistribute it and/or modify
-' it under the terms of the GNU General Public License as published by
-' the Free Software Foundation, either version 3 of the License, or
-' (at your option) any later version.
-'
-' This program is distributed in the hope that it will be useful,
-' but WITHOUT ANY WARRANTY
-Imports PersonalUtilities.Tools
-Imports PersonalUtilities.Functions.XML
-Imports PersonalUtilities.Functions.XML.Base
-Imports PersonalUtilities.Functions.RegularExpressions
-Namespace API.Base
- Friend Class SiteSettings : Implements IDisposable
- Friend Const Header_Twitter_Authorization As String = "authorization"
- Friend Const Header_Twitter_Token As String = "x-csrf-token"
- Friend ReadOnly Site As Sites
- Friend ReadOnly Responser As WEB.Response
- Private ReadOnly _Path As XMLValue(Of SFile)
- Friend Property Path(Optional ByVal SetProp As Boolean = True) As SFile
- Get
- If _Path.IsEmptyString Then
- Dim tmpPath As SFile = SFile.GetPath($"{Settings.GlobalPath.Value.PathWithSeparator}{Site}")
- If SetProp Then _Path.Value = tmpPath Else Return tmpPath
- End If
- Return _Path.Value
- End Get
- Set(ByVal NewPath As SFile)
- _Path.Value = NewPath
- End Set
- End Property
- Private ReadOnly _SavedPostsPath As XMLValue(Of SFile)
- Friend Property SavedPostsPath(Optional ByVal GetAny As Boolean = True) As SFile
- Get
- If Not _SavedPostsPath.Value.IsEmptyString Then
- Return _SavedPostsPath.Value
- Else
- If GetAny Then
- Return $"{Path.PathNoSeparator}\!Saved\"
- Else
- Return Nothing
- End If
- End If
- End Get
- Set(ByVal NewPath As SFile)
- _SavedPostsPath.Value = NewPath
- End Set
- End Property
-#Region "Instagram"
- Friend ReadOnly Property InstaHash As XMLValue(Of String)
- Friend ReadOnly Property InstaHash_SP As XMLValue(Of String)
- Friend ReadOnly Property InstaHashUpdateRequired As XMLValue(Of Boolean)
- Friend ReadOnly Property InstagramDownloadingErrorDate As XMLValue(Of Date)
- Friend Property InstagramLastApplyingValue As Integer? = Nothing
- Friend ReadOnly Property InstagramReadyForDownload As Boolean
- Get
- With InstagramDownloadingErrorDate
- If .ValueF.Exists Then
- Return .ValueF.Value.AddMinutes(If(InstagramLastApplyingValue, 10)) < Now
- Else
- Return True
- End If
- End With
- End Get
- End Property
- Friend ReadOnly Property InstagramLastDownloadDate As XMLValue(Of Date)
- Friend ReadOnly Property InstagramLastRequestsCount As XMLValue(Of Integer)
- Private InstagramTooManyRequestsReadyForCatch As Boolean = True
- Friend Function GetInstaWaitDate() As Date
- With InstagramDownloadingErrorDate
- If .ValueF.Exists Then
- Return .ValueF.Value.AddMinutes(If(InstagramLastApplyingValue, 10))
- Else
- Return Now
- End If
- End With
- End Function
- Friend Sub InstagramTooManyRequests(ByVal Catched As Boolean)
- With InstagramDownloadingErrorDate
- If Catched Then
- If Not .ValueF.Exists Then
- .Value = Now
- If InstagramTooManyRequestsReadyForCatch Then
- InstagramLastApplyingValue = If(InstagramLastApplyingValue, 0) + 10
- InstagramTooManyRequestsReadyForCatch = False
- MyMainLOG = $"Instagram downloading error: too many requests. Try again after {If(InstagramLastApplyingValue, 10)} minutes..."
- End If
- End If
- Else
- .ValueF = Nothing
- InstagramLastApplyingValue = Nothing
- InstagramTooManyRequestsReadyForCatch = True
- End If
- End With
- End Sub
- Friend ReadOnly Property RequestsWaitTimer As XMLValue(Of Integer)
- Friend ReadOnly Property RequestsWaitTimerTaskCount As XMLValue(Of Integer)
- Friend ReadOnly Property SleepTimerOnPostsLimit As XMLValue(Of Integer)
-#End Region
- Friend ReadOnly Property Temporary As XMLValue(Of Boolean)
- Friend ReadOnly Property DownloadImages As XMLValue(Of Boolean)
- Friend ReadOnly Property DownloadVideos As XMLValue(Of Boolean)
- Friend ReadOnly Property GetUserMediaOnly As XMLValue(Of Boolean)
- Friend ReadOnly Property SavedPostsUserName As XMLValue(Of String)
- Private ReadOnly SettingsFile As SFile
- Friend Sub New(ByVal s As Sites, 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))
- Site = s
- SettingsFile = $"{SettingsFolderName}\Responser_{s}.xml"
- Responser = New WEB.Response(SettingsFile)
-
- If SettingsFile.Exists Then
- Responser.LoadSettings()
- Else
- Select Case Site
- Case Sites.Twitter
- With Responser
- .ContentType = "application/json"
- .Accept = "*/*"
- .CookiesDomain = "twitter.com"
- .Decoders.Add(SymbolsConverter.Converters.Unicode)
- With .Headers
- .Add("sec-ch-ua", " Not;A Brand" & Chr(34) & ";v=" & Chr(34) & "99" & Chr(34) & ", " & Chr(34) &
- "Google Chrome" & Chr(34) & ";v=" & Chr(34) & "91" & Chr(34) & ", " & Chr(34) & "Chromium" &
- Chr(34) & ";v=" & Chr(34) & "91" & Chr(34))
- .Add("sec-ch-ua-mobile", "?0")
- .Add("sec-fetch-dest", "empty")
- .Add("sec-fetch-mode", "cors")
- .Add("sec-fetch-site", "same-origin")
- .Add(Header_Twitter_Token, String.Empty)
- .Add("x-twitter-active-user", "yes")
- .Add("x-twitter-auth-type", "OAuth2Session")
- .Add(Header_Twitter_Authorization, String.Empty)
- End With
- End With
- Case Sites.Reddit
- Responser.CookiesDomain = "reddit.com"
- Responser.Decoders.Add(SymbolsConverter.Converters.Unicode)
- Case Sites.Instagram : Responser.CookiesDomain = "instagram.com"
- Case Sites.RedGifs : Responser.CookiesDomain = "redgifs.com"
- End Select
- Responser.SaveSettings()
- End If
-
- Dim n() As String = {SettingsCLS.Name_Node_Sites, Site.ToString}
- _Path = New XMLValue(Of SFile)("Path",, _XML, n, XMLValue(Of SFile).ToFilePath)
- _XML.Remove(Site.ToString)
-
- Temporary = New XMLValue(Of Boolean)
- Temporary.SetExtended("Temporary", False, _XML, n)
- Temporary.SetDefault(_Temp)
-
- DownloadImages = New XMLValue(Of Boolean)
- DownloadImages.SetExtended("DownloadImages", True, _XML, n)
- DownloadImages.SetDefault(_Imgs)
-
- DownloadVideos = New XMLValue(Of Boolean)
- DownloadVideos.SetExtended("DownloadVideos", True, _XML, n)
- DownloadVideos.SetDefault(_Vids)
-
- GetUserMediaOnly = New XMLValue(Of Boolean)("GetUserMediaOnly", True, _XML, n)
- _SavedPostsPath = New XMLValue(Of SFile)("SavedPostsPath",, _XML, n, XMLValue(Of SFile).ToFilePath)
-
- CreateProp(InstaHashUpdateRequired, Sites.Instagram, "InstaHashUpdateRequired", True, _XML, n)
- CreateProp(InstaHash, Sites.Instagram, "InstaHash", String.Empty, _XML, n)
- If Site = Sites.Instagram AndAlso (InstaHash.IsEmptyString Or InstaHashUpdateRequired) AndAlso Responser.Cookies.ListExists Then GatherInstaHash()
- CreateProp(InstaHash_SP, Sites.Instagram, "InstaHashSavedPosts", String.Empty, _XML, n)
- CreateProp(InstagramLastDownloadDate, Sites.Instagram, "LastDownloadDate", Now.AddDays(-1), _XML, n)
- CreateProp(InstagramLastRequestsCount, Sites.Instagram, "LastRequestsCount", 0, _XML, n)
- CreateProp(RequestsWaitTimer, Sites.Instagram, "RequestsWaitTimer", 1000, _XML, n)
- CreateProp(RequestsWaitTimerTaskCount, Sites.Instagram, "RequestsWaitTimerTaskCount", 1, _XML, n)
- CreateProp(SleepTimerOnPostsLimit, Sites.Instagram, "SleepTimerOnPostsLimit", 60000, _XML, n)
- If Site = Sites.Instagram Then
- InstagramDownloadingErrorDate = New XMLValue(Of Date) With {.ToStringFunction = Function(ss, vv) AConvert(Of String)(vv, AModes.Var, Nothing)}
- InstagramDownloadingErrorDate.SetExtended("InstagramDownloadingErrorDate", Now.AddYears(-10), _XML, n)
- Else
- InstagramDownloadingErrorDate = New XMLValue(Of Date)
- End If
-
- SavedPostsUserName = New XMLValue(Of String)("SavedPostsUserName", String.Empty, _XML, n)
- End Sub
- Private Sub CreateProp(Of T)(ByRef p As XMLValue(Of T), ByVal s As Sites,
- ByVal p_Name As String, ByVal p_Value As T, ByRef x As XmlFile, ByVal n() As String)
- If Site = s Then
- p = New XMLValue(Of T)(p_Name, p_Value, x, n)
- Else
- p = New XMLValue(Of T)
- End If
- End Sub
- Friend Sub Update()
- Responser.SaveSettings()
- End Sub
- Friend Function GatherInstaHash() As Boolean
- Try
- Dim rs As New RParams("=""([^""]+?ConsumerLibCommons[^""]+?.js)""", Nothing, 1) With {.MatchTimeOut = 10}
- Dim r$ = Responser.GetResponse("https://instagram.com",, EDP.ThrowException)
- If Not r.IsEmptyString Then
- Dim hStr$ = RegexReplace(r, rs)
- If Not hStr.IsEmptyString Then
- Do While Left(hStr, 1) = "/" : hStr = Right(hStr, hStr.Length - 1) : Loop
- hStr = $"https://instagram.com/{hStr}"
- r = Responser.GetResponse(hStr,, EDP.ThrowException)
- If Not r.IsEmptyString Then
- rs = New RParams("generatePaginationActionCreators.+?.profilePosts.byUserId.get.+?queryId:.([\d\w\S]+?)""", Nothing, 1) With {.MatchTimeOut = 10}
- Dim h$ = RegexReplace(r, rs)
- If Not h.IsEmptyString Then
- InstaHash.Value = h
- InstaHashUpdateRequired.Value = False
- Return True
- End If
- End If
- End If
- End If
- Return False
- Catch ex As Exception
- InstaHashUpdateRequired.Value = True
- InstaHash.Value = String.Empty
- Return ErrorsDescriber.Execute(EDP.SendInLog + EDP.ReturnValue, ex, "[SiteSettings.GaterInstaHash]", False)
- End Try
- End Function
-#Region "IDisposable Support"
- Private disposedValue As Boolean = False
- Protected Overridable Overloads Sub Dispose(ByVal disposing As Boolean)
- If Not disposedValue Then
- If disposing Then Responser.Dispose()
- disposedValue = True
- End If
- End Sub
- Protected Overrides Sub Finalize()
- Dispose(False)
- MyBase.Finalize()
- End Sub
- Friend Overloads Sub Dispose() Implements IDisposable.Dispose
- Dispose(True)
- GC.SuppressFinalize(Me)
- End Sub
-#End Region
- End Class
-End Namespace
\ No newline at end of file
diff --git a/SCrawler/API/Base/SiteSettingsBase.vb b/SCrawler/API/Base/SiteSettingsBase.vb
new file mode 100644
index 0000000..241435b
--- /dev/null
+++ b/SCrawler/API/Base/SiteSettingsBase.vb
@@ -0,0 +1,106 @@
+' Copyright (C) 2022 Andy
+' This program is free software: you can redistribute it and/or modify
+' it under the terms of the GNU General Public License as published by
+' the Free Software Foundation, either version 3 of the License, or
+' (at your option) any later version.
+'
+' This program is distributed in the hope that it will be useful,
+' but WITHOUT ANY WARRANTY
+Imports SCrawler.Plugin
+Imports PersonalUtilities.Functions.RegularExpressions
+Imports PersonalUtilities.Tools.WEB
+Imports Download = SCrawler.Plugin.ISiteSettings.Download
+Namespace API.Base
+ Friend MustInherit Class SiteSettingsBase : Implements ISiteSettings, IResponserContainer
+ Friend ReadOnly Property Site As String Implements ISiteSettings.Site
+ Friend Overridable ReadOnly Property Icon As Icon = Nothing Implements ISiteSettings.Icon
+ Friend Overridable ReadOnly Property Image As Image = Nothing Implements ISiteSettings.Image
+ Friend Overridable ReadOnly Property Responser As Response Implements IResponserContainer.Responser
+ Friend MustOverride Function GetInstance(ByVal What As Download) As IPluginContentProvider Implements ISiteSettings.GetInstance
+ Friend Sub New(ByVal SiteName As String)
+ Site = SiteName
+ End Sub
+ Friend Sub New(ByVal SiteName As String, ByVal CookiesDomain As String)
+ Site = SiteName
+ Responser = New Response($"{SettingsFolderName}\Responser_{Site}.xml")
+ With Responser
+ If .File.Exists Then .LoadSettings() Else .CookiesDomain = CookiesDomain : .SaveSettings()
+ End With
+ End Sub
+#Region "XML"
+ Friend Overridable Sub Load(ByVal XMLValues As IEnumerable(Of KeyValuePair(Of String, String))) Implements ISiteSettings.Load
+ End Sub
+#End Region
+#Region "Initialize"
+ Friend Overridable Sub BeginInit() Implements ISiteSettings.BeginInit
+ End Sub
+ Friend Overridable Sub EndInit() Implements ISiteSettings.EndInit
+ End Sub
+ Friend Overridable Sub BeginUpdate() Implements ISiteSettings.BeginUpdate
+ End Sub
+ Friend Overridable Sub EndUpdate() Implements ISiteSettings.EndUpdate
+ End Sub
+#End Region
+#Region "Before and After Download"
+ 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
+#Region "User info"
+ Protected UrlPatternUser As String = String.Empty
+ Protected UrlPatternChannel As String = String.Empty
+ Friend Overridable Function GetUserUrl(ByVal UserName As String, ByVal Channel As Boolean) As String Implements ISiteSettings.GetUserUrl
+ If Channel Then
+ If Not UrlPatternChannel.IsEmptyString Then Return String.Format(UrlPatternChannel, UserName)
+ Else
+ If Not UrlPatternUser.IsEmptyString Then Return String.Format(UrlPatternUser, UserName)
+ End If
+ Return String.Empty
+ End Function
+ Protected UserRegex As RParams = Nothing
+ Friend Overridable Function IsMyUser(ByVal UserURL As String) As ExchangeOptions Implements ISiteSettings.IsMyUser
+ Try
+ If Not UserRegex Is Nothing Then
+ Dim s$ = RegexReplace(UserURL, UserRegex)
+ If Not s.IsEmptyString Then Return New ExchangeOptions(Site, s)
+ End If
+ Return Nothing
+ Catch ex As Exception
+ Return ErrorsDescriber.Execute(EDP.SendInLog + EDP.ReturnValue, ex, "[API.Base.SiteSettingsBase.IsMyUser]")
+ End Try
+ End Function
+ Protected ImageVideoContains As String = String.Empty
+ Friend Overridable Function IsMyImageVideo(ByVal URL As String) As ExchangeOptions Implements ISiteSettings.IsMyImageVideo
+ If Not ImageVideoContains.IsEmptyString AndAlso URL.Contains(ImageVideoContains) Then
+ Return New ExchangeOptions With {.Exists = True}
+ Else
+ Return Nothing
+ End If
+ End Function
+ Friend Overridable Function GetSpecialData(ByVal URL As String) As IEnumerable(Of IPluginUserMedia) Implements ISiteSettings.GetSpecialData
+ Return Nothing
+ End Function
+ Friend Overridable Function GetSpecialDataF(ByVal URL As String) As IEnumerable(Of UserMedia)
+ Return Nothing
+ End Function
+#End Region
+#Region "Ready, Available"
+ Friend Overridable Function Available(ByVal What As Download) As Boolean Implements ISiteSettings.Available
+ Return True
+ End Function
+ Friend Overridable Function ReadyToDownload(ByVal What As Download) As Boolean Implements ISiteSettings.ReadyToDownload
+ Return True
+ End Function
+#End Region
+ Friend Overridable Sub UserOptions(ByRef Options As Object, ByVal OpenForm As Boolean) Implements ISiteSettings.UserOptions
+ Options = Nothing
+ End Sub
+ Friend Overridable Sub OpenSettingsForm() Implements ISiteSettings.OpenSettingsForm
+ End Sub
+ End Class
+End Namespace
\ No newline at end of file
diff --git a/SCrawler/API/Base/Structures.vb b/SCrawler/API/Base/Structures.vb
index 41657ad..eaba86a 100644
--- a/SCrawler/API/Base/Structures.vb
+++ b/SCrawler/API/Base/Structures.vb
@@ -18,8 +18,8 @@ Namespace API.Base
GIF = 50
m3u8 = 100
End Enum
- Friend Enum States : Unknown : Tried : Downloaded : Skipped : End Enum
- Friend Type As Types
+ Friend Enum States As Integer : Unknown = 0 : Tried = 1 : Downloaded = 2 : Skipped = 3 : End Enum
+ Friend [Type] As Types
Friend URL_BASE As String
Friend URL As String
Friend MD5 As String
@@ -27,6 +27,11 @@ Namespace API.Base
Friend Post As UserPost
Friend PictureOption As String
Friend State As States
+ '''
+ ''' SomeFolder
+ ''' SomeFolder\SomeFolder2
+ '''
+ Friend SpecialFolder As String
Friend Sub New(ByVal _URL As String)
URL = _URL
URL_BASE = _URL
@@ -35,7 +40,18 @@ Namespace API.Base
End Sub
Friend Sub New(ByVal _URL As String, ByVal _Type As Types)
Me.New(_URL)
- Type = _Type
+ [Type] = _Type
+ End Sub
+ Friend Sub New(ByVal m As Plugin.IPluginUserMedia)
+ If Not IsNothing(m) Then
+ [Type] = m.ContentType
+ URL = m.URL
+ MD5 = m.MD5
+ File = m.File
+ Post = New UserPost With {.ID = m.PostID, .[Date] = m.PostDate}
+ State = m.DownloadState
+ SpecialFolder = m.SpecialFolder
+ End If
End Sub
Public Shared Widening Operator CType(ByVal _URL As String) As UserMedia
Return New UserMedia(_URL)
@@ -46,6 +62,18 @@ Namespace API.Base
Public Overrides Function ToString() As String
Return URL
End Function
+ Friend Function PluginUserMedia() As Plugin.PluginUserMedia
+ Return New Plugin.PluginUserMedia With {
+ .ContentType = Type,
+ .DownloadState = State,
+ .File = File,
+ .MD5 = MD5,
+ .URL = URL,
+ .SpecialFolder = SpecialFolder,
+ .PostID = Post.ID,
+ .PostDate = Post.Date
+ }
+ End Function
Friend Overloads Function Equals(ByVal Other As UserMedia) As Boolean Implements IEquatable(Of UserMedia).Equals
Return URL = Other.URL
End Function
diff --git a/SCrawler/API/Base/UserDataBase.vb b/SCrawler/API/Base/UserDataBase.vb
index 25e4593..c4fb2c8 100644
--- a/SCrawler/API/Base/UserDataBase.vb
+++ b/SCrawler/API/Base/UserDataBase.vb
@@ -12,35 +12,80 @@ Imports PersonalUtilities.Forms.Toolbars
Imports System.IO
Imports System.Net
Imports System.Threading
+Imports SCrawler.Plugin
+Imports SCrawler.Plugin.Hosts
Imports UStates = SCrawler.API.Base.UserMedia.States
Imports UTypes = SCrawler.API.Base.UserMedia.Types
Namespace API.Base
- Friend MustInherit Class UserDataBase : Implements IUserData
+ Friend MustInherit Class UserDataBase : Implements IUserData, IPluginContentProvider, IThrower
Friend Const UserFileAppender As String = "User"
- Friend Event OnUserUpdated As IUserData.OnUserUpdatedEventHandler Implements IUserData.OnUserUpdated
- Protected Sub Raise_OnUserUpdated()
+ Private ReadOnly _OnUserUpdatedHandlers As List(Of IUserData.OnUserUpdatedEventHandler)
+ Friend Custom Event OnUserUpdated As IUserData.OnUserUpdatedEventHandler Implements IUserData.OnUserUpdated
+ AddHandler(ByVal e As IUserData.OnUserUpdatedEventHandler)
+ If Not _OnUserUpdatedHandlers.Contains(e) Then _OnUserUpdatedHandlers.Add(e)
+ End AddHandler
+ RemoveHandler(ByVal e As IUserData.OnUserUpdatedEventHandler)
+ If _OnUserUpdatedHandlers.Contains(e) Then _OnUserUpdatedHandlers.Remove(e)
+ End RemoveHandler
+ RaiseEvent(ByVal User As IUserData)
+ If _OnUserUpdatedHandlers.Count > 0 Then
+ For Each e As IUserData.OnUserUpdatedEventHandler In _OnUserUpdatedHandlers
+ Try : e.Invoke(User) : Catch : End Try
+ Next
+ End If
+ End RaiseEvent
+ End Event
+ Protected Sub RaiseEvent_OnUserUpdated()
RaiseEvent OnUserUpdated(Me)
End Sub
+ Friend Sub RemoveUpdateHandlers()
+ _OnUserUpdatedHandlers.Clear()
+ End Sub
#Region "Collection buttons"
+ Private _CollectionButtonsExists As Boolean = False
+ Private _CollectionButtonsColorsSet As Boolean = False
+ Friend InternalCollectionIndex As Integer = -1
Friend WithEvents BTT_CONTEXT_DOWN As ToolStripMenuItem
Friend WithEvents BTT_CONTEXT_EDIT As ToolStripMenuItem
Friend WithEvents BTT_CONTEXT_DELETE As ToolStripMenuItem
Friend WithEvents BTT_CONTEXT_OPEN_PATH As ToolStripMenuItem
Friend WithEvents BTT_CONTEXT_OPEN_SITE As ToolStripMenuItem
Friend Sub CreateButtons(ByVal CollectionIndex As Integer)
+ InternalCollectionIndex = CollectionIndex
Dim tn$ = $"[{Site}] - {Name}"
Dim _tn$ = $"{Site}{Name}"
Dim tnn As Func(Of String, String) = Function(Input) $"{Input}{_tn}"
Dim i As Image = Nothing
- Select Case Site
- Case Sites.Reddit : i = My.Resources.RedditIcon.ToBitmap
- Case Sites.Twitter : i = My.Resources.TwitterIcon.ToBitmap
- End Select
+ With HOST.Source
+ If Not .Icon Is Nothing Then
+ i = .Icon.ToBitmap
+ ElseIf Not .Image Is Nothing Then
+ i = .Image
+ End If
+ End With
BTT_CONTEXT_DOWN = New ToolStripMenuItem(tn, i) With {.Name = tnn("DOWN"), .Tag = CollectionIndex}
BTT_CONTEXT_EDIT = New ToolStripMenuItem(tn, i) With {.Name = tnn("EDIT"), .Tag = CollectionIndex}
BTT_CONTEXT_DELETE = New ToolStripMenuItem(tn, i) With {.Name = tnn("DELETE"), .Tag = CollectionIndex}
BTT_CONTEXT_OPEN_PATH = New ToolStripMenuItem(tn, i) With {.Name = tnn("PATH"), .Tag = CollectionIndex}
BTT_CONTEXT_OPEN_SITE = New ToolStripMenuItem(tn, i) With {.Name = tnn("SITE"), .Tag = CollectionIndex}
+ UpdateButtonsColor()
+ _CollectionButtonsExists = True
+ If _UserInformationLoaded Then _CollectionButtonsColorsSet = True
+ End Sub
+ Private Sub UpdateButtonsColor()
+ Dim cb As Color = SystemColors.Control
+ Dim cf As Color = SystemColors.ControlText
+ If Not UserExists Then
+ cb = ColorBttDeleteBack
+ cf = ColorBttDeleteFore
+ ElseIf UserSuspended Then
+ cb = ColorBttEditBack
+ cf = ColorBttEditFore
+ 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}
+ If Not b Is Nothing Then b.BackColor = cb : b.ForeColor = cf
+ Next
+ If _UserInformationLoaded Then _CollectionButtonsColorsSet = True
End Sub
#End Region
#Region "XML Declarations"
@@ -80,21 +125,17 @@ Namespace API.Base
#End Region
#End Region
#Region "Declarations"
- Friend MustOverride Property Site As Sites Implements IContentProvider.Site
- Protected _Progress As MyProgress
- Friend Overridable Property Progress As MyProgress
+ Friend ReadOnly Property Site As String Implements IContentProvider.Site
Get
- If _Progress Is Nothing Then Return MainProgress Else Return _Progress
+ Return HOST.Name
End Get
- Set(ByVal p As MyProgress)
- _Progress = p
- End Set
End Property
+ Friend Property Progress As MyProgress
Friend User As UserInfo
- Friend Property IsSavedPosts As Boolean
- Friend Overridable Property UserExists As Boolean = True Implements IUserData.Exists
- Friend Overridable Property UserSuspended As Boolean = False Implements IUserData.Suspended
- Friend Overridable Property Name As String Implements IContentProvider.Name
+ Friend Property IsSavedPosts As Boolean Implements IPluginContentProvider.IsSavedPosts
+ Friend Overridable Property UserExists As Boolean = True Implements IUserData.Exists, IPluginContentProvider.UserExists
+ Friend Overridable Property UserSuspended As Boolean = False Implements IUserData.Suspended, IPluginContentProvider.UserSuspended
+ Friend Overridable Property Name As String Implements IContentProvider.Name, IPluginContentProvider.Name
Get
Return User.Name
End Get
@@ -104,10 +145,31 @@ Namespace API.Base
Settings.UpdateUsersList(User)
End Set
End Property
- Friend Overridable Property ID As String = String.Empty Implements IContentProvider.ID
+ Friend Overridable Property ID As String = String.Empty Implements IContentProvider.ID, IPluginContentProvider.ID
Friend Overridable Property FriendlyName As String = String.Empty Implements IContentProvider.FriendlyName
- Friend Property UserDescription As String = String.Empty Implements IContentProvider.Description
- Friend Property ParseUserMediaOnly As Boolean = False Implements IUserData.ParseUserMediaOnly
+#Region "UserDescription"
+ Friend Property UserDescription As String = String.Empty Implements IContentProvider.Description, IPluginContentProvider.UserDescription
+ Protected _DescriptionEveryTime As Boolean = False
+ Protected _DescriptionChecked As Boolean = False
+ Protected Function UserDescriptionNeedToUpdate() As Boolean
+ Return (UserDescription.IsEmptyString Or _DescriptionEveryTime) And Not _DescriptionChecked
+ End Function
+ Protected Sub UserDescriptionUpdate(ByVal Descr As String)
+ If UserDescriptionNeedToUpdate() Then
+ If UserDescription.IsEmptyString Then
+ UserDescription = Descr
+ ElseIf Not UserDescription.Contains(Descr) Then
+ UserDescription &= $"{vbNewLine}----{vbNewLine}{Descr}"
+ End If
+ _DescriptionChecked = True
+ End If
+ End Sub
+ Protected Sub UserDescriptionReset()
+ _DescriptionChecked = False
+ _DescriptionEveryTime = Settings.UpdateUserDescriptionEveryTime
+ End Sub
+#End Region
+ Friend Property ParseUserMediaOnly As Boolean = False Implements IUserData.ParseUserMediaOnly, IPluginContentProvider.ParseUserMediaOnly
Protected _Favorite As Boolean = False
Friend Overridable Property Favorite As Boolean Implements IContentProvider.Favorite
Get
@@ -178,7 +240,7 @@ BlockPictureFolder:
p = New UserImage(PicList.First, l, s, MyFile)
GoTo BlockReturn
Else
- f.Delete(SFO.Path, False, False, EDP.None)
+ f.Delete(SFO.Path, Settings.DeleteMode, EDP.None)
DelPath = False
End If
End If
@@ -198,7 +260,7 @@ BlockDeletePictureFolder:
On Error GoTo BlockReturn
If DelPath Then
f = SFile.GetPath($"{MyFile.PathWithSeparator}Pictures")
- If f.Exists(SFO.Path, False) Then f.Delete(SFO.Path, False, False)
+ If f.Exists(SFO.Path, False) Then f.Delete(SFO.Path, Settings.DeleteMode)
End If
BlockReturn:
On Error GoTo BlockNullPicture
@@ -298,85 +360,121 @@ BlockNullPicture:
End Property
#End Region
#Region "Information"
- Protected _CountVideo As Integer = 0
- Protected Property _CountPictures As Integer = 0
Friend Overridable Property LastUpdated As Date?
- Friend ReadOnly Property TotalContentCount As Integer
- Get
- Return _CountVideo + _CountPictures
- End Get
- End Property
Friend Overridable Property HasError As Boolean = False Implements IUserData.HasError
Private _DownloadedPicturesTotal As Integer = 0
Private _DownloadedPicturesSession As Integer = 0
- Friend Property DownloadedPictures As Integer Implements IUserData.DownloadedPictures
+ Friend Property DownloadedPictures(ByVal Total As Boolean) As Integer Implements IUserData.DownloadedPictures
Get
- Return _DownloadedPicturesSession
+ Return IIf(Total, _DownloadedPicturesTotal, _DownloadedPicturesSession)
End Get
Set(ByVal NewValue As Integer)
- _DownloadedPicturesSession = NewValue
+ If Total Then
+ _DownloadedPicturesTotal = NewValue
+ Else
+ _DownloadedPicturesSession = NewValue
+ End If
End Set
End Property
Private _DownloadedVideosTotal As Integer = 0
Private _DownloadedVideosSession As Integer = 0
- Friend Property DownloadedVideos As Integer Implements IUserData.DownloadedVideos
+ Friend Property DownloadedVideos(ByVal Total As Boolean) As Integer Implements IUserData.DownloadedVideos
Get
- Return _DownloadedVideosSession
+ Return IIf(Total, _DownloadedVideosTotal, _DownloadedVideosSession)
End Get
Set(ByVal NewValue As Integer)
- _DownloadedVideosSession = NewValue
+ If Total Then
+ _DownloadedVideosTotal = NewValue
+ Else
+ _DownloadedVideosSession = NewValue
+ End If
End Set
End Property
Friend Overridable ReadOnly Property DownloadedTotal(Optional ByVal Total As Boolean = True) As Integer Implements IUserData.DownloadedTotal
Get
- If Total Then
- Return _DownloadedPicturesTotal + _DownloadedVideosTotal
- Else
- Return _DownloadedPicturesSession + _DownloadedVideosSession
- End If
+ Return DownloadedPictures(Total) + DownloadedVideos(Total)
End Get
End Property
Friend ReadOnly Property DownloadedInformation As String Implements IUserData.DownloadedInformation
Get
Dim luv$ = String.Empty
If LastUpdated.HasValue Then luv = $"{LastUpdated.Value.ToStringDate(ADateTime.Formats.BaseDateTime)}: "
- Return $"{luv}{Name} [{Site}]{IIf(HasError, " (with errors)", String.Empty)}: P - {_DownloadedPicturesTotal}; V - {_DownloadedVideosTotal}" &
- $" (P - {_CountPictures}; V - {_CountVideo})"
+ Return $"{luv}{Name} [{Site}]{IIf(HasError, " (with errors)", String.Empty)}: P - {DownloadedPictures(False)}; V - {DownloadedVideos(False)}" &
+ $" (P - {DownloadedPictures(True)}; V - {DownloadedVideos(True)})"
End Get
End Property
#End Region
#End Region
+#Region "Plugins Support"
+ Protected Event ProgressChanged As IPluginContentProvider.ProgressChangedEventHandler Implements IPluginContentProvider.ProgressChanged
+ Protected Event TotalCountChanged As IPluginContentProvider.TotalCountChangedEventHandler Implements IPluginContentProvider.TotalCountChanged
+ Friend Property HOST As SettingsHost Implements IUserData.HOST
+ Private Property IPluginContentProvider_Settings As ISiteSettings Implements IPluginContentProvider.Settings
+ Get
+ Return HOST.Source
+ End Get
+ Set(ByVal s As ISiteSettings)
+ End Set
+ End Property
+ Private Property IPluginContentProvider_Thrower As IThrower Implements IPluginContentProvider.Thrower
+ Private Property IPluginContentProvider_LogProvider As ILogProvider Implements IPluginContentProvider.LogProvider
+ Friend Property ExternalPlugin As IPluginContentProvider
+ Private Property IPluginContentProvider_ExistingContentList As List(Of PluginUserMedia) Implements IPluginContentProvider.ExistingContentList
+ Private Property IPluginContentProvider_TempPostsList As List(Of String) Implements IPluginContentProvider.TempPostsList
+ Private Property IPluginContentProvider_TempMediaList As List(Of IPluginUserMedia) Implements IPluginContentProvider.TempMediaList
+ Private Property IPluginContentProvider_SeparateVideoFolder As Boolean Implements IPluginContentProvider.SeparateVideoFolder
+ Private Property IPluginContentProvider_DataPath As String Implements IPluginContentProvider.DataPath
+ Private Sub IPluginContentProvider_XmlFieldsSet(ByVal Fields As List(Of KeyValuePair(Of String, String))) Implements IPluginContentProvider.XmlFieldsSet
+ End Sub
+ Private Function IPluginContentProvider_XmlFieldsGet() As List(Of KeyValuePair(Of String, String)) Implements IPluginContentProvider.XmlFieldsGet
+ Return Nothing
+ End Function
+ Private Sub IPluginContentProvider_GetMedia() Implements IPluginContentProvider.GetMedia
+ End Sub
+ Private Sub IPluginContentProvider_Download() Implements IPluginContentProvider.Download
+ End Sub
+ Friend Overridable Function ExchangeOptionsGet() As Object Implements IPluginContentProvider.ExchangeOptionsGet
+ Return Nothing
+ End Function
+ Friend Overridable Sub ExchangeOptionsSet(ByVal Obj As Object) Implements IPluginContentProvider.ExchangeOptionsSet
+ End Sub
+ Private _ExternalCompatibilityToken As CancellationToken
+#End Region
+#Region "IIndexable Support"
+ Friend Property Index As Integer = 0 Implements IIndexable.Index
+ Private Function SetIndex(ByVal Obj As Object, ByVal _Index As Integer) As Object Implements IIndexable.SetIndex
+ DirectCast(Obj, UserDataBase).Index = _Index
+ Return Obj
+ End Function
+#End Region
#Region "LVI"
- Friend ReadOnly Property LVIKey As String Implements IUserData.LVIKey
+ Friend ReadOnly Property LVIKey As String Implements IUserData.Key
Get
If Not _IsCollection Then
- Return $"{Site.ToString.ToUpper}_{Name}"
+ Return $"{IIf(IsChannel, "C", String.Empty)}{Site.ToString.ToUpper}_{Name}"
Else
Return $"CCCC_{CollectionName}"
End If
End Get
End Property
- Private _LVIIndex As Integer = -1
- Private ReadOnly Property LVIIndex As Integer Implements IUserData.LVIIndex
- Get
- Return _LVIIndex
- End Get
- End Property
Friend Function GetLVI(ByVal Destination As ListView) As ListViewItem Implements IUserData.GetLVI
- _LVIIndex = Destination.Items.Count
If Settings.ViewModeIsPicture Then
- Return New ListViewItem(ToString(), LVIKey, GetLVIGroup(Destination)) With {.Name = LVIKey, .Tag = LVIKey}
+ Return ListImagesLoader.ApplyLVIColor(Me, New ListViewItem(ToString(), LVIKey, GetLVIGroup(Destination)) With {.Name = LVIKey, .Tag = LVIKey}, True)
Else
- Return New ListViewItem(ToString(), GetLVIGroup(Destination)) With {.Name = LVIKey, .Tag = LVIKey}
+ Return ListImagesLoader.ApplyLVIColor(Me, New ListViewItem(ToString(), GetLVIGroup(Destination)) With {.Name = LVIKey, .Tag = LVIKey}, True)
End If
End Function
Friend Overridable ReadOnly Property FitToAddParams As Boolean Implements IUserData.FitToAddParams
Get
+ If Settings.LastUpdatedDate.HasValue AndAlso LastUpdated.HasValue AndAlso
+ LastUpdated.Value.Date > Settings.LastUpdatedDate.Value.Date Then Return False
If Settings.SelectedSites.Count = 0 OrElse Settings.SelectedSites.Contains(Site) Then
Select Case Settings.ShowingMode.Value
Case ShowingModes.Regular : Return Not Temporary And Not Favorite
Case ShowingModes.Temporary : Return Temporary
Case ShowingModes.Favorite : Return Favorite
+ Case ShowingModes.Deleted : Return Not UserExists
+ Case ShowingModes.Suspended : Return UserSuspended
Case ShowingModes.Labels : Return Settings.Labels.CurrentSelection.ListContains(Labels)
Case ShowingModes.NoLabels : Return Labels.Count = 0
Case Else : Return True
@@ -393,12 +491,10 @@ BlockNullPicture:
For i% = 0 To Labels.Count - 1
If Settings.Labels.CurrentSelection.Contains(Labels(i)) Then Return Destination.Groups.Item(Labels(i))
Next
- Return Destination.Groups.Item(LabelsKeeper.NoLabeledName)
- Else
- Return Destination.Groups.Item(LabelsKeeper.NoLabeledName)
End If
+ Return Destination.Groups.Item(LabelsKeeper.NoLabeledName)
Else
- Return Destination.Groups.Item(GetLviGroupName(Site, Temporary, Favorite, IsCollection, IsChannel))
+ Return Destination.Groups.Item(GetLviGroupName(HOST, Temporary, Favorite, IsCollection, IsChannel))
End If
Catch ex As Exception
Return Destination.Groups.Item(LabelsKeeper.NoLabeledName)
@@ -407,9 +503,9 @@ BlockNullPicture:
Friend Overridable Function GetUserInformation() As String
Dim OutStr$ = $"User: {Name}"
OutStr.StringAppendLine($"Path: {MyFile.CutPath.Path}")
- OutStr.StringAppendLine($"Total downloaded ({TotalContentCount.NumToString(ANumbers.Formats.Number, 3)}):")
- OutStr.StringAppendLine($"Pictures: {_CountPictures.NumToString(ANumbers.Formats.Number, 3)}")
- OutStr.StringAppendLine($"Videos: {_CountVideo.NumToString(ANumbers.Formats.Number, 3)}")
+ OutStr.StringAppendLine($"Total downloaded ({DownloadedTotal(True).NumToString(ANumbers.Formats.Number, 3)}):")
+ OutStr.StringAppendLine($"Pictures: {DownloadedPictures(True).NumToString(ANumbers.Formats.Number, 3)}")
+ OutStr.StringAppendLine($"Videos: {DownloadedVideos(True).NumToString(ANumbers.Formats.Number, 3)}")
If Not UserDescription.IsEmptyString Then
OutStr.StringAppendLine(String.Empty)
OutStr.StringAppendLine(UserDescription)
@@ -418,8 +514,8 @@ BlockNullPicture:
OutStr.StringAppendLine($"Last updated at: {AConvert(Of String)(LastUpdated, ADateTime.Formats.BaseDateTime, "not yet")}")
If _DataParsed Then
OutStr.StringAppendLine("Downloaded now:")
- OutStr.StringAppendLine($"Pictures: {_CountPictures.NumToString(ANumbers.Formats.Number, 3)}")
- OutStr.StringAppendLine($"Videos: {_CountVideo.NumToString(ANumbers.Formats.Number, 3)}")
+ OutStr.StringAppendLine($"Pictures: {DownloadedTotal(False).NumToString(ANumbers.Formats.Number, 3)}")
+ OutStr.StringAppendLine($"Videos: {DownloadedVideos(False).NumToString(ANumbers.Formats.Number, 3)}")
End If
Return OutStr
End Function
@@ -434,33 +530,34 @@ BlockNullPicture:
_TempMediaList = New List(Of UserMedia)
_TempPostsList = New List(Of String)
Labels = New List(Of String)
+ _OnUserUpdatedHandlers = New List(Of IUserData.OnUserUpdatedEventHandler)
If InvokeImageHandler Then ImageHandler(Me)
End Sub
+ Friend Sub SetEnvironment(ByRef h As SettingsHost, ByVal u As UserInfo, ByVal _LoadUserInformation As Boolean,
+ Optional ByVal AttachUserInfo As Boolean = True) Implements IUserData.SetEnvironment
+ HOST = h
+ If AttachUserInfo Then
+ User = u
+ If _LoadUserInformation Then LoadUserInformation()
+ End If
+ End Sub
'''
Friend Overloads Shared Function GetInstance(ByVal u As UserInfo, Optional ByVal _LoadUserInformation As Boolean = True) As IUserData
- Select Case u.Site
- Case Sites.Reddit
- If u.IsChannel Then
- Return New Reddit.Channel(u, _LoadUserInformation)
- Else
- Return New Reddit.UserData(u, _LoadUserInformation)
- End If
- Case Sites.Twitter : Return New Twitter.UserData(u, _LoadUserInformation)
- Case Sites.Instagram : Return New Instagram.UserData(u, _LoadUserInformation)
- Case Sites.RedGifs : Return New RedGifs.UserData(u, _LoadUserInformation)
- Case Else : Throw New ArgumentOutOfRangeException("Site", $"Site [{u.Site}] information does not recognized by loader")
- End Select
+ If Not u.Plugin.IsEmptyString Then
+ Return Settings(u.Plugin).GetInstance(u.DownloadOption, u, _LoadUserInformation)
+ Else
+ Throw New ArgumentOutOfRangeException("Plugin", $"Plugin [{u.Plugin}] information does not recognized by loader")
+ End If
End Function
#End Region
#Region "Information & Content data files loader and saver"
#Region "User information"
+ Private _UserInformationLoaded As Boolean = False
Friend Overridable Sub LoadUserInformation() Implements IUserData.LoadUserInformation
Try
If MyFile.Exists Then
FileExists = True
Using x As New XmlFile(MyFile) With {.XmlReadOnly = True}
- User.Site = Site
- Site = x.Value(Name_Site).FromXML(Of Integer)(0)
User.Name = x.Value(Name_UserName)
UserExists = x.Value(Name_UserExists).FromXML(Of Boolean)(True)
UserSuspended = x.Value(Name_UserSuspended).FromXML(Of Boolean)(False)
@@ -475,8 +572,8 @@ BlockNullPicture:
ReadyForDownload = x.Value(Name_ReadyForDownload).FromXML(Of Boolean)(True)
DownloadImages = x.Value(Name_DownloadImages).FromXML(Of Boolean)(True)
DownloadVideos = x.Value(Name_DownloadVideos).FromXML(Of Boolean)(True)
- _CountVideo = x.Value(Name_VideoCount).FromXML(Of Integer)(0)
- _CountPictures = x.Value(Name_PicturesCount).FromXML(Of Integer)(0)
+ DownloadedVideos(True) = x.Value(Name_VideoCount).FromXML(Of Integer)(0)
+ DownloadedPictures(True) = x.Value(Name_PicturesCount).FromXML(Of Integer)(0)
LastUpdated = AConvert(Of Date)(x.Value(Name_LastUpdated), ADateTime.Formats.BaseDateTime, Nothing)
DataMerging = x.Value(Name_DataMerging).FromXML(Of Boolean)(False)
ChangeCollectionName(x.Value(Name_CollectionName), False)
@@ -484,6 +581,8 @@ BlockNullPicture:
LoadUserInformation_OptionalFields(x, True)
End Using
UpdateDataFiles()
+ _UserInformationLoaded = True
+ If _CollectionButtonsExists And Not _CollectionButtonsColorsSet And (Not UserExists Or UserSuspended) Then UpdateButtonsColor()
End If
Catch ex As Exception
LogError(ex, "user information loading error")
@@ -493,7 +592,7 @@ BlockNullPicture:
Try
MyFile.Exists(SFO.Path)
Using x As New XmlFile With {.Name = "User"}
- x.Add(Name_Site, CInt(Site))
+ x.Add(Name_Site, Site)
x.Add(Name_UserName, User.Name)
x.Add(Name_UserExists, UserExists.BoolToInteger)
x.Add(Name_UserSuspended, UserSuspended.BoolToInteger)
@@ -512,8 +611,8 @@ BlockNullPicture:
x.Add(Name_ReadyForDownload, ReadyForDownload.BoolToInteger)
x.Add(Name_DownloadImages, DownloadImages.BoolToInteger)
x.Add(Name_DownloadVideos, DownloadVideos.BoolToInteger)
- x.Add(Name_VideoCount, _CountVideo)
- x.Add(Name_PicturesCount, _CountPictures)
+ x.Add(Name_VideoCount, DownloadedVideos(True))
+ x.Add(Name_PicturesCount, DownloadedPictures(True))
x.Add(Name_LastUpdated, AConvert(Of String)(LastUpdated, ADateTime.Formats.BaseDateTime, String.Empty))
x.Add(Name_CollectionName, CollectionName)
x.Add(Name_LabelsName, Labels.ListToString(, "|", EDP.ReturnValue))
@@ -597,42 +696,43 @@ BlockNullPicture:
#End Region
#End Region
#Region "Open site, folder"
- Friend Overridable Sub OpenSite() Implements IContentProvider.OpenSite
+ Friend Overridable Sub OpenSite(Optional ByVal e As ErrorsDescriber = Nothing) Implements IContentProvider.OpenSite
Try
- Dim URL$ = String.Empty
- Select Case Site
- Case Sites.Reddit : URL = $"https://www.reddit.com/{IIf(IsChannel, "r", "user")}/{Name}/"
- Case Sites.Twitter : URL = $"https://twitter.com/{Name}"
- Case Sites.Instagram : URL = $"https://www.instagram.com/{Name}/"
- Case Sites.RedGifs : URL = $"https://www.redgifs.com/users/{Name}/"
- Case Else : MsgBoxE($"Site [{Site}] opening not implemented", MsgBoxStyle.Exclamation)
- End Select
+ Dim URL$ = HOST.Source.GetUserUrl(Name, IsChannel)
If Not URL.IsEmptyString Then Process.Start(URL)
Catch ex As Exception
- MsgBoxE($"Error on trying to open [{Site}] page of user [{Name}]", MsgBoxStyle.Critical)
+ If Not e.Exists Then e = New ErrorsDescriber(EDP.ShowAllMsg)
+ MsgBoxE($"Error on trying to open [{Site}] page of user [{Name}]", MsgBoxStyle.Critical, e)
End Try
End Sub
Friend Overridable Sub OpenFolder() Implements IUserData.OpenFolder
- MyFile.CutPath.Open(SFO.Path, EDP.None)
+ GlobalOpenPath(MyFile.CutPath)
End Sub
#End Region
#Region "Download functions and options"
- Friend Overridable Property DownloadTopCount As Integer? = Nothing Implements IUserData.DownloadTopCount
+ Friend Overridable Property DownloadTopCount As Integer? = Nothing Implements IUserData.DownloadTopCount, IPluginContentProvider.PostsNumberLimit
Protected Responser As PersonalUtilities.Tools.WEB.Response
Friend Overridable Sub DownloadData(ByVal Token As CancellationToken) Implements IContentProvider.DownloadData
Dim Canceled As Boolean = False
+ _ExternalCompatibilityToken = Token
Try
UpdateDataFiles()
+ UserDescriptionReset()
If Not Responser Is Nothing Then Responser.Dispose()
Responser = New PersonalUtilities.Tools.WEB.Response
- Responser.Copy(Settings(Site).Responser)
+ If TypeOf HOST.Source Is IResponserContainer Then
+ With DirectCast(HOST.Source, IResponserContainer)
+ If Not .Responser Is Nothing Then Responser.Copy(.Responser)
+ End With
+ End If
+
Dim UpPic As Boolean = Settings.ViewModeIsPicture AndAlso GetPicture(False) Is Nothing
Dim sEnvir() As Boolean = {UserExists, UserSuspended}
Dim EnvirChanged As Func(Of Boolean) = Function() Not sEnvir(0) = UserExists Or Not sEnvir(1) = UserSuspended
UserExists = True
UserSuspended = False
- _DownloadedPicturesSession = 0
- _DownloadedVideosSession = 0
+ DownloadedPictures(False) = 0
+ DownloadedVideos(False) = 0
_TempMediaList.Clear()
_TempPostsList.Clear()
Dim __SaveData As Boolean = Not CreatedByChannel Or Not Settings.FromChannelDownloadTopUse
@@ -641,6 +741,7 @@ BlockNullPicture:
If MyFilePosts.Exists Then _TempPostsList.ListAddList(File.ReadAllLines(MyFilePosts))
If _ContentList.Count > 0 Then _TempPostsList.ListAddList(_ContentList.Select(Function(u) u.Post.ID), LNC)
+
ThrowAny(Token)
DownloadDataF(Token)
ThrowAny(Token)
@@ -658,28 +759,25 @@ BlockNullPicture:
DownloadContent(Token)
ThrowIfDisposed()
_ContentList.ListAddList(_ContentNew.Where(Function(c) c.State = UStates.Downloaded), LNC)
- _CountPictures = _ContentList.LongCount(Function(c) c.Type = UTypes.Picture)
- _CountVideo = _ContentList.LongCount(Function(c) c.Type = UTypes.Video)
- If DownloadedPictures + DownloadedVideos > 0 Or EnvirChanged.Invoke Then
+ If DownloadedTotal(False) > 0 Or EnvirChanged.Invoke Then
If __SaveData Then
LastUpdated = Now
+ DownloadedPictures(True) = SFile.GetFiles(User.File.CutPath, "*.jpg|*.jpeg|*.png|*.gif|*.webm",, EDP.ReturnValue).Count
+ DownloadedVideos(True) = SFile.GetFiles(User.File.CutPath, "*.mp4|*.mkv|*.mov", SearchOption.AllDirectories, EDP.ReturnValue).Count
If Labels.Contains(LabelsKeeper.NoParsedUser) Then Labels.Remove(LabelsKeeper.NoParsedUser)
UpdateContentInformation()
Else
- _CountVideo = 0
- _CountPictures = 0
+ DownloadedVideos(False) = 0
+ DownloadedPictures(False) = 0
_ContentList.Clear()
CreatedByChannel = False
End If
If Not UserExists Then ReadyForDownload = False
UpdateUserInformation()
+ If _CollectionButtonsExists AndAlso EnvirChanged.Invoke Then UpdateButtonsColor()
End If
ThrowIfDisposed()
- If Not CreatedByChannel Then
- _DownloadedPicturesTotal += _DownloadedPicturesSession
- _DownloadedVideosTotal += _DownloadedVideosSession
- End If
- If UpPic Or EnvirChanged.Invoke Then Raise_OnUserUpdated()
+ If UpPic Or EnvirChanged.Invoke Then RaiseEvent_OnUserUpdated()
Catch oex As OperationCanceledException When Token.IsCancellationRequested
MyMainLOG = $"{Site} - {Name}: downloading canceled"
Canceled = True
@@ -752,17 +850,22 @@ BlockNullPicture:
End Select
End If
- If __isVideo And vsf Then f.Path = $"{f.PathWithSeparator}Video"
+ If Not v.SpecialFolder.IsEmptyString Then
+ f.Path = $"{f.PathWithSeparator}{v.SpecialFolder}\".CSFileP.Path
+ f.Exists(SFO.Path)
+ End If
+ If __isVideo And vsf Then
+ f.Path = $"{f.PathWithSeparator}Video"
+ If Not v.SpecialFolder.IsEmptyString Then f.Exists(SFO.Path)
+ End If
w.DownloadFile(v.URL_BASE, f.ToString)
If __isVideo Then
v.Type = UTypes.Video
- DownloadedVideos += 1
- _CountVideo += 1
+ DownloadedVideos(False) += 1
Else
v.Type = UTypes.Picture
- DownloadedPictures += 1
- _CountPictures += 1
+ DownloadedPictures(False) += 1
End If
v.File = ChangeFileNameByProvider(f, v)
@@ -832,7 +935,7 @@ BlockNullPicture:
End Function
Friend Function DeleteF(ByVal Instance As IUserData) As Integer
Dim f As SFile = SFile.GetPath(MyFile.CutPath.Path)
- If f.Exists(SFO.Path, False) AndAlso (User.Merged OrElse f.Delete(SFO.Path, False, False)) Then
+ If f.Exists(SFO.Path, False) AndAlso (User.Merged OrElse f.Delete(SFO.Path, Settings.DeleteMode)) Then
ImageHandler(Me, False)
Settings.UsersList.Remove(User)
Settings.UpdateUsersList()
@@ -878,7 +981,7 @@ BlockNullPicture:
_TurnBack = False
Return False
End If
- f.Delete(SFO.Path, False, False, EDP.ThrowException)
+ 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)
@@ -933,7 +1036,7 @@ BlockNullPicture:
FilesMover.Invoke
If SFile.GetFiles(UserBefore.File.CutPath,, SearchOption.AllDirectories,
New ErrorsDescriber(False, False, False, New List(Of SFile))).Count = 0 Then
- UserBefore.File.CutPath.Delete(SFO.Path, False, False, EDP.SendInLog)
+ UserBefore.File.CutPath.Delete(SFO.Path, Settings.DeleteMode, EDP.SendInLog)
End If
UpdateUserInformation()
End If
@@ -963,33 +1066,42 @@ BlockNullPicture:
Protected Sub ThrowIfDisposed()
If Disposed Then Throw New ObjectDisposedException(ToString(), "Object disposed")
End Sub
+ '''
+ Private Overloads Sub ThrowAny() Implements IThrower.ThrowAny
+ ThrowAny(_ExternalCompatibilityToken)
+ End Sub
'''
'''
- Protected Sub ThrowAny(ByVal Token As CancellationToken)
+ Friend Overloads Sub ThrowAny(ByVal Token As CancellationToken)
Token.ThrowIfCancellationRequested()
ThrowIfDisposed()
End Sub
#End Region
Public Overrides Function ToString() As String
- If Settings.ViewModeIsPicture Then
- If IsCollection Then
- Return CollectionName
- Else
- Return IIf(FriendlyName.IsEmptyString, Name, FriendlyName)
- End If
+ If IsCollection Then
+ Return CollectionName
Else
- Dim t$ = String.Empty
- If Temporary Then
- t = " (T)"
- ElseIf Favorite Then
- t = " (F)"
- End If
- If IsCollection Then
- Return $"Collection [{CollectionName}]{t}"
- Else
- Return $"[{Site}]{t} {IIf(FriendlyName.IsEmptyString, Name, FriendlyName)}"
- End If
+ Return IIf(FriendlyName.IsEmptyString, Name, FriendlyName)
End If
+ 'If Settings.ViewModeIsPicture Then
+ ' If IsCollection Then
+ ' Return CollectionName
+ ' Else
+ ' Return IIf(FriendlyName.IsEmptyString, Name, FriendlyName)
+ ' End If
+ 'Else
+ ' Dim t$ = String.Empty
+ ' If Temporary Then
+ ' t = " (T)"
+ ' ElseIf Favorite Then
+ ' t = " (F)"
+ ' End If
+ ' If IsCollection Then
+ ' Return $"Collection [{CollectionName}]{t}"
+ ' Else
+ ' Return $"[{Site}]{t} {IIf(FriendlyName.IsEmptyString, Name, FriendlyName)}"
+ ' End If
+ 'End If
End Function
#Region "Buttons actions"
Private Sub BTT_CONTEXT_DOWN_Click(sender As Object, e As EventArgs) Handles BTT_CONTEXT_DOWN.Click
@@ -1012,38 +1124,25 @@ BlockNullPicture:
#End Region
#Region "IComparable Support"
Friend Overridable Function CompareTo(ByVal Other As UserDataBase) As Integer Implements IComparable(Of UserDataBase).CompareTo
- Dim x% = CompareValue(Me)
- Dim y% = CompareValue(Other)
- If x.CompareTo(y) = 0 Then
- Return Name.CompareTo(Other.Name)
- Else
- Return x.CompareTo(y)
- End If
- End Function
- Protected Function CompareValue(ByVal x As UserDataBase) As Integer
- Dim OutValue% = CInt(x.Site) * 10000
- If x.IsCollection Then OutValue -= 1000
- If x.Temporary Then OutValue += 2000
- If x.Favorite Then OutValue -= 500
- Return OutValue
+ Return Name.CompareTo(Other.Name)
End Function
Friend Overridable Function CompareTo(ByVal Obj As Object) As Integer Implements IComparable.CompareTo
- If TypeOf Obj Is Reddit.Channel Then
- Return CompareTo(DirectCast(DirectCast(Obj, Reddit.Channel).Instance, UserDataBase))
- Else
+ If Not Obj Is Nothing AndAlso TypeOf Obj Is UserDataBase Then
Return CompareTo(DirectCast(Obj, UserDataBase))
+ Else
+ Return False
End If
End Function
#End Region
#Region "IEquatable Support"
Friend Overridable Overloads Function Equals(ByVal Other As UserDataBase) As Boolean Implements IEquatable(Of UserDataBase).Equals
- Return Site = Other.Site And Name = Other.Name And IsSavedPosts = Other.IsSavedPosts
+ Return LVIKey = Other.LVIKey And IsSavedPosts = Other.IsSavedPosts
End Function
Public Overrides Function Equals(ByVal Obj As Object) As Boolean
- If TypeOf Obj Is Reddit.Channel Then
- Return Equals(DirectCast(DirectCast(Obj, Reddit.Channel).Instance, UserDataBase))
- Else
+ If Not Obj Is Nothing AndAlso TypeOf Obj Is UserDataBase Then
Return Equals(DirectCast(Obj, UserDataBase))
+ Else
+ Return False
End If
End Function
#End Region
@@ -1067,7 +1166,7 @@ BlockNullPicture:
If Not BTT_CONTEXT_DELETE Is Nothing Then BTT_CONTEXT_DELETE.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()
- If _InvokeImageHandler Then ImageHandler(Me, False)
+ _OnUserUpdatedHandlers.Clear()
End If
disposedValue = True
End If
@@ -1083,17 +1182,17 @@ BlockNullPicture:
#End Region
End Class
Friend Interface IContentProvider
- Property Site As Sites
+ ReadOnly Property Site As String
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()
+ Sub OpenSite(Optional ByVal e As ErrorsDescriber = Nothing)
Sub DownloadData(ByVal Token As CancellationToken)
End Interface
- Friend Interface IUserData : Inherits IContentProvider, IComparable(Of UserDataBase), IComparable, IEquatable(Of UserDataBase), IDisposable
+ Friend Interface IUserData : Inherits IContentProvider, IComparable(Of UserDataBase), IComparable, IEquatable(Of UserDataBase), IIndexable, IDisposable
Event OnUserUpdated(ByVal User As IUserData)
Property ParseUserMediaOnly As Boolean
#Region "Images"
@@ -1110,16 +1209,16 @@ BlockNullPicture:
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 As Integer
- Property DownloadedVideos As Integer
+ 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 LVIKey As String
- ReadOnly Property LVIIndex As Integer
+ ReadOnly Property Key As String
Property DownloadImages As Boolean
Property DownloadVideos As Boolean
Function GetLVI(ByVal Destination As ListView) As ListViewItem
@@ -1137,6 +1236,8 @@ BlockNullPicture:
Sub OpenFolder()
ReadOnly Property Self As IUserData
Property DownloadTopCount As Integer?
+ 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
diff --git a/SCrawler/API/Gfycat/Envir.vb b/SCrawler/API/Gfycat/Envir.vb
new file mode 100644
index 0000000..62e8d6f
--- /dev/null
+++ b/SCrawler/API/Gfycat/Envir.vb
@@ -0,0 +1,35 @@
+' Copyright (C) 2022 Andy
+' This program is free software: you can redistribute it and/or modify
+' it under the terms of the GNU General Public License as published by
+' the Free Software Foundation, either version 3 of the License, or
+' (at your option) any later version.
+'
+' This program is distributed in the hope that it will be useful,
+' but WITHOUT ANY WARRANTY
+Imports PersonalUtilities.Functions.RegularExpressions
+Imports System.Net
+Imports SCrawler.API.Base
+Namespace API.Gfycat
+ Friend NotInheritable Class Envir
+ Private Sub New()
+ End Sub
+ Friend Shared Function GetVideo(ByVal URL As String) As String
+ Try
+ Dim r$
+ Using w As New WebClient : r = w.DownloadString(URL) : End Using
+ If Not r.IsEmptyString Then Return RegexReplace(r, RParams.DMS("contentUrl.:.(http.?://[^""]+?\.mp4)", 1)) Else Return String.Empty
+ Catch ex As Exception
+ Dim e As EDP = EDP.ReturnValue
+ If TypeOf ex Is WebException Then
+ Dim obj As HttpWebResponse = TryCast(DirectCast(ex, WebException).Response, HttpWebResponse)
+ If Not If(obj?.StatusCode, HttpStatusCode.OK) = HttpStatusCode.NotFound Then e += EDP.SendInLog
+ End If
+ Return ErrorsDescriber.Execute(e, ex, $"[API.Gfycat.Envir.GetVideo({URL})]", String.Empty)
+ End Try
+ End Function
+ Friend Shared Function GetVideoInfo(ByVal URL As String) As IEnumerable(Of UserMedia)
+ Dim u$ = GetVideo(URL)
+ Return If(u.IsEmptyString, Nothing, {New UserMedia(u, UserMedia.Types.Video)})
+ End Function
+ End Class
+End Namespace
\ No newline at end of file
diff --git a/SCrawler/API/Instagram/AuthNullException.vb b/SCrawler/API/Instagram/AuthNullException.vb
new file mode 100644
index 0000000..cc1e6f4
--- /dev/null
+++ b/SCrawler/API/Instagram/AuthNullException.vb
@@ -0,0 +1,36 @@
+' Copyright (C) 2022 Andy
+' This program is free software: you can redistribute it and/or modify
+' it under the terms of the GNU General Public License as published by
+' the Free Software Foundation, either version 3 of the License, or
+' (at your option) any later version.
+'
+' This program is distributed in the hope that it will be useful,
+' but WITHOUT ANY WARRANTY
+Imports Sections = SCrawler.API.Instagram.UserData.Sections
+Namespace API.Instagram
+ Friend Class AuthNullException : Inherits ArgumentNullException
+ Public Overrides ReadOnly Property ParamName As String
+ Public Overrides ReadOnly Property Message As String
+ Friend Sub New(ByVal s As Sections, ByVal IsSavedPosts As Boolean)
+ If IsSavedPosts Then
+ ParamName = "HashSavedPosts"
+ ElseIf s = Sections.Timeline Then
+ ParamName = "Hash"
+ Else
+ ParamName = "IG_APP_ID, IG_WWW_CLAIM"
+ End If
+ Message = $"Instagram auth for [{s}] is not set"
+ End Sub
+ Friend Shared Sub ThrowIfNull(ByVal s As Sections, ByVal IsSavedPosts As Boolean, ByVal Host As SiteSettings)
+ Dim b As Boolean = False
+ If IsSavedPosts Then
+ If Not ACheck(Host.HashSavedPosts.Value) Then b = True
+ ElseIf s = Sections.Timeline Then
+ If Not ACheck(Host.Hash.Value) Then Host.HashUpdateRequired.Value = True : b = True
+ Else
+ If Not Host.StoriesAndTaggedReady Then b = True
+ End If
+ If b Then Throw New AuthNullException(s, IsSavedPosts)
+ End Sub
+ End Class
+End Namespace
\ No newline at end of file
diff --git a/SCrawler/API/Instagram/Declarations.vb b/SCrawler/API/Instagram/Declarations.vb
index 2af847a..e2c0974 100644
--- a/SCrawler/API/Instagram/Declarations.vb
+++ b/SCrawler/API/Instagram/Declarations.vb
@@ -9,6 +9,7 @@
Imports PersonalUtilities.Functions.RegularExpressions
Namespace API.Instagram
Friend Module Declarations
+ Friend Const InstagramSite As String = "Instagram"
Friend ReadOnly FilesPattern As RParams = RParams.DMS(".+?([^/\?]+?\.[\w\d]{3,4})(?=(\?|\Z))", 1, EDP.ReturnValue)
Friend ReadOnly Property DateProvider As New JsonDate
Friend Class JsonDate : Implements ICustomProvider
diff --git a/SCrawler/API/Instagram/EditorExchangeOptions.vb b/SCrawler/API/Instagram/EditorExchangeOptions.vb
new file mode 100644
index 0000000..9ed3b3b
--- /dev/null
+++ b/SCrawler/API/Instagram/EditorExchangeOptions.vb
@@ -0,0 +1,23 @@
+' Copyright (C) 2022 Andy
+' This program is free software: you can redistribute it and/or modify
+' it under the terms of the GNU General Public License as published by
+' the Free Software Foundation, either version 3 of the License, or
+' (at your option) any later version.
+'
+' This program is distributed in the hope that it will be useful,
+' but WITHOUT ANY WARRANTY
+Imports SCrawler.Plugin
+Namespace API.Instagram
+ Friend Class EditorExchangeOptions
+ Friend Property GetStories As Boolean
+ Friend Property GetTagged As Boolean
+ Private ReadOnly Property MySiteSettings As SiteSettings
+ Friend Sub New(ByVal h As ISiteSettings)
+ MySiteSettings = DirectCast(h, SiteSettings)
+ With MySiteSettings
+ GetStories = CBool(.GetStories.Value)
+ GetTagged = CBool(.GetTagged.Value)
+ End With
+ End Sub
+ End Class
+End Namespace
\ No newline at end of file
diff --git a/SCrawler/API/Instagram/ExitException.vb b/SCrawler/API/Instagram/ExitException.vb
new file mode 100644
index 0000000..32033df
--- /dev/null
+++ b/SCrawler/API/Instagram/ExitException.vb
@@ -0,0 +1,15 @@
+' Copyright (C) 2022 Andy
+' This program is free software: you can redistribute it and/or modify
+' it under the terms of the GNU General Public License as published by
+' the Free Software Foundation, either version 3 of the License, or
+' (at your option) any later version.
+'
+' This program is distributed in the hope that it will be useful,
+' but WITHOUT ANY WARRANTY
+Namespace API.Instagram
+ Friend Class ExitException : Inherits Exception
+ Friend Sub New(ByRef CompleteArg As Boolean)
+ CompleteArg = True
+ End Sub
+ End Class
+End Namespace
\ No newline at end of file
diff --git a/SCrawler/API/Instagram/OptionsForm.Designer.vb b/SCrawler/API/Instagram/OptionsForm.Designer.vb
new file mode 100644
index 0000000..5d83185
--- /dev/null
+++ b/SCrawler/API/Instagram/OptionsForm.Designer.vb
@@ -0,0 +1,120 @@
+' Copyright (C) 2022 Andy
+' This program is free software: you can redistribute it and/or modify
+' it under the terms of the GNU General Public License as published by
+' the Free Software Foundation, either version 3 of the License, or
+' (at your option) any later version.
+'
+' This program is distributed in the hope that it will be useful,
+' but WITHOUT ANY WARRANTY
+Namespace API.Instagram
+
+ Partial Friend Class OptionsForm : 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 TP_MAIN As System.Windows.Forms.TableLayoutPanel
+ Me.CH_GET_STORIES = New System.Windows.Forms.CheckBox()
+ Me.CH_GET_TAGGED = New System.Windows.Forms.CheckBox()
+ CONTAINER_MAIN = New System.Windows.Forms.ToolStripContainer()
+ TP_MAIN = New System.Windows.Forms.TableLayoutPanel()
+ CONTAINER_MAIN.ContentPanel.SuspendLayout()
+ CONTAINER_MAIN.SuspendLayout()
+ TP_MAIN.SuspendLayout()
+ Me.SuspendLayout()
+ '
+ 'CONTAINER_MAIN
+ '
+ '
+ 'CONTAINER_MAIN.ContentPanel
+ '
+ CONTAINER_MAIN.ContentPanel.Controls.Add(TP_MAIN)
+ CONTAINER_MAIN.ContentPanel.Size = New System.Drawing.Size(260, 53)
+ 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(260, 78)
+ CONTAINER_MAIN.TabIndex = 0
+ CONTAINER_MAIN.TopToolStripPanelVisible = False
+ '
+ 'TP_MAIN
+ '
+ TP_MAIN.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.[Single]
+ TP_MAIN.ColumnCount = 1
+ TP_MAIN.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
+ TP_MAIN.Controls.Add(Me.CH_GET_STORIES, 0, 0)
+ TP_MAIN.Controls.Add(Me.CH_GET_TAGGED, 0, 1)
+ TP_MAIN.Dock = System.Windows.Forms.DockStyle.Fill
+ TP_MAIN.Location = New System.Drawing.Point(0, 0)
+ TP_MAIN.Name = "TP_MAIN"
+ TP_MAIN.RowCount = 3
+ TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
+ TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
+ TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
+ TP_MAIN.Size = New System.Drawing.Size(260, 53)
+ TP_MAIN.TabIndex = 0
+ '
+ 'CH_GET_STORIES
+ '
+ Me.CH_GET_STORIES.AutoSize = True
+ Me.CH_GET_STORIES.Dock = System.Windows.Forms.DockStyle.Fill
+ Me.CH_GET_STORIES.Location = New System.Drawing.Point(4, 4)
+ Me.CH_GET_STORIES.Name = "CH_GET_STORIES"
+ Me.CH_GET_STORIES.Size = New System.Drawing.Size(252, 19)
+ Me.CH_GET_STORIES.TabIndex = 0
+ Me.CH_GET_STORIES.Text = "Get stories"
+ Me.CH_GET_STORIES.UseVisualStyleBackColor = True
+ '
+ 'CH_GET_TAGGED
+ '
+ Me.CH_GET_TAGGED.AutoSize = True
+ Me.CH_GET_TAGGED.Dock = System.Windows.Forms.DockStyle.Fill
+ Me.CH_GET_TAGGED.Location = New System.Drawing.Point(4, 30)
+ Me.CH_GET_TAGGED.Name = "CH_GET_TAGGED"
+ Me.CH_GET_TAGGED.Size = New System.Drawing.Size(252, 19)
+ Me.CH_GET_TAGGED.TabIndex = 1
+ Me.CH_GET_TAGGED.Text = "Get tagged data"
+ Me.CH_GET_TAGGED.UseVisualStyleBackColor = True
+ '
+ 'OptionsForm
+ '
+ Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
+ Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
+ Me.ClientSize = New System.Drawing.Size(260, 78)
+ Me.Controls.Add(CONTAINER_MAIN)
+ Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle
+ Me.KeyPreview = True
+ Me.MaximizeBox = False
+ Me.MaximumSize = New System.Drawing.Size(276, 117)
+ Me.MinimizeBox = False
+ Me.MinimumSize = New System.Drawing.Size(276, 117)
+ Me.Name = "OptionsForm"
+ Me.ShowIcon = False
+ Me.ShowInTaskbar = False
+ Me.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide
+ Me.Text = "Options"
+ CONTAINER_MAIN.ContentPanel.ResumeLayout(False)
+ CONTAINER_MAIN.ResumeLayout(False)
+ CONTAINER_MAIN.PerformLayout()
+ TP_MAIN.ResumeLayout(False)
+ TP_MAIN.PerformLayout()
+ Me.ResumeLayout(False)
+
+ End Sub
+
+ Private WithEvents CH_GET_STORIES As CheckBox
+ Private WithEvents CH_GET_TAGGED As CheckBox
+ End Class
+End Namespace
\ No newline at end of file
diff --git a/SCrawler/API/Instagram/OptionsForm.resx b/SCrawler/API/Instagram/OptionsForm.resx
new file mode 100644
index 0000000..be8e932
--- /dev/null
+++ b/SCrawler/API/Instagram/OptionsForm.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
+
+
+ False
+
+
\ No newline at end of file
diff --git a/SCrawler/API/Instagram/OptionsForm.vb b/SCrawler/API/Instagram/OptionsForm.vb
new file mode 100644
index 0000000..e6a0485
--- /dev/null
+++ b/SCrawler/API/Instagram/OptionsForm.vb
@@ -0,0 +1,44 @@
+' Copyright (C) 2022 Andy
+' This program is free software: you can redistribute it and/or modify
+' it under the terms of the GNU General Public License as published by
+' the Free Software Foundation, either version 3 of the License, or
+' (at your option) any later version.
+'
+' This program is distributed in the hope that it will be useful,
+' but WITHOUT ANY WARRANTY
+Imports PersonalUtilities.Forms
+Imports PersonalUtilities.Forms.Toolbars
+Namespace API.Instagram
+ Friend Class OptionsForm : Implements IOkCancelToolbar
+ Private ReadOnly MyDefs As DefaultFormProps
+ Private ReadOnly Property MyExchangeOptions As EditorExchangeOptions
+ Friend Sub New(ByRef ExchangeOptions As EditorExchangeOptions)
+ InitializeComponent()
+ MyExchangeOptions = ExchangeOptions
+ MyDefs = New DefaultFormProps
+ End Sub
+ Private Sub OptionsForm_Load(sender As Object, e As EventArgs) Handles Me.Load
+ With MyDefs
+ .MyViewInitialize(Me, Settings.Design, True)
+ .AddOkCancelToolbar()
+ .DelegateClosingChecker()
+ .AppendDetectors()
+ With MyExchangeOptions
+ CH_GET_STORIES.Checked = .GetStories
+ CH_GET_TAGGED.Checked = .GetTagged
+ End With
+ .EndLoaderOperations()
+ End With
+ End Sub
+ Private Sub ToolbarBttOK() Implements IOkCancelToolbar.ToolbarBttOK
+ With MyExchangeOptions
+ .GetStories = CH_GET_STORIES.Checked
+ .GetTagged = CH_GET_TAGGED.Checked
+ End With
+ MyDefs.CloseForm()
+ End Sub
+ Private Sub ToolbarBttCancel() Implements IOkCancelToolbar.ToolbarBttCancel
+ MyDefs.CloseForm(DialogResult.Cancel)
+ End Sub
+ End Class
+End Namespace
\ No newline at end of file
diff --git a/SCrawler/API/Instagram/ProfileSaved.vb b/SCrawler/API/Instagram/ProfileSaved.vb
deleted file mode 100644
index 7cea9da..0000000
--- a/SCrawler/API/Instagram/ProfileSaved.vb
+++ /dev/null
@@ -1,44 +0,0 @@
-' Copyright (C) 2022 Andy
-' This program is free software: you can redistribute it and/or modify
-' it under the terms of the GNU General Public License as published by
-' the Free Software Foundation, either version 3 of the License, or
-' (at your option) any later version.
-'
-' This program is distributed in the hope that it will be useful,
-' but WITHOUT ANY WARRANTY
-Imports SCrawler.API.Base
-Imports System.Threading
-Imports PersonalUtilities.Forms.Toolbars
-Namespace API.Instagram
- Friend NotInheritable Class ProfileSaved
- Friend Shared ReadOnly Property DataPath As SFile = Settings(Sites.Instagram).SavedPostsPath
- Private Sub New()
- End Sub
- Friend Shared Sub Download(ByRef Bar As MyProgress, ByVal Token As CancellationToken)
- Try
- Dim u As New UserInfo(Settings(Sites.Instagram).SavedPostsUserName.Value, Sites.Instagram) With {.SpecialPath = DataPath}
- u.UpdateUserFile()
- Using user As New UserData(u,, False)
- DirectCast(user.Self, UserDataBase).IsSavedPosts = True
- user.Progress = Bar
- If Not user.FileExists Then user.UpdateUserInformation()
- If Settings(Sites.Instagram).InstagramLastDownloadDate.Value < Now.AddMinutes(60) Then
- user.RequestsCount = Settings(Sites.Instagram).InstagramLastRequestsCount
- End If
- user.DownloadData(Token)
- Bar.InformationTemporary = $"Images: {user.DownloadedPictures}; Videos: {user.DownloadedVideos}"
- With Settings
- .BeginUpdate()
- With .Site(Sites.Instagram)
- .InstagramLastDownloadDate.Value = Now
- .InstagramLastRequestsCount.Value = user.RequestsCount
- End With
- .EndUpdate()
- End With
- End Using
- Catch ex As Exception
- ErrorsDescriber.Execute(EDP.SendInLog, ex, "[API.Instagram.ProfileSaved.Download]")
- End Try
- End Sub
- End Class
-End Namespace
\ No newline at end of file
diff --git a/SCrawler/API/Instagram/SiteSettings.vb b/SCrawler/API/Instagram/SiteSettings.vb
new file mode 100644
index 0000000..a69478c
--- /dev/null
+++ b/SCrawler/API/Instagram/SiteSettings.vb
@@ -0,0 +1,314 @@
+' Copyright (C) 2022 Andy
+' This program is free software: you can redistribute it and/or modify
+' it under the terms of the GNU General Public License as published by
+' the Free Software Foundation, either version 3 of the License, or
+' (at your option) any later version.
+'
+' This program is distributed in the hope that it will be useful,
+' but WITHOUT ANY WARRANTY
+Imports SCrawler.API.Base
+Imports SCrawler.Plugin
+Imports SCrawler.Plugin.Attributes
+Imports PersonalUtilities.Tools
+Imports PersonalUtilities.Functions.XML
+Imports PersonalUtilities.Functions.XML.Base
+Imports PersonalUtilities.Functions.RegularExpressions
+Imports Download = SCrawler.Plugin.ISiteSettings.Download
+Namespace API.Instagram
+
+ Friend Class SiteSettings : Inherits SiteSettingsBase
+#Region "Interface Declarations"
+ Friend Overrides ReadOnly Property Icon As Icon
+ Get
+ Return My.Resources.InstagramIcon
+ End Get
+ End Property
+ Friend Overrides ReadOnly Property Image As Image
+ Get
+ Return My.Resources.InstagramPic76
+ End Get
+ End Property
+#End Region
+#Region "Providers"
+ Private Class TimersChecker : Implements ICustomProvider
+ Private ReadOnly _LowestValue As Integer
+ Friend Sub New(ByVal LowestValue As Integer)
+ _LowestValue = LowestValue
+ End Sub
+ Private Function Convert(ByVal Value As Object, ByVal DestinationType As Type, ByVal Provider As IFormatProvider,
+ Optional ByVal NothingArg As Object = Nothing, Optional ByVal e As ErrorsDescriber = Nothing) As Object Implements ICustomProvider.Convert
+ If ACheck(Of Integer)(Value) AndAlso CInt(Value) >= _LowestValue Then
+ Return Value
+ Else
+ Return Nothing
+ End If
+ End Function
+ Private Function GetFormat(ByVal FormatType As Type) As Object Implements IFormatProvider.GetFormat
+ Throw New NotImplementedException()
+ End Function
+ End Class
+#End Region
+#Region "Authorization properties"
+
+ Friend ReadOnly Property Hash As PropertyValue
+
+ Friend ReadOnly Property HashSavedPosts As PropertyValue
+
+ Friend Property IG_APP_ID As PropertyValue
+
+ Friend Property IG_WWW_CLAIM As PropertyValue
+
+ Friend ReadOnly Property SavedPostsUserName As PropertyValue
+ Friend ReadOnly Property StoriesAndTaggedReady As Boolean
+ Get
+ Return ACheck(IG_APP_ID.Value) And ACheck(IG_WWW_CLAIM.Value)
+ End Get
+ End Property
+#End Region
+#Region "Download properties"
+ Friend ReadOnly Property HashUpdateRequired As XMLValue(Of Boolean)
+
+ Friend ReadOnly Property RequestsWaitTimer As PropertyValue
+
+ Private ReadOnly Property RequestsWaitTimerProvider As IFormatProvider
+
+ Friend ReadOnly Property RequestsWaitTimerTaskCount As PropertyValue
+
+ Private ReadOnly Property RequestsWaitTimerTaskCountProvider As IFormatProvider
+
+ Friend ReadOnly Property SleepTimerOnPostsLimit As PropertyValue
+
+ Private ReadOnly Property SleepTimerOnPostsLimitProvider As IFormatProvider
+
+ Friend ReadOnly Property GetStories As PropertyValue
+
+ Friend ReadOnly Property GetTagged As PropertyValue
+#End Region
+#Region "429 bypass"
+ Friend ReadOnly Property DownloadingErrorDate As XMLValue(Of Date)
+ Friend Property LastApplyingValue As Integer? = Nothing
+ Friend ReadOnly Property ReadyForDownload As Boolean
+ Get
+ With DownloadingErrorDate
+ If .ValueF.Exists Then
+ Return .ValueF.Value.AddMinutes(If(LastApplyingValue, 10)) < Now
+ Else
+ Return True
+ End If
+ End With
+ End Get
+ End Property
+ Friend ReadOnly Property LastDownloadDate As XMLValue(Of Date)
+ Friend ReadOnly Property LastRequestsCount As XMLValue(Of Integer)
+ Private TooManyRequestsReadyForCatch As Boolean = True
+ Friend Function GetWaitDate() As Date
+ With DownloadingErrorDate
+ If .ValueF.Exists Then
+ Return .ValueF.Value.AddMinutes(If(LastApplyingValue, 10))
+ Else
+ Return Now
+ End If
+ End With
+ End Function
+ Friend Sub TooManyRequests(ByVal Catched As Boolean)
+ With DownloadingErrorDate
+ If Catched Then
+ If Not .ValueF.Exists Then
+ .Value = Now
+ If TooManyRequestsReadyForCatch Then
+ LastApplyingValue = If(LastApplyingValue, 0) + 10
+ TooManyRequestsReadyForCatch = False
+ MyMainLOG = $"Instagram downloading error: too many requests. Try again after {If(LastApplyingValue, 10)} minutes..."
+ End If
+ End If
+ Else
+ .ValueF = Nothing
+ LastApplyingValue = Nothing
+ TooManyRequestsReadyForCatch = True
+ End If
+ End With
+ End Sub
+#End Region
+ Friend Overrides ReadOnly Property Responser As WEB.Response
+ Friend Sub New(ByRef _XML As XmlFile, ByVal GlobalPath As SFile)
+ MyBase.New(InstagramSite)
+ Responser = New WEB.Response($"{SettingsFolderName}\Responser_{Site}.xml")
+
+ Dim app_id$ = String.Empty
+ Dim www_claim$ = String.Empty
+
+ With Responser
+ If .File.Exists Then
+ .LoadSettings()
+ With .Headers
+ If .ContainsKey(Header_IG_APP_ID) Then app_id = .Item(Header_IG_APP_ID)
+ If .ContainsKey(Header_IG_WWW_CLAIM) Then www_claim = .Item(Header_IG_WWW_CLAIM)
+ End With
+ Else
+ .CookiesDomain = "instagram.com"
+ .SaveSettings()
+ End If
+ End With
+
+ Dim n() As String = {SettingsCLS.Name_Node_Sites, Site.ToString}
+
+ SavedPostsUserName = New PropertyValue(String.Empty, GetType(String))
+
+ HashUpdateRequired = New XMLValue(Of Boolean)("InstaHashUpdateRequired", True, _XML, n)
+ Hash = New PropertyValue(String.Empty, GetType(String))
+ HashSavedPosts = New PropertyValue(String.Empty, GetType(String))
+ IG_APP_ID = New PropertyValue(app_id, GetType(String), Sub(v) ChangeResponserFields(NameOf(IG_APP_ID), v))
+ IG_WWW_CLAIM = New PropertyValue(www_claim, GetType(String), Sub(v) ChangeResponserFields(NameOf(IG_WWW_CLAIM), v))
+
+ RequestsWaitTimer = New PropertyValue(1000)
+ RequestsWaitTimerProvider = New TimersChecker(100)
+ RequestsWaitTimerTaskCount = New PropertyValue(1)
+ RequestsWaitTimerTaskCountProvider = New TimersChecker(1)
+ SleepTimerOnPostsLimit = New PropertyValue(6000)
+ SleepTimerOnPostsLimitProvider = New TimersChecker(10000)
+
+ GetStories = New PropertyValue(False)
+ GetTagged = New PropertyValue(False)
+
+ DownloadingErrorDate = New XMLValue(Of Date) With {
+ .Provider = New XMLValueConversionProvider(Function(ss, vv) AConvert(Of String)(vv, AModes.Var, Nothing))}
+ DownloadingErrorDate.SetExtended("InstagramDownloadingErrorDate", Now.AddYears(-10), _XML, n)
+ LastDownloadDate = New XMLValue(Of Date)("LastDownloadDate", Now.AddDays(-1), _XML, n)
+ LastRequestsCount = New XMLValue(Of Integer)("LastRequestsCount", 0, _XML, n)
+
+ UrlPatternUser = "https://www.instagram.com/{0}/"
+ UserRegex = RParams.DMS("[htps:/]{7,8}.*?instagram.com/([^/]+)", 1)
+ ImageVideoContains = "instagram.com"
+ End Sub
+ Friend Overrides Function GetInstance(ByVal What As Download) As IPluginContentProvider
+ Select Case What
+ Case Download.Main : Return New UserData
+ Case Download.SavedPosts
+ Dim u As New UserData
+ DirectCast(u, UserDataBase).User = New UserInfo With {.Name = CStr(AConvert(Of String)(SavedPostsUserName.Value, String.Empty))}
+ Return u
+ End Select
+ Return Nothing
+ End Function
+ Private Const Header_IG_APP_ID As String = "x-ig-app-id"
+ Private Const Header_IG_WWW_CLAIM As String = "x-ig-www-claim"
+ 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(IG_APP_ID) : f = Header_IG_APP_ID
+ Case NameOf(IG_WWW_CLAIM) : f = Header_IG_WWW_CLAIM
+ End Select
+ If Not f.IsEmptyString Then
+ If Responser.Headers.Count > 0 AndAlso Responser.Headers.ContainsKey(f) 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
+
+ Private Function CheckHashControls(ByVal p As IEnumerable(Of PropertyData)) As Boolean
+ If p.ListExists(2) Then
+ Dim h$ = String.Empty
+ Dim hsp$ = String.Empty
+ For Each pp As PropertyData In p
+ Select Case pp.Name
+ Case NameOf(Hash) : h = AConvert(Of String)(pp.Value, String.Empty)
+ Case NameOf(HashSavedPosts) : hsp = AConvert(Of String)(pp.Value, String.Empty)
+ End Select
+ Next
+ If h.IsEmptyString And hsp.IsEmptyString Then
+ Return True
+ Else
+ If h = hsp Then
+ MsgBoxE({"InstaHash for saved posts must be different from InstaHash!", "InstaHash are equal"}, vbCritical)
+ Return False
+ Else
+ Return True
+ End If
+ End If
+ Else
+ Return False
+ End If
+ End Function
+ Friend Overrides Sub BeginInit()
+ End Sub
+ Friend Overrides Sub EndInit()
+ If (CStr(Hash.Value).IsEmptyString Or HashUpdateRequired) AndAlso Responser.Cookies.ListExists Then GatherInstaHash()
+ End Sub
+ Friend Overrides Function ReadyToDownload(ByVal What As Download) As Boolean
+ Return ActiveJobs < 2 AndAlso ReadyForDownload
+ End Function
+#Region "Downloading"
+ Private ActiveJobs As Integer = 0
+ Private _NextWNM As UserData.WNM = UserData.WNM.Notify
+ Friend Overrides Sub DownloadStarted(ByVal What As Download)
+ If CStr(Hash.Value).IsEmptyString Or HashUpdateRequired Then GatherInstaHash()
+ ActiveJobs += 1
+ End Sub
+ Friend Overrides Sub BeforeStartDownload(ByVal User As Object, ByVal What As Download)
+ With DirectCast(User, UserData)
+ If What = Download.Main Then .WaitNotificationMode = _NextWNM
+ If LastDownloadDate.Value.AddMinutes(60) > Now Then
+ .RequestsCount = LastRequestsCount
+ Else
+ LastRequestsCount.Value = 0
+ .RequestsCount = 0
+ End If
+ End With
+ End Sub
+ Friend Overrides Sub AfterDownload(ByVal User As Object, ByVal What As Download)
+ With DirectCast(User, UserData)
+ _NextWNM = .WaitNotificationMode
+ If _NextWNM = UserData.WNM.SkipTemp Or _NextWNM = UserData.WNM.SkipCurrent Then _NextWNM = UserData.WNM.Notify
+ LastRequestsCount.Value = .RequestsCount
+ End With
+ End Sub
+ Friend Overrides Sub DownloadDone(ByVal What As Download)
+ _NextWNM = UserData.WNM.Notify
+ LastDownloadDate.Value = Now
+ ActiveJobs -= 1
+ If HashUpdateRequired Then MyMainLOG = "Check your Instagram credentials"
+ End Sub
+#End Region
+
+ Friend Function GatherInstaHash() As Boolean
+ Try
+ If Not Responser.Cookies.ListExists Then Throw New Exception("Instagram cookies does not set")
+ Dim rs As New RParams("=""([^""]+?ConsumerLibCommons[^""]+?.js)""", Nothing, 1) With {.MatchTimeOut = 10}
+ Dim r$ = Responser.GetResponse("https://instagram.com",, EDP.ThrowException)
+ If Not r.IsEmptyString Then
+ Dim hStr$ = RegexReplace(r, rs)
+ If Not hStr.IsEmptyString Then
+ Do While Left(hStr, 1) = "/" : hStr = Right(hStr, hStr.Length - 1) : Loop
+ hStr = $"https://instagram.com/{hStr}"
+ r = Responser.GetResponse(hStr,, EDP.ThrowException)
+ If Not r.IsEmptyString Then
+ rs = New RParams("generatePaginationActionCreators.+?.profilePosts.byUserId.get.+?queryId:.([\d\w\S]+?)""", Nothing, 1) With {.MatchTimeOut = 10}
+ Dim h$ = RegexReplace(r, rs)
+ If Not h.IsEmptyString Then
+ Hash.Value = h
+ HashUpdateRequired.Value = False
+ Return True
+ End If
+ End If
+ End If
+ End If
+ Return False
+ Catch ex As Exception
+ HashUpdateRequired.Value = True
+ Hash.Value = String.Empty
+ Return ErrorsDescriber.Execute(EDP.SendInLog + EDP.ReturnValue, ex, "[SiteSettings.GaterInstaHash]", False)
+ End Try
+ End Function
+ Friend Overrides Function GetSpecialDataF(ByVal URL As String) As IEnumerable(Of UserMedia)
+ Return UserData.GetVideoInfo(URL, Responser)
+ End Function
+ Friend Overrides Sub UserOptions(ByRef Options As Object, ByVal OpenForm As Boolean)
+ If Options Is Nothing OrElse Not TypeOf Options Is EditorExchangeOptions Then Options = New EditorExchangeOptions(Me)
+ If OpenForm Then
+ Using f As New OptionsForm(Options) : f.ShowDialog() : End Using
+ End If
+ End Sub
+ End Class
+End Namespace
\ No newline at end of file
diff --git a/SCrawler/API/Instagram/UserData.vb b/SCrawler/API/Instagram/UserData.vb
index f07a512..ad04d7a 100644
--- a/SCrawler/API/Instagram/UserData.vb
+++ b/SCrawler/API/Instagram/UserData.vb
@@ -9,8 +9,8 @@
Imports PersonalUtilities.Functions.XML
Imports PersonalUtilities.Functions.Messaging
Imports PersonalUtilities.Functions.RegularExpressions
+Imports PersonalUtilities.Tools.WEB
Imports PersonalUtilities.Tools.WebDocuments.JSON
-Imports PersonalUtilities.Forms.Toolbars
Imports SCrawler.API.Base
Imports System.Threading
Imports System.Net
@@ -20,60 +20,83 @@ Namespace API.Instagram
Private Const MaxPostsCount As Integer = 200
Private Const Name_LastCursor As String = "LastCursor"
Private Const Name_FirstLoadingDone As String = "FirstLoadingDone"
- Friend Overrides Property Site As Sites = Sites.Instagram
- Friend Overrides Property Progress As MyProgress
+ Private Const Name_GetStories As String = "GetStories"
+ Private Const Name_GetTagged As String = "GetTaggedData"
+ Private Const Name_TaggedChecked As String = "TaggedChecked"
+ Private ReadOnly Property MySiteSettings As SiteSettings
Get
- If Not _Progress Is Nothing Then Return _Progress Else Return MainProgressInst
+ Return DirectCast(HOST.Source, SiteSettings)
End Get
- Set(ByVal p As MyProgress)
- _Progress = p
- End Set
End Property
Private ReadOnly _SavedPostsIDs As New List(Of String)
Private LastCursor As String = String.Empty
- Private FirstLoadingDone As Boolean = True
- ''' Video downloader initializer
- Private Sub New()
+ Private FirstLoadingDone As Boolean = False
+ Friend Property GetStories As Boolean
+ Friend Property GetTaggedData As Boolean
+ Friend Overrides Function ExchangeOptionsGet() As Object
+ Return New EditorExchangeOptions(HOST.Source) With {.GetStories = GetStories, .GetTagged = GetTaggedData}
+ End Function
+ Friend Overrides Sub ExchangeOptionsSet(ByVal Obj As Object)
+ If Not Obj Is Nothing AndAlso TypeOf Obj Is EditorExchangeOptions Then
+ With DirectCast(Obj, EditorExchangeOptions)
+ GetStories = .GetStories
+ GetTaggedData = .GetTagged
+ End With
+ End If
End Sub
- ''' Default initializer
- Friend Sub New(ByVal u As UserInfo, Optional ByVal _LoadUserInformation As Boolean = True, Optional ByVal InvokeImageHandler As Boolean = True)
- MyBase.New(InvokeImageHandler)
- User = u
- If _LoadUserInformation Then LoadUserInformation()
+ Friend Sub New()
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)
+ 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_GetStories, GetStories.BoolToInteger)
+ Container.Add(Name_GetTagged, GetTaggedData.BoolToInteger)
+ Container.Add(Name_TaggedChecked, TaggedChecked.BoolToInteger)
End If
End Sub
+#Region "Download data"
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
Try
_InstaHash = String.Empty
HasError = False
If Not LastCursor.IsEmptyString Then
- DownloadData(LastCursor, Token)
+ DownloadData(LastCursor, Sections.Timeline, Token)
ThrowAny(Token)
If Not HasError Then FirstLoadingDone = True
End If
If Not HasError Then
- DownloadData(String.Empty, Token)
+ DownloadData(String.Empty, Sections.Timeline, Token)
ThrowAny(Token)
If Not HasError Then FirstLoadingDone = True
End If
If FirstLoadingDone Then LastCursor = String.Empty
- If IsSavedPosts Then DownloadPosts(Token)
+ If IsSavedPosts Then
+ DownloadPosts(Token)
+ ElseIf MySiteSettings.StoriesAndTaggedReady Then
+ If GetStories Then DownloadData(String.Empty, Sections.Stories, Token)
+ If GetTaggedData Then DownloadData(String.Empty, Sections.Tagged, Token)
+ End If
If WaitNotificationMode = WNM.SkipTemp Or WaitNotificationMode = WNM.SkipCurrent Then WaitNotificationMode = WNM.Notify
+ Catch eex As ExitException
Catch ex As Exception
ProcessException(ex, Token, "[API.Instagram.UserData.DownloadDataF", False)
End Try
End Sub
Private _InstaHash As String = String.Empty
+ Friend Enum Sections
+ Timeline
+ Tagged
+ Stories
+ End Enum
#Region "429 bypass"
- Friend RequestsCount As Integer = 0
+ Friend Property RequestsCount As Integer = 0
Friend Enum WNM As Integer
Notify = 0
SkipCurrent = 1
@@ -85,11 +108,11 @@ Namespace API.Instagram
Private ProgressTempSet As Boolean = False
Private Const InstAborted As String = "InstAborted"
Private Function Ready() As Boolean
- With Settings(Sites.Instagram)
- If Not .InstagramReadyForDownload Then
+ With MySiteSettings
+ If Not .ReadyForDownload Then
If WaitNotificationMode = WNM.Notify Then
Dim m As New MMessage("Instagram [too many requests] error." & vbCr &
- $"The program suggests waiting {If(Settings(Sites.Instagram).InstagramLastApplyingValue, 0)} minutes." & vbCr &
+ $"The program suggests waiting {If(.LastApplyingValue, 0)} minutes." & vbCr &
"What do you want to do?", "Waiting for Instagram download...",
{
New MsgBoxButton("Wait") With {.ToolTip = "Wait and ask again when the error is found."},
@@ -105,7 +128,7 @@ Namespace API.Instagram
Case Else : WaitNotificationMode = WNM.SkipTemp
End Select
End If
- If Not ProgressTempSet Then Progress.InformationTemporary = $"Waiting until {Settings(Sites.Instagram).GetInstaWaitDate().ToString(ParsersDataDateProvider)}"
+ If Not ProgressTempSet Then Progress.InformationTemporary = $"Waiting until { .GetWaitDate().ToString(ParsersDataDateProvider)}"
ProgressTempSet = True
Return False
Else
@@ -115,22 +138,28 @@ Namespace API.Instagram
End Function
Private Sub ReconfigureAwaiter()
If WaitNotificationMode = WNM.SkipTemp Then WaitNotificationMode = WNM.Notify
- If Caught429 Then Caught429 = False : RequestsCount = 0
+ If Caught429 Then Caught429 = False ': RequestsCount = 0
ProgressTempSet = False
End Sub
Private Sub NextRequest(ByVal StartWait As Boolean)
- With Settings(Sites.Instagram)
- If StartWait And (RequestsCount Mod .RequestsWaitTimerTaskCount.Value) = 0 Then Thread.Sleep(.RequestsWaitTimer)
- If RequestsCount >= MaxPostsCount - 5 Then Thread.Sleep(.SleepTimerOnPostsLimit)
+ With MySiteSettings
+ If StartWait And RequestsCount > 0 And (RequestsCount Mod .RequestsWaitTimerTaskCount.Value) = 0 Then Thread.Sleep(CInt(.RequestsWaitTimer.Value))
+ If RequestsCount >= MaxPostsCount - 5 Then Thread.Sleep(CInt(.SleepTimerOnPostsLimit.Value))
End With
End Sub
#End Region
- Private Overloads Sub DownloadData(ByVal Cursor As String, ByVal Token As CancellationToken)
+ Private Const StoriesFolder As String = "Stories"
+ Private Const TaggedFolder As String = "Tagged"
+ Private TaggedChecked As Boolean = False
+ Private Overloads Sub DownloadData(ByVal Cursor As String, ByVal Section As Sections, ByVal Token As CancellationToken)
Dim URL$ = String.Empty
+ Dim StoriesList As List(Of String) = Nothing
+ Dim StoriesRequested As Boolean = False
Dim _DownloadComplete As Boolean = False
LastCursor = Cursor
Try
Do While Not _DownloadComplete
+ ThrowAny(Token)
If Not Ready() Then Thread.Sleep(10000) : ThrowAny(Token) : Continue Do
ReconfigureAwaiter()
@@ -138,73 +167,131 @@ Namespace API.Instagram
Dim n As EContainer, nn As EContainer, node As EContainer
Dim HasNextPage As Boolean = False
Dim EndCursor$ = String.Empty
- Dim PostID$ = String.Empty, PostDate$ = String.Empty
-
+ Dim PostID$ = String.Empty, PostDate$ = String.Empty, SpecFolder$ = String.Empty
+ Dim TaggedCount%
+ Dim ENode() As Object = Nothing
NextRequest(True)
'Check environment
If Cursor.IsEmptyString And _InstaHash.IsEmptyString Then _
- _InstaHash = If(IsSavedPosts, Settings(Sites.Instagram).InstaHash_SP, Settings(Sites.Instagram).InstaHash).Value
- If _InstaHash.IsEmptyString Then Throw New ArgumentNullException(IIf(IsSavedPosts, "InstaHashSavedPosts", "InstaHash"), "Query hash is null")
+ _InstaHash = CStr(If(IsSavedPosts, MySiteSettings.HashSavedPosts, MySiteSettings.Hash).Value)
+ AuthNullException.ThrowIfNull(Section, IsSavedPosts, MySiteSettings)
If ID.IsEmptyString Then GetUserId()
If ID.IsEmptyString Then Throw New ArgumentException("User ID is not detected", "ID")
'Create query
- Dim vars$ = "{""id"":" & ID & ",""first"":50,""after"":""" & Cursor & """}"
- vars = SymbolsConverter.ASCII.EncodeSymbolsOnly(vars)
- URL = $"https://www.instagram.com/graphql/query/?query_hash={_InstaHash}&variables={vars}"
+ Select Case Section
+ Case Sections.Timeline
+ Dim vars$ = "{""id"":" & ID & ",""first"":50,""after"":""" & Cursor & """}"
+ vars = SymbolsConverter.ASCII.EncodeSymbolsOnly(vars)
+ URL = $"https://www.instagram.com/graphql/query/?query_hash={_InstaHash}&variables={vars}"
+ ENode = {"data", "user", 0}
+ Case Sections.Tagged
+ URL = $"https://i.instagram.com/api/v1/usertags/{ID}/feed/?count=50&max_id={Cursor}"
+ ENode = {"items"}
+ SpecFolder = TaggedFolder
+ Case Sections.Stories
+ If Not StoriesRequested Then
+ StoriesList = GetStoriesList()
+ MySiteSettings.TooManyRequests(False)
+ RequestsCount += 1
+ ThrowAny(Token)
+ End If
+ If StoriesList.ListExists Then
+ GetStoriesData(StoriesList, Token)
+ MySiteSettings.TooManyRequests(False)
+ RequestsCount += 1
+ End If
+ If StoriesList.ListExists Then
+ Continue Do
+ Else
+ Throw New ExitException(_DownloadComplete)
+ End If
+ End Select
'Get response
Dim r$ = Responser.GetResponse(URL,, EDP.ThrowException)
- Settings(Sites.Instagram).InstagramTooManyRequests(False)
+ MySiteSettings.TooManyRequests(False)
RequestsCount += 1
ThrowAny(Token)
'Data
If Not r.IsEmptyString Then
Using j As EContainer = JsonDocument.Parse(r).XmlIfNothing
- n = j.ItemF({"data", "user", 0}).XmlIfNothing
+ n = j.ItemF(ENode).XmlIfNothing
If n.Count > 0 Then
- If n.Contains("page_info") Then
- With n("page_info")
- HasNextPage = .Value("has_next_page").FromXML(Of Boolean)(False)
- EndCursor = .Value("end_cursor")
- End With
- End If
- n = n("edges").XmlIfNothing
- If n.Count > 0 Then
- For Each nn In n
- ThrowAny(Token)
- node = nn(0).XmlIfNothing
- If IsSavedPosts Then
- PostID = node.Value("shortcode")
- If Not PostID.IsEmptyString Then
- If _TempPostsList.Contains(PostID) Then Exit Sub Else _SavedPostsIDs.Add(PostID)
- End If
- Else
- PostID = node.Value("id")
- If Not PostID.IsEmptyString And _TempPostsList.Contains(PostID) Then Exit Sub
- _TempPostsList.Add(PostID)
- PostDate = node.Value("taken_at_timestamp")
- ObtainMedia(node, PostID, PostDate)
+ Select Case Section
+ Case Sections.Timeline
+ If n.Contains("page_info") Then
+ With n("page_info")
+ HasNextPage = .Value("has_next_page").FromXML(Of Boolean)(False)
+ EndCursor = .Value("end_cursor")
+ End With
End If
- Next
- End If
+ n = n("edges").XmlIfNothing
+ If n.Count > 0 Then
+ For Each nn In n
+ ThrowAny(Token)
+ node = nn(0).XmlIfNothing
+ If IsSavedPosts Then
+ PostID = node.Value("shortcode")
+ If Not PostID.IsEmptyString Then
+ If _TempPostsList.Contains(PostID) Then Throw New ExitException(_DownloadComplete) Else _SavedPostsIDs.Add(PostID)
+ End If
+ Else
+ PostID = node.Value("id")
+ If Not PostID.IsEmptyString And _TempPostsList.Contains(PostID) Then Throw New ExitException(_DownloadComplete)
+ _TempPostsList.Add(PostID)
+ PostDate = node.Value("taken_at_timestamp")
+ ObtainMedia(node, PostID, PostDate, SpecFolder)
+ End If
+ Next
+ End If
+ Case Sections.Tagged
+ HasNextPage = j.Value("more_available").FromXML(Of Boolean)(False)
+ EndCursor = j.Value("next_max_id")
+ For Each nn In n
+ PostID = $"Tagged_{nn.Value("id")}"
+ If Not PostID.IsEmptyString And _TempPostsList.Contains(PostID) Then Throw New ExitException(_DownloadComplete)
+ _TempPostsList.Add(PostID)
+ ObtainMedia2(nn, PostID, SpecFolder)
+ Next
+ If Not TaggedChecked Then
+ TaggedCount = j.Value("total_count").FromXML(Of Integer)(0)
+ TaggedChecked = True
+ If TaggedChecked > 200 Then
+ Dim a% = MsgBoxE({$"The number of tagged posts is {TaggedCount.NumToString(New ANumbers With {
+ .FormatOptions = ANumbers.Options.GroupIntegral})}" & vbCr &
+ "The tagged data download operation can take a long time.", "Too much tagged data"}, vbExclamation,,,
+ {"Continue",
+ New MsgBoxButton("Disable and cancel") With {
+ .ToolTip = "Disable downloading tagged data and cancel downloading tagged data."},
+ "Cancel"})
+ If a > 0 Then
+ If a = 1 Then GetTaggedData = False
+ Throw New ExitException(_DownloadComplete)
+ End If
+ End If
+ End If
+ End Select
Else
If j.Value("status") = "ok" AndAlso j({"data", "user"}).XmlIfNothing.Count = 0 AndAlso _TempMediaList.Count = 0 Then
- Settings(Sites.Instagram).InstaHashUpdateRequired.Value = True
+ MySiteSettings.HashUpdateRequired.Value = True
UserExists = False
- _DownloadComplete = True
- Exit Sub
+ Throw New ExitException(_DownloadComplete)
End If
End If
End Using
Else
- _DownloadComplete = True
- Exit Sub
+ Throw New ExitException(_DownloadComplete)
End If
_DownloadComplete = True
- If HasNextPage And Not EndCursor.IsEmptyString Then DownloadData(EndCursor, Token)
+ If HasNextPage And Not EndCursor.IsEmptyString Then DownloadData(EndCursor, Section, Token)
+ Catch iane As AuthNullException
+ ErrorsDescriber.Execute(EDP.SendInLog, iane)
+ Throw New ExitException(_DownloadComplete)
+ Catch eex As ExitException
+ Throw eex
Catch oex As OperationCanceledException When Token.IsCancellationRequested
Exit Do
Catch dex As ObjectDisposedException When Disposed
@@ -213,6 +300,8 @@ Namespace API.Instagram
If DownloadingException(ex, $"data downloading error [{URL}]") = 1 Then Continue Do Else Exit Do
End Try
Loop
+ Catch eex2 As ExitException
+ If (Section = Sections.Timeline Or Section = Sections.Tagged) And Not Cursor.IsEmptyString Then Throw eex2
Catch oex2 As OperationCanceledException When Token.IsCancellationRequested Or oex2.HelpLink = InstAborted
If oex2.HelpLink = InstAborted Then HasError = True
Catch DoEx As Exception
@@ -225,6 +314,7 @@ Namespace API.Instagram
Dim _Index% = 0
Try
Do While Not _DownloadComplete
+ ThrowAny(Token)
If Not Ready() Then Thread.Sleep(10000) : ThrowAny(Token) : Continue Do
ReconfigureAwaiter()
@@ -238,10 +328,10 @@ Namespace API.Instagram
_Index = i
URL = $"https://instagram.com/p/{_SavedPostsIDs(i)}/?__a=1"
ThrowAny(Token)
- NextRequest((i + 1 Mod 5) = 0)
+ NextRequest(((i + 1) Mod 5) = 0)
ThrowAny(Token)
r = Responser.GetResponse(URL,, e)
- Settings(Sites.Instagram).InstagramTooManyRequests(False)
+ MySiteSettings.TooManyRequests(False)
RequestsCount += 1
If Not r.IsEmptyString Then
j = JsonDocument.Parse(r)
@@ -249,7 +339,7 @@ Namespace API.Instagram
_MediaObtained = False
If j.Contains({"graphql", "shortcode_media"}) Then
With j({"graphql", "shortcode_media"}).XmlIfNothing
- If .Count > 0 Then ObtainMedia(.Self, _SavedPostsIDs(i), String.Empty) : _MediaObtained = True
+ If .Count > 0 Then ObtainMedia(.Self, _SavedPostsIDs(i), String.Empty, String.Empty) : _MediaObtained = True
End With
End If
If Not _MediaObtained AndAlso j.Contains("items") Then
@@ -263,8 +353,8 @@ Namespace API.Instagram
End If
End If
Next
- _DownloadComplete = True
End If
+ _DownloadComplete = True
Catch oex As OperationCanceledException When Token.IsCancellationRequested
Exit Do
Catch dex As ObjectDisposedException When Disposed
@@ -279,7 +369,9 @@ Namespace API.Instagram
ProcessException(DoEx, Token, $"downloading saved posts error [{URL}]")
End Try
End Sub
- Private Sub ObtainMedia(ByVal node As EContainer, ByVal PostID As String, ByVal PostDate As String)
+#End Region
+#Region "Obtain Media"
+ Private Sub ObtainMedia(ByVal node As EContainer, ByVal PostID As String, ByVal PostDate As String, ByVal SpecFolder As String)
Dim CreateMedia As Action(Of EContainer) =
Sub(ByVal e As EContainer)
Dim t As UTypes = If(e.Value("is_video").FromXML(Of Boolean)(False), UTypes.Video, UTypes.Picture)
@@ -289,7 +381,7 @@ Namespace API.Instagram
Else
tmpValue = e.Value("video_url")
End If
- If Not tmpValue.IsEmptyString Then _TempMediaList.ListAddValue(MediaFromData(t, tmpValue, PostID, PostDate), LNC)
+ If Not tmpValue.IsEmptyString Then _TempMediaList.ListAddValue(MediaFromData(t, tmpValue, PostID, PostDate, SpecFolder), LNC)
End Sub
If node.Contains({"edge_sidecar_to_children", "edges"}) Then
For Each edge As EContainer In node({"edge_sidecar_to_children", "edges"}) : CreateMedia(edge("node").XmlIfNothing) : Next
@@ -297,7 +389,7 @@ Namespace API.Instagram
CreateMedia(node)
End If
End Sub
- Private Sub ObtainMedia2(ByVal n As EContainer, ByVal PostID As String)
+ Private Sub ObtainMedia2(ByVal n As EContainer, ByVal PostID As String, Optional ByVal SpecialFolder As String = Nothing)
Try
Dim img As Predicate(Of EContainer) = Function(_img) Not _img.Name.IsEmptyString AndAlso _img.Name.StartsWith("image_versions") AndAlso _img.Count > 0
Dim vid As Predicate(Of EContainer) = Function(_vid) Not _vid.Name.IsEmptyString AndAlso _vid.Name.StartsWith("video_versions") AndAlso _vid.Count > 0
@@ -322,7 +414,7 @@ Namespace API.Instagram
l.ListAddList(.Select(ss), LNC)
If l.Count > 0 Then
l.Sort()
- _TempMediaList.ListAddValue(MediaFromData(UTypes.Picture, l.First.Data, PostID, Nothing), LNC)
+ _TempMediaList.ListAddValue(MediaFromData(UTypes.Picture, l.First.Data, PostID, Nothing, SpecialFolder), LNC)
l.Clear()
End If
End If
@@ -337,7 +429,7 @@ Namespace API.Instagram
l.ListAddList(.Select(ss), LNC)
If l.Count > 0 Then
l.Sort()
- _TempMediaList.ListAddValue(MediaFromData(UTypes.Video, l.First.Data, PostID, Nothing), LNC)
+ _TempMediaList.ListAddValue(MediaFromData(UTypes.Video, l.First.Data, PostID, Nothing, SpecialFolder), LNC)
l.Clear()
End If
End If
@@ -346,7 +438,7 @@ Namespace API.Instagram
Case 8
With n("carousel_media").XmlIfNothing
If .Count > 0 Then
- For Each d In .Self : ObtainMedia2(d, PostID) : Next
+ For Each d In .Self : ObtainMedia2(d, PostID, SpecialFolder) : Next
End If
End With
End Select
@@ -354,9 +446,10 @@ Namespace API.Instagram
l.Clear()
End If
Catch ex As Exception
- ErrorsDescriber.Execute(EDP.SendInLog, ex, "API.Instagram.GetGallery")
+ ErrorsDescriber.Execute(EDP.SendInLog, ex, "API.Instagram.ObtainMedia2")
End Try
End Sub
+#End Region
Private Sub GetUserId()
Try
Dim r$ = Responser.GetResponse($"https://www.instagram.com/{Name}/?__a=1",, EDP.ThrowException)
@@ -373,6 +466,59 @@ Namespace API.Instagram
End If
End Try
End Sub
+#Region "Pinned stories"
+ Private Sub GetStoriesData(ByRef StoriesList As List(Of String), ByVal Token As CancellationToken)
+ Const ReqUrl$ = "https://i.instagram.com/api/v1/feed/reels_media/?{0}"
+ Dim tmpList As IEnumerable(Of String)
+ Dim qStr$, r$, sFolder$, storyID$
+ Dim i% = -1
+ Dim jj As EContainer, s As EContainer
+ ThrowAny(Token)
+ If StoriesList.ListExists Then
+ tmpList = StoriesList.Take(5)
+ If tmpList.ListExists Then
+ qStr = String.Format(ReqUrl, tmpList.Select(Function(q) $"reel_ids=highlight:{q}").ListToString(, "&"))
+ r = Responser.GetResponse(qStr,, EDP.ThrowException)
+ ThrowAny(Token)
+ If Not r.IsEmptyString Then
+ Using j As EContainer = JsonDocument.Parse(r).XmlIfNothing
+ If j.Contains("reels") Then
+ For Each jj In j("reels")
+ i += 1
+ sFolder = jj.Value("title")
+ storyID = jj.Value("id").Replace("highlight:", String.Empty)
+ If sFolder.IsEmptyString Then sFolder = $"Story_{storyID}"
+ If sFolder.IsEmptyString Then sFolder = $"Story_{i}"
+ sFolder = $"{StoriesFolder}\{sFolder}"
+ If Not storyID.IsEmptyString Then storyID &= ":"
+ With jj("items").XmlIfNothing
+ If .Count > 0 Then
+ For Each s In .Self : ThrowAny(Token) : ObtainMedia2(s, storyID & s.Value("id"), sFolder) : Next
+ End If
+ End With
+ Next
+ End If
+ End Using
+ End If
+ StoriesList.RemoveRange(0, tmpList.Count)
+ End If
+ End If
+ End Sub
+ Private Function GetStoriesList() As List(Of String)
+ Try
+ Dim r$ = Responser.GetResponse($"https://i.instagram.com/api/v1/highlights/{ID}/highlights_tray/",, EDP.ThrowException)
+ If Not r.IsEmptyString Then
+ Using j As EContainer = JsonDocument.Parse(r).XmlIfNothing()("tray").XmlIfNothing
+ If j.Count > 0 Then Return j.Select(Function(jj) jj.Value("id").Replace("highlight:", String.Empty)).ListIfNothing
+ End Using
+ End If
+ Return Nothing
+ Catch ex As Exception
+ DownloadingException(ex, "API.Instagram.GetStoriesList")
+ Return Nothing
+ End Try
+ End Function
+#End Region
Protected Overrides Sub ReparseVideo(ByVal Token As CancellationToken)
End Sub
Protected Overrides Sub DownloadContent(ByVal Token As CancellationToken)
@@ -388,38 +534,40 @@ Namespace API.Instagram
ElseIf Responser.StatusCode = HttpStatusCode.BadRequest Then
HasError = True
MyMainLOG = "Instagram credentials have expired"
- Settings(Sites.Instagram).InstaHashUpdateRequired.Value = True
+ MySiteSettings.HashUpdateRequired.Value = True
ElseIf Responser.StatusCode = 429 Then
- With Settings(Sites.Instagram)
- Dim WaiterExists As Boolean = .InstagramLastApplyingValue.HasValue
- .InstagramTooManyRequests(True)
- If Not WaiterExists Then .InstagramLastApplyingValue = 2
+ With MySiteSettings
+ Dim WaiterExists As Boolean = .LastApplyingValue.HasValue
+ .TooManyRequests(True)
+ If Not WaiterExists Then .LastApplyingValue = 2
End With
Caught429 = True
MyMainLOG = $"Number of requests before error 429: {RequestsCount}"
Return 1
Else
- Settings(Sites.Instagram).InstaHashUpdateRequired.Value = True
+ MySiteSettings.HashUpdateRequired.Value = True
If Not FromPE Then LogError(ex, Message) : HasError = True
Return 0
End If
Return 2
End Function
- Private Shared Function MediaFromData(ByVal t As UTypes, ByVal _URL As String, ByVal PostID As String, ByVal PostDate As String) As UserMedia
+ Private Shared Function MediaFromData(ByVal t As UTypes, ByVal _URL As String, ByVal PostID As String, ByVal PostDate As String,
+ Optional ByVal SpecialFolder As String = Nothing) As UserMedia
_URL = LinkFormatterSecure(RegexReplace(_URL.Replace("\", String.Empty), LinkPattern))
Dim m As New UserMedia(_URL, t) With {.Post = New UserPost With {.ID = PostID}}
If Not m.URL.IsEmptyString Then m.File = CStr(RegexReplace(m.URL, FilesPattern))
If Not PostDate.IsEmptyString Then m.Post.Date = AConvert(Of Date)(PostDate, DateProvider, Nothing) Else m.Post.Date = Nothing
+ m.SpecialFolder = SpecialFolder
Return m
End Function
- Friend Shared Function GetVideoInfo(ByVal URL As String) As IEnumerable(Of UserMedia)
+ Friend Shared Function GetVideoInfo(ByVal URL As String, ByVal r As Response) As IEnumerable(Of UserMedia)
Try
If Not URL.IsEmptyString AndAlso URL.Contains("instagram.com") Then
Dim PID$ = RegexReplace(URL, RParams.DMS(".*?instagram.com/p/([_\w\d]+)", 1))
If Not PID.IsEmptyString Then
Using t As New UserData
- t.Responser = New PersonalUtilities.Tools.WEB.Response
- t.Responser.Copy(Settings(Sites.Instagram).Responser)
+ t.Responser = New Response
+ t.Responser.Copy(r)
t._SavedPostsIDs.Add(PID)
t.DownloadPosts(Nothing)
Return ListAddList(Nothing, t._TempMediaList)
diff --git a/SCrawler/API/Reddit/Channel.vb b/SCrawler/API/Reddit/Channel.vb
index 55f6d44..fdc0b9e 100644
--- a/SCrawler/API/Reddit/Channel.vb
+++ b/SCrawler/API/Reddit/Channel.vb
@@ -10,264 +10,24 @@ Imports PersonalUtilities.Tools
Imports PersonalUtilities.Forms.Toolbars
Imports PersonalUtilities.Functions.XML
Imports SCrawler.API.Base
+Imports SCrawler.Plugin.Hosts
Imports System.Threading
Namespace API.Reddit
Friend Class Channel : Implements ICollection(Of UserPost), IEquatable(Of Channel), IComparable(Of Channel),
- IRangeSwitcherContainer(Of UserPost), ILoaderSaver, IMyEnumerator(Of UserPost), IChannelLimits, IUserData, IDisposable
+ IRangeSwitcherContainer(Of UserPost), ILoaderSaver, IMyEnumerator(Of UserPost), IChannelLimits, IDisposable
#Region "XML Nodes' Names"
Private Const Name_Name As String = "Name"
Private Const Name_ID As String = "ID"
Private Const Name_Date As String = "Date"
Private Const Name_PostsNode As String = "Posts"
Private Const Name_UsersAdded As String = "UsersAdded"
+ Private Const Name_UsersExistent As String = "UsersExistent"
Private Const Name_PostsDownloaded As String = "PostsDownloaded"
#End Region
Friend Const DefaultDownloadLimitCount As Integer = 1000
-#Region "IUserData Support"
- Private Event OnUserUpdated As IUserData.OnUserUpdatedEventHandler Implements IUserData.OnUserUpdated
- Friend Property Instance As IUserData
- Private Property IUserData_ParseUserMediaOnly As Boolean = False Implements IUserData.ParseUserMediaOnly
- Private Property IUserData_Exists As Boolean Implements IUserData.Exists
- Get
- Return Instance.Exists
- End Get
- Set(ByVal e As Boolean)
- End Set
- End Property
- Private Property IUserData_Suspended As Boolean Implements IUserData.Suspended
- Get
- Return Instance.Suspended
- End Get
- Set(ByVal s As Boolean)
- End Set
- End Property
- Private ReadOnly Property IUserData_IsCollection As Boolean Implements IUserData.IsCollection
- Get
- Return Instance.IsCollection
- End Get
- End Property
- Private Property IUserData_CollectionName As String Implements IUserData.CollectionName
- Get
- Return Instance.CollectionName
- End Get
- Set(ByVal NewName As String)
- Instance.CollectionName = NewName
- End Set
- End Property
- Private ReadOnly Property IUserData_IncludedInCollection As Boolean Implements IUserData.IncludedInCollection
- Get
- Return Instance.IncludedInCollection
- End Get
- End Property
- Private ReadOnly Property IUserData_Labels As List(Of String) Implements IUserData.Labels
- Get
- Return Instance.Labels
- End Get
- End Property
- Private ReadOnly Property IUserData_IsChannel As Boolean = True Implements IUserData.IsChannel
- Private Property IUserData_ReadyForDownload As Boolean Implements IUserData.ReadyForDownload
- Get
- Return Instance.ReadyForDownload
- End Get
- Set(ByVal IsReady As Boolean)
- Instance.ReadyForDownload = IsReady
- End Set
- End Property
- Private Property IUserData_File As SFile Implements IUserData.File
- Get
- Return Instance.File
- End Get
- Set(ByVal NewFile As SFile)
- Instance.File = NewFile
- End Set
- End Property
- Private Property IUserData_FileExists As Boolean Implements IUserData.FileExists
- Get
- Return Instance.FileExists
- End Get
- Set(ByVal IsExists As Boolean)
- Instance.FileExists = IsExists
- End Set
- End Property
- Private Property IUserData_DownloadedPictures As Integer Implements IUserData.DownloadedPictures
- Get
- Return Instance.DownloadedPictures
- End Get
- Set(ByVal c As Integer)
- Instance.DownloadedPictures = c
- End Set
- End Property
- Private Property IUserData_DownloadedVideos As Integer Implements IUserData.DownloadedVideos
- Get
- Return Instance.DownloadedVideos
- End Get
- Set(ByVal c As Integer)
- Instance.DownloadedVideos = c
- End Set
- End Property
- Private ReadOnly Property IUserData_DownloadedTotal(Optional Total As Boolean = True) As Integer Implements IUserData.DownloadedTotal
- Get
- Return Instance.DownloadedTotal
- End Get
- End Property
- Private ReadOnly Property IUserData_DownloadedInformation As String Implements IUserData.DownloadedInformation
- Get
- Return Instance.DownloadedInformation
- End Get
- End Property
- Private Property IUserData_HasError As Boolean Implements IUserData.HasError
- Get
- Return Instance.HasError
- End Get
- Set(ByVal e As Boolean)
- Instance.HasError = e
- End Set
- End Property
- Private ReadOnly Property IUserData_FitToAddParams As Boolean Implements IUserData.FitToAddParams
- Get
- Return Instance.FitToAddParams
- End Get
- End Property
- Private ReadOnly Property IUserData_LVIKey As String Implements IUserData.LVIKey
- Get
- Return Instance.LVIKey
- End Get
- End Property
- Private ReadOnly Property IUserData_LVIIndex As Integer Implements IUserData.LVIIndex
- Get
- Return Instance.LVIIndex
- End Get
- End Property
- Private Property IUserData_DownloadImages As Boolean Implements IUserData.DownloadImages
- Get
- Return Instance.DownloadImages
- End Get
- Set(ByVal d As Boolean)
- Instance.DownloadImages = d
- End Set
- End Property
- Private Property IUserData_DownloadVideos As Boolean Implements IUserData.DownloadVideos
- Get
- Return Instance.DownloadVideos
- End Get
- Set(ByVal d As Boolean)
- Instance.DownloadVideos = d
- End Set
- End Property
- Private ReadOnly Property IUserData_Self As IUserData Implements IUserData.Self
- Get
- Return Instance
- End Get
- End Property
- Private Property IUserData_DownloadTopCount As Integer? Implements IUserData.DownloadTopCount
- Get
- Return Instance.DownloadTopCount
- End Get
- Set(ByVal c As Integer?)
- Instance.DownloadTopCount = c
- End Set
- End Property
- Friend Property Site As Sites = Sites.Reddit Implements IContentProvider.Site
- Private Property IUserData_FriendlyName As String Implements IContentProvider.FriendlyName
- Get
- Return Instance.FriendlyName
- End Get
- Set(ByVal NewName As String)
- Instance.FriendlyName = NewName
- End Set
- End Property
- Private Property IUserData_Description As String Implements IContentProvider.Description
- Get
- Return Instance.Description
- End Get
- Set(ByVal d As String)
- Instance.Description = d
- End Set
- End Property
- Private Property IUserData_Favorite As Boolean Implements IContentProvider.Favorite
- Get
- Return Instance.Favorite
- End Get
- Set(ByVal f As Boolean)
- Instance.Favorite = f
- End Set
- End Property
- Private Property IUserData_Temporary As Boolean Implements IContentProvider.Temporary
- Get
- Return Instance.Temporary
- End Get
- Set(ByVal t As Boolean)
- Instance.Temporary = t
- End Set
- End Property
- Private Sub IUserData_SetPicture(ByVal f As SFile) Implements IUserData.SetPicture
- Instance.SetPicture(f)
- End Sub
- Private Sub IUserData_LoadUserInformation() Implements IUserData.LoadUserInformation
- Instance.LoadUserInformation()
- End Sub
- Private Sub IUserData_UpdateUserInformation() Implements IUserData.UpdateUserInformation
- Instance.UpdateUserInformation()
- End Sub
- Private Sub IUserData_OpenFolder() Implements IUserData.OpenFolder
- Instance.OpenFolder()
- End Sub
- Private Sub IUserData_OpenSite() Implements IContentProvider.OpenSite
- Instance.OpenSite()
- End Sub
- Private Sub IUserData_DownloadData(ByVal Token As CancellationToken) Implements IContentProvider.DownloadData
- DownloadData(Token, False, Nothing)
- End Sub
- Private Function IUserData_GetPicture() As Image Implements IUserData.GetPicture
- Return Instance.GetPicture()
- End Function
- Private Function IUserData_GetLVI(ByVal Destination As ListView) As ListViewItem Implements IUserData.GetLVI
- Return Instance.GetLVI(Destination)
- End Function
- Private Function IUserData_GetLVIGroup(ByVal Destination As ListView) As ListViewGroup Implements IUserData.GetLVIGroup
- Return Instance.GetLVIGroup(Destination)
- End Function
- Private Function IUserData_Delete() As Integer Implements IUserData.Delete
- Return DirectCast(Instance, UserDataBase).DeleteF(Me)
- End Function
- Private Function IUserData_MoveFiles(ByVal CollectionName As String) As Boolean Implements IUserData.MoveFiles
- Return DirectCast(Instance, UserDataBase).MoveFilesF(Me, CollectionName)
- End Function
-#End Region
- Private _Name As String = String.Empty
- Friend Property Name As String Implements IUserData.Name
- Get
- If IsRegularChannel Then
- Return Instance.Name
- Else
- Return _Name
- End If
- End Get
- Set(ByVal NewName As String)
- If IsRegularChannel Then
- Instance.Name = NewName
- Else
- _Name = NewName
- End If
- End Set
- End Property
- Private _ID As String = String.Empty
- Friend Property ID As String Implements IUserData.ID
- Get
- If IsRegularChannel Then
- Return Instance.ID
- Else
- Return _ID
- End If
- End Get
- Set(ByVal NewID As String)
- If IsRegularChannel Then
- Instance.ID = NewID
- Else
- _ID = NewID
- End If
- End Set
- End Property
+ Friend ReadOnly Property Site As String = RedditSite
+ Friend Property Name As String
+ Friend Property ID As String
Friend ReadOnly Property CUser As UserInfo
Get
Return New UserInfo(Me)
@@ -313,17 +73,30 @@ Namespace API.Reddit
End Get
End Property
Private ReadOnly Property Range As RangeSwitcher(Of UserPost)
- Friend ReadOnly Property CountOfAddedUsers As List(Of Integer)
- Friend ReadOnly Property CountOfLoadedPostsPerSession As List(Of Integer)
+#Region "Statistics support"
+ Private ReadOnly CountOfAddedUsers As List(Of Integer)
+ Private ReadOnly CountOfLoadedPostsPerSession As List(Of Integer)
+ Friend ReadOnly Property ChannelExistentUserNames As List(Of String)
Private _FirstUserAdded As Boolean = False
- Friend Sub UserAdded(Optional ByVal IsAdded As Boolean = True)
+ Friend Sub UserAdded(ByVal UserName As String, Optional ByVal IsAdded As Boolean = True)
If Not _FirstUserAdded Then CountOfAddedUsers.Add(0) : _FirstUserAdded = True
Dim v% = CountOfAddedUsers.Last
v += IIf(IsAdded, 1, -1)
If v < 0 Then v = 0
CountOfAddedUsers(CountOfAddedUsers.Count - 1) = v
+ If Not ChannelExistentUserNames.Contains(UserName) Then ChannelExistentUserNames.Add(UserName)
+ End Sub
+ Friend Sub UpdateUsersStats()
+ If Posts.Count > 0 Or PostsLatest.Count > 0 Then
+ ChannelExistentUserNames.ListAddList((From p As UserPost In PostsAll
+ Where Not p.UserID.IsEmptyString AndAlso
+ Settings.UsersList.Exists(Function(u) u.Site = Site And u.Name = p.UserID)
+ Select p.UserID), LAP.NotContainsOnly)
+ ChannelExistentUserNames.RemoveAll(Function(u) Not Settings.UsersList.Exists(Function(uu) uu.Site = Site And uu.Name = u))
+ End If
End Sub
Friend Function GetChannelStats(ByVal Extended As Boolean) As String
+ UpdateUsersStats()
Dim s$ = String.Empty
Dim p As New ANumbers With {.FormatOptions = ANumbers.Options.GroupIntegral}
If Extended Then
@@ -332,12 +105,15 @@ Namespace API.Reddit
s.StringAppendLine($"Users added from this channel (session): {CountOfAddedUsers.LastOrDefault.NumToString(p)}")
s.StringAppendLine($"Posts downloaded (avg): {CountOfLoadedPostsPerSession.DefaultIfEmpty(0).Average.RoundUp.NumToString(p)}")
s.StringAppendLine($"Posts downloaded (session): {CountOfLoadedPostsPerSession.LastOrDefault.NumToString(p)}")
+ s.StringAppendLine($"My users in this channel: {ChannelExistentUserNames.Count.NumToString(p)}")
Else
s.StringAppend($"Users: {CountOfAddedUsers.Sum.NumToString(p)} (avg: {CountOfAddedUsers.DefaultIfEmpty(0).Average.RoundDown.NumToString(p)}; s: {CountOfAddedUsers.LastOrDefault.NumToString(p)})")
s.StringAppend($"Posts: {CountOfLoadedPostsPerSession.DefaultIfEmpty(0).Average.RoundUp.NumToString(p)} (s: {CountOfLoadedPostsPerSession.LastOrDefault.NumToString(p)})", "; ")
+ s.StringAppend($"My users: {ChannelExistentUserNames.Count.NumToString(p)}", "; ")
End If
Return s
End Function
+#End Region
#Region "Limits Support"
Private _DownloadLimitCount As Integer? = Nothing
Friend Property DownloadLimitCount As Integer? Implements IChannelLimits.DownloadLimitCount
@@ -401,31 +177,23 @@ Namespace API.Reddit
End Sub
Friend Property AutoGetLimits As Boolean = True Implements IChannelLimits.AutoGetLimits
#End Region
- Friend ReadOnly IsRegularChannel As Boolean = False
+ Friend ReadOnly Property HOST As SettingsHost
Friend Sub New()
Posts = New List(Of UserPost)
PostsLatest = New List(Of UserPost)
Range = New RangeSwitcher(Of UserPost)(Me)
CountOfAddedUsers = New List(Of Integer)
CountOfLoadedPostsPerSession = New List(Of Integer)
+ ChannelExistentUserNames = New List(Of String)
+ HOST = Settings(RedditSiteKey)
End Sub
Friend Sub New(ByVal f As SFile)
Me.New
LoadData(f, False)
End Sub
- Friend Sub New(ByVal u As UserInfo, Optional ByVal _LoadUserInformation As Boolean = True)
- Me.New
- Instance = New UserData(u, _LoadUserInformation) With {.SaveToCache = False, .SkipExistsUsers = False, .ChannelInfo = Me}
- AutoGetLimits = True
- DirectCast(Instance, UserData).SetLimit(Me)
- IsRegularChannel = True
- End Sub
Public Shared Widening Operator CType(ByVal f As SFile) As Channel
Return New Channel(f)
End Operator
- Public Shared Widening Operator CType(ByVal c As Channel) As UserDataBase
- Return DirectCast(c.Instance, UserDataBase)
- End Operator
Public Overrides Function ToString() As String
If Not Name.IsEmptyString Then
Return Name
@@ -434,30 +202,29 @@ Namespace API.Reddit
End If
End Function
Friend Sub Delete()
- If File.Exists Then File.Delete()
+ File.Delete(, SFODelete.DeleteToRecycleBin)
End Sub
Friend Sub DownloadData(ByVal Token As CancellationToken, Optional ByVal SkipExists As Boolean = True,
Optional ByVal p As MyProgress = Nothing)
Try
_Downloading = True
- If Not Instance Is Nothing Then
- Instance.DownloadData(Token)
- Else
- Using d As New UserData(CUser, False, False) With {
- .Progress = p,
- .SaveToCache = True,
- .SkipExistsUsers = SkipExists,
- .ChannelInfo = Me
- }
- d.SetLimit(Me)
- d.DownloadData(Token)
- Dim b% = Posts.Count
- Posts.ListAddList(d.GetNewChannelPosts(), LAP.NotContainsOnly)
- If Posts.Count - b > 0 Then CountOfLoadedPostsPerSession.Add(Posts.Count - b)
- Posts.Sort()
- LatestParsedDate = If(Posts.FirstOrDefault(Function(pp) pp.Date.HasValue).Date, LatestParsedDate)
- End Using
- End If
+ Using d As New UserData With {
+ .Progress = p,
+ .SaveToCache = True,
+ .SkipExistsUsers = SkipExists,
+ .ChannelInfo = Me
+ }
+ d.SetEnvironment(HOST, CUser, False)
+ d.RemoveUpdateHandlers()
+ d.SetLimit(Me)
+ d.DownloadData(Token)
+ Dim b% = Posts.Count
+ Posts.ListAddList(d.GetNewChannelPosts(), LAP.NotContainsOnly)
+ If Posts.Count - b > 0 Then CountOfLoadedPostsPerSession.Add(Posts.Count - b)
+ Posts.Sort()
+ LatestParsedDate = If(Posts.FirstOrDefault(Function(pp) pp.Date.HasValue).Date, LatestParsedDate)
+ UpdateUsersStats()
+ End Using
Catch oex As OperationCanceledException When Token.IsCancellationRequested
Finally
_Downloading = False
@@ -498,21 +265,12 @@ Namespace API.Reddit
Friend Overloads Function Equals(ByVal Other As Channel) As Boolean Implements IEquatable(Of Channel).Equals
Return ID = Other.ID
End Function
- Private Overloads Function Equals(ByVal Other As UserDataBase) As Boolean Implements IEquatable(Of UserDataBase).Equals
- If Not Instance Is Nothing Then
- Return Instance.Equals(Other)
- Else
- Return False
- End If
- End Function
Public Overloads Overrides Function Equals(ByVal Obj As Object) As Boolean
If Not Obj Is Nothing Then
If TypeOf Obj Is String Then
Return ID = CStr(Obj)
ElseIf TypeOf Obj Is Channel Then
Return Equals(DirectCast(Obj, Channel))
- ElseIf TypeOf Obj Is UserDataBase Then
- Return Equals(DirectCast(Obj, UserDataBase))
End If
End If
Return False
@@ -526,22 +284,6 @@ Namespace API.Reddit
Return ID.CompareTo(Other.ID)
End If
End Function
- Private Overloads Function CompareTo(ByVal Other As UserDataBase) As Integer Implements IComparable(Of UserDataBase).CompareTo
- If Not Instance Is Nothing Then
- Return Instance.CompareTo(Other)
- Else
- Return 0
- End If
- End Function
- Private Overloads Function CompareTo(ByVal Obj As Object) As Integer Implements IComparable.CompareTo
- If TypeOf Obj Is Channel Then
- Return CompareTo(DirectCast(Obj, Channel))
- ElseIf TypeOf Obj Is UserDataBase And Not Instance Is Nothing Then
- Return Instance.CompareTo(Obj)
- Else
- Return 0
- End If
- End Function
#End Region
#Region "ILoaderSaver Support"
Friend Overloads Function LoadData(Optional ByVal f As SFile = Nothing, Optional ByVal e As ErrorsDescriber = Nothing) As Boolean Implements ILoaderSaver.Load
@@ -553,11 +295,13 @@ Namespace API.Reddit
x.LoadData()
If x.Count > 0 Then
Dim XMLDateProvider As New ADateTime(ADateTime.Formats.BaseDateTime)
+ Dim lc As New ListAddParams(LAP.ClearBeforeAdd)
Name = x.Value(Name_Name)
ID = x.Value(Name_ID)
LatestParsedDate = AConvert(Of Date)(x.Value(Name_Date), XMLDateProvider, Nothing)
- CountOfAddedUsers.ListAddList(x.Value(Name_UsersAdded).StringToList(Of Integer)("|"), LAP.ClearBeforeAdd)
- CountOfLoadedPostsPerSession.ListAddList(x.Value(Name_PostsDownloaded).StringToList(Of Integer)("|"), LAP.ClearBeforeAdd)
+ CountOfAddedUsers.ListAddList(x.Value(Name_UsersAdded).StringToList(Of Integer)("|"), lc)
+ CountOfLoadedPostsPerSession.ListAddList(x.Value(Name_PostsDownloaded).StringToList(Of Integer)("|"), lc)
+ ChannelExistentUserNames.ListAddList(x.Value(Name_UsersExistent).StringToList(Of String)("|"), LNC)
If Not PartialLoad Then
With x(Name_PostsNode).XmlIfNothing
If .Count > 0 Then .ForEach(Sub(ee) PostsLatest.Add(New UserPost With {
@@ -572,6 +316,7 @@ Namespace API.Reddit
End Function
Friend Overloads Function Save(Optional ByVal f As SFile = Nothing, Optional ByVal e As ErrorsDescriber = Nothing) As Boolean Implements ILoaderSaver.Save
Dim XMLDateProvider As New ADateTime(ADateTime.Formats.BaseDateTime)
+ UpdateUsersStats()
Using x As New XmlFile With {.AllowSameNames = True, .Name = "Channel"}
x.Add(Name_Name, Name)
x.Add(Name_ID, ID)
@@ -584,6 +329,7 @@ Namespace API.Reddit
x.Add(Name_PostsNode, String.Empty)
x.Add(Name_UsersAdded, CountOfAddedUsers.ListToString(, "|"))
x.Add(Name_PostsDownloaded, CountOfLoadedPostsPerSession.ListToString(, "|"))
+ x.Add(Name_UsersExistent, ChannelExistentUserNames.ListToString(, "|"))
With x(Name_PostsNode)
tmpPostList.Take(200).ToList.ForEach(Sub(p) .Add(New EContainer("Post",
String.Empty,
@@ -603,11 +349,6 @@ Namespace API.Reddit
#End Region
#Region "IDisposable Support"
Private disposedValue As Boolean = False
- Friend ReadOnly Property Disposed As Boolean Implements IUserData.Disposed
- Get
- Return disposedValue
- End Get
- End Property
Protected Overridable Overloads Sub Dispose(ByVal disposing As Boolean)
If Not disposedValue Then
If disposing Then
@@ -616,8 +357,8 @@ Namespace API.Reddit
CountOfAddedUsers.Clear()
CountOfLoadedPostsPerSession.Clear()
Range.Dispose()
- If Not Instance Is Nothing Then Instance.Dispose()
- If CachePath.Exists(SFO.Path, False) Then CachePath.Delete(SFO.Path, False, False, EDP.SendInLog)
+ ChannelExistentUserNames.Clear()
+ CachePath.Delete(SFO.Path, SFODelete.None, EDP.SendInLog)
End If
disposedValue = True
End If
diff --git a/SCrawler/API/Reddit/ChannelsCollection.vb b/SCrawler/API/Reddit/ChannelsCollection.vb
index 67d34d6..2b01e2d 100644
--- a/SCrawler/API/Reddit/ChannelsCollection.vb
+++ b/SCrawler/API/Reddit/ChannelsCollection.vb
@@ -15,15 +15,39 @@ Namespace API.Reddit
Friend Shared ReadOnly Property ChannelsPath As SFile = $"{SettingsFolderName}\Channels\"
Friend Shared ReadOnly Property ChannelsPathCache As SFile = $"{Settings.GlobalPath.Value.PathWithSeparator}_CachedData\"
Private ReadOnly Channels As List(Of Channel)
+ Friend Structure ChannelImage : Implements IEquatable(Of ChannelImage)
+ Friend File As SFile
+ Friend Channel As String
+ Friend Sub New(ByVal ChannelName As String, ByVal f As SFile)
+ Channel = ChannelName
+ File = f
+ End Sub
+ Friend Overloads Function Equals(ByVal Other As ChannelImage) As Boolean Implements IEquatable(Of ChannelImage).Equals
+ Return Channel = Other.Channel And File.File = Other.File.File
+ End Function
+ Public Overloads Overrides Function Equals(ByVal Obj As Object) As Boolean
+ Return Equals(DirectCast(Obj, ChannelImage))
+ End Function
+ End Structure
Friend ReadOnly Property Downloading As Boolean
Get
- If Count > 0 Then
- Return Channels.Exists(Function(c) c.Downloading)
- Else
- Return False
- End If
+ Return Count > 0 AndAlso Channels.Exists(Function(c) c.Downloading)
End Get
End Property
+ Friend Function GetUserFiles(ByVal UserName As String) As IEnumerable(Of ChannelImage)
+ Try
+ If Settings.ChannelsAddUserImagesFromAllChannels.Value And Count > 0 Then
+ Return Channels.SelectMany(Function(c) From p As UserPost In c.Posts Where p.UserID = UserName Select New ChannelImage(c.Name, p.CachedFile))
+ Else
+ Return Nothing
+ End If
+ Catch ex As Exception
+ Return ErrorsDescriber.Execute(EDP.SendInLog + EDP.ReturnValue, ex)
+ End Try
+ End Function
+ Friend Sub UpdateUsersStats()
+ If Channels.Count > 0 Then Channels.ForEach(Sub(c) c.UpdateUsersStats())
+ End Sub
#Region "Limits Support"
Friend Property DownloadLimitCount As Integer? Implements IChannelLimits.DownloadLimitCount
Private Property DownloadLimitPost As String Implements IChannelLimits.DownloadLimitPost
diff --git a/SCrawler/API/Reddit/Declarations.vb b/SCrawler/API/Reddit/Declarations.vb
index 4a37266..ab498b5 100644
--- a/SCrawler/API/Reddit/Declarations.vb
+++ b/SCrawler/API/Reddit/Declarations.vb
@@ -10,6 +10,8 @@ Imports PersonalUtilities.Functions.XML.Base
Imports PersonalUtilities.Functions.RegularExpressions
Namespace API.Reddit
Friend Module Declarations
+ Friend Const RedditSite As String = "Reddit"
+ Friend Const RedditSiteKey As String = "AndyProgram_Reddit"
Friend ReadOnly JsonNodesJson() As NodeParams = {New NodeParams("posts", True, True, True, True, 3)}
Friend ReadOnly ChannelJsonNodes() As NodeParams = {New NodeParams("data", True, True, True, True, 1),
New NodeParams("children", True, True, True)}
diff --git a/SCrawler/API/Reddit/M3U8.vb b/SCrawler/API/Reddit/M3U8.vb
index d7ea9f8..2b2ea8b 100644
--- a/SCrawler/API/Reddit/M3U8.vb
+++ b/SCrawler/API/Reddit/M3U8.vb
@@ -96,7 +96,7 @@ Namespace API.Reddit
Catch ex As Exception
Return ErrorsDescriber.Execute(DPED, ex, "[M3U8.Save]", New SFile)
Finally
- If Not CachePath.IsEmptyString AndAlso CachePath.Exists(SFO.Path, False) Then CachePath.Delete(SFO.Path, False, False, DPED)
+ CachePath.Delete(SFO.Path, SFODelete.None, DPED)
End Try
End Function
Friend Shared Function Download(ByVal URL As String, ByVal f As SFile) As SFile
diff --git a/SCrawler/API/Reddit/ProfileSaved.vb b/SCrawler/API/Reddit/ProfileSaved.vb
deleted file mode 100644
index 7b8cfbf..0000000
--- a/SCrawler/API/Reddit/ProfileSaved.vb
+++ /dev/null
@@ -1,33 +0,0 @@
-' Copyright (C) 2022 Andy
-' This program is free software: you can redistribute it and/or modify
-' it under the terms of the GNU General Public License as published by
-' the Free Software Foundation, either version 3 of the License, or
-' (at your option) any later version.
-'
-' This program is distributed in the hope that it will be useful,
-' but WITHOUT ANY WARRANTY
-Imports SCrawler.API.Base
-Imports System.Threading
-Imports PersonalUtilities.Forms.Toolbars
-Namespace API.Reddit
- Friend NotInheritable Class ProfileSaved
- Friend Shared ReadOnly Property DataPath As SFile = Settings(Sites.Reddit).SavedPostsPath
- Private Sub New()
- End Sub
- Friend Shared Sub Download(ByRef Bar As MyProgress, ByVal Token As CancellationToken)
- Try
- Dim u As New UserInfo(Settings(Sites.Reddit).SavedPostsUserName.Value, Sites.Reddit) With {.IsChannel = True, .SpecialPath = DataPath}
- u.UpdateUserFile()
- Using user As New UserData(u,, False)
- DirectCast(user.Self, UserDataBase).IsSavedPosts = True
- user.Progress = Bar
- If Not user.FileExists Then user.UpdateUserInformation()
- user.DownloadData(Token)
- Bar.InformationTemporary = $"Images: {user.DownloadedPictures}; Videos: {user.DownloadedVideos}"
- End Using
- Catch ex As Exception
- ErrorsDescriber.Execute(EDP.SendInLog, ex, "[API.Reddit.ProfileSaved.Download]")
- End Try
- End Sub
- End Class
-End Namespace
\ No newline at end of file
diff --git a/SCrawler/API/Reddit/SiteSettings.vb b/SCrawler/API/Reddit/SiteSettings.vb
new file mode 100644
index 0000000..c48e9db
--- /dev/null
+++ b/SCrawler/API/Reddit/SiteSettings.vb
@@ -0,0 +1,97 @@
+' Copyright (C) 2022 Andy
+' This program is free software: you can redistribute it and/or modify
+' it under the terms of the GNU General Public License as published by
+' the Free Software Foundation, either version 3 of the License, or
+' (at your option) any later version.
+'
+' This program is distributed in the hope that it will be useful,
+' but WITHOUT ANY WARRANTY
+Imports SCrawler.API.Base
+Imports SCrawler.Plugin
+Imports SCrawler.Plugin.Attributes
+Imports PersonalUtilities.Tools
+Imports PersonalUtilities.Functions.RegularExpressions
+Imports DownDetector = SCrawler.API.Base.DownDetector
+Imports Download = SCrawler.Plugin.ISiteSettings.Download
+Namespace API.Reddit
+
+ Friend Class SiteSettings : Inherits SiteSettingsBase
+ Friend Overrides ReadOnly Property Icon As Icon
+ Get
+ Return My.Resources.RedditIcon
+ End Get
+ End Property
+ Friend Overrides ReadOnly Property Image As Image
+ Get
+ Return My.Resources.RedditPic512
+ End Get
+ End Property
+
+ Friend ReadOnly Property SavedPostsUserName As PropertyValue
+ Friend Overrides ReadOnly Property Responser As WEB.Response
+ Friend Sub New()
+ MyBase.New(RedditSite)
+ Responser = New WEB.Response($"{SettingsFolderName}\Responser_{Site}.xml")
+
+ With Responser
+ If .File.Exists Then
+ .LoadSettings()
+ Else
+ .CookiesDomain = "reddit.com"
+ .Decoders.Add(SymbolsConverter.Converters.Unicode)
+ .SaveSettings()
+ End If
+ End With
+ SavedPostsUserName = New PropertyValue(String.Empty, GetType(String))
+ UrlPatternUser = "https://www.reddit.com/user/{0}/"
+ UrlPatternChannel = "https://www.reddit.com/r/{0}/"
+ ImageVideoContains = "redgifs"
+ End Sub
+ Friend Overrides Function GetInstance(ByVal What As Download) As IPluginContentProvider
+ Select Case What
+ Case Download.Main : Return New UserData
+ Case Download.Channel : Return New UserData With {.SaveToCache = False, .SkipExistsUsers = False, .AutoGetLimits = True}
+ Case Download.SavedPosts
+ Dim u As New UserData With {.IsSavedPosts = True}
+ DirectCast(u, UserDataBase).User = New UserInfo With {.Name = CStr(AConvert(Of String)(SavedPostsUserName.Value, String.Empty))}
+ Return u
+ End Select
+ Return Nothing
+ End Function
+ Private ReadOnly RedditRegEx1 As RParams = RParams.DMS("[htps:/]{7,8}.*?reddit.com/user/([^/]+)", 1)
+ Private ReadOnly RedditRegEx2 As RParams = RParams.DMS(".?u/([^/]+)", 1)
+ Private ReadOnly RedditChannelRegEx1 As RParams = RParams.DMS("[htps:/]{7,8}.*?reddit.com/r/([^/]+)", 1)
+ Private ReadOnly RedditChannelRegEx2 As RParams = RParams.DMS(".?r/([^/]+)", 1)
+ Friend Overrides Function IsMyUser(ByVal UserURL As String) As ExchangeOptions
+ Dim s$
+ Dim c% = 0
+ For Each r As RParams In {RedditRegEx1, RedditRegEx2, RedditChannelRegEx1, RedditChannelRegEx2}
+ s = RegexReplace(UserURL, r)
+ If Not s.IsEmptyString Then Return New ExchangeOptions(Site, s, c > 1)
+ c += 1
+ Next
+ Return Nothing
+ End Function
+ Friend Overrides Function Available(ByVal What As Download) As Boolean
+ Try
+ Dim dl As List(Of DownDetector.Data) = DownDetector.GetData("reddit")
+ If dl.ListExists Then
+ dl = dl.Take(4).ToList
+ Dim avg% = dl.Average(Function(d) d.Value)
+ If avg > 100 Then
+ Return MsgBoxE({"Over the past hour, Reddit has received an average of " &
+ avg.NumToString(New ANumbers With {.FormatOptions = ANumbers.Options.GroupIntegral}) & " outage reports:" & vbCr &
+ dl.ListToString(, vbCr) & vbCr & vbCr &
+ "Do you want to continue parsing Reddit data?", "There are outage reports on Reddit"}, vbYesNo) = vbYes
+ End If
+ End If
+ Return True
+ Catch ex As Exception
+ Return ErrorsDescriber.Execute(EDP.SendInLog + EDP.ReturnValue, ex, "[API.Reddit.SiteSettings.Available]", True)
+ End Try
+ End Function
+ Friend Overrides Function GetSpecialDataF(ByVal URL As String) As IEnumerable(Of UserMedia)
+ Return UserData.GetVideoInfo(URL, Responser)
+ End Function
+ 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 9f21995..a86e8dc 100644
--- a/SCrawler/API/Reddit/UserData.vb
+++ b/SCrawler/API/Reddit/UserData.vb
@@ -9,6 +9,7 @@
Imports PersonalUtilities.Functions.XML
Imports PersonalUtilities.Functions.RegularExpressions
Imports PersonalUtilities.Tools.ImageRenderer
+Imports PersonalUtilities.Tools.WEB
Imports PersonalUtilities.Tools.WebDocuments.JSON
Imports System.Net
Imports System.Threading
@@ -17,7 +18,11 @@ Imports UStates = SCrawler.API.Base.UserMedia.States
Imports UTypes = SCrawler.API.Base.UserMedia.Types
Namespace API.Reddit
Friend Class UserData : Inherits UserDataBase : Implements IChannelData
- Friend Overrides Property Site As Sites = Sites.Reddit
+ Private ReadOnly Property MySiteSettings As SiteSettings
+ Get
+ Return DirectCast(HOST.Source, SiteSettings)
+ End Get
+ End Property
#Region "Channels Support"
#Region "IChannelLimits Support"
Friend Property DownloadLimitCount As Integer? Implements IChannelLimits.DownloadLimitCount
@@ -50,22 +55,12 @@ Namespace API.Reddit
Select c.Post) Else Return Nothing
End Function
#End Region
-#Region "Initializers"
- ''' Video downloader initializer
- Private Sub New()
+#Region "Initializer"
+ Friend Sub New()
ChannelPostsNames = New List(Of String)
_ExistsUsersNames = New List(Of String)
_CrossPosts = New List(Of String)
End Sub
- ''' Default initializer
- Friend Sub New(ByVal u As UserInfo, Optional ByVal _LoadUserInformation As Boolean = True, Optional ByVal InvokeImageHandler As Boolean = True)
- MyBase.New(InvokeImageHandler)
- ChannelPostsNames = New List(Of String)
- _ExistsUsersNames = New List(Of String)
- _CrossPosts = New List(Of String)
- User = u
- If _LoadUserInformation Then LoadUserInformation()
- End Sub
#End Region
#Region "Load and Update user info"
Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean)
@@ -73,11 +68,12 @@ Namespace API.Reddit
#End Region
#Region "Download Overrides"
Friend Overrides Sub DownloadData(ByVal Token As CancellationToken)
+ UserDescriptionReset()
_CrossPosts.Clear()
- If Not IsSavedPosts AndAlso (IsChannel AndAlso Not ChannelInfo.IsRegularChannel) Then
+ If Not IsSavedPosts AndAlso (IsChannel AndAlso Not ChannelInfo Is Nothing) Then
If Not Responser Is Nothing Then Responser.Dispose()
- Responser = New PersonalUtilities.Tools.WEB.Response
- Responser.Copy(Settings(Sites.Reddit).Responser)
+ Responser = New Response
+ Responser.Copy(MySiteSettings.Responser)
ChannelPostsNames.ListAddList(ChannelInfo.PostsAll.Select(Function(p) p.ID), LNC)
If SkipExistsUsers Then _ExistsUsersNames.ListAddList(Settings.UsersList.Select(Function(p) p.Name), LNC)
DownloadDataF(Token)
@@ -93,7 +89,7 @@ Namespace API.Reddit
If IsSavedPosts Then
DownloadDataChannel(String.Empty, Token)
ElseIf IsChannel Then
- If ChannelInfo.IsRegularChannel Then
+ If ChannelInfo Is Nothing Then
ChannelPostsNames.ListAddList(_TempPostsList, LNC)
If ChannelPostsNames.Count > 0 Then
DownloadLimitCount = Nothing
@@ -104,7 +100,7 @@ Namespace API.Reddit
If DownloadTopCount.HasValue Then DownloadLimitCount = DownloadTopCount
End If
DownloadDataChannel(String.Empty, Token)
- If ChannelInfo.IsRegularChannel Then _TempPostsList.ListAddList(_TempMediaList.Select(Function(m) m.Post.ID), LNC)
+ If ChannelInfo Is Nothing Then _TempPostsList.ListAddList(_TempMediaList.Select(Function(m) m.Post.ID), LNC)
Else
DownloadDataUser(String.Empty, Token)
End If
@@ -138,6 +134,7 @@ Namespace API.Reddit
If Not r.IsEmptyString Then
Using w As EContainer = JsonDocument.Parse(r).XmlIfNothing
If w.Count > 0 Then
+ If UserDescriptionNeedToUpdate() Then UserDescriptionUpdate(w.ItemF({"subredditAboutInfo", 0, "publicDescription"}).XmlIfNothingValue)
n = w.GetNode(JsonNodesJson)
If Not n Is Nothing AndAlso n.Count > 0 Then
For Each nn In n
@@ -170,7 +167,7 @@ Namespace API.Reddit
_ItemsBefore = _TempMediaList.Count
added = True
s = nn.ItemF({"source", "url"})
- If s.XmlIfNothingValue("/").Contains("redgifs.com") Then
+ If s.XmlIfNothingValue("/").StringContains({"redgifs.com", "gfycat.com"}) Then
_TempMediaList.ListAddValue(MediaFromData(UTypes.VideoPre, s.Value, _PostID(), PostDate,, IsChannel), LNC)
ElseIf Not CreateImgurMedia(s.XmlIfNothingValue, _PostID(), PostDate,, IsChannel) Then
s = nn.ItemF({"media"}).XmlIfNothing
@@ -199,7 +196,7 @@ Namespace API.Reddit
If Not s.IsEmptyString AndAlso TryFile(s.Value) Then
With s.Value.ToLower
Select Case True
- Case .Contains("redgifs") : tmpType = UTypes.VideoPre
+ Case .Contains("redgifs"), .Contains("gfycat") : tmpType = UTypes.VideoPre
Case .Contains("m3u8") : If Settings.UseM3U8 Then tmpType = UTypes.m3u8
Case .Contains(".gif") And TryFile(s.Value) : tmpType = UTypes.GIF
Case TryFile(s.Value) : tmpType = UTypes.Picture
@@ -264,13 +261,28 @@ Namespace API.Reddit
End With
If lDate.HasValue AndAlso lDate.Value <= DownloadLimitDate.Value Then Exit Sub
End If
- NewPostDetected = True
+
+ If IsSavedPosts Then
+ If Not _TempPostsList.Contains(PostID) Then
+ NewPostDetected = True
+ _TempPostsList.Add(PostID)
+ Else
+ ExistsDetected = True
+ Continue For
+ End If
+ Else
+ NewPostDetected = True
+ End If
If s.Contains("created") Then PostDate = s("created").Value Else PostDate = String.Empty
_UserID = s.Value("author")
If SkipExistsUsers AndAlso _ExistsUsersNames.Count > 0 AndAlso
- Not _UserID.IsEmptyString AndAlso _ExistsUsersNames.Contains(_UserID) Then Continue For
+ Not _UserID.IsEmptyString AndAlso _ExistsUsersNames.Contains(_UserID) Then
+ If Not IsSavedPosts AndAlso Not ChannelInfo Is Nothing Then _
+ ChannelInfo.ChannelExistentUserNames.ListAddValue(_UserID, LNC)
+ Continue For
+ End If
tmpUrl = s.Value("url")
If Not tmpUrl.IsEmptyString AndAlso tmpUrl.Contains("redgifs.com") Then
@@ -393,7 +405,15 @@ Namespace API.Reddit
ThrowAny(Token)
If _TempMediaList(i).Type = UTypes.VideoPre Or _TempMediaList(i).Type = v2 Then
m = _TempMediaList(i)
- If _TempMediaList(i).Type = UTypes.VideoPre Then r = Responser.GetResponse(m.URL,, e) Else r = m.URL
+ If _TempMediaList(i).Type = UTypes.VideoPre Then
+ If m.URL.Contains("gfycat.com") Then
+ r = Gfycat.Envir.GetVideo(m.URL)
+ Else
+ r = Responser.GetResponse(m.URL,, e)
+ End If
+ Else
+ r = m.URL
+ End If
_TempMediaList(i) = New UserMedia
If Not r.IsEmptyString Then
v = RegexReplace(r, VideoRegEx)
@@ -410,13 +430,13 @@ Namespace API.Reddit
ProcessException(ex, Token, "video reparsing error", False)
End Try
End Sub
- Friend Shared Function GetVideoInfo(ByVal URL As String) As IEnumerable(Of UserMedia)
+ Friend Shared Function GetVideoInfo(ByVal URL As String, ByVal resp As Response) As IEnumerable(Of UserMedia)
Try
If Not URL.IsEmptyString AndAlso URL.Contains("redgifs") Then
Using r As New UserData
r._TempMediaList.Add(MediaFromData(UTypes.VideoPre, URL, String.Empty, String.Empty,, False))
- r.Responser = New PersonalUtilities.Tools.WEB.Response
- r.Responser.Copy(Settings(Sites.Reddit).Responser)
+ r.Responser = New Response
+ r.Responser.Copy(resp)
r.ReparseVideo(Nothing)
If r._TempMediaList.ListExists Then Return {r._TempMediaList(0)}
End Using
@@ -465,7 +485,7 @@ Namespace API.Reddit
If _ContentNew.Count > 0 Then
MyFile.Exists(SFO.Path)
Dim MyDir$
- If Not IsSavedPosts AndAlso (IsChannel And SaveToCache) Then
+ If Not IsSavedPosts AndAlso (IsChannel And SaveToCache And Not ChannelInfo Is Nothing) Then
MyDir = ChannelInfo.CachePath.PathNoSeparator
Else
MyDir = MyFile.CutPath.PathNoSeparator
@@ -584,8 +604,8 @@ Namespace API.Reddit
End If
If Not v.Type = UTypes.m3u8 Or Not f.IsEmptyString Then
Select Case v.Type
- Case UTypes.Picture : DownloadedPictures += 1 : _CountPictures += 1
- Case UTypes.Video, UTypes.m3u8 : DownloadedVideos += 1 : _CountVideo += 1
+ Case UTypes.Picture : DownloadedPictures(False) += 1
+ Case UTypes.Video, UTypes.m3u8 : DownloadedVideos(False) += 1
End Select
If Not IsChannel Or Not SaveToCache Then
v.File = ChangeFileNameByProvider(f, v)
diff --git a/SCrawler/API/Redgifs/Declarations.vb b/SCrawler/API/Redgifs/Declarations.vb
index 4747d85..0233d07 100644
--- a/SCrawler/API/Redgifs/Declarations.vb
+++ b/SCrawler/API/Redgifs/Declarations.vb
@@ -8,6 +8,7 @@
' but WITHOUT ANY WARRANTY
Namespace API.RedGifs
Friend Module Declarations
+ Friend Const RedGifsSite As String = "RedGifs"
Friend ReadOnly DateProvider As New JsonDate
Friend Class JsonDate : Implements ICustomProvider
Friend Function Convert(ByVal Value As Object, ByVal DestinationType As Type, ByVal Provider As IFormatProvider,
diff --git a/SCrawler/API/Redgifs/SiteSettings.vb b/SCrawler/API/Redgifs/SiteSettings.vb
new file mode 100644
index 0000000..1628ecc
--- /dev/null
+++ b/SCrawler/API/Redgifs/SiteSettings.vb
@@ -0,0 +1,29 @@
+' Copyright (C) 2022 Andy
+' This program is free software: you can redistribute it and/or modify
+' it under the terms of the GNU General Public License as published by
+' the Free Software Foundation, either version 3 of the License, or
+' (at your option) any later version.
+'
+' This program is distributed in the hope that it will be useful,
+' but WITHOUT ANY WARRANTY
+Imports SCrawler.Plugin
+Imports SCrawler.Plugin.Attributes
+Imports SCrawler.API.Base
+Imports PersonalUtilities.Functions.RegularExpressions
+Namespace API.RedGifs
+
+ Friend Class SiteSettings : Inherits SiteSettingsBase
+ Friend Sub New()
+ MyBase.New(RedGifsSite, "redgifs.com")
+ UrlPatternUser = "https://www.redgifs.com/users/{0}/"
+ UserRegex = RParams.DMS("[htps:/]{7,8}.*?redgifs.com/users/([^/]+)", 1)
+ ImageVideoContains = "redgifs"
+ End Sub
+ Friend Overrides Function GetInstance(ByVal What As ISiteSettings.Download) As IPluginContentProvider
+ Return New UserData
+ End Function
+ Friend Overrides Function GetSpecialDataF(ByVal URL As String) As IEnumerable(Of UserMedia)
+ Return Reddit.UserData.GetVideoInfo(URL, Nothing)
+ End Function
+ End Class
+End Namespace
\ No newline at end of file
diff --git a/SCrawler/API/Redgifs/UserData.vb b/SCrawler/API/Redgifs/UserData.vb
index 696a521..ff3efb8 100644
--- a/SCrawler/API/Redgifs/UserData.vb
+++ b/SCrawler/API/Redgifs/UserData.vb
@@ -15,10 +15,7 @@ Imports SCrawler.API.Base
Imports UTypes = SCrawler.API.Base.UserMedia.Types
Namespace API.RedGifs
Friend Class UserData : Inherits UserDataBase
- Friend Overrides Property Site As Sites = Sites.RedGifs
- Friend Sub New(ByVal u As UserInfo, Optional ByVal _LoadUserInformation As Boolean = True)
- User = u
- If _LoadUserInformation Then LoadUserInformation()
+ Friend Sub New()
End Sub
Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean)
End Sub
diff --git a/SCrawler/API/Twitter/Declarations.vb b/SCrawler/API/Twitter/Declarations.vb
index af6beba..b8f5166 100644
--- a/SCrawler/API/Twitter/Declarations.vb
+++ b/SCrawler/API/Twitter/Declarations.vb
@@ -10,6 +10,7 @@ Imports PersonalUtilities.Functions.XML.Base
Imports PersonalUtilities.Functions.RegularExpressions
Namespace API.Twitter
Friend Module Declarations
+ Friend Const TwitterSite As String = "Twitter"
Friend DateProvider As New ADateTime(ADateTime.Formats.BaseDateTime)
Friend ReadOnly VideoNode As NodeParams() = {New NodeParams("video_info", True, True, True, True, 10)}
Friend ReadOnly VideoSizeRegEx As RParams = RParams.DMS("\d+x(\d+)", 1, EDP.ReturnValue)
diff --git a/SCrawler/API/Twitter/SiteSettings.vb b/SCrawler/API/Twitter/SiteSettings.vb
new file mode 100644
index 0000000..24de77d
--- /dev/null
+++ b/SCrawler/API/Twitter/SiteSettings.vb
@@ -0,0 +1,99 @@
+' Copyright (C) 2022 Andy
+' This program is free software: you can redistribute it and/or modify
+' it under the terms of the GNU General Public License as published by
+' the Free Software Foundation, either version 3 of the License, or
+' (at your option) any later version.
+'
+' This program is distributed in the hope that it will be useful,
+' but WITHOUT ANY WARRANTY
+Imports SCrawler.Plugin
+Imports SCrawler.Plugin.Attributes
+Imports PersonalUtilities.Tools
+Imports PersonalUtilities.Functions.RegularExpressions
+Imports SCrawler.API.Base
+Namespace API.Twitter
+
+ Friend Class SiteSettings : Inherits SiteSettingsBase
+ Friend Const Header_Authorization As String = "authorization"
+ Friend Const Header_Token As String = "x-csrf-token"
+ Friend Overrides ReadOnly Property Icon As Icon
+ Get
+ Return My.Resources.TwitterIcon
+ End Get
+ End Property
+ Friend Overrides ReadOnly Property Image As Image
+ Get
+ Return My.Resources.TwitterPic400
+ End Get
+ End Property
+
+ Private ReadOnly Property Auth As PropertyValue
+
+ Private ReadOnly Property Token As PropertyValue
+ Friend Overrides ReadOnly Property Responser As WEB.Response
+ Friend Sub New()
+ MyBase.New(TwitterSite)
+ Responser = New WEB.Response($"{SettingsFolderName}\Responser_{Site}.xml")
+
+ Dim a$ = String.Empty
+ Dim t$ = String.Empty
+
+ With Responser
+ If .File.Exists Then
+ .LoadSettings()
+ With .Headers
+ If .ContainsKey(Header_Authorization) Then a = .Item(Header_Authorization)
+ If .ContainsKey(Header_Token) Then t = .Item(Header_Token)
+ End With
+ Else
+ .ContentType = "application/json"
+ .Accept = "*/*"
+ .CookiesDomain = "twitter.com"
+ .Decoders.Add(SymbolsConverter.Converters.Unicode)
+ With .Headers
+ .Add("sec-ch-ua", " Not;A Brand" & Chr(34) & ";v=" & Chr(34) & "99" & Chr(34) & ", " & Chr(34) &
+ "Google Chrome" & Chr(34) & ";v=" & Chr(34) & "91" & Chr(34) & ", " & Chr(34) & "Chromium" &
+ Chr(34) & ";v=" & Chr(34) & "91" & Chr(34))
+ .Add("sec-ch-ua-mobile", "?0")
+ .Add("sec-fetch-dest", "empty")
+ .Add("sec-fetch-mode", "cors")
+ .Add("sec-fetch-site", "same-origin")
+ .Add(Header_Token, String.Empty)
+ .Add("x-twitter-active-user", "yes")
+ .Add("x-twitter-auth-type", "OAuth2Session")
+ .Add(Header_Authorization, String.Empty)
+ End With
+ .SaveSettings()
+ End If
+ End With
+
+ Auth = New PropertyValue(a, GetType(String), Sub(v) ChangeResponserFields(NameOf(Auth), v))
+ Token = New PropertyValue(t, GetType(String), Sub(v) ChangeResponserFields(NameOf(Token), v))
+
+ UserRegex = RParams.DMS("[htps:/]{7,8}.*?twitter.com/([^/]+)", 1)
+ UrlPatternUser = "https://twitter.com/{0}"
+ ImageVideoContains = "twitter"
+ End Sub
+ 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
+ If Responser.Headers.Count > 0 AndAlso Responser.Headers.ContainsKey(f) 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
+ Friend Overrides Function GetInstance(ByVal What As ISiteSettings.Download) As IPluginContentProvider
+ Return New UserData
+ End Function
+ Friend Overrides Function GetSpecialDataF(ByVal URL As String) As IEnumerable(Of UserMedia)
+ Return UserData.GetVideoInfo(URL, Responser)
+ End Function
+ End Class
+End Namespace
\ No newline at end of file
diff --git a/SCrawler/API/Twitter/UserData.vb b/SCrawler/API/Twitter/UserData.vb
index ff0f107..0d97337 100644
--- a/SCrawler/API/Twitter/UserData.vb
+++ b/SCrawler/API/Twitter/UserData.vb
@@ -16,27 +16,25 @@ Imports SCrawler.API.Base
Namespace API.Twitter
Friend Class UserData : Inherits UserDataBase
#Region "Declarations"
- Friend Overrides Property Site As Sites = Sites.Twitter
+ Private ReadOnly _DataNames As List(Of String)
#End Region
#Region "Initializer"
- Friend Sub New(ByVal u As UserInfo, Optional ByVal _LoadUserInformation As Boolean = True)
- User = u
- If _LoadUserInformation Then LoadUserInformation()
+ Friend Sub New()
+ _DataNames = New List(Of String)
End Sub
#End Region
-#Region "Load and Update user info"
Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean)
End Sub
-#End Region
#Region "Download functions"
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
+ If _ContentList.Count > 0 Then _DataNames.ListAddList(_ContentList.Select(Function(c) c.File.File), LAP.ClearBeforeAdd, LAP.NotContainsOnly)
DownloadData(String.Empty, Token)
End Sub
Private Overloads Sub DownloadData(ByVal POST As String, ByVal Token As CancellationToken)
Dim URL$ = String.Empty
Try
Dim PostID$ = String.Empty
- Dim PostDate$
+ Dim PostDate$, dName$
Dim m As EContainer, nn As EContainer, s As EContainer
Dim NewPostDetected As Boolean = False
Dim ExistsDetected As Boolean = False
@@ -61,6 +59,8 @@ Namespace API.Twitter
If Not ID.IsEmptyString Then UpdateUserInformation()
End If
+ If UserDescriptionNeedToUpdate() AndAlso nn.Value({"user"}, "screen_name") = Name Then UserDescriptionUpdate(nn.Value({"user"}, "description"))
+
'Date Pattern:
'Sat Jan 01 01:10:15 +0000 2000
If nn.Contains("created_at") Then PostDate = nn("created_at").Value Else PostDate = String.Empty
@@ -81,8 +81,12 @@ Namespace API.Twitter
If Not s Is Nothing AndAlso s.Count > 0 Then
For Each m In s
If m.Count > 0 AndAlso m.Contains("media_url") Then
- _TempMediaList.ListAddValue(MediaFromData(m("media_url").Value,
- PostID, PostDate, GetPictureOption(m)), LNC)
+ dName = UrlFile(m("media_url").Value)
+ If Not dName.IsEmptyString AndAlso Not _DataNames.Contains(dName) Then
+ _DataNames.Add(dName)
+ _TempMediaList.ListAddValue(MediaFromData(m("media_url").Value,
+ PostID, PostDate, GetPictureOption(m)), LNC)
+ End If
End If
Next
End If
@@ -99,12 +103,12 @@ Namespace API.Twitter
ProcessException(ex, Token, $"data downloading error [{URL}]")
End Try
End Sub
- Friend Shared Function GetVideoInfo(ByVal URL As String) As IEnumerable(Of UserMedia)
+ Friend Shared Function GetVideoInfo(ByVal URL As String, ByVal resp As Response) As IEnumerable(Of UserMedia)
Try
If URL.Contains("twitter") Then
Dim PostID$ = RegexReplace(URL, RParams.DM("(?<=/)\d+", 0))
If Not PostID.IsEmptyString Then
- Dim r$ = DirectCast(Settings(Sites.Twitter).Responser.Copy(), Response).
+ Dim r$ = DirectCast(resp.Copy(), Response).
GetResponse($"https://api.twitter.com/1.1/statuses/show.json?id={PostID}",, EDP.ReturnValue)
If Not r.IsEmptyString Then
Using j As EContainer = JsonDocument.Parse(r)
@@ -147,7 +151,14 @@ Namespace API.Twitter
Private Function CheckVideoNode(ByVal w As EContainer, ByVal PostID As String, ByVal PostDate As String) As Boolean
Try
Dim URL$ = GetVideoNodeURL(w)
- If Not URL.IsEmptyString Then _TempMediaList.ListAddValue(MediaFromData(URL, PostID, PostDate), LNC) : Return True
+ If Not URL.IsEmptyString Then
+ Dim f$ = UrlFile(URL)
+ If Not f.IsEmptyString AndAlso Not _DataNames.Contains(f) Then
+ _DataNames.Add(f)
+ _TempMediaList.ListAddValue(MediaFromData(URL, PostID, PostDate), LNC)
+ End If
+ Return True
+ End If
Return False
Catch ex As Exception
LogError(ex, "[API.Twitter.UserData.CheckVideoNode]")
@@ -177,6 +188,14 @@ Namespace API.Twitter
End Function
Protected Overrides Sub ReparseVideo(ByVal Token As CancellationToken)
End Sub
+ Private Function UrlFile(ByVal URL As String) As String
+ Try
+ Dim f As SFile = CStr(RegexReplace(LinkFormatterSecure(RegexReplace(URL.Replace("\", String.Empty), LinkPattern)), FilesPattern))
+ If Not f.IsEmptyString Then Return f.File Else Return String.Empty
+ Catch ex As Exception
+ Return String.Empty
+ End Try
+ End Function
#End Region
Private Shared Function MediaFromData(ByVal _URL As String, ByVal PostID As String, ByVal PostDate As String,
Optional ByVal _PictureOption As String = "") As UserMedia
@@ -206,5 +225,9 @@ Namespace API.Twitter
End If
Return 1
End Function
+ Protected Overrides Sub Dispose(ByVal disposing As Boolean)
+ If Not disposedValue And disposing Then _DataNames.Clear()
+ MyBase.Dispose(disposing)
+ End Sub
End Class
End Namespace
\ No newline at end of file
diff --git a/SCrawler/API/UserDataBind.vb b/SCrawler/API/UserDataBind.vb
index 6f66892..3f753f7 100644
--- a/SCrawler/API/UserDataBind.vb
+++ b/SCrawler/API/UserDataBind.vb
@@ -8,13 +8,14 @@
' but WITHOUT ANY WARRANTY
Imports PersonalUtilities.Tools
Imports PersonalUtilities.Functions.XML
+Imports PersonalUtilities.Functions.Messaging
Imports System.Threading
Imports SCrawler.API.Base
Namespace API
Friend Class UserDataBind : Inherits UserDataBase : Implements ICollection(Of IUserData), IMyEnumerator(Of IUserData)
- Friend Event OnCollectionSelfRemoved()
+ Friend Event OnCollectionSelfRemoved(ByVal Collection As IUserData)
+ Friend Event OnUserRemoved(ByVal User As IUserData)
#Region "Declarations"
- Friend Overrides Property Site As Sites = Sites.Undefined
Friend ReadOnly Property Collections As List(Of IUserData)
Private _CollectionName As String = String.Empty
Friend Overrides Property CollectionName As String
@@ -121,7 +122,7 @@ Namespace API
Friend Overrides Property DataMerging As Boolean
Get
If Count > 0 Then
- Return DirectCast(Collections(0).Self, UserDataBase).DataMerging
+ Return DirectCast(Collections(0), UserDataBase).DataMerging
Else
Return False
End If
@@ -184,15 +185,15 @@ Namespace API
End Property
Friend Overrides Function GetUserInformation() As String
Dim OutStr$ = String.Empty
- If Count > 0 Then Collections.ForEach(Sub(c) OutStr.StringAppendLine(DirectCast(c.Self, UserDataBase).GetUserInformation(), $"{vbCrLf}{vbCrLf}"))
+ If Count > 0 Then Collections.ForEach(Sub(c) OutStr.StringAppendLine(DirectCast(c, UserDataBase).GetUserInformation(), $"{vbCrLf}{vbCrLf}"))
Return OutStr
End Function
Friend Overrides Property LastUpdated As Date?
Get
If Count > 0 Then
- With If((From c In Collections
- Where DirectCast(c.Self, UserDataBase).LastUpdated.HasValue
- Select DirectCast(c.Self, UserDataBase).LastUpdated.Value).ToList, New List(Of Date))
+ With If((From c As IUserData In Collections
+ Where DirectCast(c, UserDataBase).LastUpdated.HasValue
+ Select DirectCast(c, UserDataBase).LastUpdated.Value).ToList, New List(Of Date))
If .Count > 0 Then Return .Max
End With
End If
@@ -210,7 +211,7 @@ Namespace API
Friend ReadOnly Property ContextDown As ToolStripMenuItem()
Get
If Count > 0 Then
- Return Collections.Select(Function(c) DirectCast(c.Self, UserDataBase).BTT_CONTEXT_DOWN).ToArray
+ Return Collections.Select(Function(c) DirectCast(c, UserDataBase).BTT_CONTEXT_DOWN).ToArray
Else
Return New ToolStripMenuItem() {}
End If
@@ -219,7 +220,7 @@ Namespace API
Friend ReadOnly Property ContextEdit As ToolStripMenuItem()
Get
If Count > 0 Then
- Return Collections.Select(Function(c) DirectCast(c.Self, UserDataBase).BTT_CONTEXT_EDIT).ToArray
+ Return Collections.Select(Function(c) DirectCast(c, UserDataBase).BTT_CONTEXT_EDIT).ToArray
Else
Return New ToolStripMenuItem() {}
End If
@@ -228,7 +229,7 @@ Namespace API
Friend ReadOnly Property ContextDelete As ToolStripMenuItem()
Get
If Count > 0 Then
- Return Collections.Select(Function(c) DirectCast(c.Self, UserDataBase).BTT_CONTEXT_DELETE).ToArray
+ Return Collections.Select(Function(c) DirectCast(c, UserDataBase).BTT_CONTEXT_DELETE).ToArray
Else
Return New ToolStripMenuItem() {}
End If
@@ -237,7 +238,7 @@ Namespace API
Friend ReadOnly Property ContextPath As ToolStripMenuItem()
Get
If Count > 0 Then
- Return Collections.Select(Function(c) DirectCast(c.Self, UserDataBase).BTT_CONTEXT_OPEN_PATH).ToArray
+ Return Collections.Select(Function(c) DirectCast(c, UserDataBase).BTT_CONTEXT_OPEN_PATH).ToArray
Else
Return New ToolStripMenuItem() {}
End If
@@ -246,7 +247,7 @@ Namespace API
Friend ReadOnly Property ContextSite As ToolStripMenuItem()
Get
If Count > 0 Then
- Return Collections.Select(Function(c) DirectCast(c.Self, UserDataBase).BTT_CONTEXT_OPEN_SITE).ToArray
+ Return Collections.Select(Function(c) DirectCast(c, UserDataBase).BTT_CONTEXT_OPEN_SITE).ToArray
Else
Return New ToolStripMenuItem() {}
End If
@@ -270,7 +271,7 @@ Namespace API
If Count > 0 Then Collections.ForEach(Sub(c) c.UpdateUserInformation())
End Sub
Friend Overrides Sub LoadContentInformation()
- If Count > 0 Then Collections.ForEach(Sub(c) DirectCast(c.Self, UserDataBase).LoadContentInformation())
+ If Count > 0 Then Collections.ForEach(Sub(c) DirectCast(c, UserDataBase).LoadContentInformation())
End Sub
Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean)
End Sub
@@ -299,14 +300,15 @@ Namespace API
Return 0
End Function
Private Sub User_OnUserUpdated(ByVal User As IUserData)
- Raise_OnUserUpdated()
+ RaiseEvent_OnUserUpdated()
End Sub
- Friend Overrides Sub OpenSite()
- If Count > 0 Then Collections(0).OpenSite()
+ Friend Overrides Sub OpenSite(Optional ByVal e As ErrorsDescriber = Nothing)
+ If Not e.Exists Then e = New ErrorsDescriber(EDP.SendInLog)
+ If Count > 0 Then Collections.ForEach(Sub(c) c.OpenSite(e))
End Sub
Friend Overrides Sub OpenFolder()
Try
- If Count > 0 Then Collections(0).File.CutPath(2).Open(SFO.Path, EDP.None)
+ If Count > 0 Then GlobalOpenPath(Collections(0).File.CutPath(2))
Catch ex As Exception
End Try
End Sub
@@ -328,17 +330,20 @@ Namespace API
If DataMerging Then DirectCast(.Self, UserDataBase).MergeData()
Collections.Add(_Item)
With Collections.Last
- If Collections.Count - 1 > 0 Then
+ If Count > 1 Then
+ If _CollectionName.IsEmptyString Then _CollectionName = .CollectionName
.Temporary = Temporary
.Favorite = Favorite
+ .ReadyForDownload = ReadyForDownload
+ ConsolidateLabels()
.UpdateUserInformation()
End If
ImageHandler(_Item, False)
+ AddRemoveBttDeleteHandler(.Self, True)
AddHandler .Self.OnUserUpdated, AddressOf User_OnUserUpdated
- DirectCast(.Self, UserDataBase).CreateButtons(Count - 1)
End With
Else
- Throw New InvalidOperationException("User data doe not move to the collection folder")
+ Throw New InvalidOperationException("User data was not moved to the collection folder")
End If
End With
End Sub
@@ -346,22 +351,43 @@ Namespace API
Friend Overloads Sub Add(ByVal u As UserInfo, Optional ByVal _LoadData As Boolean = True)
Collections.Add(GetInstance(u, _LoadData))
If Not Collections.Last Is Nothing Then
- With DirectCast(Collections.Last.Self, UserDataBase)
- .CreateButtons(Count - 1)
- AddHandler .BTT_CONTEXT_DELETE.Click, AddressOf BTT_CONTEXT_DELETE_Click
+ With Collections.Last
+ If _CollectionName.IsEmptyString Then _CollectionName = .CollectionName
+ AddRemoveBttDeleteHandler(.Self, True)
+ AddHandler .OnUserUpdated, AddressOf User_OnUserUpdated
End With
- AddHandler Collections.Last.OnUserUpdated, AddressOf User_OnUserUpdated
Else
Collections.RemoveAt(Count - 1)
End If
End Sub
+ Private Sub AddRemoveBttDeleteHandler(ByRef User As IUserData, ByVal IsAdd As Boolean)
+ Try
+ With DirectCast(User, UserDataBase)
+ If IsAdd Then
+ .CreateButtons(Count - 1)
+ AddHandler .BTT_CONTEXT_DELETE.Click, AddressOf DeleteRemoveUserFromCollection
+ Else
+ RemoveHandler .BTT_CONTEXT_DELETE.Click, AddressOf DeleteRemoveUserFromCollection
+ End If
+ End With
+ Catch ex As Exception
+ 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
+ End Sub
Friend Sub AddRange(ByVal _Items As IEnumerable(Of IUserData))
If Not _Items Is Nothing AndAlso _Items.Count > 0 Then
For i% = 0 To _Items.Count - 1 : Add(_Items(i)) : Next
End If
End Sub
Friend Overrides Function MoveFiles(ByVal __CollectionName As String) As Boolean
- Throw New NotImplementedException("Files moving does not available if collection context")
+ Throw New NotImplementedException("Move files is not available in the collection context")
End Function
Friend Overloads Sub MergeData(ByVal Merging As Boolean)
If Count > 0 Then
@@ -370,7 +396,7 @@ Namespace API
MsgBoxE($"Collection [{CollectionName}] data already merged")
Else
If Collections.Count > 1 Then
- Collections.ForEach(Sub(c) DirectCast(c.Self, UserDataBase).MergeData())
+ Collections.ForEach(Sub(c) DirectCast(c, UserDataBase).MergeData())
MsgBoxE($"Collection [{CollectionName}] data merged")
Else
MsgBoxE($"Collection [{CollectionName}] contains only one user profile" & vbCr &
@@ -403,8 +429,10 @@ Namespace API
"Operation canceled", MsgBoxStyle.Critical)
Return False
Else
- DirectCast(_Item.Self, UserDataBase).MoveFiles(String.Empty)
+ DirectCast(_Item, UserDataBase).MoveFiles(String.Empty)
ImageHandler(_Item)
+ AddRemoveBttDeleteHandler(_Item, False)
+ RaiseEvent OnUserRemoved(_Item)
Return Collections.Remove(_Item)
End If
End Function
@@ -420,14 +448,16 @@ Namespace API
ImageHandler(Me, False)
Collections.ListClearDispose
Dispose(False)
- If f.Exists(SFO.Path, False) Then f.Delete(SFO.Path, True, False, EDP.SendInLog)
+ f.Delete(SFO.Path, SFODelete.EmptyOnly + Settings.DeleteMode, EDP.SendInLog)
Return 2
Else
If DataMerging Then
MsgBoxE($"Collection [{CollectionName}] data are already merged{vbCr}Cannot split merged collection{vbCr}Operation canceled", MsgBoxStyle.Exclamation)
Return 0
End If
- If MsgBoxE({$"Do you want to delete collection only?{vbCr}Users will not be deleted", "Collection deleting"},
+ If MsgBoxE({"Do you want to delete only the collection and split users' profiles??" & vbCr &
+ "Users will be removed from the collection and split by sites." & vbCr &
+ "All user data will remain.", "Collection deleting"},
MsgBoxStyle.Question + MsgBoxStyle.YesNo) = MsgBoxResult.Yes Then
f = Collections(0).File.CutPath(2)
Settings.Users.Remove(Me)
@@ -436,7 +466,7 @@ Namespace API
ImageHandler(c)
End Sub)
Collections.Clear()
- f.Delete(SFO.Path,,, EDP.SendInLog)
+ f.Delete(SFO.Path, SFODelete.Default + Settings.DeleteMode, EDP.SendInLog)
Downloader.UserRemove(Me)
ImageHandler(Me, False)
Dispose(False)
@@ -448,28 +478,45 @@ Namespace API
End If
Return 0
End Function
- Private Sub BTT_CONTEXT_DELETE_Click(sender As Object, e As EventArgs)
+ Private Sub DeleteRemoveUserFromCollection(sender As Object, e As EventArgs)
With DirectCast(sender, ToolStripMenuItem)
Dim i% = AConvert(Of Integer)(.Tag, -1)
If i >= 0 Then
Dim n$ = Collections(i).Name
Dim s$ = Collections(i).Site.ToString
- If MsgBoxE({$"Do you really want to delete user profile [{n}] of site [{s}]?" & vbCr &
- "This profile will be removed from collection and all data will be erased",
- "Profile removing"}, MsgBoxStyle.Exclamation,,, {"Process", "Cancel"}) = 0 Then
- Collections(i).Delete()
- Collections(i).Dispose()
- Collections.RemoveAt(i)
- MsgBoxE($"User profile [{n}] of site [{s}] has been removed")
- If Count = 0 Then
- Settings.Users.Remove(Me)
- ImageHandler(Me, False)
- RaiseEvent OnCollectionSelfRemoved()
- Dispose(False)
- End If
- Else
- MsgBoxE("Operation canceled")
- End If
+ Dim RemoveMeIfNull As Action = Sub()
+ If Count = 0 Then
+ Settings.Users.Remove(Me)
+ ImageHandler(Me, False)
+ RaiseEvent OnCollectionSelfRemoved(Me)
+ Dispose(False)
+ End If
+ End Sub
+ Select Case MsgBoxE({$"Are you sure you want to remove user profile [{n}] of site [{s}] from collection [{Name}]?" & vbCr &
+ "You can remove a user from the collection while keeping data (Remove) or deleting the data (Delete)" & vbCr &
+ "Deleting this profile will remove it from the collection and all its data will be erased." & vbCr &
+ "Removing this profile will remove it from the collection and all its data will remain." &
+ "This user will still appear in the program, but not in the collection.",
+ "Deleting a user"}, vbExclamation,,,
+ {
+ New MsgBoxButton("Remove") With {
+ .ToolTip = "Remove a user from the collection only. All its data will remain. The user will appear in the program."},
+ New MsgBoxButton("Delete") With {
+ .ToolTip = "Delete a user from the collection and erase their data."},
+ "Cancel"
+ }).Index
+ Case 0
+ Remove(Collections(i))
+ MsgBoxE($"User [{s} - {n}] has been removed from the collection. Now it should be displayed in the program.")
+ RemoveMeIfNull.Invoke
+ Case 1
+ Collections(i).Delete()
+ Collections(i).Dispose()
+ Collections.RemoveAt(i)
+ MsgBoxE($"User profile [{n}] of site [{s}] has been deleted")
+ RemoveMeIfNull.Invoke
+ Case Else : MsgBoxE("Operation canceled")
+ End Select
End If
End With
End Sub
@@ -482,26 +529,6 @@ Namespace API
End Function
#End Region
#End Region
- Friend Overrides Function CompareTo(ByVal Other As UserDataBase) As Integer
- If TypeOf Other Is UserDataBind Then
- Dim x% = CompareValue(Me)
- Dim y% = CompareValue(Other)
- If x.CompareTo(y) = 0 Then
- Return CollectionName.CompareTo(Other.CollectionName)
- Else
- Return x.CompareTo(y)
- End If
- Else
- Return -1
- End If
- End Function
- Friend Overrides Function CompareTo(ByVal Obj As Object) As Integer
- If TypeOf Obj Is UserDataBind Then
- Return CompareTo(DirectCast(Obj, UserDataBind))
- Else
- Return -1
- End If
- End Function
Friend Overrides Function Equals(ByVal Other As UserDataBase) As Boolean
If Other.IsCollection Then
Return CollectionName = Other.CollectionName
diff --git a/SCrawler/Channels/ChannelViewForm.vb b/SCrawler/Channels/ChannelViewForm.vb
index a859a27..a79fe05 100644
--- a/SCrawler/Channels/ChannelViewForm.vb
+++ b/SCrawler/Channels/ChannelViewForm.vb
@@ -15,6 +15,7 @@ Imports System.ComponentModel
Imports System.Threading
Imports SCrawler.API.Base
Imports SCrawler.API.Reddit
+Imports SCrawler.Plugin.Hosts
Imports CmbDefaultButtons = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons
Imports RButton = PersonalUtilities.Tools.RangeSwitcherButton.Types
Friend Class ChannelViewForm : Implements IChannelLimits
@@ -37,7 +38,7 @@ Friend Class ChannelViewForm : Implements IChannelLimits
Return u.ToString
End Operator
Friend Sub ChannelUserAdded(Optional ByVal IsAdded As Boolean = True)
- If Not Channel Is Nothing Then Channel.UserAdded(IsAdded)
+ If Not Channel Is Nothing Then Channel.UserAdded(ID, IsAdded)
End Sub
Public Overrides Function ToString() As String
Return ID
@@ -121,6 +122,7 @@ Friend Class ChannelViewForm : Implements IChannelLimits
Private Sub SetLimit(ByVal Source As IChannelLimits) Implements IChannelLimits.SetLimit
End Sub
#End Region
+ Private ReadOnly HOST As SettingsHost
Private ReadOnly PendingUsers As List(Of PendingUser)
Private ReadOnly LNC As New ListAddParams(LAP.NotContainsOnly)
Private WithEvents MyRange As RangeSwitcher(Of UserPost)
@@ -144,6 +146,7 @@ Friend Class ChannelViewForm : Implements IChannelLimits
CProvider = New ANumbers With {.FormatOptions = ANumbers.Options.GroupIntegral}
LimitProvider = New ADateTime("dd.MM.yyyy HH:mm")
PendingUsers = New List(Of PendingUser)
+ HOST = Settings(RedditSiteKey)
CMB_CHANNELS = New ComboBoxExtended With {
.CaptionMode = ICaptionControl.Modes.CheckBox,
@@ -323,7 +326,7 @@ Friend Class ChannelViewForm : Implements IChannelLimits
Private Async Sub BTT_DOWNLOAD_Click(sender As Object, e As EventArgs) Handles BTT_DOWNLOAD.Click
Try
AppendPendingUsers()
- If Not TokenSource Is Nothing Then Exit Sub
+ If Not TokenSource Is Nothing OrElse Not HOST.Source.Available(Plugin.ISiteSettings.Download.Channel) Then Exit Sub
Dim InvokeToken As Action = Sub()
If TokenSource Is Nothing Then
CProgress.TotalCount = 0
@@ -353,6 +356,7 @@ Friend Class ChannelViewForm : Implements IChannelLimits
_CollectionDownloading = True
Settings.Channels.SetLimit(Me)
Await Task.Run(Sub() Settings.Channels.DownloadData(Token, CH_HIDE_EXISTS_USERS.Checked, CProgress))
+ Settings.Channels.UpdateUsersStats()
RaiseEvent OnDownloadDone("All channels downloaded")
Token.ThrowIfCancellationRequested()
c = GetCurrentChannel()
@@ -362,6 +366,7 @@ Friend Class ChannelViewForm : Implements IChannelLimits
InvokeToken.Invoke()
c.SetLimit(Me)
Await Task.Run(Sub() c.DownloadData(Token, CH_HIDE_EXISTS_USERS.Checked, CProgress))
+ c.UpdateUsersStats()
RaiseEvent OnDownloadDone($"Channel [{c.Name}] downloaded")
Token.ThrowIfCancellationRequested()
End If
@@ -433,25 +438,34 @@ Friend Class ChannelViewForm : Implements IChannelLimits
Dim Added% = 0, Skipped% = 0
Dim StartIndex% = Settings.Users.Count
Dim f As SFile
+ Dim umo As Boolean = HOST.GetUserMediaOnly
Settings.Labels.Add(CannelsLabelName)
Settings.Labels.Add(LabelsKeeper.NoParsedUser)
Dim rUsers$() = UserBanned(PendingUsers.Select(Function(u) u.ID).ToArray)
If rUsers.ListExists Then PendingUsers.RemoveAll(Function(u) rUsers.Contains(u))
If PendingUsers.Count > 0 Then
- With PendingUsers.Select(Function(u) New UserInfo(u, Sites.Reddit))
+ Dim c As New ListAddParams(LAP.NotContainsOnly)
+ Dim cn$
+ Dim tmpUser As IUserData
+ With PendingUsers.Select(Function(u) New UserInfo(u, HOST))
For i = 0 To .Count - 1
If Not Settings.UsersList.Contains(.ElementAt(i)) Then
f = PendingUsers(i).File
+ cn = If(PendingUsers(i).Channel?.Name, String.Empty)
Settings.UpdateUsersList(.ElementAt(i))
- Settings.Users.Add(New UserData(.ElementAt(i), False) With {
- .Temporary = Settings.ChannelsDefaultTemporary,
- .CreatedByChannel = True,
- .ReadyForDownload = Settings.ChannelsDefaultReadyForDownload
- })
+ tmpUser = HOST.GetInstance(Plugin.ISiteSettings.Download.Main, .ElementAt(i), False)
+ With DirectCast(tmpUser, UserData)
+ .Temporary = Settings.ChannelsDefaultTemporary
+ .CreatedByChannel = True
+ .ReadyForDownload = Settings.ChannelsDefaultReadyForDownload
+ .ParseUserMediaOnly = umo
+ End With
+ Settings.Users.Add(tmpUser)
With Settings.Users.Last
.Labels.Add(CannelsLabelName)
.UpdateUserInformation()
- If Settings.FromChannelCopyImageToUser And Not f.IsEmptyString And Not .File.IsEmptyString Then CopyFile(f, .File)
+ 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)
End With
Added += 1
Else
@@ -464,17 +478,26 @@ Friend Class ChannelViewForm : Implements IChannelLimits
BTT_ADD_USERS.Text = "Add"
MsgBoxE($"Added users: {Added.ToString(CProvider)}{vbCr}Skipped users: {Skipped.ToString(CProvider)}{vbCr}Total: {PendingUsers.Count.ToString(CProvider)}")
RaiseEvent OnUsersAdded(StartIndex)
+ Settings.Channels.UpdateUsersStats()
Else
MsgBoxE("No one users selected for add to collection")
End If
End Sub
- Private Sub CopyFile(ByVal Source As SFile, ByVal Destination As SFile)
+ Private Sub CopyFile(ByVal Source As IEnumerable(Of ChannelsCollection.ChannelImage), ByVal Destination As SFile)
Try
- If Not Source.IsEmptyString And Not Destination.IsEmptyString Then
+ If Source.ListExists And Not Destination.IsEmptyString Then
Destination = Destination.CutPath.PathWithSeparator & "ChannelImage\"
- Destination.Name = Source.Name
- Destination.Extension = Source.Extension
- If Source.Exists AndAlso Destination.Exists(SFO.Path) Then IO.File.Copy(Source, Destination)
+ Dim f As SFile
+ Dim i% = 0
+ If Destination.Exists(SFO.Path) Then
+ For Each ff As ChannelsCollection.ChannelImage In Source
+ f = Destination
+ f.Extension = ff.File.Extension
+ f.Name = $"{IIf(i = 0, "!", String.Empty)}{ff.Channel}_{ff.File.Name}"
+ If ff.File.Exists Then IO.File.Copy(ff.File, f)
+ i += 1
+ Next
+ End If
End If
Catch ex As Exception
End Try
@@ -660,7 +683,7 @@ Friend Class ChannelViewForm : Implements IChannelLimits
End Sub
Private Sub BTT_C_OPEN_FOLDER_Click(sender As Object, e As EventArgs) Handles BTT_C_OPEN_FOLDER.Click
Dim f As SFile = GetPostBySelected().CachedFile
- If Not f.IsEmptyString Then f.Open(SFO.Path)
+ If Not f.IsEmptyString Then GlobalOpenPath(f, EDP.LogMessageValue)
End Sub
Private Sub BTT_C_REMOVE_FROM_SELECTED_Click(sender As Object, e As EventArgs) Handles BTT_C_REMOVE_FROM_SELECTED.Click
Try
diff --git a/SCrawler/Content/Icons/DownArrow_Blue_24.ico b/SCrawler/Content/Icons/DownArrow_Blue_24.ico
new file mode 100644
index 0000000..2d3e1b7
Binary files /dev/null and b/SCrawler/Content/Icons/DownArrow_Blue_24.ico differ
diff --git a/SCrawler/Download/ActiveDownloadingProgress.Designer.vb b/SCrawler/Download/ActiveDownloadingProgress.Designer.vb
new file mode 100644
index 0000000..f4482f5
--- /dev/null
+++ b/SCrawler/Download/ActiveDownloadingProgress.Designer.vb
@@ -0,0 +1,61 @@
+' Copyright (C) 2022 Andy
+' This program is free software: you can redistribute it and/or modify
+' it under the terms of the GNU General Public License as published by
+' the Free Software Foundation, either version 3 of the License, or
+' (at your option) any later version.
+'
+' This program is distributed in the hope that it will be useful,
+' but WITHOUT ANY WARRANTY
+Namespace DownloadObjects
+
+ Partial Friend Class ActiveDownloadingProgress : 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 resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(ActiveDownloadingProgress))
+ Me.TP_MAIN = New System.Windows.Forms.TableLayoutPanel()
+ Me.SuspendLayout()
+ '
+ 'TP_MAIN
+ '
+ Me.TP_MAIN.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.[Single]
+ 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.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 = 1
+ Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 62.0!))
+ Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 62.0!))
+ Me.TP_MAIN.Size = New System.Drawing.Size(434, 61)
+ Me.TP_MAIN.TabIndex = 0
+ '
+ 'ActiveDownloadingProgress
+ '
+ Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
+ Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
+ Me.ClientSize = New System.Drawing.Size(434, 61)
+ Me.Controls.Add(Me.TP_MAIN)
+ Me.Icon = CType(resources.GetObject("$this.Icon"), System.Drawing.Icon)
+ Me.KeyPreview = True
+ Me.MinimumSize = New System.Drawing.Size(450, 100)
+ Me.Name = "ActiveDownloadingProgress"
+ Me.Text = "Active downloading progress"
+ Me.ResumeLayout(False)
+
+ End Sub
+
+ Private WithEvents TP_MAIN As TableLayoutPanel
+ End Class
+End Namespace
\ No newline at end of file
diff --git a/SCrawler/Download/ActiveDownloadingProgress.resx b/SCrawler/Download/ActiveDownloadingProgress.resx
new file mode 100644
index 0000000..1f2f6a4
--- /dev/null
+++ b/SCrawler/Download/ActiveDownloadingProgress.resx
@@ -0,0 +1,624 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 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
+
+
+
+
+ AAABAAwAMDAQAAEABABoBgAAxgAAACAgEAABAAQA6AIAAC4HAAAYGBAAAQAEAOgBAAAWCgAAEBAQAAEA
+ BAAoAQAA/gsAADAwAAABAAgAqA4AACYNAAAgIAAAAQAIAKgIAADOGwAAGBgAAAEACADIBgAAdiQAABAQ
+ AAABAAgAaAUAAD4rAAAwMAAAAQAgAKglAACmMAAAICAAAAEAIACoEAAATlYAABgYAAABACAAiAkAAPZm
+ AAAQEAAAAQAgAGgEAAB+cAAAKAAAADAAAABgAAAAAQAEAAAAAACABAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAIAAAIAAAACAgACAAAAAgACAAICAAACAgIAAwMDAAAAA/wAA/wAAAP//AP8AAAD/AP8A//8AAP//
+ /wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB4YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjsjgAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAIbOh4AAAAAAAAAAAAAAAAAAAAAAAAAAAH5mfsgAAAAAAAAAAAAAAAAAAAAAAAAAB+
+ xs54YAAAAAAAAAAAAAAAAAAAAAAAAAfsbmxo5wAAAAAAAAAAAAAAAAAAAAAAAIbObObOeMAAAAAAAAAA
+ AAAAAAAAAAAACG5+zmzsaOgAAAAAAAAAAAAAAAAAAAAABn7ObOZmbneAAAAAAAAAAAAAAAAAAAAAfs5s
+ 5+zs7I7AAAAAAAAAAAAAAAAAAAAH7Ofm7G7GbGiOAAAAAAAAAAAAAAAAAAB+fs7Ofs5+zmzngAAAAAAA
+ AAAAAAAAAAhn7Ojs5uzm7OZ4yAAAAAAAAAAAAAAAAAaOfm7Obsfsbs7OjnAAAAAAAAAAAAAAAH7Ojs7n
+ 7O7ObOZs54AAAAAAAAAAAAAABn6Ozuduzn5uznzmyOcAAAAAAAAAAAAAfn7I6M7s5+zn7Obs5oyAAAAA
+ AAAAAAAIZ+jujuzo7Obs5uxubOjnAAAAAAAAAAAG586M5+js7n7OfOfs5s54cAAAAAAAAABnzo7o5+zu
+ fs5+5uzmzmzowAAAAAAAAAdujn5+fu7Ozuzs7Ofs5+bI6AAAAAAAAHzn7OjOjI5+5+jufs5uzs5ueOAA
+ AAAAAG6M6O6O7n7Ofs7Ozo7Ofmzs53gAAAAAB+zo7IznyOzuzufufs6Ofo6Ofn4AAAAACEdsZ2Z87o5+
+ js7O7O5cjHx8jIgAAAAAAAAAAAAAfOfs7Ojs6OfgAAAAAAAAAAAAAAAAAAAAbo7O6O7O7OfAAAAAAAAA
+ AAAAAAAAAAAAfsjm7Obo7s6AAAAAAAAAAAAAAAAAAAAAaO5+zo7OyO5wAAAAAAAAAAAAAAAAAAAAzn7O
+ js7n7sjAAAAAAAAAAAAAAAAAAAAAaM6Ozuzuzu5wAAAAAAAAAAAAAAAAAAAAbn7Obn5+jshgAAAAAAAA
+ AAAAAAAAAAAAbOjn7Ozs7O5wAAAAAAAAAAAAAAAAAAAAfnzn5+bn7n7AAAAAAAAAAAAAAAAAAAAAbOjs
+ 7OzuzshgAAAAAAAAAAAAAAAAAAAAaOyOfn7I5+5wAAAAAAAAAAAAAAAAAAAAbOfs7Ozm7OfAAAAAAAAA
+ AAAAAAAAAAAAbnzn5+bs5u5wAAAAAAAAAAAAAAAAAAAAfOfsjOx+zn7AAAAAAAAAAAAAAAAAAAAAbnzn
+ 5o7OfshgAAAAAAAAAAAAAAAAAAAAx+Z+zs5uzm5gAAAAAAAAAAAAAAAAAAAAbs7H5+fObsjAAAAAAAAA
+ AAAAAAAAAAAAZ2js585s7O5wAAAAAAAAAAAAAAAAAAAAjOyH5+jn53aAAAAAAAAAAAAAAAAAAAAACGZs
+ bHxsfGgAAAAAAAAAAAD///////8AAP///////wAA////////AAD///j///8AAP//8H///wAA///gP///
+ AAD//+Af//8AAP//wB///wAA//+AD///AAD//wAH//8AAP/+AAP//wAA//4AAf//AAD//AAB//8AAP/4
+ AAD//wAA//AAAH//AAD/4AAAP/8AAP/gAAAf/wAA/8AAAB//AAD/gAAAD/8AAP8AAAAH/wAA/gAAAAP/
+ AAD+AAAAAf8AAPwAAAAB/wAA+AAAAAD/AADwAAAAAH8AAPAAAAAAPwAA4AAAAAA/AADgAAAAAD8AAP/8
+ AAH//wAA//wAAf//AAD//AAB//8AAP/8AAH//wAA//wAAf//AAD//AAB//8AAP/8AAH//wAA//wAAf//
+ AAD//AAB//8AAP/8AAH//wAA//wAAf//AAD//AAB//8AAP/8AAH//wAA//wAAf//AAD//AAB//8AAP/8
+ AAH//wAA//wAAf//AAD//AAB//8AAP/8AAH//wAA//4AA///AAAoAAAAIAAAAEAAAAABAAQAAAAAAAAC
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAgAAAAICAAIAAAACAAIAAgIAAAICAgADAwMAAAAD/AAD/
+ AAAA//8A/wAAAP8A/wD//wAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5+AAAAAAAAAAAAAAAAAA
+ BoyHAAAAAAAAAAAAAAAAAOfn7AAAAAAAAAAAAAAAAAjIzn6AAAAAAAAAAAAAAAAO5uxo7AAAAAAAAAAA
+ AAAAaM7ObI6AAAAAAAAAAAAADn5ubOboyAAAAAAAAAAAAH7Ozs5sbo4AAAAAAAAAAAjI5+fn7saM4AAA
+ AAAAAAAO7n7Ozsbs6OcAAAAAAAAAaH7O5+bn5s6MgAAAAAAABo7o5+zs7Ozm6OAAAAAAAH7I7Ozufn5u
+ zsfsAAAAAAjOjn6Ofs7s5+bsjnAAAAAG6Ozo7O7n5+zs5ujnAAAAaOyOjo587Ozo6I7IjIAAAGxmxsZ+
+ 7o7uzsbG7O4AAAAAAAAM587OyOhgAAAAAAAAAAAABo7n5+7OYAAAAAAAAAAAAAzozuzujsAAAAAAAAAA
+ AAAGjufuzs5wAAAAAAAAAAAADOfOyOfowAAAAAAAAAAAAAaOfm7O7mAAAAAAAAAAAAAM7Ofs5sjAAAAA
+ AAAAAAAABn585+7oYAAAAAAAAAAAAAyM6Oxs58AAAAAAAAAAAAAG5+zm5+5gAAAAAAAAAAAABOx+fs7I
+ wAAAAAAAAAAAAAZ+Z8hs7kAAAAAAAAAAAAAMjOjm52fAAAAAAAAAAAAAAGbExsbOAAAAAAAA///////8
+ f///+D////A////gH///4A///8AH//+AA///AAP//gAB//4AAP/8AAB/+AAAf/AAAD/gAAAf4AAAD8AA
+ AAfAAAAP/4AH//+AB///gAf//4AH//+AB///gAf//4AH//+AB///gAf//4AH//+AB///gAf//4AH///A
+ D/8oAAAAGAAAADAAAAABAAQAAAAAACABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAgAAAAICAAIAA
+ AACAAIAAgIAAAICAgADAwMAAAAD/AAD/AAAA//8A/wAAAP8A/wD//wAA////AAAAAAAAAAAAAAAAAAAA
+ AAAABgAAAAAAAAAAAAAAeOAAAAAAAAAAAAAG7IcAAAAAAAAAAAB+fujgAAAAAAAAAAbs7OyHAAAAAAAA
+ AGjufm7oYAAAAAAABo7OzsfOhwAAAAAAaO5+5+7s6GAAAAAG5+zs7Ozn7PYAAABo6Ojo7n6IjojgAAAM
+ bGzs7OyOx+wAAAAAAAfo7o7nAAAAAAAAAAaM7OyGAAAAAAAAAAbufu6MAAAAAAAAAAd+zn6GAAAAAAAA
+ AAzn7OznAAAAAAAAAAaOzuiGAAAAAAAAAAbn587sAAAAAAAAAAZ87m6GAAAAAAAAAAzozs6MAAAAAAAA
+ AAaOh+eGAAAAAAAAAABsbGxgAAAAAAAAAAAAAAAAAAAAAP///wD/7/8A/8f/AP+D/wD/Af8A/gD/APwA
+ fwD4AD8A8AAfAOAADwDAAAcA4AAPAP4A/wD+AP8A/gD/AP4A/wD+AP8A/gD/AP4A/wD+AP8A/gD/AP4A
+ /wD/Af8A////ACgAAAAQAAAAIAAAAAEABAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAACA
+ AAAAgIAAgAAAAIAAgACAgAAAgICAAMDAwAAAAP8AAP8AAAD//wD/AAAA/wD/AP//AAD///8AAAAABgAA
+ AAAAAABoYAAAAAAABo6MAAAAAABozshgAAAABo5uboYAAABo7OfOeGAABo5+fn6OhgB2xs7O6MfHgAAA
+ aOfIYAAAAABuzujgAAAAAGjn6MAAAAAAzs5+cAAAAABo5+jAAAAAAG7OyOAAAAAAaI6OcAAAAAAMbGYA
+ AAD+/wAA/H8AAPg/AADwHwAA4A8AAMAHAACAAwAAAAEAAPAfAADwHwAA8B8AAPAfAADwHwAA8B8AAPAf
+ AAD4PwAAKAAAADAAAABgAAAAAQAIAAAAAAAACQAAAAAAAAAAAAAAAQAAAAEAAAAAAACjaB0ApmsgAKlt
+ IwCrcCYArXMqALF1LACzeC4ArHYwALR6MQC4fTQAt385AMh1AADJdwQAy3gAAM17AADOfQEAy3oHAM5/
+ CQDMfA0A0H4BAMt/GQC7gDcAvoI6ALmBPADRgAIA0oIEANSDBADVhQUA1ogGANaMBwDZiQYA3I0GANKE
+ CgDWhwkA0YIMANaMCwDZiggA2o0JANyOCQDbkgsA3pAKAN6UCwDekg0A3ZQNAM+EGgDRhhUA1YkSANiN
+ EgDajhcA2pISANqSHgDalh4A3ZgaAOGUCgDgkg0A4ZUOAOSWDgDhmA4A5ZgPAOicDgDklxAA45cVAOGY
+ EADlmRAA5ZwRAOSdFwDonRIA550YAOmfGwDpoBIA6qEUAO2iFADrpBQA7qUWAO6mGADvqBgA8KUXAPCm
+ GADxqRgA9KsZAPKsGgD0rBoA9a0cAPiuGgD1sBsA+bEbAPixHADOhycAzo0lAMqKKwDRjCIA0Y8lANSP
+ JgDTji0A05ImANiRIwDdnScA1JQpANmVKwDXmCoA25stAMGJNQDAhj0A0I84AM2VPwDTmTYA3JwyANKR
+ OQDSlDgA2Js6AOGeIADknS8A3qAxANyiPwDopCAA7qogAOKiLQDtqykA8asjAPOxJwD6syAA+LMkAPq1
+ JwD2tSoA+rYsAPq4LgDiozEA6qUzAOepNQDpqTEA66wyAOmrNwDtqjUA6qw2AO2vNQDjpjoA6aw4AOir
+ PwDwrDgA67A3AO2xOQDssT8A8rIwAPazMwD2tzEA8bE2APq7NgDytDoA+Lc6APW5PAD5uToA+bw7APi7
+ PQD5vD0A/L49APzAPgCxgEMAv4VAALaFSAC5h0sAvJZmALyacgC5mnYAxYlBAMOLRgDHi0QAyY1GAMaO
+ SgDJj0oAyZBEAMyQSgDPlEkAypFNAM6TTADNlE4A0pVHANebQwDUnUUA0JRNANaaSgDDkVUAzpZWAM2X
+ WwDOmVsAzZpdANGZVgDUmlcA0ppaANOcWwDQnF4A16BHANqiSADapUkA3adLAN2lTQDbo1QA1KFeANql
+ WwDgpkMA4KtOAO6sSwDxrUYA8rdAAPGxRQD1uUAA+r1AAPSySADirl0Ax5ljAMOYZgDFmWUAz51gAM+f
+ ZADBnG4AxJ1uANGeYgDQnmUA0aBjANalYwDVoWQA2KRgAN+sYwDbqWYA3aplAN2sZwDRo2sA1KdrANWl
+ bADZqmoA3q5qAMWhcgDNpHMAzKV1AMmmeQDUqXAA1Kp5ANuwcADhrWAA569jAOCvaQDismgA5LRoAOGy
+ bADitGwA+8BDALGchQCxnokAxa2OAMqwkQDPs5MA1LiXAN3AngDjwJYAAAAAAP///wAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADc2b4AAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAOy6sLjRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAA+7oVbN2+ogAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAulgR
+ DLTFuvYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC6aCMREQzGvroAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALisLxsUERET4Nm4AAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAA6awxJhsbGxERLuC+0QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAD6rGArJiYdGxsREV7gvqIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACoYz4+NyYm
+ GxsbERGx3Lr2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKhsREM3PjcpJh8bGxQRw9y6AAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtapFSEhGOzc3KSYfGxsbE+DauAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAADXpYJIT0hIRkY3NzcmJhsbGS7w2dEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ APqeipBPT01IRkZGPjc3JiYbGxFg8L6iAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ5yzZdPT09NSEZG
+ Ozc3NyYmGxsZbvC49gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGGnLzpd5T09PTk1GRkZDNzcmJh0bEcHj
+ ugAAAAAAAAAAAAAAAAAAAAAAAAAAAACgnsuX9fV5V1dPT05NRkZGNzc3Jh8fGyPu47cAAAAAAAAAAAAA
+ AAAAAAAAAAAAAKELjs719fV+V09XT05OTkhGRjdDNyYmHxsv8dnRAAAAAAAAAAAAAAAAAAAAAAAA+AmI
+ l/X1l/WTV1dXV09PTk5IRkY3NzcpJh0bYPG+ogAAAAAAAAAAAAAAAAAAAAAACGrLl8719fWXeVdXT1RX
+ Tk5NSEZGRjc3KSYdG27tvvcAAAAAAAAAAAAAAAAAAAAIZo7Ll87OzvX1fldXVFRTT1dOTUhGRjs+NyYm
+ JhvC67YAAAAAAAAAAAAAAAAAAJ0JgZDLy8uczs7Ofld5eVdPVE9PTk1IRkY7NzcpJh8i0Ou4AAAAAAAA
+ AAAAAAAA1whxjI6Qy5PLl/XOl3l5V3l5eVdPV05NTUZGRjs3KSYfMPHj6QAAAAAAAAAAAAAABGOBjIyU
+ k8uXnJf1l3l5eXl5V1dXT1dOTU1GRjw7NzYfHzPxvvwAAAAAAAAAAACfWnFwf4CAgoyLy4yXznlPeVR5
+ V1dXV05XTneLz8/Pz8/Pz8/J79wAAAAAAAAAAADqAQECAgMDAwQEBAaXznx5VHl5V1dXV1dPV5WkpKam
+ pqarp6ezs/0AAAAAAAAAAAAAAAAAAAAAAAAAAASXy5NQVXlUeXlXV1dXTpWkAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAASXy5NPVVVUeXlXV1dXV5dnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAASQy8t4UFVVeVR5eVdXV5dnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOQjst8TFBV
+ VVR5eXlXeZeeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKMjJCOUFBQT1V5VHl5eZcWAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKCjI6MdEhPVU95VXlUeZwKAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAKCgoKOdkh0UE9VVXlUeZwKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAJ/f4KCgkhITExPT1VVeZwJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF/f4GMjHNG
+ SExPVU95VZwJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF1cXF/iHVGRkh0UFBQT5wJAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFlZXF/f4FDQ0ZGSExPVZcGAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAFlZWVxdX9vLENGRnRQT5cJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAFvY2VlcXFxKT9BRkZMTMsEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFjY2RlZXFx
+ NSwsRkZGTJMEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFgY2NkZWVxYSksPz9GRssEAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFgWVlkY2RxcTIoLCxGRowEAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAFZWWRZZGRkcTQdLCw/Q4wDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAFYWVljWWRjZGQyHSgogYECAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOcBv7+/wcHB
+ wsjHZXFxdQHnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADnAQEBAQEBAQEBAQEBAecAAAAA
+ AAAAAAAAAAAAAAAAAAD///////8AAP///////wAA////////AAD///j///8AAP//8H///wAA///gP///
+ AAD//+Af//8AAP//wB///wAA//+AD///AAD//wAH//8AAP/+AAP//wAA//4AAf//AAD//AAB//8AAP/4
+ AAD//wAA//AAAH//AAD/4AAAP/8AAP/gAAAf/wAA/8AAAB//AAD/gAAAD/8AAP8AAAAH/wAA/gAAAAP/
+ AAD+AAAAAf8AAPwAAAAB/wAA+AAAAAD/AADwAAAAAH8AAPAAAAAAPwAA4AAAAAA/AADgAAAAAD8AAP/8
+ AAH//wAA//wAAf//AAD//AAB//8AAP/8AAH//wAA//wAAf//AAD//AAB//8AAP/8AAH//wAA//wAAf//
+ AAD//AAB//8AAP/8AAH//wAA//wAAf//AAD//AAB//8AAP/8AAH//wAA//wAAf//AAD//AAB//8AAP/8
+ AAH//wAA//wAAf//AAD//AAB//8AAP/8AAH//wAA//4AA///AAAoAAAAIAAAAEAAAAABAAgAAAAAAAAE
+ AAAAAAAAAAAAAAABAAAAAQAAAAAAAKdIAACpSgAArU0AALBSAACxVQAAtFYAALdYAAC5WwAAuVwAALxe
+ AAC/YQAAvGgNALllFwC5Zx8AtWUpAMJkAADFaAAAyGsAAMxuAgDOcQYA0nYJANR5DQDFdREAz34aANd8
+ EADafxMA0IAGANSDBQDThAYA1YUFANiJBwDThg8A2YoIANqNCQDcjQoA25AKAN2QCgDekQ0A35UNAN2A
+ FgDXjxAA2Y4UAN6DGQDWjBsA3ZEQAN6UFgDdmBgA4JMKAOCSDQDhlQ4A5ZcPAOSZDgDghhsA44geAOSW
+ EADjmxEA5ZoQAOScEQDnnxQA6Z0SAOWcGgDpoBIA6aEUAO2iFADtpBYA5qAZAO+oGgDwphcA8acYAPGp
+ GAD0qxkA86waAPSsGgD1rhwA9rAbAPawHAD4sBoA+LEcAMyAIADXhSMA0IUnAN+NKgDRkScA2pYhAN2b
+ IwDcmyUA05MoANWWKQDali8A2ZksANucLQDcnS4A3Z0xAN2TOwDblT8A1po7ANKYPADVmj0A154+AOaO
+ JADjkCoA5ZIuAOqTKgDtkysA7ZQrAOiTLQDulSwA4pIxAOmWMQDrlzUA6J02AOOaOQDgnTwA6pw5APCa
+ NADynj0A36AvAN6hMADdoDgA2aE/AOSgIQDioSYA7KchAO2oJgDgoCkA6agrAPewIAD5tCIA+bQnAPOx
+ KAD5tigA+bcvAOOmMgDjpjQA5KU0AOepNQDnrD8A6Ko4AOyvOADprj8A86A/AO+zOgDysjAA8LE3APe4
+ NgD7ujEA8rY7APCzPQDytj4A9Lg6APW6PQD7vDoA+b0+AMWARADPkEUA3ZlEANmbTQDPnF8A0phUAOqe
+ QgDdoEEA2aJGAN2kRADep0kA3aVOANuiUADdplQA4ahGAO2qRwDooEgA4alKAOOtTQDzoUAA869EAPap
+ TgDsskAA77JGAOewTgDusUoA6rBMAPC1QwD0ukUA9rxGAPm9QQDwskkA8rVNAPe+SAD1uk8A+L5IAPWp
+ UADyqlQA6rRRAO22UgDuuFQA47BbAPCyUQD0vFAA8r5WAPC2XAD4sV0AyZ5nAMSlfwDfr3MA2K19AOWt
+ YgDltmcA67NgAOm5YQDsu2oA77ptAPW+awDwvm0A+bhpAOizcQDiu34A7Lt9APS9cAD7wEIA+8FGAPnA
+ SAD4wk4A+MNZAPnEWgD6w2AA/ctkAMOnhQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAABta58AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbc2/a8oAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAKpvpabIawAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADWcqgdHaa/awAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAG2JLR8dIM6tXgAAAAAAAAAAAAAAAAAAAAAAAAAAAABlsT0xIx8dLNJzngAAAAAA
+ AAAAAAAAAAAAAAAAAAAAUq58PDMxIyMdWdVr4gAAAAAAAAAAAAAAAAAAAAAAAJypj0REMzMxJSMdochr
+ AAAAAAAAAAAAAAAAAAAAAADLcN9KRURERDMxJSMjw69lAAAAAAAAAAAAAAAAAAAAAFDhuH9KSkQzRDMx
+ JSMq0o1fAAAAAAAAAAAAAAAAAAAY4Nu4gUdHQ0VEM0QlJSNU2XPJAAAAAAAAAAAAAAAAT8TcuNuSTUpK
+ R0REMzsxJSNd1WsAAAAAAAAAAAAAAJtw3bi425hNTUpKSkRERDMzMSOryGsAAAAAAAAAAADMUd2Vl7i4
+ mIFNTU1KSkRERDMzMSXQr2wAAAAAAAAAABfBsJOXmLi4g01NTU1KSkRERDk5MS3ZdJ0AAAAAAAAMz7S0
+ urrFxbiETYFNTU1KSo+xlbq6tMfTa9gAAAAAAJoGBgYGCQkL35FLTU2BTU1KthkZKys1NmSgAAAAAAAA
+ AAAAAAAAAAnfl0pLTU1NgU29FgAAAAAAAAAAAAAAAAAAAAAAAAAABt2VgkpNgU1NTdwVAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAGxrGPSkpKTU2B2xQAAAAAAAAAAAAAAAAAAAAAAAAAAAbCi5BDREpLTU3cEwAAAAAA
+ AAAAAAAAAAAAAAAAAAAABMGIi3xAR0pLTdwTAAAAAAAAAAAAAAAAAAAAAAAAAAADsoWKjzlEQ0pL3BEA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAOrhYWFOztEREq7EQAAAAAAAAAAAAAAAAAAAAAAAAAAA6tdXIV5OTtE
+ Q7YLAAAAAAAAAAAAAAAAAAAAAAAAAAABpVxcdn0nMztEtgkAAAAAAAAAAAAAAAAAAAAAAAAAAAGhWFpd
+ di8nMzu1CQAAAAAAAAAAAAAAAAAAAAAAAAAAAaVTWFxdViUnJ7EGAAAAAAAAAAAAAAAAAAAAAAAAAAAB
+ YFNYWFhcKSQnsAYAAAAAAAAAAAAAAAAAAAAAAAAAAA/XYWBjoaF3VFZWDQAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAA8BAQEBAQEEBA4AAAAAAAAAAAAAAAD///////x////4P///8D///+Af///gD///wAf//4AD//8A
+ A//+AAH//gAA//wAAH/4AAB/8AAAP+AAAB/gAAAPwAAAB8AAAA//gAf//4AH//+AB///gAf//4AH//+A
+ B///gAf//4AH//+AB///gAf//4AH//+AB///gAf//8AP/ygAAAAYAAAAMAAAAAEACAAAAAAAQAIAAAAA
+ AAAAAAAAAAEAAAABAAAAAAAApWYWAKhpGQCqbBsAqm0dAKxuHgCucCEAsHMjALJ2JgC2eioAuH4uALyC
+ MgC9gzQAvoU1AN+YEwDklw8A5ZkQAOmdEgDooBIA6aEUAO2hFQDtpRYA8KYXAPGqFwDxpxgA8qoZAPSr
+ GgD0rRoA86wcAPauHAD3sBoA97AcAPmwGgD4sRwAwIY3AMGJOQDDizwAxIs8AMWNPgDjnSUA5J4nAN6g
+ MADnoyAA5aAnAOSjKQDgoi8A7awpAPCsIwD6syIA+rcrAOKlMwDqqzIA6Kk3AOSpOgDpqz0A+rozAPq6
+ NADytjsA97s+APe8PAD5uzgA36VCAN+sVADgqUQA5axHAOmuQADkrUoA6bNKAO63TgDws0EA9btDAPS6
+ RQD6vkEA9LxMAPm+SADhrlUA5LJZAPK7UAD7wEMA+8NOAPbBUAD6xFIA+8VUAPDDaQD0xmwA9shtAPjK
+ bgD6zW8A8cmCAPPMhAD0zoYA99CHAPDPmQDw0JkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAD///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAACZcJgAAAAAAAAAAAAAAAAAAAAAAAAAAJjYnXSYAAAAAAAAAAAAAAAAAAAAA
+ AAAiRREPJ10mAAAAAAAAAAAAAAAAAAAAAAtHFhYQECddJgAAAAAAAAAAAAAAAAAACkobGxYRERAnXSYA
+ AAAAAAAAAAAAAAAJUjAbGxgbFhEQKF0mAAAAAAAAAAAAAAhSTjEgGxsbFhYRECtdJgAAAAAAAAAABjpI
+ SDggICAbGBgWERAnXSYAAAAAAAAFOVBSUk84IDAgGxtUW1paWlwmAAAAAAAABQUGBgg8IDAgMBtWCwsL
+ IiIAAAAAAAAAAAAAAAY6HiAgMCBXCgAAAAAAAAAAAAAAAAAAAAVHGx4wICBXCgAAAAAAAAAAAAAAAAAA
+ AAVJGxseMCBXCQAAAAAAAAAAAAAAAAAAAAVJLxsbHjBXCAAAAAAAAAAAAAAAAAAAAAFELhYcGx5XCAAA
+ AAAAAAAAAAAAAAAAAAFDMxMVGxtXBgAAAAAAAAAAAAAAAAAAAAFANCsWFhxVBgAAAAAAAAAAAAAAAAAA
+ AAE/MisOEBZVBQAAAAAAAAAAAAAAAAAAAAE9KS0OEBNUAwAAAAAAAAAAAAAAAAAAAAE+S0xAND9TAwAA
+ AAAAAAAAAAAAAAAAAAABAQEBAwEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP/v
+ /wD/x/8A/4P/AP8B/wD+AP8A/AB/APgAPwDwAB8A4AAPAMAABwDgAA8A/gD/AP4A/wD+AP8A/gD/AP4A
+ /wD+AP8A/gD/AP4A/wD+AP8A/gD/AP8B/wD///8AKAAAABAAAAAgAAAAAQAIAAAAAAAAAQAAAAAAAAAA
+ AAAAAQAAAAEAAAAAAAClZhYAsnYmALN4KQC4fi8A1Y0XANWOGADXkBkA2pUbANuVHADclhwA3poeAOCc
+ HwDgmyAA46EiAOaiIwDmpiMA56cnAOilIwDppiUA66omAO2rJwDuricA46YsAOyrKADurikA7q4sAPCv
+ KQDvsS4A8LAqAO2vMQDkpjgA5q8+AOqwOADwszIA4qtBAOi0TwDyvEoA7LlSAOSyXADnt18Aw5ljAMui
+ bQDqumEA5LhoAOa7cADpv3MA78FlAPDFdgDyyXkA88x6APXNegD20IAA6MeQAOnIkQDpyZcAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAP///wAAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAzcCAAAAAAAAAAAAAAAAAycFNQIAAAAA
+ AAAAAAAAAysLCAY1BAAAAAAAAAAAAy8SDg0IBjUEAAAAAAAAAzMaFBIODQgINQQAAAAAAzQlIhQaEg4f
+ Li01BAAAKQEBAQEbGhQUMAQEBAQqAAAAAAABGxsbFDMEAAAAAAAAAAAAARwbGxszBAAAAAAAAAAAAAEe
+ FBsbMwQAAAAAAAAAAAABIRQcHDMEAAAAAAAAAAAAASARFBQzBAAAAAAAAAAAAAEjFw4WMwQAAAAAAAAA
+ AAABLCgkJjMDAAAAAAAAAAAAAAEBAQEBAAAAAAAA/v8AAPx/AAD4PwAA8B8AAOAPAADABwAAgAMAAAAB
+ AADwHwAA8B8AAPAfAADwHwAA8B8AAPAfAADwHwAA+D8AACgAAAAwAAAAYAAAAAEAIAAAAAAAgCUAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAADAAAABgAAAAgAAAAGAAAAAwAA
+ AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAMAAAAFgAA
+ ABsAAAAWAAAACwAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAA
+ AAwAAAAfAAAAMwAAADwAAAAyAAAAHQAAAAsAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAACAAAACSccDyHPllLiypFQ4M2UUesdFQtZAAAANQAAABoAAAAJAAAAAgAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAEAAAAHAAAAF8aOTb/RlU3z0pVH/82UU/K/iUvcAAAAUAAAADAAAAAWAAAABgAA
+ AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ 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
+ Q//6tiz/+K4a//auG//1rRr/9Ksa//KpGf/wpxj/76MX/+2hFP/pnxP/6JwS/+WYD//hlQ//35IN/92O
+ Cv/aiwn/14gH/9SEBf/ViRP/4rJo/8+cXfS4hEjVAAAATQAAAC0AAAAUAAAABgAAAAEAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAUAAAASjl8jgq91LPfjpjr/+Ls///q+
+ Qf/7v0H/+79B//vAQ//7vDb/+bEc//iwHP/2rhz/9a0b//SrGv/yqhn/8agY/++kF//toRT/6p8T/+ic
+ Ev/lmRD/45YO/9+TDf/djwv/2owI/9eJB//UhQT/2JEj/+Gya/zRmVf5mm48sQAAAEYAAAAnAAAAEAAA
+ AAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAA5zTBtTrXIp+NOY
+ NPzytz3/+bw+//q9QP/6vkH/+79B//u/Qf/7vj7/+rMg//mxHP/4sBz/9q4c//WsG//0rBr/86oZ//Go
+ GP/vpBf/7aEV/+qfE//onRH/5ZkQ/+OWD//gkw7/3Y8J/9uMCP/XiQf/1YQG/9uaNv/armz30phU+nJR
+ LI0AAABAAAAAIgAAAAwAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAACzom
+ DSyobyXxv4Ut9e2wOv/1ujz/+Ls9//m8QP/6vUD/+r5B//u/Qf/7v0L/+rUn//mxG//5sR3/+LAc//au
+ HP/1rRv/9Kwa//OqGf/xqBj/76QX/+6iFf/roBP/6JwS/+aZEP/klg//4JMN/96OC//bjQn/2YkI/9WF
+ Bv/fpkz/06Zp8s2TUPY/LRhtAAAAOQAAABwAAAAJAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAEGRAFFKRqI9mwdibx56o3//G0Ov/ztjv/9bo8//a7Pv/5vED/+r1A//q+Qf/7wEP/+rgu//mx
+ G//5sh3/+bId//ixHP/2sBz/9a0b//SrGv/yqhn/8agY//CmF//uoxX/6qAT/+mdEf/mmRD/5JYP/+CT
+ Dv/ejwr/3IwK/9mJB//Whwn/4q5d/9GiZ+7Hj0znEg0HSAAAACcAAAAPAAAAAwAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAGoGcgpaluIvLdoDP/6q03/++yOf/xtDr/87Y7//S4PP/2uz3/+bw+//m9
+ QP/6vkL/+rs3//myHP/5sh3/+bId//myHf/5sR3/+LAc//WtHP/0rRv/9KsZ//KpGP/wphf/7qMV/+ug
+ E//onhL/55oR/+SXD//hlA3/3pAL/9yNCf/Zigf/2I0S/+S0aP/ToGPwvolJxAAAACcAAAARAAAABQAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACiaB9ppmsg9dKVL//nqjX/67A3/+6yOP/wtDr/87Y7//W4
+ PP/4uz3/+b0+//m9QP/5vUD/+rw7//myHv/5sh3/+bId//myHf/5sh3/+LEd//iwHP/2rhv/9awb//Or
+ Gv/xqBn/8KYY/+6jFf/roBP/6p4Q/+icDv/llw3/4ZQK/9+QCf/cjQb/2okF/9mRHf/ismj40ZVQ9MGK
+ SogAAAALAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACmaR7PyYgn+uSdL//loDD/56Iy/+qk
+ M//rpjT/7qk3//CrOP/xrTn/8a05//CxOP/4vD7/+bxA//izJP/4sRz/+bId//myHf/5sh3/+bId//mx
+ Hf/4sBz/9q4c//WsG//zqxr/8qkY//GrI//xsDr/8bFF//W0Sf/2s0n/9LJI//OwSP/yr0f/8a1G//Cs
+ R//urEv/5ahV6dGVT+AAAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACkaR2XpGoe/6Vq
+ H/+mayD/pmwh/6hsIv+pbSP/qW4k/6twJf+scCf/rXIn/65zKf/6vDz/+LxA//a1Kv/2sBr/+bEd//my
+ Hf/5sh3/+bId//myHf/5sR3/+LAc//auHP/1rRv/9Ksa//a3Ov/EiED/xYlB/8aKQ//Hi0T/yY1G/8qO
+ R//Lj0n/zJBK/82RS//Ok0z/0JRN/9GVTpcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK1yJ//5uzv/9rs+//a3
+ Mf/1rhr/9rAc//iyHf/5sh3/+bId//myHf/5sh3/+bEd//iwHP/2rhz/9a0b//m5Ov/Chj//AAAAKwAA
+ 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
+
+
+
\ No newline at end of file
diff --git a/SCrawler/Download/ActiveDownloadingProgress.vb b/SCrawler/Download/ActiveDownloadingProgress.vb
new file mode 100644
index 0000000..858416f
--- /dev/null
+++ b/SCrawler/Download/ActiveDownloadingProgress.vb
@@ -0,0 +1,72 @@
+' Copyright (C) 2022 Andy
+' This program is free software: you can redistribute it and/or modify
+' it under the terms of the GNU General Public License as published by
+' the Free Software Foundation, either version 3 of the License, or
+' (at your option) any later version.
+'
+' This program is distributed in the hope that it will be useful,
+' but WITHOUT ANY WARRANTY
+Imports System.ComponentModel
+Imports PersonalUtilities.Forms
+Namespace DownloadObjects
+ Friend Class ActiveDownloadingProgress
+ Private Const MinWidth As Integer = 450
+ Private MyView As FormsView
+ Friend Property Opened As Boolean = False
+ Private ReadOnly JobsList As List(Of DownloadProgress)
+ Friend Sub New()
+ InitializeComponent()
+ JobsList = New List(Of DownloadProgress)
+ AddHandler Downloader.OnReconfigured, AddressOf Downloader_OnReconfigured
+ Downloader_OnReconfigured()
+ End Sub
+ Private Sub ActiveDownloadingProgress_Load(sender As Object, e As EventArgs) Handles Me.Load
+ MyView = New FormsView(Me)
+ MyView.ImportFromXML(Settings.Design)
+ MyView.SetMeSize()
+ Opened = True
+ End Sub
+ Private Sub ActiveDownloadingProgress_Closing(sender As Object, e As CancelEventArgs) Handles Me.Closing
+ MyView.ExportToXML(Settings.Design)
+ e.Cancel = True
+ Hide()
+ End Sub
+ Private Sub Downloader_OnReconfigured()
+ Const RowHeight% = 30
+ With TP_MAIN
+ If .Controls.Count > 0 Then
+ For Each c As Control In .Controls
+ If Not c Is Nothing Then c.Dispose()
+ Next
+ .Controls.Clear()
+ End If
+ .RowStyles.Clear()
+ .RowCount = 0
+ End With
+ JobsList.ListClearDispose
+ With Downloader
+ If .Pool.Count > 0 Then
+ For Each j As TDownloader.Job In .Pool
+ With TP_MAIN
+ .RowStyles.Add(New RowStyle(SizeType.Absolute, RowHeight))
+ .RowCount += 1
+ JobsList.Add(New DownloadProgress(j))
+ AddHandler JobsList.Last.OnTotalCountChange, AddressOf Jobs_OnTotalCountChange
+ .Controls.Add(JobsList.Last.Get, 0, .RowStyles.Count - 1)
+ End With
+ Next
+ TP_MAIN.RowStyles.Add(New RowStyle(SizeType.Percent, 100))
+ TP_MAIN.RowCount += 1
+ End If
+ Dim s As Size = Size
+ s.Height = TP_MAIN.RowStyles.Count * RowHeight + PaddingE.GetOf({TP_MAIN}).Vertical(TP_MAIN.RowStyles.Count) - TP_MAIN.RowStyles.Count * 2
+ MinimumSize = New Size(MinWidth, s.Height)
+ Size = s
+ End With
+ TP_MAIN.Refresh()
+ End Sub
+ Private Sub Jobs_OnTotalCountChange()
+ If JobsList.Count > 0 Then MainProgress.TotalCount = JobsList.Sum(Function(j) CLng(j.Job.Progress.TotalCount))
+ End Sub
+ End Class
+End Namespace
\ No newline at end of file
diff --git a/SCrawler/Download/DownloadProgress.vb b/SCrawler/Download/DownloadProgress.vb
new file mode 100644
index 0000000..940c515
--- /dev/null
+++ b/SCrawler/Download/DownloadProgress.vb
@@ -0,0 +1,190 @@
+' Copyright (C) 2022 Andy
+' This program is free software: you can redistribute it and/or modify
+' it under the terms of the GNU General Public License as published by
+' the Free Software Foundation, either version 3 of the License, or
+' (at your option) any later version.
+'
+' This program is distributed in the hope that it will be useful,
+' but WITHOUT ANY WARRANTY
+Imports PersonalUtilities.Forms.Toolbars
+Imports Download = SCrawler.Plugin.ISiteSettings.Download
+Imports TDJob = SCrawler.DownloadObjects.TDownloader.Job
+Namespace DownloadObjects
+ Friend Class DownloadProgress : Implements IDisposable
+ Friend Event OnDownloadDone(ByVal Message As String)
+ Friend Event OnTotalCountChange()
+ Private ReadOnly TP_MAIN As TableLayoutPanel
+ Private ReadOnly TP_CONTROLS As TableLayoutPanel
+ Private WithEvents BTT_START As Button
+ Private WithEvents BTT_STOP As Button
+ Private WithEvents BTT_OPEN As Button
+ Private ReadOnly PR_MAIN As ProgressBar
+ Private ReadOnly LBL_INFO As Label
+ Private ReadOnly Property Instance As API.Base.ProfileSaved
+ Friend ReadOnly Property Job As TDJob
+#Region "Initializer"
+ Friend Sub New(ByVal _Job As TDJob)
+ Job = _Job
+
+ TP_MAIN = New TableLayoutPanel With {.Margin = New Padding(0), .Dock = DockStyle.Fill}
+ TP_MAIN.ColumnStyles.Add(New ColumnStyle(SizeType.Percent, 100))
+ TP_MAIN.ColumnCount = 1
+ TP_CONTROLS = New TableLayoutPanel With {.Margin = New Padding(0), .Dock = DockStyle.Fill}
+ PR_MAIN = New ProgressBar With {.Dock = DockStyle.Fill}
+ LBL_INFO = New Label With {.Text = String.Empty, .Dock = DockStyle.Fill}
+ CreateButton(BTT_STOP, My.Resources.Delete)
+
+ If Job.Type = Download.Main Then
+ LBL_INFO.Margin = New Padding(3)
+ LBL_INFO.TextAlign = ContentAlignment.MiddleLeft
+ With TP_MAIN
+ .RowStyles.Add(New RowStyle(SizeType.Percent, 100))
+ .RowCount = 1
+ End With
+ With TP_CONTROLS
+ .ColumnStyles.Add(New ColumnStyle(SizeType.Absolute, 30))
+ .ColumnStyles.Add(New ColumnStyle(SizeType.Absolute, 150))
+ .ColumnStyles.Add(New ColumnStyle(SizeType.Percent, 100))
+ .ColumnCount = .ColumnStyles.Count
+ .RowStyles.Add(New RowStyle(SizeType.Percent, 100))
+ .RowCount = 1
+ With .Controls
+ .Add(BTT_STOP, 0, 0)
+ .Add(PR_MAIN, 1, 0)
+ .Add(LBL_INFO, 2, 0)
+ End With
+ End With
+ TP_MAIN.Controls.Add(TP_CONTROLS, 0, 0)
+ Else
+ LBL_INFO.Padding = New Padding(3, 0, 3, 0)
+ LBL_INFO.TextAlign = ContentAlignment.TopCenter
+ CreateButton(BTT_START, My.Resources.StartPic_01_Green_16)
+ CreateButton(BTT_OPEN, PersonalUtilities.My.Resources.OpenFolderPic)
+ With TP_CONTROLS
+ With .ColumnStyles
+ .Add(New ColumnStyle(SizeType.Absolute, 30))
+ .Add(New ColumnStyle(SizeType.Absolute, 30))
+ .Add(New ColumnStyle(SizeType.Absolute, 30))
+ .Add(New ColumnStyle(SizeType.Percent, 100))
+ End With
+ .ColumnCount = 4
+ .RowStyles.Add(New RowStyle(SizeType.Percent, 50))
+ .RowCount = 1
+ With .Controls
+ .Add(BTT_START, 0, 0)
+ .Add(BTT_STOP, 1, 0)
+ .Add(BTT_OPEN, 2, 0)
+ .Add(PR_MAIN, 3, 0)
+ End With
+ End With
+ With TP_MAIN
+ With .RowStyles
+ .Add(New RowStyle(SizeType.Absolute, 30))
+ .Add(New RowStyle(SizeType.Percent, 100))
+ End With
+ .RowCount = 2
+ End With
+ TP_MAIN.Controls.Add(TP_CONTROLS, 0, 0)
+ TP_MAIN.Controls.Add(LBL_INFO, 0, 1)
+ End If
+
+ With Job
+ .Progress = New MyProgress(PR_MAIN, LBL_INFO) With {.DropCurrentProgressOnTotalChange = False}
+ With .Progress
+ AddHandler .OnProgressChange, AddressOf JobProgress_OnProgressChange
+ AddHandler .OnTotalCountChange, AddressOf JobProgress_OnTotalCountChange
+ End With
+ End With
+
+ If Job.Type = Download.SavedPosts And Not Job.Progress Is Nothing Then Job.Progress.InformationTemporary = Job.Host.Name
+ Instance = New API.Base.ProfileSaved(Job.Host, Job.Progress)
+ End Sub
+ Private Sub CreateButton(ByRef BTT As Button, ByVal Img As Image)
+ BTT = New Button With {
+ .BackgroundImage = Img,
+ .BackgroundImageLayout = ImageLayout.Zoom,
+ .Text = String.Empty,
+ .Dock = DockStyle.Fill
+ }
+ End Sub
+#End Region
+ Friend Function [Get]() As TableLayoutPanel
+ Return TP_MAIN
+ End Function
+#Region "Buttons"
+ Private Sub BTT_START_Click(sender As Object, e As EventArgs) Handles BTT_START.Click
+ Start()
+ End Sub
+ Private Sub BTT_STOP_Click(sender As Object, e As EventArgs) Handles BTT_STOP.Click
+ [Stop]()
+ End Sub
+ Private Sub BTT_OPEN_Click(sender As Object, e As EventArgs) Handles BTT_OPEN.Click
+ GlobalOpenPath(Job.Host.SavedPostsPath)
+ End Sub
+#End Region
+#Region "Start, Stop"
+ Friend Sub Start()
+ Job.Start(AddressOf DownloadData)
+ End Sub
+ Friend Sub [Stop]()
+ Job.Stop()
+ End Sub
+#End Region
+#Region "SavedPosts downloading"
+ Private Sub DownloadData()
+ Dim btte As Action(Of Button, Boolean) = Sub(b, e) If b.InvokeRequired Then b.Invoke(Sub() b.Enabled = e) Else b.Enabled = e
+ Try
+ btte.Invoke(BTT_START, False)
+ btte.Invoke(BTT_STOP, True)
+ Job.Progress.InformationTemporary = $"{Job.Host.Name} downloading started"
+ Job.Start()
+ Instance.Download(Job.Token)
+ RaiseEvent OnDownloadDone($"Downloading saved {Job.Host.Name} posts is completed")
+ Catch ex As Exception
+ Job.Progress.InformationTemporary = $"{Job.Host.Name} downloading error"
+ ErrorsDescriber.Execute(EDP.LogMessageValue, ex, {$"{Job.Host.Name} saved posts downloading error", "Saved posts"})
+ Finally
+ btte.Invoke(BTT_START, True)
+ btte.Invoke(BTT_STOP, False)
+ Job.Stopped()
+ If Job.Type = Download.SavedPosts Then Job.Progress.TotalCount = 0 : Job.Progress.CurrentCounter = 0
+ End Try
+ End Sub
+#End Region
+#Region "Progress, Jobs count"
+ Private Sub JobProgress_OnTotalCountChange(ByVal Source As IMyProgress, ByVal Index As Integer)
+ RaiseEvent OnTotalCountChange()
+ End Sub
+ Private Sub JobProgress_OnProgressChange(ByVal Source As IMyProgress, ByVal Index As Integer)
+ MainProgress.Perform()
+ End Sub
+#End Region
+#Region "IDisposable Support"
+ Private disposedValue As Boolean = False
+ Protected Overridable Overloads Sub Dispose(ByVal disposing As Boolean)
+ If Not disposedValue Then
+ If disposing Then
+ If Not BTT_START Is Nothing Then BTT_START.Dispose()
+ If Not BTT_STOP Is Nothing Then BTT_STOP.Dispose()
+ If Not BTT_OPEN Is Nothing Then BTT_OPEN.Dispose()
+ PR_MAIN.Dispose()
+ LBL_INFO.Dispose()
+ TP_CONTROLS.Controls.Clear()
+ TP_CONTROLS.Dispose()
+ TP_MAIN.Controls.Clear()
+ TP_MAIN.Dispose()
+ End If
+ disposedValue = True
+ End If
+ End Sub
+ Protected Overrides Sub Finalize()
+ Dispose(False)
+ MyBase.Finalize()
+ End Sub
+ Friend Overloads Sub Dispose() Implements IDisposable.Dispose
+ Dispose(True)
+ GC.SuppressFinalize(Me)
+ End Sub
+#End Region
+ End Class
+End Namespace
\ No newline at end of file
diff --git a/SCrawler/Download/DownloadSavedPostsForm.Designer.vb b/SCrawler/Download/DownloadSavedPostsForm.Designer.vb
new file mode 100644
index 0000000..21e2149
--- /dev/null
+++ b/SCrawler/Download/DownloadSavedPostsForm.Designer.vb
@@ -0,0 +1,110 @@
+' Copyright (C) 2022 Andy
+' This program is free software: you can redistribute it and/or modify
+' it under the terms of the GNU General Public License as published by
+' the Free Software Foundation, either version 3 of the License, or
+' (at your option) any later version.
+'
+' This program is distributed in the hope that it will be useful,
+' but WITHOUT ANY WARRANTY
+
+Partial Friend Class DownloadSavedPostsForm : 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_BUTTONS As System.Windows.Forms.TableLayoutPanel
+ Dim TT_MAIN As System.Windows.Forms.ToolTip
+ Dim resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(DownloadSavedPostsForm))
+ Me.BTT_DOWN_ALL = New System.Windows.Forms.Button()
+ Me.BTT_STOP_ALL = New System.Windows.Forms.Button()
+ Me.TP_MAIN = New System.Windows.Forms.TableLayoutPanel()
+ TP_BUTTONS = New System.Windows.Forms.TableLayoutPanel()
+ TT_MAIN = New System.Windows.Forms.ToolTip(Me.components)
+ TP_BUTTONS.SuspendLayout()
+ Me.TP_MAIN.SuspendLayout()
+ Me.SuspendLayout()
+ '
+ 'TP_BUTTONS
+ '
+ TP_BUTTONS.ColumnCount = 2
+ TP_BUTTONS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50.0!))
+ TP_BUTTONS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50.0!))
+ TP_BUTTONS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20.0!))
+ TP_BUTTONS.Controls.Add(Me.BTT_DOWN_ALL, 0, 0)
+ TP_BUTTONS.Controls.Add(Me.BTT_STOP_ALL, 1, 0)
+ TP_BUTTONS.Dock = System.Windows.Forms.DockStyle.Fill
+ TP_BUTTONS.Location = New System.Drawing.Point(2, 2)
+ 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(480, 37)
+ TP_BUTTONS.TabIndex = 0
+ '
+ 'BTT_DOWN_ALL
+ '
+ Me.BTT_DOWN_ALL.Dock = System.Windows.Forms.DockStyle.Fill
+ Me.BTT_DOWN_ALL.Location = New System.Drawing.Point(3, 3)
+ Me.BTT_DOWN_ALL.Name = "BTT_DOWN_ALL"
+ Me.BTT_DOWN_ALL.Size = New System.Drawing.Size(234, 31)
+ Me.BTT_DOWN_ALL.TabIndex = 0
+ Me.BTT_DOWN_ALL.Text = "Download ALL"
+ Me.BTT_DOWN_ALL.UseVisualStyleBackColor = True
+ '
+ 'BTT_STOP_ALL
+ '
+ Me.BTT_STOP_ALL.Dock = System.Windows.Forms.DockStyle.Fill
+ Me.BTT_STOP_ALL.Location = New System.Drawing.Point(243, 3)
+ Me.BTT_STOP_ALL.Name = "BTT_STOP_ALL"
+ Me.BTT_STOP_ALL.Size = New System.Drawing.Size(234, 31)
+ Me.BTT_STOP_ALL.TabIndex = 1
+ Me.BTT_STOP_ALL.Text = "Stop ALL"
+ Me.BTT_STOP_ALL.UseVisualStyleBackColor = True
+ '
+ 'TP_MAIN
+ '
+ Me.TP_MAIN.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.Inset
+ 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(TP_BUTTONS, 0, 0)
+ 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 = 1
+ Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 30.0!))
+ Me.TP_MAIN.Size = New System.Drawing.Size(484, 41)
+ Me.TP_MAIN.TabIndex = 0
+ '
+ 'DownloadSavedPostsForm
+ '
+ Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
+ Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
+ Me.ClientSize = New System.Drawing.Size(484, 41)
+ Me.Controls.Add(Me.TP_MAIN)
+ Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle
+ Me.Icon = CType(resources.GetObject("$this.Icon"), System.Drawing.Icon)
+ Me.MaximizeBox = False
+ Me.MaximumSize = New System.Drawing.Size(500, 80)
+ Me.MinimumSize = New System.Drawing.Size(500, 80)
+ Me.Name = "DownloadSavedPostsForm"
+ Me.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide
+ Me.Text = "Saved posts"
+ TP_BUTTONS.ResumeLayout(False)
+ Me.TP_MAIN.ResumeLayout(False)
+ Me.ResumeLayout(False)
+
+ End Sub
+ Private WithEvents BTT_DOWN_ALL As Button
+ Private WithEvents BTT_STOP_ALL As Button
+ Private WithEvents TP_MAIN As TableLayoutPanel
+End Class
\ No newline at end of file
diff --git a/SCrawler/DownloadSavedPostsForm.resx b/SCrawler/Download/DownloadSavedPostsForm.resx
similarity index 91%
rename from SCrawler/DownloadSavedPostsForm.resx
rename to SCrawler/Download/DownloadSavedPostsForm.resx
index 27f32ce..f9661ce 100644
--- a/SCrawler/DownloadSavedPostsForm.resx
+++ b/SCrawler/Download/DownloadSavedPostsForm.resx
@@ -117,62 +117,16 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
- False
-
False
-
+
False
-
- False
-
-
- False
-
-
- False
+
+ 17, 17
-
-
- iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
- wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP
- WQwhyWIyJIUW5NqyPb7oCVtIlhVTwYf8nv7/t2zJagel9KmqKsIACYL9RjI8UHz5zshougZr/AEvbxEP
- aZCDBY3VslixaJvX3wzkkDiOwbZtDRGA5vdNAg+TL27qgmt5XkBG/gTdAG7Gt+3PP9oOaEGFCVEC6rp+
- 5g9MfM/c5e4OsEZMZkQEtGL5H2DdZ5JRArDwPA+iKII0TfkC9vroC9j5vq8JTWw3WzWgLMtZGIaa0MR8
- vlAD8PYlSaIJTTiOowY0p0Bc19XEJo6HE59FAPuMzyAINKGJ1XLFZxHALtMrnkBXOIQIIIQ8YvF/KrgB
- cMaRN0UdBBkAAAAASUVORK5CYII=
-
-
-
- False
-
-
- False
-
-
- 17, 17
-
-
-
- iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
- wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP
- WQwhyWIyJIUW5NqyPb7oCVtIlhVTwYf8nv7/t2zJagel9KmqKsIACYL9RjI8UHz5zshougZr/AEvbxEP
- aZCDBY3VslixaJvX3wzkkDiOwbZtDRGA5vdNAg+TL27qgmt5XkBG/gTdAG7Gt+3PP9oOaEGFCVEC6rp+
- 5g9MfM/c5e4OsEZMZkQEtGL5H2DdZ5JRArDwPA+iKII0TfkC9vroC9j5vq8JTWw3WzWgLMtZGIaa0MR8
- vlAD8PYlSaIJTTiOowY0p0Bc19XEJo6HE59FAPuMzyAINKGJ1XLFZxHALtMrnkBXOIQIIIQ8YvF/KrgB
- cMaRN0UdBBkAAAAASUVORK5CYII=
-
-
-
- False
-
-
- 17, 17
-
AAABAAUAEBAAAAEAIABoBAAAVgAAABgYAAABACAAiAkAAL4EAAAgIAAAAQAgAKgQAABGDgAAMDAAAAEA
diff --git a/SCrawler/Download/DownloadSavedPostsForm.vb b/SCrawler/Download/DownloadSavedPostsForm.vb
new file mode 100644
index 0000000..424b82b
--- /dev/null
+++ b/SCrawler/Download/DownloadSavedPostsForm.vb
@@ -0,0 +1,77 @@
+' Copyright (C) 2022 Andy
+' This program is free software: you can redistribute it and/or modify
+' it under the terms of the GNU General Public License as published by
+' the Free Software Foundation, either version 3 of the License, or
+' (at your option) any later version.
+'
+' This program is distributed in the hope that it will be useful,
+' but WITHOUT ANY WARRANTY
+Imports System.ComponentModel
+Imports PersonalUtilities.Forms
+Imports SCrawler.DownloadObjects
+Imports SCrawler.Plugin.Hosts
+Friend Class DownloadSavedPostsForm
+ Friend Event OnDownloadDone(ByVal Message As String)
+ Private MyView As FormsView
+ Private ReadOnly JobsList As List(Of DownloadProgress)
+ Friend ReadOnly Property Working As Boolean
+ Get
+ Return JobsList.Count > 0 AndAlso JobsList.Exists(Function(j) j.Job.Working)
+ End Get
+ End Property
+ Friend Sub [Stop]()
+ If JobsList.Count > 0 Then JobsList.ForEach(Sub(j) j.Stop())
+ End Sub
+ Private Sub [Start]()
+ If JobsList.Count > 0 Then JobsList.ForEach(Sub(j) j.Start())
+ End Sub
+ Friend Sub New()
+ InitializeComponent()
+ JobsList = New List(Of DownloadProgress)
+ If Settings.Plugins.Count > 0 Then
+ Dim j As TDownloader.Job
+ For Each p As PluginHost In Settings.Plugins
+ If p.Settings.IsSavedPostsCompatible Then
+ j = New TDownloader.Job(Plugin.ISiteSettings.Download.SavedPosts)
+ j.AddHost(p.Settings)
+ JobsList.Add(New DownloadProgress(j))
+ End If
+ Next
+ End If
+ End Sub
+ Private Sub DownloadSavedPostsForm_Load(sender As Object, e As EventArgs) Handles Me.Load
+ MyView = New FormsView(Me) With {.LocationOnly = True}
+ MyView.ImportFromXML(Settings.Design)
+ MyView.SetMeSize()
+ If JobsList.Count > 0 Then
+ For Each j As DownloadProgress In JobsList
+ AddHandler j.OnDownloadDone, AddressOf Jobs_OnDownloadDone
+ TP_MAIN.RowStyles.Add(New RowStyle(SizeType.Absolute, 60))
+ TP_MAIN.RowCount += 1
+ TP_MAIN.Controls.Add(j.Get, 0, TP_MAIN.RowStyles.Count - 1)
+ Next
+ Dim s As Size = Size
+ s.Height += (60 * JobsList.Count + JobsList.Count)
+ MinimumSize = s
+ Size = s
+ MaximumSize = s
+ End If
+ End Sub
+ Private Sub DownloadSavedPostsForm_Closing(sender As Object, e As CancelEventArgs) Handles Me.Closing
+ e.Cancel = True
+ Hide()
+ End Sub
+ Private Sub DownloadSavedPostsForm_Disposed(sender As Object, e As EventArgs) Handles Me.Disposed
+ [Stop]()
+ MyView.Dispose(Settings.Design)
+ End Sub
+ Private Sub Jobs_OnDownloadDone(ByVal Message As String)
+ RaiseEvent OnDownloadDone(Message)
+ End Sub
+ Private Sub BTT_DOWN_ALL_Click(sender As Object, e As EventArgs) Handles BTT_DOWN_ALL.Click
+ Start()
+ End Sub
+ Private Sub BTT_STOP_ALL_Click(sender As Object, e As EventArgs) Handles BTT_STOP_ALL.Click
+ [Stop]()
+ End Sub
+End Class
\ No newline at end of file
diff --git a/SCrawler/Download/DownloadedInfoForm.Designer.vb b/SCrawler/Download/DownloadedInfoForm.Designer.vb
new file mode 100644
index 0000000..793cf83
--- /dev/null
+++ b/SCrawler/Download/DownloadedInfoForm.Designer.vb
@@ -0,0 +1,179 @@
+Namespace DownloadObjects
+
+ Partial Friend Class DownloadedInfoForm : 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 SEP_1 As System.Windows.Forms.ToolStripSeparator
+ Dim SEP_2 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.BTT_REFRESH = New System.Windows.Forms.ToolStripButton()
+ Me.BTT_UP = New System.Windows.Forms.ToolStripButton()
+ Me.BTT_DOWN = New System.Windows.Forms.ToolStripButton()
+ Me.BTT_FIND = New System.Windows.Forms.ToolStripButton()
+ Me.BTT_CLEAR = New System.Windows.Forms.ToolStripButton()
+ Me.ToolbarBOTTOM = New System.Windows.Forms.StatusStrip()
+ Me.LIST_DOWN = New System.Windows.Forms.ListBox()
+ SEP_1 = New System.Windows.Forms.ToolStripSeparator()
+ SEP_2 = New System.Windows.Forms.ToolStripSeparator()
+ Me.ToolbarTOP.SuspendLayout()
+ Me.SuspendLayout()
+ '
+ 'SEP_1
+ '
+ 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)
+ '
+ 'ToolbarTOP
+ '
+ Me.ToolbarTOP.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden
+ Me.ToolbarTOP.Items.AddRange(New System.Windows.Forms.ToolStripItem() {Me.MENU_VIEW, Me.BTT_REFRESH, SEP_1, Me.BTT_UP, Me.BTT_DOWN, Me.BTT_FIND, SEP_2, Me.BTT_CLEAR})
+ Me.ToolbarTOP.Location = New System.Drawing.Point(0, 0)
+ Me.ToolbarTOP.Name = "ToolbarTOP"
+ Me.ToolbarTOP.Size = New System.Drawing.Size(554, 25)
+ Me.ToolbarTOP.TabIndex = 0
+ '
+ '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.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.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(113, 22)
+ Me.MENU_VIEW_SESSION.Text = "Session"
+ Me.MENU_VIEW_SESSION.ToolTipText = "Show downloaded users by this session"
+ '
+ 'MENU_VIEW_ALL
+ '
+ Me.MENU_VIEW_ALL.AutoToolTip = True
+ Me.MENU_VIEW_ALL.Name = "MENU_VIEW_ALL"
+ Me.MENU_VIEW_ALL.Size = New System.Drawing.Size(113, 22)
+ Me.MENU_VIEW_ALL.Text = "All"
+ Me.MENU_VIEW_ALL.ToolTipText = "Show all users (sorted by latest download)"
+ '
+ 'BTT_REFRESH
+ '
+ Me.BTT_REFRESH.Image = Global.SCrawler.My.Resources.Resources.Refresh
+ Me.BTT_REFRESH.ImageTransparentColor = System.Drawing.Color.Magenta
+ Me.BTT_REFRESH.Name = "BTT_REFRESH"
+ Me.BTT_REFRESH.Size = New System.Drawing.Size(89, 22)
+ Me.BTT_REFRESH.Text = "Refresh (F5)"
+ Me.BTT_REFRESH.ToolTipText = "Force list refresh"
+ '
+ 'BTT_UP
+ '
+ Me.BTT_UP.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image
+ Me.BTT_UP.Image = CType(resources.GetObject("BTT_UP.Image"), System.Drawing.Image)
+ Me.BTT_UP.ImageTransparentColor = System.Drawing.Color.Magenta
+ Me.BTT_UP.Name = "BTT_UP"
+ Me.BTT_UP.Size = New System.Drawing.Size(23, 22)
+ Me.BTT_UP.Text = "Up"
+ Me.BTT_UP.ToolTipText = "Up (F3)"
+ '
+ 'BTT_DOWN
+ '
+ Me.BTT_DOWN.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image
+ Me.BTT_DOWN.Image = CType(resources.GetObject("BTT_DOWN.Image"), System.Drawing.Image)
+ Me.BTT_DOWN.ImageTransparentColor = System.Drawing.Color.Magenta
+ Me.BTT_DOWN.Name = "BTT_DOWN"
+ Me.BTT_DOWN.Size = New System.Drawing.Size(23, 22)
+ Me.BTT_DOWN.Text = "Down"
+ Me.BTT_DOWN.ToolTipText = "Down (F2)"
+ '
+ 'BTT_FIND
+ '
+ Me.BTT_FIND.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text
+ Me.BTT_FIND.Image = CType(resources.GetObject("BTT_FIND.Image"), System.Drawing.Image)
+ Me.BTT_FIND.ImageTransparentColor = System.Drawing.Color.Magenta
+ Me.BTT_FIND.Name = "BTT_FIND"
+ Me.BTT_FIND.Size = New System.Drawing.Size(57, 22)
+ Me.BTT_FIND.Text = "Find (F4)"
+ Me.BTT_FIND.ToolTipText = "Find in the main window"
+ '
+ 'BTT_CLEAR
+ '
+ Me.BTT_CLEAR.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text
+ Me.BTT_CLEAR.Image = CType(resources.GetObject("BTT_CLEAR.Image"), System.Drawing.Image)
+ Me.BTT_CLEAR.ImageTransparentColor = System.Drawing.Color.Magenta
+ Me.BTT_CLEAR.Name = "BTT_CLEAR"
+ Me.BTT_CLEAR.Size = New System.Drawing.Size(38, 22)
+ Me.BTT_CLEAR.Text = "Clear"
+ Me.BTT_CLEAR.ToolTipText = "Clear info list"
+ '
+ 'ToolbarBOTTOM
+ '
+ Me.ToolbarBOTTOM.Location = New System.Drawing.Point(0, 389)
+ Me.ToolbarBOTTOM.Name = "ToolbarBOTTOM"
+ Me.ToolbarBOTTOM.Size = New System.Drawing.Size(554, 22)
+ Me.ToolbarBOTTOM.TabIndex = 1
+ '
+ 'LIST_DOWN
+ '
+ Me.LIST_DOWN.Dock = System.Windows.Forms.DockStyle.Fill
+ Me.LIST_DOWN.FormattingEnabled = True
+ Me.LIST_DOWN.Location = New System.Drawing.Point(0, 25)
+ Me.LIST_DOWN.Name = "LIST_DOWN"
+ Me.LIST_DOWN.Size = New System.Drawing.Size(554, 364)
+ Me.LIST_DOWN.TabIndex = 2
+ '
+ 'DownloadedInfoForm
+ '
+ Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
+ Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
+ Me.ClientSize = New System.Drawing.Size(554, 411)
+ Me.Controls.Add(Me.LIST_DOWN)
+ Me.Controls.Add(Me.ToolbarBOTTOM)
+ Me.Controls.Add(Me.ToolbarTOP)
+ Me.KeyPreview = True
+ Me.MinimumSize = New System.Drawing.Size(570, 450)
+ Me.Name = "DownloadedInfoForm"
+ Me.ShowIcon = False
+ Me.Text = "Downloaded items"
+ Me.ToolbarTOP.ResumeLayout(False)
+ Me.ToolbarTOP.PerformLayout()
+ Me.ResumeLayout(False)
+ Me.PerformLayout()
+
+ End Sub
+
+ Private WithEvents ToolbarTOP As ToolStrip
+ Private WithEvents MENU_VIEW As ToolStripDropDownButton
+ Private WithEvents MENU_VIEW_SESSION As ToolStripMenuItem
+ Private WithEvents MENU_VIEW_ALL As ToolStripMenuItem
+ Private WithEvents BTT_REFRESH As ToolStripButton
+ Private WithEvents ToolbarBOTTOM As StatusStrip
+ Private WithEvents LIST_DOWN As ListBox
+ Private WithEvents BTT_CLEAR As ToolStripButton
+ Private WithEvents BTT_FIND As ToolStripButton
+ Private WithEvents BTT_UP As ToolStripButton
+ Private WithEvents BTT_DOWN As ToolStripButton
+ End Class
+End Namespace
\ No newline at end of file
diff --git a/SCrawler/DownloadedInfoForm.resx b/SCrawler/Download/DownloadedInfoForm.resx
similarity index 74%
rename from SCrawler/DownloadedInfoForm.resx
rename to SCrawler/Download/DownloadedInfoForm.resx
index 09e5614..9e3ed28 100644
--- a/SCrawler/DownloadedInfoForm.resx
+++ b/SCrawler/Download/DownloadedInfoForm.resx
@@ -117,6 +117,12 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+ False
+
+
+ False
+
17, 17
@@ -136,9 +142,44 @@
TgDQASA1MVpwzwAAAABJRU5ErkJggg==
-
- False
-
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+ YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEuSURBVDhPpZM9S8NQFIajg1b9BW6CoOLiV6Uu/gcF/RH+
+ gYa6dBQXQQyNttXNL3DUSRehxUEouLSjm2MzSCJ+lMdzaGggTWiiF1443POeh/fAvQZvDbjYgKstuN5O
+ JvVebvLz+oihBfYSHC+nUzmLkx8SgNL04mQlncqrOIURAWikvwJ2MwMAR7PSk/WiegMB1hzc7kgtPXux
+ vx8L0PpwGh4K8PkOjdNukjAkEqBxdfjehO8PeufZFsiMQBbEl40B6LAaavv+VOg0b6C6FiTpA+jw2Tqd
+ l3Pcuo1XswLVS7hPFbySAKz5borIFeRxdA6mcIvjeCG5xQm8vUnfGwfwIVRz8er54gBJFQD+85RHBaCf
+ yd8plSo5HHMYQ79kO58RmkgiJZJ42+YYX607fgE+Y9hMRvWbWAAAAABJRU5ErkJggg==
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+ YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAFASURBVDhPrZO7SgNBFEDXBxp/wVZNERRf0TT+gwr6B9aW
+ WSIolja+VpbERFsfiJAiCiIigrWdRmzsrDSLElA2Mcd7JSFCNrpRFw47M3vvmTuzMwYPV7AzAXtTsD/t
+ D43dnaR4f4GhDeKDkBhqjGQYJ9okArXpwOZwYyRHcGJtItCSfiuYC/y7QN5WD6x31WJ1S9yAxITrCOL9
+ sD0G1wdwm4abwypZ6d9lZOfHwe6tI0jIn1DJ5RJeT/58heeFTkp2nyRLFZ5LUImWe2pC4bWcKsnH8zzO
+ tuAuyzIk8Zs9UIm0VXIWg7cX8ieLkmzgrgVhK1KNqyuoYId4T8/wZLZSWJWZvyYrPwqEkhWkuBGC1Gjt
+ dz+CTypr9hgvC/5ylNtFoJdJD4fO1AipCI7ZjKFXMhcNiE2QknwhsTmzAzd7xAdy1dicvhZiDAAAAABJ
+ RU5ErkJggg==
+
+
+
+
+ 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==
+
+
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
diff --git a/SCrawler/Download/DownloadedInfoForm.vb b/SCrawler/Download/DownloadedInfoForm.vb
new file mode 100644
index 0000000..406780d
--- /dev/null
+++ b/SCrawler/Download/DownloadedInfoForm.vb
@@ -0,0 +1,191 @@
+' Copyright (C) 2022 Andy
+' This program is free software: you can redistribute it and/or modify
+' it under the terms of the GNU General Public License as published by
+' the Free Software Foundation, either version 3 of the License, or
+' (at your option) any later version.
+'
+' This program is distributed in the hope that it will be useful,
+' but WITHOUT ANY WARRANTY
+Imports System.ComponentModel
+Imports PersonalUtilities.Forms
+Imports SCrawler.API.Base
+Namespace DownloadObjects
+ Friend Class DownloadedInfoForm
+ Friend Event OnUserLooking(ByVal Key As String)
+ Private MyView As FormsView
+ Private ReadOnly LParams As New ListAddParams(LAP.IgnoreICopier) With {.e = EDP.None}
+ Friend Enum ViewModes As Integer
+ Session = 0
+ All = 1
+ End Enum
+ Friend Property ViewMode As ViewModes
+ Get
+ Return IIf(MENU_VIEW_ALL.Checked, ViewModes.All, ViewModes.Session)
+ End Get
+ Set(ByVal SMode As ViewModes)
+ Settings.InfoViewMode.Value = CInt(SMode)
+ End Set
+ End Property
+ Private ReadOnly _TempUsersList As List(Of IUserData)
+ Friend Sub New()
+ InitializeComponent()
+ _TempUsersList = New List(Of IUserData)
+ If Settings.InfoViewMode.Value = CInt(ViewModes.All) Then
+ MENU_VIEW_SESSION.Checked = False
+ MENU_VIEW_ALL.Checked = True
+ Else
+ MENU_VIEW_SESSION.Checked = True
+ MENU_VIEW_ALL.Checked = False
+ End If
+ Settings.InfoViewMode.Value = ViewMode
+ RefillList()
+ End Sub
+ Private Sub DownloadedInfoForm_Load(sender As Object, e As EventArgs) Handles Me.Load
+ Try
+ If MyView Is Nothing Then
+ MyView = New FormsView(Me)
+ MyView.ImportFromXML(Settings.Design)
+ MyView.SetMeSize()
+ End If
+ BTT_CLEAR.Visible = ViewMode = ViewModes.Session
+ RefillList()
+ Catch ex As Exception
+ End Try
+ End Sub
+ Private Sub DownloadedInfoForm_Closing(sender As Object, e As CancelEventArgs) Handles Me.Closing
+ e.Cancel = True
+ 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()
+ End Sub
+ Private Sub DownloadedInfoForm_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown
+ Dim b As Boolean = True
+ Select Case e.KeyCode
+ Case Keys.F5 : RefillList()
+ Case Keys.F2 : UpdateNavigationButtons(1)
+ Case Keys.F3 : UpdateNavigationButtons(-1)
+ Case Keys.F4 : BTT_FIND.PerformClick()
+ Case Keys.Enter : LIST_DOWN_MouseDoubleClick(Nothing, Nothing)
+ Case Else : b = False
+ End Select
+ If b Then e.Handled = True
+ End Sub
+ Private Class UsersDateOrder : Implements IComparer(Of IUserData)
+ Friend Function Compare(ByVal x As IUserData, ByVal y As IUserData) As Integer Implements IComparer(Of IUserData).Compare
+ Dim xv& = If(DirectCast(x, UserDataBase).LastUpdated.HasValue, DirectCast(x, UserDataBase).LastUpdated.Value.Ticks, 0)
+ Dim yv& = If(DirectCast(y, UserDataBase).LastUpdated.HasValue, DirectCast(y, UserDataBase).LastUpdated.Value.Ticks, 0)
+ Return xv.CompareTo(yv) * -1
+ End Function
+ End Class
+ Private Sub RefillList()
+ 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)
+ Else
+ _TempUsersList.ListAddList(Settings.Users.SelectMany(Of IUserData) _
+ (Function(u) If(u.IsCollection, DirectCast(u, API.UserDataBind).Collections, {u})), LParams)
+ End If
+ If _TempUsersList.Count > 0 Then
+ _TempUsersList.Sort(New UsersDateOrder)
+ For Each user As IUserData In _TempUsersList
+ 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
+ Dim aSel As Action = Sub() LIST_DOWN.SelectedIndex = _LatestSelected
+ If LIST_DOWN.InvokeRequired Then LIST_DOWN.Invoke(aSel) Else aSel.Invoke
+ Else
+ _LatestSelected = -1
+ End If
+ Else
+ _LatestSelected = -1
+ End If
+ Catch ex As Exception
+ ErrorsDescriber.Execute(EDP.SendInLog, ex, "[DownloadedInfoForm.RefillList]")
+ Finally
+ UpdateNavigationButtons(Nothing)
+ End Try
+ End Sub
+ 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()
+ 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()
+ End Sub
+ Private Sub BTT_REFRESH_Click(sender As Object, e As EventArgs) Handles BTT_REFRESH.Click
+ 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 i% = Settings.Users.IndexOf(_TempUsersList(_LatestSelected))
+ If i >= 0 Then RaiseEvent OnUserLooking(Settings.Users(i).Key)
+ End If
+ Catch ex As Exception
+ 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
+ End Sub
+ Private _LatestSelected As Integer = -1
+ Private Sub LIST_DOWN_SelectedIndexChanged(sender As Object, e As EventArgs) Handles LIST_DOWN.SelectedIndexChanged
+ _LatestSelected = LIST_DOWN.SelectedIndex
+ UpdateNavigationButtons(Nothing)
+ End Sub
+ Private Sub LIST_DOWN_MouseDoubleClick(sender As Object, e As MouseEventArgs) Handles LIST_DOWN.MouseDoubleClick
+ Try
+ If _LatestSelected >= 0 AndAlso _LatestSelected <= _TempUsersList.Count - 1 AndAlso
+ Not DirectCast(_TempUsersList(_LatestSelected), UserDataBase).Disposed Then _TempUsersList(_LatestSelected).OpenFolder()
+ Catch ex As Exception
+ End Try
+ End Sub
+ Friend Sub Downloader_OnDownloadCountChange()
+ If ViewMode = ViewModes.Session Then RefillList()
+ End Sub
+ Private Sub BTT_UP_Click(sender As Object, e As EventArgs) Handles BTT_UP.Click
+ UpdateNavigationButtons(-1)
+ End Sub
+ Private Sub BTT_DOWN_Click(sender As Object, e As EventArgs) Handles BTT_DOWN.Click
+ UpdateNavigationButtons(1)
+ End Sub
+ Private Sub UpdateNavigationButtons(ByVal Offset As Integer?)
+ Dim u As Boolean = False, d As Boolean = False
+ With LIST_DOWN
+ If .Items.Count > 0 Then
+ 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
+ End With
+ End Sub
+ End Class
+End Namespace
\ No newline at end of file
diff --git a/SCrawler/Download/TDownloader.vb b/SCrawler/Download/TDownloader.vb
new file mode 100644
index 0000000..581096e
--- /dev/null
+++ b/SCrawler/Download/TDownloader.vb
@@ -0,0 +1,434 @@
+' Copyright (C) 2022 Andy
+' This program is free software: you can redistribute it and/or modify
+' it under the terms of the GNU General Public License as published by
+' the Free Software Foundation, either version 3 of the License, or
+' (at your option) any later version.
+'
+' This program is distributed in the hope that it will be useful,
+' but WITHOUT ANY WARRANTY
+Imports System.Threading
+Imports PersonalUtilities.Forms.Toolbars
+Imports EOptions = PersonalUtilities.Forms.Toolbars.IMyProgress.EnableOptions
+Imports SCrawler.API
+Imports SCrawler.API.Base
+Imports SCrawler.Plugin.Hosts
+Imports Download = SCrawler.Plugin.ISiteSettings.Download
+Namespace DownloadObjects
+ Friend Class TDownloader : Implements IDisposable
+ Friend Event OnJobsChange(ByVal JobsCount As Integer)
+ Friend Event OnDownloadCountChange()
+ Friend Event OnDownloading(ByVal Value As Boolean)
+ Friend Event SendNotification(ByVal Message As String)
+ Friend Event OnReconfigured()
+ Friend ReadOnly Property Downloaded As List(Of IUserData)
+ Private ReadOnly NProv As IFormatProvider
+ Friend ReadOnly Property Working As Boolean
+ Get
+ Return Pool.Count > 0 AndAlso Pool.Exists(Function(j) j.Working)
+ End Get
+ End Property
+#Region "Jobs"
+ Friend Class Job : Implements IDisposable
+ Friend Event OnItemsCountChange(ByVal Sender As Job, ByVal Count As Integer)
+ Private ReadOnly Hosts As List(Of SettingsHost)
+ Private ReadOnly Keys As List(Of String)
+ Private ReadOnly RemovingKeys As List(Of String)
+ Private TokenSource As CancellationTokenSource
+ Friend Token As CancellationToken
+ Private [Thread] As Thread
+ Private _Working As Boolean
+ Friend ReadOnly Property Items As List(Of IUserData)
+ Friend ReadOnly Property [Type] As Download
+ Friend ReadOnly Property Count As Integer
+ Get
+ Return Items.Count
+ End Get
+ End Property
+ Friend ReadOnly Property Working As Boolean
+ Get
+ Return _Working OrElse If(Thread?.IsAlive, False)
+ End Get
+ End Property
+ Friend ReadOnly Property IsSeparated As Boolean
+ Get
+ Return Hosts.Count = 1 AndAlso Hosts(0).IsSeparatedTasks
+ End Get
+ End Property
+ Friend ReadOnly Property Name As String
+ Get
+ Return Hosts(0).Name
+ End Get
+ End Property
+ Friend ReadOnly Property TaskCount As Integer
+ Get
+ Return Hosts(0).TaskCount
+ End Get
+ End Property
+ Friend ReadOnly Property Host As SettingsHost
+ Get
+ If Hosts.Count > 0 Then
+ Dim k$ = Hosts(0).Key
+ Dim i% = Settings.Plugins.FindIndex(Function(p) p.Key = k)
+ If i >= 0 Then Return Settings.Plugins(i).Settings
+ End If
+ Return Nothing
+ End Get
+ End Property
+ Friend Property Progress As MyProgress
+ Friend Sub New(ByVal JobType As Download)
+ Hosts = New List(Of SettingsHost)
+ RemovingKeys = New List(Of String)
+ Keys = New List(Of String)
+ Items = New List(Of IUserData)
+ [Type] = JobType
+ End Sub
+ Friend Function Add(ByVal User As IUserData) As Boolean
+ With DirectCast(User, UserDataBase)
+ If Keys.Count > 0 Then
+ Dim i% = Keys.IndexOf(.User.Plugin)
+ If i >= 0 Then
+ Items.Add(User)
+ RaiseEvent OnItemsCountChange(Me, Count)
+ Return True
+ Else
+ If RemovingKeys.Count > 0 Then Return RemovingKeys.IndexOf(.User.Plugin) >= 0
+ End If
+ End If
+ End With
+ Return False
+ End Function
+ Friend Sub AddHost(ByRef h As SettingsHost)
+ Hosts.Add(h)
+ Keys.Add(h.Key)
+ End Sub
+ Friend Function UserHost(ByVal User As IUserData) As SettingsHost
+ Dim i% = Keys.IndexOf(DirectCast(User, UserDataBase).User.Plugin)
+ If i >= 0 Then Return Hosts(i) Else Throw New KeyNotFoundException($"Plugin key [{DirectCast(User, UserDataBase).User.Plugin}] not found")
+ End Function
+ Friend Function Available() As Boolean
+ If Hosts.Count > 0 Then
+ Dim k$
+ For i% = Hosts.Count - 1 To 0 Step -1
+ If Not Hosts(i).Available(Type) Then
+ k = Hosts(i).Key
+ If Not RemovingKeys.Contains(k) Then RemovingKeys.Add(k)
+ Hosts(i).DownloadDone(Type)
+ Hosts.RemoveAt(i)
+ Keys.RemoveAt(i)
+ If Items.Count > 0 Then Items.RemoveAll(Function(u) DirectCast(u, UserDataBase).HOST.Key = k)
+ End If
+ Next
+ Return Hosts.Count > 0
+ Else
+ Return False
+ End If
+ End Function
+ Friend Sub ThrowIfCancellationRequested()
+ Token.ThrowIfCancellationRequested()
+ End Sub
+ Friend ReadOnly Property IsCancellationRequested As Boolean
+ Get
+ Return Token.IsCancellationRequested
+ End Get
+ End Property
+ Friend Sub [Start](ByVal [ThreadStart] As ThreadStart)
+ Thread = New Thread(ThreadStart) With {.IsBackground = True}
+ Thread.SetApartmentState(ApartmentState.MTA)
+ Thread.Start()
+ End Sub
+ Friend Sub [Start]()
+ If Hosts.Count > 0 Then Hosts.ForEach(Sub(h) h.DownloadStarted([Type]))
+ TokenSource = New CancellationTokenSource
+ Token = TokenSource.Token
+ _Working = True
+ End Sub
+ Friend Sub [Stop]()
+ If Not TokenSource Is Nothing Then TokenSource.Cancel()
+ End Sub
+ Friend Sub Stopped()
+ _Working = False
+ TokenSource = Nothing
+ Try
+ If Not Thread Is Nothing Then
+ If Thread.IsAlive Then Thread.Abort()
+ Thread = Nothing
+ End If
+ Catch ex As Exception
+ End Try
+ If Hosts.Count > 0 Then Hosts.ForEach(Sub(h) h.DownloadDone([Type]))
+ End Sub
+#Region "IDisposable Support"
+ Private disposedValue As Boolean = False
+ Protected Overridable Overloads Sub Dispose(ByVal disposing As Boolean)
+ If Not disposedValue Then
+ If disposing Then
+ Hosts.Clear()
+ Keys.Clear()
+ RemovingKeys.Clear()
+ Items.Clear()
+ End If
+ disposedValue = True
+ End If
+ End Sub
+ Protected Overrides Sub Finalize()
+ Dispose(False)
+ MyBase.Finalize()
+ End Sub
+ Friend Overloads Sub Dispose() Implements IDisposable.Dispose
+ Dispose(True)
+ GC.SuppressFinalize(Me)
+ End Sub
+#End Region
+ End Class
+ Friend ReadOnly Pool As List(Of Job)
+#End Region
+ Friend Sub New()
+ Downloaded = New List(Of IUserData)
+ NProv = New ANumbers With {.FormatOptions = ANumbers.Options.GroupIntegral}
+ Pool = New List(Of Job)
+ End Sub
+ Friend Sub ReconfPool()
+ If Pool.Count = 0 OrElse Not Pool.Exists(Function(j) j.Working Or j.Count > 0) Then
+ Pool.ListClearDispose
+ If Settings.Plugins.Count > 0 Then
+ Pool.Add(New Job(Download.Main))
+ For Each p As PluginHost In Settings.Plugins
+ If p.Settings.IsSeparatedTasks Then
+ Pool.Add(New Job(Download.Main))
+ Pool.Last.AddHost(p.Settings)
+ Else
+ Pool(0).AddHost(p.Settings)
+ End If
+ Next
+ End If
+ RaiseEvent OnReconfigured()
+ End If
+ End Sub
+ Private CheckerThread As Thread
+ Private Sub [Start]()
+ If Not MyProgressForm.Opened AndAlso Pool.LongCount(Function(p) p.Count > 0) > 1 Then MyProgressForm.Show()
+ If Not If(CheckerThread?.IsAlive, False) Then
+ MainProgress.Enabled = True
+ CheckerThread = New Thread(New ThreadStart(AddressOf JobsChecker))
+ CheckerThread.SetApartmentState(ApartmentState.MTA)
+ CheckerThread.Start()
+ End If
+ End Sub
+ Private Sub JobsChecker()
+ Try
+ MainProgress.TotalCount = 0
+ Do While Pool.Exists(Function(p) p.Count > 0)
+ For Each j As Job In Pool
+ If j.Count > 0 And Not j.Working Then j.Start(New ThreadStart(Sub() StartDownloading(j)))
+ Next
+ Thread.Sleep(200)
+ Loop
+ Catch ex As Exception
+ Finally
+ With MainProgress
+ .TotalCount = 0
+ .CurrentCounter = 0
+ .InformationTemporary = "All data downloaded"
+ .Enabled(EOptions.ProgressBar) = False
+ End With
+ If Pool.Count > 0 Then Pool.ForEach(Sub(p) If Not p.Progress Is Nothing Then p.Progress.TotalCount = 0)
+ End Try
+ End Sub
+ Private Sub StartDownloading(ByRef _Job As Job)
+ RaiseEvent OnDownloading(True)
+ Dim isSeparated As Boolean = _Job.IsSeparated
+ Dim n$ = _Job.Name
+ Dim pt As Func(Of String, String) = Function(ByVal t As String) As String
+ Dim _t$ = If(isSeparated, $"{n} {Left(t, 1).ToLower}{Right(t, t.Length - 1)}", t)
+ RaiseEvent SendNotification(_t)
+ Return _t
+ End Function
+ Try
+ _Job.Start()
+ _Job.Progress.TotalCount = 0
+ _Job.Progress.CurrentCounter = 0
+ _Job.Progress.Enabled = True
+ Dim SiteChecked As Boolean = False
+ Do While _Job.Count > 0
+ _Job.ThrowIfCancellationRequested()
+ If Not SiteChecked Then
+ If Not _Job.Available Then Exit Sub Else SiteChecked = True : Continue Do
+ End If
+ UpdateJobsLabel()
+ DownloadData(_Job, _Job.Token)
+ _Job.ThrowIfCancellationRequested()
+ Thread.Sleep(500)
+ Loop
+ _Job.Progress.InformationTemporary = pt("All data downloaded")
+ Catch oex As OperationCanceledException When _Job.IsCancellationRequested
+ _Job.Progress.InformationTemporary = pt("Downloading canceled")
+ Catch ex As Exception
+ _Job.Progress.InformationTemporary = pt("Downloading error")
+ ErrorsDescriber.Execute(EDP.SendInLog, ex, "TDownloader.Start")
+ Finally
+ _Job.Stopped()
+ UpdateJobsLabel()
+ RaiseEvent OnDownloading(False)
+ End Try
+ End Sub
+ Friend Sub [Stop]()
+ If Pool.Count > 0 Then
+ For Each j As Job In Pool
+ If j.Working Then j.Stop()
+ Next
+ End If
+ End Sub
+ Private Sub UpdateJobsLabel()
+ RaiseEvent OnJobsChange(Pool.Sum(Function(j) j.Count))
+ End Sub
+ Private Sub DownloadData(ByRef _Job As Job, ByVal Token As CancellationToken)
+ Try
+ If _Job.Count > 0 Then
+ Const nf As ANumbers.Formats = ANumbers.Formats.Number
+ Dim t As New List(Of Task)
+ Dim i% = 0
+ Dim limit% = _Job.TaskCount
+ Dim Keys As New List(Of String)
+ Dim h As Boolean = False
+ Dim host As SettingsHost = Nothing
+ For Each _Item As IUserData In _Job.Items
+ If Not _Item.Disposed Then
+ Keys.Add(_Item.Key)
+ host = _Job.UserHost(_Item)
+ If host.Source.ReadyToDownload(Download.Main) Then
+ host.BeforeStartDownload(_Item, Download.Main)
+ _Job.ThrowIfCancellationRequested()
+ DirectCast(_Item, UserDataBase).Progress = _Job.Progress
+ t.Add(Task.Run(Sub() _Item.DownloadData(Token)))
+ i += 1
+ If i >= limit Then Exit For
+ End If
+ End If
+ Next
+ If t.Count > 0 Or Keys.Count > 0 Then
+ With _Job.Progress
+ .Enabled(EOptions.All) = True
+ .Information = IIf(_Job.IsSeparated, $"{_Job.Name} d", "D")
+ .Information &= $"ownloading {t.Count.NumToString(nf, NProv)}/{_Job.Items.Count.NumToString(nf, NProv)} profiles' data"
+ .InformationTemporary = .Information
+ End With
+ If t.Count > 0 Then Task.WaitAll(t.ToArray)
+ Dim dcc As Boolean = False
+ If Keys.Count > 0 Then
+ For Each k$ In Keys
+ i = _Job.Items.FindIndex(Function(ii) ii.Key = k)
+ If i >= 0 Then
+ With _Job.Items(i)
+ host.AfterDownload(_Job.Items(i), Download.Main)
+ If Not .Disposed AndAlso Not .IsCollection AndAlso .DownloadedTotal(False) > 0 Then
+ If Not Downloaded.Contains(.Self) Then Downloaded.Add(GetUserFromMainCollection(.Self))
+ dcc = True
+ End If
+ End With
+ _Job.Items.RemoveAt(i)
+ End If
+ Next
+ End If
+ Keys.Clear()
+ _Job.Items.RemoveAll(Function(ii) ii.Disposed)
+ If dcc Then Downloaded.RemoveAll(Function(u) u Is Nothing)
+ If dcc And Downloaded.Count > 0 Then RaiseEvent OnDownloadCountChange()
+ t.Clear()
+ End If
+ End If
+ Catch aoex As ArgumentOutOfRangeException
+ ErrorsDescriber.Execute(EDP.SendInLog, aoex, $"TDownloader.DownloadData: index out of range ({_Job.Count})")
+ Catch oex As OperationCanceledException When _Job.IsCancellationRequested
+ Catch ex As Exception
+ ErrorsDescriber.Execute(EDP.SendInLog, ex, "TDownloader.DownloadData")
+ Finally
+ If Settings.UserListUpdateRequired Then _
+ Task.WaitAll(Task.Run(Sub()
+ While Settings.UserListUpdateRequired : Settings.UpdateUsersList() : End While
+ End Sub))
+ End Try
+ End Sub
+ Private Function GetUserFromMainCollection(ByVal User As IUserData) As IUserData
+ Dim uSimple As Predicate(Of IUserData) = Function(u) u.Equals(DirectCast(User, UserDataBase))
+ Dim uCol As Predicate(Of IUserData) = Function(ByVal u As IUserData) As Boolean
+ If u.IsCollection Then
+ Return DirectCast(u, UserDataBind).Collections.Exists(uSimple)
+ Else
+ Return False
+ End If
+ End Function
+ Dim uu As Predicate(Of IUserData)
+ If User.IncludedInCollection Then uu = uCol Else uu = uSimple
+ Dim i% = Settings.Users.FindIndex(uu)
+ If i >= 0 Then
+ If Settings.Users(i).IsCollection Then
+ With DirectCast(Settings.Users(i), UserDataBind)
+ i = .Collections.FindIndex(uSimple)
+ If i >= 0 Then Return .Collections(i)
+ End With
+ Else
+ Return Settings.Users(i)
+ End If
+ End If
+ Return Nothing
+ End Function
+ Private Sub AddItem(ByVal Item As IUserData, ByVal _UpdateJobsLabel As Boolean)
+ ReconfPool()
+ If Item.IsCollection Then
+ Item.DownloadData(Nothing)
+ Else
+ If Not Contains(Item) Then
+ If Pool.Count > 0 Then
+ For i% = 0 To Pool.Count - 1
+ If Pool(i).Add(Item) Then Exit For
+ Next
+ End If
+ If _UpdateJobsLabel Then UpdateJobsLabel()
+ End If
+ End If
+ End Sub
+ Friend Sub Add(ByVal Item As IUserData)
+ AddItem(Item, True)
+ Start()
+ End Sub
+ Friend Sub AddRange(ByVal _Items As IEnumerable(Of IUserData))
+ If _Items.ListExists Then
+ For i% = 0 To _Items.Count - 1 : AddItem(_Items(i), False) : Next
+ UpdateJobsLabel()
+ End If
+ Start()
+ End Sub
+ Private Function Contains(ByVal _Item As IUserData)
+ If Pool.Count > 0 Then
+ For Each j As Job In Pool
+ If j.Items.Count > 0 AndAlso j.Items.Contains(_Item) Then Return True
+ Next
+ End If
+ Return False
+ End Function
+ Friend Sub UserRemove(ByVal _Item As IUserData)
+ If Downloaded.Count > 0 AndAlso Downloaded.Contains(_Item) Then Downloaded.Remove(_Item) : RaiseEvent OnDownloadCountChange()
+ End Sub
+#Region "IDisposable Support"
+ Private disposedValue As Boolean = False
+ Protected Overridable Overloads Sub Dispose(ByVal disposing As Boolean)
+ If Not disposedValue Then
+ If disposing Then
+ [Stop]()
+ Pool.ListClearDispose
+ Downloaded.Clear()
+ End If
+ disposedValue = True
+ End If
+ End Sub
+ Protected Overrides Sub Finalize()
+ Dispose(False)
+ MyBase.Finalize()
+ End Sub
+ Friend Overloads Sub Dispose() Implements IDisposable.Dispose
+ Dispose(True)
+ GC.SuppressFinalize(Me)
+ End Sub
+#End Region
+ End Class
+End Namespace
\ No newline at end of file
diff --git a/SCrawler/Download/VideosDownloaderForm.Designer.vb b/SCrawler/Download/VideosDownloaderForm.Designer.vb
new file mode 100644
index 0000000..d64170f
--- /dev/null
+++ b/SCrawler/Download/VideosDownloaderForm.Designer.vb
@@ -0,0 +1,160 @@
+Namespace DownloadObjects
+
+ Partial Friend Class VideosDownloaderForm : 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 SEP_1 As System.Windows.Forms.ToolStripSeparator
+ Dim SEP_2 As System.Windows.Forms.ToolStripSeparator
+ Me.ToolbarTOP = New System.Windows.Forms.ToolStrip()
+ Me.BTT_ADD = New System.Windows.Forms.ToolStripButton()
+ Me.BTT_ADD_LIST = New System.Windows.Forms.ToolStripButton()
+ Me.BTT_DELETE = New System.Windows.Forms.ToolStripButton()
+ Me.BTT_DOWN = New System.Windows.Forms.ToolStripButton()
+ Me.BTT_OPEN_PATH = New System.Windows.Forms.ToolStripButton()
+ Me.ToolbarBOTTOM = New System.Windows.Forms.StatusStrip()
+ Me.PR_V = New System.Windows.Forms.ToolStripProgressBar()
+ Me.LBL_STATUS = New System.Windows.Forms.ToolStripStatusLabel()
+ Me.LIST_VIDEOS = New System.Windows.Forms.ListBox()
+ SEP_1 = New System.Windows.Forms.ToolStripSeparator()
+ SEP_2 = New System.Windows.Forms.ToolStripSeparator()
+ Me.ToolbarTOP.SuspendLayout()
+ Me.ToolbarBOTTOM.SuspendLayout()
+ Me.SuspendLayout()
+ '
+ 'SEP_1
+ '
+ 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)
+ '
+ 'ToolbarTOP
+ '
+ Me.ToolbarTOP.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden
+ Me.ToolbarTOP.Items.AddRange(New System.Windows.Forms.ToolStripItem() {Me.BTT_ADD, Me.BTT_ADD_LIST, Me.BTT_DELETE, SEP_1, Me.BTT_DOWN, SEP_2, Me.BTT_OPEN_PATH})
+ Me.ToolbarTOP.Location = New System.Drawing.Point(0, 0)
+ Me.ToolbarTOP.Name = "ToolbarTOP"
+ Me.ToolbarTOP.Size = New System.Drawing.Size(524, 25)
+ Me.ToolbarTOP.TabIndex = 0
+ '
+ 'BTT_ADD
+ '
+ Me.BTT_ADD.AutoToolTip = False
+ Me.BTT_ADD.Image = Global.SCrawler.My.Resources.Resources.PlusPIC
+ Me.BTT_ADD.ImageTransparentColor = System.Drawing.Color.Magenta
+ Me.BTT_ADD.Name = "BTT_ADD"
+ Me.BTT_ADD.Size = New System.Drawing.Size(75, 22)
+ Me.BTT_ADD.Text = "Add (Ins)"
+ '
+ 'BTT_ADD_LIST
+ '
+ Me.BTT_ADD_LIST.AutoToolTip = False
+ Me.BTT_ADD_LIST.Image = Global.SCrawler.My.Resources.Resources.PlusPIC
+ Me.BTT_ADD_LIST.ImageTransparentColor = System.Drawing.Color.Magenta
+ Me.BTT_ADD_LIST.Name = "BTT_ADD_LIST"
+ Me.BTT_ADD_LIST.Size = New System.Drawing.Size(67, 22)
+ Me.BTT_ADD_LIST.Text = "Add list"
+ '
+ 'BTT_DELETE
+ '
+ Me.BTT_DELETE.AutoToolTip = False
+ Me.BTT_DELETE.Image = Global.SCrawler.My.Resources.Resources.Delete
+ Me.BTT_DELETE.ImageTransparentColor = System.Drawing.Color.Magenta
+ Me.BTT_DELETE.Name = "BTT_DELETE"
+ Me.BTT_DELETE.Size = New System.Drawing.Size(83, 22)
+ Me.BTT_DELETE.Text = "Delete (F8)"
+ '
+ 'BTT_DOWN
+ '
+ Me.BTT_DOWN.AutoToolTip = False
+ Me.BTT_DOWN.Image = Global.SCrawler.My.Resources.Resources.StartPic_01_Green_16
+ Me.BTT_DOWN.ImageTransparentColor = System.Drawing.Color.Magenta
+ Me.BTT_DOWN.Name = "BTT_DOWN"
+ Me.BTT_DOWN.Size = New System.Drawing.Size(104, 22)
+ Me.BTT_DOWN.Text = "Download (F5)"
+ '
+ 'BTT_OPEN_PATH
+ '
+ Me.BTT_OPEN_PATH.AutoToolTip = False
+ Me.BTT_OPEN_PATH.Image = Global.SCrawler.My.Resources.Resources.Folder_32
+ Me.BTT_OPEN_PATH.ImageTransparentColor = System.Drawing.Color.Magenta
+ Me.BTT_OPEN_PATH.Name = "BTT_OPEN_PATH"
+ Me.BTT_OPEN_PATH.Size = New System.Drawing.Size(120, 22)
+ Me.BTT_OPEN_PATH.Text = "Open saving path"
+ '
+ 'ToolbarBOTTOM
+ '
+ Me.ToolbarBOTTOM.Items.AddRange(New System.Windows.Forms.ToolStripItem() {Me.PR_V, Me.LBL_STATUS})
+ Me.ToolbarBOTTOM.Location = New System.Drawing.Point(0, 339)
+ Me.ToolbarBOTTOM.Name = "ToolbarBOTTOM"
+ Me.ToolbarBOTTOM.Size = New System.Drawing.Size(524, 22)
+ Me.ToolbarBOTTOM.TabIndex = 1
+ '
+ 'PR_V
+ '
+ Me.PR_V.Name = "PR_V"
+ Me.PR_V.Size = New System.Drawing.Size(200, 16)
+ Me.PR_V.Visible = False
+ '
+ 'LBL_STATUS
+ '
+ Me.LBL_STATUS.Name = "LBL_STATUS"
+ Me.LBL_STATUS.Size = New System.Drawing.Size(0, 17)
+ '
+ 'LIST_VIDEOS
+ '
+ Me.LIST_VIDEOS.Dock = System.Windows.Forms.DockStyle.Fill
+ Me.LIST_VIDEOS.FormattingEnabled = True
+ Me.LIST_VIDEOS.Location = New System.Drawing.Point(0, 25)
+ Me.LIST_VIDEOS.Name = "LIST_VIDEOS"
+ Me.LIST_VIDEOS.Size = New System.Drawing.Size(524, 314)
+ Me.LIST_VIDEOS.TabIndex = 2
+ '
+ 'VideosDownloaderForm
+ '
+ Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
+ Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
+ Me.ClientSize = New System.Drawing.Size(524, 361)
+ Me.Controls.Add(Me.LIST_VIDEOS)
+ Me.Controls.Add(Me.ToolbarBOTTOM)
+ Me.Controls.Add(Me.ToolbarTOP)
+ Me.KeyPreview = True
+ Me.MinimumSize = New System.Drawing.Size(540, 400)
+ Me.Name = "VideosDownloaderForm"
+ Me.ShowIcon = False
+ Me.Text = "Download Videos"
+ Me.ToolbarTOP.ResumeLayout(False)
+ Me.ToolbarTOP.PerformLayout()
+ Me.ToolbarBOTTOM.ResumeLayout(False)
+ Me.ToolbarBOTTOM.PerformLayout()
+ Me.ResumeLayout(False)
+ Me.PerformLayout()
+
+ End Sub
+
+ Private WithEvents ToolbarTOP As ToolStrip
+ Private WithEvents BTT_ADD As ToolStripButton
+ Private WithEvents BTT_ADD_LIST As ToolStripButton
+ Private WithEvents BTT_DELETE As ToolStripButton
+ Private WithEvents ToolbarBOTTOM As StatusStrip
+ Private WithEvents PR_V As ToolStripProgressBar
+ Private WithEvents LBL_STATUS As ToolStripStatusLabel
+ Private WithEvents LIST_VIDEOS As ListBox
+ Private WithEvents BTT_DOWN As ToolStripButton
+ Private WithEvents BTT_OPEN_PATH As ToolStripButton
+ End Class
+End Namespace
\ No newline at end of file
diff --git a/SCrawler/VideosDownloaderForm.resx b/SCrawler/Download/VideosDownloaderForm.resx
similarity index 100%
rename from SCrawler/VideosDownloaderForm.resx
rename to SCrawler/Download/VideosDownloaderForm.resx
diff --git a/SCrawler/Download/VideosDownloaderForm.vb b/SCrawler/Download/VideosDownloaderForm.vb
new file mode 100644
index 0000000..5a8f2e0
--- /dev/null
+++ b/SCrawler/Download/VideosDownloaderForm.vb
@@ -0,0 +1,142 @@
+' Copyright (C) 2022 Andy
+' This program is free software: you can redistribute it and/or modify
+' it under the terms of the GNU General Public License as published by
+' the Free Software Foundation, either version 3 of the License, or
+' (at your option) any later version.
+'
+' This program is distributed in the hope that it will be useful,
+' but WITHOUT ANY WARRANTY
+Imports System.ComponentModel
+Imports PersonalUtilities.Forms
+Namespace DownloadObjects
+ Friend Class VideosDownloaderForm
+ Private MyView As FormsView
+ Private ReadOnly MyPR As Toolbars.MyProgress
+ Private ReadOnly UrlList As List(Of String)
+ Private ReadOnly DownloadingUrlsFile As SFile = $"{SettingsFolderName}\VideosUrls.txt"
+ Friend Sub New()
+ InitializeComponent()
+ UrlList = New List(Of String)
+ MyPR = New Toolbars.MyProgress(ToolbarBOTTOM, PR_V, LBL_STATUS, "Downloading video")
+ If DownloadingUrlsFile.Exists Then _
+ UrlList.ListAddList(DownloadingUrlsFile.GetText.StringToList(Of String, List(Of String))(Environment.NewLine), LAP.NotContainsOnly)
+ End Sub
+ Private Sub VideosDownloaderForm_Load(sender As Object, e As EventArgs) Handles Me.Load
+ Try
+ MyView = New FormsView(Me)
+ MyView.ImportFromXML(Settings.Design)
+ MyView.SetMeSize()
+ RefillList(False)
+ Catch ex As Exception
+ End Try
+ End Sub
+ Private Sub VideosDownloaderForm_Closing(sender As Object, e As CancelEventArgs) Handles Me.Closing
+ e.Cancel = True
+ Hide()
+ End Sub
+ Private Sub VideosDownloaderForm_Disposed(sender As Object, e As EventArgs) Handles Me.Disposed
+ If Not MyView Is Nothing Then MyView.Dispose(Settings.Design)
+ If UrlList.Count > 0 Then UpdateUrlsFile()
+ UrlList.Clear()
+ End Sub
+ Private Sub VideosDownloaderForm_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown
+ Dim b As Boolean = True
+ Select Case e.KeyCode
+ Case Keys.Insert : AddVideo()
+ Case Keys.F5 : DownloadVideos()
+ Case Keys.F8 : BTT_DELETE_Click(Nothing, EventArgs.Empty)
+ Case Else : b = False
+ End Select
+ If b Then e.Handled = True
+ End Sub
+ Private Sub RefillList(Optional ByVal Update As Boolean = True)
+ Try
+ With LIST_VIDEOS
+ .Items.Clear()
+ If UrlList.Count > 0 Then UrlList.ForEach(Sub(u) .Items.Add(u))
+ If .Items.Count > 0 And _LatestSelected >= 0 And _LatestSelected <= .Items.Count - 1 Then .SelectedIndex = _LatestSelected
+ If Update Then UpdateUrlsFile()
+ End With
+ Catch ex As Exception
+ ErrorsDescriber.Execute(EDP.LogMessageValue, ex, "Error on list refill")
+ End Try
+ End Sub
+ Private Sub UpdateUrlsFile()
+ If UrlList.Count > 0 Then
+ TextSaver.SaveTextToFile(UrlList.ListToString(, Environment.NewLine), DownloadingUrlsFile, True,, EDP.SendInLog)
+ Else
+ DownloadingUrlsFile.Delete(, Settings.DeleteMode, EDP.SendInLog)
+ End If
+ End Sub
+ Private Sub BTT_ADD_Click(sender As Object, e As EventArgs) Handles BTT_ADD.Click
+ AddVideo()
+ End Sub
+ Private Sub AddVideo()
+ Dim URL$ = GetNewVideoURL()
+ If Not URL.IsEmptyString Then
+ If Not UrlList.Contains(URL) Then
+ UrlList.Add(URL)
+ RefillList()
+ Else
+ MsgBoxE("This URL already added to list")
+ End If
+ End If
+ End Sub
+ Private Sub BTT_ADD_LIST_Click(sender As Object, e As EventArgs) Handles BTT_ADD_LIST.Click
+ Dim l$ = InputBoxE("Enter URLs (new line as delimiter):", "URLs list", GetCurrentBuffer(),,,,,, True)
+ If Not l.IsEmptyString Then
+ Dim ub% = UrlList.Count
+ UrlList.ListAddList(l.StringFormatLines.StringToList(Of String, List(Of String))(vbCrLf).ListForEach(Function(u, i) u.Trim,, False))
+ If Not UrlList.Count = ub Then RefillList()
+ End If
+ End Sub
+ Private Sub BTT_DELETE_Click(sender As Object, e As EventArgs) Handles BTT_DELETE.Click
+ If _LatestSelected >= 0 And _LatestSelected <= UrlList.Count - 1 Then
+ If MsgBoxE({$"Do you really want to delete video URL:{vbCr}{UrlList(_LatestSelected)}", "Deleting URL..."},
+ MsgBoxStyle.Exclamation + MsgBoxStyle.YesNo) = MsgBoxResult.Yes Then
+ UrlList.RemoveAt(_LatestSelected)
+ RefillList()
+ End If
+ Else
+ MsgBoxE("URL does not selected", MsgBoxStyle.Exclamation)
+ End If
+ End Sub
+ Private Sub BTT_DOWN_Click(sender As Object, e As EventArgs) Handles BTT_DOWN.Click
+ DownloadVideos()
+ End Sub
+ Private Sub DownloadVideos()
+ If UrlList.Count > 0 Then
+ MyPR.TotalCount = UrlList.Count
+ MyPR.Enabled = True
+ Dim IsFirst As Boolean = True
+ For i% = UrlList.Count - 1 To 0 Step -1
+ If DownloadVideoByURL(UrlList(i), IsFirst, True) Then UrlList.RemoveAt(i)
+ MyPR.Perform()
+ IsFirst = False
+ Next
+ MyPR.Done()
+ RefillList()
+ MyPR.Enabled = False
+ Else
+ MsgBoxE("No one video added", MsgBoxStyle.Exclamation)
+ End If
+ End Sub
+ Private _LatestSelected As Integer = -1
+ Private Sub LIST_VIDEOS_SelectedIndexChanged(sender As Object, e As EventArgs) Handles LIST_VIDEOS.SelectedIndexChanged
+ _LatestSelected = LIST_VIDEOS.SelectedIndex
+ End Sub
+ Private Sub BTT_OPEN_PATH_Click(sender As Object, e As EventArgs) Handles BTT_OPEN_PATH.Click
+ With Settings.LatestSavingPath
+ If Not .Value.IsEmptyString Then
+ If .Value.Exists(SFO.Path, False) Then
+ .Value.Open(SFO.Path, EDP.ShowMainMsg)
+ Else
+ MsgBoxE($"Path [{ .Value}] does not exists!", MsgBoxStyle.Exclamation)
+ End If
+ Else
+ MsgBoxE("Saving path does not set!", MsgBoxStyle.Exclamation)
+ End If
+ End With
+ End Sub
+ End Class
+End Namespace
\ No newline at end of file
diff --git a/SCrawler/DownloadSavedPostsForm.Designer.vb b/SCrawler/DownloadSavedPostsForm.Designer.vb
deleted file mode 100644
index 3b77939..0000000
--- a/SCrawler/DownloadSavedPostsForm.Designer.vb
+++ /dev/null
@@ -1,336 +0,0 @@
-' Copyright (C) 2022 Andy
-' This program is free software: you can redistribute it and/or modify
-' it under the terms of the GNU General Public License as published by
-' the Free Software Foundation, either version 3 of the License, or
-' (at your option) any later version.
-'
-' This program is distributed in the hope that it will be useful,
-' but WITHOUT ANY WARRANTY
-
-Partial Friend Class DownloadSavedPostsForm : 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 TP_REDDIT As System.Windows.Forms.TableLayoutPanel
- Dim TP_REDDIT_PR As System.Windows.Forms.TableLayoutPanel
- Dim TP_INST As System.Windows.Forms.TableLayoutPanel
- Dim TP_INST_PR As System.Windows.Forms.TableLayoutPanel
- Dim resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(DownloadSavedPostsForm))
- Dim TT_MAIN As System.Windows.Forms.ToolTip
- Me.BTT_DOWN_ALL = New System.Windows.Forms.Button()
- Me.BTT_STOP_ALL = New System.Windows.Forms.Button()
- Me.BTT_REDDIT_START = New System.Windows.Forms.Button()
- Me.BTT_REDDIT_STOP = New System.Windows.Forms.Button()
- Me.PR_REDDIT = New System.Windows.Forms.ProgressBar()
- Me.BTT_REDDIT_OPEN = New System.Windows.Forms.Button()
- Me.LBL_REDDIT = New System.Windows.Forms.Label()
- Me.BTT_INST_START = New System.Windows.Forms.Button()
- Me.BTT_INST_STOP = New System.Windows.Forms.Button()
- Me.PR_INST = New System.Windows.Forms.ProgressBar()
- Me.BTT_INST_OPEN = New System.Windows.Forms.Button()
- Me.LBL_INST = New System.Windows.Forms.Label()
- TP_MAIN = New System.Windows.Forms.TableLayoutPanel()
- TP_BUTTONS = New System.Windows.Forms.TableLayoutPanel()
- TP_REDDIT = New System.Windows.Forms.TableLayoutPanel()
- TP_REDDIT_PR = New System.Windows.Forms.TableLayoutPanel()
- TP_INST = New System.Windows.Forms.TableLayoutPanel()
- TP_INST_PR = New System.Windows.Forms.TableLayoutPanel()
- TT_MAIN = New System.Windows.Forms.ToolTip(Me.components)
- TP_MAIN.SuspendLayout()
- TP_BUTTONS.SuspendLayout()
- TP_REDDIT.SuspendLayout()
- TP_REDDIT_PR.SuspendLayout()
- TP_INST.SuspendLayout()
- TP_INST_PR.SuspendLayout()
- Me.SuspendLayout()
- '
- 'TP_MAIN
- '
- TP_MAIN.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.Inset
- TP_MAIN.ColumnCount = 1
- TP_MAIN.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
- TP_MAIN.Controls.Add(TP_BUTTONS, 0, 0)
- TP_MAIN.Controls.Add(TP_REDDIT, 0, 1)
- TP_MAIN.Controls.Add(TP_INST, 0, 2)
- 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 = 3
- TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 30.0!))
- TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50.0!))
- TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50.0!))
- TP_MAIN.Size = New System.Drawing.Size(484, 156)
- TP_MAIN.TabIndex = 0
- '
- 'TP_BUTTONS
- '
- TP_BUTTONS.ColumnCount = 2
- TP_BUTTONS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50.0!))
- TP_BUTTONS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50.0!))
- TP_BUTTONS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20.0!))
- TP_BUTTONS.Controls.Add(Me.BTT_DOWN_ALL, 0, 0)
- TP_BUTTONS.Controls.Add(Me.BTT_STOP_ALL, 1, 0)
- TP_BUTTONS.Dock = System.Windows.Forms.DockStyle.Fill
- TP_BUTTONS.Location = New System.Drawing.Point(2, 2)
- 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(480, 30)
- TP_BUTTONS.TabIndex = 0
- '
- 'BTT_DOWN_ALL
- '
- Me.BTT_DOWN_ALL.Dock = System.Windows.Forms.DockStyle.Fill
- Me.BTT_DOWN_ALL.Location = New System.Drawing.Point(3, 3)
- Me.BTT_DOWN_ALL.Name = "BTT_DOWN_ALL"
- Me.BTT_DOWN_ALL.Size = New System.Drawing.Size(234, 24)
- Me.BTT_DOWN_ALL.TabIndex = 0
- Me.BTT_DOWN_ALL.Text = "Download ALL"
- Me.BTT_DOWN_ALL.UseVisualStyleBackColor = True
- '
- 'BTT_STOP_ALL
- '
- Me.BTT_STOP_ALL.Dock = System.Windows.Forms.DockStyle.Fill
- Me.BTT_STOP_ALL.Location = New System.Drawing.Point(243, 3)
- Me.BTT_STOP_ALL.Name = "BTT_STOP_ALL"
- Me.BTT_STOP_ALL.Size = New System.Drawing.Size(234, 24)
- Me.BTT_STOP_ALL.TabIndex = 1
- Me.BTT_STOP_ALL.Text = "Stop ALL"
- Me.BTT_STOP_ALL.UseVisualStyleBackColor = True
- '
- 'TP_REDDIT
- '
- TP_REDDIT.ColumnCount = 1
- TP_REDDIT.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
- TP_REDDIT.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20.0!))
- TP_REDDIT.Controls.Add(TP_REDDIT_PR, 0, 0)
- TP_REDDIT.Controls.Add(Me.LBL_REDDIT, 0, 1)
- TP_REDDIT.Dock = System.Windows.Forms.DockStyle.Fill
- TP_REDDIT.Location = New System.Drawing.Point(2, 34)
- TP_REDDIT.Margin = New System.Windows.Forms.Padding(0)
- TP_REDDIT.Name = "TP_REDDIT"
- TP_REDDIT.RowCount = 2
- TP_REDDIT.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50.0!))
- TP_REDDIT.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50.0!))
- TP_REDDIT.Size = New System.Drawing.Size(480, 59)
- TP_REDDIT.TabIndex = 1
- '
- 'TP_REDDIT_PR
- '
- TP_REDDIT_PR.ColumnCount = 4
- TP_REDDIT_PR.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 30.0!))
- TP_REDDIT_PR.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 30.0!))
- TP_REDDIT_PR.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 30.0!))
- TP_REDDIT_PR.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
- TP_REDDIT_PR.Controls.Add(Me.BTT_REDDIT_START, 0, 0)
- TP_REDDIT_PR.Controls.Add(Me.BTT_REDDIT_STOP, 1, 0)
- TP_REDDIT_PR.Controls.Add(Me.PR_REDDIT, 3, 0)
- TP_REDDIT_PR.Controls.Add(Me.BTT_REDDIT_OPEN, 2, 0)
- TP_REDDIT_PR.Dock = System.Windows.Forms.DockStyle.Fill
- TP_REDDIT_PR.Location = New System.Drawing.Point(0, 0)
- TP_REDDIT_PR.Margin = New System.Windows.Forms.Padding(0)
- TP_REDDIT_PR.Name = "TP_REDDIT_PR"
- TP_REDDIT_PR.RowCount = 1
- TP_REDDIT_PR.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
- TP_REDDIT_PR.Size = New System.Drawing.Size(480, 29)
- TP_REDDIT_PR.TabIndex = 0
- '
- 'BTT_REDDIT_START
- '
- Me.BTT_REDDIT_START.BackgroundImage = Global.SCrawler.My.Resources.Resources.StartPic_01_Green_16
- Me.BTT_REDDIT_START.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom
- Me.BTT_REDDIT_START.Dock = System.Windows.Forms.DockStyle.Fill
- Me.BTT_REDDIT_START.Location = New System.Drawing.Point(3, 3)
- Me.BTT_REDDIT_START.Name = "BTT_REDDIT_START"
- Me.BTT_REDDIT_START.Size = New System.Drawing.Size(24, 23)
- Me.BTT_REDDIT_START.TabIndex = 0
- TT_MAIN.SetToolTip(Me.BTT_REDDIT_START, "Start downloading saved Reddit posts")
- Me.BTT_REDDIT_START.UseVisualStyleBackColor = True
- '
- 'BTT_REDDIT_STOP
- '
- Me.BTT_REDDIT_STOP.BackgroundImage = Global.SCrawler.My.Resources.Resources.Delete
- Me.BTT_REDDIT_STOP.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom
- Me.BTT_REDDIT_STOP.Dock = System.Windows.Forms.DockStyle.Fill
- Me.BTT_REDDIT_STOP.Enabled = False
- Me.BTT_REDDIT_STOP.Location = New System.Drawing.Point(33, 3)
- Me.BTT_REDDIT_STOP.Name = "BTT_REDDIT_STOP"
- Me.BTT_REDDIT_STOP.Size = New System.Drawing.Size(24, 23)
- Me.BTT_REDDIT_STOP.TabIndex = 1
- TT_MAIN.SetToolTip(Me.BTT_REDDIT_STOP, "Stop downloading saved Reddit posts")
- Me.BTT_REDDIT_STOP.UseVisualStyleBackColor = True
- '
- 'PR_REDDIT
- '
- Me.PR_REDDIT.Dock = System.Windows.Forms.DockStyle.Fill
- Me.PR_REDDIT.Location = New System.Drawing.Point(93, 3)
- Me.PR_REDDIT.Name = "PR_REDDIT"
- Me.PR_REDDIT.Size = New System.Drawing.Size(384, 23)
- Me.PR_REDDIT.TabIndex = 2
- '
- 'BTT_REDDIT_OPEN
- '
- Me.BTT_REDDIT_OPEN.BackgroundImage = CType(resources.GetObject("BTT_REDDIT_OPEN.BackgroundImage"), System.Drawing.Image)
- Me.BTT_REDDIT_OPEN.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom
- Me.BTT_REDDIT_OPEN.Dock = System.Windows.Forms.DockStyle.Fill
- Me.BTT_REDDIT_OPEN.Location = New System.Drawing.Point(63, 3)
- Me.BTT_REDDIT_OPEN.Name = "BTT_REDDIT_OPEN"
- Me.BTT_REDDIT_OPEN.Size = New System.Drawing.Size(24, 23)
- Me.BTT_REDDIT_OPEN.TabIndex = 3
- Me.BTT_REDDIT_OPEN.UseVisualStyleBackColor = True
- '
- 'LBL_REDDIT
- '
- Me.LBL_REDDIT.AutoSize = True
- Me.LBL_REDDIT.Dock = System.Windows.Forms.DockStyle.Fill
- Me.LBL_REDDIT.Location = New System.Drawing.Point(3, 29)
- Me.LBL_REDDIT.Name = "LBL_REDDIT"
- Me.LBL_REDDIT.Size = New System.Drawing.Size(474, 30)
- Me.LBL_REDDIT.TabIndex = 1
- Me.LBL_REDDIT.Text = "Reddit"
- Me.LBL_REDDIT.TextAlign = System.Drawing.ContentAlignment.TopCenter
- '
- 'TP_INST
- '
- TP_INST.ColumnCount = 1
- TP_INST.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
- TP_INST.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20.0!))
- TP_INST.Controls.Add(TP_INST_PR, 0, 0)
- TP_INST.Controls.Add(Me.LBL_INST, 0, 1)
- TP_INST.Dock = System.Windows.Forms.DockStyle.Fill
- TP_INST.Location = New System.Drawing.Point(2, 95)
- TP_INST.Margin = New System.Windows.Forms.Padding(0)
- TP_INST.Name = "TP_INST"
- TP_INST.RowCount = 2
- TP_INST.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50.0!))
- TP_INST.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50.0!))
- TP_INST.Size = New System.Drawing.Size(480, 59)
- TP_INST.TabIndex = 2
- '
- 'TP_INST_PR
- '
- TP_INST_PR.ColumnCount = 4
- TP_INST_PR.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 30.0!))
- TP_INST_PR.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 30.0!))
- TP_INST_PR.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 30.0!))
- TP_INST_PR.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
- TP_INST_PR.Controls.Add(Me.BTT_INST_START, 0, 0)
- TP_INST_PR.Controls.Add(Me.BTT_INST_STOP, 1, 0)
- TP_INST_PR.Controls.Add(Me.PR_INST, 3, 0)
- TP_INST_PR.Controls.Add(Me.BTT_INST_OPEN, 2, 0)
- TP_INST_PR.Dock = System.Windows.Forms.DockStyle.Fill
- TP_INST_PR.Location = New System.Drawing.Point(0, 0)
- TP_INST_PR.Margin = New System.Windows.Forms.Padding(0)
- TP_INST_PR.Name = "TP_INST_PR"
- TP_INST_PR.RowCount = 1
- TP_INST_PR.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
- TP_INST_PR.Size = New System.Drawing.Size(480, 29)
- TP_INST_PR.TabIndex = 0
- '
- 'BTT_INST_START
- '
- Me.BTT_INST_START.BackgroundImage = Global.SCrawler.My.Resources.Resources.StartPic_01_Green_16
- Me.BTT_INST_START.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom
- Me.BTT_INST_START.Dock = System.Windows.Forms.DockStyle.Fill
- Me.BTT_INST_START.Location = New System.Drawing.Point(3, 3)
- Me.BTT_INST_START.Name = "BTT_INST_START"
- Me.BTT_INST_START.Size = New System.Drawing.Size(24, 23)
- Me.BTT_INST_START.TabIndex = 0
- TT_MAIN.SetToolTip(Me.BTT_INST_START, "Start downloading saved Instagram posts")
- Me.BTT_INST_START.UseVisualStyleBackColor = True
- '
- 'BTT_INST_STOP
- '
- Me.BTT_INST_STOP.BackgroundImage = Global.SCrawler.My.Resources.Resources.Delete
- Me.BTT_INST_STOP.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom
- Me.BTT_INST_STOP.Dock = System.Windows.Forms.DockStyle.Fill
- Me.BTT_INST_STOP.Enabled = False
- Me.BTT_INST_STOP.Location = New System.Drawing.Point(33, 3)
- Me.BTT_INST_STOP.Name = "BTT_INST_STOP"
- Me.BTT_INST_STOP.Size = New System.Drawing.Size(24, 23)
- Me.BTT_INST_STOP.TabIndex = 1
- TT_MAIN.SetToolTip(Me.BTT_INST_STOP, "Stop downloading saved Instagram posts")
- Me.BTT_INST_STOP.UseVisualStyleBackColor = True
- '
- 'PR_INST
- '
- Me.PR_INST.Dock = System.Windows.Forms.DockStyle.Fill
- Me.PR_INST.Location = New System.Drawing.Point(93, 3)
- Me.PR_INST.Name = "PR_INST"
- Me.PR_INST.Size = New System.Drawing.Size(384, 23)
- Me.PR_INST.TabIndex = 2
- '
- 'BTT_INST_OPEN
- '
- Me.BTT_INST_OPEN.BackgroundImage = CType(resources.GetObject("BTT_INST_OPEN.BackgroundImage"), System.Drawing.Image)
- Me.BTT_INST_OPEN.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom
- Me.BTT_INST_OPEN.Dock = System.Windows.Forms.DockStyle.Fill
- Me.BTT_INST_OPEN.Location = New System.Drawing.Point(63, 3)
- Me.BTT_INST_OPEN.Name = "BTT_INST_OPEN"
- Me.BTT_INST_OPEN.Size = New System.Drawing.Size(24, 23)
- Me.BTT_INST_OPEN.TabIndex = 3
- Me.BTT_INST_OPEN.UseVisualStyleBackColor = True
- '
- 'LBL_INST
- '
- Me.LBL_INST.AutoSize = True
- Me.LBL_INST.Dock = System.Windows.Forms.DockStyle.Fill
- Me.LBL_INST.Location = New System.Drawing.Point(3, 29)
- Me.LBL_INST.Name = "LBL_INST"
- Me.LBL_INST.Size = New System.Drawing.Size(474, 30)
- Me.LBL_INST.TabIndex = 1
- Me.LBL_INST.Text = "Instagram"
- Me.LBL_INST.TextAlign = System.Drawing.ContentAlignment.TopCenter
- '
- 'DownloadSavedPostsForm
- '
- Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
- Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
- Me.ClientSize = New System.Drawing.Size(484, 156)
- Me.Controls.Add(TP_MAIN)
- Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle
- Me.Icon = CType(resources.GetObject("$this.Icon"), System.Drawing.Icon)
- Me.MaximizeBox = False
- Me.MaximumSize = New System.Drawing.Size(500, 195)
- Me.MinimumSize = New System.Drawing.Size(500, 195)
- Me.Name = "DownloadSavedPostsForm"
- Me.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide
- Me.Text = "Saved posts"
- TP_MAIN.ResumeLayout(False)
- TP_BUTTONS.ResumeLayout(False)
- TP_REDDIT.ResumeLayout(False)
- TP_REDDIT.PerformLayout()
- TP_REDDIT_PR.ResumeLayout(False)
- TP_INST.ResumeLayout(False)
- TP_INST.PerformLayout()
- TP_INST_PR.ResumeLayout(False)
- Me.ResumeLayout(False)
-
- End Sub
- Private WithEvents BTT_DOWN_ALL As Button
- Private WithEvents BTT_STOP_ALL As Button
- Private WithEvents BTT_REDDIT_START As Button
- Private WithEvents BTT_REDDIT_STOP As Button
- Private WithEvents PR_REDDIT As ProgressBar
- Private WithEvents LBL_REDDIT As Label
- Private WithEvents BTT_INST_START As Button
- Private WithEvents BTT_INST_STOP As Button
- Private WithEvents PR_INST As ProgressBar
- Private WithEvents LBL_INST As Label
- Private WithEvents BTT_REDDIT_OPEN As Button
- Private WithEvents BTT_INST_OPEN As Button
-End Class
\ No newline at end of file
diff --git a/SCrawler/DownloadSavedPostsForm.vb b/SCrawler/DownloadSavedPostsForm.vb
deleted file mode 100644
index 57393b2..0000000
--- a/SCrawler/DownloadSavedPostsForm.vb
+++ /dev/null
@@ -1,157 +0,0 @@
-' Copyright (C) 2022 Andy
-' This program is free software: you can redistribute it and/or modify
-' it under the terms of the GNU General Public License as published by
-' the Free Software Foundation, either version 3 of the License, or
-' (at your option) any later version.
-'
-' This program is distributed in the hope that it will be useful,
-' but WITHOUT ANY WARRANTY
-Imports System.ComponentModel
-Imports System.Threading
-Imports PersonalUtilities.Forms
-Imports PersonalUtilities.Forms.Toolbars
-Imports SCrawler.API
-Imports Job = SCrawler.TDownloader.Job
-Friend Class DownloadSavedPostsForm
- Friend Event OnDownloadDone(ByVal Message As String)
- Private MyView As FormsView
- Private ReadOnly ProgressReddit As MyProgress
- Private ReadOnly ProgressInstgram As MyProgress
- Private JobReddit As Job
- Private JobInstagram As Job
- Friend ReadOnly Property Working As Boolean
- Get
- Return JobReddit Or JobInstagram
- End Get
- End Property
-#Region "Start and Stop functions"
- Friend Overloads Sub [Stop]()
- [Stop](Sites.Reddit)
- [Stop](Sites.Instagram)
- End Sub
- Private Overloads Sub [Stop](ByVal Site As Sites)
- Select Case Site
- Case Sites.Reddit : If JobReddit Then JobReddit.Stop()
- Case Sites.Instagram : If JobInstagram Then JobInstagram.Stop()
- End Select
- End Sub
- Private Overloads Sub [Start]()
- Start(Sites.Reddit)
- Start(Sites.Instagram)
- End Sub
- Private Overloads Sub [Start](ByVal Site As Sites)
- Select Case Site
- Case Sites.Reddit : If Not JobReddit Then JobReddit.Start(New ThreadStart(Sub() DownloadData(Sites.Reddit)))
- Case Sites.Instagram
- If Not JobInstagram Then
- If Not Downloader.Working(Sites.Instagram) Then
- Downloader.InstagramSavedPostsDownloading = True
- JobInstagram.Start(New ThreadStart(Sub() DownloadData(Sites.Instagram)))
- Else
- MsgBoxE({$"Downloading Instagram profiles still works.{vbCr}Wait for this to be done before starting.{vbCr}Operation canceled",
- "Instagram saved posts"}, MsgBoxStyle.Critical)
- End If
- End If
- End Select
- End Sub
-#End Region
-#Region "Form functions"
- Friend Sub New()
- InitializeComponent()
- ProgressReddit = New MyProgress(PR_REDDIT, LBL_REDDIT)
- ProgressInstgram = New MyProgress(PR_INST, LBL_INST)
- JobReddit = New Job(ProgressReddit) With {.Site = Sites.Reddit}
- JobInstagram = New Job(ProgressInstgram) With {.Site = Sites.Instagram}
- End Sub
- Private Sub DownloadSavedPostsForm_Load(sender As Object, e As EventArgs) Handles Me.Load
- MyView = New FormsView(Me) With {.LocationOnly = True}
- MyView.ImportFromXML(Settings.Design)
- MyView.SetMeSize()
- End Sub
- Private Sub DownloadSavedPostsForm_Closing(sender As Object, e As CancelEventArgs) Handles Me.Closing
- e.Cancel = True
- Hide()
- End Sub
- Private Sub DownloadSavedPostsForm_Disposed(sender As Object, e As EventArgs) Handles Me.Disposed
- [Stop]()
- MyView.Dispose(Settings.Design)
- End Sub
-#End Region
-#Region "Controls"
- Private Sub BTT_DOWN_ALL_Click(sender As Object, e As EventArgs) Handles BTT_DOWN_ALL.Click
- Start()
- End Sub
- Private Sub BTT_STOP_ALL_Click(sender As Object, e As EventArgs) Handles BTT_STOP_ALL.Click
- [Stop]()
- End Sub
-#Region "Reddit"
- Private Sub BTT_REDDIT_START_Click(sender As Object, e As EventArgs) Handles BTT_REDDIT_START.Click
- Start(Sites.Reddit)
- End Sub
- Private Sub BTT_REDDIT_STOP_Click(sender As Object, e As EventArgs) Handles BTT_REDDIT_STOP.Click
- [Stop](Sites.Reddit)
- End Sub
- Private Sub BTT_REDDIT_OPEN_Click(sender As Object, e As EventArgs) Handles BTT_REDDIT_OPEN.Click
- OpenPath(Reddit.ProfileSaved.DataPath)
- End Sub
- Private Sub LBL_REDDIT_DoubleClick(sender As Object, e As EventArgs) Handles LBL_REDDIT.DoubleClick
- OpenPath(Reddit.ProfileSaved.DataPath)
- End Sub
-#End Region
-#Region "Instagram"
- Private Sub BTT_INST_START_Click(sender As Object, e As EventArgs) Handles BTT_INST_START.Click
- Start(Sites.Instagram)
- End Sub
- Private Sub BTT_INST_STOP_Click(sender As Object, e As EventArgs) Handles BTT_INST_STOP.Click
- [Stop](Sites.Instagram)
- End Sub
- Private Sub BTT_INST_OPEN_Click(sender As Object, e As EventArgs) Handles BTT_INST_OPEN.Click
- OpenPath(Instagram.ProfileSaved.DataPath)
- End Sub
- Private Sub LBL_INST_DoubleClick(sender As Object, e As EventArgs) Handles LBL_INST.DoubleClick
- OpenPath(Instagram.ProfileSaved.DataPath)
- End Sub
-#End Region
-#End Region
- Private Sub DownloadData(ByVal Site As Sites)
- Dim btte As Action(Of Button, Boolean) = Sub(b, e) If b.InvokeRequired Then b.Invoke(Sub() b.Enabled = e) Else b.Enabled = e
- Try
- Select Case Site
- Case Sites.Reddit
- btte(BTT_REDDIT_START, False)
- btte(BTT_REDDIT_STOP, True)
- JobReddit.Progress.InformationTemporary = "Reddit downloading started"
- JobReddit.Start()
- Reddit.ProfileSaved.Download(JobReddit.Progress, JobReddit)
- Case Sites.Instagram
- btte(BTT_INST_START, False)
- btte(BTT_INST_STOP, True)
- JobInstagram.Progress.InformationTemporary = "Instagram downloading started"
- JobInstagram.Start()
- Instagram.ProfileSaved.Download(JobInstagram.Progress, JobInstagram)
- End Select
- RaiseEvent OnDownloadDone($"Downloading saved {Site} posts is completed")
- Catch ex As Exception
- Select Case Site
- Case Sites.Reddit : JobReddit.Progress.InformationTemporary = "Reddit downloading error"
- Case Sites.Instagram : JobInstagram.Progress.InformationTemporary = "Instagram downloading error"
- End Select
- ErrorsDescriber.Execute(EDP.LogMessageValue, ex, {$"{Site} saved posts downloading error", "Saved posts"})
- Finally
- Select Case Site
- Case Sites.Reddit
- JobReddit.Stopped()
- btte(BTT_REDDIT_START, True)
- btte(BTT_REDDIT_STOP, False)
- Case Sites.Instagram
- JobInstagram.Stopped()
- btte(BTT_INST_START, True)
- btte(BTT_INST_STOP, False)
- Downloader.InstagramSavedPostsDownloading = False
- End Select
- End Try
- End Sub
- Private Sub OpenPath(ByVal f As SFile)
- If f.Exists(SFO.Path, False) Then f.Open(SFO.Path)
- End Sub
-End Class
\ No newline at end of file
diff --git a/SCrawler/DownloadedInfoForm.Designer.vb b/SCrawler/DownloadedInfoForm.Designer.vb
deleted file mode 100644
index f960b36..0000000
--- a/SCrawler/DownloadedInfoForm.Designer.vb
+++ /dev/null
@@ -1,134 +0,0 @@
-
-Partial Friend Class DownloadedInfoForm : 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 resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(DownloadedInfoForm))
- Dim SEP_1 As System.Windows.Forms.ToolStripSeparator
- 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.BTT_REFRESH = New System.Windows.Forms.ToolStripButton()
- Me.ToolbarBOTTOM = New System.Windows.Forms.StatusStrip()
- Me.LIST_DOWN = New System.Windows.Forms.ListBox()
- Me.BTT_CLEAR = New System.Windows.Forms.ToolStripButton()
- SEP_1 = New System.Windows.Forms.ToolStripSeparator()
- Me.ToolbarTOP.SuspendLayout()
- Me.SuspendLayout()
- '
- 'ToolbarTOP
- '
- Me.ToolbarTOP.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden
- Me.ToolbarTOP.Items.AddRange(New System.Windows.Forms.ToolStripItem() {Me.MENU_VIEW, Me.BTT_REFRESH, SEP_1, Me.BTT_CLEAR})
- Me.ToolbarTOP.Location = New System.Drawing.Point(0, 0)
- Me.ToolbarTOP.Name = "ToolbarTOP"
- Me.ToolbarTOP.Size = New System.Drawing.Size(554, 25)
- Me.ToolbarTOP.TabIndex = 0
- '
- '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.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.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(113, 22)
- Me.MENU_VIEW_SESSION.Text = "Session"
- Me.MENU_VIEW_SESSION.ToolTipText = "Show downloaded users by this session"
- '
- 'MENU_VIEW_ALL
- '
- Me.MENU_VIEW_ALL.AutoToolTip = True
- Me.MENU_VIEW_ALL.Name = "MENU_VIEW_ALL"
- Me.MENU_VIEW_ALL.Size = New System.Drawing.Size(113, 22)
- Me.MENU_VIEW_ALL.Text = "All"
- Me.MENU_VIEW_ALL.ToolTipText = "Show all users (sorted by latest download)"
- '
- 'BTT_REFRESH
- '
- Me.BTT_REFRESH.Image = Global.SCrawler.My.Resources.Resources.Refresh
- Me.BTT_REFRESH.ImageTransparentColor = System.Drawing.Color.Magenta
- Me.BTT_REFRESH.Name = "BTT_REFRESH"
- Me.BTT_REFRESH.Size = New System.Drawing.Size(89, 22)
- Me.BTT_REFRESH.Text = "Refresh (F5)"
- Me.BTT_REFRESH.ToolTipText = "Force list refresh"
- '
- 'ToolbarBOTTOM
- '
- Me.ToolbarBOTTOM.Location = New System.Drawing.Point(0, 389)
- Me.ToolbarBOTTOM.Name = "ToolbarBOTTOM"
- Me.ToolbarBOTTOM.Size = New System.Drawing.Size(554, 22)
- Me.ToolbarBOTTOM.TabIndex = 1
- '
- 'LIST_DOWN
- '
- Me.LIST_DOWN.Dock = System.Windows.Forms.DockStyle.Fill
- Me.LIST_DOWN.FormattingEnabled = True
- Me.LIST_DOWN.Location = New System.Drawing.Point(0, 25)
- Me.LIST_DOWN.Name = "LIST_DOWN"
- Me.LIST_DOWN.Size = New System.Drawing.Size(554, 364)
- Me.LIST_DOWN.TabIndex = 2
- '
- 'SEP_1
- '
- SEP_1.Name = "SEP_1"
- SEP_1.Size = New System.Drawing.Size(6, 25)
- '
- 'BTT_CLEAR
- '
- Me.BTT_CLEAR.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text
- Me.BTT_CLEAR.Image = CType(resources.GetObject("BTT_CLEAR.Image"), System.Drawing.Image)
- Me.BTT_CLEAR.ImageTransparentColor = System.Drawing.Color.Magenta
- Me.BTT_CLEAR.Name = "BTT_CLEAR"
- Me.BTT_CLEAR.Size = New System.Drawing.Size(38, 22)
- Me.BTT_CLEAR.Text = "Clear"
- Me.BTT_CLEAR.ToolTipText = "Clear info list"
- '
- 'DownloadedInfoForm
- '
- Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
- Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
- Me.ClientSize = New System.Drawing.Size(554, 411)
- Me.Controls.Add(Me.LIST_DOWN)
- Me.Controls.Add(Me.ToolbarBOTTOM)
- Me.Controls.Add(Me.ToolbarTOP)
- Me.KeyPreview = True
- Me.MinimumSize = New System.Drawing.Size(570, 450)
- Me.Name = "DownloadedInfoForm"
- Me.ShowIcon = False
- Me.Text = "Downloaded items"
- Me.ToolbarTOP.ResumeLayout(False)
- Me.ToolbarTOP.PerformLayout()
- Me.ResumeLayout(False)
- Me.PerformLayout()
-
- End Sub
-
- Private WithEvents ToolbarTOP As ToolStrip
- Private WithEvents MENU_VIEW As ToolStripDropDownButton
- Private WithEvents MENU_VIEW_SESSION As ToolStripMenuItem
- Private WithEvents MENU_VIEW_ALL As ToolStripMenuItem
- Private WithEvents BTT_REFRESH As ToolStripButton
- Private WithEvents ToolbarBOTTOM As StatusStrip
- Private WithEvents LIST_DOWN As ListBox
- Private WithEvents BTT_CLEAR As ToolStripButton
-End Class
\ No newline at end of file
diff --git a/SCrawler/DownloadedInfoForm.vb b/SCrawler/DownloadedInfoForm.vb
deleted file mode 100644
index 269037f..0000000
--- a/SCrawler/DownloadedInfoForm.vb
+++ /dev/null
@@ -1,141 +0,0 @@
-' Copyright (C) 2022 Andy
-' This program is free software: you can redistribute it and/or modify
-' it under the terms of the GNU General Public License as published by
-' the Free Software Foundation, either version 3 of the License, or
-' (at your option) any later version.
-'
-' This program is distributed in the hope that it will be useful,
-' but WITHOUT ANY WARRANTY
-Imports System.ComponentModel
-Imports PersonalUtilities.Forms
-Imports SCrawler.API.Base
-Friend Class DownloadedInfoForm
- Private MyView As FormsView
- Private ReadOnly LParams As New ListAddParams(LAP.IgnoreICopier) With {.e = EDP.None}
- Friend Enum ViewModes As Integer
- Session = 0
- All = 1
- End Enum
- Friend Property ViewMode As ViewModes
- Get
- Return IIf(MENU_VIEW_ALL.Checked, ViewModes.All, ViewModes.Session)
- End Get
- Set(ByVal SMode As ViewModes)
- Settings.InfoViewMode.Value = CInt(SMode)
- End Set
- End Property
- Private ReadOnly _TempUsersList As List(Of IUserData)
- Friend Sub New()
- InitializeComponent()
- _TempUsersList = New List(Of IUserData)
- If Settings.InfoViewMode.Value = CInt(ViewModes.All) Then
- MENU_VIEW_SESSION.Checked = False
- MENU_VIEW_ALL.Checked = True
- Else
- MENU_VIEW_SESSION.Checked = True
- MENU_VIEW_ALL.Checked = False
- End If
- Settings.InfoViewMode.Value = ViewMode
- RefillList()
- End Sub
- Private Sub DownloadedInfoForm_Load(sender As Object, e As EventArgs) Handles Me.Load
- Try
- If MyView Is Nothing Then
- MyView = New FormsView(Me)
- MyView.ImportFromXML(Settings.Design)
- MyView.SetMeSize()
- End If
- BTT_CLEAR.Visible = ViewMode = ViewModes.Session
- RefillList()
- Catch ex As Exception
- End Try
- End Sub
- Private Sub DownloadedInfoForm_Closing(sender As Object, e As CancelEventArgs) Handles Me.Closing
- e.Cancel = True
- 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()
- End Sub
- Private Sub DownloadedInfoForm_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown
- If e.KeyCode = Keys.F5 Then RefillList() : e.Handled = True
- End Sub
- Private Class UsersDateOrder : Implements IComparer(Of IUserData)
- Friend Function Compare(ByVal x As IUserData, ByVal y As IUserData) As Integer Implements IComparer(Of IUserData).Compare
- Dim xv& = If(DirectCast(x.Self, UserDataBase).LastUpdated.HasValue, DirectCast(x.Self, UserDataBase).LastUpdated.Value.Ticks, 0)
- Dim yv& = If(DirectCast(y.Self, UserDataBase).LastUpdated.HasValue, DirectCast(y.Self, UserDataBase).LastUpdated.Value.Ticks, 0)
- Return xv.CompareTo(yv) * -1
- End Function
- End Class
- Private Sub RefillList()
- 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)
- Else
- _TempUsersList.ListAddList(Settings.Users.SelectMany(Of IUserData) _
- (Function(u) If(u.IsCollection, DirectCast(u, API.UserDataBind).Collections, {u})), LParams)
- End If
- If _TempUsersList.Count > 0 Then
- _TempUsersList.Sort(New UsersDateOrder)
- For Each user As IUserData In _TempUsersList
- 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
- Dim aSel As Action = Sub() LIST_DOWN.SelectedIndex = _LatestSelected
- If LIST_DOWN.InvokeRequired Then LIST_DOWN.Invoke(aSel) Else aSel.Invoke
- Else
- _LatestSelected = -1
- End If
- Else
- _LatestSelected = -1
- End If
- Catch ex As Exception
- ErrorsDescriber.Execute(EDP.SendInLog, ex, "[DownloadedInfoForm.RefillList]")
- End Try
- End Sub
- 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()
- 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()
- End Sub
- Private Sub BTT_REFRESH_Click(sender As Object, e As EventArgs) Handles BTT_REFRESH.Click
- RefillList()
- 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
- End Sub
- Private _LatestSelected As Integer = -1
- Private Sub LIST_DOWN_SelectedIndexChanged(sender As Object, e As EventArgs) Handles LIST_DOWN.SelectedIndexChanged
- _LatestSelected = LIST_DOWN.SelectedIndex
- End Sub
- Private Sub LIST_DOWN_MouseDoubleClick(sender As Object, e As MouseEventArgs) Handles LIST_DOWN.MouseDoubleClick
- Try
- If _LatestSelected >= 0 AndAlso _LatestSelected <= _TempUsersList.Count - 1 AndAlso
- Not DirectCast(_TempUsersList(_LatestSelected).Self, UserDataBase).Disposed Then _TempUsersList(_LatestSelected).OpenFolder()
- Catch ex As Exception
- End Try
- End Sub
- Friend Sub Downloader_OnDownloadCountChange()
- If ViewMode = ViewModes.Session Then RefillList()
- End Sub
-End Class
\ No newline at end of file
diff --git a/SCrawler/Editors/GlobalSettingsForm.Designer.vb b/SCrawler/Editors/GlobalSettingsForm.Designer.vb
index 6ab1122..d3137f0 100644
--- a/SCrawler/Editors/GlobalSettingsForm.Designer.vb
+++ b/SCrawler/Editors/GlobalSettingsForm.Designer.vb
@@ -27,6 +27,7 @@
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
+ Dim ActionButton7 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim TT_MAIN As System.Windows.Forms.ToolTip
Dim TP_CHANNELS_IMGS As System.Windows.Forms.TableLayoutPanel
Dim TAB_BASIS As System.Windows.Forms.TabPage
@@ -34,12 +35,6 @@
Dim TP_DEFS As System.Windows.Forms.TableLayoutPanel
Dim TAB_DEFS_CHANNELS As System.Windows.Forms.TabPage
Dim TP_CHANNELS As System.Windows.Forms.TableLayoutPanel
- Dim TAB_DEFS_REDDIT As System.Windows.Forms.TabPage
- 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 TAB_DEFS_TWITTER As System.Windows.Forms.TabPage
- 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.TXT_GLOBAL_PATH = New PersonalUtilities.Forms.Controls.TextBoxExtended()
Me.TXT_IMAGE_LARGE = New PersonalUtilities.Forms.Controls.TextBoxExtended()
Me.TXT_IMAGE_SMALL = New PersonalUtilities.Forms.Controls.TextBoxExtended()
@@ -58,32 +53,22 @@
Me.CH_EXIT_CONFIRM = New System.Windows.Forms.CheckBox()
Me.CH_CLOSE_TO_TRAY = New System.Windows.Forms.CheckBox()
Me.CH_SHOW_NOTIFY = New System.Windows.Forms.CheckBox()
+ Me.CH_FAST_LOAD = New System.Windows.Forms.CheckBox()
+ Me.TXT_FOLDER_CMD = New PersonalUtilities.Forms.Controls.TextBoxExtended()
Me.CH_COPY_CHANNEL_USER_IMAGE = New System.Windows.Forms.CheckBox()
Me.CH_DEF_TEMP = New System.Windows.Forms.CheckBox()
Me.CH_DOWN_IMAGES = New System.Windows.Forms.CheckBox()
Me.CH_DOWN_VIDEOS = New System.Windows.Forms.CheckBox()
Me.CH_SEPARATE_VIDEO_FOLDER = New System.Windows.Forms.CheckBox()
Me.CH_CHANNELS_USERS_TEMP = New System.Windows.Forms.CheckBox()
+ Me.CH_COPY_CHANNEL_USER_IMAGE_ALL = New System.Windows.Forms.CheckBox()
+ Me.CH_UDESCR_UP = New System.Windows.Forms.CheckBox()
Me.TXT_CHANNELS_ROWS = New PersonalUtilities.Forms.Controls.TextBoxExtended()
Me.TXT_CHANNELS_COLUMNS = New PersonalUtilities.Forms.Controls.TextBoxExtended()
Me.TXT_CHANNEL_USER_POST_LIMIT = New PersonalUtilities.Forms.Controls.TextBoxExtended()
- Me.DEFS_REDDIT = New SCrawler.Editors.SiteDefaults()
- Me.TXT_REDDIT_SAVED_POSTS_USER = New PersonalUtilities.Forms.Controls.TextBoxExtended()
- Me.CH_REDDIT_USER_MEDIA = New System.Windows.Forms.CheckBox()
- Me.TXT_REDDIT_SAVED_POSTS_PATH = New PersonalUtilities.Forms.Controls.TextBoxExtended()
- Me.DEFS_TWITTER = New SCrawler.Editors.SiteDefaults()
- Me.CH_TWITTER_USER_MEDIA = New System.Windows.Forms.CheckBox()
- Me.TXT_REQ_WAIT_TIMER = New PersonalUtilities.Forms.Controls.TextBoxExtended()
- Me.TXT_REQ_COUNT = New PersonalUtilities.Forms.Controls.TextBoxExtended()
- Me.TXT_LIMIT_TIMER = New PersonalUtilities.Forms.Controls.TextBoxExtended()
Me.TAB_MAIN = New System.Windows.Forms.TabControl()
- Me.TAB_DEFS_INSTAGRAM = New System.Windows.Forms.TabPage()
- Me.DEFS_INST = New SCrawler.Editors.SiteDefaults()
- Me.TXT_INST_SAVED_POSTS_USER = New PersonalUtilities.Forms.Controls.TextBoxExtended()
- Me.TXT_INST_SAVED_POSTS_PATH = New PersonalUtilities.Forms.Controls.TextBoxExtended()
- Me.TAB_DEFS_REDGIFS = New System.Windows.Forms.TabPage()
- Me.DEFS_REDGIFS = New SCrawler.Editors.SiteDefaults()
Me.CONTAINER_MAIN = New System.Windows.Forms.ToolStripContainer()
+ Me.CH_RECYCLE_DEL = 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()
@@ -96,8 +81,6 @@
TP_DEFS = New System.Windows.Forms.TableLayoutPanel()
TAB_DEFS_CHANNELS = New System.Windows.Forms.TabPage()
TP_CHANNELS = New System.Windows.Forms.TableLayoutPanel()
- TAB_DEFS_REDDIT = New System.Windows.Forms.TabPage()
- TAB_DEFS_TWITTER = New System.Windows.Forms.TabPage()
TP_BASIS.SuspendLayout()
CType(Me.TXT_GLOBAL_PATH, System.ComponentModel.ISupportInitialize).BeginInit()
TP_IMAGES.SuspendLayout()
@@ -109,6 +92,7 @@
CType(Me.TXT_IMGUR_CLIENT_ID, System.ComponentModel.ISupportInitialize).BeginInit()
TP_FILE_NAME.SuspendLayout()
TP_FILE_PATTERNS.SuspendLayout()
+ CType(Me.TXT_FOLDER_CMD, System.ComponentModel.ISupportInitialize).BeginInit()
TP_CHANNELS_IMGS.SuspendLayout()
CType(Me.TXT_CHANNELS_ROWS, System.ComponentModel.ISupportInitialize).BeginInit()
CType(Me.TXT_CHANNELS_COLUMNS, System.ComponentModel.ISupportInitialize).BeginInit()
@@ -118,21 +102,7 @@
TAB_DEFS_CHANNELS.SuspendLayout()
TP_CHANNELS.SuspendLayout()
CType(Me.TXT_CHANNEL_USER_POST_LIMIT, System.ComponentModel.ISupportInitialize).BeginInit()
- TAB_DEFS_REDDIT.SuspendLayout()
- Me.DEFS_REDDIT.SuspendLayout()
- CType(Me.TXT_REDDIT_SAVED_POSTS_USER, System.ComponentModel.ISupportInitialize).BeginInit()
- CType(Me.TXT_REDDIT_SAVED_POSTS_PATH, System.ComponentModel.ISupportInitialize).BeginInit()
- TAB_DEFS_TWITTER.SuspendLayout()
- Me.DEFS_TWITTER.SuspendLayout()
- CType(Me.TXT_REQ_WAIT_TIMER, System.ComponentModel.ISupportInitialize).BeginInit()
- CType(Me.TXT_REQ_COUNT, System.ComponentModel.ISupportInitialize).BeginInit()
- CType(Me.TXT_LIMIT_TIMER, System.ComponentModel.ISupportInitialize).BeginInit()
Me.TAB_MAIN.SuspendLayout()
- Me.TAB_DEFS_INSTAGRAM.SuspendLayout()
- Me.DEFS_INST.SuspendLayout()
- CType(Me.TXT_INST_SAVED_POSTS_USER, System.ComponentModel.ISupportInitialize).BeginInit()
- CType(Me.TXT_INST_SAVED_POSTS_PATH, System.ComponentModel.ISupportInitialize).BeginInit()
- Me.TAB_DEFS_REDGIFS.SuspendLayout()
Me.CONTAINER_MAIN.ContentPanel.SuspendLayout()
Me.CONTAINER_MAIN.SuspendLayout()
Me.SuspendLayout()
@@ -154,10 +124,13 @@
TP_BASIS.Controls.Add(Me.CH_EXIT_CONFIRM, 0, 9)
TP_BASIS.Controls.Add(Me.CH_CLOSE_TO_TRAY, 0, 10)
TP_BASIS.Controls.Add(Me.CH_SHOW_NOTIFY, 0, 11)
+ TP_BASIS.Controls.Add(Me.CH_FAST_LOAD, 0, 12)
+ TP_BASIS.Controls.Add(Me.TXT_FOLDER_CMD, 0, 13)
+ TP_BASIS.Controls.Add(Me.CH_RECYCLE_DEL, 0, 14)
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!))
@@ -170,8 +143,11 @@
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.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.Percent, 100.0!))
- TP_BASIS.Size = New System.Drawing.Size(570, 366)
+ TP_BASIS.Size = New System.Drawing.Size(570, 422)
TP_BASIS.TabIndex = 0
'
'TXT_GLOBAL_PATH
@@ -489,6 +465,40 @@
Me.CH_SHOW_NOTIFY.Text = "Show notifications"
Me.CH_SHOW_NOTIFY.UseVisualStyleBackColor = True
'
+ 'CH_FAST_LOAD
+ '
+ Me.CH_FAST_LOAD.AutoSize = True
+ Me.CH_FAST_LOAD.Dock = System.Windows.Forms.DockStyle.Fill
+ Me.CH_FAST_LOAD.Location = New System.Drawing.Point(4, 344)
+ Me.CH_FAST_LOAD.Name = "CH_FAST_LOAD"
+ Me.CH_FAST_LOAD.Size = New System.Drawing.Size(562, 19)
+ Me.CH_FAST_LOAD.TabIndex = 12
+ Me.CH_FAST_LOAD.Text = "Fast profiles loading"
+ TT_MAIN.SetToolTip(Me.CH_FAST_LOAD, "Fast loading of profiles in the main window")
+ Me.CH_FAST_LOAD.UseVisualStyleBackColor = True
+ '
+ 'TXT_FOLDER_CMD
+ '
+ Me.TXT_FOLDER_CMD.AutoShowClearButton = True
+ ActionButton7.BackgroundImage = CType(resources.GetObject("ActionButton7.BackgroundImage"), System.Drawing.Image)
+ ActionButton7.Enabled = False
+ ActionButton7.Index = 0
+ ActionButton7.Name = "BTT_CLEAR"
+ ActionButton7.Visible = False
+ Me.TXT_FOLDER_CMD.Buttons.Add(ActionButton7)
+ 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.Location = New System.Drawing.Point(4, 370)
+ Me.TXT_FOLDER_CMD.Name = "TXT_FOLDER_CMD"
+ Me.TXT_FOLDER_CMD.PlaceholderEnabled = True
+ Me.TXT_FOLDER_CMD.PlaceholderText = "MyCommand /arg {0}"
+ Me.TXT_FOLDER_CMD.Size = New System.Drawing.Size(562, 22)
+ Me.TXT_FOLDER_CMD.TabIndex = 13
+ '
'CH_COPY_CHANNEL_USER_IMAGE
'
Me.CH_COPY_CHANNEL_USER_IMAGE.AutoSize = True
@@ -554,14 +564,39 @@
'
Me.CH_CHANNELS_USERS_TEMP.AutoSize = True
Me.CH_CHANNELS_USERS_TEMP.Dock = System.Windows.Forms.DockStyle.Fill
- Me.CH_CHANNELS_USERS_TEMP.Location = New System.Drawing.Point(4, 88)
+ Me.CH_CHANNELS_USERS_TEMP.Location = New System.Drawing.Point(4, 114)
Me.CH_CHANNELS_USERS_TEMP.Name = "CH_CHANNELS_USERS_TEMP"
Me.CH_CHANNELS_USERS_TEMP.Size = New System.Drawing.Size(562, 19)
- Me.CH_CHANNELS_USERS_TEMP.TabIndex = 3
+ Me.CH_CHANNELS_USERS_TEMP.TabIndex = 4
Me.CH_CHANNELS_USERS_TEMP.Text = "Create temporary users"
TT_MAIN.SetToolTip(Me.CH_CHANNELS_USERS_TEMP, "Users added from channels will be created with this parameter")
Me.CH_CHANNELS_USERS_TEMP.UseVisualStyleBackColor = True
'
+ 'CH_COPY_CHANNEL_USER_IMAGE_ALL
+ '
+ Me.CH_COPY_CHANNEL_USER_IMAGE_ALL.AutoSize = True
+ Me.CH_COPY_CHANNEL_USER_IMAGE_ALL.Dock = System.Windows.Forms.DockStyle.Fill
+ Me.CH_COPY_CHANNEL_USER_IMAGE_ALL.Location = New System.Drawing.Point(4, 88)
+ Me.CH_COPY_CHANNEL_USER_IMAGE_ALL.Name = "CH_COPY_CHANNEL_USER_IMAGE_ALL"
+ Me.CH_COPY_CHANNEL_USER_IMAGE_ALL.Size = New System.Drawing.Size(562, 19)
+ Me.CH_COPY_CHANNEL_USER_IMAGE_ALL.TabIndex = 3
+ Me.CH_COPY_CHANNEL_USER_IMAGE_ALL.Text = "Copy images from all channels"
+ TT_MAIN.SetToolTip(Me.CH_COPY_CHANNEL_USER_IMAGE_ALL, "Copy user images from all channels you have when adding a user from a channel")
+ Me.CH_COPY_CHANNEL_USER_IMAGE_ALL.UseVisualStyleBackColor = True
+ '
+ 'CH_UDESCR_UP
+ '
+ Me.CH_UDESCR_UP.AutoSize = True
+ Me.CH_UDESCR_UP.Dock = System.Windows.Forms.DockStyle.Fill
+ Me.CH_UDESCR_UP.Location = New System.Drawing.Point(4, 108)
+ Me.CH_UDESCR_UP.Name = "CH_UDESCR_UP"
+ Me.CH_UDESCR_UP.Size = New System.Drawing.Size(562, 19)
+ Me.CH_UDESCR_UP.TabIndex = 4
+ Me.CH_UDESCR_UP.Text = "Update user description every time"
+ TT_MAIN.SetToolTip(Me.CH_UDESCR_UP, "If the user description does not contain a new user description, then the new one" &
+ " will be added via a new line")
+ Me.CH_UDESCR_UP.UseVisualStyleBackColor = True
+ '
'TP_CHANNELS_IMGS
'
TP_CHANNELS_IMGS.ColumnCount = 2
@@ -613,7 +648,7 @@
TAB_BASIS.Location = New System.Drawing.Point(4, 22)
TAB_BASIS.Name = "TAB_BASIS"
TAB_BASIS.Padding = New System.Windows.Forms.Padding(3)
- TAB_BASIS.Size = New System.Drawing.Size(576, 372)
+ TAB_BASIS.Size = New System.Drawing.Size(576, 428)
TAB_BASIS.TabIndex = 0
TAB_BASIS.Text = "Basis"
'
@@ -623,7 +658,7 @@
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, 372)
+ TAB_DEFAULTS.Size = New System.Drawing.Size(576, 426)
TAB_DEFAULTS.TabIndex = 1
TAB_DEFAULTS.Text = "Defaults"
'
@@ -636,16 +671,18 @@
TP_DEFS.Controls.Add(Me.CH_DOWN_VIDEOS, 0, 3)
TP_DEFS.Controls.Add(Me.CH_DOWN_IMAGES, 0, 2)
TP_DEFS.Controls.Add(Me.CH_DEF_TEMP, 0, 1)
+ TP_DEFS.Controls.Add(Me.CH_UDESCR_UP, 0, 4)
TP_DEFS.Dock = System.Windows.Forms.DockStyle.Fill
TP_DEFS.Location = New System.Drawing.Point(3, 3)
TP_DEFS.Name = "TP_DEFS"
- TP_DEFS.RowCount = 5
+ TP_DEFS.RowCount = 6
+ 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.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.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, 366)
+ TP_DEFS.Size = New System.Drawing.Size(570, 420)
TP_DEFS.TabIndex = 0
'
'TAB_DEFS_CHANNELS
@@ -654,7 +691,7 @@
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, 372)
+ TAB_DEFS_CHANNELS.Size = New System.Drawing.Size(576, 426)
TAB_DEFS_CHANNELS.TabIndex = 4
TAB_DEFS_CHANNELS.Text = "Channels"
'
@@ -666,19 +703,19 @@
TP_CHANNELS.Controls.Add(Me.TXT_CHANNEL_USER_POST_LIMIT, 0, 1)
TP_CHANNELS.Controls.Add(TP_CHANNELS_IMGS, 0, 0)
TP_CHANNELS.Controls.Add(Me.CH_COPY_CHANNEL_USER_IMAGE, 0, 2)
- TP_CHANNELS.Controls.Add(Me.CH_CHANNELS_USERS_TEMP, 0, 3)
+ TP_CHANNELS.Controls.Add(Me.CH_CHANNELS_USERS_TEMP, 0, 4)
+ TP_CHANNELS.Controls.Add(Me.CH_COPY_CHANNEL_USER_IMAGE_ALL, 0, 3)
TP_CHANNELS.Dock = System.Windows.Forms.DockStyle.Fill
TP_CHANNELS.Location = New System.Drawing.Point(3, 3)
TP_CHANNELS.Name = "TP_CHANNELS"
- TP_CHANNELS.RowCount = 5
+ TP_CHANNELS.RowCount = 6
TP_CHANNELS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
TP_CHANNELS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.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.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.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20.0!))
- TP_CHANNELS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20.0!))
- TP_CHANNELS.Size = New System.Drawing.Size(570, 366)
+ TP_CHANNELS.Size = New System.Drawing.Size(570, 420)
TP_CHANNELS.TabIndex = 0
'
'TXT_CHANNEL_USER_POST_LIMIT
@@ -701,284 +738,58 @@
Me.TXT_CHANNEL_USER_POST_LIMIT.Text = "1"
Me.TXT_CHANNEL_USER_POST_LIMIT.TextBoxTextAlign = System.Windows.Forms.HorizontalAlignment.Center
'
- 'TAB_DEFS_REDDIT
- '
- TAB_DEFS_REDDIT.Controls.Add(Me.DEFS_REDDIT)
- TAB_DEFS_REDDIT.Location = New System.Drawing.Point(4, 22)
- TAB_DEFS_REDDIT.Name = "TAB_DEFS_REDDIT"
- TAB_DEFS_REDDIT.Padding = New System.Windows.Forms.Padding(3)
- TAB_DEFS_REDDIT.Size = New System.Drawing.Size(576, 372)
- TAB_DEFS_REDDIT.TabIndex = 2
- TAB_DEFS_REDDIT.Text = "Reddit"
- '
- 'DEFS_REDDIT
- '
- Me.DEFS_REDDIT.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.[Single]
- Me.DEFS_REDDIT.ColumnCount = 1
- Me.DEFS_REDDIT.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
- Me.DEFS_REDDIT.Controls.Add(Me.TXT_REDDIT_SAVED_POSTS_USER, 0, 4)
- Me.DEFS_REDDIT.Controls.Add(Me.CH_REDDIT_USER_MEDIA, 0, 3)
- Me.DEFS_REDDIT.Controls.Add(Me.TXT_REDDIT_SAVED_POSTS_PATH, 0, 5)
- Me.DEFS_REDDIT.Dock = System.Windows.Forms.DockStyle.Fill
- Me.DEFS_REDDIT.Location = New System.Drawing.Point(3, 3)
- Me.DEFS_REDDIT.Name = "DEFS_REDDIT"
- Me.DEFS_REDDIT.RowCount = 7
- Me.DEFS_REDDIT.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
- Me.DEFS_REDDIT.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
- Me.DEFS_REDDIT.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
- Me.DEFS_REDDIT.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
- Me.DEFS_REDDIT.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
- Me.DEFS_REDDIT.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
- Me.DEFS_REDDIT.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
- Me.DEFS_REDDIT.Size = New System.Drawing.Size(570, 366)
- Me.DEFS_REDDIT.TabIndex = 1
- '
- 'TXT_REDDIT_SAVED_POSTS_USER
- '
- Me.TXT_REDDIT_SAVED_POSTS_USER.CaptionText = "Saved posts user"
- Me.TXT_REDDIT_SAVED_POSTS_USER.Dock = System.Windows.Forms.DockStyle.Fill
- Me.TXT_REDDIT_SAVED_POSTS_USER.Location = New System.Drawing.Point(4, 108)
- Me.TXT_REDDIT_SAVED_POSTS_USER.Name = "TXT_REDDIT_SAVED_POSTS_USER"
- Me.TXT_REDDIT_SAVED_POSTS_USER.Size = New System.Drawing.Size(562, 22)
- Me.TXT_REDDIT_SAVED_POSTS_USER.TabIndex = 4
- '
- 'CH_REDDIT_USER_MEDIA
- '
- Me.CH_REDDIT_USER_MEDIA.AutoSize = True
- Me.CH_REDDIT_USER_MEDIA.Dock = System.Windows.Forms.DockStyle.Fill
- Me.CH_REDDIT_USER_MEDIA.Location = New System.Drawing.Point(4, 82)
- Me.CH_REDDIT_USER_MEDIA.Name = "CH_REDDIT_USER_MEDIA"
- Me.CH_REDDIT_USER_MEDIA.Size = New System.Drawing.Size(562, 19)
- Me.CH_REDDIT_USER_MEDIA.TabIndex = 3
- Me.CH_REDDIT_USER_MEDIA.Text = "Get user media only"
- Me.CH_REDDIT_USER_MEDIA.UseVisualStyleBackColor = True
- '
- 'TXT_REDDIT_SAVED_POSTS_PATH
- '
- ActionButton7.BackgroundImage = CType(resources.GetObject("ActionButton7.BackgroundImage"), System.Drawing.Image)
- ActionButton7.Index = 0
- ActionButton7.Name = "BTT_OPEN"
- ActionButton8.BackgroundImage = CType(resources.GetObject("ActionButton8.BackgroundImage"), System.Drawing.Image)
- ActionButton8.Index = 1
- ActionButton8.Name = "BTT_CLEAR"
- Me.TXT_REDDIT_SAVED_POSTS_PATH.Buttons.Add(ActionButton7)
- Me.TXT_REDDIT_SAVED_POSTS_PATH.Buttons.Add(ActionButton8)
- Me.TXT_REDDIT_SAVED_POSTS_PATH.CaptionText = "Saved posts path"
- Me.TXT_REDDIT_SAVED_POSTS_PATH.CaptionToolTipEnabled = True
- Me.TXT_REDDIT_SAVED_POSTS_PATH.CaptionToolTipText = "Special path (clear to use default)"
- Me.TXT_REDDIT_SAVED_POSTS_PATH.Dock = System.Windows.Forms.DockStyle.Fill
- Me.TXT_REDDIT_SAVED_POSTS_PATH.Location = New System.Drawing.Point(4, 137)
- Me.TXT_REDDIT_SAVED_POSTS_PATH.Name = "TXT_REDDIT_SAVED_POSTS_PATH"
- Me.TXT_REDDIT_SAVED_POSTS_PATH.Size = New System.Drawing.Size(562, 22)
- Me.TXT_REDDIT_SAVED_POSTS_PATH.TabIndex = 8
- '
- 'TAB_DEFS_TWITTER
- '
- TAB_DEFS_TWITTER.Controls.Add(Me.DEFS_TWITTER)
- TAB_DEFS_TWITTER.Location = New System.Drawing.Point(4, 22)
- TAB_DEFS_TWITTER.Name = "TAB_DEFS_TWITTER"
- TAB_DEFS_TWITTER.Padding = New System.Windows.Forms.Padding(3)
- TAB_DEFS_TWITTER.Size = New System.Drawing.Size(576, 372)
- TAB_DEFS_TWITTER.TabIndex = 3
- TAB_DEFS_TWITTER.Text = "Twitter"
- '
- 'DEFS_TWITTER
- '
- Me.DEFS_TWITTER.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.[Single]
- Me.DEFS_TWITTER.ColumnCount = 1
- Me.DEFS_TWITTER.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
- Me.DEFS_TWITTER.Controls.Add(Me.CH_TWITTER_USER_MEDIA, 0, 3)
- Me.DEFS_TWITTER.Dock = System.Windows.Forms.DockStyle.Fill
- Me.DEFS_TWITTER.Location = New System.Drawing.Point(3, 3)
- Me.DEFS_TWITTER.Name = "DEFS_TWITTER"
- Me.DEFS_TWITTER.RowCount = 5
- Me.DEFS_TWITTER.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
- Me.DEFS_TWITTER.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
- Me.DEFS_TWITTER.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
- Me.DEFS_TWITTER.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
- Me.DEFS_TWITTER.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
- Me.DEFS_TWITTER.Size = New System.Drawing.Size(570, 366)
- Me.DEFS_TWITTER.TabIndex = 1
- '
- 'CH_TWITTER_USER_MEDIA
- '
- Me.CH_TWITTER_USER_MEDIA.AutoSize = True
- Me.CH_TWITTER_USER_MEDIA.Dock = System.Windows.Forms.DockStyle.Fill
- Me.CH_TWITTER_USER_MEDIA.Location = New System.Drawing.Point(4, 82)
- Me.CH_TWITTER_USER_MEDIA.Name = "CH_TWITTER_USER_MEDIA"
- Me.CH_TWITTER_USER_MEDIA.Size = New System.Drawing.Size(562, 19)
- Me.CH_TWITTER_USER_MEDIA.TabIndex = 3
- Me.CH_TWITTER_USER_MEDIA.Text = "Get user media only"
- Me.CH_TWITTER_USER_MEDIA.UseVisualStyleBackColor = True
- '
- 'TXT_REQ_WAIT_TIMER
- '
- Me.TXT_REQ_WAIT_TIMER.CaptionText = "Request timer"
- Me.TXT_REQ_WAIT_TIMER.CaptionWidth = 120.0R
- Me.TXT_REQ_WAIT_TIMER.Dock = System.Windows.Forms.DockStyle.Fill
- Me.TXT_REQ_WAIT_TIMER.Location = New System.Drawing.Point(4, 82)
- Me.TXT_REQ_WAIT_TIMER.Name = "TXT_REQ_WAIT_TIMER"
- Me.TXT_REQ_WAIT_TIMER.Size = New System.Drawing.Size(568, 22)
- Me.TXT_REQ_WAIT_TIMER.TabIndex = 3
- '
- 'TXT_REQ_COUNT
- '
- Me.TXT_REQ_COUNT.CaptionText = "Request timer counter"
- Me.TXT_REQ_COUNT.CaptionWidth = 120.0R
- Me.TXT_REQ_COUNT.Dock = System.Windows.Forms.DockStyle.Fill
- Me.TXT_REQ_COUNT.Location = New System.Drawing.Point(4, 111)
- Me.TXT_REQ_COUNT.Name = "TXT_REQ_COUNT"
- Me.TXT_REQ_COUNT.Size = New System.Drawing.Size(568, 22)
- Me.TXT_REQ_COUNT.TabIndex = 4
- '
- 'TXT_LIMIT_TIMER
- '
- Me.TXT_LIMIT_TIMER.CaptionText = "Posts limit timer"
- Me.TXT_LIMIT_TIMER.CaptionWidth = 120.0R
- Me.TXT_LIMIT_TIMER.Dock = System.Windows.Forms.DockStyle.Fill
- Me.TXT_LIMIT_TIMER.Location = New System.Drawing.Point(4, 140)
- Me.TXT_LIMIT_TIMER.Name = "TXT_LIMIT_TIMER"
- Me.TXT_LIMIT_TIMER.Size = New System.Drawing.Size(568, 22)
- Me.TXT_LIMIT_TIMER.TabIndex = 5
- '
'TAB_MAIN
'
Me.TAB_MAIN.Controls.Add(TAB_BASIS)
Me.TAB_MAIN.Controls.Add(TAB_DEFAULTS)
Me.TAB_MAIN.Controls.Add(TAB_DEFS_CHANNELS)
- Me.TAB_MAIN.Controls.Add(TAB_DEFS_REDDIT)
- Me.TAB_MAIN.Controls.Add(TAB_DEFS_TWITTER)
- Me.TAB_MAIN.Controls.Add(Me.TAB_DEFS_INSTAGRAM)
- Me.TAB_MAIN.Controls.Add(Me.TAB_DEFS_REDGIFS)
Me.TAB_MAIN.Dock = System.Windows.Forms.DockStyle.Fill
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, 398)
+ Me.TAB_MAIN.Size = New System.Drawing.Size(584, 454)
Me.TAB_MAIN.TabIndex = 1
'
- 'TAB_DEFS_INSTAGRAM
- '
- Me.TAB_DEFS_INSTAGRAM.BackColor = System.Drawing.SystemColors.Control
- Me.TAB_DEFS_INSTAGRAM.Controls.Add(Me.DEFS_INST)
- Me.TAB_DEFS_INSTAGRAM.Location = New System.Drawing.Point(4, 22)
- Me.TAB_DEFS_INSTAGRAM.Name = "TAB_DEFS_INSTAGRAM"
- Me.TAB_DEFS_INSTAGRAM.Size = New System.Drawing.Size(576, 372)
- Me.TAB_DEFS_INSTAGRAM.TabIndex = 5
- Me.TAB_DEFS_INSTAGRAM.Text = "Instagram"
- '
- 'DEFS_INST
- '
- Me.DEFS_INST.BaseControlsPadding = New System.Windows.Forms.Padding(120, 0, 0, 0)
- Me.DEFS_INST.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.[Single]
- Me.DEFS_INST.ColumnCount = 1
- Me.DEFS_INST.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
- Me.DEFS_INST.Controls.Add(Me.TXT_LIMIT_TIMER, 0, 5)
- Me.DEFS_INST.Controls.Add(Me.TXT_REQ_COUNT, 0, 4)
- Me.DEFS_INST.Controls.Add(Me.TXT_REQ_WAIT_TIMER, 0, 3)
- Me.DEFS_INST.Controls.Add(Me.TXT_INST_SAVED_POSTS_USER, 0, 6)
- Me.DEFS_INST.Controls.Add(Me.TXT_INST_SAVED_POSTS_PATH, 0, 7)
- Me.DEFS_INST.Dock = System.Windows.Forms.DockStyle.Fill
- Me.DEFS_INST.Location = New System.Drawing.Point(0, 0)
- Me.DEFS_INST.Name = "DEFS_INST"
- Me.DEFS_INST.RowCount = 9
- Me.DEFS_INST.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
- Me.DEFS_INST.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
- Me.DEFS_INST.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
- Me.DEFS_INST.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
- Me.DEFS_INST.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
- Me.DEFS_INST.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
- Me.DEFS_INST.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
- Me.DEFS_INST.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
- Me.DEFS_INST.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
- Me.DEFS_INST.Size = New System.Drawing.Size(576, 372)
- Me.DEFS_INST.TabIndex = 1
- '
- 'TXT_INST_SAVED_POSTS_USER
- '
- Me.TXT_INST_SAVED_POSTS_USER.CaptionText = "Saved posts user"
- Me.TXT_INST_SAVED_POSTS_USER.CaptionWidth = 120.0R
- Me.TXT_INST_SAVED_POSTS_USER.Dock = System.Windows.Forms.DockStyle.Fill
- Me.TXT_INST_SAVED_POSTS_USER.Location = New System.Drawing.Point(4, 169)
- Me.TXT_INST_SAVED_POSTS_USER.Name = "TXT_INST_SAVED_POSTS_USER"
- Me.TXT_INST_SAVED_POSTS_USER.Size = New System.Drawing.Size(568, 22)
- Me.TXT_INST_SAVED_POSTS_USER.TabIndex = 9
- '
- 'TXT_INST_SAVED_POSTS_PATH
- '
- ActionButton9.BackgroundImage = CType(resources.GetObject("ActionButton9.BackgroundImage"), System.Drawing.Image)
- ActionButton9.Index = 0
- ActionButton9.Name = "BTT_OPEN"
- ActionButton10.BackgroundImage = CType(resources.GetObject("ActionButton10.BackgroundImage"), System.Drawing.Image)
- ActionButton10.Index = 1
- ActionButton10.Name = "BTT_CLEAR"
- Me.TXT_INST_SAVED_POSTS_PATH.Buttons.Add(ActionButton9)
- Me.TXT_INST_SAVED_POSTS_PATH.Buttons.Add(ActionButton10)
- Me.TXT_INST_SAVED_POSTS_PATH.CaptionText = "Saved posts path"
- Me.TXT_INST_SAVED_POSTS_PATH.CaptionToolTipEnabled = True
- Me.TXT_INST_SAVED_POSTS_PATH.CaptionToolTipText = "Special path (clear to use default)"
- Me.TXT_INST_SAVED_POSTS_PATH.CaptionWidth = 120.0R
- Me.TXT_INST_SAVED_POSTS_PATH.Dock = System.Windows.Forms.DockStyle.Fill
- Me.TXT_INST_SAVED_POSTS_PATH.Location = New System.Drawing.Point(4, 198)
- Me.TXT_INST_SAVED_POSTS_PATH.Name = "TXT_INST_SAVED_POSTS_PATH"
- Me.TXT_INST_SAVED_POSTS_PATH.Size = New System.Drawing.Size(568, 22)
- Me.TXT_INST_SAVED_POSTS_PATH.TabIndex = 13
- '
- 'TAB_DEFS_REDGIFS
- '
- Me.TAB_DEFS_REDGIFS.BackColor = System.Drawing.SystemColors.Control
- Me.TAB_DEFS_REDGIFS.Controls.Add(Me.DEFS_REDGIFS)
- Me.TAB_DEFS_REDGIFS.Location = New System.Drawing.Point(4, 22)
- Me.TAB_DEFS_REDGIFS.Name = "TAB_DEFS_REDGIFS"
- Me.TAB_DEFS_REDGIFS.Size = New System.Drawing.Size(576, 372)
- Me.TAB_DEFS_REDGIFS.TabIndex = 6
- Me.TAB_DEFS_REDGIFS.Text = "RedGifs"
- '
- 'DEFS_REDGIFS
- '
- Me.DEFS_REDGIFS.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.[Single]
- Me.DEFS_REDGIFS.ColumnCount = 1
- Me.DEFS_REDGIFS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
- Me.DEFS_REDGIFS.Dock = System.Windows.Forms.DockStyle.Fill
- Me.DEFS_REDGIFS.Location = New System.Drawing.Point(0, 0)
- Me.DEFS_REDGIFS.Name = "DEFS_REDGIFS"
- Me.DEFS_REDGIFS.RowCount = 4
- Me.DEFS_REDGIFS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
- Me.DEFS_REDGIFS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
- Me.DEFS_REDGIFS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
- Me.DEFS_REDGIFS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
- Me.DEFS_REDGIFS.Size = New System.Drawing.Size(576, 372)
- Me.DEFS_REDGIFS.TabIndex = 0
- '
'CONTAINER_MAIN
'
'
'CONTAINER_MAIN.ContentPanel
'
Me.CONTAINER_MAIN.ContentPanel.Controls.Add(Me.TAB_MAIN)
- Me.CONTAINER_MAIN.ContentPanel.Size = New System.Drawing.Size(584, 398)
+ Me.CONTAINER_MAIN.ContentPanel.Size = New System.Drawing.Size(584, 454)
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, 398)
+ Me.CONTAINER_MAIN.Size = New System.Drawing.Size(584, 479)
Me.CONTAINER_MAIN.TabIndex = 0
Me.CONTAINER_MAIN.TopToolStripPanelVisible = False
'
+ 'CH_RECYCLE_DEL
+ '
+ Me.CH_RECYCLE_DEL.AutoSize = True
+ Me.CH_RECYCLE_DEL.Dock = System.Windows.Forms.DockStyle.Fill
+ Me.CH_RECYCLE_DEL.Location = New System.Drawing.Point(4, 399)
+ Me.CH_RECYCLE_DEL.Name = "CH_RECYCLE_DEL"
+ Me.CH_RECYCLE_DEL.Size = New System.Drawing.Size(562, 19)
+ Me.CH_RECYCLE_DEL.TabIndex = 14
+ Me.CH_RECYCLE_DEL.Text = "Delete data to recycle bin"
+ Me.CH_RECYCLE_DEL.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, 398)
+ Me.ClientSize = New System.Drawing.Size(584, 479)
Me.Controls.Add(Me.CONTAINER_MAIN)
Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle
Me.Icon = CType(resources.GetObject("$this.Icon"), System.Drawing.Icon)
Me.KeyPreview = True
Me.MaximizeBox = False
- Me.MaximumSize = New System.Drawing.Size(600, 437)
+ Me.MaximumSize = New System.Drawing.Size(600, 518)
Me.MinimizeBox = False
- Me.MinimumSize = New System.Drawing.Size(600, 437)
+ Me.MinimumSize = New System.Drawing.Size(600, 518)
Me.Name = "GlobalSettingsForm"
Me.ShowInTaskbar = False
Me.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide
@@ -997,6 +808,7 @@
TP_FILE_NAME.PerformLayout()
TP_FILE_PATTERNS.ResumeLayout(False)
TP_FILE_PATTERNS.PerformLayout()
+ CType(Me.TXT_FOLDER_CMD, System.ComponentModel.ISupportInitialize).EndInit()
TP_CHANNELS_IMGS.ResumeLayout(False)
CType(Me.TXT_CHANNELS_ROWS, System.ComponentModel.ISupportInitialize).EndInit()
CType(Me.TXT_CHANNELS_COLUMNS, System.ComponentModel.ISupportInitialize).EndInit()
@@ -1008,23 +820,7 @@
TP_CHANNELS.ResumeLayout(False)
TP_CHANNELS.PerformLayout()
CType(Me.TXT_CHANNEL_USER_POST_LIMIT, System.ComponentModel.ISupportInitialize).EndInit()
- TAB_DEFS_REDDIT.ResumeLayout(False)
- Me.DEFS_REDDIT.ResumeLayout(False)
- Me.DEFS_REDDIT.PerformLayout()
- CType(Me.TXT_REDDIT_SAVED_POSTS_USER, System.ComponentModel.ISupportInitialize).EndInit()
- CType(Me.TXT_REDDIT_SAVED_POSTS_PATH, System.ComponentModel.ISupportInitialize).EndInit()
- TAB_DEFS_TWITTER.ResumeLayout(False)
- Me.DEFS_TWITTER.ResumeLayout(False)
- Me.DEFS_TWITTER.PerformLayout()
- CType(Me.TXT_REQ_WAIT_TIMER, System.ComponentModel.ISupportInitialize).EndInit()
- CType(Me.TXT_REQ_COUNT, System.ComponentModel.ISupportInitialize).EndInit()
- CType(Me.TXT_LIMIT_TIMER, System.ComponentModel.ISupportInitialize).EndInit()
Me.TAB_MAIN.ResumeLayout(False)
- Me.TAB_DEFS_INSTAGRAM.ResumeLayout(False)
- Me.DEFS_INST.ResumeLayout(False)
- CType(Me.TXT_INST_SAVED_POSTS_USER, System.ComponentModel.ISupportInitialize).EndInit()
- CType(Me.TXT_INST_SAVED_POSTS_PATH, System.ComponentModel.ISupportInitialize).EndInit()
- Me.TAB_DEFS_REDGIFS.ResumeLayout(False)
Me.CONTAINER_MAIN.ContentPanel.ResumeLayout(False)
Me.CONTAINER_MAIN.ResumeLayout(False)
Me.CONTAINER_MAIN.PerformLayout()
@@ -1048,11 +844,8 @@
Private WithEvents CH_DOWN_VIDEOS As CheckBox
Private WithEvents CH_DOWN_IMAGES As CheckBox
Private WithEvents CH_DEF_TEMP As CheckBox
- Private WithEvents CH_TWITTER_USER_MEDIA As CheckBox
Private WithEvents CH_CHANNELS_USERS_TEMP As CheckBox
- Private WithEvents TAB_DEFS_INSTAGRAM As TabPage
Private WithEvents TXT_IMGUR_CLIENT_ID As PersonalUtilities.Forms.Controls.TextBoxExtended
- Private WithEvents TXT_REDDIT_SAVED_POSTS_USER As PersonalUtilities.Forms.Controls.TextBoxExtended
Private WithEvents OPT_FILE_NAME_REPLACE As RadioButton
Private WithEvents OPT_FILE_NAME_ADD_DATE As RadioButton
Private WithEvents CH_FILE_NAME_CHANGE As CheckBox
@@ -1062,19 +855,12 @@
Private WithEvents OPT_FILE_DATE_END As RadioButton
Private WithEvents CH_EXIT_CONFIRM As CheckBox
Private WithEvents CH_CLOSE_TO_TRAY As CheckBox
- Private WithEvents TXT_REQ_WAIT_TIMER As PersonalUtilities.Forms.Controls.TextBoxExtended
- Private WithEvents TXT_REQ_COUNT As PersonalUtilities.Forms.Controls.TextBoxExtended
- Private WithEvents TXT_LIMIT_TIMER As PersonalUtilities.Forms.Controls.TextBoxExtended
- Private WithEvents TAB_DEFS_REDGIFS As TabPage
Private WithEvents TAB_MAIN As TabControl
- Private WithEvents DEFS_TWITTER As SiteDefaults
- Private WithEvents DEFS_REDDIT As SiteDefaults
- Private WithEvents DEFS_INST As SiteDefaults
- Private WithEvents DEFS_REDGIFS As SiteDefaults
- Private WithEvents TXT_INST_SAVED_POSTS_USER As PersonalUtilities.Forms.Controls.TextBoxExtended
Private WithEvents CH_SHOW_NOTIFY As CheckBox
- Private WithEvents CH_REDDIT_USER_MEDIA As CheckBox
- Private WithEvents TXT_REDDIT_SAVED_POSTS_PATH As PersonalUtilities.Forms.Controls.TextBoxExtended
- Private WithEvents TXT_INST_SAVED_POSTS_PATH As PersonalUtilities.Forms.Controls.TextBoxExtended
+ Private WithEvents CH_COPY_CHANNEL_USER_IMAGE_ALL As CheckBox
+ Private WithEvents CH_UDESCR_UP As CheckBox
+ Private WithEvents CH_FAST_LOAD As CheckBox
+ Private WithEvents TXT_FOLDER_CMD As PersonalUtilities.Forms.Controls.TextBoxExtended
+ Private WithEvents CH_RECYCLE_DEL 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 11a5449..16e5e4e 100644
--- a/SCrawler/Editors/GlobalSettingsForm.resx
+++ b/SCrawler/Editors/GlobalSettingsForm.resx
@@ -206,6 +206,14 @@
17, 17
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
+ xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
+ tbbB43rK5xSAQq1VYFtmeQBoqZTSreVZvgTknM8yyyjA/qodsDF9gspD2Bj6B+DH+NqzhQQAG+POMnSX
+ AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC
+
+
This is a global setting for newly added users only.
This parameter specifies how the video will be stored in the users' download path.
@@ -229,50 +237,6 @@ If checked, videos will be stored in separate folder; otherwise, videos will be
False
-
- 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
-
-
-
- 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
-
-
AAABAA8AAAAQAAEABAAwOgAA9gAAADAwEAABAAQAaAYAACg7AAAgIBAAAQAEAOgCAACQQQAAGBgQAAEA
diff --git a/SCrawler/Editors/GlobalSettingsForm.vb b/SCrawler/Editors/GlobalSettingsForm.vb
index c4f25fd..05ec89e 100644
--- a/SCrawler/Editors/GlobalSettingsForm.vb
+++ b/SCrawler/Editors/GlobalSettingsForm.vb
@@ -12,38 +12,6 @@ Imports PersonalUtilities.Forms.Toolbars
Namespace Editors
Friend Class GlobalSettingsForm : Implements IOkCancelToolbar
Private ReadOnly MyDefs As DefaultFormProps(Of FieldsChecker)
-#Region "Checkers declarations"
- Private Class SavedPostsChecker : Implements ICustomProvider
- Private Function Convert(ByVal Value As Object, ByVal DestinationType As Type, ByVal Provider As IFormatProvider,
- Optional ByVal NothingArg As Object = Nothing, Optional ByVal e As ErrorsDescriber = Nothing) As Object Implements ICustomProvider.Convert
- If Not ACheck(Value) OrElse CStr(Value).Contains("/") Then
- Return Nothing
- Else
- Return Value
- End If
- End Function
- Private Function GetFormat(ByVal FormatType As Type) As Object Implements IFormatProvider.GetFormat
- Throw New NotImplementedException()
- End Function
- End Class
- Private Class InstaTimersChecker : Implements ICustomProvider
- Private ReadOnly _LowestValue As Integer
- Friend Sub New(ByVal LowestValue As Integer)
- _LowestValue = LowestValue
- End Sub
- Private Function Convert(ByVal Value As Object, ByVal DestinationType As Type, ByVal Provider As IFormatProvider,
- Optional ByVal NothingArg As Object = Nothing, Optional ByVal e As ErrorsDescriber = Nothing) As Object Implements ICustomProvider.Convert
- If ACheck(Of Integer)(Value) AndAlso CInt(Value) >= _LowestValue Then
- Return Value
- Else
- Return Nothing
- End If
- End Function
- Private Function GetFormat(ByVal FormatType As Type) As Object Implements IFormatProvider.GetFormat
- Throw New NotImplementedException()
- End Function
- End Class
-#End Region
Friend Sub New()
InitializeComponent()
MyDefs = New DefaultFormProps(Of FieldsChecker)
@@ -64,17 +32,24 @@ Namespace Editors
TXT_MAX_JOBS_CHANNELS.Value = .ChannelsMaxJobsCount.Value
CH_CHECK_VER_START.Checked = .CheckUpdatesAtStart
TXT_IMGUR_CLIENT_ID.Text = .ImgurClientID
+ CH_FAST_LOAD.Checked = .FastProfilesLoading
+ TXT_FOLDER_CMD.Text = .OpenFolderInOtherProgram
+ TXT_FOLDER_CMD.Checked = .OpenFolderInOtherProgram.Attribute
+ CH_RECYCLE_DEL.Checked = .DeleteToRecycleBin
'Defaults
CH_SEPARATE_VIDEO_FOLDER.Checked = .SeparateVideoFolder.Value
CH_DEF_TEMP.Checked = .DefaultTemporary
CH_DOWN_IMAGES.Checked = .DefaultDownloadImages
CH_DOWN_VIDEOS.Checked = .DefaultDownloadVideos
+ CH_UDESCR_UP.Checked = .UpdateUserDescriptionEveryTime
'Channels
TXT_CHANNELS_ROWS.Value = .ChannelsImagesRows.Value
TXT_CHANNELS_COLUMNS.Value = .ChannelsImagesColumns.Value
TXT_CHANNEL_USER_POST_LIMIT.Value = .FromChannelDownloadTop.Value
TXT_CHANNEL_USER_POST_LIMIT.Checked = .FromChannelDownloadTopUse.Value
CH_COPY_CHANNEL_USER_IMAGE.Checked = .FromChannelCopyImageToUser
+ CH_COPY_CHANNEL_USER_IMAGE_ALL.Checked = .ChannelsAddUserImagesFromAllChannels
+ CH_COPY_CHANNEL_USER_IMAGE_ALL.Enabled = CH_COPY_CHANNEL_USER_IMAGE.Checked
CH_CHANNELS_USERS_TEMP.Checked = .ChannelsDefaultTemporary
'Channels filenames
CH_FILE_NAME_CHANGE.Checked = .FileReplaceNameByDate Or .FileAddDateToFileName Or .FileAddTimeToFileName
@@ -88,38 +63,11 @@ Namespace Editors
CH_EXIT_CONFIRM.Checked = .ExitConfirm
CH_CLOSE_TO_TRAY.Checked = .CloseToTray
CH_SHOW_NOTIFY.Checked = .ShowNotifications
- 'Reddit
- With .Site(Sites.Reddit)
- SetChecker(DEFS_REDDIT, Sites.Reddit)
- CH_REDDIT_USER_MEDIA.Checked = .GetUserMediaOnly
- TXT_REDDIT_SAVED_POSTS_USER.Text = .SavedPostsUserName
- TXT_REDDIT_SAVED_POSTS_PATH.Text = .SavedPostsPath(False)
- End With
- 'Twitter
- With .Site(Sites.Twitter)
- SetChecker(DEFS_TWITTER, Sites.Twitter)
- CH_TWITTER_USER_MEDIA.Checked = .GetUserMediaOnly
- End With
- 'Instagram
- With .Site(Sites.Instagram)
- SetChecker(DEFS_INST, Sites.Instagram)
- TXT_REQ_WAIT_TIMER.Text = .RequestsWaitTimer
- TXT_REQ_COUNT.Text = .RequestsWaitTimerTaskCount
- TXT_LIMIT_TIMER.Text = .SleepTimerOnPostsLimit
- TXT_INST_SAVED_POSTS_USER.Text = .SavedPostsUserName
- TXT_INST_SAVED_POSTS_PATH.Text = .SavedPostsPath(False)
- End With
- 'RedGifs
- SetChecker(DEFS_REDGIFS, Sites.RedGifs)
End With
.MyFieldsChecker = New FieldsChecker
With .MyFieldsChecker
.AddControl(Of String)(TXT_GLOBAL_PATH, TXT_GLOBAL_PATH.CaptionText)
.AddControl(Of String)(TXT_COLLECTIONS_PATH, TXT_COLLECTIONS_PATH.CaptionText)
- .AddControl(Of String)(TXT_REDDIT_SAVED_POSTS_USER, TXT_REDDIT_SAVED_POSTS_USER.CaptionText, True, New SavedPostsChecker)
- .AddControl(Of Integer)(TXT_REQ_WAIT_TIMER, TXT_REQ_WAIT_TIMER.CaptionText,, New InstaTimersChecker(100))
- .AddControl(Of Integer)(TXT_REQ_COUNT, TXT_REQ_COUNT.CaptionText,, New InstaTimersChecker(1))
- .AddControl(Of Integer)(TXT_LIMIT_TIMER, TXT_LIMIT_TIMER.CaptionText,, New InstaTimersChecker(10000))
.EndLoaderOperations()
End With
.AppendDetectors()
@@ -130,35 +78,6 @@ Namespace Editors
MyDefs.InvokeLoaderError(ex)
End Try
End Sub
- Private Overloads Sub SetChecker(ByRef CH As SiteDefaults, ByVal s As Sites)
- With Settings(s)
- SetChecker(CH.MyTemporary, .Temporary)
- SetChecker(CH.MyImagesDown, .DownloadImages)
- SetChecker(CH.MyVideosDown, .DownloadVideos)
- End With
- End Sub
- Private Overloads Sub SetChecker(ByRef State As CheckState, ByVal Prop As XML.Base.XMLValue(Of Boolean))
- If Prop.ValueF.Exists Then
- State = If(Prop.Value, CheckState.Checked, CheckState.Unchecked)
- Else
- State = CheckState.Indeterminate
- End If
- End Sub
- Private Overloads Sub SetPropByChecker(ByRef CH As SiteDefaults, ByVal s As Sites)
- With Settings(s)
- SetPropByChecker(CH.MyTemporary, .Temporary)
- SetPropByChecker(CH.MyTemporary, .Temporary)
- SetPropByChecker(CH.MyImagesDown, .DownloadImages)
- SetPropByChecker(CH.MyVideosDown, .DownloadVideos)
- End With
- End Sub
- Private Overloads Sub SetPropByChecker(ByVal State As CheckState, ByRef Prop As XML.Base.XMLValue(Of Boolean))
- Select Case State
- Case CheckState.Checked : Prop.Value = True
- Case CheckState.Unchecked : Prop.Value = False
- Case CheckState.Indeterminate : Prop.ValueF = Nothing
- End Select
- End Sub
Private Sub ToolbarBttOK() Implements IOkCancelToolbar.ToolbarBttOK
If MyDefs.MyFieldsChecker.AllParamsOK Then
With Settings
@@ -190,6 +109,7 @@ Namespace Editors
End If
.BeginUpdate()
+
'Basis
.GlobalPath.Value = TXT_GLOBAL_PATH.Text
.MaxLargeImageHeigh.Value = CInt(TXT_IMAGE_LARGE.Value)
@@ -199,17 +119,23 @@ Namespace Editors
.ChannelsMaxJobsCount.Value = TXT_MAX_JOBS_CHANNELS.Value
.CheckUpdatesAtStart.Value = CH_CHECK_VER_START.Checked
.ImgurClientID.Value = TXT_IMGUR_CLIENT_ID.Text
+ .FastProfilesLoading.Value = CH_FAST_LOAD.Checked
+ .OpenFolderInOtherProgram.Value = TXT_FOLDER_CMD.Text
+ .OpenFolderInOtherProgram.Attribute.Value = TXT_FOLDER_CMD.Checked
+ .DeleteToRecycleBin.Value = CH_RECYCLE_DEL.Checked
'Defaults
.SeparateVideoFolder.Value = CH_SEPARATE_VIDEO_FOLDER.Checked
.DefaultTemporary.Value = CH_DEF_TEMP.Checked
.DefaultDownloadImages.Value = CH_DOWN_IMAGES.Checked
.DefaultDownloadVideos.Value = CH_DOWN_VIDEOS.Checked
+ .UpdateUserDescriptionEveryTime.Value = CH_UDESCR_UP.Checked
'Channels
.ChannelsImagesRows.Value = CInt(TXT_CHANNELS_ROWS.Value)
.ChannelsImagesColumns.Value = CInt(TXT_CHANNELS_COLUMNS.Value)
.FromChannelDownloadTop.Value = CInt(TXT_CHANNEL_USER_POST_LIMIT.Value)
.FromChannelDownloadTopUse.Value = TXT_CHANNEL_USER_POST_LIMIT.Checked
.FromChannelCopyImageToUser.Value = CH_COPY_CHANNEL_USER_IMAGE.Checked
+ .ChannelsAddUserImagesFromAllChannels.Value = CH_COPY_CHANNEL_USER_IMAGE_ALL.Checked
.ChannelsDefaultTemporary.Value = CH_CHANNELS_USERS_TEMP.Checked
'Other program settings
.ExitConfirm.Value = CH_EXIT_CONFIRM.Checked
@@ -226,29 +152,6 @@ Namespace Editors
.FileAddTimeToFileName.Value = False
.FileReplaceNameByDate.Value = False
End If
- 'Reddit
- With .Site(Sites.Reddit)
- SetPropByChecker(DEFS_REDDIT, Sites.Reddit)
- .GetUserMediaOnly.Value = CH_REDDIT_USER_MEDIA.Checked
- .SavedPostsUserName.Value = TXT_REDDIT_SAVED_POSTS_USER.Text
- .SavedPostsPath = TXT_REDDIT_SAVED_POSTS_PATH.Text
- End With
- 'Twitter
- With .Site(Sites.Twitter)
- SetPropByChecker(DEFS_TWITTER, Sites.Twitter)
- .GetUserMediaOnly.Value = CH_TWITTER_USER_MEDIA.Checked
- End With
- 'Instagram
- With .Site(Sites.Instagram)
- SetPropByChecker(DEFS_INST, Sites.Instagram)
- .RequestsWaitTimer.Value = AConvert(Of Integer)(TXT_REQ_WAIT_TIMER.Text)
- .RequestsWaitTimerTaskCount.Value = AConvert(Of Integer)(TXT_REQ_COUNT.Text)
- .SleepTimerOnPostsLimit.Value = AConvert(Of Integer)(TXT_LIMIT_TIMER.Text)
- .SavedPostsUserName.Value = TXT_INST_SAVED_POSTS_USER.Text
- .SavedPostsPath = TXT_INST_SAVED_POSTS_PATH.Text
- End With
- 'RedGifs
- SetPropByChecker(DEFS_REDGIFS, Sites.RedGifs)
.EndUpdate()
End With
@@ -292,17 +195,8 @@ Namespace Editors
CH_FILE_TIME.Enabled = b
ChangePositionControlsEnabling()
End Sub
- Private Sub TXT_REDDIT_SAVED_POSTS_PATH_ActionOnButtonClick(ByVal Sender As ActionButton) Handles TXT_REDDIT_SAVED_POSTS_PATH.ActionOnButtonClick
- If Sender.DefaultButton = ActionButton.DefaultButtons.Open Then
- Dim f As SFile = SFile.SelectPath
- If Not f.IsEmptyString Then TXT_REDDIT_SAVED_POSTS_PATH.Text = f
- End If
- End Sub
- Private Sub TXT_INST_SAVED_POSTS_PATH_ActionOnButtonClick(ByVal Sender As ActionButton) Handles TXT_INST_SAVED_POSTS_PATH.ActionOnButtonClick
- If Sender.DefaultButton = ActionButton.DefaultButtons.Open Then
- Dim f As SFile = SFile.SelectPath
- If Not f.IsEmptyString Then TXT_INST_SAVED_POSTS_PATH.Text = f
- End If
+ Private Sub CH_COPY_CHANNEL_USER_IMAGE_CheckedChanged(sender As Object, e As EventArgs) Handles CH_COPY_CHANNEL_USER_IMAGE.CheckedChanged
+ CH_COPY_CHANNEL_USER_IMAGE_ALL.Enabled = CH_COPY_CHANNEL_USER_IMAGE.Checked
End Sub
End Class
End Namespace
\ No newline at end of file
diff --git a/SCrawler/Editors/LabelsForm.vb b/SCrawler/Editors/LabelsForm.vb
index f66c1bc..18ec7f7 100644
--- a/SCrawler/Editors/LabelsForm.vb
+++ b/SCrawler/Editors/LabelsForm.vb
@@ -81,7 +81,7 @@ Friend Class LabelsForm : Implements IOkCancelToolbar
If Sender.DefaultButton = ActionButton.DefaultButtons.Add Then AddNewLabel()
End Sub
Private Sub CMB_LABELS_ActionOnButtonClearClick() Handles CMB_LABELS.ActionOnButtonClearClick
- CMB_LABELS.Clear(ComboBoxExtended.ClearMode.CheckedInexes)
+ CMB_LABELS.Clear(ComboBoxExtended.ClearMode.CheckedIndexes)
End Sub
Private Sub AddNewLabel()
Dim nl$ = InputBoxE("Enter new label name:", "New label")
diff --git a/SCrawler/Editors/SiteDefaults.vb b/SCrawler/Editors/SiteDefaults.vb
index 3342afa..cfa62dc 100644
--- a/SCrawler/Editors/SiteDefaults.vb
+++ b/SCrawler/Editors/SiteDefaults.vb
@@ -7,6 +7,7 @@
' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY
Imports System.ComponentModel
+Imports SCrawler.Plugin.Hosts
Namespace Editors
Public Class SiteDefaults : Inherits TableLayoutPanel
Private ReadOnly CH_TEMP As CheckBox
@@ -99,4 +100,32 @@ Namespace Editors
End Set
End Property
End Class
+ Friend NotInheritable Class SiteDefaultsFunctions
+ Private Sub New()
+ End Sub
+ Friend Overloads Shared Sub SetChecker(ByRef CH As SiteDefaults, ByRef h As SettingsHost)
+ SetChecker(CH.MyTemporary, h.Temporary)
+ SetChecker(CH.MyImagesDown, h.DownloadImages)
+ SetChecker(CH.MyVideosDown, h.DownloadVideos)
+ End Sub
+ Private Overloads Shared Sub SetChecker(ByRef State As CheckState, ByVal Prop As XML.Base.XMLValue(Of Boolean))
+ If Prop.ValueF.Exists Then
+ State = If(Prop.Value, CheckState.Checked, CheckState.Unchecked)
+ Else
+ State = CheckState.Indeterminate
+ End If
+ End Sub
+ Friend Overloads Shared Sub SetPropByChecker(ByRef CH As SiteDefaults, ByRef h As SettingsHost)
+ SetPropByChecker(CH.MyTemporary, h.Temporary)
+ SetPropByChecker(CH.MyImagesDown, h.DownloadImages)
+ SetPropByChecker(CH.MyVideosDown, h.DownloadVideos)
+ End Sub
+ Private Overloads Shared Sub SetPropByChecker(ByVal State As CheckState, ByRef Prop As XML.Base.XMLValue(Of Boolean))
+ Select Case State
+ Case CheckState.Checked : Prop.Value = True
+ Case CheckState.Unchecked : Prop.Value = False
+ Case CheckState.Indeterminate : Prop.ValueF = Nothing
+ End Select
+ End Sub
+ End Class
End Namespace
\ No newline at end of file
diff --git a/SCrawler/Editors/SiteEditorForm.Designer.vb b/SCrawler/Editors/SiteEditorForm.Designer.vb
index 3064a4e..8831353 100644
--- a/SCrawler/Editors/SiteEditorForm.Designer.vb
+++ b/SCrawler/Editors/SiteEditorForm.Designer.vb
@@ -24,14 +24,14 @@
Me.TP_MAIN = New System.Windows.Forms.TableLayoutPanel()
Me.TXT_PATH = New PersonalUtilities.Forms.Controls.TextBoxExtended()
Me.TXT_COOKIES = New PersonalUtilities.Forms.Controls.TextBoxExtended()
- Me.TXT_TOKEN = New PersonalUtilities.Forms.Controls.TextBoxExtended()
- Me.TXT_AUTH = New PersonalUtilities.Forms.Controls.TextBoxExtended()
+ Me.TP_SITE_PROPS = New SCrawler.Editors.SiteDefaults()
+ Me.TXT_PATH_SAVED_POSTS = New PersonalUtilities.Forms.Controls.TextBoxExtended()
+ Me.CH_GET_USER_MEDIA_ONLY = New System.Windows.Forms.CheckBox()
Me.CONTAINER_MAIN = New System.Windows.Forms.ToolStripContainer()
Me.TP_MAIN.SuspendLayout()
CType(Me.TXT_PATH, System.ComponentModel.ISupportInitialize).BeginInit()
CType(Me.TXT_COOKIES, System.ComponentModel.ISupportInitialize).BeginInit()
- CType(Me.TXT_TOKEN, System.ComponentModel.ISupportInitialize).BeginInit()
- CType(Me.TXT_AUTH, System.ComponentModel.ISupportInitialize).BeginInit()
+ CType(Me.TXT_PATH_SAVED_POSTS, System.ComponentModel.ISupportInitialize).BeginInit()
Me.CONTAINER_MAIN.ContentPanel.SuspendLayout()
Me.CONTAINER_MAIN.SuspendLayout()
Me.SuspendLayout()
@@ -40,20 +40,21 @@
'
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(Me.TXT_PATH, 0, 0)
- Me.TP_MAIN.Controls.Add(Me.TXT_COOKIES, 0, 1)
- Me.TP_MAIN.Controls.Add(Me.TXT_TOKEN, 0, 2)
- Me.TP_MAIN.Controls.Add(Me.TXT_AUTH, 0, 3)
+ Me.TP_MAIN.Controls.Add(Me.TXT_COOKIES, 0, 2)
+ Me.TP_MAIN.Controls.Add(Me.TP_SITE_PROPS, 0, 4)
+ Me.TP_MAIN.Controls.Add(Me.TXT_PATH_SAVED_POSTS, 0, 1)
+ Me.TP_MAIN.Controls.Add(Me.CH_GET_USER_MEDIA_ONLY, 0, 3)
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 = 4
- Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25.0!))
- Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25.0!))
- Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25.0!))
- Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25.0!))
- Me.TP_MAIN.Size = New System.Drawing.Size(544, 132)
+ Me.TP_MAIN.RowCount = 5
+ 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!))
+ 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(544, 219)
Me.TP_MAIN.TabIndex = 0
'
'TXT_PATH
@@ -88,42 +89,57 @@
Me.TXT_COOKIES.CaptionText = "Cookies"
Me.TXT_COOKIES.ClearTextByButtonClear = False
Me.TXT_COOKIES.Dock = System.Windows.Forms.DockStyle.Fill
- Me.TXT_COOKIES.Location = New System.Drawing.Point(3, 36)
+ Me.TXT_COOKIES.Location = New System.Drawing.Point(3, 59)
Me.TXT_COOKIES.Name = "TXT_COOKIES"
Me.TXT_COOKIES.Size = New System.Drawing.Size(538, 22)
- Me.TXT_COOKIES.TabIndex = 1
+ Me.TXT_COOKIES.TabIndex = 2
Me.TXT_COOKIES.TextBoxReadOnly = True
'
- 'TXT_TOKEN
+ 'TP_SITE_PROPS
+ '
+ Me.TP_SITE_PROPS.BaseControlsPadding = New System.Windows.Forms.Padding(97, 0, 0, 0)
+ Me.TP_SITE_PROPS.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.[Single]
+ Me.TP_SITE_PROPS.ColumnCount = 1
+ Me.TP_SITE_PROPS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
+ Me.TP_SITE_PROPS.Dock = System.Windows.Forms.DockStyle.Fill
+ Me.TP_SITE_PROPS.Location = New System.Drawing.Point(3, 112)
+ Me.TP_SITE_PROPS.Name = "TP_SITE_PROPS"
+ Me.TP_SITE_PROPS.RowCount = 4
+ Me.TP_SITE_PROPS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
+ Me.TP_SITE_PROPS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
+ Me.TP_SITE_PROPS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
+ Me.TP_SITE_PROPS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
+ Me.TP_SITE_PROPS.Size = New System.Drawing.Size(538, 104)
+ Me.TP_SITE_PROPS.TabIndex = 4
+ '
+ 'TXT_PATH_SAVED_POSTS
'
ActionButton5.BackgroundImage = CType(resources.GetObject("ActionButton5.BackgroundImage"), System.Drawing.Image)
ActionButton5.Index = 0
- ActionButton5.Name = "BTT_CLEAR"
- Me.TXT_TOKEN.Buttons.Add(ActionButton5)
- Me.TXT_TOKEN.CaptionText = "Token"
- Me.TXT_TOKEN.CaptionToolTipEnabled = True
- Me.TXT_TOKEN.CaptionToolTipText = "Set token from [x-csrf-token] response header"
- Me.TXT_TOKEN.Dock = System.Windows.Forms.DockStyle.Fill
- Me.TXT_TOKEN.Location = New System.Drawing.Point(3, 69)
- Me.TXT_TOKEN.Name = "TXT_TOKEN"
- Me.TXT_TOKEN.Size = New System.Drawing.Size(538, 22)
- Me.TXT_TOKEN.TabIndex = 2
- '
- 'TXT_AUTH
- '
+ ActionButton5.Name = "BTT_OPEN"
ActionButton6.BackgroundImage = CType(resources.GetObject("ActionButton6.BackgroundImage"), System.Drawing.Image)
- ActionButton6.Index = 0
+ ActionButton6.Index = 1
ActionButton6.Name = "BTT_CLEAR"
- Me.TXT_AUTH.Buttons.Add(ActionButton6)
- Me.TXT_AUTH.CaptionText = "Authorization"
- Me.TXT_AUTH.CaptionToolTipEnabled = True
- Me.TXT_AUTH.CaptionToolTipText = "Set authorization from [authorization] response header. This field must start fro" &
- "m [Bearer] key word"
- Me.TXT_AUTH.Dock = System.Windows.Forms.DockStyle.Fill
- Me.TXT_AUTH.Location = New System.Drawing.Point(3, 102)
- Me.TXT_AUTH.Name = "TXT_AUTH"
- Me.TXT_AUTH.Size = New System.Drawing.Size(538, 22)
- Me.TXT_AUTH.TabIndex = 3
+ Me.TXT_PATH_SAVED_POSTS.Buttons.Add(ActionButton5)
+ Me.TXT_PATH_SAVED_POSTS.Buttons.Add(ActionButton6)
+ Me.TXT_PATH_SAVED_POSTS.CaptionText = "Saved posts path"
+ Me.TXT_PATH_SAVED_POSTS.Dock = System.Windows.Forms.DockStyle.Fill
+ Me.TXT_PATH_SAVED_POSTS.Location = New System.Drawing.Point(3, 31)
+ Me.TXT_PATH_SAVED_POSTS.Name = "TXT_PATH_SAVED_POSTS"
+ Me.TXT_PATH_SAVED_POSTS.Size = New System.Drawing.Size(538, 22)
+ Me.TXT_PATH_SAVED_POSTS.TabIndex = 1
+ '
+ 'CH_GET_USER_MEDIA_ONLY
+ '
+ Me.CH_GET_USER_MEDIA_ONLY.AutoSize = True
+ Me.CH_GET_USER_MEDIA_ONLY.Dock = System.Windows.Forms.DockStyle.Fill
+ Me.CH_GET_USER_MEDIA_ONLY.Location = New System.Drawing.Point(3, 87)
+ Me.CH_GET_USER_MEDIA_ONLY.Name = "CH_GET_USER_MEDIA_ONLY"
+ Me.CH_GET_USER_MEDIA_ONLY.Padding = New System.Windows.Forms.Padding(100, 0, 0, 0)
+ Me.CH_GET_USER_MEDIA_ONLY.Size = New System.Drawing.Size(538, 19)
+ Me.CH_GET_USER_MEDIA_ONLY.TabIndex = 3
+ Me.CH_GET_USER_MEDIA_ONLY.Text = "Get user media only"
+ Me.CH_GET_USER_MEDIA_ONLY.UseVisualStyleBackColor = True
'
'CONTAINER_MAIN
'
@@ -131,13 +147,13 @@
'CONTAINER_MAIN.ContentPanel
'
Me.CONTAINER_MAIN.ContentPanel.Controls.Add(Me.TP_MAIN)
- Me.CONTAINER_MAIN.ContentPanel.Size = New System.Drawing.Size(544, 132)
+ Me.CONTAINER_MAIN.ContentPanel.Size = New System.Drawing.Size(544, 219)
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(544, 132)
+ Me.CONTAINER_MAIN.Size = New System.Drawing.Size(544, 219)
Me.CONTAINER_MAIN.TabIndex = 0
Me.CONTAINER_MAIN.TopToolStripPanelVisible = False
'
@@ -145,23 +161,23 @@
'
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
- Me.ClientSize = New System.Drawing.Size(544, 132)
+ Me.ClientSize = New System.Drawing.Size(544, 219)
Me.Controls.Add(Me.CONTAINER_MAIN)
Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle
Me.KeyPreview = True
Me.MaximizeBox = False
- Me.MaximumSize = New System.Drawing.Size(560, 171)
+ Me.MaximumSize = New System.Drawing.Size(560, 258)
Me.MinimizeBox = False
- Me.MinimumSize = New System.Drawing.Size(560, 171)
+ Me.MinimumSize = New System.Drawing.Size(560, 258)
Me.Name = "SiteEditorForm"
Me.ShowInTaskbar = False
Me.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide
Me.Text = "Site"
Me.TP_MAIN.ResumeLayout(False)
+ Me.TP_MAIN.PerformLayout()
CType(Me.TXT_PATH, System.ComponentModel.ISupportInitialize).EndInit()
CType(Me.TXT_COOKIES, System.ComponentModel.ISupportInitialize).EndInit()
- CType(Me.TXT_TOKEN, System.ComponentModel.ISupportInitialize).EndInit()
- CType(Me.TXT_AUTH, System.ComponentModel.ISupportInitialize).EndInit()
+ CType(Me.TXT_PATH_SAVED_POSTS, System.ComponentModel.ISupportInitialize).EndInit()
Me.CONTAINER_MAIN.ContentPanel.ResumeLayout(False)
Me.CONTAINER_MAIN.ResumeLayout(False)
Me.CONTAINER_MAIN.PerformLayout()
@@ -172,8 +188,9 @@
Private WithEvents CONTAINER_MAIN As ToolStripContainer
Private WithEvents TXT_PATH As PersonalUtilities.Forms.Controls.TextBoxExtended
Private WithEvents TXT_COOKIES As PersonalUtilities.Forms.Controls.TextBoxExtended
- Private WithEvents TXT_TOKEN As PersonalUtilities.Forms.Controls.TextBoxExtended
- Private WithEvents TXT_AUTH As PersonalUtilities.Forms.Controls.TextBoxExtended
Private WithEvents TP_MAIN As TableLayoutPanel
+ Private WithEvents TXT_PATH_SAVED_POSTS As PersonalUtilities.Forms.Controls.TextBoxExtended
+ Private WithEvents TP_SITE_PROPS As SiteDefaults
+ Private WithEvents CH_GET_USER_MEDIA_ONLY As CheckBox
End Class
End Namespace
\ No newline at end of file
diff --git a/SCrawler/Editors/SiteEditorForm.resx b/SCrawler/Editors/SiteEditorForm.resx
index 42ac1a2..4fea2ed 100644
--- a/SCrawler/Editors/SiteEditorForm.resx
+++ b/SCrawler/Editors/SiteEditorForm.resx
@@ -206,9 +206,12 @@
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
- xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
- tbbB43rK5xSAQq1VYFtmeQBoqZTSreVZvgTknM8yyyjA/qodsDF9gspD2Bj6B+DH+NqzhQQAG+POMnSX
- AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC
+ wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP
+ WQwhyWIyJIUW5NqyPb7oCVtIlhVTwYf8nv7/t2zJagel9KmqKsIACYL9RjI8UHz5zshougZr/AEvbxEP
+ aZCDBY3VslixaJvX3wzkkDiOwbZtDRGA5vdNAg+TL27qgmt5XkBG/gTdAG7Gt+3PP9oOaEGFCVEC6rp+
+ 5g9MfM/c5e4OsEZMZkQEtGL5H2DdZ5JRArDwPA+iKII0TfkC9vroC9j5vq8JTWw3WzWgLMtZGIaa0MR8
+ vlAD8PYlSaIJTTiOowY0p0Bc19XEJo6HE59FAPuMzyAINKGJ1XLFZxHALtMrnkBXOIQIIIQ8YvF/KrgB
+ cMaRN0UdBBkAAAAASUVORK5CYII=
diff --git a/SCrawler/Editors/SiteEditorForm.vb b/SCrawler/Editors/SiteEditorForm.vb
index 894bd1b..e28a49f 100644
--- a/SCrawler/Editors/SiteEditorForm.vb
+++ b/SCrawler/Editors/SiteEditorForm.vb
@@ -11,142 +11,221 @@ Imports PersonalUtilities.Forms.Controls
Imports PersonalUtilities.Forms.Controls.Base
Imports PersonalUtilities.Forms.Toolbars
Imports PersonalUtilities.Tools.WEB
+Imports SCrawler.Plugin
+Imports SCrawler.Plugin.Hosts
Namespace Editors
Friend Class SiteEditorForm : Implements IOkCancelToolbar
+ Private ReadOnly LBL_AUTH As Label
+ Private ReadOnly LBL_OTHER As Label
Private ReadOnly MyDefs As DefaultFormProps(Of FieldsChecker)
- Private ReadOnly MySite As Sites
- Friend Sub New(ByVal s As Sites)
+ Private SpecialButton As Button
+#Region "Providers"
+ Private Class SavedPostsChecker : Implements ICustomProvider
+ Private Function Convert(ByVal Value As Object, ByVal DestinationType As Type, ByVal Provider As IFormatProvider,
+ Optional ByVal NothingArg As Object = Nothing, Optional ByVal e As ErrorsDescriber = Nothing) As Object Implements ICustomProvider.Convert
+ If Not ACheck(Value) OrElse CStr(Value).Contains("/") Then
+ Return Nothing
+ Else
+ Return Value
+ End If
+ End Function
+ Private Function GetFormat(ByVal FormatType As Type) As Object Implements IFormatProvider.GetFormat
+ Throw New NotImplementedException()
+ End Function
+ End Class
+#End Region
+ Private ReadOnly Property Host As SettingsHost
+ Friend Sub New(ByVal h As SettingsHost)
InitializeComponent()
- MySite = s
MyDefs = New DefaultFormProps(Of FieldsChecker)
+ Host = h
+ LBL_AUTH = New Label With {.Text = "Authorization", .TextAlign = ContentAlignment.MiddleCenter, .Dock = DockStyle.Fill}
+ LBL_OTHER = New Label With {.Text = "Other Parameters", .TextAlign = ContentAlignment.MiddleCenter, .Dock = DockStyle.Fill}
End Sub
Private Sub SiteEditorForm_Load(sender As Object, e As EventArgs) Handles Me.Load
+ Const LBorder% = 3
+ Const DOffset% = 100
Try
With MyDefs
.MyViewInitialize(Me, Settings.Design, True)
.AddOkCancelToolbar()
.DelegateClosingChecker()
- Select Case MySite
- Case Sites.Reddit : Icon = My.Resources.RedditIcon
- Case Sites.Twitter : Icon = My.Resources.TwitterIcon
- Case Sites.Instagram : Icon = My.Resources.InstagramIcon
- Case Else : ShowIcon = False
- End Select
- Text = MySite.ToString
-
- With Settings(MySite)
- TXT_PATH.Text = .Path(False)
- With .Responser
- If .Cookies Is Nothing Then .Cookies = New CookieKeeper(.CookiesDomain)
- SetCookieText()
- If MySite = Sites.Twitter Then
- TXT_TOKEN.Text = .Headers(API.Base.SiteSettings.Header_Twitter_Token)
- TXT_AUTH.Text = .Headers(API.Base.SiteSettings.Header_Twitter_Authorization)
- End If
- End With
- If MySite = Sites.Instagram Then
- TXT_TOKEN.Text = .InstaHash
- TXT_AUTH.Text = .InstaHash_SP
- End If
- End With
-
- If MySite = Sites.Twitter Or MySite = Sites.Instagram Then
- If MySite = Sites.Instagram Then
- TXT_TOKEN.CaptionText = "Hash"
- TXT_TOKEN.CaptionToolTipText = "Instagram session hash"
- TXT_TOKEN.Buttons.Clear()
- TXT_TOKEN.Buttons.AddRange({ActionButton.DefaultButtons.Refresh, ActionButton.DefaultButtons.Clear})
- TXT_AUTH.CaptionText = "Hash 2"
- TXT_AUTH.CaptionToolTipText = "Instagram session hash for saved posts"
- End If
- Else
- TXT_AUTH.Visible = False
- TXT_TOKEN.Visible = False
- Dim p As PaddingE = PaddingE.GetOf({TP_MAIN})
- Dim s As New Size(Size.Width, Size.Height - p.Vertical(2) - TXT_AUTH.NeededHeight - TXT_TOKEN.NeededHeight)
- With TP_MAIN
- .RowStyles(2).Height = 0
- .RowStyles(3).Height = 0
- End With
- MinimumSize = s
- Size = s
- MaximumSize = s
- End If
.MyFieldsChecker = New FieldsChecker
- With .MyFieldsChecker
- If MySite = Sites.Twitter Or MySite = Sites.Instagram Then
- .AddControl(Of String)(TXT_TOKEN, TXT_TOKEN.CaptionText)
- .AddControl(Of String)(TXT_AUTH, TXT_AUTH.CaptionText, MySite = Sites.Instagram)
+ With Host
+ With .Source
+ Text = .Site
+ If Not .Icon Is Nothing Then Icon = .Icon Else ShowIcon = False
+ End With
+
+ SetCookieText()
+
+ TXT_PATH.Text = .Path(False)
+ TXT_PATH_SAVED_POSTS.Text = .SavedPostsPath(False)
+ CH_GET_USER_MEDIA_ONLY.Checked = .GetUserMediaOnly.Value
+
+ SiteDefaultsFunctions.SetChecker(TP_SITE_PROPS, Host)
+
+ With MyDefs.MyFieldsChecker
+ .AddControl(Of String)(TXT_PATH, TXT_PATH.CaptionText, True, New SavedPostsChecker)
+ .AddControl(Of String)(TXT_PATH_SAVED_POSTS, TXT_PATH_SAVED_POSTS.CaptionText, True, New SavedPostsChecker)
+ End With
+
+ If .PropList.Count > 0 Then
+ Dim offset% = DOffset
+ Dim h% = 0, c% = 0
+ Dim laAdded As Boolean = False
+ Dim loAdded As Boolean = False
+ If Not Host.IsMyClass Then
+ h -= 28
+ TXT_COOKIES.Enabled = False
+ TXT_COOKIES.Visible = False
+ TP_MAIN.RowStyles(2).Height = 0
+ End If
+ Dim AddTpControl As Action(Of Control, Integer) = Sub(ByVal cnt As Control, ByVal _height As Integer)
+ TP_SITE_PROPS.RowStyles.Add(New RowStyle(SizeType.Absolute, _height))
+ TP_SITE_PROPS.RowCount += 1
+ TP_SITE_PROPS.Controls.Add(cnt, 0, TP_SITE_PROPS.RowStyles.Count - 1)
+ h += _height
+ c += 1
+ End Sub
+ Dim pArr() As Boolean
+ If .PropList.Exists(Function(p) If(p.Options?.IsAuth, False)) Then pArr = {True, False} Else pArr = {False}
+ .PropList.Sort()
+ For Each pAuth As Boolean In pArr
+ For Each prop As PropertyValueHost In .PropList
+ If Not prop.Options Is Nothing Then
+ With prop
+ If .Options.IsAuth = pAuth Then
+
+ If pArr.Length = 2 Then
+ Select Case pAuth
+ Case True
+ If Not laAdded Then AddTpControl(LBL_AUTH, 25) : laAdded = True
+ Case False
+ If Not loAdded Then AddTpControl(LBL_OTHER, 25) : loAdded = True
+ End Select
+ End If
+
+ .CreateControl()
+ AddTpControl(.Control, .ControlHeight)
+ If .Options.LeftOffset > offset Then offset = .Options.LeftOffset
+ If Not .Options.AllowNull Or Not .ProviderFieldsChecker Is Nothing Then
+ MyDefs.MyFieldsChecker.AddControl(.Control, .Options.ControlText, .Type, .Options.AllowNull, .ProviderFieldsChecker)
+ End If
+ End If
+ End With
+ End If
+ Next
+ Next
+ SpecialButton = .GetSettingsButtonInternal
+ If Not SpecialButton Is Nothing Then AddTpControl(SpecialButton, 28)
+ offset -= LBorder
+ TP_SITE_PROPS.BaseControlsPadding = New Padding(offset, 0, 0, 0)
+ If offset > DOffset - LBorder Then
+ TXT_PATH.CaptionWidth = offset
+ TXT_PATH_SAVED_POSTS.CaptionWidth = offset
+ TXT_COOKIES.CaptionWidth = offset
+ End If
+ If c > 0 Or Not Host.IsMyClass Then
+ Dim ss As New Size(Size.Width, Size.Height + h + c)
+ MinimumSize = ss
+ Size = ss
+ MaximumSize = ss
+ End If
End If
- .EndLoaderOperations()
End With
- TextBoxExtended.SetFalseDetector(Me, True, AddressOf .Detector)
+
+ .MyFieldsChecker.EndLoaderOperations()
+ .AppendDetectors()
.EndLoaderOperations()
End With
Catch ex As Exception
MyDefs.InvokeLoaderError(ex)
End Try
End Sub
+ Private Sub SiteEditorForm_Disposed(sender As Object, e As EventArgs) Handles Me.Disposed
+ If Host.PropList.Count > 0 Then Host.PropList.ForEach(Sub(p) p.DisposeControl())
+ If Not SpecialButton Is Nothing Then SpecialButton.Dispose()
+ LBL_AUTH.Dispose()
+ LBL_OTHER.Dispose()
+ End Sub
Private Sub ToolbarBttOK() Implements IOkCancelToolbar.ToolbarBttOK
If MyDefs.MyFieldsChecker.AllParamsOK Then
- If MySite = Sites.Instagram Then
- If Not TXT_TOKEN.IsEmptyString AndAlso Not TXT_AUTH.IsEmptyString AndAlso TXT_TOKEN.Text = TXT_AUTH.Text Then
- MsgBoxE({"InstaHash for saved posts must be different from InstaHash!", "InstaHash are equal"}, vbCritical)
- Exit Sub
+ Dim i%, ii%
+ With Host
+ Dim indxList As New List(Of Integer)
+ For i = 0 To .PropList.Count - 1
+ If .PropList(i).PropertiesChecking.ListExists And Not .PropList(i).PropertiesCheckingMethod Is Nothing Then indxList.Add(i)
+ Next
+ If indxList.Count > 0 Then
+ Dim pList As New List(Of PropertyData)
+ Dim n$()
+ For i = 0 To indxList.Count - 1
+ n = .PropList(indxList(i)).PropertiesChecking
+ For ii = 0 To .PropList.Count - 1
+ With .PropList(ii)
+ If n.Contains(.Name) Then pList.Add(New PropertyData(.Name, .GetControlValue))
+ End With
+ Next
+ If pList.Count > 0 AndAlso Not CBool(.PropList(indxList(i)).PropertiesCheckingMethod.Invoke(.Source, {pList})) Then Exit Sub
+ Next
End If
- End If
- With Settings(MySite)
- If TXT_PATH.IsEmptyString Then .Path = Nothing Else .Path = TXT_PATH.Text
- Select Case MySite
- Case Sites.Twitter
- With .Responser
- .Headers(API.Base.SiteSettings.Header_Twitter_Token) = TXT_TOKEN.Text
- .Headers(API.Base.SiteSettings.Header_Twitter_Authorization) = TXT_AUTH.Text
- End With
- Case Sites.Instagram
- .InstaHash.Value = TXT_TOKEN.Text
- .InstaHash_SP.Value = TXT_AUTH.Text
- End Select
- .Update()
End With
+
+ Settings.BeginUpdate()
+
+ If Not Host Is Nothing Then
+ With Host
+ SiteDefaultsFunctions.SetPropByChecker(TP_SITE_PROPS, Host)
+ If TXT_PATH.IsEmptyString Then .Path = Nothing Else .Path = TXT_PATH.Text
+ .SavedPostsPath = TXT_PATH_SAVED_POSTS.Text
+ .GetUserMediaOnly.Value = CH_GET_USER_MEDIA_ONLY.Checked
+
+ If .PropList.Count > 0 Then .PropList.ForEach(Sub(p) If Not p.Options Is Nothing Then p.UpdateValueByControl())
+ End With
+ End If
+
+ Settings.EndUpdate()
+
MyDefs.CloseForm()
End If
End Sub
Private Sub ToolbarBttCancel() Implements IOkCancelToolbar.ToolbarBttCancel
MyDefs.CloseForm(DialogResult.Cancel)
End Sub
- Private Sub TXT_TOKEN_ActionOnButtonClick(ByVal Sender As ActionButton) Handles TXT_TOKEN.ActionOnButtonClick
- If Sender.DefaultButton = ActionButton.DefaultButtons.Refresh Then
- With Settings(Sites.Instagram)
- If .GatherInstaHash() Then
- .InstaHashUpdateRequired.Value = Not .InstaHash.IsEmptyString
- TXT_TOKEN.Text = .InstaHash
- End If
- End With
- End If
- End Sub
Private Sub TXT_PATH_ActionOnButtonClick(ByVal Sender As ActionButton) Handles TXT_PATH.ActionOnButtonClick
+ ChangePath(Sender, Host.Path(False), TXT_PATH)
+ End Sub
+ Private Sub TXT_PATH_SAVED_POSTS_ActionOnButtonClick(Sender As ActionButton) Handles TXT_PATH_SAVED_POSTS.ActionOnButtonClick
+ ChangePath(Sender, Host.SavedPostsPath(False), TXT_PATH_SAVED_POSTS)
+ End Sub
+ Private Sub ChangePath(ByVal Sender As ActionButton, ByVal PathValue As SFile, ByRef CNT As TextBoxExtended)
If Sender.DefaultButton = ActionButton.DefaultButtons.Open Then
- Dim f As SFile = SFile.SelectPath(Settings(MySite).Path(False))
- If Not f.IsEmptyString Then TXT_PATH.Text = f
+ Dim f As SFile = SFile.SelectPath(PathValue)
+ If Not f.IsEmptyString Then CNT.Text = f
End If
End Sub
Private Sub TXT_COOKIES_ActionOnButtonClick(ByVal Sender As ActionButton) Handles TXT_COOKIES.ActionOnButtonClick
If Sender.DefaultButton = ActionButton.DefaultButtons.Edit Then
- Using f As New CookieListForm(Settings(MySite).Responser.Cookies) With {.MyDesignXML = Settings.Design} : f.ShowDialog() : End Using
- SetCookieText()
+ If TypeOf Host.Source Is IResponserContainer Then
+ Using f As New CookieListForm(DirectCast(Host.Source, IResponserContainer).Responser.Cookies) With {.MyDesignXML = Settings.Design} : f.ShowDialog() : End Using
+ SetCookieText()
+ End If
End If
End Sub
Private Sub TXT_COOKIES_ActionOnButtonClearClick() Handles TXT_COOKIES.ActionOnButtonClearClick
- With Settings(MySite).Responser
- If Not .Cookies Is Nothing Then .Cookies.Dispose()
- .Cookies = New CookieKeeper(.CookiesDomain)
- End With
- SetCookieText()
+ If TypeOf Host.Source Is IResponserContainer Then
+ With DirectCast(Host.Source, IResponserContainer).Responser
+ If Not .Cookies Is Nothing Then .Cookies.Dispose()
+ .Cookies = New CookieKeeper(.CookiesDomain)
+ End With
+ SetCookieText()
+ End If
End Sub
Private Sub SetCookieText()
- TXT_COOKIES.Text = $"{If(Settings(MySite).Responser.Cookies?.Count, 0)} cookies"
+ If TypeOf Host.Source Is IResponserContainer Then _
+ TXT_COOKIES.Text = $"{If(DirectCast(Host.Source, IResponserContainer).Responser.Cookies?.Count, 0)} cookies"
End Sub
End Class
End Namespace
\ No newline at end of file
diff --git a/SCrawler/Editors/SiteSelectionForm.Designer.vb b/SCrawler/Editors/SiteSelectionForm.Designer.vb
index 8ffb952..4e03089 100644
--- a/SCrawler/Editors/SiteSelectionForm.Designer.vb
+++ b/SCrawler/Editors/SiteSelectionForm.Designer.vb
@@ -26,7 +26,6 @@ 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(SiteSelectionForm))
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.CMB_SITES = New PersonalUtilities.Forms.Controls.ComboBoxExtended()
CONTAINER_MAIN = New System.Windows.Forms.ToolStripContainer()
CONTAINER_MAIN.ContentPanel.SuspendLayout()
@@ -60,13 +59,9 @@ Namespace Editors
ListColumn1.DisplayMember = True
ListColumn1.Name = "COL_DISPLAY"
ListColumn1.Text = "Site"
+ ListColumn1.ValueMember = True
ListColumn1.Width = -1
- ListColumn2.Name = "COL_VALUE"
- ListColumn2.Text = "Value"
- ListColumn2.ValueMember = True
- ListColumn2.Visible = False
Me.CMB_SITES.Columns.Add(ListColumn1)
- Me.CMB_SITES.Columns.Add(ListColumn2)
Me.CMB_SITES.Dock = System.Windows.Forms.DockStyle.Fill
Me.CMB_SITES.ListCheckBoxes = True
Me.CMB_SITES.ListDropDownStyle = PersonalUtilities.Forms.Controls.ComboBoxExtended.ListMode.Simple
diff --git a/SCrawler/Editors/SiteSelectionForm.vb b/SCrawler/Editors/SiteSelectionForm.vb
index e601395..d20085e 100644
--- a/SCrawler/Editors/SiteSelectionForm.vb
+++ b/SCrawler/Editors/SiteSelectionForm.vb
@@ -12,11 +12,11 @@ Imports PersonalUtilities.Forms.Controls.Base
Namespace Editors
Friend Class SiteSelectionForm : Implements IOkCancelToolbar
Private ReadOnly MyDefs As DefaultFormProps
- Friend ReadOnly Property SelectedSites As List(Of Sites)
- Friend Sub New(ByVal s As List(Of Sites))
+ Friend ReadOnly Property SelectedSites As List(Of String)
+ Friend Sub New(ByVal s As List(Of String))
InitializeComponent()
SelectedSites.ListAddList(s)
- If SelectedSites Is Nothing Then SelectedSites = New List(Of Sites)
+ If SelectedSites Is Nothing Then SelectedSites = New List(Of String)
MyDefs = New DefaultFormProps
End Sub
Private Sub SiteSelectionForm_Load(sender As Object, e As EventArgs) Handles Me.Load
@@ -25,8 +25,8 @@ Namespace Editors
.DelegateClosingChecker()
.AddOkCancelToolbar()
CMB_SITES.BeginUpdate()
- Dim sl As List(Of Sites) = ListAddList(Of Sites)(Nothing, [Enum].GetValues(GetType(Sites))).ListWithRemove(Sites.Undefined)
- CMB_SITES.Items.AddRange(sl.Select(Function(s) New ListItem({s.ToString, CInt(s)})))
+ Dim sl As List(Of String) = ListAddList(Nothing, Settings.Plugins.Select(Function(p) p.Name))
+ CMB_SITES.Items.AddRange(sl.Select(Function(s) New ListItem(s)))
Dim l As New List(Of Integer)
If SelectedSites.Count > 0 Then sl.ForEach(Sub(s) If SelectedSites.Contains(s) Then l.Add(sl.IndexOf(s)))
sl.Clear()
@@ -41,7 +41,7 @@ Namespace Editors
End Sub
Public Sub ToolbarBttOK() Implements IOkCancelToolbar.ToolbarBttOK
Try
- SelectedSites.ListAddList(CMB_SITES.Items.CheckedItems.Select(Function(i) DirectCast(i.Value(1), Sites)), LAP.ClearBeforeAdd)
+ SelectedSites.ListAddList(CMB_SITES.Items.CheckedItems.Select(Function(i) CStr(i.Value(0))), LAP.ClearBeforeAdd)
MyDefs.CloseForm()
Catch ex As Exception
ErrorsDescriber.Execute(EDP.LogMessageValue, ex)
diff --git a/SCrawler/Editors/UserCreatorForm.Designer.vb b/SCrawler/Editors/UserCreatorForm.Designer.vb
index bd93345..b5c954a 100644
--- a/SCrawler/Editors/UserCreatorForm.Designer.vb
+++ b/SCrawler/Editors/UserCreatorForm.Designer.vb
@@ -16,23 +16,24 @@
Private Sub InitializeComponent()
Me.components = New System.ComponentModel.Container()
Dim TP_MAIN As System.Windows.Forms.TableLayoutPanel
- Dim TP_PARAMS As System.Windows.Forms.TableLayoutPanel
- Dim TP_OTHER As System.Windows.Forms.TableLayoutPanel
+ Dim TP_SITE 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(UserCreatorForm))
+ 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_PARAMS As System.Windows.Forms.TableLayoutPanel
+ Dim TP_OTHER As System.Windows.Forms.TableLayoutPanel
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_DOWN_OPTIONS As System.Windows.Forms.TableLayoutPanel
Dim ActionButton4 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
+ Dim TP_DOWN_OPTIONS As System.Windows.Forms.TableLayoutPanel
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 TT_MAIN As System.Windows.Forms.ToolTip
Me.TXT_USER = New PersonalUtilities.Forms.Controls.TextBoxExtended()
- Me.TP_SITE = New System.Windows.Forms.TableLayoutPanel()
- Me.OPT_REDDIT = New System.Windows.Forms.RadioButton()
- Me.OPT_TWITTER = New System.Windows.Forms.RadioButton()
Me.CH_IS_CHANNEL = New System.Windows.Forms.CheckBox()
- Me.OPT_INSTAGRAM = New System.Windows.Forms.RadioButton()
- Me.OPT_REDGIFS = New System.Windows.Forms.RadioButton()
+ Me.CMB_SITE = New PersonalUtilities.Forms.Controls.ComboBoxExtended()
+ Me.BTT_OTHER_SETTINGS = New System.Windows.Forms.Button()
Me.CH_TEMP = New System.Windows.Forms.CheckBox()
Me.CH_FAV = New System.Windows.Forms.CheckBox()
Me.CH_PARSE_USER_MEDIA = New System.Windows.Forms.CheckBox()
@@ -48,13 +49,15 @@
Me.TXT_SPEC_FOLDER = New PersonalUtilities.Forms.Controls.TextBoxExtended()
Me.CONTAINER_MAIN = New System.Windows.Forms.ToolStripContainer()
TP_MAIN = New System.Windows.Forms.TableLayoutPanel()
+ TP_SITE = New System.Windows.Forms.TableLayoutPanel()
TP_PARAMS = New System.Windows.Forms.TableLayoutPanel()
TP_OTHER = New System.Windows.Forms.TableLayoutPanel()
TP_DOWN_OPTIONS = New System.Windows.Forms.TableLayoutPanel()
TT_MAIN = New System.Windows.Forms.ToolTip(Me.components)
TP_MAIN.SuspendLayout()
CType(Me.TXT_USER, System.ComponentModel.ISupportInitialize).BeginInit()
- Me.TP_SITE.SuspendLayout()
+ TP_SITE.SuspendLayout()
+ CType(Me.CMB_SITE, System.ComponentModel.ISupportInitialize).BeginInit()
TP_PARAMS.SuspendLayout()
TP_OTHER.SuspendLayout()
CType(Me.TXT_DESCR, System.ComponentModel.ISupportInitialize).BeginInit()
@@ -73,7 +76,7 @@
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_USER, 0, 0)
- TP_MAIN.Controls.Add(Me.TP_SITE, 0, 3)
+ TP_MAIN.Controls.Add(TP_SITE, 0, 3)
TP_MAIN.Controls.Add(TP_PARAMS, 0, 4)
TP_MAIN.Controls.Add(TP_OTHER, 0, 6)
TP_MAIN.Controls.Add(Me.TXT_DESCR, 0, 9)
@@ -112,85 +115,69 @@
'
'TP_SITE
'
- Me.TP_SITE.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.[Single]
- Me.TP_SITE.ColumnCount = 5
- Me.TP_SITE.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 20.0!))
- Me.TP_SITE.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 20.0!))
- Me.TP_SITE.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 20.0!))
- Me.TP_SITE.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 20.0!))
- Me.TP_SITE.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 20.0!))
- Me.TP_SITE.Controls.Add(Me.OPT_REDDIT, 0, 0)
- Me.TP_SITE.Controls.Add(Me.OPT_TWITTER, 2, 0)
- Me.TP_SITE.Controls.Add(Me.CH_IS_CHANNEL, 1, 0)
- Me.TP_SITE.Controls.Add(Me.OPT_INSTAGRAM, 3, 0)
- Me.TP_SITE.Controls.Add(Me.OPT_REDGIFS, 4, 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)
- Me.TP_SITE.Name = "TP_SITE"
- Me.TP_SITE.RowCount = 1
- Me.TP_SITE.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
- Me.TP_SITE.Size = New System.Drawing.Size(452, 31)
- Me.TP_SITE.TabIndex = 3
- '
- 'OPT_REDDIT
- '
- Me.OPT_REDDIT.AutoSize = True
- Me.OPT_REDDIT.Dock = System.Windows.Forms.DockStyle.Fill
- Me.OPT_REDDIT.Location = New System.Drawing.Point(4, 4)
- Me.OPT_REDDIT.Name = "OPT_REDDIT"
- Me.OPT_REDDIT.Size = New System.Drawing.Size(83, 23)
- Me.OPT_REDDIT.TabIndex = 0
- Me.OPT_REDDIT.TabStop = True
- Me.OPT_REDDIT.Text = "Reddit"
- Me.OPT_REDDIT.UseVisualStyleBackColor = True
- '
- 'OPT_TWITTER
- '
- Me.OPT_TWITTER.AutoSize = True
- Me.OPT_TWITTER.Dock = System.Windows.Forms.DockStyle.Fill
- Me.OPT_TWITTER.Location = New System.Drawing.Point(184, 4)
- Me.OPT_TWITTER.Name = "OPT_TWITTER"
- Me.OPT_TWITTER.Size = New System.Drawing.Size(83, 23)
- Me.OPT_TWITTER.TabIndex = 1
- Me.OPT_TWITTER.TabStop = True
- Me.OPT_TWITTER.Text = "Twitter"
- Me.OPT_TWITTER.UseVisualStyleBackColor = True
+ TP_SITE.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.[Single]
+ TP_SITE.ColumnCount = 3
+ TP_SITE.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 79.0!))
+ TP_SITE.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
+ TP_SITE.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 88.0!))
+ TP_SITE.Controls.Add(Me.CH_IS_CHANNEL, 0, 0)
+ TP_SITE.Controls.Add(Me.CMB_SITE, 1, 0)
+ TP_SITE.Controls.Add(Me.BTT_OTHER_SETTINGS, 2, 0)
+ TP_SITE.Dock = System.Windows.Forms.DockStyle.Fill
+ TP_SITE.Location = New System.Drawing.Point(1, 88)
+ TP_SITE.Margin = New System.Windows.Forms.Padding(0)
+ TP_SITE.Name = "TP_SITE"
+ TP_SITE.RowCount = 1
+ TP_SITE.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
+ TP_SITE.Size = New System.Drawing.Size(452, 31)
+ TP_SITE.TabIndex = 3
'
'CH_IS_CHANNEL
'
Me.CH_IS_CHANNEL.AutoSize = True
Me.CH_IS_CHANNEL.Dock = System.Windows.Forms.DockStyle.Fill
- Me.CH_IS_CHANNEL.Location = New System.Drawing.Point(94, 4)
+ Me.CH_IS_CHANNEL.Location = New System.Drawing.Point(4, 4)
Me.CH_IS_CHANNEL.Name = "CH_IS_CHANNEL"
- Me.CH_IS_CHANNEL.Size = New System.Drawing.Size(83, 23)
- Me.CH_IS_CHANNEL.TabIndex = 2
+ Me.CH_IS_CHANNEL.Size = New System.Drawing.Size(73, 23)
+ Me.CH_IS_CHANNEL.TabIndex = 0
Me.CH_IS_CHANNEL.Text = "Channel"
Me.CH_IS_CHANNEL.UseVisualStyleBackColor = True
'
- 'OPT_INSTAGRAM
+ 'CMB_SITE
'
- Me.OPT_INSTAGRAM.AutoSize = True
- Me.OPT_INSTAGRAM.Dock = System.Windows.Forms.DockStyle.Fill
- Me.OPT_INSTAGRAM.Location = New System.Drawing.Point(274, 4)
- Me.OPT_INSTAGRAM.Name = "OPT_INSTAGRAM"
- Me.OPT_INSTAGRAM.Size = New System.Drawing.Size(83, 23)
- Me.OPT_INSTAGRAM.TabIndex = 3
- Me.OPT_INSTAGRAM.TabStop = True
- Me.OPT_INSTAGRAM.Text = "Instagram"
- Me.OPT_INSTAGRAM.UseVisualStyleBackColor = True
+ ActionButton1.BackgroundImage = CType(resources.GetObject("ActionButton1.BackgroundImage"), System.Drawing.Image)
+ ActionButton1.Index = 0
+ ActionButton1.Name = "BTT_COMBOBOX_ARROW"
+ Me.CMB_SITE.Buttons.Add(ActionButton1)
+ ListColumn1.Name = "_COL_KEY"
+ ListColumn1.Text = "Key"
+ ListColumn1.ValueMember = True
+ ListColumn1.Visible = False
+ ListColumn2.DisplayMember = True
+ ListColumn2.Name = "_COL_VALUE"
+ ListColumn2.Text = "Value"
+ ListColumn2.Width = -1
+ 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(84, 2)
+ Me.CMB_SITE.Margin = New System.Windows.Forms.Padding(3, 1, 3, 3)
+ Me.CMB_SITE.Name = "CMB_SITE"
+ Me.CMB_SITE.Size = New System.Drawing.Size(275, 22)
+ Me.CMB_SITE.TabIndex = 1
+ Me.CMB_SITE.TextBoxBorderStyle = System.Windows.Forms.BorderStyle.FixedSingle
'
- 'OPT_REDGIFS
+ 'BTT_OTHER_SETTINGS
'
- Me.OPT_REDGIFS.AutoSize = True
- Me.OPT_REDGIFS.Dock = System.Windows.Forms.DockStyle.Fill
- Me.OPT_REDGIFS.Location = New System.Drawing.Point(364, 4)
- Me.OPT_REDGIFS.Name = "OPT_REDGIFS"
- Me.OPT_REDGIFS.Size = New System.Drawing.Size(84, 23)
- Me.OPT_REDGIFS.TabIndex = 4
- Me.OPT_REDGIFS.TabStop = True
- Me.OPT_REDGIFS.Text = "RedGifs"
- Me.OPT_REDGIFS.UseVisualStyleBackColor = True
+ Me.BTT_OTHER_SETTINGS.Dock = System.Windows.Forms.DockStyle.Fill
+ Me.BTT_OTHER_SETTINGS.Location = New System.Drawing.Point(364, 2)
+ 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(86, 27)
+ Me.BTT_OTHER_SETTINGS.TabIndex = 2
+ Me.BTT_OTHER_SETTINGS.Text = "Options"
+ TT_MAIN.SetToolTip(Me.BTT_OTHER_SETTINGS, "Other settings")
+ Me.BTT_OTHER_SETTINGS.UseVisualStyleBackColor = True
'
'TP_PARAMS
'
@@ -277,11 +264,11 @@
'
'TXT_DESCR
'
- ActionButton1.BackgroundImage = CType(resources.GetObject("ActionButton1.BackgroundImage"), System.Drawing.Image)
- ActionButton1.Dock = System.Windows.Forms.DockStyle.Top
- ActionButton1.Index = 0
- ActionButton1.Name = "BTT_CLEAR"
- Me.TXT_DESCR.Buttons.Add(ActionButton1)
+ ActionButton2.BackgroundImage = CType(resources.GetObject("ActionButton2.BackgroundImage"), System.Drawing.Image)
+ ActionButton2.Dock = System.Windows.Forms.DockStyle.Top
+ ActionButton2.Index = 0
+ ActionButton2.Name = "BTT_CLEAR"
+ Me.TXT_DESCR.Buttons.Add(ActionButton2)
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
@@ -345,14 +332,14 @@
'
'TXT_LABELS
'
- ActionButton2.BackgroundImage = CType(resources.GetObject("ActionButton2.BackgroundImage"), System.Drawing.Image)
- ActionButton2.Index = 0
- ActionButton2.Name = "BTT_OPEN"
ActionButton3.BackgroundImage = CType(resources.GetObject("ActionButton3.BackgroundImage"), System.Drawing.Image)
- ActionButton3.Index = 1
- ActionButton3.Name = "BTT_CLEAR"
- Me.TXT_LABELS.Buttons.Add(ActionButton2)
+ ActionButton3.Index = 0
+ ActionButton3.Name = "BTT_OPEN"
+ ActionButton4.BackgroundImage = CType(resources.GetObject("ActionButton4.BackgroundImage"), System.Drawing.Image)
+ ActionButton4.Index = 1
+ ActionButton4.Name = "BTT_CLEAR"
Me.TXT_LABELS.Buttons.Add(ActionButton3)
+ Me.TXT_LABELS.Buttons.Add(ActionButton4)
Me.TXT_LABELS.CaptionText = "Labels"
Me.TXT_LABELS.CaptionWidth = 50.0R
Me.TXT_LABELS.Dock = System.Windows.Forms.DockStyle.Fill
@@ -404,16 +391,16 @@
'
'TXT_SPEC_FOLDER
'
- ActionButton4.BackgroundImage = CType(resources.GetObject("ActionButton4.BackgroundImage"), System.Drawing.Image)
- ActionButton4.Index = 0
- ActionButton4.Name = "BTT_OPEN"
- ActionButton4.ToolTipText = "Select a new path in the folder selection dialog"
ActionButton5.BackgroundImage = CType(resources.GetObject("ActionButton5.BackgroundImage"), System.Drawing.Image)
- ActionButton5.Index = 1
- ActionButton5.Name = "BTT_CLEAR"
- ActionButton5.ToolTipText = "Clear"
- Me.TXT_SPEC_FOLDER.Buttons.Add(ActionButton4)
+ ActionButton5.Index = 0
+ ActionButton5.Name = "BTT_OPEN"
+ ActionButton5.ToolTipText = "Select a new path in the folder selection dialog"
+ ActionButton6.BackgroundImage = CType(resources.GetObject("ActionButton6.BackgroundImage"), System.Drawing.Image)
+ ActionButton6.Index = 1
+ ActionButton6.Name = "BTT_CLEAR"
+ ActionButton6.ToolTipText = "Clear"
Me.TXT_SPEC_FOLDER.Buttons.Add(ActionButton5)
+ Me.TXT_SPEC_FOLDER.Buttons.Add(ActionButton6)
Me.TXT_SPEC_FOLDER.CaptionText = "Special path"
Me.TXT_SPEC_FOLDER.Dock = System.Windows.Forms.DockStyle.Fill
Me.TXT_SPEC_FOLDER.Location = New System.Drawing.Point(4, 62)
@@ -456,8 +443,9 @@
Me.Text = "Create User"
TP_MAIN.ResumeLayout(False)
CType(Me.TXT_USER, System.ComponentModel.ISupportInitialize).EndInit()
- Me.TP_SITE.ResumeLayout(False)
- Me.TP_SITE.PerformLayout()
+ TP_SITE.ResumeLayout(False)
+ TP_SITE.PerformLayout()
+ CType(Me.CMB_SITE, System.ComponentModel.ISupportInitialize).EndInit()
TP_PARAMS.ResumeLayout(False)
TP_PARAMS.PerformLayout()
TP_OTHER.ResumeLayout(False)
@@ -479,8 +467,6 @@
Private WithEvents CONTAINER_MAIN As ToolStripContainer
Private WithEvents TXT_USER As PersonalUtilities.Forms.Controls.TextBoxExtended
- Private WithEvents OPT_REDDIT As RadioButton
- Private WithEvents OPT_TWITTER As RadioButton
Private WithEvents CH_TEMP As CheckBox
Private WithEvents CH_FAV As CheckBox
Private WithEvents CH_PARSE_USER_MEDIA As CheckBox
@@ -494,9 +480,8 @@
Private WithEvents CH_DOWN_IMAGES As CheckBox
Private WithEvents CH_DOWN_VIDEOS As CheckBox
Private WithEvents CH_IS_CHANNEL As CheckBox
- Private WithEvents OPT_INSTAGRAM As RadioButton
Private WithEvents TXT_SPEC_FOLDER As PersonalUtilities.Forms.Controls.TextBoxExtended
- Private WithEvents OPT_REDGIFS As RadioButton
- Private WithEvents TP_SITE As TableLayoutPanel
+ Private WithEvents CMB_SITE As PersonalUtilities.Forms.Controls.ComboBoxExtended
+ Private WithEvents BTT_OTHER_SETTINGS As Button
End Class
End Namespace
\ No newline at end of file
diff --git a/SCrawler/Editors/UserCreatorForm.resx b/SCrawler/Editors/UserCreatorForm.resx
index de954cd..0fda744 100644
--- a/SCrawler/Editors/UserCreatorForm.resx
+++ b/SCrawler/Editors/UserCreatorForm.resx
@@ -120,20 +120,113 @@
False
-
- False
-
-
+
False
+
+
+
+ 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
+ AAAASUVORK5CYII=
+
+
False
17, 17
-
-
+
+ False
+
+
+ False
+
+
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
@@ -141,7 +234,7 @@
AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC
-
+
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP
@@ -152,7 +245,7 @@
cMaRN0UdBBkAAAAASUVORK5CYII=
-
+
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
@@ -163,7 +256,7 @@
False
-
+
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP
@@ -174,7 +267,7 @@
cMaRN0UdBBkAAAAASUVORK5CYII=
-
+
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
diff --git a/SCrawler/Editors/UserCreatorForm.vb b/SCrawler/Editors/UserCreatorForm.vb
index 437294f..9363df1 100644
--- a/SCrawler/Editors/UserCreatorForm.vb
+++ b/SCrawler/Editors/UserCreatorForm.vb
@@ -8,10 +8,13 @@
' but WITHOUT ANY WARRANTY
Imports System.ComponentModel
Imports PersonalUtilities.Forms
+Imports PersonalUtilities.Forms.Controls
Imports PersonalUtilities.Forms.Controls.Base
Imports PersonalUtilities.Forms.Toolbars
Imports PersonalUtilities.Functions.RegularExpressions
Imports SCrawler.API.Base
+Imports SCrawler.Plugin
+Imports SCrawler.Plugin.Hosts
Namespace Editors
Friend Class UserCreatorForm : Implements IOkCancelToolbar
Private ReadOnly MyDef As DefaultFormProps(Of FieldsChecker)
@@ -62,8 +65,9 @@ Namespace Editors
Return TXT_USER_FRIENDLY.Text
End Get
End Property
+ Friend Property MyExchangeOptions As Object = Nothing
Private ReadOnly _SpecPathPattern As RParams = RParams.DM("\w:\\.*", 0, EDP.ReturnValue)
- Private ReadOnly Property SpecialPath(ByVal s As Sites) As SFile
+ Private ReadOnly Property SpecialPath(ByVal s As SettingsHost) As SFile
Get
If TXT_SPEC_FOLDER.IsEmptyString Then
Return Nothing
@@ -71,9 +75,8 @@ Namespace Editors
If Not CStr(RegexReplace(TXT_SPEC_FOLDER.Text, _SpecPathPattern)).IsEmptyString Then
Return $"{TXT_SPEC_FOLDER.Text}\"
Else
- Return $"{Settings(s).Path.PathWithSeparator}{TXT_SPEC_FOLDER.Text}\"
+ Return $"{s.Path.PathWithSeparator}{TXT_SPEC_FOLDER.Text}\"
End If
-
End If
End Get
End Property
@@ -89,7 +92,7 @@ Namespace Editors
Me.New
If Not _Instance Is Nothing Then
UserInstance = _Instance
- User = DirectCast(UserInstance.Self, UserDataBase).User
+ User = DirectCast(UserInstance, UserDataBase).User
End If
End Sub
Private Sub UserCreatorForm_Load(sender As Object, e As EventArgs) Handles Me.Load
@@ -98,26 +101,26 @@ Namespace Editors
.MyViewInitialize(Me, Settings.Design, True)
.AddOkCancelToolbar()
CH_AUTO_DETECT_SITE.Enabled = False
+ With CMB_SITE
+ .BeginUpdate()
+ .Items.AddRange(Settings.Plugins.Select(Function(p) New ListItem({p.Key, p.Name})))
+ .EndUpdate(True)
+ End With
If User.Name.IsEmptyString Then
- OPT_REDDIT.Checked = False
- OPT_TWITTER.Checked = False
- OPT_INSTAGRAM.Checked = False
CH_READY_FOR_DOWN.Checked = True
CH_TEMP.Checked = Settings.DefaultTemporary
CH_DOWN_IMAGES.Checked = Settings.DefaultDownloadImages
CH_DOWN_VIDEOS.Checked = Settings.DefaultDownloadVideos
+ SetParamsBySite()
Else
TP_ADD_BY_LIST.Enabled = False
TXT_USER.Text = User.Name
TXT_SPEC_FOLDER.Text = User.SpecialPath
- Select Case User.Site
- Case Sites.Reddit : OPT_REDDIT.Checked = True
- Case Sites.Twitter : OPT_TWITTER.Checked = True
- Case Sites.Instagram : OPT_INSTAGRAM.Checked = True
- Case Sites.RedGifs : OPT_REDGIFS.Checked = True
- End Select
+ Dim i% = Settings.Plugins.FindIndex(Function(p) p.Key = User.Plugin)
+ If i >= 0 Then CMB_SITE.SelectedIndex = i
SetParamsBySite()
- TP_SITE.Enabled = False
+ CH_IS_CHANNEL.Enabled = False
+ CMB_SITE.Enabled = False
CH_IS_CHANNEL.Checked = User.IsChannel
If Not UserInstance Is Nothing Then
TXT_USER.Enabled = False
@@ -167,31 +170,26 @@ Namespace Editors
Private Sub UserCreatorForm_Disposed(sender As Object, e As EventArgs) Handles Me.Disposed
UserLabels.Clear()
End Sub
- Private Function GetSiteByCheckers() As Sites
- Select Case True
- Case OPT_REDDIT.Checked : Return Sites.Reddit
- Case OPT_TWITTER.Checked : Return Sites.Twitter
- Case OPT_INSTAGRAM.Checked : Return Sites.Instagram
- Case OPT_REDGIFS.Checked : Return Sites.RedGifs
- Case Else : Return Sites.Undefined
- End Select
+ Private Function GetSiteByCheckers() As SettingsHost
+ Return If(CMB_SITE.SelectedIndex >= 0, Settings(CStr(CMB_SITE.Items(CMB_SITE.SelectedIndex).Value(0))), Nothing)
End Function
Private Sub ToolbarBttOK() Implements IOkCancelToolbar.ToolbarBttOK
If Not CH_ADD_BY_LIST.Checked Then
If MyDef.MyFieldsChecker.AllParamsOK Then
- Dim s As Sites = GetSiteByCheckers()
- If Not s = Sites.Undefined 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
+ .Site = s.Name
+ .Plugin = s.Key
.IsChannel = CH_IS_CHANNEL.Checked
.UpdateUserFile()
End With
User = tmpUser
If Not UserInstance Is Nothing Then
- With DirectCast(UserInstance.Self, UserDataBase)
+ With DirectCast(UserInstance, UserDataBase)
.User = User
.FriendlyName = TXT_USER_FRIENDLY.Text
.Favorite = CH_FAV.Checked
@@ -200,6 +198,7 @@ Namespace Editors
.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, API.UserDataBind)
@@ -227,65 +226,54 @@ CloseForm:
Private Sub ToolbarBttCancel() Implements IOkCancelToolbar.ToolbarBttCancel
MyDef.CloseForm(IIf(StartIndex >= 0, DialogResult.OK, DialogResult.Cancel))
End Sub
- Private ReadOnly TwitterRegEx As RParams = RParams.DMS("[htps:/]{7,8}.*?twitter.com/([^/]+)", 1)
- Private ReadOnly RedditRegEx1 As RParams = RParams.DMS("[htps:/]{7,8}.*?reddit.com/user/([^/]+)", 1)
- Private ReadOnly RedditRegEx2 As RParams = RParams.DMS(".?u/([^/]+)", 1)
- Private ReadOnly RedditChannelRegEx1 As RParams = RParams.DMS("[htps:/]{7,8}.*?reddit.com/r/([^/]+)", 1)
- Private ReadOnly RedditChannelRegEx2 As RParams = RParams.DMS(".?r/([^/]+)", 1)
- Private ReadOnly InstagramRegEx As RParams = RParams.DMS("[htps:/]{7,8}.*?instagram.com/([^/]+)", 1)
- Private ReadOnly RedGifsRegEx As RParams = RParams.DMS("[htps:/]{7,8}.*?redgifs.com/users/([^/]+)", 1)
Private _TextChangeInvoked As Boolean = False
Private Sub TXT_USER_ActionOnTextChange() Handles TXT_USER.ActionOnTextChange
Try
If Not _TextChangeInvoked Then
_TextChangeInvoked = True
If Not CH_ADD_BY_LIST.Checked Then
- Dim s() As Object = GetSiteByText(TXT_USER.Text)
- Select Case s(0)
- Case Sites.Twitter : OPT_TWITTER.Checked = True
- Case Sites.Reddit : OPT_REDDIT.Checked = True
- Case Sites.Instagram : OPT_INSTAGRAM.Checked = True
- Case Sites.RedGifs : OPT_REDGIFS.Checked = True
- Case Else : OPT_TWITTER.Checked = False : OPT_REDDIT.Checked = False : OPT_INSTAGRAM.Checked = False
- End Select
- CH_IS_CHANNEL.Checked = CBool(s(1))
+ Dim s As ExchangeOptions = GetSiteByText(TXT_USER.Text)
+ Dim found As Boolean = False
+ If Not s.UserName.IsEmptyString Then
+ Dim i% = Settings.Plugins.FindIndex(Function(p) p.Key = s.HostKey)
+ If i >= 0 Then
+ CMB_SITE.SelectedIndex = i
+ CH_IS_CHANNEL.Checked = s.IsChannel
+ TXT_USER.Text = s.UserName
+ found = True
+ End If
+ End If
+ If Not found Then
+ CMB_SITE.SelectedIndex = -1
+ CMB_SITE.Clear(ComboBoxExtended.ClearMode.Text)
+ CH_IS_CHANNEL.Checked = False
+ End If
End If
_TextChangeInvoked = False
End If
Catch ex As Exception
End Try
End Sub
- Private Function GetSiteByText(ByRef TXT As String) As Object()
- If Not TXT.IsEmptyString AndAlso TXT.Length > 8 Then
- If CheckRegex(TXT, TwitterRegEx) Then
- Return {Sites.Twitter, False}
- ElseIf CheckRegex(TXT, RedditRegEx1) OrElse CheckRegex(TXT, RedditRegEx2) Then
- Return {Sites.Reddit, False}
- ElseIf CheckRegex(TXT, RedditChannelRegEx1) OrElse CheckRegex(TXT, RedditChannelRegEx2) Then
- Return {Sites.Reddit, True}
- ElseIf CheckRegex(TXT, InstagramRegEx) Then
- Return {Sites.Instagram, False}
- ElseIf CheckRegex(TXT, RedGifsRegEx) Then
- Return {Sites.RedGifs, False}
- End If
+ Private Function GetSiteByText(ByRef TXT As String) As ExchangeOptions
+ Dim s As ExchangeOptions
+ For Each p As PluginHost In Settings.Plugins
+ s = p.Settings.IsMyUser(TXT)
+ If Not s.UserName.IsEmptyString Then Return s
+ Next
+ Return Nothing
+ End Function
+ Private Sub CMB_SITE_ActionSelectedItemChanged(ByVal _Item As ListViewItem) Handles CMB_SITE.ActionSelectedItemChanged
+ CH_IS_CHANNEL.Checked = False
+ MyExchangeOptions = Nothing
+ SetParamsBySite()
+ End Sub
+ Private Sub BTT_OTHER_SETTINGS_Click(sender As Object, e As EventArgs) Handles BTT_OTHER_SETTINGS.Click
+ Dim s As SettingsHost = GetSiteByCheckers()
+ If Not s Is Nothing Then
+ s.Source.UserOptions(MyExchangeOptions, True)
+ MyDef.ChangesDetected = True
+ MyDef.MyOkCancel.EnableOK = True
End If
- Return {Sites.Undefined, False}
- End Function
- Private Function CheckRegex(ByRef TXT As String, ByVal r As RParams) As Boolean
- Dim s$ = RegexReplace(TXT, r)
- If Not s.IsEmptyString Then TXT = s : Return True Else Return False
- End Function
- Private Sub OPT_REDDIT_CheckedChanged(sender As Object, e As EventArgs) Handles OPT_REDDIT.CheckedChanged
- If OPT_REDDIT.Checked Then CH_IS_CHANNEL.Enabled = True : SetParamsBySite()
- End Sub
- Private Sub OPT_TWITTER_CheckedChanged(sender As Object, e As EventArgs) Handles OPT_TWITTER.CheckedChanged
- If OPT_TWITTER.Checked Then CH_IS_CHANNEL.Checked = False : CH_IS_CHANNEL.Enabled = False : SetParamsBySite()
- End Sub
- Private Sub OPT_INSTAGRAM_CheckedChanged(sender As Object, e As EventArgs) Handles OPT_INSTAGRAM.CheckedChanged
- If OPT_INSTAGRAM.Checked Then CH_IS_CHANNEL.Checked = False : CH_IS_CHANNEL.Enabled = False : SetParamsBySite()
- End Sub
- Private Sub OPT_REDGIFS_CheckedChanged(sender As Object, e As EventArgs) Handles OPT_REDGIFS.CheckedChanged
- If OPT_REDGIFS.Checked Then CH_IS_CHANNEL.Checked = False : CH_IS_CHANNEL.Enabled = False : SetParamsBySite()
End Sub
Private Sub TXT_SPEC_FOLDER_ActionOnButtonClick(ByVal Sender As ActionButton) Handles TXT_SPEC_FOLDER.ActionOnButtonClick
If Sender.DefaultButton = ActionButton.DefaultButtons.Open Then
@@ -302,15 +290,27 @@ CloseForm:
If CH_FAV.Checked Then CH_TEMP.Checked = False
End Sub
Private Sub SetParamsBySite()
- Dim s As Sites = GetSiteByCheckers()
- If Not s = Sites.Undefined Then
- With Settings(s)
+ Dim s As SettingsHost = GetSiteByCheckers()
+ If Not s Is Nothing Then
+ With s
CH_TEMP.Checked = .Temporary
CH_DOWN_IMAGES.Checked = .DownloadImages
CH_DOWN_VIDEOS.Checked = .DownloadVideos
CH_PARSE_USER_MEDIA.Checked = .GetUserMediaOnly.Value
CH_READY_FOR_DOWN.Checked = Not CH_TEMP.Checked
+ If s.HasSpecialOptions Then
+ BTT_OTHER_SETTINGS.Enabled = True
+ If UserInstance Is Nothing Then
+ s.Source.UserOptions(MyExchangeOptions, False)
+ Else
+ MyExchangeOptions = DirectCast(UserInstance, UserDataBase).ExchangeOptionsGet
+ End If
+ Else
+ BTT_OTHER_SETTINGS.Enabled = False
+ End If
End With
+ Else
+ BTT_OTHER_SETTINGS.Enabled = False
End If
End Sub
Private Sub CH_ADD_BY_LIST_CheckedChanged(sender As Object, e As EventArgs) Handles CH_ADD_BY_LIST.CheckedChanged
@@ -327,10 +327,14 @@ CloseForm:
TXT_USER_FRIENDLY.Enabled = Not CH_ADD_BY_LIST.Checked
End Sub
Private Sub CH_AUTO_DETECT_SITE_CheckedChanged(sender As Object, e As EventArgs) Handles CH_AUTO_DETECT_SITE.CheckedChanged
- OPT_REDDIT.Enabled = Not CH_AUTO_DETECT_SITE.Checked
- OPT_TWITTER.Enabled = Not CH_AUTO_DETECT_SITE.Checked
- OPT_INSTAGRAM.Enabled = Not CH_AUTO_DETECT_SITE.Checked
CH_IS_CHANNEL.Enabled = Not CH_AUTO_DETECT_SITE.Checked
+ CMB_SITE.Enabled = Not CH_AUTO_DETECT_SITE.Checked
+ If CH_AUTO_DETECT_SITE.Checked Then
+ BTT_OTHER_SETTINGS.Enabled = False
+ MyExchangeOptions = Nothing
+ Else
+ BTT_OTHER_SETTINGS.Enabled = True
+ End If
End Sub
Private Function CreateUsersByList() As Boolean
Try
@@ -343,24 +347,28 @@ CloseForm:
Dim BannedUsers() As String = Nothing
Dim uu$
Dim tmpUser As UserInfo
- Dim s As Sites = GetSiteByCheckers()
- Dim sObj() As Object
+ Dim s As SettingsHost = GetSiteByCheckers()
+ Dim sObj As ExchangeOptions
Dim _IsChannel As Boolean = CH_IS_CHANNEL.Checked
Dim Added% = 0
Dim Skipped% = 0
Dim uid%
- Dim sf As Func(Of Sites, String) = Function(__s) SpecialPath(__s).PathWithSeparator
- Dim __sf As Func(Of String, Sites, SFile) = Function(Input, __s) IIf(sf(__s).IsEmptyString, Nothing, New SFile($"{sf(__s)}{Input}\"))
+ 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}\"))
For i% = 0 To u.Count - 1
uu = u(i)
If CH_AUTO_DETECT_SITE.Checked Then
sObj = GetSiteByText(uu)
- s = sObj(0)
- _IsChannel = CBool(sObj(1))
+ If Not sObj.UserName.IsEmptyString Then
+ s = Settings(sObj.HostKey)
+ uu = sObj.UserName
+ Else
+ s = Nothing
+ End If
End If
- If Not s = Sites.Undefined Then
+ If Not s Is Nothing Then
tmpUser = New UserInfo(uu, s,,, __sf(uu, s)) With {.IsChannel = _IsChannel}
uid = -1
If Settings.UsersList.Count > 0 Then uid = Settings.UsersList.IndexOf(tmpUser)
@@ -391,6 +399,9 @@ CloseForm:
.DownloadVideos = CH_DOWN_VIDEOS.Checked
.Labels.ListAddList(UserLabels)
.ParseUserMediaOnly = CH_PARSE_USER_MEDIA.Checked
+ If Not CH_AUTO_DETECT_SITE.Checked Then _
+ DirectCast(.Self, UserDataBase).HOST.Source.UserOptions(MyExchangeOptions, False)
+ DirectCast(.Self, UserDataBase).ExchangeOptionsSet(MyExchangeOptions)
.UpdateUserInformation()
End With
Added += 1
diff --git a/SCrawler/FDatePickerForm.Designer.vb b/SCrawler/FDatePickerForm.Designer.vb
new file mode 100644
index 0000000..7b89d97
--- /dev/null
+++ b/SCrawler/FDatePickerForm.Designer.vb
@@ -0,0 +1,80 @@
+' Copyright (C) 2022 Andy
+' This program is free software: you can redistribute it and/or modify
+' it under the terms of the GNU General Public License as published by
+' the Free Software Foundation, either version 3 of the License, or
+' (at your option) any later version.
+'
+' This program is distributed in the hope that it will be useful,
+' but WITHOUT ANY WARRANTY
+
+Partial Friend Class FDatePickerForm : 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
+ Me.DT = New System.Windows.Forms.DateTimePicker()
+ CONTAINER_MAIN = New System.Windows.Forms.ToolStripContainer()
+ CONTAINER_MAIN.ContentPanel.SuspendLayout()
+ CONTAINER_MAIN.SuspendLayout()
+ Me.SuspendLayout()
+ '
+ 'CONTAINER_MAIN
+ '
+ '
+ 'CONTAINER_MAIN.ContentPanel
+ '
+ CONTAINER_MAIN.ContentPanel.Controls.Add(Me.DT)
+ CONTAINER_MAIN.ContentPanel.Size = New System.Drawing.Size(209, 47)
+ 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(209, 47)
+ CONTAINER_MAIN.TabIndex = 0
+ CONTAINER_MAIN.TopToolStripPanelVisible = False
+ '
+ 'DT
+ '
+ Me.DT.Dock = System.Windows.Forms.DockStyle.Fill
+ Me.DT.Location = New System.Drawing.Point(0, 0)
+ Me.DT.Name = "DT"
+ Me.DT.ShowCheckBox = True
+ Me.DT.Size = New System.Drawing.Size(209, 20)
+ Me.DT.TabIndex = 0
+ '
+ 'FDatePickerForm
+ '
+ Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
+ Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
+ Me.ClientSize = New System.Drawing.Size(209, 47)
+ Me.Controls.Add(CONTAINER_MAIN)
+ Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle
+ Me.KeyPreview = True
+ Me.MaximizeBox = False
+ Me.MaximumSize = New System.Drawing.Size(225, 86)
+ Me.MinimizeBox = False
+ Me.MinimumSize = New System.Drawing.Size(225, 86)
+ Me.Name = "FDatePickerForm"
+ Me.ShowIcon = False
+ Me.ShowInTaskbar = False
+ Me.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide
+ Me.Text = "Date limit"
+ CONTAINER_MAIN.ContentPanel.ResumeLayout(False)
+ CONTAINER_MAIN.ResumeLayout(False)
+ CONTAINER_MAIN.PerformLayout()
+ Me.ResumeLayout(False)
+
+ End Sub
+ Private WithEvents DT As DateTimePicker
+End Class
\ No newline at end of file
diff --git a/SCrawler/FDatePickerForm.resx b/SCrawler/FDatePickerForm.resx
new file mode 100644
index 0000000..56d6ce4
--- /dev/null
+++ b/SCrawler/FDatePickerForm.resx
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 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
+
+
\ No newline at end of file
diff --git a/SCrawler/FDatePickerForm.vb b/SCrawler/FDatePickerForm.vb
new file mode 100644
index 0000000..2b8b835
--- /dev/null
+++ b/SCrawler/FDatePickerForm.vb
@@ -0,0 +1,50 @@
+' Copyright (C) 2022 Andy
+' This program is free software: you can redistribute it and/or modify
+' it under the terms of the GNU General Public License as published by
+' the Free Software Foundation, either version 3 of the License, or
+' (at your option) any later version.
+'
+' This program is distributed in the hope that it will be useful,
+' but WITHOUT ANY WARRANTY
+Imports PersonalUtilities.Forms
+Imports PersonalUtilities.Forms.Toolbars
+Friend Class FDatePickerForm : Implements IOkCancelDeleteToolbar
+ Private MyDefs As DefaultFormProps
+ Friend ReadOnly Property SelectedDate As Date?
+ Get
+ If DT.Checked Then Return DT.Value.Date Else Return Nothing
+ End Get
+ End Property
+ Friend Sub New()
+ InitializeComponent()
+ End Sub
+ Private Sub FDatePickerForm_Load(sender As Object, e As EventArgs) Handles Me.Load
+ Try
+ MyDefs = New DefaultFormProps
+ With MyDefs
+ .MyViewInitialize(Me, Settings.Design, True)
+ .AddOkCancelToolbar()
+ .DelegateClosingChecker()
+ If Settings.LastUpdatedDate.HasValue Then
+ DT.Checked = True
+ DT.Value = Settings.LastUpdatedDate.Value.Date
+ Else
+ DT.Checked = False
+ End If
+ .EndLoaderOperations()
+ MyDefs.MyOkCancel.EnableOK = True
+ End With
+ Catch ex As Exception
+ MyDefs.InvokeLoaderError(ex)
+ End Try
+ End Sub
+ Private Sub ToolbarBttOK() Implements IOkCancelToolbar.ToolbarBttOK
+ MyDefs.CloseForm()
+ End Sub
+ Private Sub ToolbarBttCancel() Implements IOkCancelToolbar.ToolbarBttCancel
+ MyDefs.CloseForm(DialogResult.Cancel)
+ End Sub
+ Private Sub ToolbarBttDelete() Implements IOkCancelDeleteToolbar.ToolbarBttDelete
+ MyDefs.CloseForm(DialogResult.Abort)
+ End Sub
+End Class
\ No newline at end of file
diff --git a/SCrawler/LabelsKeeper.vb b/SCrawler/LabelsKeeper.vb
index ef80917..d2f2d0b 100644
--- a/SCrawler/LabelsKeeper.vb
+++ b/SCrawler/LabelsKeeper.vb
@@ -52,7 +52,7 @@ Friend Class LabelsKeeper : Implements ICollection(Of String), IMyEnumerator(Of
LabelsList.Sort()
TextSaver.SaveTextToFile(LabelsList.ListToString(, vbNewLine), LabelsFile, True, False, EDP.SendInLog)
Else
- If LabelsFile.Exists Then LabelsFile.Delete(,,, EDP.SendInLog)
+ LabelsFile.Delete(, Settings.DeleteMode, EDP.SendInLog)
End If
End Sub
Friend Overloads Sub Add(ByVal _Item As String) Implements ICollection(Of String).Add
@@ -65,10 +65,10 @@ Friend Class LabelsKeeper : Implements ICollection(Of String), IMyEnumerator(Of
If UpdateMainFrame Then RaiseEvent NewLabelAdded()
End If
End Sub
- Friend Sub AddRange(ByVal _Items As IEnumerable(Of String))
+ Friend Sub AddRange(ByVal _Items As IEnumerable(Of String), ByVal UpdateMainFrame As Boolean)
If _Items.ListExists Then
For Each i$ In _Items : Add(i, False) : Next
- RaiseEvent NewLabelAdded()
+ If UpdateMainFrame Then RaiseEvent NewLabelAdded()
End If
End Sub
Friend Function Contains(ByVal _Item As String) As Boolean Implements ICollection(Of String).Contains
@@ -93,10 +93,7 @@ Friend Class LabelsKeeper : Implements ICollection(Of String), IMyEnumerator(Of
Private disposedValue As Boolean = False
Protected Overridable Overloads Sub Dispose(ByVal disposing As Boolean)
If Not disposedValue Then
- If disposing Then
- Clear()
- CurrentSelection.Clear()
- End If
+ If disposing Then Clear() : CurrentSelection.Clear()
disposedValue = True
End If
End Sub
diff --git a/SCrawler/ListImagesLoader.vb b/SCrawler/ListImagesLoader.vb
new file mode 100644
index 0000000..7ddfa1a
--- /dev/null
+++ b/SCrawler/ListImagesLoader.vb
@@ -0,0 +1,187 @@
+' Copyright (C) 2022 Andy
+' This program is free software: you can redistribute it and/or modify
+' it under the terms of the GNU General Public License as published by
+' the Free Software Foundation, either version 3 of the License, or
+' (at your option) any later version.
+'
+' This program is distributed in the hope that it will be useful,
+' but WITHOUT ANY WARRANTY
+Imports SCrawler.API
+Imports SCrawler.API.Base
+Friend Class ListImagesLoader
+ Private ReadOnly Property MyList As ListView
+ Private Class UserOption : Implements IComparable(Of UserOption)
+ Friend ReadOnly User As IUserData
+ Friend ReadOnly LVI As ListViewItem
+ Friend Index As Integer
+ Friend ReadOnly Property Key As String
+ Get
+ Return LVI.Name
+ End Get
+ End Property
+ Friend [Image] As Image
+ Friend Sub New(ByVal u As IUserData, ByVal l As ListView, ByVal GetImage As Boolean)
+ User = u
+ LVI = u.GetLVI(l)
+ Index = u.Index
+ If GetImage Then Image = u.GetPicture
+ End Sub
+ Friend Sub UpdateImage()
+ Image = User.GetPicture
+ End Sub
+ Friend Function CompareTo(ByVal Other As UserOption) As Integer Implements IComparable(Of UserOption).CompareTo
+ Return Index.CompareTo(Other.Index)
+ End Function
+ End Class
+ Friend Sub New(ByRef l As ListView)
+ MyList = l
+ End Sub
+ Friend Sub Update()
+ Dim a As Action = Sub()
+ With MyList
+ .Items.Clear()
+ If Not .LargeImageList Is Nothing Then .LargeImageList.Images.Clear()
+ .LargeImageList = New ImageList
+ If Not .SmallImageList Is Nothing Then .SmallImageList.Images.Clear()
+ .SmallImageList = New ImageList
+ If Settings.ViewModeIsPicture Then
+ .LargeImageList.ColorDepth = ColorDepth.Depth32Bit
+ .SmallImageList.ColorDepth = ColorDepth.Depth32Bit
+ .LargeImageList.ImageSize = New Size(DivideWithZeroChecking(Settings.MaxLargeImageHeigh.Value, 100) * 75, Settings.MaxLargeImageHeigh.Value)
+ .SmallImageList.ImageSize = New Size(DivideWithZeroChecking(Settings.MaxSmallImageHeigh.Value, 100) * 75, Settings.MaxSmallImageHeigh.Value)
+ End If
+ End With
+ End Sub
+ If MyList.InvokeRequired Then MyList.Invoke(a) Else a.Invoke
+ If Settings.Users.Count > 0 Then
+ Settings.Users.Sort()
+ Dim v As View = Settings.ViewMode.Value
+ Dim i%
+
+ With MyList
+ MyList.BeginUpdate()
+
+ If Settings.FastProfilesLoading Then
+ Settings.Users.ListReindex
+ Dim UData As List(Of UserOption)
+
+ If Settings.ViewModeIsPicture Then
+ UData = GetUsersWithImages()
+ If UData.ListExists Then
+ UData.Sort()
+ Select Case v
+ Case View.LargeIcon : .LargeImageList.Images.AddRange(UData.Select(Function(u) u.Image).ToArray)
+ Case View.SmallIcon : .SmallImageList.Images.AddRange(UData.Select(Function(u) u.Image).ToArray)
+ End Select
+ End If
+ Else
+ UData = (From u As IUserData In Settings.Users Where u.FitToAddParams Select New UserOption(u, MyList, False)).ListIfNothing
+ If UData.ListExists Then UData.Sort()
+ End If
+
+ If UData.ListExists Then
+ If Settings.ViewModeIsPicture Then
+ For i = 0 To UData.Count - 1
+ Select Case v
+ Case View.LargeIcon : .LargeImageList.Images.SetKeyName(i, UData(i).Key)
+ Case View.SmallIcon : .SmallImageList.Images.SetKeyName(i, UData(i).Key)
+ End Select
+ Next
+ End If
+ .Items.AddRange(UData.Select(Function(u) u.LVI).ToArray)
+ UData.Clear()
+ End If
+ Else
+ Dim t As New List(Of Task)
+ For Each User As IUserData In Settings.Users
+ If Settings.ViewModeIsPicture Then
+ t.Add(Task.Run(Sub() UpdateUser(User, True)))
+ Else
+ UpdateUser(User, True)
+ End If
+ Next
+ If t.Count > 0 Then Task.WhenAll(t.ToArray) : t.Clear()
+ End If
+ End With
+ MyList.EndUpdate()
+ End If
+ End Sub
+ Friend Sub UpdateUser(ByVal User As IUserData, ByVal Add As Boolean)
+ Try
+ Dim a As Action
+ If Add Then
+ a = Sub()
+ With MyList
+ Select Case Settings.ViewMode.Value
+ Case View.LargeIcon : .LargeImageList.Images.Add(User.Key, User.GetPicture())
+ Case View.SmallIcon : .SmallImageList.Images.Add(User.Key, User.GetPicture())
+ End Select
+ .Items.Add(User.GetLVI(MyList))
+ End With
+ End Sub
+ Else
+ a = Sub()
+ With MyList
+ Dim i% = .Items.IndexOfKey(User.Key)
+ Dim ImgIndx%
+ If i >= 0 Then
+ Select Case Settings.ViewMode.Value
+ Case View.LargeIcon
+ ImgIndx = .LargeImageList.Images.IndexOfKey(User.Key)
+ If ImgIndx >= 0 Then .LargeImageList.Images(ImgIndx) = User.GetPicture()
+ Case View.SmallIcon
+ ImgIndx = .SmallImageList.Images.IndexOfKey(User.Key)
+ If ImgIndx >= 0 Then .SmallImageList.Images(ImgIndx) = User.GetPicture()
+ End Select
+ With .Items(i) : .Text = User.ToString() : .Group = User.GetLVIGroup(MyList) : End With
+ ApplyLVIColor(User, .Items(i), False)
+ End If
+ End With
+ End Sub
+ End If
+ If MyList.InvokeRequired Then MyList.Invoke(a) Else a.Invoke
+ Catch ex As Exception
+ End Try
+ End Sub
+ Friend Shared Function ApplyLVIColor(ByVal User As IUserData, ByVal LVI As ListViewItem, ByVal IsInit As Boolean) As ListViewItem
+ With LVI
+ If Not User.Exists Then
+ .BackColor = ColorBttDeleteBack
+ .ForeColor = ColorBttDeleteFore
+ ElseIf User.Suspended Then
+ .BackColor = ColorBttEditBack
+ .ForeColor = ColorBttEditFore
+ ElseIf CheckUserCollection(User) Then
+ .BackColor = Color.LightSkyBlue
+ .ForeColor = Color.MidnightBlue
+ ElseIf Not IsInit Then
+ .BackColor = SystemColors.Window
+ .ForeColor = SystemColors.WindowText
+ End If
+ End With
+ Return LVI
+ End Function
+ Private Shared Function CheckUserCollection(ByVal User As IUserData) As Boolean
+ If User.IsCollection Then
+ With DirectCast(User, UserDataBind)
+ If .Count > 0 Then Return .Collections.Exists(Function(c) Not c.Exists) Else Return False
+ End With
+ Else
+ Return False
+ End If
+ End Function
+ Private Function GetUsersWithImages() As List(Of UserOption)
+ Dim t As New List(Of Task)
+ Dim l As New List(Of UserOption)
+ For Each u As IUserData In Settings.Users
+ If u.FitToAddParams Then t.Add(Task.Run(Sub() l.Add(New UserOption(u, MyList, True))))
+ Next
+ If t.Count > 0 Then Task.WaitAll(t.ToArray) : t.Clear()
+ If l.Count > 0 Then
+ For i% = 0 To l.Count - 1
+ If l(i).Image Is Nothing Then l(i).UpdateImage()
+ Next
+ End If
+ Return l
+ End Function
+End Class
\ No newline at end of file
diff --git a/SCrawler/MainFrame.Designer.vb b/SCrawler/MainFrame.Designer.vb
index 289307e..3042f43 100644
--- a/SCrawler/MainFrame.Designer.vb
+++ b/SCrawler/MainFrame.Designer.vb
@@ -17,7 +17,6 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form
Dim SEP_1 As System.Windows.Forms.ToolStripSeparator
Dim SEP_2 As System.Windows.Forms.ToolStripSeparator
Dim CONTEXT_SEP_1 As System.Windows.Forms.ToolStripSeparator
- Dim MENU_SETTINGS As System.Windows.Forms.ToolStripDropDownButton
Dim MENU_SETTINGS_SEP_1 As System.Windows.Forms.ToolStripSeparator
Dim SEP_3 As System.Windows.Forms.ToolStripSeparator
Dim CONTEXT_SEP_2 As System.Windows.Forms.ToolStripSeparator
@@ -29,11 +28,9 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form
Dim MENU_VIEW_SEP_3 As System.Windows.Forms.ToolStripSeparator
Dim MENU_VIEW_SEP_2 As System.Windows.Forms.ToolStripSeparator
Dim TRAY_SEP_1 As System.Windows.Forms.ToolStripSeparator
+ Dim MENU_VIEW_SEP_4 As System.Windows.Forms.ToolStripSeparator
Dim resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(MainFrame))
- Me.BTT_SETTINGS_REDDIT = New System.Windows.Forms.ToolStripMenuItem()
- Me.BTT_SETTINGS_TWITTER = New System.Windows.Forms.ToolStripMenuItem()
- Me.BTT_SETTINGS_INSTAGRAM = New System.Windows.Forms.ToolStripMenuItem()
- Me.BTT_SETTINGS_REDGIFS = 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()
Me.BTT_ADD_USER = New System.Windows.Forms.ToolStripButton()
@@ -51,15 +48,19 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form
Me.BTT_VIEW_LARGE = New System.Windows.Forms.ToolStripMenuItem()
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_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()
Me.BTT_SHOW_REGULAR = New System.Windows.Forms.ToolStripMenuItem()
Me.BTT_SHOW_TEMP = New System.Windows.Forms.ToolStripMenuItem()
Me.BTT_SHOW_FAV = New System.Windows.Forms.ToolStripMenuItem()
+ Me.BTT_SHOW_DELETED = New System.Windows.Forms.ToolStripMenuItem()
+ Me.BTT_SHOW_SUSPENDED = New System.Windows.Forms.ToolStripMenuItem()
Me.BTT_SHOW_LABELS = New System.Windows.Forms.ToolStripMenuItem()
Me.BTT_SHOW_NO_LABELS = New System.Windows.Forms.ToolStripMenuItem()
Me.BTT_SELECT_LABELS = New System.Windows.Forms.ToolStripMenuItem()
+ Me.BTT_SHOW_LIMIT_DATES = New System.Windows.Forms.ToolStripMenuItem()
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()
@@ -67,10 +68,8 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form
Me.PR_MAIN = New System.Windows.Forms.ToolStripProgressBar()
Me.LBL_JOBS_COUNT = New System.Windows.Forms.ToolStripStatusLabel()
Me.LBL_STATUS = New System.Windows.Forms.ToolStripStatusLabel()
- Me.PR_INST = New System.Windows.Forms.ToolStripProgressBar()
- Me.LBL_JOBS_INST_COUNT = New System.Windows.Forms.ToolStripStatusLabel()
- Me.LBL_STATUS_INST = New System.Windows.Forms.ToolStripStatusLabel()
Me.LIST_PROFILES = New System.Windows.Forms.ListView()
+ Me.COL_DEF = CType(New System.Windows.Forms.ColumnHeader(), System.Windows.Forms.ColumnHeader)
Me.USER_CONTEXT = New System.Windows.Forms.ContextMenuStrip(Me.components)
Me.BTT_CONTEXT_DOWN = New System.Windows.Forms.ToolStripMenuItem()
Me.BTT_CONTEXT_DOWN_LIMITED = New System.Windows.Forms.ToolStripMenuItem()
@@ -91,10 +90,10 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form
Me.TRAY_CONTEXT = New System.Windows.Forms.ContextMenuStrip(Me.components)
Me.BTT_TRAY_SHOW_HIDE = New System.Windows.Forms.ToolStripMenuItem()
Me.BTT_TRAY_CLOSE = New System.Windows.Forms.ToolStripMenuItem()
+ Me.BTT_PR_INFO = New System.Windows.Forms.ToolStripStatusLabel()
SEP_1 = New System.Windows.Forms.ToolStripSeparator()
SEP_2 = New System.Windows.Forms.ToolStripSeparator()
CONTEXT_SEP_1 = New System.Windows.Forms.ToolStripSeparator()
- MENU_SETTINGS = New System.Windows.Forms.ToolStripDropDownButton()
MENU_SETTINGS_SEP_1 = New System.Windows.Forms.ToolStripSeparator()
SEP_3 = New System.Windows.Forms.ToolStripSeparator()
CONTEXT_SEP_2 = New System.Windows.Forms.ToolStripSeparator()
@@ -106,6 +105,7 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form
MENU_VIEW_SEP_3 = New System.Windows.Forms.ToolStripSeparator()
MENU_VIEW_SEP_2 = New System.Windows.Forms.ToolStripSeparator()
TRAY_SEP_1 = New System.Windows.Forms.ToolStripSeparator()
+ MENU_VIEW_SEP_4 = New System.Windows.Forms.ToolStripSeparator()
Me.Toolbar_TOP.SuspendLayout()
Me.Toolbar_BOTTOM.SuspendLayout()
Me.USER_CONTEXT.SuspendLayout()
@@ -127,53 +127,10 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form
CONTEXT_SEP_1.Name = "CONTEXT_SEP_1"
CONTEXT_SEP_1.Size = New System.Drawing.Size(218, 6)
'
- 'MENU_SETTINGS
- '
- MENU_SETTINGS.AutoToolTip = False
- MENU_SETTINGS.DropDownItems.AddRange(New System.Windows.Forms.ToolStripItem() {Me.BTT_SETTINGS_REDDIT, Me.BTT_SETTINGS_TWITTER, Me.BTT_SETTINGS_INSTAGRAM, Me.BTT_SETTINGS_REDGIFS, MENU_SETTINGS_SEP_1, Me.BTT_SETTINGS})
- MENU_SETTINGS.ImageTransparentColor = System.Drawing.Color.Magenta
- MENU_SETTINGS.Name = "MENU_SETTINGS"
- MENU_SETTINGS.Size = New System.Drawing.Size(62, 22)
- MENU_SETTINGS.Text = "Settings"
- '
- 'BTT_SETTINGS_REDDIT
- '
- Me.BTT_SETTINGS_REDDIT.Image = Global.SCrawler.My.Resources.Resources.RedditPic512
- Me.BTT_SETTINGS_REDDIT.Name = "BTT_SETTINGS_REDDIT"
- Me.BTT_SETTINGS_REDDIT.Size = New System.Drawing.Size(127, 22)
- Me.BTT_SETTINGS_REDDIT.Text = "Reddit"
- '
- 'BTT_SETTINGS_TWITTER
- '
- Me.BTT_SETTINGS_TWITTER.Image = Global.SCrawler.My.Resources.Resources.TwitterPic400
- Me.BTT_SETTINGS_TWITTER.Name = "BTT_SETTINGS_TWITTER"
- Me.BTT_SETTINGS_TWITTER.Size = New System.Drawing.Size(127, 22)
- Me.BTT_SETTINGS_TWITTER.Text = "Twitter"
- '
- 'BTT_SETTINGS_INSTAGRAM
- '
- Me.BTT_SETTINGS_INSTAGRAM.Image = Global.SCrawler.My.Resources.Resources.InstagramPic76
- Me.BTT_SETTINGS_INSTAGRAM.Name = "BTT_SETTINGS_INSTAGRAM"
- Me.BTT_SETTINGS_INSTAGRAM.Size = New System.Drawing.Size(127, 22)
- Me.BTT_SETTINGS_INSTAGRAM.Text = "Instagram"
- '
- 'BTT_SETTINGS_REDGIFS
- '
- Me.BTT_SETTINGS_REDGIFS.Name = "BTT_SETTINGS_REDGIFS"
- Me.BTT_SETTINGS_REDGIFS.Size = New System.Drawing.Size(127, 22)
- Me.BTT_SETTINGS_REDGIFS.Text = "RedGifs"
- '
'MENU_SETTINGS_SEP_1
'
MENU_SETTINGS_SEP_1.Name = "MENU_SETTINGS_SEP_1"
- MENU_SETTINGS_SEP_1.Size = New System.Drawing.Size(124, 6)
- '
- 'BTT_SETTINGS
- '
- Me.BTT_SETTINGS.Image = Global.SCrawler.My.Resources.Resources.SettingsPic_16
- Me.BTT_SETTINGS.Name = "BTT_SETTINGS"
- Me.BTT_SETTINGS.Size = New System.Drawing.Size(127, 22)
- Me.BTT_SETTINGS.Text = "Settings"
+ MENU_SETTINGS_SEP_1.Size = New System.Drawing.Size(113, 6)
'
'SEP_3
'
@@ -225,10 +182,31 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form
TRAY_SEP_1.Name = "TRAY_SEP_1"
TRAY_SEP_1.Size = New System.Drawing.Size(130, 6)
'
+ 'MENU_VIEW_SEP_4
+ '
+ MENU_VIEW_SEP_4.Name = "MENU_VIEW_SEP_4"
+ MENU_VIEW_SEP_4.Size = New System.Drawing.Size(141, 6)
+ '
+ 'MENU_SETTINGS
+ '
+ Me.MENU_SETTINGS.AutoToolTip = False
+ Me.MENU_SETTINGS.DropDownItems.AddRange(New System.Windows.Forms.ToolStripItem() {MENU_SETTINGS_SEP_1, Me.BTT_SETTINGS})
+ Me.MENU_SETTINGS.ImageTransparentColor = System.Drawing.Color.Magenta
+ Me.MENU_SETTINGS.Name = "MENU_SETTINGS"
+ Me.MENU_SETTINGS.Size = New System.Drawing.Size(62, 22)
+ Me.MENU_SETTINGS.Text = "Settings"
+ '
+ 'BTT_SETTINGS
+ '
+ Me.BTT_SETTINGS.Image = Global.SCrawler.My.Resources.Resources.SettingsPic_16
+ Me.BTT_SETTINGS.Name = "BTT_SETTINGS"
+ Me.BTT_SETTINGS.Size = New System.Drawing.Size(116, 22)
+ Me.BTT_SETTINGS.Text = "Settings"
+ '
'Toolbar_TOP
'
Me.Toolbar_TOP.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden
- Me.Toolbar_TOP.Items.AddRange(New System.Windows.Forms.ToolStripItem() {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_CHANNELS, Me.BTT_DOWN_SAVED, SEP_2, Me.BTT_DOWN_SELECTED, Me.BTT_DOWN_ALL, Me.BTT_DOWN_VIDEO, 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, Me.BTT_SHOW_INFO, Me.BTT_CHANNELS, Me.BTT_DOWN_SAVED, SEP_2, Me.BTT_DOWN_SELECTED, Me.BTT_DOWN_ALL, Me.BTT_DOWN_VIDEO, Me.BTT_DOWN_STOP, SEP_3, Me.MENU_VIEW, SEP_4, Me.BTT_LOG, Me.BTT_VERSION_INFO, Me.BTT_DONATE})
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)
@@ -339,7 +317,7 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form
'
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.BTT_VIEW_LARGE, Me.BTT_VIEW_SMALL, Me.BTT_VIEW_LIST, 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_LABELS, Me.BTT_SHOW_NO_LABELS, MENU_VIEW_SEP_3, Me.BTT_SELECT_LABELS})
+ 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, MENU_VIEW_SEP_3, Me.BTT_SELECT_LABELS, MENU_VIEW_SEP_4, Me.BTT_SHOW_LIMIT_DATES})
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"
@@ -364,6 +342,12 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form
Me.BTT_VIEW_LIST.Size = New System.Drawing.Size(144, 22)
Me.BTT_VIEW_LIST.Text = "List"
'
+ 'BTT_VIEW_DETAILS
+ '
+ Me.BTT_VIEW_DETAILS.Name = "BTT_VIEW_DETAILS"
+ Me.BTT_VIEW_DETAILS.Size = New System.Drawing.Size(144, 22)
+ Me.BTT_VIEW_DETAILS.Text = "Details"
+ '
'BTT_SITE_ALL
'
Me.BTT_SITE_ALL.Name = "BTT_SITE_ALL"
@@ -400,6 +384,22 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form
Me.BTT_SHOW_FAV.Size = New System.Drawing.Size(144, 22)
Me.BTT_SHOW_FAV.Text = "Favorites"
'
+ 'BTT_SHOW_DELETED
+ '
+ Me.BTT_SHOW_DELETED.BackColor = System.Drawing.Color.FromArgb(CType(CType(255, Byte), Integer), CType(CType(192, Byte), Integer), CType(CType(192, Byte), Integer))
+ Me.BTT_SHOW_DELETED.ForeColor = System.Drawing.Color.Maroon
+ Me.BTT_SHOW_DELETED.Name = "BTT_SHOW_DELETED"
+ Me.BTT_SHOW_DELETED.Size = New System.Drawing.Size(144, 22)
+ Me.BTT_SHOW_DELETED.Text = "Deleted"
+ '
+ 'BTT_SHOW_SUSPENDED
+ '
+ Me.BTT_SHOW_SUSPENDED.BackColor = System.Drawing.Color.PapayaWhip
+ Me.BTT_SHOW_SUSPENDED.ForeColor = System.Drawing.Color.SaddleBrown
+ Me.BTT_SHOW_SUSPENDED.Name = "BTT_SHOW_SUSPENDED"
+ Me.BTT_SHOW_SUSPENDED.Size = New System.Drawing.Size(144, 22)
+ Me.BTT_SHOW_SUSPENDED.Text = "Suspended"
+ '
'BTT_SHOW_LABELS
'
Me.BTT_SHOW_LABELS.Name = "BTT_SHOW_LABELS"
@@ -418,6 +418,14 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form
Me.BTT_SELECT_LABELS.Size = New System.Drawing.Size(144, 22)
Me.BTT_SELECT_LABELS.Text = "Select labels"
'
+ 'BTT_SHOW_LIMIT_DATES
+ '
+ Me.BTT_SHOW_LIMIT_DATES.AutoToolTip = True
+ Me.BTT_SHOW_LIMIT_DATES.Name = "BTT_SHOW_LIMIT_DATES"
+ Me.BTT_SHOW_LIMIT_DATES.Size = New System.Drawing.Size(144, 22)
+ Me.BTT_SHOW_LIMIT_DATES.Text = "Limit dates"
+ Me.BTT_SHOW_LIMIT_DATES.ToolTipText = "Show profiles that haven't downloaded new data since date..."
+ '
'BTT_LOG
'
Me.BTT_LOG.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text
@@ -452,7 +460,7 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form
'
'Toolbar_BOTTOM
'
- Me.Toolbar_BOTTOM.Items.AddRange(New System.Windows.Forms.ToolStripItem() {Me.PR_MAIN, Me.LBL_JOBS_COUNT, Me.LBL_STATUS, Me.PR_INST, Me.LBL_JOBS_INST_COUNT, Me.LBL_STATUS_INST})
+ Me.Toolbar_BOTTOM.Items.AddRange(New System.Windows.Forms.ToolStripItem() {Me.BTT_PR_INFO, Me.PR_MAIN, Me.LBL_JOBS_COUNT, Me.LBL_STATUS})
Me.Toolbar_BOTTOM.Location = New System.Drawing.Point(0, 439)
Me.Toolbar_BOTTOM.Name = "Toolbar_BOTTOM"
Me.Toolbar_BOTTOM.Size = New System.Drawing.Size(934, 22)
@@ -474,27 +482,13 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form
Me.LBL_STATUS.Name = "LBL_STATUS"
Me.LBL_STATUS.Size = New System.Drawing.Size(0, 17)
'
- 'PR_INST
- '
- Me.PR_INST.Name = "PR_INST"
- Me.PR_INST.Size = New System.Drawing.Size(200, 16)
- Me.PR_INST.Visible = False
- '
- 'LBL_JOBS_INST_COUNT
- '
- Me.LBL_JOBS_INST_COUNT.Name = "LBL_JOBS_INST_COUNT"
- Me.LBL_JOBS_INST_COUNT.Size = New System.Drawing.Size(0, 17)
- '
- 'LBL_STATUS_INST
- '
- Me.LBL_STATUS_INST.Name = "LBL_STATUS_INST"
- Me.LBL_STATUS_INST.Size = New System.Drawing.Size(0, 17)
- '
'LIST_PROFILES
'
Me.LIST_PROFILES.Activation = System.Windows.Forms.ItemActivation.OneClick
+ Me.LIST_PROFILES.Columns.AddRange(New System.Windows.Forms.ColumnHeader() {Me.COL_DEF})
Me.LIST_PROFILES.ContextMenuStrip = Me.USER_CONTEXT
Me.LIST_PROFILES.Dock = System.Windows.Forms.DockStyle.Fill
+ Me.LIST_PROFILES.GridLines = True
Me.LIST_PROFILES.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None
Me.LIST_PROFILES.HideSelection = False
Me.LIST_PROFILES.Location = New System.Drawing.Point(0, 25)
@@ -503,6 +497,10 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form
Me.LIST_PROFILES.TabIndex = 3
Me.LIST_PROFILES.UseCompatibleStateImageBehavior = False
'
+ 'COL_DEF
+ '
+ Me.COL_DEF.Text = "Data"
+ '
'USER_CONTEXT
'
Me.USER_CONTEXT.Items.AddRange(New System.Windows.Forms.ToolStripItem() {Me.BTT_CONTEXT_DOWN, Me.BTT_CONTEXT_DOWN_LIMITED, Me.BTT_CONTEXT_EDIT, Me.BTT_CONTEXT_DELETE, CONTEXT_SEP_1, Me.BTT_CONTEXT_FAV, Me.BTT_CONTEXT_TEMP, Me.BTT_CONTEXT_READY, Me.BTT_CONTEXT_GROUPS, 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})
@@ -643,6 +641,14 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form
Me.BTT_TRAY_CLOSE.Size = New System.Drawing.Size(133, 22)
Me.BTT_TRAY_CLOSE.Text = "Close"
'
+ 'BTT_PR_INFO
+ '
+ Me.BTT_PR_INFO.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image
+ Me.BTT_PR_INFO.Image = Global.SCrawler.My.Resources.Resources.InfoPic_32
+ Me.BTT_PR_INFO.Name = "BTT_PR_INFO"
+ Me.BTT_PR_INFO.Padding = New System.Windows.Forms.Padding(0, 0, 3, 0)
+ Me.BTT_PR_INFO.Size = New System.Drawing.Size(19, 17)
+ '
'MainFrame
'
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
@@ -666,9 +672,6 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form
Me.PerformLayout()
End Sub
-
- Private WithEvents BTT_SETTINGS_REDDIT As ToolStripMenuItem
- Private WithEvents BTT_SETTINGS_TWITTER As ToolStripMenuItem
Private WithEvents BTT_SETTINGS As ToolStripMenuItem
Private WithEvents BTT_ADD_USER As ToolStripButton
Private WithEvents BTT_DELETE_USER As ToolStripButton
@@ -714,18 +717,20 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form
Private WithEvents BTT_VERSION_INFO As ToolStripButton
Private WithEvents BTT_CONTEXT_DOWN_LIMITED As ToolStripMenuItem
Private WithEvents BTT_CONTEXT_READY As ToolStripMenuItem
- Private WithEvents BTT_SETTINGS_INSTAGRAM As ToolStripMenuItem
Private WithEvents BTT_SITE_ALL As ToolStripMenuItem
Private WithEvents BTT_SITE_SPECIFIC As ToolStripMenuItem
Private WithEvents BTT_CONTEXT_CHANGE_FOLDER As ToolStripMenuItem
Private WithEvents BTT_DOWN_SAVED As ToolStripButton
- Private WithEvents PR_INST As ToolStripProgressBar
- Private WithEvents LBL_JOBS_INST_COUNT As ToolStripStatusLabel
- Private WithEvents LBL_STATUS_INST As ToolStripStatusLabel
Private WithEvents TrayIcon As NotifyIcon
Private WithEvents TRAY_CONTEXT As ContextMenuStrip
Private WithEvents BTT_TRAY_SHOW_HIDE As ToolStripMenuItem
Private WithEvents BTT_TRAY_CLOSE As ToolStripMenuItem
- Private WithEvents BTT_SETTINGS_REDGIFS As ToolStripMenuItem
Private WithEvents BTT_DONATE As ToolStripButton
+ Private WithEvents BTT_SHOW_DELETED As ToolStripMenuItem
+ Private WithEvents BTT_SHOW_SUSPENDED As ToolStripMenuItem
+ Private WithEvents BTT_SHOW_LIMIT_DATES As ToolStripMenuItem
+ Private WithEvents BTT_VIEW_DETAILS As ToolStripMenuItem
+ Private WithEvents COL_DEF As ColumnHeader
+ Private WithEvents MENU_SETTINGS As ToolStripDropDownButton
+ Private WithEvents BTT_PR_INFO As ToolStripStatusLabel
End Class
\ No newline at end of file
diff --git a/SCrawler/MainFrame.resx b/SCrawler/MainFrame.resx
index 073a238..49f5134 100644
--- a/SCrawler/MainFrame.resx
+++ b/SCrawler/MainFrame.resx
@@ -126,9 +126,6 @@
False
-
- False
-
False
@@ -162,6 +159,9 @@
False
+
+ False
+
132, 17
diff --git a/SCrawler/MainFrame.vb b/SCrawler/MainFrame.vb
index 64641e6..f20fe77 100644
--- a/SCrawler/MainFrame.vb
+++ b/SCrawler/MainFrame.vb
@@ -13,6 +13,8 @@ Imports PersonalUtilities.Forms
Imports SCrawler.API
Imports SCrawler.API.Base
Imports SCrawler.Editors
+Imports SCrawler.DownloadObjects
+Imports SCrawler.Plugin.Hosts
Public Class MainFrame
Private MyView As FormsView
Private ReadOnly _VideoDownloadingMode As Boolean = False
@@ -26,6 +28,13 @@ Public Class MainFrame
n.TimeSeparator = String.Empty
Twitter.DateProvider = New ADateTime(DirectCast(n.Clone, DateTimeFormatInfo)) With {.DateTimeStyle = DateTimeStyles.AssumeUniversal}
Settings = New SettingsCLS
+ With Settings.Plugins
+ If .Count > 0 Then
+ For i% = 0 To .Count - 1
+ MENU_SETTINGS.DropDownItems.Insert(MENU_SETTINGS.DropDownItems.Count - 2, .Item(i).Settings.GetSettingsButton)
+ Next
+ End If
+ End With
Dim Args() As String = Environment.GetCommandLineArgs
If Args.ListExists(2) AndAlso Args(1) = "v" Then
Using f As New VideosDownloaderForm : f.ShowDialog() : End Using
@@ -34,61 +43,46 @@ Public Class MainFrame
End Sub
Private Sub MainFrame_Load(sender As Object, e As EventArgs) Handles Me.Load
If _VideoDownloadingMode Then GoTo FormClosingInvoker
- Settings.DeleteCachPath()
- MainProgress = New Toolbars.MyProgress(Toolbar_BOTTOM, PR_MAIN, LBL_STATUS) With {.DropCurrentProgressOnTotalChange = False, .Enabled = False}
- MainProgressInst = New Toolbars.MyProgress(Toolbar_BOTTOM, PR_INST, LBL_STATUS_INST) With {.DropCurrentProgressOnTotalChange = False, .Enabled = False}
+ Settings.DeleteCachePath()
+ MainFrameObj = New MainFrameObjects(Me)
+ MainProgress = New Toolbars.MyProgress(Toolbar_BOTTOM, PR_MAIN, LBL_STATUS, "Downloading profiles' data") With {
+ .DropCurrentProgressOnTotalChange = False, .Enabled = False}
Downloader = New TDownloader
InfoForm = New DownloadedInfoForm
+ MyProgressForm = New ActiveDownloadingProgress
+ Downloader.ReconfPool()
AddHandler Downloader.OnJobsChange, AddressOf Downloader_UpdateJobsCount
AddHandler Downloader.OnDownloading, AddressOf Downloader_OnDownloading
AddHandler Downloader.OnDownloadCountChange, AddressOf InfoForm.Downloader_OnDownloadCountChange
AddHandler Downloader.SendNotification, AddressOf NotificationMessage
+ AddHandler InfoForm.OnUserLooking, AddressOf Info_OnUserLooking
Settings.LoadUsers()
MyView = New FormsView(Me)
MyView.ImportFromXML(Settings.Design)
MyView.SetMeSize()
If Settings.CloseToTray Then TrayIcon.Visible = True
- Dim gk$
With LIST_PROFILES.Groups
- 'Collections
- gk = GetLviGroupName(Sites.Undefined, False, True, True, False)
- .Add(New ListViewGroup(gk, gk))
- gk = GetLviGroupName(Sites.Undefined, False, False, True, False)
- .Add(New ListViewGroup(gk, gk))
- gk = GetLviGroupName(Sites.Undefined, True, False, True, False)
- .Add(New ListViewGroup(gk, gk))
- 'Channels
- gk = GetLviGroupName(Sites.Undefined, False, True, False, True)
- .Add(New ListViewGroup(gk, gk))
- gk = GetLviGroupName(Sites.Undefined, False, False, False, True)
- .Add(New ListViewGroup(gk, gk))
- gk = GetLviGroupName(Sites.Undefined, True, False, False, True)
- .Add(New ListViewGroup(gk, gk))
- 'Sites
- For Each s As Sites In [Enum].GetValues(GetType(Sites))
- If Not s = Sites.Undefined Then
- gk = GetLviGroupName(s, False, True, False, False)
- .Add(New ListViewGroup(gk, gk))
- gk = GetLviGroupName(s, False, False, False, False)
- .Add(New ListViewGroup(gk, gk))
- gk = GetLviGroupName(s, True, False, False, False)
- .Add(New ListViewGroup(gk, gk))
- End If
- Next
+ .AddRange(GetLviGroupName(Nothing, True, False)) 'collections
+ .AddRange(GetLviGroupName(Nothing, False, True)) 'channels
+ If Settings.Plugins.Count > 0 Then
+ For Each h As SettingsHost In Settings.Plugins.Select(Function(hh) hh.Settings) : .AddRange(GetLviGroupName(h, False, False)) : Next
+ End If
If Settings.Labels.Count > 0 Then Settings.Labels.ToList.ForEach(Sub(l) .Add(New ListViewGroup(l, l)))
.Add(Settings.Labels.NoLabel)
End With
With Settings
LIST_PROFILES.View = .ViewMode
- SetViewButtonsCheckers(.ViewMode.Value = View.LargeIcon, .ViewMode.Value = View.SmallIcon, .ViewMode.Value = View.List)
+ ApplyViewPattern(.ViewMode.Value)
AddHandler .Labels.NewLabelAdded, AddressOf UpdateLabelsGroups
End With
+ UserListLoader = New ListImagesLoader(LIST_PROFILES)
RefillList()
UpdateLabelsGroups()
SetShowButtonsCheckers(Settings.ShowingMode.Value)
CheckVersion(False)
BTT_SITE_ALL.Checked = Settings.SelectedSites.Count = 0
BTT_SITE_SPECIFIC.Checked = Settings.SelectedSites.Count > 0
+ BTT_SHOW_LIMIT_DATES.Checked = Settings.LastUpdatedDate.HasValue
_UFinit = False
GoTo EndFunction
FormClosingInvoker:
@@ -145,6 +139,7 @@ DropCloseParams:
_CloseInvoked = False
Exit Sub
CloseContinue:
+ If Not BATCH Is Nothing Then BATCH.Dispose() : BATCH = Nothing
If Not MyMainLOG.IsEmptyString Then SaveLogToFile()
If _CloseInvoked Then Close()
CloseResume:
@@ -175,6 +170,7 @@ CloseResume:
Select Case e.KeyCode
Case Keys.Insert : BTT_ADD_USER.PerformClick()
Case Keys.Delete : DeleteSelectedUser()
+ Case Keys.Enter : OpenFolder()
Case Keys.F1 : BTT_VERSION_INFO.PerformClick()
Case Keys.F2 : DownloadVideoByURL()
Case Keys.F3 : EditSelectedUser()
@@ -188,92 +184,11 @@ CloseResume:
CheckVersion(True)
End Sub
Friend Sub RefillList()
- Dim a As Action = Sub()
- With LIST_PROFILES
- .Items.Clear()
- If Not .LargeImageList Is Nothing Then .LargeImageList.Images.Clear()
- .LargeImageList = New ImageList
- If Not .SmallImageList Is Nothing Then .SmallImageList.Images.Clear()
- .SmallImageList = New ImageList
- .LargeImageList.ColorDepth = ColorDepth.Depth32Bit
- .SmallImageList.ColorDepth = ColorDepth.Depth32Bit
- .LargeImageList.ImageSize = New Size(DivideWithZeroChecking(Settings.MaxLargeImageHeigh.Value, 100) * 75, Settings.MaxLargeImageHeigh.Value)
- .SmallImageList.ImageSize = New Size(DivideWithZeroChecking(Settings.MaxSmallImageHeigh.Value, 100) * 75, Settings.MaxSmallImageHeigh.Value)
- End With
- End Sub
- If LIST_PROFILES.InvokeRequired Then LIST_PROFILES.Invoke(a) Else a.Invoke
- If Settings.Users.Count > 0 Then
- Settings.Users.Sort()
- Dim t As New List(Of Task)
- For Each User As IUserData In Settings.Users
- If User.FitToAddParams Then
- If Settings.ViewModeIsPicture Then
- t.Add(Task.Run(Sub() UserListUpdate(User, True)))
- Else
- UserListUpdate(User, True)
- End If
- End If
- Next
- If t.Count > 0 Then Task.WhenAll(t.ToArray) : t.Clear()
- End If
+ UserListLoader.Update()
+ GC.Collect()
End Sub
Private Sub UserListUpdate(ByVal User As IUserData, ByVal Add As Boolean)
- Try
- Dim a As Action
- If Add Then
- a = Sub()
- With LIST_PROFILES
- Select Case Settings.ViewMode.Value
- Case View.LargeIcon : .LargeImageList.Images.Add(User.LVIKey, User.GetPicture())
- Case View.SmallIcon : .SmallImageList.Images.Add(User.LVIKey, User.GetPicture())
- End Select
- .Items.Add(User.GetLVI(LIST_PROFILES))
- If Not User.Exists Then
- With .Items(.Items.Count - 1)
- .BackColor = ColorBttDeleteBack
- .ForeColor = ColorBttDeleteFore
- End With
- ElseIf User.Suspended Then
- With .Items(.Items.Count - 1)
- .BackColor = ColorBttEditBack
- .ForeColor = ColorBttEditFore
- End With
- End If
- End With
- End Sub
- Else
- a = Sub()
- With LIST_PROFILES
- Dim i% = .Items.IndexOfKey(User.LVIKey)
- Dim ImgIndx%
- If i >= 0 Then
- Select Case Settings.ViewMode.Value
- Case View.LargeIcon
- ImgIndx = .LargeImageList.Images.IndexOfKey(User.LVIKey)
- If ImgIndx >= 0 Then .LargeImageList.Images(ImgIndx) = User.GetPicture()
- Case View.SmallIcon
- ImgIndx = .SmallImageList.Images.IndexOfKey(User.LVIKey)
- If ImgIndx >= 0 Then .SmallImageList.Images(ImgIndx) = User.GetPicture()
- End Select
- .Items(i).Text = User.ToString
- .Items(i).Group = User.GetLVIGroup(LIST_PROFILES)
- If Not User.Exists Then
- .Items(i).BackColor = ColorBttDeleteBack
- .Items(i).ForeColor = ColorBttDeleteFore
- ElseIf User.Suspended Then
- .Items(i).BackColor = ColorBttEditBack
- .Items(i).ForeColor = ColorBttEditFore
- Else
- .Items(i).BackColor = SystemColors.Window
- .Items(i).ForeColor = SystemColors.WindowText
- End If
- End If
- End With
- End Sub
- End If
- If LIST_PROFILES.InvokeRequired Then LIST_PROFILES.Invoke(a) Else a.Invoke
- Catch ex As Exception
- End Try
+ UserListLoader.UpdateUser(User, Add)
End Sub
Private Sub UpdateLabelsGroups()
If Settings.Labels.NewLabelsExists Then
@@ -297,18 +212,6 @@ CloseResume:
End Sub
#Region "Toolbar buttons"
#Region "Settings"
- Private Sub BTT_SETTINGS_REDDIT_Click(sender As Object, e As EventArgs) Handles BTT_SETTINGS_REDDIT.Click
- Using f As New SiteEditorForm(Sites.Reddit) : f.ShowDialog() : End Using
- End Sub
- Private Sub BTT_SETTINGS_TWITTER_Click(sender As Object, e As EventArgs) Handles BTT_SETTINGS_TWITTER.Click
- Using f As New SiteEditorForm(Sites.Twitter) : f.ShowDialog() : End Using
- End Sub
- Private Sub BTT_SETTINGS_INSTAGRAM_Click(sender As Object, e As EventArgs) Handles BTT_SETTINGS_INSTAGRAM.Click
- Using f As New SiteEditorForm(Sites.Instagram) : f.ShowDialog() : End Using
- End Sub
- Private Sub BTT_SETTINGS_REDGIFS_Click(sender As Object, e As EventArgs) Handles BTT_SETTINGS_REDGIFS.Click
- Using f As New SiteEditorForm(Sites.RedGifs) : f.ShowDialog() : End Using
- End Sub
Private Sub BTT_SETTINGS_Click(sender As Object, e As EventArgs) Handles BTT_SETTINGS.Click
Dim mhl% = Settings.MaxLargeImageHeigh.Value
Dim mhs% = Settings.MaxSmallImageHeigh.Value
@@ -346,33 +249,18 @@ CloseResume:
.DownloadVideos = f.DownloadVideos
.FriendlyName = f.UserFriendly
.Description = f.UserDescr
+ If Not f.MyExchangeOptions Is Nothing Then DirectCast(.Self, UserDataBase).ExchangeOptionsSet(f.MyExchangeOptions)
.Self.Labels.ListAddList(f.UserLabels, LAP.ClearBeforeAdd, LAP.NotContainsOnly)
.UpdateUserInformation()
End If
End With
UserListUpdate(Settings.Users.Last, True)
- i = LIST_PROFILES.Items.IndexOfKey(Settings.Users(Settings.Users.Count - 1).LVIKey)
- If i >= 0 Then
- LIST_PROFILES.SelectedIndices.Clear()
- With LIST_PROFILES.Items(i)
- .Selected = True
- .Focused = True
- End With
- LIST_PROFILES.EnsureVisible(i)
- End If
+ FocusUser(Settings.Users(Settings.Users.Count - 1).Key)
Else
MsgBoxE($"User [{f.User.Name}] was not added")
End If
Else
- i = LIST_PROFILES.Items.IndexOfKey(Settings.Users(i).LVIKey)
- If i >= 0 Then
- LIST_PROFILES.SelectedIndices.Clear()
- With LIST_PROFILES.Items(i)
- .Selected = True
- .Focused = True
- End With
- LIST_PROFILES.EnsureVisible(i)
- End If
+ FocusUser(Settings.Users(i).Key)
MsgBoxE($"User [{f.User.Name}] already exists", MsgBoxStyle.Exclamation)
End If
End If
@@ -435,49 +323,54 @@ CloseResume:
#End Region
#Region "View"
Private Sub BTT_VIEW_LARGE_Click(sender As Object, e As EventArgs) Handles BTT_VIEW_LARGE.Click
- LIST_PROFILES.View = View.LargeIcon
- Dim b As Boolean = Not (Settings.ViewMode.Value = View.LargeIcon)
- Settings.ViewMode.Value = View.LargeIcon
- SetViewButtonsCheckers(True, False, False)
- If b Then RefillList()
+ ApplyViewPattern(ViewModes.IconLarge)
End Sub
Private Sub BTT_VIEW_SMALL_Click(sender As Object, e As EventArgs) Handles BTT_VIEW_SMALL.Click
- LIST_PROFILES.View = View.SmallIcon
- Dim b As Boolean = Not (Settings.ViewMode.Value = View.SmallIcon)
- Settings.ViewMode.Value = View.SmallIcon
- SetViewButtonsCheckers(False, True, False)
- If b Then RefillList()
+ ApplyViewPattern(ViewModes.IconSmall)
End Sub
Private Sub BTT_VIEW_LIST_Click(sender As Object, e As EventArgs) Handles BTT_VIEW_LIST.Click
- LIST_PROFILES.View = View.List
- Dim b As Boolean = Not (Settings.ViewMode.Value = View.List)
- Settings.ViewMode.Value = View.List
- SetViewButtonsCheckers(False, False, True)
- If b Then
- With LIST_PROFILES
- .LargeImageList.Images.Clear()
- .SmallImageList.Images.Clear()
- End With
- End If
+ ApplyViewPattern(ViewModes.List)
End Sub
- Private Sub SetViewButtonsCheckers(ByVal Large As Boolean, ByVal Small As Boolean, ByVal List As Boolean)
- BTT_VIEW_LARGE.Checked = Large
- BTT_VIEW_SMALL.Checked = Small
- BTT_VIEW_LIST.Checked = List
+ Private Sub BTT_VIEW_DETAILS_Click(sender As Object, e As EventArgs) Handles BTT_VIEW_DETAILS.Click
+ ApplyViewPattern(ViewModes.Details)
+ End Sub
+ Private Sub ApplyViewPattern(ByVal v As ViewModes)
+ LIST_PROFILES.View = v
+ Dim b As Boolean = Not (Settings.ViewMode.Value = v)
+ Settings.ViewMode.Value = v
+
+ BTT_VIEW_LARGE.Checked = v = ViewModes.IconLarge
+ BTT_VIEW_SMALL.Checked = v = ViewModes.IconSmall
+ BTT_VIEW_LIST.Checked = v = ViewModes.List
+ BTT_VIEW_DETAILS.Checked = v = ViewModes.Details
+
+ If v = View.Details Then
+ LIST_PROFILES.Columns(0).Width = -2
+ LIST_PROFILES.FullRowSelect = True
+ LIST_PROFILES.GridLines = True
+ End If
+
+ If b Then
+ If Settings.ViewModeIsPicture Then
+ With LIST_PROFILES : .LargeImageList.Images.Clear() : .SmallImageList.Images.Clear() : End With
+ End If
+ RefillList()
+ End If
End Sub
#End Region
#Region "View Site"
Private Sub BTT_SITE_ALL_Click(sender As Object, e As EventArgs) Handles BTT_SITE_ALL.Click
- Settings.SelectedSites = Nothing
- If Not BTT_SITE_ALL.Checked Then Settings.SelectedSites = Nothing : RefillList()
+ Settings.SelectedSites.Clear()
+ If Not BTT_SITE_ALL.Checked Then RefillList()
BTT_SITE_ALL.Checked = True
BTT_SITE_SPECIFIC.Checked = False
End Sub
Private Sub BTT_SITE_SPECIFIC_Click(sender As Object, e As EventArgs) Handles BTT_SITE_SPECIFIC.Click
- Using f As New SiteSelectionForm(Settings.SelectedSites)
+ Using f As New SiteSelectionForm(Settings.SelectedSites.ValuesList)
f.ShowDialog()
If f.DialogResult = DialogResult.OK Then
- Settings.SelectedSites = f.SelectedSites
+ Settings.SelectedSites.Clear()
+ Settings.SelectedSites.AddRange(f.SelectedSites)
BTT_SITE_SPECIFIC.Checked = Settings.SelectedSites.Count > 0
BTT_SITE_ALL.Checked = Settings.SelectedSites.Count = 0
RefillList()
@@ -498,6 +391,12 @@ CloseResume:
Private Sub BTT_SHOW_FAV_Click(sender As Object, e As EventArgs) Handles BTT_SHOW_FAV.Click
SetShowButtonsCheckers(ShowingModes.Favorite)
End Sub
+ Private Sub BTT_SHOW_DELETED_Click(sender As Object, e As EventArgs) Handles BTT_SHOW_DELETED.Click
+ SetShowButtonsCheckers(ShowingModes.Deleted)
+ End Sub
+ Private Sub BTT_SHOW_SUSPENDED_Click(sender As Object, e As EventArgs) Handles BTT_SHOW_SUSPENDED.Click
+ SetShowButtonsCheckers(ShowingModes.Suspended)
+ End Sub
Private Sub BTT_SHOW_LABELS_Click(sender As Object, e As EventArgs) Handles BTT_SHOW_LABELS.Click
SetShowButtonsCheckers(ShowingModes.Labels)
End Sub
@@ -509,6 +408,8 @@ CloseResume:
BTT_SHOW_REGULAR.Checked = m = ShowingModes.Regular
BTT_SHOW_TEMP.Checked = m = ShowingModes.Temporary
BTT_SHOW_FAV.Checked = m = ShowingModes.Favorite
+ BTT_SHOW_DELETED.Checked = m = ShowingModes.Deleted
+ BTT_SHOW_SUSPENDED.Checked = m = ShowingModes.Suspended
BTT_SHOW_LABELS.Checked = m = ShowingModes.Labels
BTT_SHOW_NO_LABELS.Checked = m = ShowingModes.NoLabels
BTT_SELECT_LABELS.Enabled = BTT_SHOW_LABELS.Checked
@@ -559,12 +460,34 @@ CloseResume:
End If
End Using
End Sub
+ Private Sub BTT_SHOW_LIMIT_DATES_Click(sender As Object, e As EventArgs) Handles BTT_SHOW_LIMIT_DATES.Click
+ Dim r As Boolean = False
+ Dim snd As Action(Of Date?) = Sub(ByVal d As Date?)
+ With Settings.LastUpdatedDate
+ If .HasValue And d.HasValue Then
+ r = Not .Value.Date = d.Value.Date
+ Else
+ r = True
+ End If
+ End With
+ Settings.LastUpdatedDate = d
+ End Sub
+ Using f As New FDatePickerForm
+ f.ShowDialog()
+ Select Case f.DialogResult
+ Case DialogResult.Abort : snd(Nothing)
+ Case DialogResult.OK : snd(f.SelectedDate)
+ End Select
+ End Using
+ BTT_SHOW_LIMIT_DATES.Checked = Settings.LastUpdatedDate.HasValue
+ If r Then RefillList()
+ End Sub
#End Region
Private Sub BTT_LOG_Click(sender As Object, e As EventArgs) Handles BTT_LOG.Click
MyMainLOG_ShowForm(Settings.Design)
End Sub
Private Sub BTT_DONATE_Click(sender As Object, e As EventArgs) Handles BTT_DONATE.Click
- Try : Process.Start("https://ko-fi.com/andyprogram") : Catch : End Try
+ Try : Process.Start("https://github.com/AAndyProgram/SCrawler/HowToSupport.md") : Catch : End Try
End Sub
#Region "List functions"
Private _LatestSelected As Integer = -1
@@ -641,7 +564,7 @@ CloseResume:
If .Count > 0 Then .Collections.ForEach(Sub(uu) uu.Labels.ListAddList(f.LabelsList, lp))
End With
Else
- u.Self.Labels.ListAddList(f.LabelsList, lp)
+ u.Labels.ListAddList(f.LabelsList, lp)
End If
u.UpdateUserInformation()
End Sub)
@@ -664,7 +587,8 @@ CloseResume:
Private Sub BTT_CHANGE_IMAGE_Click(sender As Object, e As EventArgs) Handles BTT_CHANGE_IMAGE.Click
Dim user As IUserData = GetSelectedUser()
If Not user Is Nothing Then
- Dim f As SFile = SFile.SelectFiles(user.File, False, "Select new user picture", "Pictures|*.jpeg;*.jpg;*.png").FirstOrDefault
+ Dim f As SFile = SFile.SelectFiles(user.File.CutPath(IIf(user.IsCollection, 2, 1)), False, "Select new user picture",
+ "Pictures|*.jpeg;*.jpg;*.png|GIF|*.gif|All Files|*.*").FirstOrDefault
If Not f.IsEmptyString Then
user.SetPicture(f)
UserListUpdate(user, False)
@@ -689,22 +613,18 @@ CloseResume:
Dim Added As Boolean = i < 0
If i < 0 Then
.Users.Add(New UserDataBind(f.Collection))
+ CollectionHandler(DirectCast(.Users.Last, UserDataBind))
i = .Users.Count - 1
End If
Try
DirectCast(.Users(i), UserDataBind).Add(user)
RemoveUserFromList(user)
i = .Users.FindIndex(fCol)
- If i >= 0 Then UserListUpdate(.Users(i), Added) Else RefillList()
+ If i >= 0 Then UserListUpdate(.Users(i), Added)
MsgBoxE($"[{user.Name}] was added to collection [{f.Collection}]")
Catch ex As InvalidOperationException
i = .Users.FindIndex(fCol)
- If i >= 0 Then
- If DirectCast(.Users(i), UserDataBind).Count = 0 Then
- .Users(i).Dispose()
- .Users.RemoveAt(i)
- End If
- End If
+ If i >= 0 AndAlso DirectCast(.Users(i), UserDataBind).Count = 0 Then .Users(i).Dispose() : .Users.RemoveAt(i)
End Try
End With
End If
@@ -745,7 +665,7 @@ CloseResume:
If .Count = 0 Then
Throw New ArgumentOutOfRangeException("Collection", "Collection is empty")
Else
- With DirectCast(.Collections(0).Self, UserDataBase)
+ With DirectCast(.Collections(0), UserDataBase)
If Not .User.Merged Then CutOption = 2
End With
End If
@@ -764,14 +684,14 @@ CloseResume:
(Not NewDest.Exists(SFO.Path, False) OrElse
(
SFile.GetFiles(NewDest,, IO.SearchOption.AllDirectories, EDP.ThrowException).ListIfNothing.Count = 0 AndAlso
- NewDest.Delete(SFO.Path, False, False, EDP.ThrowException) AndAlso
+ NewDest.Delete(SFO.Path, Settings.DeleteMode, EDP.ThrowException) AndAlso
Not NewDest.Exists(SFO.Path, False)
)
) Then
NewDest.CutPath.Exists(SFO.Path)
IO.Directory.Move(CurrDir.Path, NewDest.Path)
Dim ApplyChanges As Action(Of IUserData) = Sub(ByVal __user As IUserData)
- With DirectCast(__user.Self, UserDataBase)
+ With DirectCast(__user, UserDataBase)
Dim u As UserInfo = .User.Clone
Settings.UsersList.Remove(u)
Dim d As SFile = Nothing
@@ -820,7 +740,7 @@ CloseResume:
End Sub
Private Sub BTT_CONTEXT_INFO_Click(sender As Object, e As EventArgs) Handles BTT_CONTEXT_INFO.Click
Dim user As IUserData = GetSelectedUser()
- If Not user Is Nothing Then MsgBoxE(DirectCast(user.Self, UserDataBase).GetUserInformation())
+ If Not user Is Nothing Then MsgBoxE(DirectCast(user, UserDataBase).GetUserInformation())
End Sub
Private Sub USER_CONTEXT_VisibleChanged(sender As Object, e As EventArgs) Handles USER_CONTEXT.VisibleChanged
Try
@@ -850,7 +770,7 @@ CloseResume:
Private Function GetSelectedUser() As IUserData
If _LatestSelected >= 0 And _LatestSelected <= LIST_PROFILES.Items.Count - 1 Then
Dim k$ = LIST_PROFILES.Items(_LatestSelected).Name
- Dim i% = Settings.Users.FindIndex(Function(u) u.LVIKey = k)
+ Dim i% = Settings.Users.FindIndex(Function(u) u.Key = k)
If i >= 0 Then
Return Settings.Users(i)
Else
@@ -868,7 +788,7 @@ CloseResume:
Dim indx%
For i% = 0 To .SelectedIndices.Count - 1
k = .Items(.SelectedIndices(i)).Name
- indx = Settings.Users.FindIndex(Function(u) u.LVIKey = k)
+ indx = Settings.Users.FindIndex(Function(u) u.Key = k)
If i >= 0 Then l.Add(Settings.Users(indx))
Next
Return l
@@ -880,7 +800,7 @@ CloseResume:
End Try
End Function
Private Overloads Sub RemoveUserFromList(ByVal _User As IUserData)
- RemoveUserFromList(LIST_PROFILES.Items.IndexOfKey(_User.LVIKey), _User.LVIKey)
+ RemoveUserFromList(LIST_PROFILES.Items.IndexOfKey(_User.Key), _User.Key)
End Sub
Private Overloads Sub RemoveUserFromList(ByVal _Index As Integer, ByVal Key As String)
Dim a As Action = Sub()
@@ -952,10 +872,10 @@ CloseResume:
If banUser Then Settings.BlackList.ListAddValue(New UserBan(user.Name, reason), l) : b = True
If user.IsCollection Then
With DirectCast(user, UserDataBind)
- If .Count > 0 Then .Collections.ForEach(Sub(c) Settings.UsersList.Remove(DirectCast(c.Self, UserDataBase).User))
+ If .Count > 0 Then .Collections.ForEach(Sub(c) Settings.UsersList.Remove(DirectCast(c, UserDataBase).User))
End With
Else
- Settings.UsersList.Remove(DirectCast(user.Self, UserDataBase).User)
+ Settings.UsersList.Remove(DirectCast(user, UserDataBase).User)
End If
Settings.Users.Remove(user)
Settings.UpdateUsersList()
@@ -997,6 +917,14 @@ CloseResume:
ErrorsDescriber.Execute(EDP.LogMessageValue, ex, "Error on trying to delete user / collection")
End Try
End Sub
+ Friend Sub UserRemovedFromCollection(ByVal User As IUserData)
+ If LIST_PROFILES.Items.Count = 0 OrElse Not LIST_PROFILES.Items.ContainsKey(User.Key) Then UserListUpdate(User, True)
+ End Sub
+ Friend Sub CollectionRemoved(ByVal User As IUserData)
+ With LIST_PROFILES.Items
+ If .Count > 0 AndAlso .ContainsKey(User.Key) Then .RemoveByKey(User.Key)
+ End With
+ End Sub
Private Sub DownloadSelectedUser(ByVal UseLimits As Boolean)
Dim users As List(Of IUserData) = GetSelectedUserArray()
If users.ListExists Then
@@ -1044,17 +972,27 @@ ResumeDownloadingOperation:
If Not user Is Nothing Then user.OpenFolder()
End Sub
#End Region
+ Private Sub Info_OnUserLooking(ByVal Key As String)
+ FocusUser(Key, True)
+ End Sub
+ Private Sub FocusUser(ByVal Key As String, Optional ByVal ActivateMe As Boolean = False)
+ Dim i% = LIST_PROFILES.Items.IndexOfKey(Key)
+ If i >= 0 Then
+ LIST_PROFILES.Select()
+ LIST_PROFILES.SelectedIndices.Clear()
+ With LIST_PROFILES.Items(i) : .Selected = True : .Focused = True : End With
+ LIST_PROFILES.EnsureVisible(i)
+ If ActivateMe Then
+ If Visible Then BringToFront() Else Visible = True
+ End If
+ End If
+ End Sub
Friend Sub User_OnUserUpdated(ByVal User As IUserData)
UserListUpdate(User, False)
End Sub
Private _LogColorChanged As Boolean = False
- Private Sub Downloader_UpdateJobsCount(ByVal Site As Sites, ByVal TotalCount As Integer)
- Dim a As Action
- If Site = Sites.Instagram Then
- a = Sub() LBL_JOBS_INST_COUNT.Text = IIf(TotalCount = 0, String.Empty, $"[Jobs {TotalCount}]")
- Else
- a = Sub() LBL_JOBS_COUNT.Text = IIf(TotalCount = 0, String.Empty, $"[Jobs {TotalCount}]")
- End If
+ Private Sub Downloader_UpdateJobsCount(ByVal TotalCount As Integer)
+ Dim a As Action = Sub() LBL_JOBS_COUNT.Text = IIf(TotalCount = 0, String.Empty, $"[Jobs {TotalCount}]")
If Toolbar_BOTTOM.InvokeRequired Then Toolbar_BOTTOM.Invoke(a) Else a.Invoke
If Not _LogColorChanged AndAlso Not MyMainLOG.IsEmptyString Then
a = Sub() BTT_LOG.ControlChangeColor(False)
@@ -1073,4 +1011,7 @@ ResumeDownloadingOperation:
Private Sub NotificationMessage(ByVal Message As String)
If Settings.ShowNotifications Then TrayIcon.ShowBalloonTip(2000, TrayIcon.BalloonTipTitle, Message, ToolTipIcon.Info)
End Sub
+ Private Sub BTT_PR_INFO_Click(sender As Object, e As EventArgs) Handles BTT_PR_INFO.Click
+ If MyProgressForm.Visible Then MyProgressForm.BringToFront() Else MyProgressForm.Show()
+ End Sub
End Class
\ No newline at end of file
diff --git a/SCrawler/MainFrameObjects.vb b/SCrawler/MainFrameObjects.vb
new file mode 100644
index 0000000..f4c7583
--- /dev/null
+++ b/SCrawler/MainFrameObjects.vb
@@ -0,0 +1,37 @@
+' Copyright (C) 2022 Andy
+' This program is free software: you can redistribute it and/or modify
+' it under the terms of the GNU General Public License as published by
+' the Free Software Foundation, either version 3 of the License, or
+' (at your option) any later version.
+'
+' This program is distributed in the hope that it will be useful,
+' but WITHOUT ANY WARRANTY
+Imports SCrawler.API
+Imports SCrawler.API.Base
+Friend Class MainFrameObjects
+ Private ReadOnly Property MF As MainFrame
+ Friend Sub New(ByRef f As MainFrame)
+ MF = f
+ End Sub
+ Friend Sub ImageHandler(ByVal User As IUserData)
+ ImageHandler(User, False)
+ ImageHandler(User, True)
+ End Sub
+ Friend Sub ImageHandler(ByVal User As IUserData, ByVal Add As Boolean)
+ Try
+ If Add Then
+ AddHandler User.OnUserUpdated, AddressOf MF.User_OnUserUpdated
+ Else
+ RemoveHandler User.OnUserUpdated, AddressOf MF.User_OnUserUpdated
+ End If
+ Catch ex As Exception
+ End Try
+ End Sub
+ Friend Sub CollectionHandler(ByVal [Collection] As UserDataBind)
+ Try
+ AddHandler Collection.OnCollectionSelfRemoved, AddressOf MF.CollectionRemoved
+ AddHandler Collection.OnUserRemoved, AddressOf MF.UserRemovedFromCollection
+ Catch ex As Exception
+ End Try
+ End Sub
+End Class
\ No newline at end of file
diff --git a/SCrawler/MainMod.vb b/SCrawler/MainMod.vb
index 66a6a93..f79c2b9 100644
--- a/SCrawler/MainMod.vb
+++ b/SCrawler/MainMod.vb
@@ -7,11 +7,16 @@
' 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.RegularExpressions
+Imports PersonalUtilities.Tools
Imports PersonalUtilities.Tools.WEB
Imports PersonalUtilities.Forms.Toolbars
Imports SCrawler.API
Imports SCrawler.API.Base
+Imports SCrawler.Plugin.Hosts
+Imports SCrawler.DownloadObjects
+Imports DownOptions = SCrawler.Plugin.ISiteSettings.Download
Friend Module MainMod
Friend Settings As SettingsCLS
Friend Const SettingsFolderName As String = "Settings"
@@ -22,10 +27,38 @@ Friend Module MainMod
Friend Const CannelsLabelName As String = "Channels"
Friend Const LVI_CollectionOption As String = "Collection"
Friend Const LVI_ChannelOption As String = "Channel"
+ Friend Property BATCH As BatchExecutor
+ Private _BatchLogSent As Boolean = False
+ '''
+ Friend Sub GlobalOpenPath(ByVal f As SFile, Optional ByVal e As ErrorsDescriber = Nothing)
+ Dim b As Boolean = False
+ If Not e.Exists Then e = EDP.None
+ Try
+ If f.Exists(SFO.Path, False) Then
+ If Settings.OpenFolderInOtherProgram.Attribute.Value AndAlso Not Settings.OpenFolderInOtherProgram.IsEmptyString Then
+ If BATCH Is Nothing Then BATCH = New BatchExecutor With {.RedirectStandardError = True}
+ b = True
+ With BATCH
+ .Reset()
+ .Execute({String.Format(Settings.OpenFolderInOtherProgram.Value, f.PathWithSeparator)}, EDP.SendInLog + EDP.ThrowException)
+ If .HasError Or Not .ErrorOutput.IsEmptyString Then Throw New Exception(.ErrorOutput, .ErrorException)
+ End With
+ Else
+ f.Open(SFO.Path, e)
+ End If
+ End If
+ Catch ex As Exception
+ If b Then
+ If Not _BatchLogSent Then ErrorsDescriber.Execute(EDP.SendInLog, ex, $"GlobalOpenPath({f.Path})") : _BatchLogSent = True
+ f.Open(SFO.Path, e)
+ End If
+ End Try
+ End Sub
Friend Enum ViewModes As Integer
- IconLarge = 0
- IconSmall = 2
- List = 3
+ IconLarge = View.LargeIcon
+ IconSmall = View.SmallIcon
+ List = View.Tile
+ Details = View.Details
End Enum
Friend Enum ShowingModes As Integer
All = 0
@@ -34,11 +67,17 @@ Friend Module MainMod
Favorite = 100
Labels = 500
NoLabels = 1000
+ Deleted = 10000
+ Suspended = 12000
End Enum
Friend Downloader As TDownloader
Friend InfoForm As DownloadedInfoForm
Friend VideoDownloader As VideosDownloaderForm
+ Friend UserListLoader As ListImagesLoader
+ Friend MyProgressForm As ActiveDownloadingProgress
+ Friend MainFrameObj As MainFrameObjects
Friend ReadOnly ParsersDataDateProvider As New ADateTime(ADateTime.Formats.BaseDateTime)
+ Friend ReadOnly LogConnector As New LogHost
#Region "File name operations"
Friend FileDateAppenderProvider As IFormatProvider
''' File, Date
@@ -51,8 +90,18 @@ Friend Module MainMod
End Class
#End Region
Friend Property MainProgress As MyProgress
- Friend Property MainProgressInst As MyProgress
- Friend Function GetLviGroupName(ByVal Site As Sites, ByVal Temp As Boolean, ByVal Fav As Boolean,
+ Friend Function GetLviGroupName(ByVal Host As SettingsHost, ByVal IsCollection As Boolean, ByVal IsChannel As Boolean) As ListViewGroup()
+ Dim l As New List(Of ListViewGroup)
+ Dim t$
+ t = GetLviGroupName(Host, False, True, IsCollection, IsChannel)
+ l.Add(New ListViewGroup(t, t))
+ t = GetLviGroupName(Host, False, False, IsCollection, IsChannel)
+ l.Add(New ListViewGroup(t, t))
+ t = GetLviGroupName(Host, True, False, IsCollection, IsChannel)
+ l.Add(New ListViewGroup(t, t))
+ Return l.ToArray
+ End Function
+ Friend Function GetLviGroupName(ByVal Host As SettingsHost, ByVal Temp As Boolean, ByVal Fav As Boolean,
ByVal IsCollection As Boolean, ByVal IsChannel As Boolean) As String
Dim Opt$ = String.Empty
If Temp Then
@@ -66,50 +115,61 @@ Friend Module MainMod
ElseIf IsChannel Then
Return $"{LVI_ChannelOption}{Opt}"
Else
- Return $"{Site}{Opt}"
+ Return $"{If(Host?.Name, String.Empty)}{Opt}"
End If
End Function
- Friend Enum Sites As Integer
- Undefined = 0
- Reddit = 1
- Twitter = 2
- Instagram = 3
- RedGifs = 4
- End Enum
- Friend Structure UserInfo : Implements IComparable(Of UserInfo), IEquatable(Of UserInfo), ICloneable
+ Friend Structure UserInfo : Implements IComparable(Of UserInfo), IEquatable(Of UserInfo), ICloneable, IEContainerProvider
Friend Const Name_Site As String = "Site"
+ Friend Const Name_Plugin As String = "Plugin"
Friend Const Name_Collection As String = "Collection"
Friend Const Name_Merged As String = "Merged"
Friend Const Name_IsChannel As String = "IsChannel"
Friend Const Name_SpecialPath As String = "SpecialPath"
Friend Name As String
- Friend Site As Sites
+ Friend Site As String
+ Friend Plugin As String
Friend File As SFile
Friend SpecialPath As SFile
Friend Merged As Boolean
Friend IncludedInCollection As Boolean
Friend CollectionName As String
Friend IsChannel As Boolean
- Friend Sub New(ByVal _Name As String, ByVal s As Sites, Optional ByVal Collection As String = Nothing,
+ Friend [Protected] As Boolean
+ Friend ReadOnly Property DownloadOption As DownOptions
+ Get
+ If IsChannel Then
+ Return DownOptions.Channel
+ Else
+ Return DownOptions.Main
+ End If
+ End Get
+ End Property
+ Friend Sub New(ByVal _Name As String, ByVal Host As SettingsHost, Optional ByVal Collection As String = Nothing,
Optional ByVal _Merged As Boolean = False, Optional ByVal _SpecialPath As SFile = Nothing)
Name = _Name
- Site = s
+ Site = Host.Name
+ Plugin = Host.Key
IncludedInCollection = Not Collection.IsEmptyString
CollectionName = Collection
Merged = _Merged
SpecialPath = _SpecialPath
UpdateUserFile()
End Sub
- Friend Sub New(ByVal x As EContainer)
- Me.New(x.Value,
- x.Attribute(Name_Site).Value.FromXML(Of Integer)(CInt(Sites.Undefined)),
- x.Attribute(Name_Collection).Value, x.Attribute(Name_Merged).Value.FromXML(Of Boolean)(False),
- SFile.GetPath(x.Attribute(Name_SpecialPath).Value))
+ Private Sub New(ByVal x As EContainer)
+ Name = x.Value
+ Site = x.Attribute(Name_Site).Value
+ Plugin = x.Attribute(Name_Plugin).Value
+ CollectionName = x.Attribute(Name_Collection).Value
+ IncludedInCollection = Not CollectionName.IsEmptyString
+ Merged = x.Attribute(Name_Merged).Value.FromXML(Of Boolean)(False)
+ SpecialPath = SFile.GetPath(x.Attribute(Name_SpecialPath).Value)
IsChannel = x.Attribute(Name_IsChannel).Value.FromXML(Of Boolean)(False)
+ 'UpdateUserFile()
End Sub
Friend Sub New(ByVal c As Reddit.Channel)
Name = c.Name
- Site = Sites.Reddit
+ Site = Reddit.RedditSite
+ Plugin = Reddit.RedditSiteKey
File = c.File
IsChannel = True
End Sub
@@ -137,6 +197,7 @@ Friend Module MainMod
}
End Sub
Private Function GetFilePathByParams() As String
+ If [Protected] Then Return String.Empty
If Not SpecialPath.IsEmptyString Then
Return $"{SpecialPath.PathWithSeparator}{SettingsFolderName}"
ElseIf Merged And IncludedInCollection Then
@@ -144,13 +205,18 @@ Friend Module MainMod
Else
If IncludedInCollection Then
Return $"{Settings.CollectionsPathF.PathNoSeparator}\{CollectionName}\{Site}_{Name}\{SettingsFolderName}"
+ ElseIf Not Settings(Plugin) Is Nothing Then
+ Return $"{Settings(Plugin).Path.PathNoSeparator}\{Name}\{SettingsFolderName}"
Else
- Return $"{Settings(Site).Path.PathNoSeparator}\{Name}\{SettingsFolderName}"
+ 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 Function
- Friend Function GetContainer() As EContainer
- Return New EContainer("User", Name, {New EAttribute(Name_Site, CInt(Site)),
+ Friend Function GetContainer(Optional ByVal e As ErrorsDescriber = Nothing) As EContainer Implements IEContainerProvider.ToEContainer
+ Return New EContainer("User", Name, {New EAttribute(Name_Site, Site),
+ New EAttribute(Name_Plugin, Plugin),
New EAttribute(Name_Collection, CollectionName),
New EAttribute(Name_Merged, Merged.BoolToInteger),
New EAttribute(Name_IsChannel, IsChannel.BoolToInteger),
@@ -160,7 +226,7 @@ Friend Module MainMod
If Site = Other.Site Then
Return Name.CompareTo(Other.Name)
Else
- Return CInt(Site).CompareTo(CInt(Other.Site))
+ Return Site.CompareTo(Other.Site)
End If
End Function
Friend Overloads Function Equals(ByVal Other As UserInfo) As Boolean Implements IEquatable(Of UserInfo).Equals
@@ -173,12 +239,14 @@ Friend Module MainMod
Return New UserInfo With {
.Name = Name,
.Site = Site,
+ .Plugin = Plugin,
.File = File,
.SpecialPath = SpecialPath,
.Merged = Merged,
.IncludedInCollection = IncludedInCollection,
.CollectionName = CollectionName,
- .IsChannel = IsChannel
+ .IsChannel = IsChannel,
+ .[Protected] = [Protected]
}
End Function
End Structure
@@ -188,20 +256,10 @@ Friend Module MainMod
ImageHandler(User, True)
End Sub
Friend Sub ImageHandler(ByVal User As IUserData, ByVal Add As Boolean)
- Try
- If Add Then
- AddHandler User.Self.OnUserUpdated, AddressOf MainFrame.User_OnUserUpdated
- Else
- RemoveHandler User.Self.OnUserUpdated, AddressOf MainFrame.User_OnUserUpdated
- End If
- Catch ex As Exception
- End Try
+ MainFrameObj.ImageHandler(User, Add)
End Sub
Friend Sub CollectionHandler(ByVal [Collection] As UserDataBind)
- Try
- AddHandler Collection.OnCollectionSelfRemoved, AddressOf MainFrame.RefillList
- Catch ex As Exception
- End Try
+ MainFrameObj.CollectionHandler(Collection)
End Sub
#End Region
#Region "Standalone video download functions"
@@ -211,8 +269,7 @@ Friend Module MainMod
Return b
End Function
Friend Function GetNewVideoURL() As String
- Dim b$ = GetCurrentBuffer()
- Dim URL$ = InputBoxE("Enter video URL:", "Download video by URL", b)
+ Dim URL$ = InputBoxE("Enter video URL:", "Download video by URL", GetCurrentBuffer())
If Not URL.IsEmptyString Then Return URL Else Return String.Empty
End Function
Friend Sub DownloadVideoByURL()
@@ -229,27 +286,34 @@ Friend Module MainMod
Dim Result As Boolean = False
If Not URL.IsEmptyString Then
Dim um As IEnumerable(Of UserMedia) = Nothing
- Dim site As Sites
- Dim IsImgur As Boolean = False
- If URL.Contains("twitter") Then
- um = Twitter.UserData.GetVideoInfo(URL)
- site = Sites.Twitter
- ElseIf URL.Contains("redgifs") Then
- um = Reddit.UserData.GetVideoInfo(URL)
- site = Sites.Reddit
- ElseIf URL.Contains("instagram.com") Then
- um = Instagram.UserData.GetVideoInfo(URL)
- site = Sites.Instagram
- ElseIf URL.Contains("imgur.com") Then
- um = Imgur.Envir.GetVideoInfo(URL)
- IsImgur = True
- Else
- MsgBoxE("Site of video URL does not recognized" & vbCr & "Operation canceled", MsgBoxStyle.Exclamation, e)
- Return False
+ Dim found As Boolean = False
+ Dim d As Plugin.ExchangeOptions
+ If Settings.Plugins.Count > 0 Then
+ For Each p As PluginHost In Settings.Plugins
+ d = p.Settings.IsMyImageVideo(URL)
+ If d.Exists Then
+ um = Settings(d.HostKey).GetSpecialData(URL)
+ found = True
+ Exit For
+ End If
+ Next
+ End If
+ If Not found Then
+ If URL.Contains("gfycat") Then
+ um = Gfycat.Envir.GetVideoInfo(URL)
+ ElseIf URL.Contains("imgur.com") Then
+ um = Imgur.Envir.GetVideoInfo(URL)
+ Else
+ MsgBoxE("Site of video URL does not recognized" & vbCr & "Operation canceled", MsgBoxStyle.Exclamation, e)
+ Return False
+ End If
End If
If um.ListExists Then
Dim f As SFile, ff As SFile
+ Dim dURL$
+ Dim FileDownloaded As Boolean = False
+
For Each u As UserMedia In um
If Not u.URL.IsEmptyString Or Not u.URL_BASE.IsEmptyString Then
f = u.File
@@ -259,23 +323,18 @@ Friend Module MainMod
Settings.LatestSavingPath.Value.Exists(SFO.Path, False) Then f.Path = Settings.LatestSavingPath.Value
If AskForPath OrElse Not f.Exists(SFO.Path, False) Then
#Disable Warning BC40000
- If site = Sites.Instagram Or IsImgur Then
- ff = SFile.SaveAs(f, "Files destination",,,, EDP.ReturnValue)
- If Not ff.IsEmptyString Then
- f.Path = ff.Path
- Else
- f = Nothing
- End If
+ ff = SFile.SaveAs(f, "Files destination",,,, EDP.ReturnValue)
+ If Not ff.IsEmptyString Then
+ f.Path = ff.Path
Else
- f = SFile.SaveAs(f, "Video file destination", True, "mp4", "Video|*.mp4|All files|*.*", EDP.ReturnValue)
+ f = Nothing
End If
#Enable Warning
AskForPath = False
End If
If Not f.IsEmptyString Then
Settings.LatestSavingPath.Value = f.PathWithSeparator
- Dim dURL$
- Dim FileDownloaded As Boolean = False
+ FileDownloaded = False
Using w As New Net.WebClient
For i% = 0 To 1
If i = 0 Then dURL = u.URL Else dURL = u.URL_BASE
diff --git a/SCrawler/My Project/AssemblyInfo.vb b/SCrawler/My Project/AssemblyInfo.vb
index 268cb57..5b2ca8e 100644
--- a/SCrawler/My Project/AssemblyInfo.vb
+++ b/SCrawler/My Project/AssemblyInfo.vb
@@ -32,6 +32,6 @@ Imports System.Runtime.InteropServices
' by using the '*' as shown below:
'
-
-
+
+
diff --git a/SCrawler/PluginsEnvironment/Attributes/AttributesInternal.vb b/SCrawler/PluginsEnvironment/Attributes/AttributesInternal.vb
new file mode 100644
index 0000000..32c1415
--- /dev/null
+++ b/SCrawler/PluginsEnvironment/Attributes/AttributesInternal.vb
@@ -0,0 +1,12 @@
+' Copyright (C) 2022 Andy
+' This program is free software: you can redistribute it and/or modify
+' it under the terms of the GNU General Public License as published by
+' the Free Software Foundation, either version 3 of the License, or
+' (at your option) any later version.
+'
+' This program is distributed in the hope that it will be useful,
+' but WITHOUT ANY WARRANTY
+Namespace Plugin.Attributes
+ Friend NotInheritable Class UseClassAsIs : Inherits Attribute
+ End Class
+End Namespace
\ No newline at end of file
diff --git a/SCrawler/PluginsEnvironment/Hosts/IResponserContainer.vb b/SCrawler/PluginsEnvironment/Hosts/IResponserContainer.vb
new file mode 100644
index 0000000..7ee1feb
--- /dev/null
+++ b/SCrawler/PluginsEnvironment/Hosts/IResponserContainer.vb
@@ -0,0 +1,13 @@
+' Copyright (C) 2022 Andy
+' This program is free software: you can redistribute it and/or modify
+' it under the terms of the GNU General Public License as published by
+' the Free Software Foundation, either version 3 of the License, or
+' (at your option) any later version.
+'
+' This program is distributed in the hope that it will be useful,
+' but WITHOUT ANY WARRANTY
+Namespace Plugin
+ Friend Interface IResponserContainer
+ ReadOnly Property Responser As PersonalUtilities.Tools.WEB.Response
+ End Interface
+End Namespace
\ No newline at end of file
diff --git a/SCrawler/PluginsEnvironment/Hosts/LogHost.vb b/SCrawler/PluginsEnvironment/Hosts/LogHost.vb
new file mode 100644
index 0000000..1e47259
--- /dev/null
+++ b/SCrawler/PluginsEnvironment/Hosts/LogHost.vb
@@ -0,0 +1,20 @@
+' Copyright (C) 2022 Andy
+' This program is free software: you can redistribute it and/or modify
+' it under the terms of the GNU General Public License as published by
+' the Free Software Foundation, either version 3 of the License, or
+' (at your option) any later version.
+'
+' This program is distributed in the hope that it will be useful,
+' but WITHOUT ANY WARRANTY
+Namespace Plugin.Hosts
+ Friend Class LogHost : Implements ILogProvider
+ Friend Sub Add(ByVal Message As String) Implements ILogProvider.Add
+ MyMainLOG = Message
+ End Sub
+ Friend Sub Add(ByVal ex As Exception, ByVal Message As String,
+ Optional ByVal ShowMainMsg As Boolean = False, Optional ByVal ShowErrorMsg As Boolean = False,
+ Optional ByVal SendInLog As Boolean = True) Implements ILogProvider.Add
+ ErrorsDescriber.Execute(New ErrorsDescriber(ShowMainMsg, ShowErrorMsg, SendInLog), ex, Message)
+ End Sub
+ End Class
+End Namespace
\ No newline at end of file
diff --git a/SCrawler/PluginsEnvironment/Hosts/PluginHost.vb b/SCrawler/PluginsEnvironment/Hosts/PluginHost.vb
new file mode 100644
index 0000000..0a14a50
--- /dev/null
+++ b/SCrawler/PluginsEnvironment/Hosts/PluginHost.vb
@@ -0,0 +1,101 @@
+' Copyright (C) 2022 Andy
+' This program is free software: you can redistribute it and/or modify
+' it under the terms of the GNU General Public License as published by
+' the Free Software Foundation, either version 3 of the License, or
+' (at your option) any later version.
+'
+' This program is distributed in the hope that it will be useful,
+' but WITHOUT ANY WARRANTY
+Imports System.Reflection
+Imports SCrawler.Plugin.Attributes
+Imports PersonalUtilities.Functions.XML
+Imports PersonalUtilities.Functions.XML.Base
+Imports PersonalUtilities.Tools.WEB.GitHub
+Namespace Plugin.Hosts
+ Friend Class PluginHost
+ Friend ReadOnly Property Settings As SettingsHost
+ Friend ReadOnly Property Name As String
+ Get
+ Return Settings.Name
+ End Get
+ End Property
+ Friend ReadOnly Property Key As String
+ Get
+ Return Settings.Key
+ End Get
+ End Property
+ Friend ReadOnly Property Exists As Boolean
+ Get
+ Return Not Settings Is Nothing
+ End Get
+ End Property
+ Private ReadOnly GitHubInfo As Github
+ Private ReadOnly AssemblyVersion As Version
+ Friend ReadOnly Property HasNewVersion As Boolean
+ Get
+ If Not GitHubInfo Is Nothing Then
+ Return NewVersionExists(AssemblyVersion, GitHubInfo.UserName, GitHubInfo.Repository)
+ Else
+ Return False
+ End If
+ End Get
+ End Property
+ Friend ReadOnly Property HasError As Boolean
+ Private Sub New(ByVal s 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))
+ Settings = New SettingsHost(s, _XML, GlobalPath, _Temp, _Imgs, _Vids)
+ End Sub
+ Private Sub New(ByVal AssemblyFile As SFile, 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))
+ Try
+ Dim a As Assembly = Assembly.Load(AssemblyName.GetAssemblyName(AssemblyFile))
+ If Not a Is Nothing Then
+ GitHubInfo = a.GetCustomAttribute(Of Github)()
+ AssemblyVersion = New Version(FileVersionInfo.GetVersionInfo(a.Location).FileVersion)
+ Dim t() As Type = a.GetTypes
+ If t.ListExists Then
+ Dim tSettings$ = GetType(ISiteSettings).FullName
+ For Each tt As Type In t
+ If tt.IsInterface Or tt.IsAbstract Then
+ Continue For
+ Else
+ If Not tt.GetInterface(tSettings) Is Nothing Then
+ Settings = New SettingsHost(Activator.CreateInstance(tt), _XML, GlobalPath, _Temp, _Imgs, _Vids)
+ End If
+ End If
+ Next
+ End If
+ End If
+ Catch ex As Exception
+ ErrorsDescriber.Execute(EDP.SendInLog, ex, "[PluginHost.New]")
+ _HasError = True
+ End Try
+ End Sub
+ Friend Shared Function GetMyHosts(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)) As IEnumerable(Of PluginHost)
+ Return {New PluginHost(New API.Reddit.SiteSettings, _XML, GlobalPath, _Temp, _Imgs, _Vids),
+ New PluginHost(New API.Twitter.SiteSettings, _XML, GlobalPath, _Temp, _Imgs, _Vids),
+ New PluginHost(New API.Instagram.SiteSettings(_XML, GlobalPath), _XML, GlobalPath, _Temp, _Imgs, _Vids),
+ New PluginHost(New API.RedGifs.SiteSettings, _XML, GlobalPath, _Temp, _Imgs, _Vids)}
+ End Function
+ Friend Shared Function GetPluginsHosts(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)) As IEnumerable(Of PluginHost)
+ Try
+ Dim pList As New List(Of PluginHost)
+ Dim PluginsDir As SFile = SFile.GetPath($"{Application.StartupPath.CSFilePSN}\Plugins")
+ PluginsDir.Exists(SFO.Path)
+ Dim fList As List(Of SFile) = SFile.GetFiles(PluginsDir, "*.dll",, EDP.ReturnValue).ListIfNothing
+ If fList.Count > 0 Then
+ For Each f As SFile In fList : pList.Add(New PluginHost(f, _XML, GlobalPath, _Temp, _Imgs, _Vids)) : Next
+ pList.RemoveAll(Function(p) Not p.Exists Or p.HasError)
+ End If
+ Return pList
+ Catch ex As Exception
+ ErrorsDescriber.Execute(EDP.SendInLog, ex, "[PluginHost.GetPluginsHosts]")
+ Return Nothing
+ End Try
+ End Function
+ End Class
+End Namespace
\ No newline at end of file
diff --git a/SCrawler/PluginsEnvironment/Hosts/PropertyValueHost.vb b/SCrawler/PluginsEnvironment/Hosts/PropertyValueHost.vb
new file mode 100644
index 0000000..65c0b08
--- /dev/null
+++ b/SCrawler/PluginsEnvironment/Hosts/PropertyValueHost.vb
@@ -0,0 +1,204 @@
+' Copyright (C) 2022 Andy
+' This program is free software: you can redistribute it and/or modify
+' it under the terms of the GNU General Public License as published by
+' the Free Software Foundation, either version 3 of the License, or
+' (at your option) any later version.
+'
+' This program is distributed in the hope that it will be useful,
+' but WITHOUT ANY WARRANTY
+Imports PersonalUtilities.Functions.XML.Base
+Imports PersonalUtilities.Forms
+Imports PersonalUtilities.Forms.Controls
+Imports PersonalUtilities.Forms.Controls.Base
+Imports ADB = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons
+Imports SCrawler.Plugin.Attributes
+Imports System.Reflection
+Namespace Plugin.Hosts
+ Friend Class PropertyValueHost : Implements IPropertyValue, IComparable(Of PropertyValueHost)
+ Friend Event OnPropertyUpdateRequested(ByVal Sender As PropertyValueHost)
+ Private Event ValueChanged As IPropertyValue.ValueChangedEventHandler Implements IPropertyValue.ValueChanged
+ Private _Type As Type
+ Friend Property [Type] As Type Implements IPropertyValue.Type
+ Get
+ Return If(ExternalValue?.Type, _Type)
+ End Get
+ Set(ByVal t As Type)
+ _Type = t
+ End Set
+ End Property
+#Region "Control"
+ Friend Property Control As Control
+ Private ReadOnly ControlNumber As Integer = -1
+ Friend ReadOnly Property ControlHeight As Integer
+ Get
+ If Not Control Is Nothing Then
+ Return IIf(TypeOf Control Is CheckBox, 25, 28)
+ Else
+ Return 0
+ End If
+ End Get
+ End Property
+ Friend Sub CreateControl(Optional ByVal TT As ToolTip = Nothing)
+ With Options
+ If Type Is GetType(Boolean) Then
+ Control = New CheckBox
+ If Not .ControlToolTip.IsEmptyString And Not TT Is Nothing Then TT.SetToolTip(Control, .ControlToolTip)
+ DirectCast(Control, CheckBox).ThreeState = .ThreeStates
+ DirectCast(Control, CheckBox).Text = .ControlText
+ If .ThreeStates Then
+ DirectCast(Control, CheckBox).CheckState = CInt(AConvert(Of Integer)(Value, CInt(CheckState.Indeterminate)))
+ Else
+ DirectCast(Control, CheckBox).Checked = CBool(AConvert(Of Boolean)(Value, False))
+ End If
+ If .LeftOffset > 0 Then Control.Padding = New PaddingE(Control.Padding) With {.Left = Options.LeftOffset}
+ Else
+ Control = New TextBoxExtended
+ With DirectCast(Control, TextBoxExtended)
+ .CaptionText = Options.ControlText
+ .CaptionToolTipEnabled = Not Options.ControlToolTip.IsEmptyString
+ If Options.LeftOffset > 0 Then .CaptionWidth = Options.LeftOffset
+ If Not Options.ControlToolTip.IsEmptyString Then .CaptionToolTipText = Options.ControlToolTip : .CaptionToolTipEnabled = True
+ .Text = CStr(AConvert(Of String)(Value, String.Empty))
+ With .Buttons
+ .BeginInit()
+ If Not Source Is Nothing And Not UpdateMethod Is Nothing Then .Add(New ActionButton(ADB.Refresh))
+ .Add(ADB.Clear)
+ .EndInit(True)
+ End With
+ AddHandler .ActionOnButtonClick, AddressOf TextBoxClick
+ End With
+ End If
+ Control.Tag = Name
+ Control.Dock = DockStyle.Fill
+ End With
+ End Sub
+ Friend Sub DisposeControl()
+ If Not Control Is Nothing Then Control.Dispose() : Control = Nothing
+ End Sub
+ Private Sub TextBoxClick(ByVal Sender As ActionButton)
+ 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)
+ DirectCast(Control, TextBoxExtended).Text = CStr(AConvert(Of String)(Value, String.Empty))
+ End If
+ End If
+ Catch ex As Exception
+ ErrorsDescriber.Execute(EDP.LogMessageValue, ex, $"Updating [{Name}] property")
+ End Try
+ End Sub
+ Friend Sub UpdateValueByControl()
+ If Not Control Is Nothing Then
+ If TypeOf Control Is CheckBox Then
+ With DirectCast(Control, CheckBox)
+ If Options.ThreeStates Then Value = CInt(.CheckState) Else Value = .Checked
+ End With
+ Else
+ Value = AConvert(DirectCast(Control, TextBoxExtended).Text, AModes.Var, [Type],,,, ProviderValue)
+ End If
+ End If
+ End Sub
+ Friend Function GetControlValue() As Object
+ If Not Control Is Nothing Then
+ If TypeOf Control Is CheckBox Then
+ With DirectCast(Control, CheckBox)
+ If Options.ThreeStates Then Return CInt(.CheckState) Else Return .Checked
+ End With
+ Else
+ Return AConvert(DirectCast(Control, TextBoxExtended).Text, AModes.Var, [Type],,,, ProviderValue)
+ End If
+ Else
+ Return Nothing
+ End If
+ End Function
+#End Region
+#Region "Compatibility"
+ Private ReadOnly Source As Object
+ Friend ReadOnly Name As String
+ Private ReadOnly _XmlName As String
+ Friend ReadOnly Options As PropertyOption
+#Region "Providers"
+ Friend Property ProviderFieldsChecker As IFormatProvider
+ Friend Property ProviderValue As IFormatProvider
+ Friend Sub SetProvider(ByVal Provider As IFormatProvider, ByVal FC As Boolean)
+ If FC Then ProviderFieldsChecker = Provider Else ProviderValue = Provider
+ End Sub
+#End Region
+ 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())
+ UpdateMethod = m
+ _UpdateDependencies = Dependencies
+ End Sub
+ Friend ReadOnly IsTaskCounter As Boolean
+#End Region
+ Friend ReadOnly Exists As Boolean = False
+#Region "Initializer"
+ Friend Sub New(ByRef PropertySource As Object, ByVal Member As MemberInfo)
+ Source = PropertySource
+ Name = Member.Name
+
+ ControlNumber = If(Member.GetCustomAttribute(Of ControlNumber)()?.PropertyNumber, -1)
+
+ If DirectCast(Member, PropertyInfo).PropertyType Is GetType(PropertyValue) Then
+ ExternalValue = DirectCast(DirectCast(Member, PropertyInfo).GetValue(Source), PropertyValue)
+ _Value = ExternalValue.Value
+ AddHandler ExternalValue.ValueChanged, AddressOf ExternalValueChanged
+ Options = Member.GetCustomAttribute(Of PropertyOption)()
+ IsTaskCounter = Not Member.GetCustomAttribute(Of TaskCounter)() Is Nothing
+ _XmlName = If(Member.GetCustomAttribute(Of PXML)()?.ElementName, String.Empty)
+ If Not _XmlName.IsEmptyString Then XValue = XMLValueBase.CreateInstance([Type])
+ Exists = True
+ End If
+ End Sub
+ Friend Sub SetXmlEnvironment(ByRef Container As Object, Optional ByVal _Nodes() As String = Nothing,
+ Optional ByVal FormatProvider As IFormatProvider = Nothing)
+ If Not _XmlName.IsEmptyString And Not XValue Is Nothing Then
+ XValue.SetEnvironment(_XmlName, _Value, Container, _Nodes, If(ProviderValue, FormatProvider))
+ Value(False) = XValue.Value
+ End If
+ End Sub
+#End Region
+#Region "Value"
+ Private ReadOnly Property ExternalValue As PropertyValue
+ Friend ReadOnly Property XValue As IXMLValue
+ Private _Value As Object
+ Friend Overloads Property Value As Object Implements IPropertyValue.Value
+ Get
+ Return _Value
+ End Get
+ Set(ByVal NewValue As Object)
+ Value(True) = NewValue
+ End Set
+ End Property
+ Private Overloads WriteOnly Property Value(ByVal UpdateXML As Boolean) As Object
+ Set(ByVal NewValue As Object)
+ _Value = NewValue
+ If Not ExternalValue Is Nothing And Not _ExternalInvoked Then ExternalValue.Value = _Value
+ If UpdateXML And Not XValue Is Nothing Then XValue.Value = NewValue
+ RaiseEvent ValueChanged(_Value)
+ End Set
+ End Property
+ Private _ExternalInvoked As Boolean = False
+ Private Sub ExternalValueChanged(ByVal NewValue As Object)
+ If Not _ExternalInvoked Then
+ _ExternalInvoked = True
+ Value = NewValue
+ _ExternalInvoked = False
+ End If
+ End Sub
+#End Region
+#Region "IComparable Support"
+ Private Function CompareTo(ByVal Other As PropertyValueHost) As Integer Implements IComparable(Of PropertyValueHost).CompareTo
+ Return ControlNumber.CompareTo(Other.ControlNumber)
+ End Function
+#End Region
+ End Class
+End Namespace
\ No newline at end of file
diff --git a/SCrawler/PluginsEnvironment/Hosts/SettingsHost.vb b/SCrawler/PluginsEnvironment/Hosts/SettingsHost.vb
new file mode 100644
index 0000000..ece23ad
--- /dev/null
+++ b/SCrawler/PluginsEnvironment/Hosts/SettingsHost.vb
@@ -0,0 +1,321 @@
+' Copyright (C) 2022 Andy
+' This program is free software: you can redistribute it and/or modify
+' it under the terms of the GNU General Public License as published by
+' the Free Software Foundation, either version 3 of the License, or
+' (at your option) any later version.
+'
+' This program is distributed in the hope that it will be useful,
+' but WITHOUT ANY WARRANTY
+Imports System.Reflection
+Imports SCrawler.API.Base
+Imports SCrawler.Plugin.Attributes
+Imports PersonalUtilities.Functions.XML
+Imports PersonalUtilities.Functions.XML.Base
+Imports Download = SCrawler.Plugin.ISiteSettings.Download
+Namespace Plugin.Hosts
+ Friend Class SettingsHost
+#Region "Controls"
+ Private WithEvents BTT_SETTINGS As ToolStripMenuItem
+ Private WithEvents BTT_SETTINGS_INTERNAL As Button
+ Private ReadOnly SpecialFormAttribute As SpecialForm = Nothing
+ Friend Function GetSettingsButton() As ToolStripItem
+ BTT_SETTINGS = New ToolStripMenuItem With {.Text = Source.Site}
+ If Not Source.Image Is Nothing Then BTT_SETTINGS.Image = Source.Image
+ Return BTT_SETTINGS
+ End Function
+ Friend Function GetSettingsButtonInternal() As Button
+ If Not SpecialFormAttribute Is Nothing AndAlso SpecialFormAttribute.SettingsForm Then
+ BTT_SETTINGS_INTERNAL = New Button With {.Text = "Other settings", .Dock = DockStyle.Right}
+ Return BTT_SETTINGS_INTERNAL
+ Else
+ Return Nothing
+ End If
+ End Function
+ Private Sub BTT_SETTINGS_Click(sender As Object, e As EventArgs) Handles BTT_SETTINGS.Click
+ Try
+ Using f As New Editors.SiteEditorForm(Me) : f.ShowDialog() : End Using
+ Catch ex As Exception
+ ErrorsDescriber.Execute(EDP.LogMessageValue, ex, "[Plugin.Hosts.SettingsHost.OpenSettingsForm]")
+ End Try
+ End Sub
+ Private Sub BTT_SETTINGS_INTERNAL_Click(sender As Object, e As EventArgs) Handles BTT_SETTINGS_INTERNAL.Click
+ Try
+ If Not SpecialFormAttribute Is Nothing AndAlso SpecialFormAttribute.SettingsForm Then Source.OpenSettingsForm()
+ Catch ex As Exception
+ ErrorsDescriber.Execute(EDP.LogMessageValue, ex, "[Plugin.Hosts.SettingsHost.OpenSettingsForm(Button)]")
+ End Try
+ End Sub
+#End Region
+#Region "Host declarations"
+ Friend ReadOnly Property Source As ISiteSettings
+ Friend ReadOnly Property PropList As List(Of PropertyValueHost)
+ Friend ReadOnly Property Name As String
+ Get
+ Return Source.Site
+ End Get
+ End Property
+ Private ReadOnly _Key As String
+ Friend ReadOnly Property Key As String
+ Get
+ Return IIf(_Key.IsEmptyString, Name, _Key)
+ End Get
+ End Property
+ Friend ReadOnly Property IsMyClass As Boolean = False
+ Friend ReadOnly Property IsSeparatedTasks As Boolean = False
+ Friend ReadOnly Property IsSavedPostsCompatible As Boolean = False
+ Private ReadOnly _TaskCountDefined As Integer? = Nothing
+ Friend ReadOnly Property TaskCount As Integer
+ Get
+ If IsSeparatedTasks Then
+ If PropList.Count > 0 Then
+ Dim i% = PropList.FindIndex(Function(p) p.IsTaskCounter)
+ If i >= 0 Then Return CInt(PropList(i).Value)
+ End If
+ If _TaskCountDefined.HasValue Then Return _TaskCountDefined.Value
+ End If
+ Return Settings.MaxUsersJobsCount
+ End Get
+ End Property
+ Friend ReadOnly Property HasSpecialOptions As Boolean = False
+#End Region
+#Region "Base properties compatibility"
+ Friend ReadOnly Property Temporary As XMLValue(Of Boolean)
+ Friend ReadOnly Property DownloadImages As XMLValue(Of Boolean)
+ Friend ReadOnly Property DownloadVideos As XMLValue(Of Boolean)
+ Private ReadOnly _Path As XMLValue(Of SFile)
+ Friend Property Path(Optional ByVal SetProp As Boolean = True) As SFile
+ Get
+ If _Path.IsEmptyString Then
+ Dim tmpPath As SFile = SFile.GetPath($"{Settings.GlobalPath.Value.PathWithSeparator}{Source.Site}")
+ If SetProp Then _Path.Value = tmpPath Else Return tmpPath
+ End If
+ Return _Path.Value
+ End Get
+ Set(ByVal NewPath As SFile)
+ _Path.Value = NewPath
+ End Set
+ End Property
+ Private ReadOnly _SavedPostsPath As XMLValue(Of SFile)
+ Friend Property SavedPostsPath(Optional ByVal GetAny As Boolean = True) As SFile
+ Get
+ If Not _SavedPostsPath.Value.IsEmptyString Then
+ Return _SavedPostsPath.Value
+ Else
+ If GetAny Then
+ Return $"{Path.PathNoSeparator}\!Saved\"
+ Else
+ Return Nothing
+ End If
+ End If
+ End Get
+ Set(ByVal NewPath As SFile)
+ _SavedPostsPath.Value = NewPath
+ 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))
+ Source = Plugin
+
+ PropList = New List(Of PropertyValueHost)
+
+ Dim ClsAttr As IEnumerable(Of Attribute) = Source.GetType.GetCustomAttributes
+ If ClsAttr.ListExists Then
+ For Each a As Attribute In ClsAttr
+ If TypeOf a Is Manifest Then
+ _Key = DirectCast(a, Manifest).GUID
+ ElseIf TypeOf a Is UseClassAsIs Then
+ IsMyClass = True
+ ElseIf TypeOf a Is SeparatedTasks Then
+ IsSeparatedTasks = True
+ With DirectCast(a, SeparatedTasks)
+ If .TasksCount > 0 Then _TaskCountDefined = .TasksCount
+ End With
+ ElseIf TypeOf a Is SavedPosts Then
+ IsSavedPostsCompatible = True
+ ElseIf TypeOf a Is SpecialForm Then
+ With DirectCast(a, SpecialForm)
+ If .SettingsForm Then
+ SpecialFormAttribute = a
+ Else
+ HasSpecialOptions = True
+ End If
+ End With
+ End If
+ Next
+ End If
+
+ Dim i%
+
+ Source.BeginInit()
+
+ Dim n() As String = {SettingsCLS.Name_Node_Sites, Name}
+ If If(_XML(n)?.Count, 0) > 0 Then Source.Load(_XML(n).ToKeyValuePair)
+ Dim Members As IEnumerable(Of MemberInfo) = Plugin.GetType.GetTypeInfo.DeclaredMembers
+ If Members.ListExists Then
+ 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))
+ With m.GetCustomAttributes()
+ If .ListExists Then
+ If m.MemberType = MemberTypes.Method Then
+ For i = 0 To .Count - 1
+ If .ElementAt(i).GetType Is GetType(PropertyUpdater) Then
+ Updaters.Add(m)
+ ElseIf .ElementAt(i).GetType Is GetType(PropertiesDataChecker) Then
+ PropCheckers.Add(m)
+ End If
+ Next
+ ElseIf m.MemberType = MemberTypes.Property Then
+ If Not m.GetCustomAttribute(Of Provider)() Is Nothing Then Providers.Add(m)
+ End If
+ End If
+ End With
+ Next
+ PropList.RemoveAll(Function(p) Not p.Exists)
+ If Updaters.Count > 0 Then
+ Dim up As PropertyUpdater
+ 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)
+ Next
+ Updaters.Clear()
+ End If
+ If Providers.Count > 0 Then
+ Dim prov As Provider
+ For Each m In Providers
+ prov = m.GetCustomAttribute(Of Provider)()
+ i = PropList.FindIndex(Function(p) p.Name = prov.Name)
+ If i >= 0 Then
+ PropList(i).SetProvider(DirectCast(DirectCast(m, PropertyInfo).GetValue(Source), IFormatProvider),
+ m.GetCustomAttribute(Of Provider)().FieldsChecker)
+ End If
+ Next
+ Providers.Clear()
+ End If
+ If PropCheckers.Count > 0 Then
+ Dim pc As PropertiesDataChecker
+ For Each m In PropCheckers
+ pc = m.GetCustomAttribute(Of PropertiesDataChecker)()
+ If pc.ComparableNames.ListExists Then
+ i = PropList.FindIndex(Function(p) p.Name = pc.ComparableNames(0))
+ If i >= 0 Then
+ With PropList(i)
+ .PropertiesChecking = pc.ComparableNames
+ .PropertiesCheckingMethod = DirectCast(m, MethodInfo)
+ End With
+ End If
+ End If
+ Next
+ PropCheckers.Clear()
+ End If
+ End If
+
+ _Path = New XMLValue(Of SFile)("Path",, _XML, n, New XMLValueBase.ToFilePath)
+ _SavedPostsPath = New XMLValue(Of SFile)("SavedPostsPath",, _XML, n, New XMLValueBase.ToFilePath)
+
+ Temporary = New XMLValue(Of Boolean)
+ Temporary.SetExtended("Temporary", False, _XML, n)
+ Temporary.SetDefault(_Temp)
+
+ DownloadImages = New XMLValue(Of Boolean)
+ DownloadImages.SetExtended("DownloadImages", True, _XML, n)
+ DownloadImages.SetDefault(_Imgs)
+
+ DownloadVideos = New XMLValue(Of Boolean)
+ DownloadVideos.SetExtended("DownloadVideos", True, _XML, n)
+ DownloadVideos.SetDefault(_Vids)
+
+ GetUserMediaOnly = New XMLValue(Of Boolean)("GetUserMediaOnly", True, _XML, n)
+ If PropList.Count > 0 Then
+ For Each p As PropertyValueHost In PropList
+ p.SetXmlEnvironment(_XML, n)
+ AddHandler p.OnPropertyUpdateRequested, AddressOf PropHost_OnPropertyUpdateRequested
+ Next
+ End If
+
+ Source.EndInit()
+ End Sub
+#Region "Forks"
+ Friend Function IsMyUser(ByVal UserURL As String) As ExchangeOptions
+ Dim s As ExchangeOptions = Source.IsMyUser(UserURL)
+ If Not s.UserName.IsEmptyString Then s.HostKey = Key
+ Return s
+ End Function
+ Friend Function IsMyImageVideo(ByVal URL As String) As ExchangeOptions
+ Dim s As ExchangeOptions = Source.IsMyImageVideo(URL)
+ If s.Exists Then s.SiteName = Name : s.HostKey = Key
+ Return s
+ End Function
+ Friend Function GetSpecialData(ByVal URL As String) As IEnumerable(Of UserMedia)
+ If IsMyClass Then
+ Return DirectCast(Source, SiteSettingsBase).GetSpecialDataF(URL)
+ Else
+ Dim um As IEnumerable(Of IPluginUserMedia) = Source.GetSpecialData(URL)
+ If um.ListExists Then
+ Dim u As New List(Of UserMedia)
+ For Each d As IPluginUserMedia In um : u.Add(New UserMedia(d)) : Next
+ Return u
+ End If
+ Return Nothing
+ End If
+ End Function
+ Friend Function GetInstance(ByVal What As Download, ByVal u As UserInfo, Optional ByVal _LoadUserInformation As Boolean = True,
+ Optional ByVal AttachUserInfo As Boolean = True) As IUserData
+ Dim p As IPluginContentProvider = Source.GetInstance(What)
+ If Not p Is Nothing Then
+ Dim pp As IUserData
+ If IsMyClass Then pp = p Else pp = New UserDataHost(p)
+ pp.SetEnvironment(Me, u, _LoadUserInformation, AttachUserInfo)
+ Return pp
+ Else
+ Throw New ArgumentNullException("IPluginContentProvider", $"Plugin [{Key}] does not provide user instance")
+ End If
+ End Function
+ Private _AvailableValue As Boolean = True
+ Private _AvailableAsked As Boolean = False
+ Private _ActiveTaskCount As Integer = 0
+ Friend Function Available(ByVal What As Download) As Boolean
+ If Not _AvailableAsked Then
+ _AvailableValue = Source.Available(What)
+ _AvailableAsked = True
+ End If
+ Return _AvailableValue
+ End Function
+ Friend Sub DownloadStarted(ByVal What As Download)
+ _ActiveTaskCount += 1
+ Source.DownloadStarted(What)
+ End Sub
+ Friend Sub BeforeStartDownload(ByVal User As IUserData, ByVal What As Download)
+ Source.BeforeStartDownload(ConvertUser(User), What)
+ End Sub
+ Friend Sub AfterDownload(ByVal User As IUserData, ByVal What As Download)
+ Source.AfterDownload(ConvertUser(User), What)
+ End Sub
+ Friend Sub DownloadDone(ByVal What As Download)
+ _ActiveTaskCount -= 1
+ If _ActiveTaskCount = 0 Then _AvailableAsked = False
+ Source.DownloadDone(What)
+ End Sub
+ Private Function ConvertUser(ByVal User As IUserData) As Object
+ If IsMyClass Then Return User Else Return DirectCast(User, UserDataBase).ExternalPlugin
+ End Function
+#End Region
+ End Class
+End Namespace
\ No newline at end of file
diff --git a/SCrawler/PluginsEnvironment/Hosts/UserDataHost.vb b/SCrawler/PluginsEnvironment/Hosts/UserDataHost.vb
new file mode 100644
index 0000000..1cb2659
--- /dev/null
+++ b/SCrawler/PluginsEnvironment/Hosts/UserDataHost.vb
@@ -0,0 +1,86 @@
+' Copyright (C) 2022 Andy
+' This program is free software: you can redistribute it and/or modify
+' it under the terms of the GNU General Public License as published by
+' the Free Software Foundation, either version 3 of the License, or
+' (at your option) any later version.
+'
+' This program is distributed in the hope that it will be useful,
+' but WITHOUT ANY WARRANTY
+Imports SCrawler.API.Base
+Imports PersonalUtilities.Functions.XML
+Imports System.Threading
+Imports System.Reflection
+Namespace Plugin.Hosts
+ Friend Class UserDataHost : Inherits UserDataBase
+ Private ReadOnly UseInternalDownloader As Boolean
+ Friend Overrides Function ExchangeOptionsGet() As Object
+ Return ExternalPlugin.ExchangeOptionsGet
+ End Function
+ Friend Overrides Sub ExchangeOptionsSet(ByVal Obj As Object)
+ ExternalPlugin.ExchangeOptionsSet(Obj)
+ End Sub
+ Friend Sub New(ByVal SourceClass As IPluginContentProvider)
+ ExternalPlugin = SourceClass
+ UseInternalDownloader = Not ExternalPlugin.GetType.GetCustomAttribute(Of Attributes.UseInternalDownloader)() Is Nothing
+ AddHandler ExternalPlugin.ProgressChanged, AddressOf ExternalPlugin_ProgressChanged
+ AddHandler ExternalPlugin.TotalCountChanged, AddressOf ExternalPlugin_TotalCountChanged
+ End Sub
+ Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean)
+ If Loading Then
+ ExternalPlugin.XmlFieldsSet(Container.ToKeyValuePair)
+ Else
+ Dim fl As List(Of KeyValuePair(Of String, String)) = ExternalPlugin.XmlFieldsGet
+ If fl.ListExists Then
+ For Each fle As KeyValuePair(Of String, String) In fl : Container.Add(fle.Key, fle.Value) : Next
+ fl.Clear()
+ End If
+ End If
+ End Sub
+ Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
+ With ExternalPlugin
+ .Settings = HOST.Source
+ .Thrower = Me
+ .LogProvider = LogConnector
+ .Name = Name
+ .ID = ID
+ .ParseUserMediaOnly = ParseUserMediaOnly
+ .UserDescription = UserDescription
+ .UserExists = .UserExists
+ .UserSuspended = UserSuspended
+ .IsSavedPosts = IsSavedPosts
+ .SeparateVideoFolder = SeparateVideoFolderF
+ .DataPath = MyFile.CutPath.PathNoSeparator
+ .PostsNumberLimit = DownloadTopCount
+
+ If _ContentList.Count > 0 Then ExternalPlugin.ExistingContentList = _ContentList.Select(Function(u) u.PluginUserMedia).ToList
+ ExternalPlugin.TempPostsList = ListAddList(Nothing, _TempPostsList)
+
+ .GetMedia()
+
+ If .TempMediaList.ListExists Then _TempMediaList.ListAddList(.TempMediaList.Select(Function(tm) New UserMedia(tm)), LNC)
+
+ If Not .Name = Name Then Name = .Name
+ ID = .ID
+ UserDescription = .UserDescription
+ UserExists = .UserExists
+ UserSuspended = .UserSuspended
+ End With
+ End Sub
+ Protected Overrides Sub ReparseVideo(ByVal Token As CancellationToken)
+ End Sub
+ Protected Overrides Sub DownloadContent(ByVal Token As CancellationToken)
+ If UseInternalDownloader Then DownloadContentDefault(Token) Else ExternalPlugin.Download()
+ End Sub
+ Protected Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False) As Integer
+ LogError(ex, Message)
+ HasError = True
+ Return 0
+ End Function
+ Private Sub ExternalPlugin_ProgressChanged(ByVal Count As Integer)
+ Progress.Perform(Count)
+ End Sub
+ Private Sub ExternalPlugin_TotalCountChanged(ByVal Count As Integer)
+ Progress.TotalCount += Count
+ End Sub
+ End Class
+End Namespace
\ No newline at end of file
diff --git a/SCrawler/SCrawler.vbproj b/SCrawler/SCrawler.vbproj
index a63aad7..cf7e04a 100644
--- a/SCrawler/SCrawler.vbproj
+++ b/SCrawler/SCrawler.vbproj
@@ -134,22 +134,45 @@
-
+
+
+
+
+
+
+
+ OptionsForm.vb
+
+
+ Form
+
+
+ ActiveDownloadingProgress.vb
+
+
+ Form
+
+
+
+
+
-
+
-
+
+
+
@@ -164,16 +187,16 @@
Form
-
+
DownloadedInfoForm.vb
-
+
Form
-
+
DownloadSavedPostsForm.vb
-
+
Form
@@ -215,7 +238,14 @@
Form
+
+ FDatePickerForm.vb
+
+
+ Form
+
+
Form
@@ -240,27 +270,39 @@
Settings.settings
True
+
+
+
+
+
+
-
+
-
+
VideosDownloaderForm.vb
-
+
Form
+
+ OptionsForm.vb
+
ChannelsStatsForm.vb
ChannelViewForm.vb
-
+
DownloadedInfoForm.vb
-
+
+ ActiveDownloadingProgress.vb
+
+
DownloadSavedPostsForm.vb
@@ -281,6 +323,9 @@
UserCreatorForm.vb
+
+ FDatePickerForm.vb
+
MainFrame.vb
@@ -290,7 +335,7 @@
My.Resources
Designer
-
+
VideosDownloaderForm.vb
@@ -313,6 +358,10 @@
{8405896b-2685-4916-bc93-1fb514c323a9}
PersonalUtilities
+
+ {d4650f6b-5a54-44b6-999b-6c675b7116b1}
+ SCrawler.PluginProvider
+
@@ -338,5 +387,6 @@
false
+
\ No newline at end of file
diff --git a/SCrawler/SettingsCLS.vb b/SCrawler/SettingsCLS.vb
index 7a42ba3..4102a31 100644
--- a/SCrawler/SettingsCLS.vb
+++ b/SCrawler/SettingsCLS.vb
@@ -10,21 +10,23 @@ Imports PersonalUtilities.Functions.XML
Imports PersonalUtilities.Functions.XML.Base
Imports SCrawler.API
Imports SCrawler.API.Base
+Imports SCrawler.Plugin.Hosts
+Imports SCrawler.DownloadObjects
Friend Class SettingsCLS : Implements IDisposable
Friend Const DefaultMaxDownloadingTasks As Integer = 5
Friend Const Name_Node_Sites As String = "Sites"
Private Const SitesValuesSeparator As String = ","
Friend ReadOnly Design As XmlFile
Private ReadOnly MyXML As XmlFile
- Friend ReadOnly OS64 As Boolean
- Friend ReadOnly FfmpegExists As Boolean
+ Private ReadOnly OS64 As Boolean
+ Private ReadOnly FfmpegExists As Boolean
Friend ReadOnly FfmpegFile As SFile
Friend ReadOnly Property UseM3U8 As Boolean
Get
Return OS64 And FfmpegExists
End Get
End Property
- Private ReadOnly MySites As Dictionary(Of Sites, SiteSettings)
+ Friend ReadOnly Plugins As List(Of PluginHost)
Friend ReadOnly Property Users As List(Of IUserData)
Friend ReadOnly Property UsersList As List(Of UserInfo)
Friend Property Channels As Reddit.ChannelsCollection
@@ -39,12 +41,14 @@ Friend Class SettingsCLS : Implements IDisposable
If OS64 And Not FfmpegExists Then MsgBoxE("[ffmpeg.exe] is missing", vbExclamation)
Design = New XmlFile("Settings\Design.xml", Protector.Modes.All)
MyXML = New XmlFile(Nothing) With {.AutoUpdateFile = True}
+ MyXML.BeginUpdate()
Users = New List(Of IUserData)
UsersList = New List(Of UserInfo)
BlackList = New List(Of UserBan)
+ Plugins = New List(Of PluginHost)
GlobalPath = New XMLValue(Of SFile)("GlobalPath", New SFile($"{SFile.GetPath(Application.StartupPath).PathWithSeparator}Data\"), MyXML,,
- XMLValue(Of SFile).ToFilePath)
+ New XMLValueBase.ToFilePath)
SeparateVideoFolder = New XMLValue(Of Boolean)("SeparateVideoFolder", True, MyXML)
CollectionsPath = New XMLValue(Of String)("CollectionsPath", "Collections", MyXML)
@@ -55,29 +59,25 @@ Friend Class SettingsCLS : Implements IDisposable
DefaultDownloadVideos = New XMLValue(Of Boolean)("DownloadVideos", True, MyXML, n)
ChangeReadyForDownOnTempChange = New XMLValue(Of Boolean)("ChangeReadyForDownOnTempChange", True, MyXML, n)
- MySites = New Dictionary(Of Sites, SiteSettings) From {
- {Sites.Reddit, New SiteSettings(Sites.Reddit, MyXML, GlobalPath.Value, DefaultTemporary, DefaultDownloadImages, DefaultDownloadVideos)},
- {Sites.Twitter, New SiteSettings(Sites.Twitter, MyXML, GlobalPath.Value, DefaultTemporary, DefaultDownloadImages, DefaultDownloadVideos)},
- {Sites.Instagram, New SiteSettings(Sites.Instagram, MyXML, GlobalPath.Value, DefaultTemporary, DefaultDownloadImages, DefaultDownloadVideos)},
- {Sites.RedGifs, New SiteSettings(Sites.RedGifs, MyXML, GlobalPath.Value, DefaultTemporary, DefaultDownloadImages, DefaultDownloadVideos)}
- }
- MySites(Sites.Reddit).Responser.Decoders.Add(SymbolsConverter.Converters.Unicode)
+ Plugins.AddRange(PluginHost.GetMyHosts(MyXML, GlobalPath.Value, DefaultTemporary, DefaultDownloadImages, DefaultDownloadVideos))
+ Dim tmpPluginList As IEnumerable(Of PluginHost) = PluginHost.GetPluginsHosts(MyXML, GlobalPath.Value, DefaultTemporary,
+ DefaultDownloadImages, DefaultDownloadVideos)
+ If tmpPluginList.ListExists Then Plugins.AddRange(tmpPluginList)
+ FastProfilesLoading = New XMLValue(Of Boolean)("FastProfilesLoading", False, MyXML)
MaxLargeImageHeigh = New XMLValue(Of Integer)("MaxLargeImageHeigh", 150, MyXML)
MaxSmallImageHeigh = New XMLValue(Of Integer)("MaxSmallImageHeigh", 15, MyXML)
InfoViewMode = New XMLValue(Of Integer)("InfoViewMode", DownloadedInfoForm.ViewModes.Session, MyXML)
ViewMode = New XMLValue(Of Integer)("ViewMode", ViewModes.IconLarge, MyXML)
ShowingMode = New XMLValue(Of Integer)("ShowingMode", ShowingModes.All, MyXML)
- LatestSavingPath = New XMLValue(Of SFile)("LatestSavingPath", Nothing, MyXML,, XMLValue(Of SFile).ToFilePath)
+ LatestSavingPath = New XMLValue(Of SFile)("LatestSavingPath", Nothing, MyXML,, New XMLValueBase.ToFilePath)
LatestSelectedLabels = New XMLValue(Of String)("LatestSelectedLabels",, MyXML)
LatestSelectedChannel = New XMLValue(Of String)("LatestSelectedChannel",, MyXML)
+ LastUpdatedLimit = New XMLValue(Of Date)
+ LastUpdatedLimit.SetExtended("LastUpdatedLimit",, MyXML)
- XMLSelectedSites = New XMLValue(Of String)("SelectedSites", String.Empty, MyXML, {Name_Node_Sites})
- If Not XMLSelectedSites.IsEmptyString Then
- _SelectedSites = XMLSelectedSites.Value.StringToList(Of Sites)(SitesValuesSeparator)
- End If
- If _SelectedSites Is Nothing Then _SelectedSites = New List(Of Sites)
+ SelectedSites = New XMLValuesCollection(Of String)(XMLValueBase.ListModes.String, "SelectedSites", MyXML, {Name_Node_Sites})
ImgurClientID = New XMLValue(Of String)("ImgurClientID", String.Empty, MyXML, {Name_Node_Sites})
@@ -89,17 +89,22 @@ Friend Class SettingsCLS : Implements IDisposable
ChannelsImagesColumns = New XMLValue(Of Integer)("ImagesColumns", 5, MyXML, n)
ChannelsHideExistsUser = New XMLValue(Of Boolean)("HideExistsUser", True, MyXML, n)
ChannelsMaxJobsCount = New XMLValue(Of Integer)("MaxJobsCount", DefaultMaxDownloadingTasks, MyXML, n)
+ ChannelsAddUserImagesFromAllChannels = New XMLValue(Of Boolean)("AddUserImagesFromAllChannels", True, MyXML, n)
n = {"Users"}
FromChannelDownloadTop = New XMLValue(Of Integer)("FromChannelDownloadTop", 10, MyXML, n)
FromChannelDownloadTopUse = New XMLValue(Of Boolean)("FromChannelDownloadTopUse", False, MyXML, n)
FromChannelCopyImageToUser = New XMLValue(Of Boolean)("FromChannelCopyImageToUser", True, MyXML, n)
+ UpdateUserDescriptionEveryTime = New XMLValue(Of Boolean)("UpdateUserDescriptionEveryTime", True, MyXML, n)
n = {"Users", "FileName"}
MaxUsersJobsCount = New XMLValue(Of Integer)("MaxJobsCount", DefaultMaxDownloadingTasks, MyXML, n)
- FileAddDateToFileName = New XMLValue(Of Boolean)("FileAddDateToFileName", False, MyXML, n) With {.OnChangeFunction = AddressOf ChangeDateProvider}
- FileAddTimeToFileName = New XMLValue(Of Boolean)("FileAddTimeToFileName", False, MyXML, n) With {.OnChangeFunction = AddressOf ChangeDateProvider}
- FileDateTimePositionEnd = New XMLValue(Of Boolean)("FileDateTimePositionEnd", True, MyXML, n) With {.OnChangeFunction = AddressOf ChangeDateProvider}
+ FileAddDateToFileName = New XMLValue(Of Boolean)("FileAddDateToFileName", False, MyXML, n)
+ AddHandler FileAddDateToFileName.OnValueChanged, AddressOf ChangeDateProvider
+ FileAddTimeToFileName = New XMLValue(Of Boolean)("FileAddTimeToFileName", False, MyXML, n)
+ AddHandler FileAddTimeToFileName.OnValueChanged, AddressOf ChangeDateProvider
+ FileDateTimePositionEnd = New XMLValue(Of Boolean)("FileDateTimePositionEnd", True, MyXML, n)
+ AddHandler FileDateTimePositionEnd.OnValueChanged, AddressOf ChangeDateProvider
FileReplaceNameByDate = New XMLValue(Of Boolean)("FileReplaceNameByDate", False, MyXML, n)
CheckUpdatesAtStart = New XMLValue(Of Boolean)("CheckUpdatesAtStart", True, MyXML)
@@ -109,7 +114,10 @@ Friend Class SettingsCLS : Implements IDisposable
ExitConfirm = New XMLValue(Of Boolean)("ExitConfirm", True, MyXML)
CloseToTray = New XMLValue(Of Boolean)("CloseToTray", True, MyXML)
ShowNotifications = New XMLValue(Of Boolean)("ShowNotifications", True, MyXML)
+ OpenFolderInOtherProgram = New XMLValueAttribute(Of String, Boolean)("OpenFolderInOtherProgram", "Use",,, MyXML)
+ DeleteToRecycleBin = New XMLValue(Of Boolean)("DeleteToRecycleBin", True, MyXML)
+ MyXML.EndUpdate()
If MyXML.ChangesDetected Then MyXML.Sort() : MyXML.UpdateData()
Labels = New LabelsKeeper
@@ -130,6 +138,7 @@ Friend Class SettingsCLS : Implements IDisposable
If FileDateTimePositionEnd Then FileDateAppenderPattern = "{0}_{1}" Else FileDateAppenderPattern = "{1}_{0}"
End If
End Sub
+#Region "USERS"
Friend Sub LoadUsers()
Try
Users.Clear()
@@ -138,15 +147,16 @@ Friend Class SettingsCLS : Implements IDisposable
x.LoadData()
If x.Count > 0 Then x.ForEach(Sub(xx) UsersList.Add(xx))
End Using
- Dim PNC As Func(Of UserInfo, Boolean) = Function(u) Not u.IncludedInCollection
+ UsersCompatibilityCheck()
+ Dim PNC As Func(Of UserInfo, Boolean) = Function(u) Not u.IncludedInCollection And Not u.Protected
Dim NeedUpdate As Boolean = False
If UsersList.Count > 0 Then
- Dim cUsers As List(Of UserInfo) = UsersList.Where(Function(u) u.IncludedInCollection).ToList
+ Dim cUsers As List(Of UserInfo) = UsersList.Where(Function(u) u.IncludedInCollection And Not u.Protected).ToList
If cUsers.ListExists Then
Dim d As New Dictionary(Of SFile, List(Of UserInfo))
cUsers = cUsers.ListForEachCopy(Of List(Of UserInfo))(Function(ByVal f As UserInfo, ByVal f_indx As Integer) As UserInfo
Dim m% = IIf(f.Merged, 1, 2)
- If SFile.GetPath(f.File.CutPath(m - 1).Path).Exists(SFO.Path, False) Then
+ If Not f.Protected AndAlso SFile.GetPath(f.File.CutPath(m - 1).Path).Exists(SFO.Path, False) Then
Dim fp As SFile = SFile.GetPath(f.File.CutPath(m).Path)
If Not d.ContainsKey(fp) Then
d.Add(fp, New List(Of UserInfo) From {f})
@@ -155,8 +165,7 @@ Friend Class SettingsCLS : Implements IDisposable
End If
Return f
Else
- NeedUpdate = True
- UsersList.Remove(f)
+ If Not f.Protected Then NeedUpdate = True : UsersList.Remove(f)
Return Nothing
End If
End Function, True)
@@ -179,36 +188,89 @@ Friend Class SettingsCLS : Implements IDisposable
Task.WaitAll(t.ToArray)
t.Clear()
Dim du As List(Of UserInfo) = (From u As IUserData In Users
- Where Not u.IsCollection AndAlso Not u.FileExists
- Select DirectCast(u.Self, UserDataBase).User).ToList
+ Where Not u.IsCollection AndAlso Not u.FileExists AndAlso Not DirectCast(u, UserDataBase).User.Protected
+ Select DirectCast(u, UserDataBase).User).ToList
If du.ListExists Then du.ForEach(Sub(u) UsersList.Remove(u)) : du.Clear()
Users.ListDisposeRemoveAll(Function(ByVal u As IUserData) As Boolean
- If u.IsCollection Then
- With DirectCast(u, UserDataBind)
- If .Count > 0 Then
- For i% = .Count - 1 To 0 Step -1
- If Not .Item(i).FileExists Then
- .Item(i).Delete()
- .Collections.RemoveAt(i)
- End If
- Next
- End If
- Return Not .FileExists
- End With
+ If Not DirectCast(u, UserDataBase).User.Protected Then
+ If u.IsCollection Then
+ With DirectCast(u, UserDataBind)
+ If .Count > 0 Then
+ For i% = .Count - 1 To 0 Step -1
+ If Not .Item(i).FileExists Then
+ .Item(i).Delete()
+ .Collections.RemoveAt(i)
+ End If
+ Next
+ End If
+ Return Not .FileExists
+ End With
+ Else
+ Return Not u.FileExists
+ End If
Else
- Return Not u.FileExists
+ Return False
End If
End Function)
End If
If NeedUpdate Then UpdateUsersList()
End If
If Users.Count > 0 Then
- Labels.ToList.ListAddList(Users.SelectMany(Function(u) u.Labels), LAP.NotContainsOnly)
+ Labels.AddRange(Users.SelectMany(Function(u) u.Labels), False)
If Labels.NewLabelsExists Then Labels.Update() : Labels.NewLabels.Clear()
End If
Catch ex As Exception
End Try
End Sub
+ Private Sub UsersCompatibilityCheck()
+ With UsersList
+ Dim user As UserInfo
+ Dim uKeysList As List(Of String) = Nothing
+ If Plugins.Count > 0 Then uKeysList = Plugins.Select(Function(p) p.Key).ListIfNothing
+ If uKeysList Is Nothing Then uKeysList = New List(Of String)
+ Dim i%
+ If .Count > 0 AndAlso (uKeysList.Count = 0 OrElse
+ .Exists(Function(u) u.Site.Length = 1 Or u.Plugin.IsEmptyString Or Not uKeysList.Contains(u.Plugin))) Then
+ Dim indx%
+ Dim c As Boolean = False
+ For i = 0 To .Count - 1
+ user = .Item(i)
+ With user
+ If .Site.Length = 1 Then
+ Select Case .Site
+ Case "1" : .Site = Reddit.RedditSite : c = True
+ Case "2" : .Site = Twitter.TwitterSite : c = True
+ Case "3" : .Site = Instagram.InstagramSite : c = True
+ Case "4" : .Site = RedGifs.RedGifsSite : c = True
+ End Select
+ End If
+ If Not .Site.IsEmptyString Then
+ If .Plugin.IsEmptyString Then
+ indx = Plugins.FindIndex(Function(p) p.Settings.Name.ToLower = .Site.ToLower)
+ If indx >= 0 Then .Plugin = Plugins(indx).Settings.Key : c = True Else .Protected = True
+ Else
+ indx = Plugins.FindIndex(Function(p) p.Key = .Plugin)
+ If indx < 0 Then .Protected = True
+ End If
+ End If
+ End With
+ .Item(i) = user
+ Next
+ If c Then UpdateUsersList()
+ End If
+ .Clear()
+ Using x As New XmlFile(UsersSettingsFile, Protector.Modes.All, False) With {.AllowSameNames = True}
+ x.LoadData()
+ If x.Count > 0 Then
+ For i = 0 To x.Count - 1
+ user = x(i)
+ user.UpdateUserFile()
+ .Add(user)
+ Next
+ End If
+ End Using
+ End With
+ End Sub
Private _UserListUpdateRequired As Boolean = False
Friend ReadOnly Property UserListUpdateRequired As Boolean
Get
@@ -228,7 +290,7 @@ Friend Class SettingsCLS : Implements IDisposable
Try
If UsersList.Count > 0 Then
Using x As New XmlFile With {.AllowSameNames = True, .Name = "Users"}
- UsersList.ForEach(Sub(u) x.Add(u.GetContainer()))
+ x.AddRange(UsersList)
x.Save(UsersSettingsFile)
End Using
End If
@@ -237,18 +299,18 @@ Friend Class SettingsCLS : Implements IDisposable
_UserListUpdateRequired = True
End Try
End Sub
+#End Region
Friend Sub UpdateBlackList()
If BlackList.Count > 0 Then
TextSaver.SaveTextToFile(BlackList.ListToString(, vbNewLine), BlackListFile, True, False, EDP.None)
Else
- If BlackListFile.Exists Then BlackListFile.Delete()
+ BlackListFile.Delete(, Settings.DeleteMode)
End If
End Sub
- Friend Sub DeleteCachPath()
- If Reddit.ChannelsCollection.ChannelsPathCache.Exists(SFO.Path, False) Then _
- Reddit.ChannelsCollection.ChannelsPathCache.Delete(SFO.Path, False, False, EDP.None)
+ Friend Sub DeleteCachePath()
+ Reddit.ChannelsCollection.ChannelsPathCache.Delete(SFO.Path, SFODelete.None, EDP.None)
End Sub
- Friend Overloads Function UserExists(ByVal s As Sites, ByVal UserID As String) As Boolean
+ Friend Overloads Function UserExists(ByVal s As String, ByVal UserID As String) As Boolean
Dim UserFinderBase As Predicate(Of IUserData) = Function(user) user.Site = s And user.Name = UserID
Dim UserFinder As Predicate(Of IUserData) = Function(ByVal user As IUserData) As Boolean
If user.IsCollection Then
@@ -271,16 +333,19 @@ Friend Class SettingsCLS : Implements IDisposable
Friend Sub BeginUpdate()
MyXML.BeginUpdate()
_UpdatesSuspended = True
+ If Plugins.Count > 0 Then Plugins.ForEach(Sub(p) p.Settings.Source.BeginUpdate())
End Sub
Friend Sub EndUpdate()
+ If Plugins.Count > 0 Then Plugins.ForEach(Sub(p) p.Settings.Source.EndUpdate())
MyXML.EndUpdate()
If MyXML.ChangesDetected Then MyXML.UpdateData()
_UpdatesSuspended = False
ChangeDateProvider(Nothing, Nothing, Nothing)
End Sub
- Default Friend ReadOnly Property Site(ByVal s As Sites) As SiteSettings
+ Default Friend ReadOnly Property Site(ByVal PluginKey As String) As SettingsHost
Get
- Return MySites(s)
+ Dim i% = Plugins.FindIndex(Function(p) p.Key = PluginKey)
+ If i >= 0 Then Return Plugins(i).Settings Else Return Nothing
End Get
End Property
Friend ReadOnly Property GlobalPath As XMLValue(Of SFile)
@@ -307,6 +372,7 @@ Friend Class SettingsCLS : Implements IDisposable
Friend ReadOnly Property FromChannelDownloadTop As XMLValue(Of Integer)
Friend ReadOnly Property FromChannelDownloadTopUse As XMLValue(Of Boolean)
Friend ReadOnly Property FromChannelCopyImageToUser As XMLValue(Of Boolean)
+ Friend ReadOnly Property UpdateUserDescriptionEveryTime As XMLValue(Of Boolean)
#Region "File naming"
Friend ReadOnly Property FileAddDateToFileName As XMLValue(Of Boolean)
Friend ReadOnly Property FileAddTimeToFileName As XMLValue(Of Boolean)
@@ -315,6 +381,7 @@ Friend Class SettingsCLS : Implements IDisposable
#End Region
#End Region
#Region "View"
+ Friend ReadOnly Property FastProfilesLoading As XMLValue(Of Boolean)
Friend ReadOnly Property MaxLargeImageHeigh As XMLValue(Of Integer)
Friend ReadOnly Property MaxSmallImageHeigh As XMLValue(Of Integer)
Friend ReadOnly Property InfoViewMode As XMLValue(Of Integer)
@@ -328,20 +395,14 @@ Friend Class SettingsCLS : Implements IDisposable
End Get
End Property
Friend ReadOnly Property ShowingMode As XMLValue(Of Integer)
- Private ReadOnly Property XMLSelectedSites As XMLValue(Of String)
- Private ReadOnly _SelectedSites As List(Of Sites)
- Friend Property SelectedSites As List(Of Sites)
+ Friend ReadOnly Property SelectedSites As XMLValuesCollection(Of String)
+ Private ReadOnly LastUpdatedLimit As XMLValue(Of Date)
+ Friend Property LastUpdatedDate As Date?
Get
- Return _SelectedSites
+ If LastUpdatedLimit.ValueF.Exists Then Return LastUpdatedLimit.Value Else Return Nothing
End Get
- Set(ByVal s As List(Of Sites))
- _SelectedSites.Clear()
- If s.ListExists Then
- _SelectedSites.ListAddList(s)
- XMLSelectedSites.Value = ListAddList(Of Integer, Sites)(Nothing, s).ListToString(, SitesValuesSeparator)
- Else
- XMLSelectedSites.Value = String.Empty
- End If
+ Set(ByVal NewDate As Date?)
+ If Not NewDate.HasValue Then LastUpdatedLimit.ValueF = Nothing Else LastUpdatedLimit.Value = NewDate.Value
End Set
End Property
#End Region
@@ -358,6 +419,7 @@ Friend Class SettingsCLS : Implements IDisposable
Friend ReadOnly Property ChannelsImagesColumns As XMLValue(Of Integer)
Friend ReadOnly Property ChannelsHideExistsUser As XMLValue(Of Boolean)
Friend ReadOnly Property ChannelsMaxJobsCount As XMLValue(Of Integer)
+ Friend ReadOnly Property ChannelsAddUserImagesFromAllChannels As XMLValue(Of Boolean)
#End Region
#Region "New version properties"
Friend ReadOnly Property CheckUpdatesAtStart As XMLValue(Of Boolean)
@@ -368,6 +430,13 @@ Friend Class SettingsCLS : Implements IDisposable
Friend ReadOnly Property ExitConfirm As XMLValue(Of Boolean)
Friend ReadOnly Property CloseToTray As XMLValue(Of Boolean)
Friend ReadOnly Property ShowNotifications As XMLValue(Of Boolean)
+ Friend ReadOnly Property OpenFolderInOtherProgram As XMLValueAttribute(Of String, Boolean)
+ Friend ReadOnly Property DeleteToRecycleBin As XMLValue(Of Boolean)
+ Friend ReadOnly Property DeleteMode As SFODelete
+ Get
+ Return If(DeleteToRecycleBin, SFODelete.DeleteToRecycleBin, SFODelete.None)
+ End Get
+ End Property
#End Region
#Region "IDisposable Support"
Private disposedValue As Boolean = False
@@ -377,12 +446,12 @@ Friend Class SettingsCLS : Implements IDisposable
If UserListUpdateRequired Then UpdateUsersList()
If Not Channels Is Nothing Then
Channels.Dispose()
- DeleteCachPath()
+ DeleteCachePath()
End If
- For Each kv In MySites : kv.Value.Dispose() : Next
- MySites.Clear()
+ Plugins.Clear()
Users.ListClearDispose
UsersList.Clear()
+ SelectedSites.Dispose()
Design.Dispose()
MyXML.Dispose()
End If
diff --git a/SCrawler/TDownloader.vb b/SCrawler/TDownloader.vb
deleted file mode 100644
index 06f85f0..0000000
--- a/SCrawler/TDownloader.vb
+++ /dev/null
@@ -1,349 +0,0 @@
-' Copyright (C) 2022 Andy
-' This program is free software: you can redistribute it and/or modify
-' it under the terms of the GNU General Public License as published by
-' the Free Software Foundation, either version 3 of the License, or
-' (at your option) any later version.
-'
-' This program is distributed in the hope that it will be useful,
-' but WITHOUT ANY WARRANTY
-Imports System.Threading
-Imports PersonalUtilities.Forms.Toolbars
-Imports EOptions = PersonalUtilities.Forms.Toolbars.IMyProgress.EnableOptions
-Imports SCrawler.API
-Imports SCrawler.API.Base
-Friend Class TDownloader : Implements IDisposable
- Friend Event OnJobsChange(ByVal Site As Sites, ByVal JobsCount As Integer)
- Friend Event OnDownloadCountChange()
- Friend Event OnDownloading(ByVal Value As Boolean)
- Friend Event SendNotification(ByVal Message As String)
- Friend ReadOnly Property Downloaded As List(Of IUserData)
- Private ReadOnly NProv As IFormatProvider
- Friend ReadOnly Property Working(Optional ByVal Site As Sites = Sites.Undefined) As Boolean
- Get
- If Site = Sites.Instagram Then
- Return JobInst.Working
- Else
- Return JobDefault.Working Or JobInst.Working
- End If
- End Get
- End Property
- Friend Property InstagramSavedPostsDownloading As Boolean = False
-#Region "Jobs"
- Friend Structure Job
- Friend Site As Sites
- Private TokenSource As CancellationTokenSource
- Private Token As CancellationToken
- Private [Thread] As Thread
- Private _Working As Boolean
- Friend ReadOnly Items As List(Of IUserData)
- Friend ReadOnly Property Count As Integer
- Get
- Return Items.Count
- End Get
- End Property
- Friend ReadOnly Property Working As Boolean
- Get
- Return _Working OrElse If(Thread?.IsAlive, False)
- End Get
- End Property
- Friend ReadOnly Progress As MyProgress
- Friend Sub New(ByRef _Progress As MyProgress)
- Progress = _Progress
- Items = New List(Of IUserData)
- End Sub
- Public Shared Widening Operator CType(ByVal j As Job) As CancellationToken
- Return j.Token
- End Operator
- Public Shared Widening Operator CType(ByVal j As Job) As Boolean
- Return j.Working
- End Operator
- Public Shared Operator And(ByVal x As Job, ByVal y As Job) As Boolean
- Return x.Working And y.Working
- End Operator
- Public Shared Operator And(ByVal x As Job, ByVal y As Boolean) As Boolean
- Return x.Working And y
- End Operator
- Public Shared Operator And(ByVal x As Boolean, ByVal y As Job) As Boolean
- Return x And y.Working
- End Operator
- Public Shared Operator Or(ByVal x As Job, ByVal y As Job) As Boolean
- Return x.Working Or y.Working
- End Operator
- Public Shared Operator Or(ByVal x As Job, ByVal y As Boolean) As Boolean
- Return x.Working Or y
- End Operator
- Public Shared Operator Or(ByVal x As Boolean, ByVal y As Job) As Boolean
- Return x Or y.Working
- End Operator
- Public Shared Operator Not(ByVal j As Job) As Boolean
- Return Not j.Working
- End Operator
- Friend Sub ThrowIfCancellationRequested()
- Token.ThrowIfCancellationRequested()
- End Sub
- Friend ReadOnly Property IsCancellationRequested As Boolean
- Get
- Return Token.IsCancellationRequested
- End Get
- End Property
- Friend ReadOnly Property IsInstagram As Boolean
- Get
- Return Site = Sites.Instagram
- End Get
- End Property
- Friend Sub [Start](ByVal [ThreadStart] As ThreadStart)
- Thread = New Thread(ThreadStart) With {.IsBackground = True}
- Thread.SetApartmentState(ApartmentState.MTA)
- Thread.Start()
- End Sub
- Friend Sub [Start]()
- TokenSource = New CancellationTokenSource
- Token = TokenSource.Token
- _Working = True
- End Sub
- Friend Sub [Stop]()
- If Not TokenSource Is Nothing Then TokenSource.Cancel()
- End Sub
- Friend Sub Stopped()
- _Working = False
- TokenSource = Nothing
- Try
- If Not Thread Is Nothing Then
- If Thread.IsAlive Then Thread.Abort()
- Thread = Nothing
- End If
- Catch ex As Exception
- End Try
- End Sub
- End Structure
- Private JobDefault As Job
- Private JobInst As Job
-#End Region
- Friend Sub New()
- Downloaded = New List(Of IUserData)
- NProv = New ANumbers With {.FormatOptions = ANumbers.Options.GroupIntegral}
- JobDefault = New Job(MainProgress)
- JobInst = New Job(MainProgressInst) With {.Site = Sites.Instagram}
- End Sub
- Friend Sub [Start]()
- If Not JobDefault.Working And JobDefault.Count > 0 Then JobDefault.Start(New ThreadStart(Sub() StartDownloading(JobDefault)))
- If Not JobInst.Working And JobInst.Count > 0 And Not InstagramSavedPostsDownloading Then _
- JobInst.Start(New ThreadStart(Sub() StartDownloading(JobInst)))
- End Sub
- Private Sub StartDownloading(ByRef _Job As Job)
- RaiseEvent OnDownloading(True)
- Dim isInst As Boolean = _Job.IsInstagram
- Dim pt As Func(Of String, String) = Function(ByVal t As String) As String
- Dim _t$ = If(isInst, $"Instagram {Left(t, 1).ToLower}{Right(t, t.Length - 1)}", t)
- RaiseEvent SendNotification(_t)
- Return _t
- End Function
- Try
- _Job.Start()
- _Job.Progress.TotalCount = 0
- _Job.Progress.CurrentCounter = 0
- _Job.Progress.Enabled = True
- Do While _Job.Count > 0
- _Job.ThrowIfCancellationRequested()
- UpdateJobsLabel(_Job)
- DownloadData(_Job, _Job)
- _Job.ThrowIfCancellationRequested()
- Thread.Sleep(500)
- Loop
- _Job.Progress.InformationTemporary = pt("All data downloaded")
- Catch oex As OperationCanceledException When _Job.IsCancellationRequested
- _Job.Progress.InformationTemporary = pt("Downloading canceled")
- Catch ex As Exception
- _Job.Progress.InformationTemporary = pt("Downloading error")
- ErrorsDescriber.Execute(EDP.SendInLog, ex, "TDownloader.Start")
- Finally
- _Job.Stopped()
- UpdateJobsLabel(_Job)
- If _Job.Site = Sites.Instagram Then
- Settings(Sites.Instagram).InstagramLastDownloadDate.Value = Now
- If Settings(Sites.Instagram).InstaHashUpdateRequired Then MyMainLOG = "Check your Instagram credentials"
- End If
- _Job.Progress.Enabled(EOptions.ProgressBar) = False
- RaiseEvent OnDownloading(False)
- End Try
- End Sub
- Friend Sub [Stop]()
- If JobDefault.Working Then JobDefault.Stop()
- If JobInst.Working Then JobInst.Stop()
- End Sub
- Private Overloads Sub UpdateJobsLabel()
- UpdateJobsLabel(JobDefault)
- UpdateJobsLabel(JobInst)
- End Sub
- Private Overloads Sub UpdateJobsLabel(ByVal _Job As Job)
- RaiseEvent OnJobsChange(_Job.Site, _Job.Count)
- End Sub
- Private _InstagramNextWNM As Instagram.UserData.WNM = Instagram.UserData.WNM.Notify
- Private Sub DownloadData(ByRef _Job As Job, ByVal Token As CancellationToken)
- Try
- If _Job.Count > 0 Then
- Const nf As ANumbers.Formats = ANumbers.Formats.Number
- Dim t As New List(Of Task)
- Dim i% = 0
- Dim j% = Settings.MaxUsersJobsCount
- Dim limit% = IIf(_Job.Site = Sites.Instagram, 1, j)
- Dim Keys As New List(Of String)
- Dim h As Boolean = False
- Dim InstaReady As Boolean = Settings(Sites.Instagram).InstagramReadyForDownload
- For Each _Item As IUserData In _Job.Items
- If Not _Item.Disposed Then
- Keys.Add(_Item.LVIKey)
- If Not _Item.Site = Sites.Instagram Or InstaReady Then
- If _Item.Site = Sites.Instagram Then
- h = True
- With DirectCast(_Item, Instagram.UserData)
- .WaitNotificationMode = _InstagramNextWNM
- If Settings(Sites.Instagram).InstagramLastDownloadDate.Value < Now.AddMinutes(60) Then
- .RequestsCount = Settings(Sites.Instagram).InstagramLastRequestsCount
- End If
- End With
- End If
- _Job.ThrowIfCancellationRequested()
- t.Add(Task.Run(Sub() _Item.DownloadData(Token)))
- i += 1
- If i >= limit Then Exit For
- End If
- End If
- Next
- If t.Count > 0 Or Keys.Count > 0 Then
- If h Then
- With Settings(Sites.Instagram)
- If .InstaHash.IsEmptyString Or .InstaHashUpdateRequired Then .GatherInstaHash()
- End With
- End If
- With _Job.Progress
- .Enabled(EOptions.All) = True
- .Information = IIf(_Job.IsInstagram, "Instagram d", "D")
- .Information &= $"ownloading {t.Count.NumToString(nf, NProv)}/{_Job.Items.Count.NumToString(nf, NProv)} profiles' data"
- .InformationTemporary = .Information
- End With
- If t.Count > 0 Then Task.WaitAll(t.ToArray)
- Dim dcc As Boolean = False
- If Keys.Count > 0 Then
- For Each k$ In Keys
- i = _Job.Items.FindIndex(Function(ii) ii.LVIKey = k)
- If i >= 0 Then
- With _Job.Items(i)
- If _Job.Site = Sites.Instagram Then
- With DirectCast(.Self, Instagram.UserData)
- _InstagramNextWNM = .WaitNotificationMode
- If _InstagramNextWNM = Instagram.UserData.WNM.SkipTemp Or _InstagramNextWNM = Instagram.UserData.WNM.SkipCurrent Then _
- _InstagramNextWNM = Instagram.UserData.WNM.Notify
- Settings(Sites.Instagram).InstagramLastRequestsCount.Value = .RequestsCount
- End With
- End If
- If Not .Disposed AndAlso Not .IsCollection AndAlso .DownloadedTotal(False) > 0 Then
- If Not Downloaded.Contains(.Self) Then Downloaded.Add(GetUserFromMainCollection(.Self))
- dcc = True
- End If
- End With
- _Job.Items.RemoveAt(i)
- End If
- Next
- End If
- Keys.Clear()
- _Job.Items.RemoveAll(Function(ii) ii.Disposed)
- If dcc Then Downloaded.RemoveAll(Function(u) u Is Nothing)
- If dcc And Downloaded.Count > 0 Then RaiseEvent OnDownloadCountChange()
- t.Clear()
- End If
- End If
- Catch aoex As ArgumentOutOfRangeException
- ErrorsDescriber.Execute(EDP.SendInLog, aoex, $"TDownloader.DownloadData: index out of range ({_Job.Count})")
- Catch oex As OperationCanceledException When _Job.IsCancellationRequested
- Catch ex As Exception
- ErrorsDescriber.Execute(EDP.SendInLog, ex, "TDownloader.DownloadData")
- Finally
- If Settings.UserListUpdateRequired Then _
- Task.WaitAll(Task.Run(Sub()
- While Settings.UserListUpdateRequired : Settings.UpdateUsersList() : End While
- End Sub))
- If _Job.Site = Sites.Instagram Then Settings(Sites.Instagram).InstagramLastDownloadDate.Value = Now
- End Try
- End Sub
- Private Function GetUserFromMainCollection(ByVal User As IUserData) As IUserData
- Dim uSimple As Predicate(Of IUserData) = Function(u) u.Equals(DirectCast(User.Self, UserDataBase))
- Dim uCol As Predicate(Of IUserData) = Function(ByVal u As IUserData) As Boolean
- If u.IsCollection Then
- Return DirectCast(u, UserDataBind).Collections.Exists(uSimple)
- Else
- Return False
- End If
- End Function
- Dim uu As Predicate(Of IUserData)
- If User.IncludedInCollection Then uu = uCol Else uu = uSimple
- Dim i% = Settings.Users.FindIndex(uu)
- If i >= 0 Then
- If Settings.Users(i).IsCollection Then
- With DirectCast(Settings.Users(i), UserDataBind)
- i = .Collections.FindIndex(uSimple)
- If i >= 0 Then Return .Collections(i)
- End With
- Else
- Return Settings.Users(i)
- End If
- End If
- Return Nothing
- End Function
- Private Sub AddItem(ByVal Item As IUserData, ByVal _UpdateJobsLabel As Boolean)
- If Not Contains(Item) Then
- If Item.IsCollection Then
- Item.DownloadData(Nothing)
- ElseIf Item.Site = Sites.Instagram Then
- JobInst.Items.Add(Item)
- If _UpdateJobsLabel Then UpdateJobsLabel(JobInst)
- Else
- JobDefault.Items.Add(Item)
- If _UpdateJobsLabel Then UpdateJobsLabel(JobDefault)
- End If
- End If
- End Sub
- Friend Sub Add(ByVal Item As IUserData)
- AddItem(Item, True)
- If JobDefault.Count > 0 Or JobInst.Count > 0 Then Start()
- End Sub
- Friend Sub AddRange(ByVal _Items As IEnumerable(Of IUserData))
- If _Items.ListExists Then
- For i% = 0 To _Items.Count - 1 : AddItem(_Items(i), False) : Next
- UpdateJobsLabel()
- End If
- If JobDefault.Count > 0 Or JobInst.Count > 0 Then Start()
- End Sub
- Private Function Contains(ByVal _Item As IUserData)
- If _Item.Site = Sites.Instagram Then
- Return JobInst.Items.Contains(_Item)
- Else
- Return JobDefault.Items.Contains(_Item)
- End If
- End Function
- Friend Sub UserRemove(ByVal _Item As IUserData)
- If Downloaded.Count > 0 AndAlso Downloaded.Contains(_Item) Then Downloaded.Remove(_Item) : RaiseEvent OnDownloadCountChange()
- End Sub
-#Region "IDisposable Support"
- Private disposedValue As Boolean = False
- Protected Overridable Sub Dispose(ByVal disposing As Boolean)
- If Not disposedValue Then
- If disposing Then
- [Stop]()
- JobDefault.Items.Clear()
- JobInst.Items.Clear()
- Downloaded.Clear()
- End If
- disposedValue = True
- End If
- End Sub
- Protected Overrides Sub Finalize()
- Dispose(False)
- MyBase.Finalize()
- End Sub
- Friend Sub Dispose() Implements IDisposable.Dispose
- Dispose(True)
- GC.SuppressFinalize(Me)
- End Sub
-#End Region
-End Class
\ No newline at end of file
diff --git a/SCrawler/VideosDownloaderForm.Designer.vb b/SCrawler/VideosDownloaderForm.Designer.vb
deleted file mode 100644
index dbd8770..0000000
--- a/SCrawler/VideosDownloaderForm.Designer.vb
+++ /dev/null
@@ -1,158 +0,0 @@
-
-Partial Friend Class VideosDownloaderForm : 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 SEP_1 As System.Windows.Forms.ToolStripSeparator
- Dim SEP_2 As System.Windows.Forms.ToolStripSeparator
- Me.ToolbarTOP = New System.Windows.Forms.ToolStrip()
- Me.BTT_ADD = New System.Windows.Forms.ToolStripButton()
- Me.BTT_ADD_LIST = New System.Windows.Forms.ToolStripButton()
- Me.BTT_DELETE = New System.Windows.Forms.ToolStripButton()
- Me.BTT_DOWN = New System.Windows.Forms.ToolStripButton()
- Me.BTT_OPEN_PATH = New System.Windows.Forms.ToolStripButton()
- Me.ToolbarBOTTOM = New System.Windows.Forms.StatusStrip()
- Me.PR_V = New System.Windows.Forms.ToolStripProgressBar()
- Me.LBL_STATUS = New System.Windows.Forms.ToolStripStatusLabel()
- Me.LIST_VIDEOS = New System.Windows.Forms.ListBox()
- SEP_1 = New System.Windows.Forms.ToolStripSeparator()
- SEP_2 = New System.Windows.Forms.ToolStripSeparator()
- Me.ToolbarTOP.SuspendLayout()
- Me.ToolbarBOTTOM.SuspendLayout()
- Me.SuspendLayout()
- '
- 'SEP_1
- '
- 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)
- '
- 'ToolbarTOP
- '
- Me.ToolbarTOP.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden
- Me.ToolbarTOP.Items.AddRange(New System.Windows.Forms.ToolStripItem() {Me.BTT_ADD, Me.BTT_ADD_LIST, Me.BTT_DELETE, SEP_1, Me.BTT_DOWN, SEP_2, Me.BTT_OPEN_PATH})
- Me.ToolbarTOP.Location = New System.Drawing.Point(0, 0)
- Me.ToolbarTOP.Name = "ToolbarTOP"
- Me.ToolbarTOP.Size = New System.Drawing.Size(524, 25)
- Me.ToolbarTOP.TabIndex = 0
- '
- 'BTT_ADD
- '
- Me.BTT_ADD.AutoToolTip = False
- Me.BTT_ADD.Image = Global.SCrawler.My.Resources.Resources.PlusPIC
- Me.BTT_ADD.ImageTransparentColor = System.Drawing.Color.Magenta
- Me.BTT_ADD.Name = "BTT_ADD"
- Me.BTT_ADD.Size = New System.Drawing.Size(75, 22)
- Me.BTT_ADD.Text = "Add (Ins)"
- '
- 'BTT_ADD_LIST
- '
- Me.BTT_ADD_LIST.AutoToolTip = False
- Me.BTT_ADD_LIST.Image = Global.SCrawler.My.Resources.Resources.PlusPIC
- Me.BTT_ADD_LIST.ImageTransparentColor = System.Drawing.Color.Magenta
- Me.BTT_ADD_LIST.Name = "BTT_ADD_LIST"
- Me.BTT_ADD_LIST.Size = New System.Drawing.Size(67, 22)
- Me.BTT_ADD_LIST.Text = "Add list"
- '
- 'BTT_DELETE
- '
- Me.BTT_DELETE.AutoToolTip = False
- Me.BTT_DELETE.Image = Global.SCrawler.My.Resources.Resources.Delete
- Me.BTT_DELETE.ImageTransparentColor = System.Drawing.Color.Magenta
- Me.BTT_DELETE.Name = "BTT_DELETE"
- Me.BTT_DELETE.Size = New System.Drawing.Size(83, 22)
- Me.BTT_DELETE.Text = "Delete (F8)"
- '
- 'BTT_DOWN
- '
- Me.BTT_DOWN.AutoToolTip = False
- Me.BTT_DOWN.Image = Global.SCrawler.My.Resources.Resources.StartPic_01_Green_16
- Me.BTT_DOWN.ImageTransparentColor = System.Drawing.Color.Magenta
- Me.BTT_DOWN.Name = "BTT_DOWN"
- Me.BTT_DOWN.Size = New System.Drawing.Size(104, 22)
- Me.BTT_DOWN.Text = "Download (F5)"
- '
- 'BTT_OPEN_PATH
- '
- Me.BTT_OPEN_PATH.AutoToolTip = False
- Me.BTT_OPEN_PATH.Image = Global.SCrawler.My.Resources.Resources.Folder_32
- Me.BTT_OPEN_PATH.ImageTransparentColor = System.Drawing.Color.Magenta
- Me.BTT_OPEN_PATH.Name = "BTT_OPEN_PATH"
- Me.BTT_OPEN_PATH.Size = New System.Drawing.Size(120, 22)
- Me.BTT_OPEN_PATH.Text = "Open saving path"
- '
- 'ToolbarBOTTOM
- '
- Me.ToolbarBOTTOM.Items.AddRange(New System.Windows.Forms.ToolStripItem() {Me.PR_V, Me.LBL_STATUS})
- Me.ToolbarBOTTOM.Location = New System.Drawing.Point(0, 339)
- Me.ToolbarBOTTOM.Name = "ToolbarBOTTOM"
- Me.ToolbarBOTTOM.Size = New System.Drawing.Size(524, 22)
- Me.ToolbarBOTTOM.TabIndex = 1
- '
- 'PR_V
- '
- Me.PR_V.Name = "PR_V"
- Me.PR_V.Size = New System.Drawing.Size(200, 16)
- Me.PR_V.Visible = False
- '
- 'LBL_STATUS
- '
- Me.LBL_STATUS.Name = "LBL_STATUS"
- Me.LBL_STATUS.Size = New System.Drawing.Size(0, 17)
- '
- 'LIST_VIDEOS
- '
- Me.LIST_VIDEOS.Dock = System.Windows.Forms.DockStyle.Fill
- Me.LIST_VIDEOS.FormattingEnabled = True
- Me.LIST_VIDEOS.Location = New System.Drawing.Point(0, 25)
- Me.LIST_VIDEOS.Name = "LIST_VIDEOS"
- Me.LIST_VIDEOS.Size = New System.Drawing.Size(524, 314)
- Me.LIST_VIDEOS.TabIndex = 2
- '
- 'VideosDownloaderForm
- '
- Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
- Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
- Me.ClientSize = New System.Drawing.Size(524, 361)
- Me.Controls.Add(Me.LIST_VIDEOS)
- Me.Controls.Add(Me.ToolbarBOTTOM)
- Me.Controls.Add(Me.ToolbarTOP)
- Me.KeyPreview = True
- Me.MinimumSize = New System.Drawing.Size(540, 400)
- Me.Name = "VideosDownloaderForm"
- Me.ShowIcon = False
- Me.Text = "Download Videos"
- Me.ToolbarTOP.ResumeLayout(False)
- Me.ToolbarTOP.PerformLayout()
- Me.ToolbarBOTTOM.ResumeLayout(False)
- Me.ToolbarBOTTOM.PerformLayout()
- Me.ResumeLayout(False)
- Me.PerformLayout()
-
- End Sub
-
- Private WithEvents ToolbarTOP As ToolStrip
- Private WithEvents BTT_ADD As ToolStripButton
- Private WithEvents BTT_ADD_LIST As ToolStripButton
- Private WithEvents BTT_DELETE As ToolStripButton
- Private WithEvents ToolbarBOTTOM As StatusStrip
- Private WithEvents PR_V As ToolStripProgressBar
- Private WithEvents LBL_STATUS As ToolStripStatusLabel
- Private WithEvents LIST_VIDEOS As ListBox
- Private WithEvents BTT_DOWN As ToolStripButton
- Private WithEvents BTT_OPEN_PATH As ToolStripButton
-End Class
\ No newline at end of file
diff --git a/SCrawler/VideosDownloaderForm.vb b/SCrawler/VideosDownloaderForm.vb
deleted file mode 100644
index eb2a585..0000000
--- a/SCrawler/VideosDownloaderForm.vb
+++ /dev/null
@@ -1,141 +0,0 @@
-' Copyright (C) 2022 Andy
-' This program is free software: you can redistribute it and/or modify
-' it under the terms of the GNU General Public License as published by
-' the Free Software Foundation, either version 3 of the License, or
-' (at your option) any later version.
-'
-' This program is distributed in the hope that it will be useful,
-' but WITHOUT ANY WARRANTY
-Imports System.ComponentModel
-Imports PersonalUtilities.Forms
-Friend Class VideosDownloaderForm
- Private MyView As FormsView
- Private ReadOnly MyPR As Toolbars.MyProgress
- Private ReadOnly UrlList As List(Of String)
- Private ReadOnly DownloadingUrlsFile As SFile = $"{SettingsFolderName}\VideosUrls.txt"
- Friend Sub New()
- InitializeComponent()
- UrlList = New List(Of String)
- MyPR = New Toolbars.MyProgress(ToolbarBOTTOM, PR_V, LBL_STATUS, "Downloading video")
- If DownloadingUrlsFile.Exists Then
- UrlList.ListAddList(DownloadingUrlsFile.GetText.StringToList(Of String, List(Of String))(Environment.NewLine), LAP.NotContainsOnly)
- End If
- End Sub
- Private Sub VideosDownloaderForm_Load(sender As Object, e As EventArgs) Handles Me.Load
- Try
- MyView = New FormsView(Me)
- MyView.ImportFromXML(Settings.Design)
- MyView.SetMeSize()
- RefillList(False)
- Catch ex As Exception
- End Try
- End Sub
- Private Sub VideosDownloaderForm_Closing(sender As Object, e As CancelEventArgs) Handles Me.Closing
- e.Cancel = True
- Hide()
- End Sub
- Private Sub VideosDownloaderForm_Disposed(sender As Object, e As EventArgs) Handles Me.Disposed
- If Not MyView Is Nothing Then MyView.Dispose(Settings.Design)
- If UrlList.Count > 0 Then UpdateUrlsFile()
- UrlList.Clear()
- End Sub
- Private Sub VideosDownloaderForm_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown
- Dim b As Boolean = True
- Select Case e.KeyCode
- Case Keys.Insert : AddVideo()
- Case Keys.F5 : DownloadVideos()
- Case Keys.F8 : BTT_DELETE_Click(Nothing, EventArgs.Empty)
- Case Else : b = False
- End Select
- If b Then e.Handled = True
- End Sub
- Private Sub RefillList(Optional ByVal Update As Boolean = True)
- Try
- With LIST_VIDEOS
- .Items.Clear()
- If UrlList.Count > 0 Then UrlList.ForEach(Sub(u) .Items.Add(u))
- If .Items.Count > 0 And _LatestSelected >= 0 And _LatestSelected <= .Items.Count - 1 Then .SelectedIndex = _LatestSelected
- If Update Then UpdateUrlsFile()
- End With
- Catch ex As Exception
- ErrorsDescriber.Execute(EDP.LogMessageValue, ex, "Error on list refill")
- End Try
- End Sub
- Private Sub UpdateUrlsFile()
- If UrlList.Count > 0 Then
- TextSaver.SaveTextToFile(UrlList.ListToString(, Environment.NewLine), DownloadingUrlsFile, True,, EDP.SendInLog)
- Else
- If DownloadingUrlsFile.Exists Then DownloadingUrlsFile.Delete(,,, EDP.SendInLog)
- End If
- End Sub
- Private Sub BTT_ADD_Click(sender As Object, e As EventArgs) Handles BTT_ADD.Click
- AddVideo()
- End Sub
- Private Sub AddVideo()
- Dim URL$ = GetNewVideoURL()
- If Not URL.IsEmptyString Then
- If Not UrlList.Contains(URL) Then
- UrlList.Add(URL)
- RefillList()
- Else
- MsgBoxE("This URL already added to list")
- End If
- End If
- End Sub
- Private Sub BTT_ADD_LIST_Click(sender As Object, e As EventArgs) Handles BTT_ADD_LIST.Click
- Dim l$ = InputBoxE("Enter URLs (new line as delimiter):", "URLs list", GetCurrentBuffer(),,,,,, True)
- If Not l.IsEmptyString Then
- Dim ub% = UrlList.Count
- UrlList.ListAddList(l.StringToList(Of String, List(Of String))(Environment.NewLine))
- If Not UrlList.Count = ub Then RefillList()
- End If
- End Sub
- Private Sub BTT_DELETE_Click(sender As Object, e As EventArgs) Handles BTT_DELETE.Click
- If _LatestSelected >= 0 And _LatestSelected <= UrlList.Count - 1 Then
- If MsgBoxE({$"Do you really want to delete video URL:{vbCr}{UrlList(_LatestSelected)}", "Deleting URL..."},
- MsgBoxStyle.Exclamation + MsgBoxStyle.YesNo).DialogResult = MsgBoxResult.Yes Then
- UrlList.RemoveAt(_LatestSelected)
- RefillList()
- End If
- Else
- MsgBoxE("URL does not selected", MsgBoxStyle.Exclamation)
- End If
- End Sub
- Private Sub BTT_DOWN_Click(sender As Object, e As EventArgs) Handles BTT_DOWN.Click
- DownloadVideos()
- End Sub
- Private Sub DownloadVideos()
- If UrlList.Count > 0 Then
- MyPR.TotalCount = UrlList.Count
- MyPR.Enabled = True
- Dim IsFirst As Boolean = True
- For i% = UrlList.Count - 1 To 0 Step -1
- If DownloadVideoByURL(UrlList(i), IsFirst, True) Then UrlList.RemoveAt(i)
- MyPR.Perform()
- IsFirst = False
- Next
- MyPR.Done()
- RefillList()
- MyPR.Enabled = False
- Else
- MsgBoxE("No one video added", MsgBoxStyle.Exclamation)
- End If
- End Sub
- Private _LatestSelected As Integer = -1
- Private Sub LIST_VIDEOS_SelectedIndexChanged(sender As Object, e As EventArgs) Handles LIST_VIDEOS.SelectedIndexChanged
- _LatestSelected = LIST_VIDEOS.SelectedIndex
- End Sub
- Private Sub BTT_OPEN_PATH_Click(sender As Object, e As EventArgs) Handles BTT_OPEN_PATH.Click
- With Settings.LatestSavingPath
- If Not .Value.IsEmptyString Then
- If .Value.Exists(SFO.Path, False) Then
- .Value.Open(SFO.Path, EDP.ShowMainMsg)
- Else
- MsgBoxE($"Path [{ .Value}] does not exists!", MsgBoxStyle.Exclamation)
- End If
- Else
- MsgBoxE("Saving path does not set!", MsgBoxStyle.Exclamation)
- End If
- End With
- End Sub
-End Class
\ No newline at end of file
diff --git a/Tools/ArchiveSCrawlerUsersDataFiles.bat b/Tools/ArchiveSCrawlerUsersDataFiles.bat
new file mode 100644
index 0000000..c727306
--- /dev/null
+++ b/Tools/ArchiveSCrawlerUsersDataFiles.bat
@@ -0,0 +1 @@
+"C:\Program Files\WinRAR\WinRAR.exe" a -r -ep1 -o+ -ag_YYYYMMDD_HHMMSS -m5 -tl -n*.txt -n*.xml "d:\Downloads\SocialNetworks\SCrawlerBackup.rar" "d:\Downloads\SocialNetworks\"
\ No newline at end of file