Compare commits

..

3 Commits

Author SHA1 Message Date
Andy
2f838929cc 2025.2.25.0
PluginProvider
IPluginContentProvider: add 'NameTrue' property

YT
YouTubeSettings: remove the 'CreateDescriptionFiles_AddUploadDate' property
PlayListParserForm: add 'RDAMP' as default value when initializing form
YouTubeMediaContainerBase: fix line breaks

SCrawler
API.Base: add 'EditorExchangeOptionsBase' class
API.Base.GDL: move functions to Pinterest.UserData
API.Base.IUserData: add 'NameTrue' property
API.Base.SiteSettingsBase: update the 'GetUserUrl' function to use the 'NameTrue' property; update 'UserOptions' function
API.Base.UserDataBase: add 'NameTrue' property; add 'SimpleDownloadAvatar' function to get rid of the  same functions of other classes; Update 'UserDescriptionUpdate' function
API.Base.InternalSettingsForm: calculate max offset

ADD API.Bluesky

API.Facebook: add 'Reels' downloads; fix video downloading
API.Instagram: add inheritance 'EditorExchangeOptionsBase'; remove 'GetUserUrl' function from 'SiteSettings'; update 'UserData' class to new environment; add saving 'heic' file along with 'jpg'
API.LPSG.UserData: simplify 403 error
API.Mastodon: update classes to new environment
API.OnlyFans: add 'AppTokenDefault'; disable cookies update; update 'UserData' class to new environment
API.Pinterest: add sub-boards downloading; update download functions
API.PornHub: fix photo & video downloading; remove 'ModelHub' support
API.Reddit: fix token update; update 'UserData' class to new environment
API.ThisVid: update 'UserData' class to new environment
API.ThreadsNet: fix data download; update classes to new environment; fix 'UserID' extraction; add the ability to manually change the UserName
API.TikTok: update classes to new environment
API.Twitter: update classes to new environment; add sleep timers to fully download large profiles; add 'CookiesUpdate' hidden property
API.Xhamster: update classes to new environment
API.XVIDEOS: update classes to new environment

Feed: add the ability to invert selection; open post URL when double-clicking on subscription image
FeedSpecialCollection: update 'FeedsComparer'
GlobalSettingsForm: remove 'UserAgent' from the 'Basis' tab
UserDataHost: update class to new environment
SettingsCLS: set the 'UserSiteNameAsFriendly' property to 'True' by default; disable 'DownDetector'; add 'UsersListProtected' property
2025-02-25 19:47:33 +03:00
Andy
4d74f5204b 2025.1.12.0
YT
YouTubeSettings: add 'FileAddChannelToFileName' property
YouTubeMediaContainerBase: add channel name and video URL to info file; add channel name to file name

SCrawler
DownDetector: fix 403 error; add 'IDownDetector' interface and 'Checker' class; create an isolated environment
API.Instagram: update 'SiteSettings' to the new 'DownDetector' environment; make 'PostKV' public; add static function 'LoadSavePostsKV'
API.OnlyFans: add 'EnableCookiesUpdate' hidden property; add support for DRM keys; add the ability to disable cookie updates
API.Pinterest: add 'x-pinterest-pws-handler' header
API.Reddit: update 'SiteSettings' to the new 'DownDetector' environment
API.ThisVid: fix subscription videos images
API.Threads: change 'heic' extension to 'jpg'
API.Twitter: add broadcasts download
API.Xhamster: fix absolute M3U8 URLs
API.YouTube: add support of personal API instances ('YouTube-operational-API') for download communities
SiteEditorForm: add 'Ctrl+Enter' hotkey to force save settings, ignoring  requirements
PluginsEnvironment.Attributes: add 'UseDownDetectorAttribute' attribute
SettingsHost: update to the new 'DownDetector' environment; add 'AvailableDownDetector' property
SettingsHostCollection: update to the new 'DownDetector' environment; minor bugs in multiprofile
SettingsCLS: add 'DownDetectorEnabled' property
2025-01-12 23:16:57 +03:00
Andy
b42832719f 2024.11.21.0
API.Instagram: code refactoring (settings); add setting to skip errors; add 'ForceUpdateUserName' and 'ForceUpdateUserInfo' properties; add 'IgnoreStoriesDownloadingErrors' to the settings; improve username update algorithm
API.OnlyFans: add 'UpdateRules401' property; update the code to handle error 401
API.YouTube: 404 error handling (community)
UserDataBase: raise event to update user in exceptions; add extra buttons for special download (limited and dated)
UserDataBind: extra buttons (UserDataBase)
UserCreatorForm: fix network paths
GlobalSettingsForm, MainFrame, SettingsCLS: add ability to change the feed opening shortcut
MainFrame: update button captions, update 'DownloadSelectedUser' function
2024-11-21 17:50:19 +03:00
94 changed files with 3202 additions and 1409 deletions

View File

@@ -1,14 +1,94 @@
# 2025.2.25.0
*2025-02-25*
- Added
- Sites:
- **Bluesky**
- Facebook: **`Reels` downloads**
- OnlyFans: default value for `App-Token`
- Pinterest: **sub-boards downloading**
- Threads: ability to manually change `UserName`
- Twitter:
- new icon support
- **sleep timers to fully download large profiles**
- Feed:
- ability to invert selection
- open post URL when double-clicking on subscription image
- Minor improvements
- Updated
- yt-dlp up to version **2025.02.19**
- gallery-dl up to version **1.28.5**
- PluginProvider
- `IPluginContentProvider`: added property `NameTrue`
- Fixed
- Sites:
- Facebook: videos are not downloading
- LPSG: simplified 403 error
- PornHub: photos & videos are not downloading
- Reddit: **token does not update automatically**
- Threads: **data is not downloading**
- Minor bugs
# 2025.1.12.0
*2025-01-12*
- Added
- Sites:
- YouTube (standalone app):
- ability to add channel name to file name (`Add channel to file name`)
- adding channel name and video URL to info file
- OnlyFans: **built-in usage of DRM keys**
- Threads: automatically change `heic` extension to `jpg`
- Twitter: download broadcasts *(user option)*
- Minor improvements
- Updated
- yt-dlp up to version **2024.12.23**
- gallery-dl up to version **1.28.3**
- **OF-Scraper** up to version **3.12.9** *(you must update it personally)*
- Fixed
- Sites:
- DownDetector: fixed 403 error
- OnlyFans: **DRM videos not downloading**
- xHamster: some videos are not downloading
- YouTube: **communities are not downloading** *(see settings in wiki)*
- Minor bugs
# 2024.11.21.0
*2024-11-21*
- Added
- Sites:
- Instagram:
- setting to skip errors without disabling download *(site settings)*
- settings to force update of username and/or user information *(user settings)*
- setting to continue downloading profile if error 560 occurs while downloading user stories *(site settings)*
- improve username update algorithm
- YouTube: 404 error handling (community)
- Main window: add extra buttons for special download (limited and dated) in collection
- Global settings: ability to change the feed opening shortcut (`Ctrl+F`/`Alt+F` *(Settings - Behavior)*)
- Minor improvements
- Updated
- yt-dlp up to version **2024.11.18**
- gallery-dl up to version **1.27.7**
- Fixed
- Users: network paths aren't working
- Main window: in some cases users are not updated in the list
- Minor bugs
# 2024.10.24.0 # 2024.10.24.0
*2024-10-24* *2024-10-24*
- Added - Added
- YouTube (standalone app) - YouTube (standalone app):
- settings `Embed thumbnail (cover)` and `Allow webm formats` - settings `Embed thumbnail (cover)` and `Allow webm formats`
- changed cover selection for music downloads - changed cover selection for music downloads
- allow `webm` formats is there are no `mp4` formats via http protocol (issue #211) - allow `webm` formats if there are no `mp4` formats via http protocol (issue #211)
- Sites: - Sites:
- Instagram - Instagram:
- **ability to manually change username** - **ability to manually change username**
- **mark user as non-existent if user `ID` cannot be obtained** - **mark user as non-existent if user `ID` cannot be obtained**
- Twitter: **ability to manually change username** - Twitter: **ability to manually change username**

13
FAQ.md
View File

@@ -33,15 +33,15 @@ I strongly recommend you to **regularly** create backup copies of the settings f
## General questions ## General questions
- **PROFILES** - **PROFILES**
- I added a profile but **nothing downloaded** :arrow_forward: check your cookies and [site requirements](https://github.com/AAndyProgram/SCrawler/wiki/Settings#sites-requirements). If there are any optional fields that you don't fill in, do so. Still nothing works - [report it](#how-to-report-a-problem)! - I added a profile but **nothing downloaded** :arrow_forward: check your cookies and [site requirements](https://github.com/AAndyProgram/SCrawler/wiki/Settings#sites-requirements). If there are any optional fields that you don't fill in, do so. Still nothing works - [report it](#how-to-report-a-problem)!
- User downloading failed :arrow_forward: check your credentials and **[SITES REQUIREMENTS](https://github.com/AAndyProgram/SCrawler/wiki/Settings#sites-requirements)**. If all settings are set and nothing works, [report it](#how-to-report-a-problem). <u>Don't forget to attach the LOG.</u> - User downloading failed :arrow_forward: check your credentials and **[SITES REQUIREMENTS](https://github.com/AAndyProgram/SCrawler/wiki/Settings#sites-requirements)**. If all settings are set and nothing works, [report it](#how-to-report-a-problem). **Don't forget to attach the LOG.**
- [How to redownload user](https://github.com/AAndyProgram/SCrawler/wiki#redownload-user) - [How to redownload user](https://github.com/AAndyProgram/SCrawler/wiki#redownload-user)
- How to **add profile** to download :arrow_forward: copy the **[profile URL](https://github.com/AAndyProgram/SCrawler/wiki#add-user)** and press `Insert` or `Ctrl+Insert`. **ALWAYS PASTE THE USER PROFILE URL**. After that select this user and press `F5` or click the `Download selected` button. - How to **add profile** to download :arrow_forward: copy the **[profile URL](https://github.com/AAndyProgram/SCrawler/wiki#add-user)** and press `Insert` or `Ctrl+Insert`. **ALWAYS PASTE THE USER PROFILE URL**. After that select this user and press `F5` or click the `Download selected` button.
- How to download **[saved posts](https://github.com/AAndyProgram/SCrawler/wiki#saved-posts)** - How to download **[saved posts](https://github.com/AAndyProgram/SCrawler/wiki#saved-posts)**
- **[HOW TO ADD COOKIES](https://github.com/AAndyProgram/SCrawler/wiki/Settings#how-to-set-up-cookies)** - **[HOW TO ADD COOKIES](https://github.com/AAndyProgram/SCrawler/wiki/Settings#how-to-set-up-cookies)**
- [How to report a problem](#how-to-report-a-problem) - [How to report a problem](#how-to-report-a-problem)
- I want you to **add the site** to SCrawler :arrow_forward: **I'm not currently accepting requests to add new sites**, but you can [create a plugin](https://github.com/AAndyProgram/SCrawler/wiki/Plugins) (for your site) for SCrawler. - I want you to **add the site** to SCrawler :arrow_forward: **I'm not currently accepting requests to add new sites**, but you can [create a plugin](https://github.com/AAndyProgram/SCrawler/wiki/Plugins) (for your site) for SCrawler.
- What language is SCrawler written in :arrow_forward: vb.net - What language is SCrawler written in :arrow_forward: `vb.net`
- I don't know vb.net and I can't write a plugin :arrow_forward: you can write a plugin in `C#` - I don't know `vb.net` and I can't write a plugin :arrow_forward: you can write a plugin in `C#`
- I have a suggestion, will it be added :arrow_forward: maybe if it interested me. - I have a suggestion, will it be added :arrow_forward: maybe if it interested me.
- How to name files using a pattern (e.g. `Site_PostID_Name.jpg`) :arrow_forward: **there is no such functionality and there are no such plans**. - How to name files using a pattern (e.g. `Site_PostID_Name.jpg`) :arrow_forward: **there is no such functionality and there are no such plans**.
- **DON'T CHANGE THE DEFAULT SITE SETTINGS UNLESS YOU KNOW EXACTLY WHAT YOU'RE DOING!** SCrawler already has all the default settings to work. You only need to add credentials (where [required](https://github.com/AAndyProgram/SCrawler/wiki/Settings#sites-requirements)). - **DON'T CHANGE THE DEFAULT SITE SETTINGS UNLESS YOU KNOW EXACTLY WHAT YOU'RE DOING!** SCrawler already has all the default settings to work. You only need to add credentials (where [required](https://github.com/AAndyProgram/SCrawler/wiki/Settings#sites-requirements)).
@@ -55,19 +55,20 @@ I strongly recommend you to **regularly** create backup copies of the settings f
- [Video how to configure](#video-how-to-configure) - [Video how to configure](#video-how-to-configure)
- **Antivirus** - **Antivirus**
- **Antivirus detects SCrawler as a virus** :arrow_forward: SCrawler doesn't contain any viruses at all. All code is posted on GitHub. You can review it. I have nothing to hide. SCrawler just downloads pictures and videos. That's all. If you trust SCrawler, you should just add it to the antivirus exceptions, as I did. Sometimes antiviruses identify SCawler as a virus. This is usually related to the number of files being edited (users' settings files) and the number of files being downloaded. In this case, the antivirus can also remove these files, which will damage users' settings. **If you don't trust SCrawler, just delete it.** - **Antivirus detects SCrawler as a virus** :arrow_forward: SCrawler doesn't contain any viruses at all. All code is posted on GitHub. You can review it. I have nothing to hide. SCrawler just downloads pictures and videos. That's all. If you trust SCrawler, you should just add it to the antivirus exceptions, as I did. Sometimes antiviruses identify SCawler as a virus. This is usually related to the number of files being edited (users' settings files) and the number of files being downloaded. In this case, the antivirus can also remove these files, which will damage users' settings. **If you don't trust SCrawler, just delete it.**
- **Antivirus detects gallery-dl as a virus** :arrow_forward: it's a trustworthy program that is trusted by thousands of people around the world. Antiviruses identify some builds as containing viruses, but this is not true. **If you don't trust gallery-dl, you can simply delete it**. **But if you delete it, you won't be able to download [Twitter & Pinterest](https://github.com/AAndyProgram/SCrawler/wiki/Settings#gallery-dl).** You should decide for yourself. - **Antivirus detects gallery-dl as a virus** :arrow_forward: it's a trustworthy program that is trusted by thousands of people around the world. Antiviruses identify some builds as containing viruses, but this is not true. **If you don't trust gallery-dl, you can simply delete it. But if you delete it, you won't be able to download [Twitter & Pinterest](https://github.com/AAndyProgram/SCrawler/wiki/Settings#gallery-dl).** You should decide for yourself.
## Sites questions ## Sites questions
*How to use: find the site you need in the list and read the answer.* *How to use: find the site you need in the list and read the answer.*
- Reddit: don't use credentials at all or configure [OAuth](https://github.com/AAndyProgram/SCrawler/wiki/Settings#how-to-get-reddit-credentials). **Reddit profiles can be downloaded without any credentials at all. Subreddits require OAuth! If nothing downloads, use OAuth!** - Reddit: don't use credentials at all or configure [OAuth](https://github.com/AAndyProgram/SCrawler/wiki/Settings#how-to-get-reddit-credentials). **Reddit profiles can be downloaded without any credentials at all. Subreddits require OAuth! If nothing downloads, use OAuth!** Don't use OAuth token to download saved posts (use cookies only).
- **META** (**Instagram**, Threads, Facebook): you need **cookies** and fill in **all fields** - **META** (**Instagram**, Threads, Facebook): you need **cookies** and fill in **all fields**
- **Instagram [TIPS](https://github.com/AAndyProgram/SCrawler/wiki/Settings#instagram-tips)**
- **Instagram saved posts**: I don't consider questions like "I have 10k saved posts and only 1000 were downloaded". Download posts, remove them from saved posts, delete the `Saved posts` **settings folder**, repeat. - **Instagram saved posts**: I don't consider questions like "I have 10k saved posts and only 1000 were downloaded". Download posts, remove them from saved posts, delete the `Saved posts` **settings folder**, repeat.
- TikTok: works via yt-dlp. If something doesn't download, we need to wait until yt-dlp fixes it. TikTok doesn't require cookies to download. - TikTok: works via yt-dlp. If something doesn't download, we need to wait until yt-dlp fixes it. TikTok doesn't require cookies to download.
- Porn sites: **COOKIES**! - Porn sites: **COOKIES**!
- ThisVid: https://github.com/AAndyProgram/SCrawler/wiki/Settings#thisvid-faq - ThisVid: https://github.com/AAndyProgram/SCrawler/wiki/Settings#thisvid-faq
- **OnlyFans**: cookies + **all fields** + [OF-Scraper (download the correct version that I pointed)](https://github.com/AAndyProgram/SCrawler/wiki/Settings#of-scraper) & [mp4decrypt](https://www.bento4.com/downloads/) to download DRM protected videos. [OF-Scraper support](https://github.com/AAndyProgram/SCrawler/wiki/Settings#of-scraper-support). Also read [this](https://github.com/AAndyProgram/SCrawler/wiki/Settings#onlyfans-faq) - **OnlyFans**: cookies + **all fields** + [OF-Scraper (download the correct version that I pointed)](https://github.com/AAndyProgram/SCrawler/wiki/Settings#of-scraper) & [mp4decrypt](https://www.bento4.com/downloads/) & **DRM keys** to download DRM protected videos. [OF-Scraper support](https://github.com/AAndyProgram/SCrawler/wiki/Settings#of-scraper-support). Also read [this](https://github.com/AAndyProgram/SCrawler/wiki/Settings#onlyfans-faq)
- **JustForFans**: **THE VIDEO ISN'T DOWNLOADING AT THE MOMENT** ([Issue](https://discord.com/channels/1124032649682493462/1205547615199039551/1231349555132366870)) - **JustForFans**: **THE VIDEO ISN'T DOWNLOADING AT THE MOMENT** ([Issue](https://discord.com/channels/1124032649682493462/1205547615199039551/1231349555132366870))
## Other questions ## Other questions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -38,6 +38,7 @@ A program to download photo and video from [any site](#supported-sites) (e.g. Yo
- Reddit images, galleries of images, videos, saved posts; - Reddit images, galleries of images, videos, saved posts;
- Redgifs images and videos (https://www.redgifs.com/); - Redgifs images and videos (https://www.redgifs.com/);
- Twitter images and videos, saved (bookmarked) posts, likes, communities; - Twitter images and videos, saved (bookmarked) posts, likes, communities;
- Bluesky images and videos;
- OnlyFans images and videos, saved (bookmarked) posts, stories; - OnlyFans images and videos, saved (bookmarked) posts, stories;
- JustForFans images and videos, saved (bookmarked) posts; - JustForFans images and videos, saved (bookmarked) posts;
- Mastodon images and videos, saved (bookmarked) posts; - Mastodon images and videos, saved (bookmarked) posts;
@@ -78,6 +79,7 @@ A program to download photo and video from [any site](#supported-sites) (e.g. Yo
- **YouTube Music** - **YouTube Music**
- **Reddit** - **Reddit**
- **Twitter** - **Twitter**
- **Bluesky**
- **OnlyFans** *(partial support)*[^1] - **OnlyFans** *(partial support)*[^1]
- **Instagram** - **Instagram**
- **Threads** - **Threads**
@@ -131,6 +133,7 @@ First, the program downloads the full profile. After the program downloads only
- **[SITES REQUIREMENTS](https://github.com/AAndyProgram/SCrawler/wiki/Settings#sites-requirements)** - **[SITES REQUIREMENTS](https://github.com/AAndyProgram/SCrawler/wiki/Settings#sites-requirements)**
- [Reddit](https://github.com/AAndyProgram/SCrawler/wiki/Settings#reddit) - [Reddit](https://github.com/AAndyProgram/SCrawler/wiki/Settings#reddit)
- [Twitter](https://github.com/AAndyProgram/SCrawler/wiki/Settings#twitter) - [Twitter](https://github.com/AAndyProgram/SCrawler/wiki/Settings#twitter)
- [Bluesky](https://github.com/AAndyProgram/SCrawler/wiki/Settings#bluesky)
- [OnlyFans](https://github.com/AAndyProgram/SCrawler/wiki/Settings#onlyfans) - [OnlyFans](https://github.com/AAndyProgram/SCrawler/wiki/Settings#onlyfans)
- [Mastodon](https://github.com/AAndyProgram/SCrawler/wiki/Settings#mastodon) - [Mastodon](https://github.com/AAndyProgram/SCrawler/wiki/Settings#mastodon)
- [Instagram](https://github.com/AAndyProgram/SCrawler/wiki/Settings#instagram) - [Instagram](https://github.com/AAndyProgram/SCrawler/wiki/Settings#instagram)
@@ -215,16 +218,4 @@ F5-->[*]
Discord server: https://discord.gg/uFNUXvFFmg Discord server: https://discord.gg/uFNUXvFFmg
<!--
[e-mail](mailto:andyprogram@proton.me): andyprogram@proton.me
Matrix (Element): https://matrix.to/#/@andyprogram:matrix.org
Discord (contact the developer): andyprogram
Discord server: https://discord.gg/uFNUXvFFmg
[Wire](https://account.wire.com/user-profile/?id=93985052-cf2c-4b72-ac75-bbe3231cf544): @andyprogram
-->
[^1]: Partial support means that I don't have personal accounts on paid porn sites because I don't pay for porn. If this site has stopped downloading and you want me to fix it, please be ready to give me access to an account with at least one active subscription. Otherwise, the download from this site will not be fixed. [^1]: Partial support means that I don't have personal accounts on paid porn sites because I don't pay for porn. If this site has stopped downloading and you want me to fix it, please be ready to give me access to an account with at least one active subscription. Otherwise, the download from this site will not be fixed.

View File

@@ -17,6 +17,7 @@ Namespace Plugin
Property Settings As ISiteSettings Property Settings As ISiteSettings
Property AccountName As String Property AccountName As String
Property Name As String Property Name As String
Property NameTrue As String
Property ID As String Property ID As String
Property Options As String Property Options As String
Property ParseUserMediaOnly As Boolean Property ParseUserMediaOnly As Boolean

View File

@@ -13,7 +13,7 @@ Imports System.Runtime.InteropServices
<Assembly: AssemblyDescription("Plugin provider for SCrawler")> <Assembly: AssemblyDescription("Plugin provider for SCrawler")>
<Assembly: AssemblyCompany("AndyProgram")> <Assembly: AssemblyCompany("AndyProgram")>
<Assembly: AssemblyProduct("SCrawler.PluginProvider")> <Assembly: AssemblyProduct("SCrawler.PluginProvider")>
<Assembly: AssemblyCopyright("Copyright © 2024")> <Assembly: AssemblyCopyright("Copyright © 2025")>
<Assembly: AssemblyTrademark("AndyProgram")> <Assembly: AssemblyTrademark("AndyProgram")>
<Assembly: ComVisible(False)> <Assembly: ComVisible(False)>
@@ -32,6 +32,6 @@ Imports System.Runtime.InteropServices
' by using the '*' as shown below: ' by using the '*' as shown below:
' <Assembly: AssemblyVersion("1.0.*")> ' <Assembly: AssemblyVersion("1.0.*")>
<Assembly: AssemblyVersion("2024.5.18.0")> <Assembly: AssemblyVersion("2025.2.25.0")>
<Assembly: AssemblyFileVersion("2024.5.18.0")> <Assembly: AssemblyFileVersion("2025.2.25.0")>
<Assembly: NeutralResourcesLanguage("en")> <Assembly: NeutralResourcesLanguage("en")>

View File

@@ -168,9 +168,6 @@ Namespace API.YouTube.Base
<Browsable(True), GridVisible, XMLVN({"Info"}), Category("Info"), DisplayName("Create description files"), <Browsable(True), GridVisible, XMLVN({"Info"}), Category("Info"), DisplayName("Create description files"),
Description("Create video description files. Default: false.")> Description("Create video description files. Default: false.")>
Public ReadOnly Property CreateDescriptionFiles As XMLValue(Of Boolean) Public ReadOnly Property CreateDescriptionFiles As XMLValue(Of Boolean)
<Browsable(True), GridVisible, XMLVN({"Info"}, True), Category("Info"), DisplayName("Create description files: add upload date"),
Description("Add the upload date to the top of the description file. Default: true.")>
Public ReadOnly Property CreateDescriptionFiles_AddUploadDate As XMLValue(Of Boolean)
<Browsable(True), GridVisible, XMLVN({"Info"}, True), Category("Info"), DisplayName("Create description files: create without description"), <Browsable(True), GridVisible, XMLVN({"Info"}, True), Category("Info"), DisplayName("Create description files: create without description"),
Description("Create a description file with the upload date, even if the description does not exist. Default: true.")> Description("Create a description file with the upload date, even if the description does not exist. Default: true.")>
Public ReadOnly Property CreateDescriptionFiles_CreateWithNoDescription As XMLValue(Of Boolean) Public ReadOnly Property CreateDescriptionFiles_CreateWithNoDescription As XMLValue(Of Boolean)
@@ -191,7 +188,7 @@ Namespace API.YouTube.Base
<Browsable(True), GridVisible, XMLVN({"Defaults"}), Category("Defaults"), DisplayName("Use cookies"), <Browsable(True), GridVisible, XMLVN({"Defaults"}), Category("Defaults"), DisplayName("Use cookies"),
Description("By default, use cookies when downloading from YouTube.")> Description("By default, use cookies when downloading from YouTube.")>
Public ReadOnly Property DefaultUseCookies As XMLValue(Of Boolean) Public ReadOnly Property DefaultUseCookies As XMLValue(Of Boolean)
<Browsable(True), GridVisible, XMLVN({"Defaults"}, Protocols.Any), Category("Defaults"), DisplayName("Protocol"), <Browsable(True), GridVisible, XMLVN({"Defaults"}, Protocols.https), Category("Defaults"), DisplayName("Protocol"),
Description("Priority download protocol. Default: 'Any'")> Description("Priority download protocol. Default: 'Any'")>
Public ReadOnly Property DefaultProtocol As XMLValue(Of Protocols) Public ReadOnly Property DefaultProtocol As XMLValue(Of Protocols)
<Browsable(True), GridVisible(False), XMLVN({"Defaults"}), Category("Defaults"), <Browsable(True), GridVisible(False), XMLVN({"Defaults"}), Category("Defaults"),
@@ -267,6 +264,9 @@ Namespace API.YouTube.Base
<Browsable(True), GridVisible, XMLVN({"Defaults"}), Category("Defaults"), DisplayName("Add date to title: video list"), <Browsable(True), GridVisible, XMLVN({"Defaults"}), Category("Defaults"), DisplayName("Add date to title: video list"),
Description("Add video upload date before video title (visual only) in the video list")> Description("Add video upload date before video title (visual only) in the video list")>
Public ReadOnly Property FileAddDateToFileName_VideoList As XMLValue(Of Boolean) Public ReadOnly Property FileAddDateToFileName_VideoList As XMLValue(Of Boolean)
<Browsable(True), GridVisible, XMLVN({"Defaults"}, FileDateMode.None), Category("Defaults"), DisplayName("Add channel to file name"),
Description("Add channel name before/after the file name")>
Public ReadOnly Property FileAddChannelToFileName As XMLValue(Of FileDateMode)
#End Region #End Region
#Region "Defaults ChannelsDownload" #Region "Defaults ChannelsDownload"
<Browsable(True), GridVisible, XMLVN({"Defaults", "Channels"}), Category("Defaults"), DisplayName("Default download tabs for channels"), <Browsable(True), GridVisible, XMLVN({"Defaults", "Channels"}), Category("Defaults"), DisplayName("Default download tabs for channels"),

View File

@@ -121,9 +121,10 @@ Namespace API.YouTube.Controls
Me.TXT_LIMIT.Location = New System.Drawing.Point(3, 3) Me.TXT_LIMIT.Location = New System.Drawing.Point(3, 3)
Me.TXT_LIMIT.Name = "TXT_LIMIT" Me.TXT_LIMIT.Name = "TXT_LIMIT"
Me.TXT_LIMIT.PlaceholderEnabled = True Me.TXT_LIMIT.PlaceholderEnabled = True
Me.TXT_LIMIT.PlaceholderText = "e.g. ABCDE" Me.TXT_LIMIT.PlaceholderText = "e.g. RDAMP"
Me.TXT_LIMIT.Size = New System.Drawing.Size(378, 22) Me.TXT_LIMIT.Size = New System.Drawing.Size(378, 22)
Me.TXT_LIMIT.TabIndex = 2 Me.TXT_LIMIT.TabIndex = 2
Me.TXT_LIMIT.Text = "RDAMP"
' '
'CONTAINER_MAIN 'CONTAINER_MAIN
' '

View File

@@ -13,7 +13,7 @@ Imports System.Runtime.InteropServices
<Assembly: AssemblyDescription("YouTube plugin environment")> <Assembly: AssemblyDescription("YouTube plugin environment")>
<Assembly: AssemblyCompany("AndyProgram")> <Assembly: AssemblyCompany("AndyProgram")>
<Assembly: AssemblyProduct("SCrawler.YouTube")> <Assembly: AssemblyProduct("SCrawler.YouTube")>
<Assembly: AssemblyCopyright("Copyright © 2024")> <Assembly: AssemblyCopyright("Copyright © 2025")>
<Assembly: AssemblyTrademark("AndyProgram")> <Assembly: AssemblyTrademark("AndyProgram")>
<Assembly: ComVisible(False)> <Assembly: ComVisible(False)>
@@ -32,6 +32,6 @@ Imports System.Runtime.InteropServices
' by using the '*' as shown below: ' by using the '*' as shown below:
' <Assembly: AssemblyVersion("1.0.*")> ' <Assembly: AssemblyVersion("1.0.*")>
<Assembly: AssemblyVersion("2024.10.24.0")> <Assembly: AssemblyVersion("2025.2.25.0")>
<Assembly: AssemblyFileVersion("2024.10.24.0")> <Assembly: AssemblyFileVersion("2025.2.25.0")>
<Assembly: NeutralResourcesLanguage("en")> <Assembly: NeutralResourcesLanguage("en")>

View File

@@ -685,10 +685,17 @@ Namespace API.YouTube.Objects
Friend Sub FileDateUpdate() Friend Sub FileDateUpdate()
Dim n$ = _File.Name.StringTrim Dim n$ = _File.Name.StringTrim
Dim s$ = IIf(n.IsEmptyString, String.Empty, " ") Dim s$ = IIf(n.IsEmptyString, String.Empty, " ")
Dim c$ = AccountName.IfNullOrEmpty(UserID)
Select Case MyYouTubeSettings.FileAddDateToFileName.Value Select Case MyYouTubeSettings.FileAddDateToFileName.Value
Case FileDateMode.Before : n = $"[{DateAdded:yyyy-MM-dd}]{s}{n}" Case FileDateMode.Before : n = $"[{DateAdded:yyyy-MM-dd}]{s}{n}"
Case FileDateMode.After : n = $"{n}{s}[{DateAdded:yyyy-MM-dd}]" Case FileDateMode.After : n = $"{n}{s}[{DateAdded:yyyy-MM-dd}]"
End Select End Select
If Not c.IsEmptyString Then
Select Case MyYouTubeSettings.FileAddChannelToFileName.Value
Case FileDateMode.Before : n = $"[{c}] {n}"
Case FileDateMode.After : n = $"{n} [{c}]"
End Select
End If
_File.Name = n _File.Name = n
End Sub End Sub
Public Property FileSettings As SFile Public Property FileSettings As SFile
@@ -1208,12 +1215,15 @@ Namespace API.YouTube.Objects
End If End If
With MyYouTubeSettings With MyYouTubeSettings
If .CreateDescriptionFiles And (Not Description.IsEmptyString Or If .CreateDescriptionFiles And (Not Description.IsEmptyString Or .CreateDescriptionFiles_CreateWithNoDescription) Then
(.CreateDescriptionFiles_CreateWithNoDescription And .CreateDescriptionFiles_AddUploadDate)) Then
Dim fileDesr As SFile = File Dim fileDesr As SFile = File
fileDesr.Extension = "txt" fileDesr.Extension = "txt"
Using fileDesrText As New TextSaver(fileDesr) Using fileDesrText As New TextSaver(fileDesr)
If .CreateDescriptionFiles_AddUploadDate Then fileDesrText.Append($"Uploaded: {DateAdded:yyyy-MM-dd HH:mm:ss}") fileDesrText.Append($"Uploaded: {DateAdded:yyyy-MM-dd HH:mm:ss}")
fileDesrText.AppendLine()
fileDesrText.AppendLine($"URL: {URL}")
fileDesrText.AppendLine($"Channel name: {AccountName}")
fileDesrText.AppendLine($"Channel ID: {UserID}")
If Not Description.IsEmptyString Then If Not Description.IsEmptyString Then
If Not fileDesrText.IsEmptyString Then fileDesrText.AppendLine.AppendLine() If Not fileDesrText.IsEmptyString Then fileDesrText.AppendLine.AppendLine()
fileDesrText.Append(Description) fileDesrText.Append(Description)

View File

@@ -13,7 +13,7 @@ Imports System.Runtime.InteropServices
<Assembly: AssemblyDescription("SCrawler YouTube downloader")> <Assembly: AssemblyDescription("SCrawler YouTube downloader")>
<Assembly: AssemblyCompany("AndyProgram")> <Assembly: AssemblyCompany("AndyProgram")>
<Assembly: AssemblyProduct("SCrawler.YouTubeDownloader")> <Assembly: AssemblyProduct("SCrawler.YouTubeDownloader")>
<Assembly: AssemblyCopyright("Copyright © 2024")> <Assembly: AssemblyCopyright("Copyright © 2025")>
<Assembly: AssemblyTrademark("AndyProgram")> <Assembly: AssemblyTrademark("AndyProgram")>
<Assembly: ComVisible(False)> <Assembly: ComVisible(False)>
@@ -32,6 +32,6 @@ Imports System.Runtime.InteropServices
' by using the '*' as shown below: ' by using the '*' as shown below:
' <Assembly: AssemblyVersion("1.0.*")> ' <Assembly: AssemblyVersion("1.0.*")>
<Assembly: AssemblyVersion("2024.10.24.0")> <Assembly: AssemblyVersion("2025.2.25.0")>
<Assembly: AssemblyFileVersion("2024.10.24.0")> <Assembly: AssemblyFileVersion("2025.2.25.0")>
<Assembly: NeutralResourcesLanguage("en")> <Assembly: NeutralResourcesLanguage("en")>

View File

@@ -6,8 +6,9 @@
' '
' This program is distributed in the hope that it will be useful, ' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY ' but WITHOUT ANY WARRANTY
Imports System.Net Imports SCrawler.Plugin
Imports PersonalUtilities.Functions.RegularExpressions Imports PersonalUtilities.Functions.RegularExpressions
Imports Download = SCrawler.Plugin.ISiteSettings.Download
Namespace API.Base Namespace API.Base
Friend NotInheritable Class DownDetector Friend NotInheritable Class DownDetector
Private Shared ReadOnly Property Params As New RParams("x:.'([\S]+?)',.y:.(\d+)", -1, Nothing, RegexReturn.List) Private Shared ReadOnly Property Params As New RParams("x:.'([\S]+?)',.y:.(\d+)", -1, Nothing, RegexReturn.List)
@@ -34,34 +35,106 @@ Namespace API.Base
Try Try
Dim l As List(Of Data) = Nothing Dim l As List(Of Data) = Nothing
Dim l2 As List(Of Data) = Nothing Dim l2 As List(Of Data) = Nothing
Using w As New WebClient Dim r$ = GetWebString($"https://downdetector.co.uk/status/{Site}/",, EDP.ThrowException)
Dim r$ = w.DownloadString($"https://downdetector.co.uk/status/{Site}/") If Not r.IsEmptyString Then
If Not r.IsEmptyString Then l = RegexFields(Of Data)(r, {Params}, {1, 2})
l = RegexFields(Of Data)(r, {Params}, {1, 2}) If l.ListExists(2) Then
If l.ListExists(2) Then l.Sort()
l.Sort() l2 = New List(Of Data)
l2 = New List(Of Data) Dim d As Data
Dim d As Data Dim eDates As New List(Of Date)
Dim eDates As New List(Of Date) Dim MaxValue As Func(Of Date, Integer) = Function(dd) (From ddd As Data In l Where ddd.Date = dd Select ddd.Value).DefaultIfEmpty(0).Max
Dim MaxValue As Func(Of Date, Integer) = Function(dd) (From ddd In l Where ddd.Date = dd Select ddd.Value).DefaultIfEmpty(0).Max For i% = 0 To l.Count - 1
For i% = 0 To l.Count - 1 If Not eDates.Contains(l(i).Date) Then
If Not eDates.Contains(l(i).Date) Then d = l(i)
d = l(i) d.Value = MaxValue(d.Date)
d.Value = MaxValue(d.Date) l2.Add(d)
l2.Add(d) eDates.Add(d.Date)
eDates.Add(d.Date) End If
End If Next
Next eDates.Clear()
eDates.Clear() l.Clear()
l.Clear() l2.Sort()
l2.Sort()
End If
End If End If
End Using End If
Return l2 Return l2
Catch ex As Exception Catch ex As Exception
Return ErrorsDescriber.Execute(EDP.SendToLog + EDP.ReturnValue, ex, $"[DownDetector.GetData({Site})]") Return ErrorsDescriber.Execute(EDP.SendToLog + EDP.ReturnValue, ex, $"[DownDetector.GetData({Site})]")
End Try End Try
End Function End Function
Friend Interface IDownDetector
ReadOnly Property Value As Integer
ReadOnly Property AddToLog As Boolean
ReadOnly Property CheckSite As String
Function Available(ByVal What As Download, ByVal Silent As Boolean) As Boolean
End Interface
Friend Class Checker(Of T As {ISiteSettings, IDownDetector})
Protected ReadOnly Property Source As T
Private ReadOnly NP As New ANumbers With {.FormatOptions = ANumbers.Options.GroupIntegral}
Friend Sub New(ByRef _Source As T)
Source = _Source
End Sub
Private ____AvailableChecked As Boolean = False
Private ____AvailableResult As Boolean = False
Friend Overridable Function Available(ByVal What As Download, ByVal Silent As Boolean) As Boolean
If Settings.DownDetectorEnabled And Source.Value >= 0 Then
If Not ____AvailableChecked Then
____AvailableResult = AvailableImpl(What, Silent)
____AvailableChecked = True
End If
Return ____AvailableResult
Else
Return True
End If
End Function
Protected Overridable Function AvailableImpl(ByVal What As Download, ByVal Silent As Boolean) As Boolean
Try
Source.AvailableText = String.Empty
If Source.Value < 0 Then
Return True
Else
Dim dl As List(Of Data) = GetData(Source.CheckSite)
If dl.ListExists Then
dl = dl.Take(4).ToList
Dim avg% = dl.Average(Function(d) d.Value)
If avg > Source.Value Then
Source.AvailableText = $"Over the past hour, {Source.Site} has received an average of {avg.NumToString(NP)} outage reports:{vbCr}{dl.ListToString(vbCr)}"
If Source.AddToLog Then MyMainLOG = Source.AvailableText
If Silent Then
Return AvailableImpl_FALSE_SILENT()
Else
If MsgBoxE({$"{Source.AvailableText}{vbCr}{vbCr}Do you want to continue parsing {Source.Site} data?",
$"There are outage reports on {Source.Site}"}, vbYesNo) = vbYes Then
Return AvailableImpl_FALSE_SILENT_NOT_MSG_YES()
Else
Return AvailableImpl_FALSE_SILENT_NOT_MSG_NO()
End If
End If
End If
End If
Return AvailableImpl_TRUE()
End If
Catch ex As Exception
Return ErrorsDescriber.Execute(EDP.SendToLog + EDP.ReturnValue, ex, $"[API.{Source.Site}.SiteSettings.Available([DownDetector])]", True)
End Try
End Function
Protected Overridable Function AvailableImpl_TRUE() As Boolean
Return True
End Function
Protected Overridable Function AvailableImpl_FALSE_SILENT() As Boolean
Return False
End Function
Protected Overridable Function AvailableImpl_FALSE_SILENT_NOT_MSG_YES() As Boolean
Return True
End Function
Protected Overridable Function AvailableImpl_FALSE_SILENT_NOT_MSG_NO() As Boolean
Return False
End Function
Friend Overridable Sub Reset()
____AvailableChecked = False
____AvailableResult = False
Source.AvailableText = String.Empty
End Sub
End Class
End Class End Class
End Namespace End Namespace

View File

@@ -0,0 +1,22 @@
' Copyright (C) Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify
' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or
' (at your option) any later version.
'
' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY
Imports SCrawler.Plugin.Attributes
Imports DN = SCrawler.API.Base.DeclaredNames
Namespace API.Base
Friend Class EditorExchangeOptionsBase
Friend Overridable Property SiteKey As String
<PSetting(Address:=SettingAddress.User, Caption:=DN.UserNameChangeCaption, ToolTip:=DN.UserNameChangeToolTip)>
Friend Overridable Property UserName As String = String.Empty
Friend Sub New(ByVal u As UserDataBase)
UserName = u.NameTrue(True)
End Sub
Friend Sub New()
End Sub
End Class
End Namespace

View File

@@ -6,66 +6,7 @@
' '
' This program is distributed in the hope that it will be useful, ' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY ' but WITHOUT ANY WARRANTY
Imports PersonalUtilities.Tools
Imports PersonalUtilities.Functions.RegularExpressions
Namespace API.Base.GDL Namespace API.Base.GDL
Friend Module Declarations
Private Structure GDLURL : Implements IRegExCreator
Private _URL As String
Friend ReadOnly Property URL As String
Get
Return _URL
End Get
End Property
Public Shared Widening Operator CType(ByVal u As String) As GDLURL
Return New GDLURL With {._URL = u}
End Operator
Public Shared Widening Operator CType(ByVal u As GDLURL) As String
Return u.URL
End Operator
Private Function CreateFromArray(ByVal ParamsArray() As String) As Object Implements IRegExCreator.CreateFromArray
If ParamsArray.ListExists(2) Then
Dim u$ = ParamsArray(0).StringTrim.StringTrimEnd("/"), u2$
If Not u.IsEmptyString Then
u2 = ParamsArray(1).StringTrim
If Not u2.IsEmptyString AndAlso u2.StartsWith("GET", StringComparison.OrdinalIgnoreCase) Then
u2 = u2.Remove(0, 3).StringTrim.StringTrimStart("/")
If Not u2.IsEmptyString Then _URL = $"{u}/{u2}"
End If
End If
End If
Return Me
End Function
Public Shared Operator =(ByVal x As GDLURL, ByVal y As GDLURL) As Boolean
Return x.URL = y.URL
End Operator
Public Shared Operator <>(ByVal x As GDLURL, ByVal y As GDLURL) As Boolean
Return Not x.URL = y.URL
End Operator
Public Overrides Function ToString() As String
Return URL
End Function
Public Overrides Function Equals(ByVal Obj As Object) As Boolean
Return URL = CType(Obj, String)
End Function
End Structure
Private ReadOnly Property GdlUrlPattern As RParams = RParams.DM(GDLBatch.UrlLibStart.Replace("[", "\[").Replace("]", "\]") &
"([^""]+?)""(GET [^""]+)""", 0, EDP.ReturnValue)
Friend Function GetUrlsFromGalleryDl(ByVal Batch As BatchExecutor, ByVal Command As String) As List(Of String)
Dim urls As New List(Of String)
Dim u As GDLURL
With Batch
.Execute(Command)
If .ErrorOutputData.Count > 0 Then
For Each eValue$ In .ErrorOutputData
u = RegexFields(Of GDLURL)(eValue, {GdlUrlPattern}, {1, 2}, EDP.ReturnValue).ListIfNothing.FirstOrDefault
If Not u.URL.IsEmptyString Then urls.ListAddValue(u, LNC)
Next
End If
End With
Return urls
End Function
End Module
Friend Class GDLBatch : Inherits TokenBatch Friend Class GDLBatch : Inherits TokenBatch
Friend Const UrlLibStart As String = "[urllib3.connectionpool][debug]" Friend Const UrlLibStart As String = "[urllib3.connectionpool][debug]"
Friend Const UrlTextStart As String = UrlLibStart & " https" Friend Const UrlTextStart As String = UrlLibStart & " https"

View File

@@ -18,6 +18,7 @@ Namespace API.Base
End Enum End Enum
ReadOnly Property Site As String ReadOnly Property Site As String
ReadOnly Property Name As String ReadOnly Property Name As String
Property NameTrue As String
Property ID As String Property ID As String
Property Options As String Property Options As String
Property FriendlyName As String Property FriendlyName As String

View File

@@ -33,7 +33,7 @@ Namespace API.Base
End Property End Property
Friend Property AccountName As String Implements ISiteSettings.AccountName Friend Property AccountName As String Implements ISiteSettings.AccountName
Friend Property Temporary As Boolean = False Implements ISiteSettings.Temporary Friend Property Temporary As Boolean = False Implements ISiteSettings.Temporary
Friend Property DefaultInstance As ISiteSettings = Nothing Implements ISiteSettings.DefaultInstance Friend Overridable Property DefaultInstance As ISiteSettings = Nothing Implements ISiteSettings.DefaultInstance
Protected _UserAgentDefault As String = String.Empty Protected _UserAgentDefault As String = String.Empty
Friend Overridable Property UserAgentDefault As String Implements ISiteSettings.UserAgentDefault Friend Overridable Property UserAgentDefault As String Implements ISiteSettings.UserAgentDefault
Get Get
@@ -55,6 +55,11 @@ Namespace API.Base
Friend Overridable ReadOnly Property Responser As Responser Friend Overridable ReadOnly Property Responser As Responser
Private _UserOptionsExists As Boolean = False Private _UserOptionsExists As Boolean = False
Private _UserOptionsType As Type = Nothing Private _UserOptionsType As Type = Nothing
Protected Overridable Function UserOptionsValid(ByVal Options As Object) As Boolean
Return True
End Function
Protected Overridable Sub UserOptionsSetParameters(ByRef Options As Object)
End Sub
Protected Property UserOptionsType As Type Protected Property UserOptionsType As Type
Get Get
Return _UserOptionsType Return _UserOptionsType
@@ -243,7 +248,7 @@ Namespace API.Base
#Region "User info" #Region "User info"
Protected UrlPatternUser As String = String.Empty Protected UrlPatternUser As String = String.Empty
Friend Overridable Function GetUserUrl(ByVal User As IPluginContentProvider) As String Implements ISiteSettings.GetUserUrl Friend Overridable Function GetUserUrl(ByVal User As IPluginContentProvider) As String Implements ISiteSettings.GetUserUrl
If Not UrlPatternUser.IsEmptyString Then Return String.Format(UrlPatternUser, User.Name) If Not UrlPatternUser.IsEmptyString Then Return String.Format(UrlPatternUser, User.NameTrue.IfNullOrEmpty(User.Name))
Return String.Empty Return String.Empty
End Function End Function
Private Function ISiteSettings_GetUserPostUrl(ByVal User As IPluginContentProvider, ByVal Media As IUserMedia) As String Implements ISiteSettings.GetUserPostUrl Private Function ISiteSettings_GetUserPostUrl(ByVal User As IPluginContentProvider, ByVal Media As IUserMedia) As String Implements ISiteSettings.GetUserPostUrl
@@ -380,11 +385,40 @@ Namespace API.Base
End Sub End Sub
Friend Overridable Sub UserOptions(ByRef Options As Object, ByVal OpenForm As Boolean) Implements ISiteSettings.UserOptions Friend Overridable Sub UserOptions(ByRef Options As Object, ByVal OpenForm As Boolean) Implements ISiteSettings.UserOptions
If _UserOptionsExists Then If _UserOptionsExists Then
If Options Is Nothing OrElse Not Options.GetType Is _UserOptionsType Then If Options Is Nothing OrElse (Not Options.GetType Is _UserOptionsType OrElse Not UserOptionsValid(Options)) Then
Options = AConvert(Me, AModes.Var, _UserOptionsType,, True, Nothing) Dim args% = 0
Dim constructor As ConstructorInfo = Nothing
With _UserOptionsType.GetTypeInfo.DeclaredConstructors
If .ListExists Then
With .Where(Function(ByVal c As ConstructorInfo) As Boolean
With c.GetParameters
If .ListExists Then
If .Count = 1 Then
Return .Self()(0).ParameterType.GetInterfaces.ListIfNothing.Where(Function(i) i Is Me.GetType).Count = 1
Else
Return False
End If
Else
Return True
End If
End With
Return If(c.GetParameters?.Count, 0).ValueBetween(0, 1)
End Function)
If .ListExists Then
args = .Max(Of Integer)(Function(c) If(c.GetParameters?.Count, 0))
constructor = .First(Function(c) If(c.GetParameters?.Count, 0) = args)
End If
End With
End If
End With
If Not constructor Is Nothing Then
If args > 0 AndAlso Not constructor.GetParameters()(0).ParameterType Is GetType(ISiteSettings) Then Throw New Exception
If args = 0 Then Options = constructor.Invoke(Nothing) Else Options = constructor.Invoke({Me})
End If
If Options Is Nothing Then Options = Activator.CreateInstance(_UserOptionsType) If Options Is Nothing Then Options = Activator.CreateInstance(_UserOptionsType)
If Not Options Is Nothing Then UserOptionsSetParameters(Options)
End If End If
If OpenForm Then If Not Options Is Nothing And OpenForm Then
Using f As New InternalSettingsForm(Options, Me, False) : f.ShowDialog() : End Using Using f As New InternalSettingsForm(Options, Me, False) : f.ShowDialog() : End Using
End If End If
Else Else

View File

@@ -80,6 +80,8 @@ Namespace API.Base
Private _CollectionButtonsExists As Boolean = False Private _CollectionButtonsExists As Boolean = False
Private _CollectionButtonsColorsSet As Boolean = False Private _CollectionButtonsColorsSet As Boolean = False
Friend WithEvents BTT_CONTEXT_DOWN As ToolStripKeyMenuItem Friend WithEvents BTT_CONTEXT_DOWN As ToolStripKeyMenuItem
Friend WithEvents BTT_CONTEXT_DOWN_LIMIT As ToolStripKeyMenuItem
Friend WithEvents BTT_CONTEXT_DOWN_DATE As ToolStripKeyMenuItem
Friend WithEvents BTT_CONTEXT_EDIT As ToolStripMenuItem Friend WithEvents BTT_CONTEXT_EDIT As ToolStripMenuItem
Friend WithEvents BTT_CONTEXT_DELETE As ToolStripMenuItem Friend WithEvents BTT_CONTEXT_DELETE As ToolStripMenuItem
Friend WithEvents BTT_CONTEXT_ERASE As ToolStripMenuItem Friend WithEvents BTT_CONTEXT_ERASE As ToolStripMenuItem
@@ -98,6 +100,8 @@ Namespace API.Base
End If End If
End With End With
BTT_CONTEXT_DOWN = New ToolStripKeyMenuItem(tn, i) With {.Name = tnn("DOWN"), .Tag = Me} BTT_CONTEXT_DOWN = New ToolStripKeyMenuItem(tn, i) With {.Name = tnn("DOWN"), .Tag = Me}
BTT_CONTEXT_DOWN_LIMIT = New ToolStripKeyMenuItem(tn, i) With {.Name = tnn("DOWN_LIMIT"), .Tag = Me}
BTT_CONTEXT_DOWN_DATE = New ToolStripKeyMenuItem(tn, i) With {.Name = tnn("DOWN_DATE"), .Tag = Me}
BTT_CONTEXT_EDIT = New ToolStripMenuItem(tn, i) With {.Name = tnn("EDIT"), .Tag = Me} BTT_CONTEXT_EDIT = New ToolStripMenuItem(tn, i) With {.Name = tnn("EDIT"), .Tag = Me}
BTT_CONTEXT_DELETE = New ToolStripMenuItem(tn, i) With {.Name = tnn("DELETE"), .Tag = Me} BTT_CONTEXT_DELETE = New ToolStripMenuItem(tn, i) With {.Name = tnn("DELETE"), .Tag = Me}
BTT_CONTEXT_ERASE = New ToolStripMenuItem(tn, i) With {.Name = tnn("ERASE"), .Tag = Me} BTT_CONTEXT_ERASE = New ToolStripMenuItem(tn, i) With {.Name = tnn("ERASE"), .Tag = Me}
@@ -117,7 +121,8 @@ Namespace API.Base
cb = MyColor.EditBack cb = MyColor.EditBack
cf = MyColor.EditFore cf = MyColor.EditFore
End If End If
For Each b As ToolStripMenuItem In {BTT_CONTEXT_DOWN, BTT_CONTEXT_EDIT, BTT_CONTEXT_DELETE, BTT_CONTEXT_ERASE, For Each b As ToolStripMenuItem In {BTT_CONTEXT_DOWN, BTT_CONTEXT_DOWN_LIMIT, BTT_CONTEXT_DOWN_DATE,
BTT_CONTEXT_EDIT, BTT_CONTEXT_DELETE, BTT_CONTEXT_ERASE,
BTT_CONTEXT_OPEN_PATH, BTT_CONTEXT_OPEN_SITE} BTT_CONTEXT_OPEN_PATH, BTT_CONTEXT_OPEN_SITE}
If Not b Is Nothing Then b.BackColor = cb : b.ForeColor = cf If Not b Is Nothing Then b.BackColor = cb : b.ForeColor = cf
Next Next
@@ -173,6 +178,8 @@ Namespace API.Base
#Region "Additional names" #Region "Additional names"
Protected Const Name_SiteMode As String = "SiteMode" Protected Const Name_SiteMode As String = "SiteMode"
Protected Const Name_TrueName As String = "TrueName" Protected Const Name_TrueName As String = "TrueName"
'TODELETE Name_TrueName2
<Obsolete> Protected Const Name_TrueName2 As String = "NameTrue"
Protected Const Name_Arguments As String = "Arguments" Protected Const Name_Arguments As String = "Arguments"
#End Region #End Region
#End Region #End Region
@@ -273,6 +280,21 @@ Namespace API.Base
Return User.Name Return User.Name
End Get End Get
End Property End Property
Private _NameTrue As String = String.Empty
Friend Overridable Overloads Property NameTrue As String Implements IUserData.NameTrue, IPluginContentProvider.NameTrue
Get
Return NameTrue(False)
End Get
Set(ByVal NewName As String)
If Not _NameTrue = NewName Then EnvirChanged(NewName)
_NameTrue = NewName
End Set
End Property
Friend Overloads ReadOnly Property NameTrue(ByVal Exact As Boolean) As String
Get
Return If(Exact, _NameTrue, _NameTrue.IfNullOrEmpty(Name))
End Get
End Property
Friend Overridable Property ID As String = String.Empty Implements IUserData.ID, IPluginContentProvider.ID Friend Overridable Property ID As String = String.Empty Implements IUserData.ID, IPluginContentProvider.ID
Protected _FriendlyName As String = String.Empty Protected _FriendlyName As String = String.Empty
Friend Overridable Property FriendlyName As String Implements IUserData.FriendlyName Friend Overridable Property FriendlyName As String Implements IUserData.FriendlyName
@@ -343,12 +365,20 @@ Namespace API.Base
Protected Function UserDescriptionNeedToUpdate() As Boolean Protected Function UserDescriptionNeedToUpdate() As Boolean
Return (UserDescription.IsEmptyString Or _DescriptionEveryTime) And Not _DescriptionChecked Return (UserDescription.IsEmptyString Or _DescriptionEveryTime) And Not _DescriptionChecked
End Function End Function
Protected Sub UserDescriptionUpdate(ByVal Descr As String) Protected Sub UserDescriptionUpdate(ByVal Descr As String, Optional ByVal Force As Boolean = False,
If UserDescriptionNeedToUpdate() Then Optional ByVal InsertFirst As Boolean = False, Optional ByVal AppendDate As Boolean = False)
If UserDescriptionNeedToUpdate() Or Force Then
If AppendDate Then Descr = $"{Now.ToStringDateDef}: {Descr}"
If UserDescription.IsEmptyString Then If UserDescription.IsEmptyString Then
UserDescription = Descr UserDescription = Descr
_ForceSaveUserInfo = True
ElseIf Not UserDescription.Contains(Descr) Then ElseIf Not UserDescription.Contains(Descr) Then
UserDescription &= $"{vbNewLine}----{vbNewLine}{Descr}" If InsertFirst Then
UserDescription = $"{Descr}{vbNewLine}{UserDescription}"
Else
UserDescription &= $"{vbNewLine}----{vbNewLine}{Descr}"
End If
_ForceSaveUserInfo = True
End If End If
_DescriptionChecked = True _DescriptionChecked = True
End If End If
@@ -902,6 +932,10 @@ BlockNullPicture:
FileExists = True FileExists = True
Using x As New XmlFile(MyFileSettings) With {.XmlReadOnly = True} Using x As New XmlFile(MyFileSettings) With {.XmlReadOnly = True}
If User.Name.IsEmptyString Then User.Name = x.Value(Name_UserName) If User.Name.IsEmptyString Then User.Name = x.Value(Name_UserName)
_NameTrue = x.Value(Name_TrueName)
#Disable Warning BC40008
If _NameTrue.IsEmptyString AndAlso x.Contains(Name_TrueName2) Then _NameTrue = x.Value(Name_TrueName2)
#Enable Warning
UserExists = x.Value(Name_UserExists).FromXML(Of Boolean)(True) UserExists = x.Value(Name_UserExists).FromXML(Of Boolean)(True)
UserSuspended = x.Value(Name_UserSuspended).FromXML(Of Boolean)(False) UserSuspended = x.Value(Name_UserSuspended).FromXML(Of Boolean)(False)
ID = x.Value(Name_UserID) ID = x.Value(Name_UserID)
@@ -962,6 +996,7 @@ BlockNullPicture:
x.Add(Name_Plugin, HOST.Key) x.Add(Name_Plugin, HOST.Key)
x.Add(Name_AccountName, AccountName) x.Add(Name_AccountName, AccountName)
x.Add(Name_UserName, User.Name) x.Add(Name_UserName, User.Name)
x.Add(Name_TrueName, _NameTrue)
x.Add(Name_Model_User, CInt(UserModel)) x.Add(Name_Model_User, CInt(UserModel))
x.Add(Name_Model_Collection, CInt(CollectionModel)) x.Add(Name_Model_Collection, CInt(CollectionModel))
x.Add(Name_SpecialPath, User.SpecialPath) x.Add(Name_SpecialPath, User.SpecialPath)
@@ -1157,6 +1192,7 @@ BlockNullPicture:
Select Case Caller Select Case Caller
Case NameOf(UserExists) : If Not _EnvirUserExists = CBool(NewValue) Then _EnvirChanged = True : _EnvirInvokeUserUpdated = True Case NameOf(UserExists) : If Not _EnvirUserExists = CBool(NewValue) Then _EnvirChanged = True : _EnvirInvokeUserUpdated = True
Case NameOf(UserSuspended) : If Not _EnvirUserSuspended = CBool(NewValue) Then _EnvirChanged = True : _EnvirInvokeUserUpdated = True Case NameOf(UserSuspended) : If Not _EnvirUserSuspended = CBool(NewValue) Then _EnvirChanged = True : _EnvirInvokeUserUpdated = True
Case NameOf(NameTrue) : _EnvirChanged = True : _EnvirInvokeUserUpdated = True : _ForceSaveUserInfo = True : _ForceSaveUserInfoOnException = True
Case Else : _EnvirChanged = True Case Else : _EnvirChanged = True
End Select End Select
End If End If
@@ -1277,17 +1313,19 @@ BlockNullPicture:
UpdateUserInformation_Ex() UpdateUserInformation_Ex()
If Not exit_ex.Silent Then If Not exit_ex.Silent Then
If exit_ex.SimpleLogLine Then If exit_ex.SimpleLogLine Then
MyMainLOG = $"{ToStringForLog()}: downloading interrupted (exit) ({exit_ex.Message})" LogError(Nothing, $"downloading interrupted (exit) ({exit_ex.Message})")
Else Else
ErrorsDescriber.Execute(EDP.SendToLog, exit_ex, $"{ToStringForLog()}: downloading interrupted (exit)") LogError(exit_ex, "downloading interrupted (exit)")
End If End If
End If End If
If _EnvirInvokeUserUpdated Then OnUserUpdated()
Canceled = True Canceled = True
Catch dex As ObjectDisposedException When Disposed Catch dex As ObjectDisposedException When Disposed
Canceled = True Canceled = True
Catch ex As Exception Catch ex As Exception
UpdateUserInformation_Ex() UpdateUserInformation_Ex()
LogError(ex, "downloading data error") LogError(ex, "downloading data error")
If _EnvirInvokeUserUpdated Then OnUserUpdated()
HasError = True HasError = True
Finally Finally
If Not UserExists Then AddNonExistingUserToLog($"User '{ToStringForLog()}' not found on the site") If Not UserExists Then AddNonExistingUserToLog($"User '{ToStringForLog()}' not found on the site")
@@ -1839,6 +1877,31 @@ BlockNullPicture:
Protected Overridable Function CreateFileFromUrl(ByVal URL As String) As SFile Protected Overridable Function CreateFileFromUrl(ByVal URL As String) As SFile
Return New SFile(URL) Return New SFile(URL)
End Function End Function
Protected Overridable Function SimpleDownloadAvatar(ByVal ImageAddress As String, Optional ByVal FileCreateFunc As Func(Of String, SFile) = Nothing,
Optional ByVal e As ErrorsDescriber = Nothing) As SFile
Try
If Not ImageAddress.IsEmptyString Then
Dim f As SFile
If FileCreateFunc Is Nothing Then
f = CreateFileFromUrl(ImageAddress)
Else
f = FileCreateFunc.Invoke(ImageAddress)
End If
If Not f.Name.IsEmptyString Then f.Name = f.Name.StringRemoveWinForbiddenSymbols.StringTrim
If Not f.Name.IsEmptyString Then
f.Path = DownloadContentDefault_GetRootDir()
f.Separator = "\"
If f.Extension.IsEmptyString Then f.Extension = "jpg"
If Not f.Exists Then GetWebFile(ImageAddress, f, EDP.ReturnValue)
If f.Exists Then IconBannerDownloaded = True : Return f
End If
End If
Return Nothing
Catch ex As Exception
If Not e.Exists Then e = New ErrorsDescriber(EDP.ReturnValue)
Return ErrorsDescriber.Execute(e, ex, $"SimpleDownloadAvatar({ImageAddress})", New SFile)
End Try
End Function
Protected Overridable Function ChangeFileNameByProvider(ByVal f As SFile, ByVal m As UserMedia) As SFile Protected Overridable Function ChangeFileNameByProvider(ByVal f As SFile, ByVal m As UserMedia) As SFile
Dim ff As SFile = Nothing Dim ff As SFile = Nothing
Try Try
@@ -2191,6 +2254,12 @@ BlockNullPicture:
Private Sub BTT_CONTEXT_DOWN_KeyClick(sender As Object, e As MyKeyEventArgs) Handles BTT_CONTEXT_DOWN.KeyClick Private Sub BTT_CONTEXT_DOWN_KeyClick(sender As Object, e As MyKeyEventArgs) Handles BTT_CONTEXT_DOWN.KeyClick
Downloader.Add(Me, e.IncludeInTheFeed) Downloader.Add(Me, e.IncludeInTheFeed)
End Sub End Sub
Private Sub BTT_CONTEXT_DOWN_LIMIT_KeyClick(sender As Object, e As MyKeyEventArgs) Handles BTT_CONTEXT_DOWN_LIMIT.KeyClick
ControlInvokeFast(MainFrameObj.MF, Sub() MainFrameObj.MF.DownloadSelectedUser(MainFrame.DownUserLimits.Number, e.IncludeInTheFeed, Me), EDP.SendToLog)
End Sub
Private Sub BTT_CONTEXT_DOWN_DATE_KeyClick(sender As Object, e As MyKeyEventArgs) Handles BTT_CONTEXT_DOWN_DATE.KeyClick
ControlInvokeFast(MainFrameObj.MF, Sub() MainFrameObj.MF.DownloadSelectedUser(MainFrame.DownUserLimits.Date, e.IncludeInTheFeed, Me), EDP.SendToLog)
End Sub
Private Sub BTT_CONTEXT_EDIT_Click(sender As Object, e As EventArgs) Handles BTT_CONTEXT_EDIT.Click Private Sub BTT_CONTEXT_EDIT_Click(sender As Object, e As EventArgs) Handles BTT_CONTEXT_EDIT.Click
Using f As New Editors.UserCreatorForm(Me) Using f As New Editors.UserCreatorForm(Me)
f.ShowDialog() f.ShowDialog()
@@ -2271,6 +2340,8 @@ BlockNullPicture:
If Not ProgressPre Is Nothing Then ProgressPre.Reset() : ProgressPre.Dispose() If Not ProgressPre Is Nothing Then ProgressPre.Reset() : ProgressPre.Dispose()
If Not Responser Is Nothing Then Responser.Dispose() If Not Responser Is Nothing Then Responser.Dispose()
If Not BTT_CONTEXT_DOWN Is Nothing Then BTT_CONTEXT_DOWN.Dispose() If Not BTT_CONTEXT_DOWN Is Nothing Then BTT_CONTEXT_DOWN.Dispose()
If Not BTT_CONTEXT_DOWN_LIMIT Is Nothing Then BTT_CONTEXT_DOWN_LIMIT.Dispose()
If Not BTT_CONTEXT_DOWN_DATE Is Nothing Then BTT_CONTEXT_DOWN_DATE.Dispose()
If Not BTT_CONTEXT_EDIT Is Nothing Then BTT_CONTEXT_EDIT.Dispose() If Not BTT_CONTEXT_EDIT Is Nothing Then BTT_CONTEXT_EDIT.Dispose()
If Not BTT_CONTEXT_DELETE Is Nothing Then BTT_CONTEXT_DELETE.Dispose() If Not BTT_CONTEXT_DELETE Is Nothing Then BTT_CONTEXT_DELETE.Dispose()
If Not BTT_CONTEXT_ERASE Is Nothing Then BTT_CONTEXT_ERASE.Dispose() If Not BTT_CONTEXT_ERASE Is Nothing Then BTT_CONTEXT_ERASE.Dispose()

View File

@@ -134,6 +134,7 @@ Namespace API.Base
m.GetMemberCustomAttributes(Of Provider).ListExists m.GetMemberCustomAttributes(Of Provider).ListExists
Dim m1 As MemberInfo, m2 As MemberInfo Dim m1 As MemberInfo, m2 As MemberInfo
Dim tmpObj As Object Dim tmpObj As Object
Dim maxOffset%
members = GetObjectMembers(MyObject, Function(m) (m.MemberType = MemberTypes.Field Or m.MemberType = MemberTypes.Property) AndAlso members = GetObjectMembers(MyObject, Function(m) (m.MemberType = MemberTypes.Field Or m.MemberType = MemberTypes.Property) AndAlso
Not m.GetCustomAttribute(Of PSettingAttribute) Is Nothing,, True, Not m.GetCustomAttribute(Of PSettingAttribute) Is Nothing,, True,
@@ -175,6 +176,9 @@ Namespace API.Base
If MyMembers.Count > 0 Then If MyMembers.Count > 0 Then
maxOffset = MyMembers.Max(Function(mm) mm.LeftOffset)
If maxOffset > 0 Then MyMembers.ForEach(Sub(mm) mm.LeftOffset = maxOffset)
Dim prov As IEnumerable(Of Provider) Dim prov As IEnumerable(Of Provider)
Dim _prov As Provider Dim _prov As Provider
Dim si% = -1 Dim si% = -1

View File

@@ -0,0 +1,18 @@
' Copyright (C) Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify
' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or
' (at your option) any later version.
'
' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY
Imports PersonalUtilities.Functions.RegularExpressions
Namespace API.Bluesky
Friend Module Declarations
Friend Const BlueskySiteKey As String = "AndyProgram_Bluesky"
Friend ReadOnly DateProvider As New ADateTime("yyyy-MM-ddTHH:mm:ss.FFF%K")
Friend ReadOnly RegEx_PlayLists As RParams = RParams.DM("RESOLUTION=\d+x(\d+)\s*(\S+)", 0, RegexReturn.List, EDP.ReturnValue)
Friend ReadOnly RegEx_FilePattern As RParams = RParams.DM("(.+?)(\.|@)(gif|m3u8|[^/\?\&]+)", 0, RegexReturn.ListByMatch, EDP.ReturnValue)
Friend ReadOnly RegEx_SinglePostPattern As RParams = RParams.DM("profile/([^/]+)/post/([^/\?\&]+)", 0, RegexReturn.ListByMatch, EDP.ReturnValue)
End Module
End Namespace

View File

@@ -0,0 +1,51 @@
' Copyright (C) Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify
' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or
' (at your option) any later version.
'
' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY
Imports System.Threading
Imports SCrawler.API.Base
Imports PersonalUtilities.Forms.Toolbars
Imports PersonalUtilities.Tools.Web.Clients
Imports PersonalUtilities.Functions.RegularExpressions
Namespace API.Bluesky
Friend NotInheritable Class M3U8
Private Sub New()
End Sub
Private Shared Function GetUrlsList(ByVal URL As String) As List(Of String)
Using resp As New Responser With {.AllowAutoRedirect = False}
Dim r$ = resp.GetResponse(URL)
If Not r.IsEmptyString Then
Dim file$ = String.Empty, appender$
Dim files As List(Of Sizes) = RegexFields(Of Sizes)(r, {RegEx_PlayLists}, {1, 2})
If files.ListExists Then files.RemoveAll(Function(ff) ff.Value = 0 Or ff.Data.IsEmptyString)
If files.ListExists Then
files.Sort()
file = files(0).Data
appender = URL.Replace(URL.Split("/").Last, String.Empty)
file = M3U8Base.CreateUrl(appender, file)
If Not file.IsEmptyString Then
r = resp.GetResponse(file)
If Not r.IsEmptyString Then
Dim l As List(Of String) = RegexReplace(r, M3U8Declarations.TsFilesRegEx)
If l.ListExists Then
appender = file.Replace(file.Split("/").Last, String.Empty)
For i% = 0 To l.Count - 1 : l(i) = M3U8Base.CreateUrl(appender, l(i)) : Next
Return l
End If
End If
End If
End If
End If
End Using
Return Nothing
End Function
Friend Shared Function Download(ByVal URL As String, ByVal Destination As SFile, ByVal Token As CancellationToken,
ByVal Progress As MyProgress, ByVal UsePreProgress As Boolean) As SFile
Return M3U8Base.Download(GetUrlsList(URL), Destination,, Token, Progress, UsePreProgress)
End Function
End Class
End Namespace

View File

@@ -0,0 +1,100 @@
' Copyright (C) Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify
' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or
' (at your option) any later version.
'
' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY
Imports SCrawler.API.Base
Imports SCrawler.Plugin
Imports SCrawler.Plugin.Attributes
Imports PersonalUtilities.Functions.XML
Imports PersonalUtilities.Functions.RegularExpressions
Imports PersonalUtilities.Tools.Web.Clients
Imports PersonalUtilities.Tools.Web.Documents.JSON
Namespace API.Bluesky
<Manifest(BlueskySiteKey), SpecialForm(False)>
Friend Class SiteSettings : Inherits SiteSettingsBase
<PropertyOption(ControlText:="Cookies enabled", ControlToolTip:="If checked, cookies will be used in requests", IsAuth:=True), PXML, PClonable, HiddenControl>
Friend ReadOnly Property CookiesEnabled As PropertyValue
<PropertyOption(ControlText:="User name", IsAuth:=True, AllowNull:=False), PXML>
Friend ReadOnly Property UserHandle As PropertyValue
<PropertyOption(ControlText:="Password", IsAuth:=True, AllowNull:=False), PXML>
Friend ReadOnly Property UserPassword As PropertyValue
<PXML> Friend ReadOnly Property Token As PropertyValue
<PXML> Friend ReadOnly Property TokenUpdateTime As PropertyValue
<PropertyOption(ControlText:="Token update", ControlToolTip:="Token refresh interval (in minutes)." & vbCr & "Default: 120.", IsAuth:=True), PXML, PClonable, HiddenControl>
Friend ReadOnly Property TokenRefreshInterval As PropertyValue
Friend Sub New(ByVal AccName As String, ByVal Temp As Boolean)
MyBase.New("Bluesky", "bsky.app", AccName, Temp, My.Resources.SiteResources.BlueskyIcon_32, My.Resources.SiteResources.BlueskyPic_32)
Responser.ContentType = "application/json"
CookiesEnabled = New PropertyValue(False)
UserHandle = New PropertyValue(String.Empty, GetType(String))
UserPassword = New PropertyValue(String.Empty, GetType(String))
Token = New PropertyValue(String.Empty, GetType(String))
TokenUpdateTime = New PropertyValue(Now.AddYears(-1))
TokenRefreshInterval = New PropertyValue(120)
_AllowUserAgentUpdate = False
UrlPatternUser = "https://bsky.app/profile/{0}"
ImageVideoContains = "bsky.app"
UserRegex = RParams.DMS("bsky.app/profile/([^/\?]+)", 1, EDP.ReturnValue)
UserOptionsType = GetType(EditorExchangeOptionsBase)
End Sub
Protected Overrides Function UserOptionsValid(ByVal Options As Object) As Boolean
Return DirectCast(Options, EditorExchangeOptionsBase).SiteKey = BlueskySiteKey
End Function
Protected Overrides Sub UserOptionsSetParameters(ByRef Options As Object)
DirectCast(Options, EditorExchangeOptionsBase).SiteKey = BlueskySiteKey
End Sub
Friend Overrides Function GetInstance(ByVal What As ISiteSettings.Download) As IPluginContentProvider
Return New UserData
End Function
Friend Overrides Function BaseAuthExists() As Boolean
Return Not CStr(UserHandle.Value).IsEmptyString And Not CStr(UserPassword.Value).IsEmptyString
End Function
Friend Overrides Function Available(ByVal What As ISiteSettings.Download, ByVal Silent As Boolean) As Boolean
Return MyBase.Available(What, Silent) AndAlso UpdateToken()
End Function
Private _TokenUpdating As Boolean = False
Friend Function UpdateToken(Optional ByVal Force As Boolean = False) As Boolean
Try
While _TokenUpdating : Threading.Thread.Sleep(100) : End While
_TokenUpdating = True
If BaseAuthExists() Then
If CDate(TokenUpdateTime.Value).AddMinutes(TokenRefreshInterval.Value) < Now Or Force Then
Using resp As Responser = Responser.Copy
With resp
.Mode = Responser.Modes.Curl
.Method = "POST"
.CurlSslNoRevoke = True
.CurlInsecure = True
.CurlArgumentsLeft = "-d ""{\" & $"""identifier\"": \""{UserHandle.Value}\"", \""password\"": \""{UserPassword.Value}\""" & "}"""
Dim r$ = .GetResponse("https://bsky.social/xrpc/com.atproto.server.createSession")
If Not r.IsEmptyString Then
Using j As EContainer = JsonDocument.Parse(r, EDP.ReturnValue)
If j.ListExists Then
Dim t$ = j.Value("accessJwt")
If Not t.IsEmptyString Then Token.Value = $"Bearer {t}" : TokenUpdateTime.Value = Now : Return True
End If
End Using
End If
End With
End Using
Else
Return True
End If
End If
Return False
Catch ex As Exception
Return ErrorsDescriber.Execute(EDP.SendToLog + EDP.ReturnValue, ex, "Bluesky.SiteSettings.UpdateToken", False)
Finally
_TokenUpdating = False
End Try
End Function
End Class
End Namespace

View File

@@ -0,0 +1,330 @@
' Copyright (C) Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify
' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or
' (at your option) any later version.
'
' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY
Imports System.Threading
Imports SCrawler.API.Base
Imports SCrawler.API.YouTube.Objects
Imports PersonalUtilities.Functions.XML
Imports PersonalUtilities.Functions.RegularExpressions
Imports PersonalUtilities.Tools.Web.Documents.JSON
Imports UTypes = SCrawler.API.Base.UserMedia.Types
Imports UStates = SCrawler.API.Base.UserMedia.States
Namespace API.Bluesky
Friend Class UserData : Inherits UserDataBase
#Region "Declarations"
Private ReadOnly Property MySettings As SiteSettings
Get
Return HOST.Source
End Get
End Property
Private ReadOnly Property ID_Encoded As String
Get
Return If(ID.IsEmptyString, String.Empty, SymbolsConverter.ASCII.EncodeSymbolsOnly(ID))
End Get
End Property
#End Region
#Region "Loader"
Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean)
End Sub
Friend Overrides Function ExchangeOptionsGet() As Object
Return New EditorExchangeOptionsBase(Me) With {.SiteKey = BlueskySiteKey}
End Function
Friend Overrides Sub ExchangeOptionsSet(ByVal Obj As Object)
If Not Obj Is Nothing AndAlso TypeOf Obj Is EditorExchangeOptionsBase AndAlso
DirectCast(Obj, EditorExchangeOptionsBase).SiteKey = BlueskySiteKey Then NameTrue = DirectCast(Obj, EditorExchangeOptionsBase).UserName
End Sub
#End Region
#Region "Initializer"
Friend Sub New()
UseInternalM3U8Function = True
End Sub
#End Region
#Region "Token"
Private Function UpdateToken(Optional ByVal Force As Boolean = False, Optional ByVal OnlyAddHeader As Boolean = False) As Boolean
Dim process As Boolean = True
If CDate(MySettings.TokenUpdateTime.Value).AddHours(2) <= Now Or Force Then
process = MySettings.UpdateToken(Force)
If process Then _TokenUpdateCount += 1
End If
If process Or OnlyAddHeader Then Responser.Headers.Add("authorization", MySettings.Token.Value)
Return Not Responser.Headers.Value("authorization").IsEmptyString
End Function
Private _TokenUpdateCount As Integer = 0
Private Sub TokenUpdateCountReset()
_TokenUpdateCount = 0
End Sub
#End Region
#Region "Download"
Private _PostCount As Integer = 0
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
If Not CBool(MySettings.CookiesEnabled.Value) Then Responser.Cookies.Clear()
UpdateToken(, True)
_TokenUpdateCount = 0
_PostCount = 0
DownloadData(String.Empty, Token)
End Sub
Private Overloads Sub DownloadData(ByVal Cursor As String, ByVal Token As CancellationToken)
Dim URL$ = String.Empty
Try
If ID.IsEmptyString Then GetProfileInfo(Token)
If ID.IsEmptyString Then Throw New ArgumentNullException("ID", "ID is null")
If UpdateToken() Then
Dim nextCursor$ = String.Empty
Dim c%
URL = $"https://bsky.social/xrpc/app.bsky.feed.getAuthorFeed?actor={ID_Encoded}&filter=posts_and_author_threads&includePins=false&limit=99"
If Not Cursor.IsEmptyString Then URL &= $"&cursor={SymbolsConverter.ASCII.EncodeSymbolsOnly(Cursor)}"
Dim r$ = Responser.GetResponse(URL)
TokenUpdateCountReset()
If Not r.IsEmptyString Then
Using j As EContainer = JsonDocument.Parse(r)
If j.ListExists Then
With j("feed")
If .ListExists Then
For Each post As EContainer In .Self
With post({"post"})
c = DefaultParser(.Self,, nextCursor)
Select Case c
Case CInt(DateResult.Skip) * -1 : Continue For
Case CInt(DateResult.Exit) * -1 : Exit Sub
Case Is > 0 : _PostCount += c
End Select
If DownloadTopCount.HasValue AndAlso DownloadTopCount.Value <= _PostCount Then Exit Sub
End With
Next
End If
End With
End If
End Using
If Not nextCursor.IsEmptyString Then DownloadData(nextCursor, Token)
End If
End If
Catch ex As Exception
ProcessException(ex, Token, $"DownloadData({URL})")
End Try
End Sub
#End Region
#Region "DefaultParser"
Private Const Down_ImageAddress As String = "https://cdn.bsky.app/img/feed_fullsize/plain/{0}/{1}"
Private Function GetPostID(ByVal PostUri As String) As String
Return If(PostUri.IsEmptyString, String.Empty, PostUri.Split("/").LastOrDefault)
End Function
Private Function DefaultParser(ByVal e As EContainer, Optional ByVal CheckDateLimits As Boolean = True, Optional ByRef NextCursor As String = Nothing,
Optional ByVal CheckTempPosts As Boolean = True, Optional ByVal State As UStates = UStates.Unknown) As Integer
Const exitReturn% = CInt(DateResult.Exit) * -1
Dim postID$, postDate$, __url$, __urlBase$
Dim updateUrl As Boolean
Dim c% = 0
Dim m As UserMedia
Dim d As EContainer
With e
If .ListExists Then
postID = GetPostID(.Value("uri"))
postDate = String.Empty
__urlBase = String.Empty
With .Item({"record"})
If .ListExists Then
'2025-01-28T02:42:12.415Z
postDate = .Value("createdAt")
NextCursor = postDate
If CheckDateLimits Then
Select Case CheckDatesLimit(postDate, DateProvider)
Case DateResult.Skip : Return CInt(DateResult.Skip) * -1 'Continue For
Case DateResult.Exit : Return exitReturn 'Exit Sub
End Select
End If
If CheckTempPosts Then
If _TempPostsList.Contains(postID) Then Return exitReturn Else _TempPostsList.Add(postID)
End If
__urlBase = $"https://bsky.app/profile/{NameTrue}/post/{postID}"
End If
End With
Dim createMedia As Func(Of String, UTypes, UserMedia) =
Function(ByVal url As String, ByVal type As UTypes) As UserMedia
m = New UserMedia(url, type) With {
.URL_BASE = __urlBase,
.File = CreateFileFromUrl(url, type),
.Post = New UserPost(postID, If(AConvert(Of Date)(postDate, DateProvider, Nothing, EDP.ReturnValue), Nothing)),
.State = State
}
_TempMediaList.ListAddValue(m, LNC)
c += 1
Return m
End Function
For Each SecondExtraction As Boolean In {False, True}
With If(SecondExtraction, .Item({"record", "embed"}), .Item("embed"))
If .ListExists Then
If If(.Item("images")?.Count, 0) > 0 Then
With .Item("images")
For Each d In .Self
updateUrl = False
__url = d.Value("fullsize")
If __url.IsEmptyString Then __url = d.Value({"image", "ref"}, "$link") : updateUrl = True
If __url.IsEmptyString And SecondExtraction Then updateUrl = False : __url = e.Value({"embed"}, "thumb")
If Not __url.IsEmptyString Then createMedia(__url, UTypes.Picture)
Next
End With
End If
If Not .Value("playlist").IsEmptyString Then createMedia(.Value("playlist"), UTypes.m3u8)
If If(.Item("external")?.Count, 0) > 0 Then createMedia(.Value({"external"}, "uri"), UTypes.GIF)
End If
End With
If c > 0 Then Exit For
Next
End If
End With
Return c
End Function
#End Region
#Region "GetProfileInfo"
Private Sub GetProfileInfo(ByVal Token As CancellationToken)
Try
If UpdateToken() Then
Dim r$ = Responser.GetResponse($"https://bsky.social/xrpc/app.bsky.actor.getProfile?actor={ID.IfNullOrEmpty(NameTrue)}")
TokenUpdateCountReset()
If Not r.IsEmptyString Then
Using j As EContainer = JsonDocument.Parse(r)
If j.ListExists Then
ID = j.Value("did")
UserSiteNameUpdate(j.Value("displayName"))
UserDescriptionUpdate(j.Value("description"))
NameTrue = j.Value("handle")
SimpleDownloadAvatar(j.Value("avatar"))
SimpleDownloadAvatar(j.Value("banner"))
End If
End Using
End If
Else
Throw New ArgumentException("Token is null", "Token")
End If
Catch ex As Exception
ProcessException(ex, Token, "GetProfileInfo")
End Try
End Sub
#End Region
#Region "ReparseMissing"
Protected Overrides Sub ReparseMissing(ByVal Token As CancellationToken)
Const uriPattern$ = "at://{0}/app.bsky.feed.post/{1}"
Dim rList As New List(Of Integer)
Try
If ContentMissingExists AndAlso UpdateToken() Then
Dim r$, url$, uri$
Dim tu As Byte
Dim m As UserMedia
Dim j As EContainer
For i% = 0 To _ContentList.Count - 1
m = _ContentList(i)
If m.State = UStates.Missing Then
uri = SymbolsConverter.ASCII.EncodeSymbolsOnly(String.Format(uriPattern, NameTrue, m.Post.ID))
url = $"https://bsky.social/xrpc/app.bsky.feed.getPostThread?uri={uri}&depth=10"
For tu = 0 To 1
Try
Responser.ResetStatus()
r = Responser.GetResponse(url)
TokenUpdateCountReset()
If Not r.IsEmptyString Then
j = JsonDocument.Parse(r)
If j.ListExists Then
If DefaultParser(j({"thread", "post"}), False,, False, UStates.Missing) > 0 Then rList.Add(i)
j.Dispose()
End If
End If
Exit For
Catch eex As Exception
If ProcessException(eex, Token, $"ReparseMissing({url})",,, False) <> 1 Then Throw eex
End Try
Next
End If
Next
Else
Throw New ArgumentException("Token is null", "Token")
End If
Catch ex As Exception
ProcessException(ex, Token, "ReparseMissing error")
Finally
If rList.Count > 0 Then
For i% = rList.Count - 1 To 0 Step -1 : _ContentList.RemoveAt(rList(i)) : Next
rList.Clear()
End If
End Try
End Sub
#End Region
#Region "CreateFileFromUrl"
Protected Overloads Overrides Function CreateFileFromUrl(ByVal URL As String) As SFile
Return CreateFileFromUrl(URL, UTypes.Undefined)
End Function
Protected Overloads Function CreateFileFromUrl(ByVal URL As String, ByVal Type As UTypes) As SFile
Dim f As SFile = MyBase.CreateFileFromUrl(URL)
Dim force As Boolean = False
f.Separator = "\"
With URL.Split("/")
If .ListExists Then
With DirectCast(RegexReplace(.Last, RegEx_FilePattern), List(Of String))
If .ListExists(4) Then
f.Name = .Item(1).IfNullOrEmpty(f.Name)
f.Extension = .Item(3)
End If
End With
End If
End With
If Not f.Extension.IsEmptyString AndAlso f.Extension.ToLower = "m3u8" Then force = True : Type = UTypes.m3u8
If f.Extension.IsEmptyString Or force Then
Select Case Type
Case UTypes.Picture : f.Extension = "jpg"
Case UTypes.GIF : f.Extension = "gif"
Case UTypes.m3u8 : f.Name = "Video" : f.Extension = "mp4"
End Select
End If
Return f
End Function
#End Region
#Region "DownloadContent"
Protected Overrides Sub DownloadContent(ByVal Token As CancellationToken)
DownloadContentDefault(Token)
End Sub
Protected Overrides Function DownloadM3U8(ByVal URL As String, ByVal Media As UserMedia, ByVal DestinationFile As SFile, ByVal Token As CancellationToken) As SFile
Return M3U8.Download(URL, DestinationFile, Token, Progress, Not IsSingleObjectDownload)
End Function
#End Region
#Region "DownloadSingleObject"
Protected Overrides Sub DownloadSingleObject_GetPosts(ByVal Data As IYouTubeMediaContainer, ByVal Token As CancellationToken)
_TokenUpdateCount = 0
UpdateToken()
Dim l As List(Of String) = RegexReplace(Data.URL, RegEx_SinglePostPattern)
If l.ListExists(3) Then
NameTrue = l(1)
_ContentList.Add(New UserMedia(Data.URL) With {.State = UStates.Missing, .Post = l(2)})
ReparseMissing(Token)
End If
MyBase.DownloadSingleObject_GetPosts(Data, Token)
End Sub
#End Region
#Region "Exception"
Protected Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False,
Optional ByVal EObj As Object = Nothing) As Integer
If Responser.StatusCode = Net.HttpStatusCode.BadRequest Then '400
If _TokenUpdateCount = 0 AndAlso UpdateToken(True) Then
Return 1
Else
Return 0
End If
Else
Return 0
End If
End Function
#End Region
End Class
End Namespace

View File

@@ -18,6 +18,8 @@ Namespace API.Facebook
Friend ReadOnly Regex_FileName As RParams = RParams.DM("([^/\?]+\..{3,4})(?=(\?|\Z))", 0, EDP.ReturnValue) Friend ReadOnly Regex_FileName As RParams = RParams.DM("([^/\?]+\..{3,4})(?=(\?|\Z))", 0, EDP.ReturnValue)
Friend ReadOnly Regex_ProfileUrlID As RParams = RParams.DMS("profile.php\?id=(\d+)", 1, EDP.ReturnValue) Friend ReadOnly Regex_ProfileUrlID As RParams = RParams.DMS("profile.php\?id=(\d+)", 1, EDP.ReturnValue)
Friend ReadOnly Regex_VideoPageID As RParams = RParams.DMS("pageid.:.(\d+)", 1, RegexOptions.IgnoreCase, EDP.ReturnValue) Friend ReadOnly Regex_VideoPageID As RParams = RParams.DMS("pageid.:.(\d+)", 1, RegexOptions.IgnoreCase, EDP.ReturnValue)
Friend ReadOnly Regex_ReelsPageID As RParams = RParams.DMS("\{[^\}]*""tab_key"":""owner_reels"",?[^\}]*""id"":""([^\}""]+)""", 1, RegexOptions.IgnoreCase, EDP.ReturnValue)
Friend ReadOnly Regex_ReelsFilePattern As RParams = RParams.DM("[^/]+\.mp4", 0, EDP.ReturnValue)
Friend ReadOnly Regex_StoryBucket As RParams = RParams.DMS("story_bucket[^\>]*?(\d+)", 1, EDP.ReturnValue) Friend ReadOnly Regex_StoryBucket As RParams = RParams.DMS("story_bucket[^\>]*?(\d+)", 1, EDP.ReturnValue)
Friend ReadOnly Regex_VideoIDFromURL As RParams = RParams.DMS("facebook.com/([^/]+/videos/|watch/\D*[\?&]{1}v=)(\d+)", 2, EDP.ReturnValue) Friend ReadOnly Regex_VideoIDFromURL As RParams = RParams.DMS("facebook.com/([^/]+/videos/|watch/\D*[\?&]{1}v=)(\d+)", 2, EDP.ReturnValue)

View File

@@ -36,6 +36,8 @@ Namespace API.Facebook
Friend ReadOnly Property ParsePhotoBlock As PropertyValue Friend ReadOnly Property ParsePhotoBlock As PropertyValue
<PropertyOption(ControlText:="Download videos", IsAuth:=False, Category:=DN.CAT_UserDefs), PXML, PClonable> <PropertyOption(ControlText:="Download videos", IsAuth:=False, Category:=DN.CAT_UserDefs), PXML, PClonable>
Friend ReadOnly Property ParseVideoBlock As PropertyValue Friend ReadOnly Property ParseVideoBlock As PropertyValue
<PropertyOption(ControlText:="Download reels", IsAuth:=False, Category:=DN.CAT_UserDefs), PXML, PClonable>
Friend ReadOnly Property ParseReelsBlock As PropertyValue
<PropertyOption(ControlText:="Download stories", IsAuth:=False, Category:=DN.CAT_UserDefs), PXML, PClonable> <PropertyOption(ControlText:="Download stories", IsAuth:=False, Category:=DN.CAT_UserDefs), PXML, PClonable>
Friend ReadOnly Property ParseStoriesBlock As PropertyValue Friend ReadOnly Property ParseStoriesBlock As PropertyValue
#End Region #End Region
@@ -52,6 +54,7 @@ Namespace API.Facebook
Header_Accept = New PropertyValue(String.Empty, GetType(String)) Header_Accept = New PropertyValue(String.Empty, GetType(String))
ParsePhotoBlock = New PropertyValue(True) ParsePhotoBlock = New PropertyValue(True)
ParseVideoBlock = New PropertyValue(True) ParseVideoBlock = New PropertyValue(True)
ParseReelsBlock = New PropertyValue(False)
ParseStoriesBlock = New PropertyValue(True) ParseStoriesBlock = New PropertyValue(True)
UrlPatternUser = "https://www.facebook.com/{0}" UrlPatternUser = "https://www.facebook.com/{0}"

View File

@@ -23,9 +23,11 @@ Namespace API.Facebook
Private Const Name_IsNoNameProfile As String = "IsNoNameProfile" Private Const Name_IsNoNameProfile As String = "IsNoNameProfile"
Private Const Name_OptionsParsed As String = "OptionsParsed" Private Const Name_OptionsParsed As String = "OptionsParsed"
Private Const Name_VideoPageID As String = "VideoPageID" Private Const Name_VideoPageID As String = "VideoPageID"
Private Const Name_ReelsPageID As String = "ReelsPageID"
Private Const Name_StoryBucket As String = "StoryBucket" Private Const Name_StoryBucket As String = "StoryBucket"
Private Const Name_ParsePhotoBlock As String = "ParsePhotoBlock" Private Const Name_ParsePhotoBlock As String = "ParsePhotoBlock"
Private Const Name_ParseVideoBlock As String = "ParseVideoBlock" Private Const Name_ParseVideoBlock As String = "ParseVideoBlock"
Private Const Name_ParseReelsBlock As String = "ParseReelsBlock"
Private Const Name_ParseStoriesBlock As String = "ParseStoriesBlock" Private Const Name_ParseStoriesBlock As String = "ParseStoriesBlock"
#End Region #End Region
#Region "Declarations" #Region "Declarations"
@@ -37,15 +39,18 @@ Namespace API.Facebook
Private IsNoNameProfile As Boolean = False Private IsNoNameProfile As Boolean = False
Private OptionsParsed As Boolean = False Private OptionsParsed As Boolean = False
Private Property VideoPageID As String = String.Empty Private Property VideoPageID As String = String.Empty
Private Property ReelsPageID As String = String.Empty
Private Property StoryBucket As String = String.Empty Private Property StoryBucket As String = String.Empty
Friend Property ParsePhotoBlock As Boolean = True Friend Property ParsePhotoBlock As Boolean = True
Friend Property ParseVideoBlock As Boolean = True Friend Property ParseVideoBlock As Boolean = True
Friend Property ParseReelsBlock As Boolean = False
Friend Property ParseStoriesBlock As Boolean = True Friend Property ParseStoriesBlock As Boolean = True
Private Enum PageBlock As Integer Private Enum PageBlock As Integer
Timeline = Sections.Timeline Timeline = Sections.Timeline
Stories = Sections.Stories Stories = Sections.Stories
Photos = 100 Photos = 100
Videos = 101 Videos = 101
Reels = Sections.Reels
Undefined = -1 Undefined = -1
End Enum End Enum
#End Region #End Region
@@ -67,6 +72,7 @@ Namespace API.Facebook
With DirectCast(Obj, UserExchangeOptions) With DirectCast(Obj, UserExchangeOptions)
ParsePhotoBlock = .ParsePhotoBlock ParsePhotoBlock = .ParsePhotoBlock
ParseVideoBlock = .ParseVideoBlock ParseVideoBlock = .ParseVideoBlock
ParseReelsBlock = .ParseReelsBlock
ParseStoriesBlock = .ParseStoriesBlock ParseStoriesBlock = .ParseStoriesBlock
End With End With
End If End If
@@ -90,18 +96,22 @@ Namespace API.Facebook
End If End If
OptionsParsed = .Value(Name_OptionsParsed).FromXML(Of Boolean)(False) OptionsParsed = .Value(Name_OptionsParsed).FromXML(Of Boolean)(False)
VideoPageID = .Value(Name_VideoPageID) VideoPageID = .Value(Name_VideoPageID)
ReelsPageID = .Value(Name_ReelsPageID)
StoryBucket = .Value(Name_StoryBucket) StoryBucket = .Value(Name_StoryBucket)
ParsePhotoBlock = .Value(Name_ParsePhotoBlock).FromXML(Of Boolean)(True) ParsePhotoBlock = .Value(Name_ParsePhotoBlock).FromXML(Of Boolean)(True)
ParseVideoBlock = .Value(Name_ParseVideoBlock).FromXML(Of Boolean)(True) ParseVideoBlock = .Value(Name_ParseVideoBlock).FromXML(Of Boolean)(True)
ParseReelsBlock = .Value(Name_ParseReelsBlock).FromXML(Of Boolean)(False)
ParseStoriesBlock = .Value(Name_ParseStoriesBlock).FromXML(Of Boolean)(True) ParseStoriesBlock = .Value(Name_ParseStoriesBlock).FromXML(Of Boolean)(True)
Else Else
updateNames.Invoke updateNames.Invoke
.Add(Name_IsNoNameProfile, IsNoNameProfile.BoolToInteger) .Add(Name_IsNoNameProfile, IsNoNameProfile.BoolToInteger)
.Add(Name_OptionsParsed, OptionsParsed.BoolToInteger) .Add(Name_OptionsParsed, OptionsParsed.BoolToInteger)
.Add(Name_VideoPageID, VideoPageID) .Add(Name_VideoPageID, VideoPageID)
.Add(Name_ReelsPageID, ReelsPageID)
.Add(Name_StoryBucket, StoryBucket) .Add(Name_StoryBucket, StoryBucket)
.Add(Name_ParsePhotoBlock, ParsePhotoBlock.BoolToInteger) .Add(Name_ParsePhotoBlock, ParsePhotoBlock.BoolToInteger)
.Add(Name_ParseVideoBlock, ParseVideoBlock.BoolToInteger) .Add(Name_ParseVideoBlock, ParseVideoBlock.BoolToInteger)
.Add(Name_ParseReelsBlock, ParseReelsBlock.BoolToInteger)
.Add(Name_ParseStoriesBlock, ParseStoriesBlock.BoolToInteger) .Add(Name_ParseStoriesBlock, ParseStoriesBlock.BoolToInteger)
End If End If
End With End With
@@ -146,6 +156,7 @@ Namespace API.Facebook
Else Else
If DownloadImages And ParsePhotoBlock Then DownloadData_Photo(String.Empty, Token) If DownloadImages And ParsePhotoBlock Then DownloadData_Photo(String.Empty, Token)
If DownloadVideos And ParseVideoBlock Then DownloadData_Video(String.Empty, Token) If DownloadVideos And ParseVideoBlock Then DownloadData_Video(String.Empty, Token)
If DownloadVideos And ParseReelsBlock Then DownloadData_Reels(String.Empty, Token)
If (DownloadImages Or DownloadVideos) And ParseStoriesBlock Then DownloadData_Stories(Token) If (DownloadImages Or DownloadVideos) And ParseStoriesBlock Then DownloadData_Stories(Token)
End If End If
LoadSavePostsKV(False) LoadSavePostsKV(False)
@@ -158,10 +169,12 @@ Namespace API.Facebook
Private Const Header_fb_fr_name_Video As String = "PagesCometChannelTabAllVideosCardImplPaginationQuery" Private Const Header_fb_fr_name_Video As String = "PagesCometChannelTabAllVideosCardImplPaginationQuery"
Private Const Header_fb_fr_name_Stories As String = "StoriesSuspenseContentPaneRootWithEntryPointQuery" Private Const Header_fb_fr_name_Stories As String = "StoriesSuspenseContentPaneRootWithEntryPointQuery"
Private Const Header_fb_fr_name_SavedPosts As String = "CometSaveDashboardAllItemsPaginationQuery" Private Const Header_fb_fr_name_SavedPosts As String = "CometSaveDashboardAllItemsPaginationQuery"
Private Const Header_fb_fr_name_Reels As String = "ProfileCometAppCollectionReelsRendererPaginationQuery"
Private Const DocID_Photo As String = "6684543058255697" Private Const DocID_Photo As String = "6684543058255697"
Private Const DocID_Video As String = "24545934291687581" Private Const DocID_Video As String = "24545934291687581"
Private Const DocID_Stories As String = "6771064226315961" Private Const DocID_Stories As String = "6771064226315961"
Private Const DocID_SavedPosts As String = "7112228098805003" Private Const DocID_SavedPosts As String = "7112228098805003"
Private Const DocID_Reels As String = "28517740954539304"
Private Const Graphql_UrlPattern As String = "https://www.facebook.com/api/graphql?lsd={0}&doc_id={1}&server_timestamps=true&fb_dtsg={3}&fb_api_req_friendly_name={2}&variables={4}" Private Const Graphql_UrlPattern As String = "https://www.facebook.com/api/graphql?lsd={0}&doc_id={1}&server_timestamps=true&fb_dtsg={3}&fb_api_req_friendly_name={2}&variables={4}"
Private Const VideoHtmlUrlPattern As String = "https://www.facebook.com/watch/?v={0}" Private Const VideoHtmlUrlPattern As String = "https://www.facebook.com/watch/?v={0}"
Private Sub DownloadData_Photo(ByVal Cursor As String, ByVal Token As CancellationToken) Private Sub DownloadData_Photo(ByVal Cursor As String, ByVal Token As CancellationToken)
@@ -238,7 +251,7 @@ Namespace API.Facebook
Dim newPostsDetected As Boolean = False Dim newPostsDetected As Boolean = False
Dim pid As PostKV Dim pid As PostKV
If VideoPageID.IsEmptyString Then GetVideoPageID(Token) If VideoPageID.IsEmptyString Then GetVideoPageID(False, Token)
If VideoPageID.IsEmptyString Then Throw New TokensException("Unable to obtain 'VideoPageID'", False) If VideoPageID.IsEmptyString Then Throw New TokensException("Unable to obtain 'VideoPageID'", False)
ValidateBaseTokens() ValidateBaseTokens()
@@ -355,6 +368,123 @@ Namespace API.Facebook
ProcessException(ex, Token, $"data (stories) downloading error [{URL}]",, Responser) ProcessException(ex, Token, $"data (stories) downloading error [{URL}]",, Responser)
End Try End Try
End Sub End Sub
Private Sub DownloadData_Reels(ByVal Cursor As String, ByVal Token As CancellationToken)
Dim URL$ = String.Empty
Const VarPattern$ = """count"":10,""cursor"":{0},""feedLocation"":""COMET_MEDIA_VIEWER"",""feedbackSource"":65,""focusCommentID"":null,""renderLocation"":null,""scale"":1,""useDefaultActor"":true,""id"":""{1}"",""__relay_internal__pv__FBReelsMediaFooter_comet_enable_reels_ads_gkrelayprovider"":true,""__relay_internal__pv__IsWorkUserrelayprovider"":false"
Try
Dim nextCursor$ = String.Empty
Dim newPostsDetected As Boolean = False
Dim nodeFound As Boolean = False
Dim pid As PostKV = Nothing
Dim __urlBase$ = String.Empty
Dim lines As List(Of String)
Dim j As EContainer, rr As EContainer
Dim jDataRoot As EContainer = Nothing
Dim indx% = -1
Dim s As New List(Of Sizes)
Dim videoIdNode$() = {"profile_reel_node", "node", "video", "id"}
Dim obtainBasePostData As Action = Sub()
If indx.ValueBetween(0, jDataRoot.Count - 1) Then
With jDataRoot(indx)
pid = New PostKV(String.Empty, .Item(videoIdNode).XmlIfNothingValue.
IfNullOrEmpty(.Value({"node"}, "id")), PageBlock.Reels)
pid.Code = $"Reels:{pid.ID}"
nextCursor = .Value("cursor")
If Not .Item(videoIdNode).XmlIfNothing.IsEmptyString Then
__urlBase = $"https://www.facebook.com/reel/{pid.ID}"
Else
__urlBase = String.Empty
End If
End With
Else
pid = Nothing
nextCursor = String.Empty
__urlBase = String.Empty
End If
End Sub
Dim createFile As Func(Of String, SFile, SFile) = Function(ByVal __url As String, ByVal cFile As SFile) As SFile
Dim f As New SFile(RegexReplace(__url, Regex_ReelsFilePattern))
If Not f.IsEmptyString Then Return f Else Return cFile
End Function
If ReelsPageID.IsEmptyString Then GetVideoPageID(True, Token)
If ReelsPageID.IsEmptyString Then Throw New TokensException("Unable to obtain 'ReelsPageID'", False)
ValidateBaseTokens()
URL = String.Format(Graphql_UrlPattern, Token_lsd, DocID_Reels, Header_fb_fr_name_Reels, Token_dtsg_Var,
SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & String.Format(VarPattern, If(Cursor.IsEmptyString, "null", $"""{Cursor}"""), ReelsPageID) & "}"))
ResponserApplyDefs(Header_fb_fr_name_Reels)
ThrowAny(Token)
WaitTimer()
Dim r$ = Responser.GetResponse(URL)
If Not r.IsEmptyString Then
lines = r.StringToList(Of String)(vbCrLf).ListIfNothing
If lines.ListExists Then
For Each line$ In lines
j = JsonDocument.Parse(line, EDP.ReturnValue)
If j.ListExists Then
jDataRoot = j({"data", "node", "aggregated_fb_shorts", "edges"})
If jDataRoot.ListExists Then
With j({"extensions", "all_video_dash_prefetch_representations"})
If .ListExists Then
ProgressPre.ChangeMax(.Count)
For indx = 0 To .Count - 1
ProgressPre.Perform()
obtainBasePostData()
If Not pid.ID.IsEmptyString AndAlso Not PostKvExists(pid) Then
newPostsDetected = True
PostsKVIDs.ListAddValue(pid, LNC)
_TempPostsList.Add(pid.Code)
With .ItemF({indx, "representations"})
If .ListExists Then
s.Clear()
For Each rr In .Self : s.Add(New Sizes(rr.Value("width"), rr.Value("base_url"))) : Next
If s.Count > 0 Then s.RemoveAll(Function(ss) ss.Value = 0 Or ss.Data.IsEmptyString)
If s.Count > 0 Then
s.Sort()
_TempMediaList.ListAddValue(New UserMedia(s(0).Data, UTypes.Video) With {
.URL_BASE = __urlBase.IfNullOrEmpty(.URL_BASE),
.Post = pid.ID,
.File = createFile(s(0).Data, .File),
.SpecialFolder = "Reels*"
}, LNC)
s.Clear()
End If
End If
End With
If Limit > 0 And _TempMediaList.Count >= Limit Then j.Dispose() : Exit Sub
Else
j.Dispose()
Exit Sub
End If
Next
End If
End With
End If
j.Dispose()
End If
Next
End If
End If
If newPostsDetected And Not nextCursor.IsEmptyString Then DownloadData_Reels(nextCursor, Token)
Catch tex As TokensException When Not tex.BasicTokens
TokensException.SendToLog(Me, tex, "data (reels)")
Catch ex As Exception
ProcessException(ex, Token, $"data (reels) downloading error [{URL}]",, Responser)
End Try
End Sub
Private Sub DownloadData_SavedPosts(ByVal Cursor As String, ByVal Token As CancellationToken) Private Sub DownloadData_SavedPosts(ByVal Cursor As String, ByVal Token As CancellationToken)
Dim URL$ = String.Empty Dim URL$ = String.Empty
Const VarPattern$ = """content_filter"":[],""count"":10,""cursor"":{0},""scale"":1,""use_case"":""SAVE_DEFAULT""" Const VarPattern$ = """content_filter"":[],""count"":10,""cursor"":{0},""scale"":1,""use_case"":""SAVE_DEFAULT"""
@@ -507,13 +637,19 @@ Namespace API.Facebook
Return True Return True
End If End If
End Function End Function
Private Sub GetVideoPageID(ByVal Token As CancellationToken) Private Sub GetVideoPageID(ByVal GetReels As Boolean, ByVal Token As CancellationToken)
Dim URL$ = $"{GetProfileUrl()}\videos" Dim URL$ = $"{GetProfileUrl()}\{IIf(GetReels, "reels", "videos")}"
Dim resp As Responser = HtmlResponserCreate() Dim resp As Responser = HtmlResponserCreate()
Try Try
WaitTimer() WaitTimer()
Dim r$ = resp.GetResponse(URL) Dim r$ = resp.GetResponse(URL)
If Not r.IsEmptyString Then VideoPageID = RegexReplace(r, Regex_VideoPageID) If Not r.IsEmptyString Then
If GetReels Then
ReelsPageID = RegexReplace(r, Regex_ReelsPageID)
Else
VideoPageID = RegexReplace(r, Regex_VideoPageID)
End If
End If
Catch ex As Exception Catch ex As Exception
ProcessException(ex, Token, "get video page ID",, resp) ProcessException(ex, Token, "get video page ID",, resp)
Finally Finally
@@ -658,17 +794,39 @@ Namespace API.Facebook
HtmlResponserDispose(resp) HtmlResponserDispose(resp)
End Try End Try
End Sub End Sub
Private Structure VideoResolution : Implements IComparable(Of VideoResolution)
Friend W As Integer
Friend H As Integer
Friend B As Integer
Friend U As String
Friend ReadOnly Property Wrong As Boolean
Get
Return W = 0 Or H = 0 Or B = 0 Or U.IsEmptyString
End Get
End Property
Private Function CompareTo(ByVal Other As VideoResolution) As Integer Implements IComparable(Of VideoResolution).CompareTo
Return CLng(Math.Max(W, H) * B).CompareTo(CLng(Math.Max(Other.W, Other.H) * Other.B)) * -1
End Function
End Structure
Protected Function ReparseSingleVideo(ByVal m As UserMedia, ByVal resp As Responser, ByRef result As Boolean) As UserMedia Protected Function ReparseSingleVideo(ByVal m As UserMedia, ByVal resp As Responser, ByRef result As Boolean) As UserMedia
Const nameSD$ = "browser_native_sd_url" Const nameSD$ = "browser_native_sd_url"
Const nameHD$ = "browser_native_hd_url" Const nameHD$ = "browser_native_hd_url"
Const nameDPR$ = "all_video_dash_prefetch_representations"
Const pattern$ = "<script type=""application/json""[^\>]*data-sjs>([^<]+?{0}[^<]+)<" Const pattern$ = "<script type=""application/json""[^\>]*data-sjs>([^<]+?{0}[^<]+)<"
Dim URL$ = String.Empty Dim URL$ = String.Empty
Dim j As EContainer = Nothing Dim j As EContainer = Nothing
Try Try
Dim r$, script$, __url$ Dim r$ = String.Empty, script$ = String.Empty, __url$ = String.Empty
Dim isNewNodes As Boolean = False
Dim __date As Date? = Nothing
Dim jNode As EContainer Dim jNode As EContainer
Dim jf As Predicate(Of EContainer) = Function(ee) Not ee.Name.IsEmptyString AndAlso (ee.Name.ToLower = nameSD Or ee.Name.ToLower = nameHD) Dim jf As Predicate(Of EContainer) = Function(ee) Not ee.Name.IsEmptyString AndAlso (ee.Name.ToLower = nameSD Or ee.Name.ToLower = nameHD)
Dim re As RParams = RParams.DMS("", 1, RegexOptions.IgnoreCase, EDP.ReturnValue) Dim re As RParams = RParams.DMS("", 1, RegexOptions.IgnoreCase, EDP.ReturnValue)
Dim nf As New XML.Base.NodeParams(nameDPR, True, True, True, True, 20)
Dim __extractScript As Action(Of String) = Sub(ByVal inputName As String)
re.Pattern = String.Format(pattern, inputName)
script = RegexReplace(r, re)
End Sub
If m.Post.ID.IsEmptyString Then If m.Post.ID.IsEmptyString Then
URL = m.URL_BASE URL = m.URL_BASE
Else Else
@@ -677,30 +835,47 @@ Namespace API.Facebook
WaitTimer() WaitTimer()
r = resp.GetResponse(URL) r = resp.GetResponse(URL)
If Not r.IsEmptyString Then If Not r.IsEmptyString Then
re.Pattern = String.Format(pattern, nameHD) __extractScript(nameHD)
script = RegexReplace(r, re) If script.IsEmptyString Then __extractScript(nameSD)
If script.IsEmptyString Then If script.IsEmptyString Then __extractScript(nameDPR) : isNewNodes = True
re.Pattern = String.Format(pattern, nameSD)
script = RegexReplace(r, re)
End If
If Not script.IsEmptyString Then If Not script.IsEmptyString Then
j = JsonDocument.Parse(script) j = JsonDocument.Parse(script)
If j.ListExists Then If j.ListExists Then
j.SetSourceReferences() j.SetSourceReferences()
jNode = j.Find(jf, True) If isNewNodes Then
If Not jNode Is Nothing Then jNode = j.GetNode({nf})
With DirectCast(jNode.Source, EContainer) If Not jNode Is Nothing Then
__url = .Value(nameHD).IfNullOrEmpty(.Value(nameSD)) With jNode.ItemF({0, "representations"})
If Not __url.IsEmptyString Then If .ListExists Then
m.URL = __url Dim intE As New ErrorsDescriber(False, False, False, 0)
m.URL_BASE = URL Dim intC As Func(Of String, Integer) = Function(__input) AConvert(Of Integer)(__input, intE)
m.Type = UTypes.Video Dim dataV As List(Of VideoResolution) = .Select(Function(jj) New VideoResolution With {
m.File = CreateFileFromUrl(__url) .W = intC(jj.Value("width")),
m.Post.Date = AConvert(Of Date)(.Value("publish_time"), UnixDate32Provider, Nothing) .H = intC(jj.Value("height")),
result = True .B = intC(jj.Value("bandwidth")),
Return m .U = jj.Value("base_url")}).ListIfNothing
End If If dataV.ListExists Then dataV.RemoveAll(Function(dd) dd.Wrong)
End With If dataV.ListExists Then dataV.Sort() : __url = dataV(0).U : dataV.Clear() : __date = m.Post.Date
End If
End With
End If
Else
jNode = j.Find(jf, True)
If Not jNode Is Nothing Then
With DirectCast(jNode.Source, EContainer)
__url = .Value(nameHD).IfNullOrEmpty(.Value(nameSD))
If Not __url.IsEmptyString Then __date = AConvert(Of Date)(.Value("publish_time"), UnixDate32Provider, Nothing)
End With
End If
End If
If Not __url.IsEmptyString Then
m.URL = __url
m.URL_BASE = URL
m.Type = UTypes.Video
m.File = CreateFileFromUrl(__url)
m.Post.Date = __date
result = True
Return m
End If End If
End If End If
End If End If
@@ -738,7 +913,10 @@ Namespace API.Facebook
#End Region #End Region
#Region "DownloadSingleObject" #Region "DownloadSingleObject"
Protected Overrides Sub DownloadSingleObject_GetPosts(ByVal Data As IYouTubeMediaContainer, ByVal Token As CancellationToken) Protected Overrides Sub DownloadSingleObject_GetPosts(ByVal Data As IYouTubeMediaContainer, ByVal Token As CancellationToken)
_ContentList.Add(New UserMedia(Data.URL, UTypes.VideoPre) With {.Post = CStr(AConvert(Of String)(Data.URL, Regex_VideoIDFromURL, String.Empty))}) _ContentList.Add(New UserMedia(Data.URL, UTypes.VideoPre) With {
.Post = CStr(AConvert(Of String)(Data.URL, Regex_VideoIDFromURL, String.Empty)),
.State = UStates.Missing
})
ReparseMissing(Token) ReparseMissing(Token)
End Sub End Sub
#End Region #End Region

View File

@@ -13,6 +13,8 @@ Namespace API.Facebook
Friend Property ParsePhotoBlock As Boolean = True Friend Property ParsePhotoBlock As Boolean = True
<PSetting(NameOf(SiteSettings.ParseVideoBlock), NameOf(MySettings))> <PSetting(NameOf(SiteSettings.ParseVideoBlock), NameOf(MySettings))>
Friend Property ParseVideoBlock As Boolean = True Friend Property ParseVideoBlock As Boolean = True
<PSetting(NameOf(SiteSettings.ParseReelsBlock), NameOf(MySettings))>
Friend Property ParseReelsBlock As Boolean = False
<PSetting(NameOf(SiteSettings.ParseStoriesBlock), NameOf(MySettings))> <PSetting(NameOf(SiteSettings.ParseStoriesBlock), NameOf(MySettings))>
Friend Property ParseStoriesBlock As Boolean = True Friend Property ParseStoriesBlock As Boolean = True
Private ReadOnly Property MySettings As SiteSettings Private ReadOnly Property MySettings As SiteSettings
@@ -20,12 +22,14 @@ Namespace API.Facebook
MySettings = u.HostCollection.Default.Source MySettings = u.HostCollection.Default.Source
ParsePhotoBlock = u.ParsePhotoBlock ParsePhotoBlock = u.ParsePhotoBlock
ParseVideoBlock = u.ParseVideoBlock ParseVideoBlock = u.ParseVideoBlock
ParseReelsBlock = u.ParseReelsBlock
ParseStoriesBlock = u.ParseStoriesBlock ParseStoriesBlock = u.ParseStoriesBlock
End Sub End Sub
Friend Sub New(ByVal s As SiteSettings) Friend Sub New(ByVal s As SiteSettings)
MySettings = s MySettings = s
ParsePhotoBlock = s.ParsePhotoBlock.Value ParsePhotoBlock = s.ParsePhotoBlock.Value
ParseVideoBlock = s.ParseVideoBlock.Value ParseVideoBlock = s.ParseVideoBlock.Value
ParseReelsBlock = s.ParseReelsBlock.Value
ParseStoriesBlock = s.ParseStoriesBlock.Value ParseStoriesBlock = s.ParseStoriesBlock.Value
End Sub End Sub
End Class End Class

View File

@@ -7,9 +7,8 @@
' This program is distributed in the hope that it will be useful, ' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY ' but WITHOUT ANY WARRANTY
Imports SCrawler.Plugin.Attributes Imports SCrawler.Plugin.Attributes
Imports DN = SCrawler.API.Base.DeclaredNames
Namespace API.Instagram Namespace API.Instagram
Friend Class EditorExchangeOptions Friend NotInheritable Class EditorExchangeOptions : Inherits Base.EditorExchangeOptionsBase
#Region "Download" #Region "Download"
<PSetting(Caption:="Get timeline", ToolTip:="Download user timeline")> <PSetting(Caption:="Get timeline", ToolTip:="Download user timeline")>
Friend Property GetTimeline As Boolean Friend Property GetTimeline As Boolean
@@ -36,9 +35,13 @@ Namespace API.Instagram
#End Region #End Region
<PSetting(Caption:="Place the extracted image into the video folder")> <PSetting(Caption:="Place the extracted image into the video folder")>
Friend Property PutImageVideoFolder As Boolean Friend Property PutImageVideoFolder As Boolean
<PSetting(Address:=SettingAddress.User, Caption:=DN.UserNameChangeCaption, ToolTip:=DN.UserNameChangeToolTip)> Friend Overrides Property UserName As String
Friend Overridable Property UserName As String = String.Empty <PSetting(Address:=SettingAddress.User, Caption:="Force update UserName", ToolTip:="Try to force update UserName if it is not found on the site")>
Friend Property ForceUpdateUserName As Boolean = False
<PSetting(Address:=SettingAddress.User, Caption:="Force update user information")>
Friend Property ForceUpdateUserInfo As Boolean = False
Friend Sub New(ByVal u As UserData) Friend Sub New(ByVal u As UserData)
MyBase.New(u)
With u With u
GetTimeline = .GetTimeline GetTimeline = .GetTimeline
GetReels = .GetReels GetReels = .GetReels
@@ -54,7 +57,8 @@ Namespace API.Instagram
PutImageVideoFolder = .PutImageVideoFolder PutImageVideoFolder = .PutImageVideoFolder
UserName = .NameTrue(True) ForceUpdateUserName = .ForceUpdateUserName
ForceUpdateUserInfo = .ForceUpdateUserInfo
End With End With
End Sub End Sub
Friend Sub New(ByVal s As SiteSettings) Friend Sub New(ByVal s As SiteSettings)

View File

@@ -16,8 +16,8 @@ Imports PersonalUtilities.Tools.Web.Cookies
Imports Download = SCrawler.Plugin.ISiteSettings.Download Imports Download = SCrawler.Plugin.ISiteSettings.Download
Imports DN = SCrawler.API.Base.DeclaredNames Imports DN = SCrawler.API.Base.DeclaredNames
Namespace API.Instagram Namespace API.Instagram
<Manifest(InstagramSiteKey), SeparatedTasks(1), SavedPosts, SpecialForm(False)> <Manifest(InstagramSiteKey), SeparatedTasks(1), SavedPosts, SpecialForm(False), UseDownDetector>
Friend Class SiteSettings : Inherits SiteSettingsBase Friend Class SiteSettings : Inherits SiteSettingsBase : Implements DownDetector.IDownDetector
#Region "Declarations" #Region "Declarations"
#Region "Providers" #Region "Providers"
Friend Class TimersChecker : Inherits FieldsCheckerProviderBase Friend Class TimersChecker : Inherits FieldsCheckerProviderBase
@@ -58,8 +58,10 @@ Namespace API.Instagram
#Region "Categories" #Region "Categories"
Private Const CAT_DOWN As String = "Download data" Private Const CAT_DOWN As String = "Download data"
Private Const CAT_UserDefs_VIDEO As String = DN.CAT_UserDefs & ": extract image from video" Private Const CAT_UserDefs_VIDEO As String = DN.CAT_UserDefs & ": extract image from video"
Private Const CAT_ERRORS As String = "Errors"
#End Region #End Region
#Region "Authorization properties" #Region "Properties"
#Region "Authorization"
Friend Const Header_IG_APP_ID As String = "x-ig-app-id" Friend Const Header_IG_APP_ID As String = "x-ig-app-id"
Friend Const Header_IG_WWW_CLAIM As String = "x-ig-www-claim" Friend Const Header_IG_WWW_CLAIM As String = "x-ig-www-claim"
Friend Const Header_CSRF_TOKEN As String = "x-csrftoken" Friend Const Header_CSRF_TOKEN As String = "x-csrftoken"
@@ -68,18 +70,18 @@ Namespace API.Instagram
Friend Const Header_Browser As String = "Sec-Ch-Ua" Friend Const Header_Browser As String = "Sec-Ch-Ua"
Friend Const Header_BrowserExt As String = "Sec-Ch-Ua-Full-Version-List" Friend Const Header_BrowserExt As String = "Sec-Ch-Ua-Full-Version-List"
Friend Const Header_Platform_Verion As String = "Sec-Ch-Ua-Platform-Version" Friend Const Header_Platform_Verion As String = "Sec-Ch-Ua-Platform-Version"
<PropertyOption(ControlText:="x-csrftoken", ControlToolTip:="Can be automatically extracted from cookies", IsAuth:=True, AllowNull:=True), ControlNumber(2), PClonable(Clone:=False)> <PropertyOption(ControlText:="x-csrftoken", ControlToolTip:="Can be automatically extracted from cookies", IsAuth:=True, AllowNull:=True), PClonable(Clone:=False)>
Friend ReadOnly Property HH_CSRF_TOKEN As PropertyValue Friend ReadOnly Property HH_CSRF_TOKEN As PropertyValue
<CookieValueExtractor(NameOf(HH_CSRF_TOKEN))> <CookieValueExtractor(NameOf(HH_CSRF_TOKEN))>
Private Function GetValueFromCookies(ByVal PropName As String, ByVal c As CookieKeeper) As String Private Function GetValueFromCookies(ByVal PropName As String, ByVal c As CookieKeeper) As String
Return c.GetCookieValue(Header_CSRF_TOKEN_COOKIE, PropName, NameOf(HH_CSRF_TOKEN)) Return c.GetCookieValue(Header_CSRF_TOKEN_COOKIE, PropName, NameOf(HH_CSRF_TOKEN))
End Function End Function
<PropertyOption(ControlText:="x-ig-app-id", IsAuth:=True, AllowNull:=False), ControlNumber(3), PClonable(Clone:=False)> <PropertyOption(ControlText:="x-ig-app-id", IsAuth:=True, AllowNull:=False), PClonable(Clone:=False)>
Friend ReadOnly Property HH_IG_APP_ID As PropertyValue Friend ReadOnly Property HH_IG_APP_ID As PropertyValue
<PropertyOption(ControlText:="x-asbd-id", IsAuth:=True, AllowNull:=True), ControlNumber(4), PClonable(Clone:=False)> <PropertyOption(ControlText:="x-asbd-id", IsAuth:=True, AllowNull:=True), PClonable(Clone:=False)>
Friend ReadOnly Property HH_ASBD_ID As PropertyValue Friend ReadOnly Property HH_ASBD_ID As PropertyValue
'PropertyOption(ControlText:="x-ig-www-claim", IsAuth:=True, AllowNull:=True) 'PropertyOption(ControlText:="x-ig-www-claim", IsAuth:=True, AllowNull:=True)
<ControlNumber(5), PClonable(Clone:=False)> <PClonable(Clone:=False)>
Friend ReadOnly Property HH_IG_WWW_CLAIM As PropertyValue Friend ReadOnly Property HH_IG_WWW_CLAIM As PropertyValue
Private ReadOnly Property HH_IG_WWW_CLAIM_IS_ZERO As Boolean Private ReadOnly Property HH_IG_WWW_CLAIM_IS_ZERO As Boolean
Get Get
@@ -88,16 +90,16 @@ Namespace API.Instagram
End Get End Get
End Property End Property
<PropertyOption(ControlText:="sec-ch-ua", IsAuth:=True, AllowNull:=True, <PropertyOption(ControlText:="sec-ch-ua", IsAuth:=True, AllowNull:=True,
InheritanceName:=SettingsCLS.HEADER_DEF_sec_ch_ua), ControlNumber(6), PClonable, PXML(OnlyForChecked:=True)> InheritanceName:=SettingsCLS.HEADER_DEF_sec_ch_ua), PClonable, PXML(OnlyForChecked:=True)>
Private ReadOnly Property HH_BROWSER As PropertyValue Private ReadOnly Property HH_BROWSER As PropertyValue
<PropertyOption(ControlText:="sec-ch-ua-full", ControlToolTip:="sec-ch-ua-full-version-list", IsAuth:=True, AllowNull:=True, <PropertyOption(ControlText:="sec-ch-ua-full", ControlToolTip:="sec-ch-ua-full-version-list", IsAuth:=True, AllowNull:=True,
InheritanceName:=SettingsCLS.HEADER_DEF_sec_ch_ua_full_version_list), ControlNumber(7), PClonable, PXML(OnlyForChecked:=True)> InheritanceName:=SettingsCLS.HEADER_DEF_sec_ch_ua_full_version_list), PClonable, PXML(OnlyForChecked:=True)>
Private ReadOnly Property HH_BROWSER_EXT As PropertyValue Private ReadOnly Property HH_BROWSER_EXT As PropertyValue
<PropertyOption(ControlText:="sec-ch-ua-platform-ver", ControlToolTip:="sec-ch-ua-platform-version", IsAuth:=True, AllowNull:=True, LeftOffset:=135, <PropertyOption(ControlText:="sec-ch-ua-platform-ver", ControlToolTip:="sec-ch-ua-platform-version", IsAuth:=True, AllowNull:=True, LeftOffset:=135,
InheritanceName:=SettingsCLS.HEADER_DEF_sec_ch_ua_platform_version), ControlNumber(8), PClonable, PXML(OnlyForChecked:=True)> InheritanceName:=SettingsCLS.HEADER_DEF_sec_ch_ua_platform_version), PClonable, PXML(OnlyForChecked:=True)>
Private ReadOnly Property HH_PLATFORM As PropertyValue Private ReadOnly Property HH_PLATFORM As PropertyValue
<PropertyOption(ControlText:="UserAgent", IsAuth:=True, AllowNull:=True, <PropertyOption(ControlText:="UserAgent", IsAuth:=True, AllowNull:=True,
InheritanceName:=SettingsCLS.HEADER_DEF_UserAgent), ControlNumber(9), PClonable, PXML(OnlyForChecked:=True)> InheritanceName:=SettingsCLS.HEADER_DEF_UserAgent), PClonable, PXML(OnlyForChecked:=True)>
Private ReadOnly Property HH_USER_AGENT As PropertyValue Private ReadOnly Property HH_USER_AGENT As PropertyValue
Friend Overrides Function BaseAuthExists() As Boolean Friend Overrides Function BaseAuthExists() As Boolean
Return Responser.CookiesExists And ACheck(HH_IG_APP_ID.Value) And ACheck(HH_CSRF_TOKEN.Value) Return Responser.CookiesExists And ACheck(HH_IG_APP_ID.Value) And ACheck(HH_CSRF_TOKEN.Value)
@@ -126,113 +128,167 @@ Namespace API.Instagram
End If End If
End Sub End Sub
#Region "HH_IG_WWW_CLAIM" #Region "HH_IG_WWW_CLAIM"
<PropertyOption(ControlText:="ig-www-claim update interval", IsAuth:=True, LeftOffset:=150), PXML, ControlNumber(10), PClonable, HiddenControl> <PropertyOption(ControlText:="ig-www-claim update interval", IsAuth:=True, LeftOffset:=150), PXML, PClonable, HiddenControl>
Private ReadOnly Property HH_IG_WWW_CLAIM_UPDATE_INTERVAL As PropertyValue Private ReadOnly Property HH_IG_WWW_CLAIM_UPDATE_INTERVAL As PropertyValue
<PropertyOption(ControlText:="ig-www-claim: always 0", ControlToolTip:="Keep token value always = 0", IsAuth:=True), <PropertyOption(ControlText:="ig-www-claim: always 0", ControlToolTip:="Keep token value always = 0", IsAuth:=True),
PXML, ControlNumber(11), PClonable, HiddenControl> PXML, PClonable, HiddenControl>
Friend ReadOnly Property HH_IG_WWW_CLAIM_ALWAYS_ZERO As PropertyValue Friend ReadOnly Property HH_IG_WWW_CLAIM_ALWAYS_ZERO As PropertyValue
<PropertyOption(ControlText:="ig-www-claim: reset each session", ControlToolTip:="Set 'x-ig-www-claim' to '0' before each session", IsAuth:=True), <PropertyOption(ControlText:="ig-www-claim: reset each session", ControlToolTip:="Set 'x-ig-www-claim' to '0' before each session", IsAuth:=True),
PXML, ControlNumber(12), PClonable, HiddenControl> PXML, PClonable, HiddenControl>
Friend ReadOnly Property HH_IG_WWW_CLAIM_RESET_EACH_SESSION As PropertyValue Friend ReadOnly Property HH_IG_WWW_CLAIM_RESET_EACH_SESSION As PropertyValue
<PropertyOption(ControlText:="ig-www-claim: reset each target", ControlToolTip:="Set 'x-ig-www-claim' to '0' before each target", IsAuth:=True), <PropertyOption(ControlText:="ig-www-claim: reset each target", ControlToolTip:="Set 'x-ig-www-claim' to '0' before each target", IsAuth:=True),
PXML, ControlNumber(13), PClonable, HiddenControl> PXML, PClonable, HiddenControl>
Friend ReadOnly Property HH_IG_WWW_CLAIM_RESET_EACH_TARGET As PropertyValue Friend ReadOnly Property HH_IG_WWW_CLAIM_RESET_EACH_TARGET As PropertyValue
<PropertyOption(ControlText:="ig-www-claim: use in requests", IsAuth:=True), PXML, ControlNumber(14), PClonable, HiddenControl> <PropertyOption(ControlText:="ig-www-claim: use in requests", IsAuth:=True), PXML, PClonable, HiddenControl>
Friend ReadOnly Property HH_IG_WWW_CLAIM_USE As PropertyValue Friend ReadOnly Property HH_IG_WWW_CLAIM_USE As PropertyValue
<PropertyOption(ControlText:="ig-www-claim: use default algorithm to update", IsAuth:=True), PXML, ControlNumber(15), PClonable, HiddenControl> <PropertyOption(ControlText:="ig-www-claim: use default algorithm to update", IsAuth:=True), PXML, PClonable, HiddenControl>
Friend ReadOnly Property HH_IG_WWW_CLAIM_USE_DEFAULT_ALGO As PropertyValue Friend ReadOnly Property HH_IG_WWW_CLAIM_USE_DEFAULT_ALGO As PropertyValue
<Provider(NameOf(HH_IG_WWW_CLAIM_UPDATE_INTERVAL), FieldsChecker:=True)> <Provider(NameOf(HH_IG_WWW_CLAIM_UPDATE_INTERVAL), FieldsChecker:=True)>
Private ReadOnly Property TokenUpdateIntervalProvider As IFormatProvider Private ReadOnly Property TokenUpdateIntervalProvider As IFormatProvider
#End Region #End Region
<PropertyOption(ControlText:="Use GraphQL to download", IsAuth:=True), PXML, ControlNumber(16), PClonable> <PropertyOption(ControlText:="Use GraphQL to download", IsAuth:=True), PXML, PClonable>
Friend ReadOnly Property USE_GQL As PropertyValue Friend ReadOnly Property USE_GQL As PropertyValue
#End Region #End Region
#Region "Download properties" #Region "Download data"
<PropertyOption(ControlText:="DownDetector", <PropertyOption(ControlText:="Download timeline", Category:=CAT_DOWN), PXML, PClonable>
ControlToolTip:="Use 'DownDetector' to determine if the site is accessible. -1 to disable." & vbCr & Friend ReadOnly Property DownloadTimeline As PropertyValue
"The value represents the average number of error reports over the last 4 hours"), <PXML> Private ReadOnly Property DownloadTimeline_Def As PropertyValue
PClonable, PXML, ControlNumber(17)> <PropertyOption(ControlText:="Download reels", Category:=CAT_DOWN), PXML, PClonable>
Private ReadOnly Property DownDetectorValue As PropertyValue Friend ReadOnly Property DownloadReels As PropertyValue
<Provider(NameOf(DownDetectorValue), FieldsChecker:=True)> <PXML> Private ReadOnly Property DownloadReels_Def As PropertyValue
Private ReadOnly Property DownDetectorValueProvider As IFormatProvider <PropertyOption(ControlText:="Download stories", Category:=CAT_DOWN), PXML, PClonable>
<PropertyOption(ControlText:="Add 'DownDetector' information to the log."), PClonable, PXML, ControlNumber(18), HiddenControl> Friend ReadOnly Property DownloadStories As PropertyValue
Private ReadOnly Property DownDetectorValueAddToLog As PropertyValue <PXML> Private ReadOnly Property DownloadStories_Def As PropertyValue
<PropertyOption(ControlText:="Download stories: user", Category:=CAT_DOWN), PXML, PClonable>
Friend ReadOnly Property DownloadStoriesUser As PropertyValue
<PXML> Private ReadOnly Property DownloadStoriesUser_Def As PropertyValue
<PropertyOption(ControlText:="Download tagged posts", Category:=CAT_DOWN), PXML, PClonable>
Friend ReadOnly Property DownloadTagged As PropertyValue
<PXML> Private ReadOnly Property DownloadTagged_Def As PropertyValue
#End Region
#Region "Timers"
Friend Const TimersUrgentTip As String = vbCr & "It is highly recommended not to change the default value." Friend Const TimersUrgentTip As String = vbCr & "It is highly recommended not to change the default value."
<PropertyOption(ControlText:="Request timer (any)", <PropertyOption(ControlText:="Request timer (any)",
ControlToolTip:="The timer (in milliseconds) that SCrawler should wait before executing the next request." & ControlToolTip:="The timer (in milliseconds) that SCrawler should wait before executing the next request." &
vbCr & "The default value is 1'000." & vbCr & "The minimum value is 0." & TimersUrgentTip, AllowNull:=False, Category:=DN.CAT_Timers), vbCr & "The default value is 1'000." & vbCr & "The minimum value is 0." & TimersUrgentTip, AllowNull:=False, Category:=DN.CAT_Timers),
PXML, ControlNumber(19), PClonable> PXML, PClonable>
Friend ReadOnly Property RequestsWaitTimer_Any As PropertyValue Friend ReadOnly Property RequestsWaitTimer_Any As PropertyValue
<Provider(NameOf(RequestsWaitTimer_Any), FieldsChecker:=True)> <Provider(NameOf(RequestsWaitTimer_Any), FieldsChecker:=True)>
Private ReadOnly Property RequestsWaitTimer_AnyProvider As IFormatProvider Private ReadOnly Property RequestsWaitTimer_AnyProvider As IFormatProvider
<PropertyOption(ControlText:="Request timer", <PropertyOption(ControlText:="Request timer",
ControlToolTip:="The time value (in milliseconds) that the program will wait before processing the next 'Request time counter' request." & ControlToolTip:="The time value (in milliseconds) that the program will wait before processing the next 'Request time counter' request." &
vbCr & "The default value is 1'000." & vbCr & "The minimum value is 100." & TimersUrgentTip, vbCr & "The default value is 1'000." & vbCr & "The minimum value is 100." & TimersUrgentTip,
AllowNull:=False, Category:=DN.CAT_Timers), PXML, ControlNumber(20), PClonable> AllowNull:=False, Category:=DN.CAT_Timers), PXML, PClonable>
Friend ReadOnly Property RequestsWaitTimer As PropertyValue Friend ReadOnly Property RequestsWaitTimer As PropertyValue
<Provider(NameOf(RequestsWaitTimer), FieldsChecker:=True)> <Provider(NameOf(RequestsWaitTimer), FieldsChecker:=True)>
Private ReadOnly Property RequestsWaitTimerProvider As IFormatProvider Private ReadOnly Property RequestsWaitTimerProvider As IFormatProvider
<PropertyOption(ControlText:="Request timer counter", <PropertyOption(ControlText:="Request timer counter",
ControlToolTip:="How many requests will be sent to Instagram before the program waits 'Request timer'." & ControlToolTip:="How many requests will be sent to Instagram before the program waits 'Request timer'." &
vbCr & "The default value is 1." & vbCr & "The minimum value is 1." & TimersUrgentTip, vbCr & "The default value is 1." & vbCr & "The minimum value is 1." & TimersUrgentTip,
AllowNull:=False, LeftOffset:=120, Category:=DN.CAT_Timers), PXML, ControlNumber(21), PClonable> AllowNull:=False, LeftOffset:=120, Category:=DN.CAT_Timers), PXML, PClonable>
Friend ReadOnly Property RequestsWaitTimerTaskCount As PropertyValue Friend ReadOnly Property RequestsWaitTimerTaskCount As PropertyValue
<Provider(NameOf(RequestsWaitTimerTaskCount), FieldsChecker:=True)> <Provider(NameOf(RequestsWaitTimerTaskCount), FieldsChecker:=True)>
Private ReadOnly Property RequestsWaitTimerTaskCountProvider As IFormatProvider Private ReadOnly Property RequestsWaitTimerTaskCountProvider As IFormatProvider
<PropertyOption(ControlText:="Posts limit timer", <PropertyOption(ControlText:="Posts limit timer",
ControlToolTip:="The time value (in milliseconds) the program will wait before processing the next request after 195 requests." & ControlToolTip:="The time value (in milliseconds) the program will wait before processing the next request after 195 requests." &
vbCr & "The default value is 60'000." & vbCr & "The minimum value is 10'000." & TimersUrgentTip, vbCr & "The default value is 60'000." & vbCr & "The minimum value is 10'000." & TimersUrgentTip,
AllowNull:=False, Category:=DN.CAT_Timers), PXML, ControlNumber(22), PClonable> AllowNull:=False, Category:=DN.CAT_Timers), PXML, PClonable>
Friend ReadOnly Property SleepTimerOnPostsLimit As PropertyValue Friend ReadOnly Property SleepTimerOnPostsLimit As PropertyValue
<Provider(NameOf(SleepTimerOnPostsLimit), FieldsChecker:=True)> <Provider(NameOf(SleepTimerOnPostsLimit), FieldsChecker:=True)>
Private ReadOnly Property SleepTimerOnPostsLimitProvider As IFormatProvider Private ReadOnly Property SleepTimerOnPostsLimitProvider As IFormatProvider
<PropertyOption(ControlText:="Get timeline", ControlToolTip:="Default value for new users", Category:=DN.CAT_UserDefs), PXML, ControlNumber(23), PClonable> #End Region
#Region "New user defaults"
<PropertyOption(ControlText:="Get timeline", ControlToolTip:="Default value for new users", Category:=DN.CAT_UserDefs), PXML, PClonable>
Friend ReadOnly Property GetTimeline As PropertyValue Friend ReadOnly Property GetTimeline As PropertyValue
<PropertyOption(ControlText:="From timeline", ControlToolTip:="Default value for new users", Category:=CAT_UserDefs_VIDEO), PXML, ControlNumber(23), PClonable> <PropertyOption(ControlText:="From timeline", ControlToolTip:="Default value for new users", Category:=CAT_UserDefs_VIDEO), PXML, PClonable>
Friend ReadOnly Property GetTimeline_VideoPic As PropertyValue Friend ReadOnly Property GetTimeline_VideoPic As PropertyValue
<PropertyOption(ControlText:="Get reels", ControlToolTip:="Default value for new users", Category:=DN.CAT_UserDefs), PXML, ControlNumber(24), PClonable> <PropertyOption(ControlText:="Get reels", ControlToolTip:="Default value for new users", Category:=DN.CAT_UserDefs), PXML, PClonable>
Friend ReadOnly Property GetReels As PropertyValue Friend ReadOnly Property GetReels As PropertyValue
<PropertyOption(ControlText:="From reels", ControlToolTip:="Default value for new users", Category:=CAT_UserDefs_VIDEO), PXML, ControlNumber(24), PClonable> <PropertyOption(ControlText:="From reels", ControlToolTip:="Default value for new users", Category:=CAT_UserDefs_VIDEO), PXML, PClonable>
Friend ReadOnly Property GetReels_VideoPic As PropertyValue Friend ReadOnly Property GetReels_VideoPic As PropertyValue
<PropertyOption(ControlText:="Get stories", ControlToolTip:="Default value for new users", Category:=DN.CAT_UserDefs), PXML, ControlNumber(25), PClonable> <PropertyOption(ControlText:="Get stories", ControlToolTip:="Default value for new users", Category:=DN.CAT_UserDefs), PXML, PClonable>
Friend ReadOnly Property GetStories As PropertyValue Friend ReadOnly Property GetStories As PropertyValue
<PropertyOption(ControlText:="From stories", ControlToolTip:="Default value for new users", Category:=CAT_UserDefs_VIDEO), PXML, ControlNumber(25), PClonable> <PropertyOption(ControlText:="From stories", ControlToolTip:="Default value for new users", Category:=CAT_UserDefs_VIDEO), PXML, PClonable>
Friend ReadOnly Property GetStories_VideoPic As PropertyValue Friend ReadOnly Property GetStories_VideoPic As PropertyValue
<PropertyOption(ControlText:="Get stories: user", ControlToolTip:="Default value for new users", Category:=DN.CAT_UserDefs), PXML, ControlNumber(26), PClonable> <PropertyOption(ControlText:="Get stories: user", ControlToolTip:="Default value for new users", Category:=DN.CAT_UserDefs), PXML, PClonable>
Friend ReadOnly Property GetStoriesUser As PropertyValue Friend ReadOnly Property GetStoriesUser As PropertyValue
<PropertyOption(ControlText:="From stories: user", ControlToolTip:="Default value for new users", Category:=CAT_UserDefs_VIDEO), PXML, ControlNumber(26), PClonable> <PropertyOption(ControlText:="From stories: user", ControlToolTip:="Default value for new users", Category:=CAT_UserDefs_VIDEO), PXML, PClonable>
Friend ReadOnly Property GetStoriesUser_VideoPic As PropertyValue Friend ReadOnly Property GetStoriesUser_VideoPic As PropertyValue
<PropertyOption(ControlText:="Get tagged posts", ControlToolTip:="Default value for new users", Category:=DN.CAT_UserDefs), PXML, ControlNumber(27), PClonable> <PropertyOption(ControlText:="Get tagged posts", ControlToolTip:="Default value for new users", Category:=DN.CAT_UserDefs), PXML, PClonable>
Friend ReadOnly Property GetTagged As PropertyValue Friend ReadOnly Property GetTagged As PropertyValue
<PropertyOption(ControlText:="From tagged posts", ControlToolTip:="Default value for new users", Category:=CAT_UserDefs_VIDEO), PXML, ControlNumber(27), PClonable> <PropertyOption(ControlText:="From tagged posts", ControlToolTip:="Default value for new users", Category:=CAT_UserDefs_VIDEO), PXML, PClonable>
Friend ReadOnly Property GetTagged_VideoPic As PropertyValue Friend ReadOnly Property GetTagged_VideoPic As PropertyValue
<PropertyOption(ControlText:="From saved posts", ControlToolTip:="Default value for new users", Category:=CAT_UserDefs_VIDEO), PXML, ControlNumber(28), PClonable> <PropertyOption(ControlText:="From saved posts", ControlToolTip:="Default value for new users", Category:=CAT_UserDefs_VIDEO), PXML, PClonable>
Friend ReadOnly Property GetSavedPosts_VideoPic As PropertyValue Friend ReadOnly Property GetSavedPosts_VideoPic As PropertyValue
<PropertyOption(ControlText:="Place the extracted image into the video folder", ControlToolTip:="Default value for new users", Category:=CAT_UserDefs_VIDEO), PXML, ControlNumber(29), PClonable> <PropertyOption(ControlText:="Place the extracted image into the video folder", ControlToolTip:="Default value for new users", Category:=CAT_UserDefs_VIDEO), PXML, PClonable>
Friend ReadOnly Property PutImageVideoFolder As PropertyValue Friend ReadOnly Property PutImageVideoFolder As PropertyValue
#End Region
#Region "Errors"
Private Const ErrorsDefault As String = "572"
<PropertyOption(ControlText:="Skip errors",
ControlToolTip:="Skip the following errors (comma separated)." & vbCr &
"Facing these errors will not disable the download, but will add a simple line to the log.", Category:=CAT_ERRORS),
PClonable, PXML>
Private ReadOnly Property SkipErrors As PropertyValue
<PropertyOption(ControlText:="Add skipped errors to the log", Category:=CAT_ERRORS), PClonable, PXML>
Private ReadOnly Property SkipErrors_AddToLog As PropertyValue
<PropertyOption(ControlText:="Skip errors (exclude)",
ControlToolTip:="Exclude the following errors from being added to the log (comma separated)", Category:=CAT_ERRORS), PClonable, PXML>
Private ReadOnly Property SkipErrors_AddToLog_Silent As PropertyValue
Friend ReadOnly Property ErrorSpecialHandling(ByVal ErrCode As Integer) As Boolean
Get
With CStr(SkipErrors.Value) : Return Not .IsEmptyString AndAlso .Contains(ErrCode) : End With
End Get
End Property
Friend ReadOnly Property ErrorSpecialHandling_AddToLog(ByVal ErrCode As Integer) As Boolean
Get
With CStr(SkipErrors_AddToLog_Silent.Value)
Return CBool(SkipErrors_AddToLog.Value) AndAlso (.IsEmptyString OrElse Not .Contains(ErrCode))
End With
End Get
End Property
<PropertyOption(ControlText:="Ignore stories downloading errors (560)",
ControlToolTip:="If checked, error 560 will be skipped and the download will continue. Otherwise, the download will be interrupted.",
Category:=CAT_ERRORS), PClonable, PXML>
Friend ReadOnly Property IgnoreStoriesDownloadingErrors As PropertyValue
#End Region
#Region "Other params"
<PropertyOption(ControlText:="DownDetector",
ControlToolTip:="Use 'DownDetector' to determine if the site is accessible. -1 to disable." & vbCr &
"The value represents the average number of error reports over the last 4 hours"),
PClonable, PXML>
Private ReadOnly Property DownDetectorValue As PropertyValue
<Provider(NameOf(DownDetectorValue), FieldsChecker:=True)>
Private ReadOnly Property DownDetectorValueProvider As IFormatProvider
<PropertyOption(ControlText:="Add 'DownDetector' information to the log."), PClonable, PXML, HiddenControl>
Private ReadOnly Property DownDetectorValueAddToLog As PropertyValue
<PropertyOption(ControlText:="Tagged notify limit", <PropertyOption(ControlText:="Tagged notify limit",
ControlToolTip:="If the number of tagged posts exceeds this number you will be notified." & vbCr & ControlToolTip:="If the number of tagged posts exceeds this number you will be notified." & vbCr &
"-1 to disable"), PXML, ControlNumber(27), PClonable> "-1 to disable"), PXML, PClonable>
Friend ReadOnly Property TaggedNotifyLimit As PropertyValue Friend ReadOnly Property TaggedNotifyLimit As PropertyValue
<Provider(NameOf(TaggedNotifyLimit), FieldsChecker:=True)> <Provider(NameOf(TaggedNotifyLimit), FieldsChecker:=True)>
Private ReadOnly Property TaggedNotifyLimitProvider As IFormatProvider Private ReadOnly Property TaggedNotifyLimitProvider As IFormatProvider
#End Region #End Region
#Region "Download ready" #End Region
<PropertyOption(ControlText:="Download timeline", Category:=CAT_DOWN), PXML, ControlNumber(10), PClonable> #Region "IDownDetector Support"
Friend ReadOnly Property DownloadTimeline As PropertyValue Private ReadOnly Property IDownDetector_Value As Integer Implements DownDetector.IDownDetector.Value
<PXML> Private ReadOnly Property DownloadTimeline_Def As PropertyValue Get
<PropertyOption(ControlText:="Download reels", Category:=CAT_DOWN), PXML, ControlNumber(11), PClonable> Return DownDetectorValue.Value
Friend ReadOnly Property DownloadReels As PropertyValue End Get
<PXML> Private ReadOnly Property DownloadReels_Def As PropertyValue End Property
<PropertyOption(ControlText:="Download stories", Category:=CAT_DOWN), PXML, ControlNumber(12), PClonable> Private ReadOnly Property IDownDetector_AddToLog As Boolean Implements DownDetector.IDownDetector.AddToLog
Friend ReadOnly Property DownloadStories As PropertyValue Get
<PXML> Private ReadOnly Property DownloadStories_Def As PropertyValue Return DownDetectorValueAddToLog.Value
<PropertyOption(ControlText:="Download stories: user", Category:=CAT_DOWN), PXML, ControlNumber(13), PClonable> End Get
Friend ReadOnly Property DownloadStoriesUser As PropertyValue End Property
<PXML> Private ReadOnly Property DownloadStoriesUser_Def As PropertyValue Private ReadOnly Property IDownDetector_CheckSite As String Implements DownDetector.IDownDetector.CheckSite
<PropertyOption(ControlText:="Download tagged posts", Category:=CAT_DOWN), PXML, ControlNumber(14), PClonable> Get
Friend ReadOnly Property DownloadTagged As PropertyValue Return "instagram"
<PXML> Private ReadOnly Property DownloadTagged_Def As PropertyValue End Get
End Property
Private Function IDownDetector_Available(ByVal What As Download, ByVal Silent As Boolean) As Boolean Implements DownDetector.IDownDetector.Available
Return MDD.Available(What, Silent)
End Function
#End Region #End Region
#Region "429 bypass" #Region "429 bypass"
<PXML("InstagramDownloadingErrorDate")> <PXML("InstagramDownloadingErrorDate")>
@@ -266,8 +322,40 @@ Namespace API.Instagram
End Get End Get
End Property End Property
Private Const LastDownloadDateResetInterval As Integer = 60 Private Const LastDownloadDateResetInterval As Integer = 60
Private TooManyRequestsReadyForCatch As Boolean = True
Friend Function GetWaitDate() As Date
With DownloadingErrorDate
If ACheck(Of Date)(.Value) Then
Return CDate(.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 ACheck(Of Date)(.Value) 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
.Value = Nothing
LastApplyingValue = Nothing
TooManyRequestsReadyForCatch = True
End If
End With
End Sub
#End Region
#Region "LastRequestsCount, Label"
<PXML> Private ReadOnly Property LastDownloadDate As PropertyValue <PXML> Private ReadOnly Property LastDownloadDate As PropertyValue
<PXML> Private ReadOnly Property LastRequestsCount As PropertyValue <PXML> Private ReadOnly Property LastRequestsCount As PropertyValue
<PropertyOption(IsInformationLabel:=True)>
Private ReadOnly Property LastRequestsCountLabel As PropertyValue
Private ReadOnly MyLastRequests As Dictionary(Of Date, Integer) Private ReadOnly MyLastRequests As Dictionary(Of Date, Integer)
Private ReadOnly Property MyLastRequestsDate As Date Private ReadOnly Property MyLastRequestsDate As Date
Get Get
@@ -321,36 +409,6 @@ Namespace API.Instagram
ErrorsDescriber.Execute(EDP.SendToLog, ex, "[SiteSettings.Instagram.RefreshMyLastRequests]") ErrorsDescriber.Execute(EDP.SendToLog, ex, "[SiteSettings.Instagram.RefreshMyLastRequests]")
End Try End Try
End Sub End Sub
<PropertyOption(IsInformationLabel:=True), ControlNumber(100)>
Private ReadOnly Property LastRequestsCountLabel As PropertyValue
Private TooManyRequestsReadyForCatch As Boolean = True
Friend Function GetWaitDate() As Date
With DownloadingErrorDate
If ACheck(Of Date)(.Value) Then
Return CDate(.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 ACheck(Of Date)(.Value) 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
.Value = Nothing
LastApplyingValue = Nothing
TooManyRequestsReadyForCatch = True
End If
End With
End Sub
#End Region #End Region
#End Region #End Region
#Region "Initializer" #Region "Initializer"
@@ -427,9 +485,6 @@ Namespace API.Instagram
DownloadTagged = New PropertyValue(False) DownloadTagged = New PropertyValue(False)
DownloadTagged_Def = New PropertyValue(DownloadTagged.Value, GetType(Boolean)) DownloadTagged_Def = New PropertyValue(DownloadTagged.Value, GetType(Boolean))
DownDetectorValue = New PropertyValue(20)
DownDetectorValueProvider = New TimersChecker(-1)
DownDetectorValueAddToLog = New PropertyValue(False)
RequestsWaitTimer_Any = New PropertyValue(1000) RequestsWaitTimer_Any = New PropertyValue(1000)
RequestsWaitTimer_AnyProvider = New TimersChecker(0) RequestsWaitTimer_AnyProvider = New TimersChecker(0)
RequestsWaitTimer = New PropertyValue(1000) RequestsWaitTimer = New PropertyValue(1000)
@@ -451,6 +506,15 @@ Namespace API.Instagram
GetTagged_VideoPic = New PropertyValue(True) GetTagged_VideoPic = New PropertyValue(True)
GetSavedPosts_VideoPic = New PropertyValue(True) GetSavedPosts_VideoPic = New PropertyValue(True)
PutImageVideoFolder = New PropertyValue(False) PutImageVideoFolder = New PropertyValue(False)
SkipErrors = New PropertyValue(ErrorsDefault)
SkipErrors_AddToLog = New PropertyValue(True)
SkipErrors_AddToLog_Silent = New PropertyValue(String.Empty, GetType(String))
IgnoreStoriesDownloadingErrors = New PropertyValue(False)
DownDetectorValue = New PropertyValue(20)
DownDetectorValueProvider = New TimersChecker(-1)
DownDetectorValueAddToLog = New PropertyValue(False)
TaggedNotifyLimit = New PropertyValue(200) TaggedNotifyLimit = New PropertyValue(200)
TaggedNotifyLimitProvider = New TaggedNotifyLimitChecker TaggedNotifyLimitProvider = New TaggedNotifyLimitChecker
@@ -460,6 +524,8 @@ Namespace API.Instagram
LastRequestsCountLabel = New PropertyValue(String.Empty, GetType(String)) LastRequestsCountLabel = New PropertyValue(String.Empty, GetType(String))
MyLastRequests = New Dictionary(Of Date, Integer) MyLastRequests = New Dictionary(Of Date, Integer)
MDD = New DownDetector.Checker(Of SiteSettings)(Me)
_AllowUserAgentUpdate = False _AllowUserAgentUpdate = False
UrlPatternUser = "https://www.instagram.com/{0}/" UrlPatternUser = "https://www.instagram.com/{0}/"
UserRegex = RParams.DMS(String.Format(UserRegexDefaultPattern, "instagram.com/"), 1) UserRegex = RParams.DMS(String.Format(UserRegexDefaultPattern, "instagram.com/"), 1)
@@ -507,18 +573,10 @@ Namespace API.Instagram
End Function End Function
#End Region #End Region
#Region "Downloading" #Region "Downloading"
Private ____DownloadStarted As Boolean = False Private ReadOnly MDD As DownDetector.Checker(Of SiteSettings)
Private ____AvailableRequested As Boolean = False
Private ____AvailableSilent As Boolean = True
Private ____AvailableChecked As Boolean = False
Private ____AvailableResult As Boolean = False
Private Sub ResetDownloadOptions() Private Sub ResetDownloadOptions()
If ActiveJobs < 1 Then If ActiveJobs < 1 Then
____DownloadStarted = False MDD.Reset()
____AvailableRequested = False
____AvailableChecked = False
____AvailableSilent = True
____AvailableResult = False
If ActiveSessionRequestsExists Then RefreshMyLastRequests(Now) If ActiveSessionRequestsExists Then RefreshMyLastRequests(Now)
ActiveSessionRequestsExists = False ActiveSessionRequestsExists = False
_NextWNM = UserData.WNM.Notify _NextWNM = UserData.WNM.Notify
@@ -529,69 +587,11 @@ Namespace API.Instagram
End If End If
End Sub End Sub
Friend Overrides Function Available(ByVal What As Download, ByVal Silent As Boolean) As Boolean Friend Overrides Function Available(ByVal What As Download, ByVal Silent As Boolean) As Boolean
If MyBase.Available(What, Silent) And ActiveJobs < 2 Then Return MyBase.Available(What, Silent) And ActiveJobs < 2
If CInt(DownDetectorValue.Value) >= 0 Then
If ____DownloadStarted Then
____AvailableRequested = True
____AvailableSilent = Silent
Return True
Else
Return AvailableImpl(What, Silent)
End If
Else
Return True
End If
Else
Return False
End If
End Function
#Disable Warning IDE0060
Private Function AvailableImpl(ByVal What As Download, ByVal Silent As Boolean) As Boolean
#Enable Warning
Try
AvailableText = String.Empty
If CInt(DownDetectorValue.Value) = -1 Then
Return True
Else
Dim dl As List(Of DownDetector.Data) = DownDetector.GetData("instagram")
If dl.ListExists Then
dl = dl.Take(4).ToList
Dim avg% = dl.Average(Function(d) d.Value)
If avg > CInt(DownDetectorValue.Value) Then
AvailableText = "Over the past hour, Instagram has received an average of " &
avg.NumToString(New ANumbers With {.FormatOptions = ANumbers.Options.GroupIntegral}) & " outage reports:" & vbCr &
dl.ListToString(vbCr)
If CBool(DownDetectorValueAddToLog.Value) Then MyMainLOG = AvailableText
If Silent Then
Return False
Else
Return MsgBoxE({$"{AvailableText}{vbCr}{vbCr}Do you want to continue parsing Instagram data?",
"There are outage reports on Instagram"}, vbYesNo) = vbYes
End If
End If
End If
Return True
End If
Catch ex As Exception
Return ErrorsDescriber.Execute(EDP.SendToLog + EDP.ReturnValue, ex, "[API.Instagram.SiteSettings.Available]", True)
End Try
End Function End Function
Friend Property SkipUntilNextSession As Boolean = False Friend Property SkipUntilNextSession As Boolean = False
Friend Overrides Function ReadyToDownload(ByVal What As Download) As Boolean Friend Overrides Function ReadyToDownload(ByVal What As Download) As Boolean
If ActiveJobs < 2 AndAlso Not SkipUntilNextSession AndAlso ReadyForDownload AndAlso BaseAuthExists() AndAlso CBool(DownloadTimeline.Value) Then Return ActiveJobs < 2 AndAlso Not SkipUntilNextSession AndAlso ReadyForDownload AndAlso BaseAuthExists() AndAlso CBool(DownloadTimeline.Value)
If ____DownloadStarted And ____AvailableRequested Then
____AvailableResult = AvailableImpl(What, ____AvailableSilent)
____AvailableChecked = True
____AvailableRequested = False
Return ____AvailableResult
ElseIf ____AvailableChecked Then
Return ____AvailableResult
Else
Return True
End If
Else
Return False
End If
End Function End Function
Private ActiveJobs As Integer = 0 Private ActiveJobs As Integer = 0
Private ActiveSessionDate As Date Private ActiveSessionDate As Date
@@ -601,7 +601,7 @@ Namespace API.Instagram
Friend Overrides Sub DownloadStarted(ByVal What As Download) Friend Overrides Sub DownloadStarted(ByVal What As Download)
ResetDownloadOptions() ResetDownloadOptions()
ActiveJobs += 1 ActiveJobs += 1
If ActiveJobs = 1 Then ____DownloadStarted = True : ActiveSessionDate = Now If ActiveJobs = 1 Then ActiveSessionDate = Now
If Not HH_IG_WWW_CLAIM_IS_ZERO AndAlso If Not HH_IG_WWW_CLAIM_IS_ZERO AndAlso
( (
(CBool(HH_IG_WWW_CLAIM_USE_DEFAULT_ALGO.Value) AndAlso MyLastRequestsDate.AddMinutes(HH_IG_WWW_CLAIM_UPDATE_INTERVAL.Value) < Now) Or (CBool(HH_IG_WWW_CLAIM_USE_DEFAULT_ALGO.Value) AndAlso MyLastRequestsDate.AddMinutes(HH_IG_WWW_CLAIM_UPDATE_INTERVAL.Value) < Now) Or
@@ -732,9 +732,6 @@ Namespace API.Instagram
Using f As New InternalSettingsForm(Options, Me, False) : f.ShowDialog() : End Using Using f As New InternalSettingsForm(Options, Me, False) : f.ShowDialog() : End Using
End If End If
End Sub End Sub
Friend Overrides Function GetUserUrl(ByVal User As IPluginContentProvider) As String
Return String.Format(UrlPatternUser, DirectCast(User, UserData).NameTrue)
End Function
Friend Overrides Function GetUserPostUrl(ByVal User As UserDataBase, ByVal Media As UserMedia) As String Friend Overrides Function GetUserPostUrl(ByVal User As UserDataBase, ByVal Media As UserMedia) As String
Try Try
Dim code$ = DirectCast(User, UserData).GetPostCodeById(Media.Post.ID) Dim code$ = DirectCast(User, UserData).GetPostCodeById(Media.Post.ID)

View File

@@ -37,10 +37,11 @@ Namespace API.Instagram
Private Const Name_GetTagged_VideoPic As String = "GetTaggedData_VideoPic" Private Const Name_GetTagged_VideoPic As String = "GetTaggedData_VideoPic"
Private Const Name_PutImageVideoFolder As String = "PutImageVideoFolder" Private Const Name_PutImageVideoFolder As String = "PutImageVideoFolder"
Private Const Name_TaggedChecked As String = "TaggedChecked" Private Const Name_TaggedChecked As String = "TaggedChecked"
Private Const Name_NameTrue As String = "NameTrue" Private Const Name_ForceUpdateUserName As String = "ForceUpdateUserName"
Private Const Name_ForceUpdateUserInfo As String = "ForceUpdateUserInfo"
#End Region #End Region
#Region "Declarations" #Region "Declarations"
Protected Structure PostKV : Implements IEContainerProvider Friend Structure PostKV : Implements IEContainerProvider
Private Const Name_Code As String = "Code" Private Const Name_Code As String = "Code"
Private Const Name_Section As String = "Section" Private Const Name_Section As String = "Section"
Friend Code As String Friend Code As String
@@ -111,13 +112,9 @@ Namespace API.Instagram
Case Else : Return True Case Else : Return True
End Select End Select
End Function End Function
Protected _NameTrue As String = String.Empty
Friend ReadOnly Property NameTrue(Optional ByVal Exact As Boolean = False) As String
Get
Return If(Exact, _NameTrue, _NameTrue.IfNullOrEmpty(Name))
End Get
End Property
Private UserNameRequested As Boolean = False Private UserNameRequested As Boolean = False
Friend Property ForceUpdateUserName As Boolean = False
Friend Property ForceUpdateUserInfo As Boolean = False
#End Region #End Region
#Region "Loader" #Region "Loader"
Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean) Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean)
@@ -137,7 +134,8 @@ Namespace API.Instagram
GetTaggedData = .Value(Name_GetTagged).FromXML(Of Boolean)(CBool(MySiteSettings.GetTagged.Value)) GetTaggedData = .Value(Name_GetTagged).FromXML(Of Boolean)(CBool(MySiteSettings.GetTagged.Value))
GetTaggedData_VideoPic = .Value(Name_GetTagged_VideoPic).FromXML(Of Boolean)(CBool(MySiteSettings.GetTagged_VideoPic.Value)) GetTaggedData_VideoPic = .Value(Name_GetTagged_VideoPic).FromXML(Of Boolean)(CBool(MySiteSettings.GetTagged_VideoPic.Value))
TaggedChecked = .Value(Name_TaggedChecked).FromXML(Of Boolean)(False) TaggedChecked = .Value(Name_TaggedChecked).FromXML(Of Boolean)(False)
_NameTrue = .Value(Name_NameTrue) ForceUpdateUserName = .Value(Name_ForceUpdateUserName).FromXML(Of Boolean)(False)
ForceUpdateUserInfo = .Value(Name_ForceUpdateUserInfo).FromXML(Of Boolean)(False)
Else Else
.Add(Name_LastCursor, LastCursor) .Add(Name_LastCursor, LastCursor)
.Add(Name_FirstLoadingDone, FirstLoadingDone.BoolToInteger) .Add(Name_FirstLoadingDone, FirstLoadingDone.BoolToInteger)
@@ -153,7 +151,8 @@ Namespace API.Instagram
.Add(Name_GetTagged_VideoPic, GetTaggedData_VideoPic.BoolToInteger) .Add(Name_GetTagged_VideoPic, GetTaggedData_VideoPic.BoolToInteger)
.Add(Name_PutImageVideoFolder, PutImageVideoFolder.BoolToInteger) .Add(Name_PutImageVideoFolder, PutImageVideoFolder.BoolToInteger)
.Add(Name_TaggedChecked, TaggedChecked.BoolToInteger) .Add(Name_TaggedChecked, TaggedChecked.BoolToInteger)
.Add(Name_NameTrue, _NameTrue) .Add(Name_ForceUpdateUserName, ForceUpdateUserName.BoolToInteger)
.Add(Name_ForceUpdateUserInfo, ForceUpdateUserInfo.BoolToInteger)
End If End If
End With End With
End Sub End Sub
@@ -179,7 +178,9 @@ Namespace API.Instagram
PutImageVideoFolder = .PutImageVideoFolder PutImageVideoFolder = .PutImageVideoFolder
_NameTrue = .UserName NameTrue = .UserName
ForceUpdateUserName = .ForceUpdateUserName
ForceUpdateUserInfo = .ForceUpdateUserInfo
End With End With
End If End If
End Sub End Sub
@@ -197,15 +198,32 @@ Namespace API.Instagram
Private WwwClaimUse As Boolean = True Private WwwClaimUse As Boolean = True
Private E560Thrown As Boolean = False Private E560Thrown As Boolean = False
Friend Err5xx As Integer = -1 Friend Err5xx As Integer = -1
Private _ErrHandling As Integer = -1
Private Property ErrHandling As Integer
Get
Return _ErrHandling
End Get
Set(ByVal ErrCode As Integer)
_ErrHandling = ErrCode
Err5xx = ErrCode
End Set
End Property
Private ErrHandlingLog As Boolean = True
Private ErrHandlingSection As Sections = Sections.Timeline
Private Const ErrHandlingValue As Integer = 100
Private Const ErrHandlingValueStories As Integer = 150
Private Class ExitException : Inherits Exception Private Class ExitException : Inherits Exception
Friend Property Is560 As Boolean = False Friend Property Is560 As Boolean = False
Friend Property IsTokens As Boolean = False Friend Property IsTokens As Boolean = False
Friend Property TokensData As String = String.Empty Friend Property TokensData As String = String.Empty
Friend Shared Sub Throw560(ByRef Source As UserData) Friend Shared Sub Throw560(ByRef Source As UserData)
If Not Source.E560Thrown Then With Source
MyMainLOG = $"{Source.ToStringForLog}: ({IIf(Source.Err5xx > 0, Source.Err5xx, 560)}) Download skipped until next session" If Not .E560Thrown Then
Source.E560Thrown = True If .ErrHandling = -1 Or .ErrHandlingLog Then _
End If MyMainLOG = $"{ .ToStringForLog}: ({IIf(.Err5xx > 0, .Err5xx, 560)}) Download skipped {If(.ErrHandling = -1, "until next session", $"({ .ErrHandlingSection})")}"
.E560Thrown = True
End If
End With
Throw New ExitException With {.Is560 = True} Throw New ExitException With {.Is560 = True}
End Sub End Sub
Friend Shared Sub ThrowTokens(ByRef Source As UserData, ByVal Data As String) Friend Shared Sub ThrowTokens(ByRef Source As UserData, ByVal Data As String)
@@ -225,25 +243,28 @@ Namespace API.Instagram
End If End If
End Get End Get
End Property End Property
Protected Sub LoadSavePostsKV(ByVal Load As Boolean) Friend Overloads Shared Sub LoadSavePostsKV(ByVal Load As Boolean, ByVal fPosts As SFile, ByRef List As List(Of PostKV))
Dim x As XmlFile Dim x As XmlFile
Dim f As SFile = MyFilePostsKV Dim f As SFile = fPosts
If Not f.IsEmptyString Then If Not f.IsEmptyString Then
If Load Then If Load Then
PostsKVIDs.Clear() List.Clear()
x = New XmlFile(f, Protector.Modes.All, False) With {.AllowSameNames = True, .XmlReadOnly = True} x = New XmlFile(f, Protector.Modes.All, False) With {.AllowSameNames = True, .XmlReadOnly = True}
x.LoadData() x.LoadData()
If x.Count > 0 Then PostsKVIDs.ListAddList(x, LAP.IgnoreICopier) If x.Count > 0 Then List.ListAddList(x, LAP.IgnoreICopier)
x.Dispose() x.Dispose()
Else Else
x = New XmlFile With {.AllowSameNames = True} x = New XmlFile With {.AllowSameNames = True}
x.AddRange(PostsKVIDs) x.AddRange(List)
x.Name = "Posts" x.Name = "Posts"
x.Save(f, EDP.SendToLog) x.Save(f, EDP.SendToLog)
x.Dispose() x.Dispose()
End If End If
End If End If
End Sub End Sub
Protected Overloads Sub LoadSavePostsKV(ByVal Load As Boolean)
LoadSavePostsKV(Load, MyFilePostsKV, PostsKVIDs)
End Sub
Protected Overloads Function PostKvExists(ByVal pkv As PostKV) As Boolean Protected Overloads Function PostKvExists(ByVal pkv As PostKV) As Boolean
Return PostKvExists(pkv.ID, False, pkv.Section) OrElse PostKvExists(pkv.Code, True, pkv.Section) Return PostKvExists(pkv.ID, False, pkv.Section) OrElse PostKvExists(pkv.Code, True, pkv.Section)
End Function End Function
@@ -366,6 +387,9 @@ Namespace API.Instagram
Dim errorFound As Boolean = False Dim errorFound As Boolean = False
Try Try
Err5xx = -1 Err5xx = -1
ErrHandling = -1
ErrHandlingLog = True
ErrHandlingSection = Sections.Timeline
_Limit = If(DownloadTopCount, -1) _Limit = If(DownloadTopCount, -1)
_TotalPostsParsed = 0 _TotalPostsParsed = 0
LoadSavePostsKV(True) LoadSavePostsKV(True)
@@ -446,18 +470,25 @@ Namespace API.Instagram
If Not errorFound Then LoadSavePostsKV(False) If Not errorFound Then LoadSavePostsKV(False)
End Try End Try
End Sub End Sub
Private Sub ValidateExtension() Protected Sub ValidateExtension()
Dim tmpList As List(Of UserMedia) = Nothing
Try Try
Const heic$ = "heic" Const heic$ = "heic"
If _TempMediaList.Count > 0 AndAlso _TempMediaList.Exists(Function(mm) mm.File.Extension = heic) Then If _TempMediaList.Count > 0 AndAlso _TempMediaList.Exists(Function(mm) mm.File.Extension = heic) Then
Dim m As UserMedia Dim m As UserMedia
For i% = 0 To _TempMediaList.Count - 1 tmpList = New List(Of UserMedia)
m = _TempMediaList(i) tmpList.ListAddList(_TempMediaList)
_TempMediaList.Clear()
For i% = 0 To tmpList.Count - 1
m = tmpList(i)
_TempMediaList.Add(m)
If m.Type = UTypes.Picture AndAlso Not m.File.Extension.IsEmptyString AndAlso m.File.Extension = heic Then _ If m.Type = UTypes.Picture AndAlso Not m.File.Extension.IsEmptyString AndAlso m.File.Extension = heic Then _
m.File.Extension = "jpg" : _TempMediaList(i) = m m.File.Extension = "jpg" : _TempMediaList.Add(m)
Next Next
tmpList.Clear()
End If End If
Catch ex As Exception Catch ex As Exception
If tmpList.ListExists Then _TempMediaList.Clear() : _TempMediaList.ListAddList(tmpList) : tmpList.Clear()
End Try End Try
End Sub End Sub
Protected Overridable Sub UpdateResponser() Protected Overridable Sub UpdateResponser()
@@ -473,7 +504,7 @@ Namespace API.Instagram
Protected Overrides Sub Responser_ResponseReceived(ByVal Sender As Object, ByVal e As EventArguments.WebDataResponse) Protected Overrides Sub Responser_ResponseReceived(ByVal Sender As Object, ByVal e As EventArguments.WebDataResponse)
Declarations.UpdateResponser(e, Responser, WwwClaimUpdate) Declarations.UpdateResponser(e, Responser, WwwClaimUpdate)
End Sub End Sub
Protected Enum Sections : Timeline : Reels : Tagged : Stories : UserStories : SavedPosts : End Enum Friend Enum Sections : Timeline : Reels : Tagged : Stories : UserStories : SavedPosts : End Enum
Protected Const StoriesFolder As String = "Stories" Protected Const StoriesFolder As String = "Stories"
Private Const TaggedFolder As String = "Tagged" Private Const TaggedFolder As String = "Tagged"
#Region "429 bypass" #Region "429 bypass"
@@ -638,6 +669,8 @@ Namespace API.Instagram
If Not ValidateBaseTokens() Then GetPageTokens() If Not ValidateBaseTokens() Then GetPageTokens()
If Not ValidateBaseTokens(TokensErrData) Then ValidateBaseTokens_Error(TokensErrData) If Not ValidateBaseTokens(TokensErrData) Then ValidateBaseTokens_Error(TokensErrData)
End If End If
If ForceUpdateUserName Then GetUserNameById()
If ForceUpdateUserInfo Then GetUserData()
End If End If
'Create query 'Create query
@@ -720,6 +753,14 @@ Namespace API.Instagram
Select Case Section Select Case Section
Case Sections.Timeline Case Sections.Timeline
With n With n
If If(n("user")?.Count, 0) = 0 And Cursor.IsEmptyString Then
If Not UserNameRequested Then
ForceUpdateUserName = True
Continue Do
Else
UserExists = False
End If
End If
HasNextPage = .Value("more_available").FromXML(Of Boolean)(False) HasNextPage = .Value("more_available").FromXML(Of Boolean)(False)
EndCursor = .Value("next_max_id") EndCursor = .Value("next_max_id")
If If(.Item("items")?.Count, 0) > 0 Then If If(.Item("items")?.Count, 0) > 0 Then
@@ -803,6 +844,11 @@ NextPageBlock:
Throw eex Throw eex
Catch ex As Exception Catch ex As Exception
dValue = ProcessException(ex, Token, $"data downloading error [{URL}]",, Section, False) dValue = ProcessException(ex, Token, $"data downloading error [{URL}]",, Section, False)
If dValue = ErrHandlingValue Then
ExitException.Throw560(Me)
ElseIf dValue = ErrHandlingValueStories Then
Exit Sub
End If
End Try End Try
Loop Loop
Catch jsonNull2 As JsonDocumentException When jsonNull2.State = WebDocumentEventArgs.States.Error And Catch jsonNull2 As JsonDocumentException When jsonNull2.State = WebDocumentEventArgs.States.Error And
@@ -1143,6 +1189,7 @@ NextPageBlock:
#Region "GetUserId, GetUserName" #Region "GetUserId, GetUserName"
Private Sub GetUserData() Private Sub GetUserData()
Dim __idFound As Boolean = False Dim __idFound As Boolean = False
If ForceUpdateUserInfo Then ForceUpdateUserInfo = False : _ForceSaveUserInfo = True
Try Try
ChangeResponserMode(False) ChangeResponserMode(False)
UpdateRequestNumber() UpdateRequestNumber()
@@ -1161,12 +1208,10 @@ NextPageBlock:
If Not eUrl.IsEmptyString AndAlso (descr.IsEmptyString OrElse Not descr.Contains(eUrl)) Then descr.StringAppendLine(eUrl) If Not eUrl.IsEmptyString AndAlso (descr.IsEmptyString OrElse Not descr.Contains(eUrl)) Then descr.StringAppendLine(eUrl)
UserDescriptionUpdate(descr) UserDescriptionUpdate(descr)
Dim f As New SFile With {.Path = DownloadContentDefault_GetRootDir(), .Name = "ProfilePicture", .Extension = "jpg"} Dim f As New SFile With {.Path = DownloadContentDefault_GetRootDir(), .Name = "ProfilePicture", .Extension = "jpg"}
f = SFile.IndexReindex(f)
If Not f.Exists Then If Not f.Exists Then
Dim profilePicture$ = .Value("profile_pic_url_hd") If SimpleDownloadAvatar(.Value("profile_pic_url_hd"), Function(ff) f).IsEmptyString Then _
If profilePicture.IsEmptyString OrElse Not GetWebFile(profilePicture, f, EDP.ReturnValue) Then SimpleDownloadAvatar(.Value("profile_pic_url"), Function(ff) f)
profilePicture = .Value("profile_pic_url")
If Not profilePicture.IsEmptyString Then GetWebFile(profilePicture, f, EDP.ReturnValue)
End If
End If End If
End With End With
End If End If
@@ -1187,6 +1232,7 @@ NextPageBlock:
End Sub End Sub
Private Function GetUserNameById() As Boolean Private Function GetUserNameById() As Boolean
UserNameRequested = True UserNameRequested = True
If ForceUpdateUserName Then ForceUpdateUserName = False : _ForceSaveUserInfo = True
Try Try
If Not ID.IsEmptyString Then If Not ID.IsEmptyString Then
UpdateRequestNumber() UpdateRequestNumber()
@@ -1198,12 +1244,11 @@ NextPageBlock:
If Not newName.IsEmptyString Then If Not newName.IsEmptyString Then
Dim oldName$ = NameTrue Dim oldName$ = NameTrue
If Not newName = oldName Then If Not newName = oldName Then
MyMainLOG = $"{ToStringForLog()}: username changed from '{oldName}' to '{newName}'" Dim uStr$ = $"username changed from '{oldName}' to '{newName}'"
_NameTrue = newName LogError(Nothing, uStr)
Dim descr$ = $"Username changed from '{oldName}' to '{newName}' ({Now.ToStringDate(ADateTime.Formats.BaseDateTime)})!" NameTrue = newName
descr.StringAppendLine(UserDescription) UserDescriptionUpdate(uStr, True, True, True)
UserDescription = descr _ForceSaveUserInfo = True
_ForceSaveUserData = True
End If End If
Return True Return True
End If End If
@@ -1282,20 +1327,15 @@ NextPageBlock:
End If End If
End Sub End Sub
Private Function GetStoriesList() As List(Of String) Private Function GetStoriesList() As List(Of String)
Try UpdateRequestNumber()
UpdateRequestNumber() Dim r$ = Responser.GetResponse($"https://i.instagram.com/api/v1/highlights/{ID}/highlights_tray/",, EDP.ThrowException)
Dim r$ = Responser.GetResponse($"https://i.instagram.com/api/v1/highlights/{ID}/highlights_tray/",, EDP.ThrowException) If Not r.IsEmptyString Then
If Not r.IsEmptyString Then Dim ee As New ErrorsDescriber(EDP.ReturnValue) With {.DeclaredMessage = New MMessage($"{ToStringForLog()}:")}
Dim ee As New ErrorsDescriber(EDP.ReturnValue) With {.DeclaredMessage = New MMessage($"{ToStringForLog()}:")} Using j As EContainer = JsonDocument.Parse(r, ee).XmlIfNothing()("tray").XmlIfNothing
Using j As EContainer = JsonDocument.Parse(r, ee).XmlIfNothing()("tray").XmlIfNothing If j.Count > 0 Then Return j.Select(Function(jj) jj.Value("id").Replace("highlight:", String.Empty)).ListIfNothing
If j.Count > 0 Then Return j.Select(Function(jj) jj.Value("id").Replace("highlight:", String.Empty)).ListIfNothing End Using
End Using End If
End If Return Nothing
Return Nothing
Catch ex As Exception
DownloadingException(ex, "API.Instagram.GetStoriesList", False, Sections.Stories)
Return Nothing
End Try
End Function End Function
#End Region #End Region
#Region "Download content" #Region "Download content"
@@ -1341,11 +1381,26 @@ NextPageBlock:
MyMainLOG = $"Number of requests before error 429: {RequestsCount}" MyMainLOG = $"Number of requests before error 429: {RequestsCount}"
Return 1 Return 1
ElseIf Responser.StatusCode = 560 Or Responser.StatusCode = HttpStatusCode.InternalServerError Then '560, 500 ElseIf Responser.StatusCode = 560 Or Responser.StatusCode = HttpStatusCode.InternalServerError Then '560, 500
MySiteSettings.SkipUntilNextSession = True If Responser.StatusCode = 560 And s = Sections.Stories And MySiteSettings.IgnoreStoriesDownloadingErrors Then
Err5xx = Responser.StatusCode MyMainLOG = $"{ToStringForLog()}: Stories downloading skipped (560)"
Return ErrHandlingValueStories
Else
MySiteSettings.SkipUntilNextSession = True
Err5xx = Responser.StatusCode
End If
ElseIf Responser.StatusCode = -1 And Responser.Status = -1 Then ElseIf Responser.StatusCode = -1 And Responser.Status = -1 Then
MySiteSettings.SkipUntilNextSession = True MySiteSettings.SkipUntilNextSession = True
Err5xx = Responser.StatusCode Err5xx = Responser.StatusCode
ElseIf MySiteSettings.ErrorSpecialHandling(Responser.StatusCode) Then
ErrHandlingLog = MySiteSettings.ErrorSpecialHandling_AddToLog(Responser.StatusCode)
ErrHandling = Responser.StatusCode
ErrHandlingSection = s
Return ErrHandlingValue
ElseIf MySiteSettings.ErrorSpecialHandling(Responser.Status) Then
ErrHandlingLog = MySiteSettings.ErrorSpecialHandling_AddToLog(Responser.Status)
ErrHandling = Responser.Status
ErrHandlingSection = s
Return ErrHandlingValue
Else Else
MyMainLOG = $"Something is wrong. Your credentials may have expired [{CInt(Responser.StatusCode)}/{CInt(Responser.Status)}]: {ToString()} [{s}]" MyMainLOG = $"Something is wrong. Your credentials may have expired [{CInt(Responser.StatusCode)}/{CInt(Responser.Status)}]: {ToString()} [{s}]"
DisableSection(s) DisableSection(s)

View File

@@ -106,7 +106,8 @@ Namespace API.LPSG
End Sub End Sub
Protected Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False, Protected Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False,
Optional ByVal EObj As Object = Nothing) As Integer Optional ByVal EObj As Object = Nothing) As Integer
If Responser.StatusCode = Net.HttpStatusCode.ServiceUnavailable Then '503 If Responser.StatusCode = Net.HttpStatusCode.ServiceUnavailable Or
Responser.StatusCode = Net.HttpStatusCode.Forbidden Then '503, 403
MyMainLOG = $"{ToStringForLog()}: LPSG not available" MyMainLOG = $"{ToStringForLog()}: LPSG not available"
Return 1 Return 1
ElseIf Responser.StatusCode = Net.HttpStatusCode.NotFound Then '404 ElseIf Responser.StatusCode = Net.HttpStatusCode.NotFound Then '404

View File

@@ -15,6 +15,7 @@ Namespace API.Mastodon
<PSetting(Address:=SettingAddress.None)> Friend Overrides Property DownloadModelSearch As Boolean <PSetting(Address:=SettingAddress.None)> Friend Overrides Property DownloadModelSearch As Boolean
<PSetting(Address:=SettingAddress.None)> Friend Overrides Property DownloadModelForceApply As Boolean <PSetting(Address:=SettingAddress.None)> Friend Overrides Property DownloadModelForceApply As Boolean
<PSetting(Address:=SettingAddress.None)> Friend Overrides Property DownloadModelLikes As Boolean <PSetting(Address:=SettingAddress.None)> Friend Overrides Property DownloadModelLikes As Boolean
<PSetting(Address:=SettingAddress.None)> Friend Overrides Property DownloadBroadcasts As Boolean
<PSetting(Address:=SettingAddress.None)> Friend Overrides Property UserName As String <PSetting(Address:=SettingAddress.None)> Friend Overrides Property UserName As String
Friend Sub New(ByVal s As SiteSettings) Friend Sub New(ByVal s As SiteSettings)
MyBase.New(s) MyBase.New(s)

View File

@@ -171,12 +171,12 @@ Namespace API.Mastodon
With DirectCast(User, UserData) With DirectCast(User, UserData)
If UserRelatedToMyDomain.Value Then If UserRelatedToMyDomain.Value Then
If MyDomain.Value = .UserDomain Then If MyDomain.Value = .UserDomain Then
Return $"https://{ .UserDomain}/@{ .TrueName}" Return $"https://{ .UserDomain}/@{ .NameTrue}"
Else Else
Return $"https://{MyDomain.Value}/@{ .TrueName}@{ .UserDomain}" Return $"https://{MyDomain.Value}/@{ .NameTrue}@{ .UserDomain}"
End If End If
Else Else
Return $"https://{ .UserDomain}/@{ .TrueName}" Return $"https://{ .UserDomain}/@{ .NameTrue}"
End If End If
End With End With
End Function End Function

View File

@@ -29,7 +29,6 @@ Namespace API.Mastodon
_UserDomain = d _UserDomain = d
End Set End Set
End Property End Property
Friend Property TrueName As String = String.Empty
Private ReadOnly Property MySettings As SiteSettings Private ReadOnly Property MySettings As SiteSettings
Get Get
Return HOST.Source Return HOST.Source
@@ -52,22 +51,21 @@ Namespace API.Mastodon
Dim l$() = Name.Split("@") Dim l$() = Name.Split("@")
If l.ListExists(2) Then If l.ListExists(2) Then
_UserDomain = l(0) _UserDomain = l(0)
TrueName = l(1) NameTrue = l(1)
Else Else
_UserDomain = MySettings.MyDomain.Value _UserDomain = MySettings.MyDomain.Value
TrueName = Name NameTrue = Name
End If End If
If FriendlyName.IsEmptyString Then FriendlyName = TrueName If FriendlyName.IsEmptyString Then FriendlyName = NameTrue
End If End If
End Sub End Sub
If Loading Then If Loading Then
_UserDomain = Container.Value(Name_UserDomain) _UserDomain = Container.Value(Name_UserDomain)
TrueName = Container.Value(Name_TrueName)
obtainNames.Invoke obtainNames.Invoke
Else Else
obtainNames.Invoke obtainNames.Invoke
Container.Add(Name_UserDomain, _UserDomain) Container.Add(Name_UserDomain, _UserDomain)
Container.Add(Name_TrueName, TrueName) Container.Add(Name_TrueName, NameTrue(True))
Container.Value(Name_FriendlyName) = FriendlyName Container.Value(Name_FriendlyName) = FriendlyName
End If End If
End Sub End Sub
@@ -208,12 +206,12 @@ Namespace API.Mastodon
Dim url$ = $"https://{MyCredentials.Domain}/api/v1/accounts/lookup?acct=" Dim url$ = $"https://{MyCredentials.Domain}/api/v1/accounts/lookup?acct="
If Not UserDomain.IsEmptyString Then If Not UserDomain.IsEmptyString Then
If UserDomain = MyCredentials.Domain Then If UserDomain = MyCredentials.Domain Then
url &= $"@{TrueName}" url &= $"@{NameTrue}"
Else Else
url &= $"@{TrueName}@{UserDomain}" url &= $"@{NameTrue}@{UserDomain}"
End If End If
Else Else
url &= $"@{TrueName}" url &= $"@{NameTrue}"
End If End If
Dim r$ = Responser.GetResponse(url) Dim r$ = Responser.GetResponse(url)
If Not r.IsEmptyString Then If Not r.IsEmptyString Then

View File

@@ -19,6 +19,7 @@ Namespace API.OnlyFans
#Region "Declarations" #Region "Declarations"
#Region "Categories" #Region "Categories"
Private Const CAT_OFS As String = "OF-Scraper support" Private Const CAT_OFS As String = "OF-Scraper support"
Private Const CAT_ERRORS As String = "Errors"
#End Region #End Region
#Region "Options" #Region "Options"
<PropertyOption(ControlText:="Download timeline", ControlToolTip:="Download user timeline", Category:=DN.CAT_UserDefs), PXML, PClonable> <PropertyOption(ControlText:="Download timeline", ControlToolTip:="Download user timeline", Category:=DN.CAT_UserDefs), PXML, PClonable>
@@ -35,12 +36,18 @@ Namespace API.OnlyFans
Private Const HeaderUserID As String = "User-Id" Private Const HeaderUserID As String = "User-Id"
Friend Const HeaderXBC As String = "X-Bc" Friend Const HeaderXBC As String = "X-Bc"
Friend Const HeaderAppToken As String = "App-Token" Friend Const HeaderAppToken As String = "App-Token"
Private Const AppTokenDefault As String = "33d57ade8c02dbc5a333db99ff9ae26a"
<PropertyOption(ControlText:=HeaderUserID, AllowNull:=False, IsAuth:=True), PClonable(Clone:=False)> <PropertyOption(ControlText:=HeaderUserID, AllowNull:=False, IsAuth:=True), PClonable(Clone:=False)>
Friend ReadOnly Property HH_USER_ID As PropertyValue Friend ReadOnly Property HH_USER_ID As PropertyValue
<PropertyOption(ControlText:=HeaderXBC, AllowNull:=False, IsAuth:=True), PClonable(Clone:=False)> <PropertyOption(ControlText:=HeaderXBC, AllowNull:=False, IsAuth:=True), PClonable(Clone:=False)>
Private ReadOnly Property HH_X_BC As PropertyValue Private ReadOnly Property HH_X_BC As PropertyValue
<PropertyOption(ControlText:=HeaderAppToken, AllowNull:=False, IsAuth:=True), PClonable(Clone:=False)> <PropertyOption(ControlText:=HeaderAppToken, AllowNull:=False, IsAuth:=True), PClonable(Clone:=False)>
Private ReadOnly Property HH_APP_TOKEN As PropertyValue Private ReadOnly Property HH_APP_TOKEN As PropertyValue
<PropertyUpdater(NameOf(HH_APP_TOKEN))>
Private Function UpdateAppToken() As Boolean
HH_APP_TOKEN.Value = AppTokenDefault
Return True
End Function
<PropertyOption(ControlText:=HeaderBrowser, ControlToolTip:="Can be null", AllowNull:=True, <PropertyOption(ControlText:=HeaderBrowser, ControlToolTip:="Can be null", AllowNull:=True,
InheritanceName:=SettingsCLS.HEADER_DEF_sec_ch_ua, IsAuth:=True), PClonable, PXML(OnlyForChecked:=True)> InheritanceName:=SettingsCLS.HEADER_DEF_sec_ch_ua, IsAuth:=True), PClonable, PXML(OnlyForChecked:=True)>
Private ReadOnly Property HH_BROWSER As PropertyValue Private ReadOnly Property HH_BROWSER As PropertyValue
@@ -72,6 +79,23 @@ Namespace API.OnlyFans
End If End If
Return String.Empty Return String.Empty
End Function End Function
<PropertyOption(ControlText:="Update cookies during requests",
ControlToolTip:="If unchecked, cookies will not be updated during requests. Initial cookies will always be used.", IsAuth:=True),
PClonable, PXML, HiddenControl>
Friend ReadOnly Property EnableCookiesUpdate As PropertyValue
#End Region
#Region "Errors"
<PClonable, PXML("UpdateRules401")> Private ReadOnly Property UpdateRules401_XML As PropertyValue
<PropertyOption(ControlText:="Try updating rules when you get a 401 error", Category:=CAT_ERRORS), HiddenControl>
Friend ReadOnly Property UpdateRules401 As PropertyValue
Get
If Not DefaultInstance Is Nothing Then
Return DirectCast(DefaultInstance, SiteSettings).UpdateRules401_XML
Else
Return UpdateRules401_XML
End If
End Get
End Property
#End Region #End Region
#Region "OFScraper" #Region "OFScraper"
<PClonable, PXML("OFScraperPath")> Private ReadOnly Property OFScraperPath_XML As PropertyValue <PClonable, PXML("OFScraperPath")> Private ReadOnly Property OFScraperPath_XML As PropertyValue
@@ -98,7 +122,7 @@ Namespace API.OnlyFans
End Property End Property
Friend Const KeyModeDefault_Default As String = "cdrm" Friend Const KeyModeDefault_Default As String = "cdrm"
<PClonable, PXML("KeyModeDefault")> Private ReadOnly Property KeyModeDefault_XML As PropertyValue <PClonable, PXML("KeyModeDefault")> Private ReadOnly Property KeyModeDefault_XML As PropertyValue
<PropertyOption(ControlText:="key-mode-default", Category:=CAT_OFS)> <PropertyOption(ControlText:="key-mode-default", ControlToolTip:="Examples: cdrm, cdrm2, keydb, manual", Category:=CAT_OFS)>
Friend ReadOnly Property KeyModeDefault As PropertyValue Friend ReadOnly Property KeyModeDefault As PropertyValue
Get Get
If Not DefaultInstance Is Nothing Then If Not DefaultInstance Is Nothing Then
@@ -119,6 +143,62 @@ Namespace API.OnlyFans
End If End If
End Get End Get
End Property End Property
<PClonable, PXML("KEYS_Key")> Private ReadOnly Property OFS_KEYS_Key_XML As PropertyValue
<PropertyOption(ControlText:="Private key", ControlToolTip:="Path to the DRM key file 'private_key.pem'", Category:=CAT_OFS)>
Friend ReadOnly Property OFS_KEYS_Key As PropertyValue
Get
If Not DefaultInstance Is Nothing Then
Return DirectCast(DefaultInstance, SiteSettings).OFS_KEYS_Key_XML
Else
Return OFS_KEYS_Key_XML
End If
End Get
End Property
<PClonable, PXML("KEYS_ClientID")> Private ReadOnly Property OFS_KEYS_ClientID_XML As PropertyValue
<PropertyOption(ControlText:="Client ID", ControlToolTip:="Path to the DRM key file 'client_id.bin'", Category:=CAT_OFS)>
Friend ReadOnly Property OFS_KEYS_ClientID As PropertyValue
Get
If Not DefaultInstance Is Nothing Then
Return DirectCast(DefaultInstance, SiteSettings).OFS_KEYS_ClientID_XML
Else
Return OFS_KEYS_ClientID_XML
End If
End Get
End Property
<PropertiesDataChecker({NameOf(KeyModeDefault), NameOf(OFS_KEYS_Key), NameOf(OFS_KEYS_ClientID)})>
Private Function OFS_KEYS_CHECKER(ByVal p As IEnumerable(Of PropertyData)) As Boolean
Const manualMode$ = "manual"
If p.ListExists Then
Dim m$ = String.Empty, k$ = String.Empty, cid$ = String.Empty
For Each pp As PropertyData In p
Select Case pp.Name
Case NameOf(KeyModeDefault) : m = pp.Value
Case NameOf(OFS_KEYS_Key) : k = pp.Value
Case NameOf(OFS_KEYS_ClientID) : cid = pp.Value
Case Else : Throw New ArgumentException($"Property name '{pp.Name}' is not implemented", "Property Name")
End Select
Next
If k.IsEmptyString And cid.IsEmptyString Then
Return True
ElseIf Not k.IsEmptyString And Not cid.IsEmptyString Then
If m = manualMode Then
Return True
Else
Return MsgBoxE({$"You are using key files and have selected '{m}' mode." & vbCr &
$"To use key files, you should use the '{manualMode}' mode" & vbCr &
"Are you sure you want to use this mode?", "Incorrect mode"}, vbExclamation + vbYesNo) = vbYes
End If
End If
Dim t As New MMessage("", "Key missing",, vbCritical)
If k.IsEmptyString Then
t.Text = "'Private key' is missing"
ElseIf cid.IsEmptyString Then
t.Text = "'Client ID' is missing"
End If
If Not t.Text.IsEmptyString Then t.Show()
End If
Return False
End Function
#End Region #End Region
#End Region #End Region
#Region "Initializer" #Region "Initializer"
@@ -151,12 +231,14 @@ Namespace API.OnlyFans
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.AcceptEncoding)) .Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.AcceptEncoding))
HH_USER_ID = New PropertyValue(.Value(HeaderUserID), GetType(String), Sub(v) UpdateHeader(NameOf(HH_USER_ID), v)) HH_USER_ID = New PropertyValue(.Value(HeaderUserID), GetType(String), Sub(v) UpdateHeader(NameOf(HH_USER_ID), v))
HH_X_BC = New PropertyValue(.Value(HeaderXBC), GetType(String), Sub(v) UpdateHeader(NameOf(HH_X_BC), v)) HH_X_BC = New PropertyValue(.Value(HeaderXBC), GetType(String), Sub(v) UpdateHeader(NameOf(HH_X_BC), v))
HH_APP_TOKEN = New PropertyValue(.Value(HeaderAppToken), GetType(String), Sub(v) UpdateHeader(NameOf(HH_APP_TOKEN), v)) HH_APP_TOKEN = New PropertyValue(.Value(HeaderAppToken).IfNullOrEmpty(AppTokenDefault), GetType(String), Sub(v) UpdateHeader(NameOf(HH_APP_TOKEN), v))
HH_BROWSER = New PropertyValue(.Value(HeaderBrowser), GetType(String), Sub(v) UpdateHeader(NameOf(HH_BROWSER), v)) HH_BROWSER = New PropertyValue(.Value(HeaderBrowser), GetType(String), Sub(v) UpdateHeader(NameOf(HH_BROWSER), v))
End With End With
UserAgent = New PropertyValue(IIf(.UserAgentExists, .UserAgent, String.Empty), GetType(String), Sub(v) UpdateHeader(NameOf(UserAgent), v)) UserAgent = New PropertyValue(IIf(.UserAgentExists, .UserAgent, String.Empty), GetType(String), Sub(v) UpdateHeader(NameOf(UserAgent), v))
End With End With
EnableCookiesUpdate = New PropertyValue(False)
DownloadTimeline = New PropertyValue(True) DownloadTimeline = New PropertyValue(True)
DownloadStories = New PropertyValue(True) DownloadStories = New PropertyValue(True)
DownloadHighlights = New PropertyValue(True) DownloadHighlights = New PropertyValue(True)
@@ -177,11 +259,24 @@ Namespace API.OnlyFans
OFScraperMP4decrypt_XML = New PropertyValue(String.Empty, GetType(String)) OFScraperMP4decrypt_XML = New PropertyValue(String.Empty, GetType(String))
KeyModeDefault_XML = New PropertyValue(KeyModeDefault_Default) KeyModeDefault_XML = New PropertyValue(KeyModeDefault_Default)
Keydb_Api_XML = New PropertyValue(String.Empty, GetType(String)) Keydb_Api_XML = New PropertyValue(String.Empty, GetType(String))
OFS_KEYS_Key_XML = New PropertyValue(String.Empty, GetType(String))
OFS_KEYS_ClientID_XML = New PropertyValue(String.Empty, GetType(String))
UpdateRules401_XML = New PropertyValue(False)
UserRegex = RParams.DMS(String.Format(UserRegexDefaultPattern, "onlyfans.com/"), 1, EDP.ReturnValue) UserRegex = RParams.DMS(String.Format(UserRegexDefaultPattern, "onlyfans.com/"), 1, EDP.ReturnValue)
UrlPatternUser = "https://onlyfans.com/{0}" UrlPatternUser = "https://onlyfans.com/{0}"
ImageVideoContains = "onlyfans.com" ImageVideoContains = "onlyfans.com"
End Sub End Sub
Private Const SettingsVersionCurrent As Integer = 1
Friend Overrides Sub EndInit()
If CInt(SettingsVersion.Value) < SettingsVersionCurrent Then
If CStr(HH_APP_TOKEN.Value).IsEmptyString Then HH_APP_TOKEN.Value = AppTokenDefault
EnableCookiesUpdate.Value = False
SettingsVersion.Value = SettingsVersionCurrent
End If
MyBase.EndInit()
End Sub
#End Region #End Region
#Region "GetInstance" #Region "GetInstance"
Friend Overrides Function GetInstance(ByVal What As ISiteSettings.Download) As IPluginContentProvider Friend Overrides Function GetInstance(ByVal What As ISiteSettings.Download) As IPluginContentProvider
@@ -232,7 +327,7 @@ Namespace API.OnlyFans
#Region "GetUserUrl, GetUserPostUrl, UserOptions" #Region "GetUserUrl, GetUserPostUrl, UserOptions"
Friend Const UserPostPattern As String = "https://onlyfans.com/{0}/{1}" Friend Const UserPostPattern As String = "https://onlyfans.com/{0}/{1}"
Friend Overrides Function GetUserUrl(ByVal User As IPluginContentProvider) As String Friend Overrides Function GetUserUrl(ByVal User As IPluginContentProvider) As String
Return String.Format(UrlPatternUser, If(User.ID.IsEmptyString, User.Name, $"u{User.ID}")) Return String.Format(UrlPatternUser, If(User.ID.IsEmptyString, User.NameTrue.IfNullOrEmpty(User.Name), $"u{User.ID}"))
End Function End Function
Friend Overrides Function GetUserPostUrl(ByVal User As UserDataBase, ByVal Media As UserMedia) As String Friend Overrides Function GetUserPostUrl(ByVal User As UserDataBase, ByVal Media As UserMedia) As String
If Not Media.Post.ID.IsEmptyString Then If Not Media.Post.ID.IsEmptyString Then

View File

@@ -99,7 +99,7 @@ Namespace API.OnlyFans
If Not CCookie Is Nothing Then CCookie.Dispose() If Not CCookie Is Nothing Then CCookie.Dispose()
CCookie = Responser.Cookies.Copy CCookie = Responser.Cookies.Copy
Responser.Cookies.Clear() Responser.Cookies.Clear()
AddHandler Responser.ResponseReceived, AddressOf Responser_ResponseReceived If MySettings.EnableCookiesUpdate.Value Then AddHandler Responser.ResponseReceived, AddressOf Responser_ResponseReceived
UpdateCookieHeader() UpdateCookieHeader()
If Not IsSavedPosts Then If Not IsSavedPosts Then
@@ -119,7 +119,7 @@ Namespace API.OnlyFans
End Try End Try
End Sub End Sub
Protected Overrides Sub Responser_ResponseReceived(ByVal Sender As Object, ByVal e As WebDataResponse) Protected Overrides Sub Responser_ResponseReceived(ByVal Sender As Object, ByVal e As WebDataResponse)
If e.CookiesExists Then If e.CookiesExists And CBool(MySettings.EnableCookiesUpdate.Value) Then
CCookie.Update(e.Cookies, CookieKeeper.UpdateModes.ReplaceByNameAll,, EDP.ReturnValue) CCookie.Update(e.Cookies, CookieKeeper.UpdateModes.ReplaceByNameAll,, EDP.ReturnValue)
UpdateCookieHeader() UpdateCookieHeader()
End If End If
@@ -467,16 +467,8 @@ Namespace API.OnlyFans
Dim descr$ = j.Value("about") Dim descr$ = j.Value("about")
If Not descr.IsEmptyString Then descr = descr.Replace(brTag, String.Empty) If Not descr.IsEmptyString Then descr = descr.Replace(brTag, String.Empty)
UserDescriptionUpdate(descr) UserDescriptionUpdate(descr)
Dim a As Action(Of String) = Sub(ByVal address As String) SimpleDownloadAvatar(j.Value("avatar"))
If Not address.IsEmptyString Then SimpleDownloadAvatar(j.Value("header"))
Dim f As SFile = address
f.Separator = "\"
f.Path = DownloadContentDefault_GetRootDir()
If Not f.Exists Then GetWebFile(address, f, EDP.None)
End If
End Sub
a.Invoke(j.Value("avatar"))
a.Invoke(j.Value("header"))
End If End If
End Using End Using
End If End If
@@ -612,7 +604,7 @@ Namespace API.OnlyFans
'#If DEBUG Then '#If DEBUG Then
'Debug.WriteLine(command) 'Debug.WriteLine(command)
'#End If '#End If
Using b As New TokenBatch(Token) : b.Execute(command) : End Using Using b As New TokenBatch(Token) With {.DebugMode = False} : b.Execute(command) : End Using
Return SFile.GetFiles(conf, "*.mp4", IO.SearchOption.AllDirectories, EDP.ReturnValue) Return SFile.GetFiles(conf, "*.mp4", IO.SearchOption.AllDirectories, EDP.ReturnValue)
End If End If
Return Nothing Return Nothing
@@ -623,7 +615,13 @@ Namespace API.OnlyFans
Private Function OFS_CreateConfig() As SFile Private Function OFS_CreateConfig() As SFile
Try Try
Const confMainPattern$ = "{0}"": ""([^""]*)""" Const confMainPattern$ = "{0}"": ""([^""]*)"""
Const confMainPattern_Keys$ = "{0}"": ([^,]*)"
Const confMainPatternRulesManual$ = "DYNAMIC_RULE"": (""[^""]*"")" Const confMainPatternRulesManual$ = "DYNAMIC_RULE"": (""[^""]*"")"
Const m1 As Byte = 0 'not rules
Const m2 As Byte = 1 'rules
Const m3 As Byte = 2 'keys
If OFSCache Is Nothing Then OFSCache = If(IsSingleObjectDownload, Settings.Cache.NewInstance, CreateCache()) If OFSCache Is Nothing Then OFSCache = If(IsSingleObjectDownload, Settings.Cache.NewInstance, CreateCache())
Dim currentCache As CacheKeeper = OFSCache.NewInstance Dim currentCache As CacheKeeper = OFSCache.NewInstance
currentCache.Validate() currentCache.Validate()
@@ -637,35 +635,47 @@ Namespace API.OnlyFans
CType(Function(input) replaceValue, Func(Of String, String)), String.Empty, EDP.ReturnValue) CType(Function(input) replaceValue, Func(Of String, String)), String.Empty, EDP.ReturnValue)
Dim ff As SFile Dim ff As SFile
configText = f.GetText configText = f.GetText
Dim updateConf As Action(Of String, String, Boolean) = Dim updateConf As Action(Of String, String, Byte) =
Sub(ByVal patternValue As String, ByVal __replaceValue As String, ByVal __isRules As Boolean) Sub(ByVal patternValue As String, ByVal __replaceValue As String, ByVal mode As Byte)
rp.Pattern = String.Format(IIf(__isRules, confMainPatternRulesManual, confMainPattern), patternValue) Select Case mode
Case m1 : rp.Pattern = String.Format(confMainPattern, patternValue)
Case m2 : rp.Pattern = String.Format(confMainPatternRulesManual, patternValue)
Case m3 : rp.Pattern = String.Format(confMainPattern_Keys, patternValue) : __replaceValue = $"""{__replaceValue}"""
Case Else : Throw New ArgumentException($"Mode '{mode}' is not implemented", "mode")
End Select
rp.Nothing = configText rp.Nothing = configText
replaceValue = __replaceValue replaceValue = __replaceValue
configText = RegexReplace(configText, rp) configText = RegexReplace(configText, rp)
End Sub End Sub
If Not configText.IsEmptyString Then If Not configText.IsEmptyString Then
updateConf("save_location", cacheRoot.PathNoSeparator.Replace("\", "/"), False) updateConf("save_location", cacheRoot.PathNoSeparator.Replace("\", "/"), m1)
If ACheck(MySettings.OFScraperMP4decrypt.Value) Then If ACheck(MySettings.OFScraperMP4decrypt.Value) Then
ff = CStr(MySettings.OFScraperMP4decrypt.Value) ff = CStr(MySettings.OFScraperMP4decrypt.Value)
If ff.Exists Then updateConf("mp4decrypt", ff.ToString.Replace("\", "/"), False) If ff.Exists Then updateConf("mp4decrypt", ff.ToString.Replace("\", "/"), m1)
End If End If
If Settings.FfmpegFile.Exists Then updateConf("ffmpeg", Settings.FfmpegFile.File.ToString.Replace("\", "/"), False) If Settings.FfmpegFile.Exists Then updateConf("ffmpeg", Settings.FfmpegFile.File.ToString.Replace("\", "/"), m1)
updateConf("key-mode-default", CStr(MySettings.KeyModeDefault.Value).IfNullOrEmpty(SiteSettings.KeyModeDefault_Default), False)
updateConf("keydb_api", CStr(MySettings.Keydb_Api.Value), False) updateConf("key-mode-default", CStr(MySettings.KeyModeDefault.Value).IfNullOrEmpty(SiteSettings.KeyModeDefault_Default), m1)
updateConf("keydb_api", CStr(MySettings.Keydb_Api.Value), m1)
If Not CStr(MySettings.OFS_KEYS_Key.Value).IsEmptyString And Not CStr(MySettings.OFS_KEYS_ClientID.Value).IsEmptyString Then
updateConf("private-key", CStr(MySettings.OFS_KEYS_Key.Value).Replace("\", "/"), m3)
updateConf("client-id", CStr(MySettings.OFS_KEYS_ClientID.Value).Replace("\", "/"), m3)
End If
If Rules.RulesReplaceConfig Then If Rules.RulesReplaceConfig Then
If Rules.RulesConfigManualMode Then If Rules.RulesConfigManualMode Then
updateConf(DynamicRulesEnv.DynamicRulesConfig_Mode_NodeName, "manual", False) updateConf(DynamicRulesEnv.DynamicRulesConfig_Mode_NodeName, "manual", m1)
configText = configText.Replace(DynamicRulesEnv.DynamicRulesConfigNodeName_URL, DynamicRulesEnv.DynamicRulesConfigNodeName_RULES) configText = configText.Replace(DynamicRulesEnv.DynamicRulesConfigNodeName_URL, DynamicRulesEnv.DynamicRulesConfigNodeName_RULES)
updateConf(DynamicRulesEnv.DynamicRulesConfigNodeName_RULES, Rules.CurrentContainerRulesText, True) updateConf(DynamicRulesEnv.DynamicRulesConfigNodeName_RULES, Rules.CurrentContainerRulesText, m2)
Else Else
Dim confUrlNode$ = If(Rules.RulesConstants.ContainsKey(DynamicRulesEnv.DynamicRulesConfigNodeName_URL_CONST_NAME), Dim confUrlNode$ = If(Rules.RulesConstants.ContainsKey(DynamicRulesEnv.DynamicRulesConfigNodeName_URL_CONST_NAME),
Rules.RulesConstants(DynamicRulesEnv.DynamicRulesConfigNodeName_URL_CONST_NAME), Rules.RulesConstants(DynamicRulesEnv.DynamicRulesConfigNodeName_URL_CONST_NAME),
DynamicRulesEnv.DynamicRulesConfigNodeName_URL) DynamicRulesEnv.DynamicRulesConfigNodeName_URL)
updateConf(DynamicRulesEnv.DynamicRulesConfigNodeName_URL, Rules.CurrentRule.UrlRaw, False) updateConf(DynamicRulesEnv.DynamicRulesConfigNodeName_URL, Rules.CurrentRule.UrlRaw, m1)
configText = configText.Replace(DynamicRulesEnv.DynamicRulesConfigNodeName_URL, confUrlNode) configText = configText.Replace(DynamicRulesEnv.DynamicRulesConfigNodeName_URL, confUrlNode)
If Rules.RulesConstants.ContainsKey(DynamicRulesEnv.DynamicRulesConfig_Mode_NodeName) Then _ If Rules.RulesConstants.ContainsKey(DynamicRulesEnv.DynamicRulesConfig_Mode_NodeName) Then _
updateConf(DynamicRulesEnv.DynamicRulesConfig_Mode_NodeName, Rules.RulesConstants(DynamicRulesEnv.DynamicRulesConfig_Mode_NodeName), False) updateConf(DynamicRulesEnv.DynamicRulesConfig_Mode_NodeName, Rules.RulesConstants(DynamicRulesEnv.DynamicRulesConfig_Mode_NodeName), m1)
End If End If
End If End If
f = currentCache f = currentCache
@@ -760,14 +770,20 @@ Namespace API.OnlyFans
Private _DownloadingException_AuthFileUpdate As Boolean = False Private _DownloadingException_AuthFileUpdate As Boolean = False
Protected Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False, Protected Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False,
Optional ByVal EObj As Object = Nothing) As Integer Optional ByVal EObj As Object = Nothing) As Integer
If Responser.StatusCode = Net.HttpStatusCode.BadRequest Then '400 If Responser.StatusCode = Net.HttpStatusCode.BadRequest Or
(Responser.StatusCode = Net.HttpStatusCode.Unauthorized And CBool(MySettings.UpdateRules401.Value)) Then '400, [401]
If Not _DownloadingException_AuthFileUpdate AndAlso Rules.Update(True) Then If Not _DownloadingException_AuthFileUpdate AndAlso Rules.Update(True) Then
_DownloadingException_AuthFileUpdate = True _DownloadingException_AuthFileUpdate = True
Return 2 Return 2
Else Else
MySettings.SessionAborted = True MySettings.SessionAborted = True
MyMainLOG = $"{ToStringForLog()} [{CInt(Responser.StatusCode)}]: OnlyFans credentials expired" MyMainLOG = $"{ToStringForLog()} [{CInt(Responser.StatusCode)}]: OnlyFans credentials expired"
Return 1 If Responser.StatusCode = Net.HttpStatusCode.BadRequest Then
Return 1
Else
MyMainLOG = $"{ToStringForLog()}: Rules updated (401)"
Return 3
End If
End If End If
ElseIf Responser.StatusCode = Net.HttpStatusCode.NotFound Then '404 ElseIf Responser.StatusCode = Net.HttpStatusCode.NotFound Then '404
UserExists = False UserExists = False

View File

@@ -7,9 +7,18 @@
' This program is distributed in the hope that it will be useful, ' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY ' but WITHOUT ANY WARRANTY
Imports System.Globalization Imports System.Globalization
Imports System.Text.RegularExpressions
Imports PersonalUtilities.Tools.Web.Clients
Imports PersonalUtilities.Functions.RegularExpressions
Namespace API.Pinterest Namespace API.Pinterest
Friend Module Declarations Friend Module Declarations
Friend ReadOnly DateProvider As ADateTime = GetDateProvider() Friend ReadOnly DateProvider As ADateTime = GetDateProvider()
Friend ReadOnly PwsHeader As New HttpHeader("x-pinterest-pws-handler", "www/[username]/pins.js")
Friend ReadOnly GdlUrlPattern As RParams = RParams.DM(Base.GDL.GDLBatch.UrlLibStart.Replace("[", "\[").Replace("]", "\]") &
"([^""]+?)""(GET [^""]+)""", 0, EDP.ReturnValue)
Friend ReadOnly SubBoardRegEx As RParams = RParams.DMS("\[pinterest\]\[debug\] Using PinterestSectionExtractor for '[^']+id:(\d+)'", 1,
RegexOptions.IgnoreCase, EDP.ReturnValue)
Friend ReadOnly BoardInfoRootNode As String() = {"resource_response", "data"}
Private Function GetDateProvider() As ADateTime Private Function GetDateProvider() As ADateTime
Dim n As DateTimeFormatInfo = CultureInfo.GetCultureInfo("en-us").DateTimeFormat.Clone Dim n As DateTimeFormatInfo = CultureInfo.GetCultureInfo("en-us").DateTimeFormat.Clone
n.FullDateTimePattern = "ddd dd MMM yyyy HH:mm:ss" n.FullDateTimePattern = "ddd dd MMM yyyy HH:mm:ss"

View File

@@ -0,0 +1,20 @@
' Copyright (C) Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify
' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or
' (at your option) any later version.
'
' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY
Imports SCrawler.Plugin.Attributes
Namespace API.Pinterest
Friend Class EditorExchangeOptions
<PSetting(Caption:="Get Sub-Boards", ToolTip:="Extract the Sub-Boards from the boards and download them")>
Friend Property ExtractSubBoards As Boolean = True
Friend Sub New(ByVal u As UserData)
ExtractSubBoards = u.ExtractSubBoards
End Sub
Friend Sub New()
End Sub
End Class
End Namespace

View File

@@ -11,7 +11,7 @@ Imports SCrawler.Plugin
Imports SCrawler.Plugin.Attributes Imports SCrawler.Plugin.Attributes
Imports PersonalUtilities.Functions.RegularExpressions Imports PersonalUtilities.Functions.RegularExpressions
Namespace API.Pinterest Namespace API.Pinterest
<Manifest("AndyProgram_Pinterest"), SavedPosts, SeparatedTasks> <Manifest("AndyProgram_Pinterest"), SavedPosts, SeparatedTasks, SpecialForm(False)>
Friend Class SiteSettings : Inherits SiteSettingsBase Friend Class SiteSettings : Inherits SiteSettingsBase
#Region "Declarations" #Region "Declarations"
<PropertyOption(ControlText:=DeclaredNames.ConcurrentDownloadsCaption, <PropertyOption(ControlText:=DeclaredNames.ConcurrentDownloadsCaption,
@@ -30,7 +30,7 @@ Namespace API.Pinterest
MyConcurrentDownloadsProvider = New ConcurrentDownloadsProvider MyConcurrentDownloadsProvider = New ConcurrentDownloadsProvider
CheckNetscapeCookiesOnEndInit = True CheckNetscapeCookiesOnEndInit = True
UseNetscapeCookies = True UseNetscapeCookies = True
UserRegex = RParams.DMS("https?://w{0,3}.?[^/]*?.?pinterest.com/([^/]+)/?(?(_)|([^/]*))", 0, RegexReturn.ListByMatch, EDP.ReturnValue) UserRegex = RParams.DMS("https?://w{0,3}.?[^/]*?.?pinterest.com/([^/]+)/?(?(_)|([^/]*))/?([^/\?]*)", 0, RegexReturn.ListByMatch, EDP.ReturnValue)
End Sub End Sub
#End Region #End Region
#Region "GetInstance, Available" #Region "GetInstance, Available"
@@ -41,13 +41,14 @@ Namespace API.Pinterest
Return Settings.GalleryDLFile.Exists And (Not What = ISiteSettings.Download.SavedPosts OrElse ACheck(SavedPostsUserName.Value)) Return Settings.GalleryDLFile.Exists And (Not What = ISiteSettings.Download.SavedPosts OrElse ACheck(SavedPostsUserName.Value))
End Function End Function
#End Region #End Region
#Region "IsMyUser, IsMyImageVideo, GetUserUrl, GetUserPostUrl" #Region "IsMyUser, IsMyImageVideo, GetUserUrl, GetUserPostUrl, UserOptions"
Friend Overrides Function IsMyUser(ByVal UserURL As String) As ExchangeOptions Friend Overrides Function IsMyUser(ByVal UserURL As String) As ExchangeOptions
If Not UserURL.IsEmptyString Then If Not UserURL.IsEmptyString Then
Dim l As List(Of String) = RegexReplace(UserURL, UserRegex) Dim l As List(Of String) = RegexReplace(UserURL, UserRegex)
If l.ListExists(3) Then If l.ListExists(3) Then
Dim n$ = l(1) Dim n$ = l(1)
If Not l(2).IsEmptyString Then n &= $"@{l(2)}" If Not l(2).IsEmptyString Then n &= $"@{l(2)}"
If l.Count > 3 AndAlso Not l(3).IsEmptyString Then n &= $"@{l(3)}"
Return New ExchangeOptions(Site, n) With {.Exists = True} Return New ExchangeOptions(Site, n) With {.Exists = True}
End If End If
End If End If
@@ -71,6 +72,12 @@ Namespace API.Pinterest
Return String.Empty Return String.Empty
End If End If
End Function End Function
Friend Overrides Sub UserOptions(ByRef Options As Object, ByVal OpenForm As Boolean)
If Options Is Nothing Then Options = New EditorExchangeOptions
If OpenForm Then
Using f As New InternalSettingsForm(Options, Me, False) : f.ShowDialog() : End Using
End If
End Sub
#End Region #End Region
End Class End Class
End Namespace End Namespace

View File

@@ -7,10 +7,12 @@
' This program is distributed in the hope that it will be useful, ' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY ' but WITHOUT ANY WARRANTY
Imports System.Threading Imports System.Threading
Imports System.Text.RegularExpressions
Imports SCrawler.API.Base Imports SCrawler.API.Base
Imports SCrawler.API.Base.GDL Imports SCrawler.API.Base.GDL
Imports SCrawler.API.YouTube.Objects Imports SCrawler.API.YouTube.Objects
Imports PersonalUtilities.Functions.XML Imports PersonalUtilities.Functions.XML
Imports PersonalUtilities.Functions.RegularExpressions
Imports PersonalUtilities.Tools.Web.Documents.JSON Imports PersonalUtilities.Tools.Web.Documents.JSON
Namespace API.Pinterest Namespace API.Pinterest
Friend Class UserData : Inherits UserDataBase Friend Class UserData : Inherits UserDataBase
@@ -18,6 +20,8 @@ Namespace API.Pinterest
Private Const Name_IsUser As String = "IsUser" Private Const Name_IsUser As String = "IsUser"
Private Const Name_TrueUserName As String = "TrueUserName" Private Const Name_TrueUserName As String = "TrueUserName"
Private Const Name_TrueBoardName As String = "TrueBoardName" Private Const Name_TrueBoardName As String = "TrueBoardName"
Private Const Name_ExtractSubBoards As String = "ExtractSubBoards"
Private Const Name_IsSubBoard As String = "IsSubBoard"
#End Region #End Region
#Region "Structures" #Region "Structures"
Private Structure BoardInfo Private Structure BoardInfo
@@ -38,6 +42,8 @@ Namespace API.Pinterest
Friend Property TrueUserName As String Friend Property TrueUserName As String
Friend Property TrueBoardName As String Friend Property TrueBoardName As String
Friend Property IsUser_NB As Boolean Friend Property IsUser_NB As Boolean
Private Property IsSubBoard As Boolean = False
Friend Property ExtractSubBoards As Boolean = True
Private Const BoardLabelName As String = "Board" Private Const BoardLabelName As String = "Board"
Friend Overrides ReadOnly Property SpecialLabels As IEnumerable(Of String) Friend Overrides ReadOnly Property SpecialLabels As IEnumerable(Of String)
Get Get
@@ -45,14 +51,18 @@ Namespace API.Pinterest
End Get End Get
End Property End Property
#End Region #End Region
#Region "Load" #Region "Load, Exchange"
Private Function ReconfUserName() As Boolean Private Function ReconfUserName() As Boolean
If TrueUserName.IsEmptyString Then If TrueUserName.IsEmptyString Then
Dim n$() = Name.Split("@") Dim n$() = Name.Split("@")
If n.ListExists Then If n.ListExists Then
TrueUserName = n(0) TrueUserName = n(0)
IsUser_NB = True IsUser_NB = True
If n.Length > 1 Then TrueBoardName = n(1) : IsUser_NB = False If n.Length > 1 Then
TrueBoardName = n(1)
If n.Length > 2 AndAlso Not n(2).IsEmptyString Then TrueBoardName &= $"/{n(2)}" : IsSubBoard = True
IsUser_NB = False
End If
If Not IsSavedPosts And Not IsSingleObjectDownload Then If Not IsSavedPosts And Not IsSingleObjectDownload Then
Dim l$ = IIf(IsUser_NB, UserLabelName, BoardLabelName) Dim l$ = IIf(IsUser_NB, UserLabelName, BoardLabelName)
Settings.Labels.Add(l) Settings.Labels.Add(l)
@@ -70,15 +80,25 @@ Namespace API.Pinterest
TrueUserName = .Value(Name_TrueUserName) TrueUserName = .Value(Name_TrueUserName)
TrueBoardName = .Value(Name_TrueBoardName) TrueBoardName = .Value(Name_TrueBoardName)
IsUser_NB = .Value(Name_IsUser).FromXML(Of Boolean)(False) IsUser_NB = .Value(Name_IsUser).FromXML(Of Boolean)(False)
ExtractSubBoards = .Value(Name_ExtractSubBoards).FromXML(Of Boolean)(True)
IsSubBoard = .Value(Name_IsSubBoard).FromXML(Of Boolean)(False)
ReconfUserName() ReconfUserName()
Else Else
If ReconfUserName() Then .Value(Name_LabelsName) = LabelsString If ReconfUserName() Then .Value(Name_LabelsName) = LabelsString
.Add(Name_TrueUserName, TrueUserName) .Add(Name_TrueUserName, TrueUserName)
.Add(Name_TrueBoardName, TrueBoardName) .Add(Name_TrueBoardName, TrueBoardName)
.Add(Name_IsUser, IsUser_NB.BoolToInteger) .Add(Name_IsUser, IsUser_NB.BoolToInteger)
.Add(Name_ExtractSubBoards, ExtractSubBoards.BoolToInteger)
.Add(Name_IsSubBoard, IsSubBoard.BoolToInteger)
End If End If
End With End With
End Sub End Sub
Friend Overrides Function ExchangeOptionsGet() As Object
Return New EditorExchangeOptions(Me)
End Function
Friend Overrides Sub ExchangeOptionsSet(ByVal Obj As Object)
If Not Obj Is Nothing AndAlso TypeOf Obj Is EditorExchangeOptions Then ExtractSubBoards = DirectCast(Obj, EditorExchangeOptions).ExtractSubBoards
End Sub
#End Region #End Region
#Region "Download overrides" #Region "Download overrides"
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken) Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
@@ -125,6 +145,19 @@ Namespace API.Pinterest
End Sub End Sub
#End Region #End Region
#Region "Get boards, images" #Region "Get boards, images"
Private Function GetBoardInfo(ByVal e As EContainer) As BoardInfo
If Not e Is Nothing Then
Dim b As New BoardInfo With {
.URL = e.Value("url"),
.Title = TitleHtmlConverter(e.Value("name")).IfNullOrEmpty(TitleHtmlConverter(e.Value("title"))),
.ID = e.Value("id")
}
If Not b.URL.IsEmptyString Then b.URL = $"https://www.pinterest.com/{b.URL.StringTrimStart("/").StringTrimEnd("/")}/"
Return b
Else
Return Nothing
End If
End Function
Private Function GetBoards(ByVal Token As CancellationToken) As List(Of BoardInfo) Private Function GetBoards(ByVal Token As CancellationToken) As List(Of BoardInfo)
Dim URL$ = $"https://www.pinterest.com/{TrueUserName}/" Dim URL$ = $"https://www.pinterest.com/{TrueUserName}/"
Try Try
@@ -132,9 +165,9 @@ Namespace API.Pinterest
Dim b As BoardInfo Dim b As BoardInfo
Dim r$ Dim r$
Dim j As EContainer, jj As EContainer Dim j As EContainer, jj As EContainer
Dim rootNode$() = {"resource_response", "data"}
Dim jErr As New ErrorsDescriber(EDP.SendToLog + EDP.ReturnValue) Dim jErr As New ErrorsDescriber(EDP.SendToLog + EDP.ReturnValue)
Dim urls As List(Of String) = GetDataFromGalleryDL(URL, True, Token) Dim urls As New List(Of String)
urls.ListAddList(GetDataFromGalleryDL(URL, True, Token), LNC)
If urls.ListExists Then urls.RemoveAll(Function(__url) Not __url.Contains("BoardsResource/get/")) If urls.ListExists Then urls.RemoveAll(Function(__url) Not __url.Contains("BoardsResource/get/"))
If urls.ListExists Then If urls.ListExists Then
ProgressPre.ChangeMax(urls.Count) ProgressPre.ChangeMax(urls.Count)
@@ -145,17 +178,10 @@ Namespace API.Pinterest
If Not r.IsEmptyString Then If Not r.IsEmptyString Then
j = JsonDocument.Parse(r, jErr) j = JsonDocument.Parse(r, jErr)
If Not j Is Nothing Then If Not j Is Nothing Then
If If(j(rootNode)?.Count, 0) > 0 Then If If(j(BoardInfoRootNode)?.Count, 0) > 0 Then
For Each jj In j(rootNode) For Each jj In j(BoardInfoRootNode)
b = New BoardInfo With { b = GetBoardInfo(jj)
.URL = jj.Value("url"), If Not b.URL.IsEmptyString Then boards.Add(b)
.Title = TitleHtmlConverter(jj.Value("name")),
.ID = jj.Value("id")
}
If Not b.URL.IsEmptyString Then
b.URL = $"https://www.pinterest.com/{b.URL.StringTrimStart("/").StringTrimEnd("/")}/"
boards.Add(b)
End If
Next Next
End If End If
j.Dispose() j.Dispose()
@@ -170,92 +196,156 @@ Namespace API.Pinterest
End Try End Try
End Function End Function
Private Sub DownloadBoardImages(ByRef Board As BoardInfo, ByVal Token As CancellationToken) Private Sub DownloadBoardImages(ByRef Board As BoardInfo, ByVal Token As CancellationToken)
Dim bUrl$ = String.Empty Dim bUrl As GDLURL = Nothing
Try Try
Dim r$ Dim r$
Dim j As EContainer, jj As EContainer Dim j As EContainer, jj As EContainer
Dim u As UserMedia Dim u As UserMedia
Dim folder$ = If(IsUser_NB, Board.Title.IfNullOrEmpty(Board.ID), String.Empty) Dim __getBoardTitle As Func(Of BoardInfo, String) = Function(__board) __board.Title.IfNullOrEmpty(__board.ID)
Dim folderDef$ = If(IsUser_NB, __getBoardTitle(Board), String.Empty)
Dim titleExists As Boolean = Not Board.Title.IsEmptyString Dim titleExists As Boolean = Not Board.Title.IsEmptyString
Dim i% = -1 Dim i% = -1
Dim jErr As New ErrorsDescriber(EDP.SendToLog + EDP.ReturnValue) Dim jErr As New ErrorsDescriber(EDP.SendToLog + EDP.ReturnValue)
Dim rootNode$() = {"resource_response", "data"} Dim rErr As New ErrorsDescriber(EDP.ReturnValue)
Dim images As List(Of Sizes) Dim images As List(Of Sizes)
Dim imgSelector As Func(Of EContainer, Sizes) = Function(img) New Sizes(img.Value("width"), img.Value("url")) Dim imgSelector As Func(Of EContainer, Sizes) = Function(img) New Sizes(img.Value("width"), img.Value("url"))
Dim fullData As Predicate(Of EContainer) = Function(e) e.Count > 5 Dim fullData As Predicate(Of EContainer) = Function(e) e.Count > 5
Dim l As List(Of String) = GetDataFromGalleryDL(Board.URL, False, Token) Dim subBoard As BoardInfo = Nothing
If l.ListExists Then l.RemoveAll(Function(ll) Not ll.Contains("BoardFeedResource/get/")) Dim subBoardAppender As Func(Of String) = Function() _
If(Not __getBoardTitle(subBoard).IsEmptyString,
$"{IIf(folderDef.IsEmptyString, String.Empty, "\")}{__getBoardTitle(subBoard)}",
String.Empty)
Dim __getSubBoard As Func(Of Boolean) = Function() ExtractSubBoards Or (IsSubBoard And i = -1)
Dim sbCount% = 0
Dim __getBoardInfo As Action(Of GDLURL) = Sub(ByVal sb As GDLURL)
sbCount += 1
r = Responser.GetResponse(sb.URL,, rErr)
If Not r.IsEmptyString Then
Using jsb As EContainer = JsonDocument.Parse(r, jErr)
If jsb.ListExists Then subBoard = GetBoardInfo(jsb(BoardInfoRootNode)) : Exit Sub
End Using
End If
subBoard = Nothing
End Sub
Dim l As List(Of GDLURL) = GetDataFromGalleryDL(Board.URL, False, Token)
If l.ListExists Then l.RemoveAll(Function(ll) ll.URL.IsEmptyString)
If l.ListExists Then If l.ListExists Then
Responser.Headers.Add(PwsHeader)
ProgressPre.ChangeMax(l.Count) ProgressPre.ChangeMax(l.Count)
For Each bUrl In l For Each bUrl In l
ProgressPre.Perform() ProgressPre.Perform()
ThrowAny(Token) ThrowAny(Token)
r = Responser.GetResponse(bUrl,, EDP.ReturnValue)
If Not r.IsEmptyString Then
j = JsonDocument.Parse(r, jErr)
If Not j Is Nothing Then
If If(j(rootNode)?.Count, 0) > 0 Then
ProgressPre.ChangeMax(j(rootNode).Count)
For Each jj In j(rootNode)
ProgressPre.Perform()
With jj
If .Contains("images") Then
images = .Item("images").Select(imgSelector).ToList
If images.Count > 0 Then
images.Sort()
i += 1
u = New UserMedia(images(0).Data) With {
.Post = New UserPost(jj.Value("id"), AConvert(Of Date)(jj.Value("created_at"), DateProvider, Nothing)),
.Type = UserMedia.Types.Picture,
.SpecialFolder = folder
}
If i = 0 Then
If Board.Title.IsEmptyString Or Board.ID.IsEmptyString Then
Board.Title = TitleHtmlConverter(.Value({"board"}, "name"))
Board.ID = .Value({"board"}, "id")
End If
Board.UserID = .Value({"board", "owner"}, "id")
Board.UserTitle = TitleHtmlConverter(.Value({"board", "owner"}, "full_name"))
If Not titleExists And IsUser_NB Then
If Not Board.Title.IsEmptyString Then
folder = Board.Title
ElseIf Not Board.ID.IsEmptyString Then
folder = Board.ID
End If
u.SpecialFolder = folder
End If
End If
If Not u.URL.IsEmptyString Then If bUrl.URL.Contains("BoardFeedResource/get/") Or (bUrl.URL.Contains("BoardSectionPinsResource/get/") And (ExtractSubBoards Or (IsSubBoard And sbCount = 1))) Then
If u.Post.Date.HasValue Then r = Responser.GetResponse(bUrl.URL,, rErr)
Select Case CheckDatesLimit(u.Post.Date.Value, Nothing) If Not r.IsEmptyString Then
Case DateResult.Skip : _TempPostsList.ListAddValue(u.Post.ID, LNC) : Continue For j = JsonDocument.Parse(r, jErr)
Case DateResult.Exit : Exit Sub If Not j Is Nothing Then
End Select If If(j(BoardInfoRootNode)?.Count, 0) > 0 Then
ProgressPre.ChangeMax(j(BoardInfoRootNode).Count)
For Each jj In j(BoardInfoRootNode)
ProgressPre.Perform()
With jj
If .Contains("images") Then
images = .Item("images").Select(imgSelector).ToList
If images.Count > 0 Then
images.Sort()
i += 1
u = New UserMedia(images(0).Data) With {
.Post = New UserPost(jj.Value("id"), AConvert(Of Date)(jj.Value("created_at"), DateProvider, Nothing)),
.Type = UserMedia.Types.Picture,
.SpecialFolder = folderDef & subBoardAppender.Invoke
}
If i = 0 Then
If Board.Title.IsEmptyString Or Board.ID.IsEmptyString Then
Board.Title = TitleHtmlConverter(.Value({"board"}, "name"))
Board.ID = .Value({"board"}, "id")
End If
Board.UserID = .Value({"board", "owner"}, "id")
Board.UserTitle = TitleHtmlConverter(.Value({"board", "owner"}, "full_name"))
If Not titleExists And IsUser_NB Then
folderDef = Board.Title.IfNullOrEmpty(Board.ID).IfNullOrEmpty(folderDef)
u.SpecialFolder = folderDef & subBoardAppender.Invoke
End If
End If End If
If Not _TempPostsList.Contains(u.Post.ID) Then
_TempPostsList.ListAddValue(u.Post.ID, LNC) If Not u.URL.IsEmptyString Then
_TempMediaList.ListAddValue(u, LNC) If u.Post.Date.HasValue Then
Else Select Case CheckDatesLimit(u.Post.Date.Value, Nothing)
Exit For Case DateResult.Skip : _TempPostsList.ListAddValue(u.Post.ID, LNC) : Continue For
Case DateResult.Exit : Exit Sub
End Select
End If
If Not _TempPostsList.Contains(u.Post.ID) Then
_TempPostsList.ListAddValue(u.Post.ID, LNC)
_TempMediaList.ListAddValue(u, LNC)
Else
Exit For
End If
End If End If
End If End If
End If End If
End If End With
End With Next
Next End If
j.Dispose()
End If End If
j.Dispose() End If
ElseIf bUrl.URL.Contains("BoardSectionResource/get/") And (ExtractSubBoards Or (IsSubBoard And i = -1)) Then
__getBoardInfo(bUrl)
If IsSubBoard And i = -1 And Board.Title.IsEmptyString Then
Board.Title = subBoard.Title
If Board.ID.IsEmptyString Then Board.ID = subBoard.ID
subBoard = Nothing
folderDef = String.Empty
End If End If
End If End If
Next Next
End If End If
Catch ex As Exception Catch ex As Exception
ProcessException(ex, Token, $"data (gallery-dl images) downloading error [{bUrl}]") ProcessException(ex, Token, $"data (gallery-dl images) downloading error [{bUrl.URL}]")
Finally
Responser.Headers.Remove(PwsHeader)
End Try End Try
End Sub End Sub
#End Region #End Region
#Region "Gallery-DL Support" #Region "Gallery-DL Support"
Private Structure GDLURL : Implements IRegExCreator
Friend URL As String
Friend BoardId As String
Public Shared Widening Operator CType(ByVal u As String) As GDLURL
Return New GDLURL With {.URL = u}
End Operator
Public Shared Widening Operator CType(ByVal u As GDLURL) As String
Return u.URL
End Operator
Private Function CreateFromArray(ByVal ParamsArray() As String) As Object Implements IRegExCreator.CreateFromArray
If ParamsArray.ListExists(2) Then
Dim u$ = ParamsArray(0).StringTrim.StringTrimEnd("/"), u2$
If Not u.IsEmptyString Then
u2 = ParamsArray(1).StringTrim
If Not u2.IsEmptyString AndAlso u2.StartsWith("GET", StringComparison.OrdinalIgnoreCase) Then
u2 = u2.Remove(0, 3).StringTrim.StringTrimStart("/")
If Not u2.IsEmptyString Then URL = $"{u}/{u2}"
End If
End If
End If
Return Me
End Function
Public Shared Operator =(ByVal x As GDLURL, ByVal y As GDLURL) As Boolean
Return x.URL = y.URL
End Operator
Public Shared Operator <>(ByVal x As GDLURL, ByVal y As GDLURL) As Boolean
Return Not x.URL = y.URL
End Operator
Public Overrides Function ToString() As String
Return URL
End Function
Public Overrides Function Equals(ByVal Obj As Object) As Boolean
Return URL = CType(Obj, String)
End Function
End Structure
Private Class GDLBatch : Inherits GDL.GDLBatch Private Class GDLBatch : Inherits GDL.GDLBatch
Private ReadOnly Property Source As UserData Private ReadOnly Property Source As UserData
Private ReadOnly IsBoardsRequested As Boolean Private ReadOnly IsBoardsRequested As Boolean
@@ -286,14 +376,30 @@ Namespace API.Pinterest
End If End If
End Function End Function
End Class End Class
Private Function GetDataFromGalleryDL(ByVal URL As String, ByVal IsBoardsRequested As Boolean, ByVal Token As CancellationToken) As List(Of String) Private Function GetDataFromGalleryDL(ByVal URL As String, ByVal IsBoardsRequested As Boolean, ByVal Token As CancellationToken) As List(Of GDLURL)
Dim command$ = $"""{Settings.GalleryDLFile.File}"" --verbose --simulate " Dim command$ = $"""{Settings.GalleryDLFile.File}"" --verbose --simulate "
Try Try
If Not URL.IsEmptyString Then If Not URL.IsEmptyString Then
Dim urls As New List(Of GDLURL)
Dim u As GDLURL
Dim s$ = String.Empty
If MySettings.CookiesNetscapeFile.Exists Then command &= $"--cookies ""{MySettings.CookiesNetscapeFile}"" " If MySettings.CookiesNetscapeFile.Exists Then command &= $"--cookies ""{MySettings.CookiesNetscapeFile}"" "
command &= URL command &= URL
Using batch As New GDLBatch(Me, IsBoardsRequested, Token) Using batch As New GDLBatch(Me, IsBoardsRequested, Token)
Return GetUrlsFromGalleryDl(batch, command) With batch
.Execute(command)
If .ErrorOutputData.Count > 0 Then
For Each eValue$ In .ErrorOutputData
s = CStr(RegexReplace(eValue, SubBoardRegEx)).IfNullOrEmpty(s)
u = RegexFields(Of GDLURL)(eValue, {GdlUrlPattern}, {1, 2}, EDP.ReturnValue).ListIfNothing.FirstOrDefault
If Not u.URL.IsEmptyString Then
If Not s.IsEmptyString Then u.BoardId = s
urls.Add(u)
End If
Next
Return urls
End If
End With
End Using End Using
End If End If
Return Nothing Return Nothing

View File

@@ -36,11 +36,12 @@ Namespace API.PornHub
Friend ReadOnly Regex_Gif_UrlName As RParams = RParams.DMS("""name"":.*?""([^""]*)""[^\}]+?""contentUrl"":.*?""([^""]+)""", 0, RegexReturn.ListByMatch, EDP.ReturnValue) Friend ReadOnly Regex_Gif_UrlName As RParams = RParams.DMS("""name"":.*?""([^""]*)""[^\}]+?""contentUrl"":.*?""([^""]+)""", 0, RegexReturn.ListByMatch, EDP.ReturnValue)
#End Region #End Region
#Region "Declarations photo" #Region "Declarations photo"
Friend ReadOnly Regex_Photo_ModelHub_PhotoBlocks As RParams = RParams.DM("var PHOTOS_ARRAY_(\d+) = \{[\r\n\s]*?(urls:.*?\[[^]]*\])", 0, RegexReturn.List, EDP.ReturnValue)
Friend ReadOnly Regex_Photo_PornHub_PhotoBlocks As RParams = RParams.DM("photoAlbumListContainer[\r\n\s\S]+?title=""([^""]+)""[\r\n\s\S]+?a href=""(/album/\d+)""", 0, RegexReturn.List) Friend ReadOnly Regex_Photo_PornHub_PhotoBlocks As RParams = RParams.DM("photoAlbumListContainer[\r\n\s\S]+?title=""([^""]+)""[\r\n\s\S]+?a href=""(/album/\d+)""", 0, RegexReturn.List)
Friend ReadOnly Regex_Photo_PornHub_AlbumPhotoArr As RParams = RParams.DMS("\<a href=""(/photo/\d+)""", 1, RegexReturn.List, EDP.ReturnValue, Friend ReadOnly Regex_Photo_PornHub_PhotoBlocks2 As RParams = RParams.DM("albumInfoTitle"" href=""([^""]+)""\>([^\<]+)", 0, RegexReturn.List)
Friend ReadOnly Regex_Photo_PornHub_AlbumPhotoArr As RParams = RParams.DMS("href=""(/photo/\d+)""", 1, RegexReturn.List, EDP.ReturnValue,
CType(Function(Input$) If(Input.IsEmptyString, String.Empty, $"https://www.pornhub.com{Input.Trim}"), Func(Of String, String))) CType(Function(Input$) If(Input.IsEmptyString, String.Empty, $"https://www.pornhub.com{Input.Trim}"), Func(Of String, String)))
Friend ReadOnly Regex_Photo_PornHub_SinglePhoto As RParams = RParams.DMS("(?<!thumbImage.+?)<img src=""(https://[^""]+\d+[^""]+)""", 1, EDP.ReturnValue) Friend ReadOnly Regex_Photo_PornHub_SinglePhoto As RParams = RParams.DM("data-image=""([^""]+)""\s*src=""([^""]+)""", 0, RegexReturn.ListByMatch, EDP.ReturnValue)
Friend ReadOnly Regex_Photo_PornHub_SinglePhoto2 As RParams = RParams.DMS("image:src"" content=""([^""]+)""", 1, EDP.ReturnValue)
Friend ReadOnly Regex_Photo_File As RParams = RParams.DM("\d+\.[\w]{3,4}", 0, EDP.ReturnValue) Friend ReadOnly Regex_Photo_File As RParams = RParams.DM("\d+\.[\w]{3,4}", 0, EDP.ReturnValue)
#End Region #End Region
End Module End Module

View File

@@ -29,10 +29,6 @@ Namespace API.PornHub
Friend ReadOnly Property DownloadGifs As PropertyValue Friend ReadOnly Property DownloadGifs As PropertyValue
<PropertyOption(ControlText:="Download GIFs as mp4", ControlToolTip:="Download gifs in 'mp4' format instead of native 'webm'"), PXML, PClonable> <PropertyOption(ControlText:="Download GIFs as mp4", ControlToolTip:="Download gifs in 'mp4' format instead of native 'webm'"), PXML, PClonable>
Friend ReadOnly Property DownloadGifsAsMp4 As PropertyValue Friend ReadOnly Property DownloadGifsAsMp4 As PropertyValue
<PropertyOption(ControlText:="Photo ModelHub only",
ControlToolTip:="Download photo only from ModelHub. Prornstar photos hosted on PornHub itself will not be downloaded." & vbCr &
"Attention! Downloading photos hosted on PornHub is a very heavy job."), PXML, PClonable>
Friend ReadOnly Property DownloadPhotoOnlyFromModelHub As PropertyValue
<PropertyOption(ControlText:=DeclaredNames.SavedPostsUserNameCaption, ControlToolTip:=DeclaredNames.SavedPostsUserNameToolTip), PXML, PClonable(Clone:=False)> <PropertyOption(ControlText:=DeclaredNames.SavedPostsUserNameCaption, ControlToolTip:=DeclaredNames.SavedPostsUserNameToolTip), PXML, PClonable(Clone:=False)>
Friend ReadOnly Property SavedPostsUserName As PropertyValue Friend ReadOnly Property SavedPostsUserName As PropertyValue
#End Region #End Region
@@ -48,7 +44,6 @@ Namespace API.PornHub
DownloadFavorite = New PropertyValue(False) DownloadFavorite = New PropertyValue(False)
DownloadGifsAsMp4 = New PropertyValue(True) DownloadGifsAsMp4 = New PropertyValue(True)
DownloadGifs = New PropertyValue(CInt(CheckState.Indeterminate), GetType(Integer)) DownloadGifs = New PropertyValue(CInt(CheckState.Indeterminate), GetType(Integer))
DownloadPhotoOnlyFromModelHub = New PropertyValue(True)
SavedPostsUserName = New PropertyValue(String.Empty, GetType(String)) SavedPostsUserName = New PropertyValue(String.Empty, GetType(String))
_SubscriptionsAllowed = True _SubscriptionsAllowed = True

View File

@@ -20,15 +20,12 @@ Namespace API.PornHub
#Region "Declarations" #Region "Declarations"
#Region "XML names" #Region "XML names"
Private Const Name_PersonType As String = "PersonType" Private Const Name_PersonType As String = "PersonType"
Private Const Name_NameTrue As String = "NameTrue"
Private Const Name_PhotoPageModel As String = "PhotoPageModel"
Private Const Name_DownloadUHD As String = "DownloadUHD" Private Const Name_DownloadUHD As String = "DownloadUHD"
Private Const Name_DownloadUploaded As String = "DownloadUploaded" Private Const Name_DownloadUploaded As String = "DownloadUploaded"
Private Const Name_DownloadTagged As String = "DownloadTagged" Private Const Name_DownloadTagged As String = "DownloadTagged"
Private Const Name_DownloadPrivate As String = "DownloadPrivate" Private Const Name_DownloadPrivate As String = "DownloadPrivate"
Private Const Name_DownloadFavorite As String = "DownloadFavorite" Private Const Name_DownloadFavorite As String = "DownloadFavorite"
Private Const Name_DownloadGifs As String = "DownloadGifs" Private Const Name_DownloadGifs As String = "DownloadGifs"
Private Const Name_DownloadPhotoOnlyFromModelHub As String = "DownloadPhotoOnlyFromModelHub"
#End Region #End Region
#Region "Structures" #Region "Structures"
Private Structure FlashVar : Implements IRegExCreator Private Structure FlashVar : Implements IRegExCreator
@@ -98,11 +95,6 @@ Namespace API.PornHub
End Structure End Structure
#End Region #End Region
#Region "Enums" #Region "Enums"
Private Enum PhotoPageModels As Integer
Undefined = 0
PornHubPage = 1
ModelHubPage = 2
End Enum
Private Enum VideoTypes Private Enum VideoTypes
Undefined Undefined
Uploaded Uploaded
@@ -121,7 +113,6 @@ Namespace API.PornHub
#End Region #End Region
#Region "Person" #Region "Person"
Friend Property PersonType As String Friend Property PersonType As String
Friend Property NameTrue As String
Friend Overrides Property FriendlyName As String Friend Overrides Property FriendlyName As String
Get Get
If _FriendlyName.IsEmptyString Then Return NameTrue Else Return _FriendlyName If _FriendlyName.IsEmptyString Then Return NameTrue Else Return _FriendlyName
@@ -137,14 +128,12 @@ Namespace API.PornHub
Return IsUser Or SiteMode = SiteModes.Playlists Return IsUser Or SiteMode = SiteModes.Playlists
End Get End Get
End Property End Property
Private Property PhotoPageModel As PhotoPageModels = PhotoPageModels.Undefined
Friend Property DownloadUHD As Boolean = False Friend Property DownloadUHD As Boolean = False
Friend Property DownloadUploaded As Boolean = True Friend Property DownloadUploaded As Boolean = True
Friend Property DownloadTagged As Boolean = False Friend Property DownloadTagged As Boolean = False
Friend Property DownloadPrivate As Boolean = False Friend Property DownloadPrivate As Boolean = False
Friend Property DownloadFavorite As Boolean = False Friend Property DownloadFavorite As Boolean = False
Friend Property DownloadGifs As Boolean Friend Property DownloadGifs As Boolean
Friend Property DownloadPhotoOnlyFromModelHub As Boolean = True
Friend Overrides ReadOnly Property IsUser As Boolean Friend Overrides ReadOnly Property IsUser As Boolean
Get Get
Return SiteMode = SiteModes.User Return SiteMode = SiteModes.User
@@ -182,7 +171,6 @@ Namespace API.PornHub
DownloadPrivate = .DownloadPrivate DownloadPrivate = .DownloadPrivate
DownloadFavorite = .DownloadFavorite DownloadFavorite = .DownloadFavorite
DownloadGifs = .DownloadGifs DownloadGifs = .DownloadGifs
DownloadPhotoOnlyFromModelHub = .DownloadPhotoOnlyFromModelHub
QueryString = .QueryString QueryString = .QueryString
End With End With
End If End If
@@ -244,29 +232,23 @@ Namespace API.PornHub
With Container With Container
If Loading Then If Loading Then
PersonType = .Value(Name_PersonType) PersonType = .Value(Name_PersonType)
NameTrue = .Value(Name_NameTrue)
PhotoPageModel = .Value(Name_PhotoPageModel).FromXML(Of Integer)(PhotoPageModels.Undefined)
DownloadUHD = .Value(Name_DownloadUHD).FromXML(Of Boolean)(False) DownloadUHD = .Value(Name_DownloadUHD).FromXML(Of Boolean)(False)
DownloadUploaded = .Value(Name_DownloadUploaded).FromXML(Of Boolean)(True) DownloadUploaded = .Value(Name_DownloadUploaded).FromXML(Of Boolean)(True)
DownloadTagged = .Value(Name_DownloadTagged).FromXML(Of Boolean)(False) DownloadTagged = .Value(Name_DownloadTagged).FromXML(Of Boolean)(False)
DownloadPrivate = .Value(Name_DownloadPrivate).FromXML(Of Boolean)(False) DownloadPrivate = .Value(Name_DownloadPrivate).FromXML(Of Boolean)(False)
DownloadFavorite = .Value(Name_DownloadFavorite).FromXML(Of Boolean)(False) DownloadFavorite = .Value(Name_DownloadFavorite).FromXML(Of Boolean)(False)
DownloadGifs = .Value(Name_DownloadGifs).FromXML(Of Integer)(False) DownloadGifs = .Value(Name_DownloadGifs).FromXML(Of Integer)(False)
DownloadPhotoOnlyFromModelHub = .Value(Name_DownloadPhotoOnlyFromModelHub).FromXML(Of Boolean)(True)
SiteMode = .Value(Name_SiteMode).FromXML(Of Integer)(SiteModes.User) SiteMode = .Value(Name_SiteMode).FromXML(Of Integer)(SiteModes.User)
UpdateUserOptions() UpdateUserOptions()
Else Else
If UpdateUserOptions() Then .Value(Name_LabelsName) = LabelsString If UpdateUserOptions() Then .Value(Name_LabelsName) = LabelsString
.Add(Name_PersonType, PersonType) .Add(Name_PersonType, PersonType)
.Add(Name_NameTrue, NameTrue)
.Add(Name_PhotoPageModel, CInt(PhotoPageModel))
.Add(Name_DownloadUHD, DownloadUHD.BoolToInteger) .Add(Name_DownloadUHD, DownloadUHD.BoolToInteger)
.Add(Name_DownloadUploaded, DownloadUploaded.BoolToInteger) .Add(Name_DownloadUploaded, DownloadUploaded.BoolToInteger)
.Add(Name_DownloadTagged, DownloadTagged.BoolToInteger) .Add(Name_DownloadTagged, DownloadTagged.BoolToInteger)
.Add(Name_DownloadPrivate, DownloadPrivate.BoolToInteger) .Add(Name_DownloadPrivate, DownloadPrivate.BoolToInteger)
.Add(Name_DownloadFavorite, DownloadFavorite.BoolToInteger) .Add(Name_DownloadFavorite, DownloadFavorite.BoolToInteger)
.Add(Name_DownloadGifs, DownloadGifs.BoolToInteger) .Add(Name_DownloadGifs, DownloadGifs.BoolToInteger)
.Add(Name_DownloadPhotoOnlyFromModelHub, DownloadPhotoOnlyFromModelHub.BoolToInteger)
.Add(Name_SiteMode, CInt(SiteMode)) .Add(Name_SiteMode, CInt(SiteMode))
'Debug.WriteLine(GetNonUserUrl(0)) 'Debug.WriteLine(GetNonUserUrl(0))
@@ -283,6 +265,7 @@ Namespace API.PornHub
Private _PageVideosRepeat As Integer = 0 Private _PageVideosRepeat As Integer = 0
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken) Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
Try Try
UpdateM3U8URLS = False
PlaylistToken = String.Empty PlaylistToken = String.Empty
Responser.ResetStatus() Responser.ResetStatus()
_PageVideosRepeat = 0 _PageVideosRepeat = 0
@@ -295,7 +278,6 @@ Namespace API.PornHub
Dim limit% = If(DownloadTopCount, -1) Dim limit% = If(DownloadTopCount, -1)
If DownloadVideos Then If DownloadVideos Then
If SiteMode = SiteModes.Playlists Then If SiteMode = SiteModes.Playlists Then
Responser.Mode = Responser.Modes.Default Responser.Mode = Responser.Modes.Default
GetPlaylistToken(Token) GetPlaylistToken(Token)
@@ -519,25 +501,12 @@ Namespace API.PornHub
Dim pFile$ = RegexReplace(URL, Regex_Photo_File) Dim pFile$ = RegexReplace(URL, Regex_Photo_File)
If Not pFile.IsEmptyString Then Return New SFile(pFile) Else Return File If Not pFile.IsEmptyString Then Return New SFile(pFile) Else Return File
End Function End Function
Private Const PhotoUrlPattern_ModelHub As String = "https://www.modelhub.com/{0}/photos"
Private Const PhotoUrlPattern_PornHub As String = "https://www.pornhub.com/{0}/{1}/photos" Private Const PhotoUrlPattern_PornHub As String = "https://www.pornhub.com/{0}/{1}/photos"
Private Sub DownloadUserPhotos(ByVal Token As CancellationToken) Private Sub DownloadUserPhotos(ByVal Token As CancellationToken)
Try Try
If IsSavedPosts Then If IsSavedPosts Then
DownloadUserPhotos_SavedPosts(Token) DownloadUserPhotos_SavedPosts(Token)
ElseIf PersonType = PersonTypeModel Then Else
If PhotoPageModel = PhotoPageModels.Undefined Then
If DownloadUserPhotos_ModelHub(Token) Then PhotoPageModel = PhotoPageModels.ModelHubPage
ThrowAny(Token)
If PhotoPageModel = PhotoPageModels.Undefined AndAlso Not DownloadPhotoOnlyFromModelHub AndAlso
DownloadUserPhotos_PornHub(Token) Then PhotoPageModel = PhotoPageModels.PornHubPage
Else
Select Case PhotoPageModel
Case PhotoPageModels.ModelHubPage : DownloadUserPhotos_ModelHub(Token)
Case PhotoPageModels.PornHubPage : If Not DownloadPhotoOnlyFromModelHub Then DownloadUserPhotos_PornHub(Token)
End Select
End If
ElseIf Not DownloadPhotoOnlyFromModelHub Then
DownloadUserPhotos_PornHub(Token) DownloadUserPhotos_PornHub(Token)
End If End If
ThrowAny(Token) ThrowAny(Token)
@@ -545,48 +514,6 @@ Namespace API.PornHub
ProcessException(ex, Token, "photos downloading error") ProcessException(ex, Token, "photos downloading error")
End Try End Try
End Sub End Sub
Private Function DownloadUserPhotos_ModelHub(ByVal Token As CancellationToken) As Boolean
Dim URL$ = String.Empty
Try
Dim j As EContainer
Dim jErr As New ErrorsDescriber(EDP.SendToLog + EDP.ReturnValue)
Dim albumName$
If PersonType = PersonTypeModel Then
URL = String.Format(PhotoUrlPattern_ModelHub, NameTrue)
Dim r$ = Responser.GetResponse(URL)
If Not r.IsEmptyString Then
Dim l As List(Of PhotoBlock) = RegexFields(Of PhotoBlock)(r, {Regex_Photo_ModelHub_PhotoBlocks}, {1, 2}, EDP.ReturnValue)
If l.ListExists Then l.RemoveAll(Function(ll) ll.Data.IsEmptyString)
If l.ListExists Then
ProgressPre.ChangeMax(l.Count)
Dim albumRegex As RParams = RParams.DMS("", 1, EDP.ReturnValue)
For Each block As PhotoBlock In l
ProgressPre.Perform()
If Not _TempPostsList.Contains(block.AlbumID) Then _TempPostsList.Add(block.AlbumID) Else Continue For
albumRegex.Pattern = "<li id=""" & block.AlbumID & """ class=""modelBox"">[\r\n\s]*?<div class=""modelPhoto"">[\r\n\s]*?\<[^\>]*?alt=""([^""]*)"""
albumName = StringTrim(RegexReplace(r, albumRegex))
If albumName.IsEmptyString Then albumName = block.AlbumID
j = JsonDocument.Parse("{" & block.Data & "}", jErr)
If Not j Is Nothing Then
If If(j("urls")?.Count, 0) > 0 Then
_TempMediaList.ListAddList(j("urls").Select(Function(jj) _
New UserMedia(jj.ItemF({0}).XmlIfNothingValue, UTypes.Picture) With {
.SpecialFolder = $"Albums\{albumName}\",
.File = CreatePhotoFile(.URL, .File)}), LNC)
End If
j.Dispose()
End If
Next
l.Clear()
End If
End If
End If
Return True
Catch ex As Exception
ThrowAny(Token)
Return False
End Try
End Function
Private Overloads Function DownloadUserPhotos_PornHub(ByVal Token As CancellationToken) As Boolean Private Overloads Function DownloadUserPhotos_PornHub(ByVal Token As CancellationToken) As Boolean
Try Try
Dim albumName$ Dim albumName$
@@ -594,6 +521,7 @@ Namespace API.PornHub
Dim r$ = Responser.GetResponse(String.Format(PhotoUrlPattern_PornHub, PersonType, NameTrue)) Dim r$ = Responser.GetResponse(String.Format(PhotoUrlPattern_PornHub, PersonType, NameTrue))
If Not r.IsEmptyString Then If Not r.IsEmptyString Then
Dim l As List(Of PhotoBlock) = RegexFields(Of PhotoBlock)(r, {Regex_Photo_PornHub_PhotoBlocks}, {2, 1}, EDP.ReturnValue) Dim l As List(Of PhotoBlock) = RegexFields(Of PhotoBlock)(r, {Regex_Photo_PornHub_PhotoBlocks}, {2, 1}, EDP.ReturnValue)
l.ListAddList(RegexFields(Of PhotoBlock)(r, {Regex_Photo_PornHub_PhotoBlocks2}, {1, 2}, EDP.ReturnValue))
If l.ListExists Then l.RemoveAll(Function(ll) ll.AlbumID.IsEmptyString) If l.ListExists Then l.RemoveAll(Function(ll) ll.AlbumID.IsEmptyString)
If l.ListExists Then If l.ListExists Then
ProgressPre.ChangeMax(l.Count) ProgressPre.ChangeMax(l.Count)
@@ -618,6 +546,14 @@ Namespace API.PornHub
Return False Return False
End Try End Try
End Function End Function
Private Function DownloadUserPhotos_PornHub_ParseSinglePhoto(ByVal r As String) As String
Dim url$ = String.Empty
With DirectCast(RegexReplace(r, Regex_Photo_PornHub_SinglePhoto), List(Of String))
If .ListExists(3) Then url = .Item(2).IfNullOrEmpty(.Item(1)).StringTrim
End With
If url.IsEmptyString Then url = RegexReplace(r, Regex_Photo_PornHub_SinglePhoto2)
Return url
End Function
Private Overloads Function DownloadUserPhotos_PornHub(ByVal Page As Integer, ByVal AlbumID As String, ByVal AlbumName As String, Private Overloads Function DownloadUserPhotos_PornHub(ByVal Page As Integer, ByVal AlbumID As String, ByVal AlbumName As String,
ByVal Token As CancellationToken) As Boolean ByVal Token As CancellationToken) As Boolean
Try Try
@@ -633,7 +569,7 @@ Namespace API.PornHub
Try Try
r = Responser.GetResponse(url) r = Responser.GetResponse(url)
If Not r.IsEmptyString Then If Not r.IsEmptyString Then
url = RegexReplace(r, Regex_Photo_PornHub_SinglePhoto) url = DownloadUserPhotos_PornHub_ParseSinglePhoto(r)
If Not url.IsEmptyString Then _ If Not url.IsEmptyString Then _
_TempMediaList.ListAddValue(New UserMedia(url, UTypes.Picture) With { _TempMediaList.ListAddValue(New UserMedia(url, UTypes.Picture) With {
.SpecialFolder = $"Albums\{AlbumName}\", .SpecialFolder = $"Albums\{AlbumName}\",
@@ -679,7 +615,7 @@ Namespace API.PornHub
Try Try
r = Responser.GetResponse(m.URL) r = Responser.GetResponse(m.URL)
If Not r.IsEmptyString Then If Not r.IsEmptyString Then
NewUrl = RegexReplace(r, Regex_Photo_PornHub_SinglePhoto) NewUrl = DownloadUserPhotos_PornHub_ParseSinglePhoto(r)
If Not NewUrl.IsEmptyString Then If Not NewUrl.IsEmptyString Then
m.URL = NewUrl m.URL = NewUrl
pFile = RegexReplace(NewUrl, Regex_Photo_File) pFile = RegexReplace(NewUrl, Regex_Photo_File)
@@ -852,11 +788,34 @@ Namespace API.PornHub
End Sub End Sub
#End Region #End Region
#Region "Download content" #Region "Download content"
Private UpdateM3U8URLS As Boolean = False
Private UpdateM3U8URLS_Error As Boolean = False
Protected Overrides Sub DownloadContent(ByVal Token As CancellationToken) Protected Overrides Sub DownloadContent(ByVal Token As CancellationToken)
DownloadContentDefault(Token) Try : DownloadContentDefault(Token) : Finally : UpdateM3U8URLS = False : End Try
End Sub End Sub
Protected Overrides Function DownloadM3U8(ByVal URL As String, ByVal Media As UserMedia, ByVal DestinationFile As SFile, ByVal Token As CancellationToken) As SFile Protected Overloads Overrides Function DownloadM3U8(ByVal URL As String, ByVal Media As UserMedia, ByVal DestinationFile As SFile,
Return M3U8.Download(URL, Responser, DestinationFile, DownloadUHD, Token, Progress, Not IsSingleObjectDownload) ByVal Token As CancellationToken) As SFile
UpdateM3U8URLS_Error = False
Return DownloadM3U8(URL, Media, DestinationFile, Token, UpdateM3U8URLS)
End Function
Private Overloads Function DownloadM3U8(ByVal URL As String, ByVal Media As UserMedia, ByVal DestinationFile As SFile,
ByVal Token As CancellationToken, ByVal Second As Boolean) As SFile
Try
If Second Then
Dim r$ = Responser.Curl(Media.URL_BASE,, EDP.ReturnValue)
If Not r.IsEmptyString Then Media.URL = CreateVideoURL(r).IfNullOrEmpty(URL) : URL = Media.URL
End If
Dim f As SFile = M3U8.Download(URL, Responser, DestinationFile, DownloadUHD, Token, Progress, Not IsSingleObjectDownload)
If Not f.Exists And Not Second Then UpdateM3U8URLS = True : f = DownloadM3U8(URL, Media, DestinationFile, Token, True)
Return f
Catch ex As Exception
If Not UpdateM3U8URLS_Error Then
UpdateM3U8URLS_Error = True
Thread.Sleep(1000)
Return DownloadM3U8(URL, Media, DestinationFile, Token, True)
End If
Return Nothing
End Try
End Function End Function
#End Region #End Region
#Region "CreateVideoURL" #Region "CreateVideoURL"
@@ -953,6 +912,7 @@ Namespace API.PornHub
#End Region #End Region
#Region "DownloadSingleObject" #Region "DownloadSingleObject"
Protected Overrides Sub DownloadSingleObject_GetPosts(ByVal Data As IYouTubeMediaContainer, ByVal Token As CancellationToken) Protected Overrides Sub DownloadSingleObject_GetPosts(ByVal Data As IYouTubeMediaContainer, ByVal Token As CancellationToken)
UpdateM3U8URLS = False
_TempMediaList.Add(New UserMedia(Data.URL, UTypes.VideoPre)) _TempMediaList.Add(New UserMedia(Data.URL, UTypes.VideoPre))
ReparseVideo(Token, True, Data) ReparseVideo(Token, True, Data)
End Sub End Sub

View File

@@ -21,8 +21,6 @@ Namespace API.PornHub
Friend Property DownloadFavorite As Boolean Friend Property DownloadFavorite As Boolean
<PSetting(Caption:="Download gifs")> <PSetting(Caption:="Download gifs")>
Friend Property DownloadGifs As Boolean Friend Property DownloadGifs As Boolean
<PSetting(NameOf(SiteSettings.DownloadPhotoOnlyFromModelHub), NameOf(MySettings), Caption:="Download photo only from ModelHub")>
Friend Property DownloadPhotoOnlyFromModelHub As Boolean
Private ReadOnly Property MySettings As SiteSettings Private ReadOnly Property MySettings As SiteSettings
Friend Sub New(ByVal u As UserData) Friend Sub New(ByVal u As UserData)
DownloadUHD = u.DownloadUHD DownloadUHD = u.DownloadUHD
@@ -31,7 +29,6 @@ Namespace API.PornHub
DownloadPrivate = u.DownloadPrivate DownloadPrivate = u.DownloadPrivate
DownloadFavorite = u.DownloadFavorite DownloadFavorite = u.DownloadFavorite
DownloadGifs = u.DownloadGifs DownloadGifs = u.DownloadGifs
DownloadPhotoOnlyFromModelHub = u.DownloadPhotoOnlyFromModelHub
QueryString = u.QueryString QueryString = u.QueryString
MySettings = u.HOST.Source MySettings = u.HOST.Source
End Sub End Sub
@@ -43,7 +40,6 @@ Namespace API.PornHub
DownloadPrivate = s.DownloadPrivate.Value DownloadPrivate = s.DownloadPrivate.Value
DownloadFavorite = s.DownloadFavorite.Value DownloadFavorite = s.DownloadFavorite.Value
DownloadGifs = Not v = CheckState.Unchecked DownloadGifs = Not v = CheckState.Unchecked
DownloadPhotoOnlyFromModelHub = s.DownloadPhotoOnlyFromModelHub.Value
MySettings = s MySettings = s
End Sub End Sub
End Class End Class

View File

@@ -17,8 +17,8 @@ Imports PersonalUtilities.Functions.RegularExpressions
Imports DownDetector = SCrawler.API.Base.DownDetector Imports DownDetector = SCrawler.API.Base.DownDetector
Imports Download = SCrawler.Plugin.ISiteSettings.Download Imports Download = SCrawler.Plugin.ISiteSettings.Download
Namespace API.Reddit Namespace API.Reddit
<Manifest(RedditSiteKey), SavedPosts, SpecialForm(False)> <Manifest(RedditSiteKey), SavedPosts, SpecialForm(False), UseDownDetector>
Friend Class SiteSettings : Inherits SiteSettingsBase Friend Class SiteSettings : Inherits SiteSettingsBase : Implements DownDetector.IDownDetector
#Region "Declarations" #Region "Declarations"
#Region "Authorization" #Region "Authorization"
<PropertyOption(ControlText:="Login", ControlToolTip:="Your authorization username", IsAuth:=True), PXML, PClonable(Clone:=False)> <PropertyOption(ControlText:="Login", ControlToolTip:="Your authorization username", IsAuth:=True), PXML, PClonable(Clone:=False)>
@@ -67,6 +67,26 @@ Namespace API.Reddit
<PropertyOption(ControlText:="Check image: get original", ControlToolTip:="Get the original image if it exists", IsAuth:=False), PXML, PClonable> <PropertyOption(ControlText:="Check image: get original", ControlToolTip:="Get the original image if it exists", IsAuth:=False), PXML, PClonable>
Friend ReadOnly Property CheckImageReturnOrig As PropertyValue Friend ReadOnly Property CheckImageReturnOrig As PropertyValue
#End Region #End Region
#Region "IDownDetector Support"
Private ReadOnly Property IDownDetector_Value As Integer Implements DownDetector.IDownDetector.Value
Get
Return 100
End Get
End Property
Private ReadOnly Property IDownDetector_AddToLog As Boolean Implements DownDetector.IDownDetector.AddToLog
Get
Return False
End Get
End Property
Private ReadOnly Property IDownDetector_CheckSite As String Implements DownDetector.IDownDetector.CheckSite
Get
Return "reddit"
End Get
End Property
Private Function IDownDetector_Available(ByVal What As Download, ByVal Silent As Boolean) As Boolean Implements DownDetector.IDownDetector.Available
Return MDD.Available(What, Silent)
End Function
#End Region
#End Region #End Region
#Region "Initializer" #Region "Initializer"
Friend Sub New(ByVal AccName As String, ByVal Temp As Boolean) Friend Sub New(ByVal AccName As String, ByVal Temp As Boolean)
@@ -97,6 +117,8 @@ Namespace API.Reddit
CheckImage = New PropertyValue(False) CheckImage = New PropertyValue(False)
CheckImageReturnOrig = New PropertyValue(True) CheckImageReturnOrig = New PropertyValue(True)
MDD = New MyDownDetector(Me)
UrlPatternUser = "https://www.reddit.com/{0}/{1}/" UrlPatternUser = "https://www.reddit.com/{0}/{1}/"
ImageVideoContains = "reddit.com" ImageVideoContains = "reddit.com"
UserRegex = RParams.DM("[htps:/]{7,8}.*?reddit.com/([user]{1,4})/([^/\?&]+)", 0, RegexReturn.ListByMatch, EDP.ReturnValue) UserRegex = RParams.DM("[htps:/]{7,8}.*?reddit.com/([user]{1,4})/([^/\?&]+)", 0, RegexReturn.ListByMatch, EDP.ReturnValue)
@@ -116,81 +138,48 @@ Namespace API.Reddit
End Function End Function
#End Region #End Region
#Region "DownloadStarted, ReadyToDownload, Available, DownloadDone, UpdateRedGifsToken" #Region "DownloadStarted, ReadyToDownload, Available, DownloadDone, UpdateRedGifsToken"
Private ____DownloadStarted As Boolean = False Private ReadOnly MDD As MyDownDetector
Friend Overrides Sub DownloadStarted(ByVal What As Download) Private Class MyDownDetector : Inherits DownDetector.Checker(Of SiteSettings)
If What = Download.Main Then ____DownloadStarted = True Private __TrueValue As Boolean = False
MyBase.DownloadStarted(What) Friend Sub New(ByRef _Source As SiteSettings)
End Sub MyBase.New(_Source)
End Sub
Protected Overrides Function AvailableImpl(ByVal What As Download, ByVal Silent As Boolean) As Boolean
__TrueValue = Source.AvailableTrueValue(What)
Return MyBase.AvailableImpl(What, Silent)
End Function
Protected Overrides Function AvailableImpl_TRUE() As Boolean
Return AvailableImpl_TrueValueReturn()
End Function
Protected Overrides Function AvailableImpl_FALSE_SILENT_NOT_MSG_YES() As Boolean
Return AvailableImpl_TrueValueReturn()
End Function
Private Function AvailableImpl_TrueValueReturn() As Boolean
If __TrueValue Then Source.UpdateRedGifsToken()
Return __TrueValue AndAlso Source.UpdateTokenIfRequired()
End Function
Friend Overrides Sub Reset()
__TrueValue = False
MyBase.Reset()
End Sub
End Class
Friend Property SessionInterrupted As Boolean = False Friend Property SessionInterrupted As Boolean = False
Friend Overrides Function ReadyToDownload(ByVal What As Download) As Boolean Friend Overrides Function ReadyToDownload(ByVal What As Download) As Boolean
If What = Download.Main Then If What = Download.Main Then
Dim result As Boolean = Not SessionInterrupted Return Not SessionInterrupted
If result Then
If ____DownloadStarted And ____AvailableRequested Then
____AvailableResult = AvailableImpl(What, ____AvailableSilent)
____AvailableChecked = True
____AvailableRequested = False
result = ____AvailableResult
ElseIf ____AvailableChecked Then
result = ____AvailableResult
End If
End If
Return result
Else Else
Return True Return True
End If End If
End Function End Function
Private ____AvailableRequested As Boolean = False
Private ____AvailableSilent As Boolean = True
Private ____AvailableChecked As Boolean = False
Private ____AvailableResult As Boolean = False
Friend Overrides Function Available(ByVal What As Download, ByVal Silent As Boolean) As Boolean Friend Overrides Function Available(ByVal What As Download, ByVal Silent As Boolean) As Boolean
If What = Download.Main And ____DownloadStarted Then Return AvailableTrueValue(What) AndAlso UpdateTokenIfRequired()
____AvailableRequested = True
____AvailableSilent = Silent
Return True
Else
Return AvailableImpl(What, Silent)
End If
End Function End Function
Private Function AvailableImpl(ByVal What As Download, ByVal Silent As Boolean) As Boolean Private Function AvailableTrueValue(ByVal What As Download) As Boolean
Try Return Not What = Download.SavedPosts OrElse (Responser.CookiesExists And ACheck(SavedPostsUserName.Value))
AvailableText = String.Empty
Dim trueValue As Boolean = Not What = Download.SavedPosts OrElse (Responser.CookiesExists And ACheck(SavedPostsUserName.Value))
If Not trueValue Then Return False
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
AvailableText = "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)
If Silent Then
Return False
Else
If MsgBoxE({$"{AvailableText}{vbCr}{vbCr}Do you want to continue parsing Reddit data?", "There are outage reports on Reddit"}, vbYesNo) = vbYes Then
If trueValue Then UpdateRedGifsToken()
Return trueValue AndAlso UpdateTokenIfRequired()
Else
Return False
End If
End If
End If
End If
If trueValue Then UpdateRedGifsToken()
Return trueValue AndAlso UpdateTokenIfRequired()
Catch ex As Exception
Return ErrorsDescriber.Execute(EDP.SendToLog + EDP.ReturnValue, ex, "[API.Reddit.SiteSettings.Available]", True)
End Try
End Function End Function
Friend Overrides Sub DownloadDone(ByVal What As Download) Friend Overrides Sub DownloadDone(ByVal What As Download)
SessionInterrupted = False SessionInterrupted = False
____DownloadStarted = False MDD.Reset()
____AvailableRequested = False
____AvailableChecked = False
____AvailableSilent = True
____AvailableResult = False
MyBase.DownloadDone(What) MyBase.DownloadDone(What)
End Sub End Sub
Private Sub UpdateRedGifsToken() Private Sub UpdateRedGifsToken()
@@ -210,7 +199,7 @@ Namespace API.Reddit
End If End If
End Function End Function
Friend Overrides Function GetUserUrl(ByVal User As IPluginContentProvider) As String Friend Overrides Function GetUserUrl(ByVal User As IPluginContentProvider) As String
With DirectCast(User, UserData) : Return String.Format(UrlPatternUser, IIf(.IsChannel, ChannelOption, "user"), .TrueName) : End With With DirectCast(User, UserData) : Return String.Format(UrlPatternUser, IIf(.IsChannel, ChannelOption, "user"), .NameTrue) : End With
End Function End Function
Friend Overrides Function GetUserPostUrl(ByVal User As UserDataBase, ByVal Media As UserMedia) As String Friend Overrides Function GetUserPostUrl(ByVal User As UserDataBase, ByVal Media As UserMedia) As String
If Not Media.Post.ID.IsEmptyString Then If Not Media.Post.ID.IsEmptyString Then
@@ -261,6 +250,7 @@ Namespace API.Reddit
Return False Return False
End Function End Function
Private Function UpdateTokenIfRequired() As Boolean Private Function UpdateTokenIfRequired() As Boolean
UpdateRedGifsToken()
If (CBool(UseTokenForTimelines.Value) Or CBool(UseTokenForSavedPosts.Value)) AndAlso CredentialsExists Then If (CBool(UseTokenForTimelines.Value) Or CBool(UseTokenForSavedPosts.Value)) AndAlso CredentialsExists Then
If CDate(BearerTokenDateUpdate.Value).AddMinutes(TokenUpdateInterval.Value) <= Now Then Return UpdateToken() If CDate(BearerTokenDateUpdate.Value).AddMinutes(TokenUpdateInterval.Value) <= Now Then Return UpdateToken()
End If End If

View File

@@ -42,7 +42,6 @@ Namespace API.Reddit
End Get End Get
End Property End Property
Friend Property IsChannel As Boolean = False Friend Property IsChannel As Boolean = False
Friend Property TrueName As String = String.Empty
Friend Overrides ReadOnly Property SpecialLabels As IEnumerable(Of String) Friend Overrides ReadOnly Property SpecialLabels As IEnumerable(Of String)
Get Get
Return {CannelsLabelName, CannelsLabelName_ChannelsForm, UserLabelName} Return {CannelsLabelName, CannelsLabelName_ChannelsForm, UserLabelName}
@@ -173,16 +172,16 @@ Namespace API.Reddit
#End Region #End Region
#Region "Load and Update user info" #Region "Load and Update user info"
Private Function UpdateNames() As Boolean Private Function UpdateNames() As Boolean
If TrueName.IsEmptyString Then If NameTrue(True).IsEmptyString Then
Dim n$() = Name.Split("@") Dim n$() = Name.Split("@")
If n.ListExists Then If n.ListExists Then
If n.Length = 2 Then If n.Length = 2 Then
TrueName = n(0) NameTrue = n(0)
IsChannel = True IsChannel = True
ElseIf IsChannel Then ElseIf IsChannel Then
TrueName = Name NameTrue = Name
Else Else
TrueName = n(0) NameTrue = n(0)
End If End If
End If End If
If Not IsSavedPosts Then If Not IsSavedPosts Then
@@ -201,7 +200,6 @@ Namespace API.Reddit
ViewMode = .Value(Name_ViewMode).FromXML(Of Integer)(CInt(CView.New)) ViewMode = .Value(Name_ViewMode).FromXML(Of Integer)(CInt(CView.New))
ViewPeriod = .Value(Name_ViewPeriod).FromXML(Of Integer)(CInt(CPeriod.All)) ViewPeriod = .Value(Name_ViewPeriod).FromXML(Of Integer)(CInt(CPeriod.All))
IsChannel = .Value(Name_IsChannel).FromXML(Of Boolean)(False) IsChannel = .Value(Name_IsChannel).FromXML(Of Boolean)(False)
TrueName = .Value(Name_TrueName)
RedGifsAccount = .Value(Name_RedGifsAccount) RedGifsAccount = .Value(Name_RedGifsAccount)
RedditAccount = .Value(Name_RedditAccount) RedditAccount = .Value(Name_RedditAccount)
UpdateNames() UpdateNames()
@@ -210,7 +208,7 @@ Namespace API.Reddit
.Add(Name_ViewMode, CInt(ViewMode)) .Add(Name_ViewMode, CInt(ViewMode))
.Add(Name_ViewPeriod, CInt(ViewPeriod)) .Add(Name_ViewPeriod, CInt(ViewPeriod))
.Add(Name_IsChannel, IsChannel.BoolToInteger) .Add(Name_IsChannel, IsChannel.BoolToInteger)
.Add(Name_TrueName, TrueName) .Add(Name_TrueName, NameTrue(True))
.Add(Name_RedGifsAccount, RedGifsAccount) .Add(Name_RedGifsAccount, RedGifsAccount)
.Add(Name_RedditAccount, RedditAccount) .Add(Name_RedditAccount, RedditAccount)
End If End If
@@ -230,7 +228,7 @@ Namespace API.Reddit
If CreatedByChannel And Settings.FromChannelDownloadTopUse And Settings.FromChannelDownloadTop > 0 Then _ If CreatedByChannel And Settings.FromChannelDownloadTopUse And Settings.FromChannelDownloadTop > 0 Then _
DownloadTopCount = Settings.FromChannelDownloadTop.Value DownloadTopCount = Settings.FromChannelDownloadTop.Value
If IsChannel Or IsSavedPosts Then UseMD5Comparison = False If IsChannel Or IsSavedPosts Then UseMD5Comparison = False
If IsSavedPosts Then TrueName = MySiteSettings.SavedPostsUserName.Value If IsSavedPosts Then NameTrue = MySiteSettings.SavedPostsUserName.Value
UpdateNames() UpdateNames()
If IsChannelForm Then If IsChannelForm Then
UseMD5Comparison = False UseMD5Comparison = False
@@ -310,10 +308,10 @@ Namespace API.Reddit
Dim NewPostDetected As Boolean = False Dim NewPostDetected As Boolean = False
Dim ExistsDetected As Boolean = False Dim ExistsDetected As Boolean = False
Dim IsCrossPost As Predicate(Of EContainer) = Function(e) Not e.Value(Node_CrosspostRootId).IsEmptyString Or Not e.Value(Node_CrosspostParentId).IsEmptyString Or Not e.Value(Node_CrosspostParent).IsEmptyString Dim IsCrossPost As Predicate(Of EContainer) = Function(e) Not e.Value(Node_CrosspostRootId).IsEmptyString Or Not e.Value(Node_CrosspostParentId).IsEmptyString Or Not e.Value(Node_CrosspostParent).IsEmptyString
Dim CheckNode As Predicate(Of EContainer) = Function(e) Not ParseUserMediaOnly OrElse If(e("author")?.Value, "/").ToLower.Equals(TrueName.StringToLower) Dim CheckNode As Predicate(Of EContainer) = Function(e) Not ParseUserMediaOnly OrElse If(e("author")?.Value, "/").ToLower.Equals(NameTrue.StringToLower)
Dim _PostID As Func(Of String) = Function() PostTmp.IfNullOrEmpty(PostID) Dim _PostID As Func(Of String) = Function() PostTmp.IfNullOrEmpty(PostID)
URL = $"https://gateway.reddit.com/desktopapi/v1/user/{TrueName}/posts?rtj=only&allow_quarantined=true&allow_over18=1&include=identity&after={POST}&dist=25&sort={View}&t={Period}&layout=classic" URL = $"https://gateway.reddit.com/desktopapi/v1/user/{NameTrue}/posts?rtj=only&allow_quarantined=true&allow_over18=1&include=identity&after={POST}&dist=25&sort={View}&t={Period}&layout=classic"
ThrowAny(Token) ThrowAny(Token)
Dim r$ = Responser.GetResponse(URL) Dim r$ = Responser.GetResponse(URL)
If Not r.IsEmptyString Then If Not r.IsEmptyString Then
@@ -394,10 +392,10 @@ Namespace API.Reddit
Dim lDate As Date? Dim lDate As Date?
If IsSavedPosts Then If IsSavedPosts Then
URL = $"https://www.reddit.com/user/{TrueName}/saved.json?after={POST}" URL = $"https://www.reddit.com/user/{NameTrue}/saved.json?after={POST}"
If Not POST.IsEmptyString Then Thread.Sleep(savedPostsSleepTimer) If Not POST.IsEmptyString Then Thread.Sleep(savedPostsSleepTimer)
Else Else
URL = $"https://reddit.com/r/{TrueName}/{View}.json?allow_quarantined=true&allow_over18=1&include=identity&after={POST}&dist=25&sort={View}&t={Period}&layout=classic" URL = $"https://reddit.com/r/{NameTrue}/{View}.json?allow_quarantined=true&allow_over18=1&include=identity&after={POST}&dist=25&sort={View}&t={Period}&layout=classic"
End If End If
ThrowAny(Token) ThrowAny(Token)
@@ -480,7 +478,7 @@ Namespace API.Reddit
Private Sub GetUserInfo() Private Sub GetUserInfo()
Try Try
If Not IsSavedPosts And ChannelInfo Is Nothing Then If Not IsSavedPosts And ChannelInfo Is Nothing Then
Dim r$ = Responser.GetResponse($"https://reddit.com/{IIf(IsChannel, "r", "user")}/{TrueName}/about.json",, EDP.ReturnValue) Dim r$ = Responser.GetResponse($"https://reddit.com/{IIf(IsChannel, "r", "user")}/{NameTrue}/about.json",, EDP.ReturnValue)
If Not r.IsEmptyString Then If Not r.IsEmptyString Then
Using j As EContainer = JsonDocument.Parse(r) Using j As EContainer = JsonDocument.Parse(r)
If Not j Is Nothing AndAlso j.Contains({"data", "subreddit"}) Then If Not j Is Nothing AndAlso j.Contains({"data", "subreddit"}) Then
@@ -489,20 +487,10 @@ Namespace API.Reddit
UserSiteNameUpdate(.Value("title")) UserSiteNameUpdate(.Value("title"))
UserDescriptionUpdate(.Value("public_description")) UserDescriptionUpdate(.Value("public_description"))
Dim dir As SFile = MyFile.CutPath Dim dir As SFile = MyFile.CutPath
Dim __getFile As Action(Of String) = Sub(ByVal img As String) Dim fileCrFunc As Func(Of String, SFile) = Function(img) CreateFileFromUrl(img)
If Not img.IsEmptyString Then
Dim f As SFile = CreateFileFromUrl(img)
If Not f.Name.IsEmptyString Then
If f.Extension.IsEmptyString Then f.Extension = "jpg"
f.Path = dir.Path
If Not f.Exists Then GetWebFile(img, f, EDP.ReturnValue)
If f.Exists Then IconBannerDownloaded = True
End If
End If
End Sub
If DownloadIconBanner Then If DownloadIconBanner Then
__getFile.Invoke(.Value("icon_img")) SimpleDownloadAvatar(.Value("icon_img"), fileCrFunc)
__getFile.Invoke(.Value("banner_img")) SimpleDownloadAvatar(.Value("banner_img"), fileCrFunc)
End If End If
End With End With
End If End If

View File

@@ -44,7 +44,6 @@ Namespace API.ThisVid
Friend Property DownloadPrivate As Boolean = True Friend Property DownloadPrivate As Boolean = True
Friend Property DownloadFavourite As Boolean = False Friend Property DownloadFavourite As Boolean = False
Friend Property DifferentFolders As Boolean = True Friend Property DifferentFolders As Boolean = True
Friend Property TrueName As String = String.Empty
Friend Property SiteMode As SiteModes = SiteModes.User Friend Property SiteMode As SiteModes = SiteModes.User
Private Property Arguments As String = String.Empty Private Property Arguments As String = String.Empty
Friend Overrides ReadOnly Property SpecialLabels As IEnumerable(Of String) Friend Overrides ReadOnly Property SpecialLabels As IEnumerable(Of String)
@@ -80,7 +79,7 @@ Namespace API.ThisVid
If Not Force OrElse (Not SiteMode = SiteModes.User AndAlso Not NewUrl.IsEmptyString AndAlso MyFileSettings.Exists) Then If Not Force OrElse (Not SiteMode = SiteModes.User AndAlso Not NewUrl.IsEmptyString AndAlso MyFileSettings.Exists) Then
Dim eObj As Plugin.ExchangeOptions = Nothing Dim eObj As Plugin.ExchangeOptions = Nothing
If Force Then eObj = MySettings.IsMyUser(NewUrl) If Force Then eObj = MySettings.IsMyUser(NewUrl)
If (Force And Not eObj.UserName.IsEmptyString) Or (Not Force And TrueName.IsEmptyString) Then If (Force And Not eObj.UserName.IsEmptyString) Or (Not Force And NameTrue(True).IsEmptyString) Then
Dim n$() = If(Force, eObj.UserName, Name).Split("@") Dim n$() = If(Force, eObj.UserName, Name).Split("@")
If n.ListExists(2) Then If n.ListExists(2) Then
@@ -98,8 +97,8 @@ Namespace API.ThisVid
End If End If
__TrueName = n(1) __TrueName = n(1)
If Force AndAlso (Not TrueName = __TrueName Or Not SiteMode = __Mode) Then If Force AndAlso (Not NameTrue(True) = __TrueName Or Not SiteMode = __Mode) Then
If ValidateChangeSearchOptions(ToStringForLog, $"{__Mode}: {__TrueName}", $"{SiteMode}: {TrueName}") Then If ValidateChangeSearchOptions(ToStringForLog, $"{__Mode}: {__TrueName}", $"{SiteMode}: {NameTrue(True)}") Then
__ForceApply = True __ForceApply = True
Else Else
Return False Return False
@@ -109,21 +108,21 @@ Namespace API.ThisVid
Arguments = __Arguments Arguments = __Arguments
Options = If(Force, eObj.Options, Options) Options = If(Force, eObj.Options, Options)
If Not Force Then If Not Force Then
TrueName = __TrueName NameTrue = __TrueName
SiteMode = __Mode SiteMode = __Mode
Settings.Labels.Add(SearchRequestLabelName) Settings.Labels.Add(SearchRequestLabelName)
Labels.ListAddValue(SearchRequestLabelName, LNC) Labels.ListAddValue(SearchRequestLabelName, LNC)
Labels.Sort() Labels.Sort()
UserSiteName = $"{SiteMode}: {TrueName}" UserSiteName = $"{SiteMode}: {NameTrue}"
If FriendlyName.IsEmptyString Then FriendlyName = UserSiteName If FriendlyName.IsEmptyString Then FriendlyName = UserSiteName
ElseIf Force And __ForceApply Then ElseIf Force And __ForceApply Then
TrueName = __TrueName NameTrue = __TrueName
SiteMode = __Mode SiteMode = __Mode
End If End If
Return True Return True
Else Else
SiteMode = SiteModes.User SiteMode = SiteModes.User
TrueName = Name NameTrue = Name
End If End If
End If End If
End If End If
@@ -136,7 +135,6 @@ Namespace API.ThisVid
DownloadPrivate = .Value(Name_DownloadPrivate).FromXML(Of Boolean)(True) DownloadPrivate = .Value(Name_DownloadPrivate).FromXML(Of Boolean)(True)
DownloadFavourite = .Value(Name_DownloadFavourite).FromXML(Of Boolean)(False) DownloadFavourite = .Value(Name_DownloadFavourite).FromXML(Of Boolean)(False)
DifferentFolders = .Value(Name_DifferentFolders).FromXML(Of Boolean)(True) DifferentFolders = .Value(Name_DifferentFolders).FromXML(Of Boolean)(True)
TrueName = .Value(Name_TrueName)
SiteMode = .Value(Name_SiteMode).FromXML(Of Integer)(SiteModes.User) SiteMode = .Value(Name_SiteMode).FromXML(Of Integer)(SiteModes.User)
Arguments = .Value(Name_Arguments) Arguments = .Value(Name_Arguments)
UpdateUserOptions() UpdateUserOptions()
@@ -150,7 +148,7 @@ Namespace API.ThisVid
.Add(Name_DownloadPrivate, DownloadPrivate.BoolToInteger) .Add(Name_DownloadPrivate, DownloadPrivate.BoolToInteger)
.Add(Name_DownloadFavourite, DownloadFavourite.BoolToInteger) .Add(Name_DownloadFavourite, DownloadFavourite.BoolToInteger)
.Add(Name_DifferentFolders, DifferentFolders.BoolToInteger) .Add(Name_DifferentFolders, DifferentFolders.BoolToInteger)
.Add(Name_TrueName, TrueName) .Add(Name_TrueName, NameTrue(True))
.Add(Name_SiteMode, CInt(SiteMode)) .Add(Name_SiteMode, CInt(SiteMode))
.Add(Name_Arguments, Arguments) .Add(Name_Arguments, Arguments)
@@ -259,18 +257,18 @@ Namespace API.ThisVid
Dim url$ = String.Empty Dim url$ = String.Empty
Select Case SiteMode Select Case SiteMode
Case SiteModes.Tags Case SiteModes.Tags
url = $"https://thisvid.com/{SiteSettings.P_Tags}/{TrueName}/" url = $"https://thisvid.com/{SiteSettings.P_Tags}/{NameTrue}/"
If Not Arguments.IsEmptyString Then url &= $"{Arguments}/" If Not Arguments.IsEmptyString Then url &= $"{Arguments}/"
If Page > 1 Then url &= $"{Page}/" If Page > 1 Then url &= $"{Page}/"
Case SiteModes.Categories Case SiteModes.Categories
url = $"https://thisvid.com/{SiteSettings.P_Categories}/{TrueName}/" url = $"https://thisvid.com/{SiteSettings.P_Categories}/{NameTrue}/"
If Not Arguments.IsEmptyString Then url &= $"{Arguments}/" If Not Arguments.IsEmptyString Then url &= $"{Arguments}/"
If Page > 1 Then url &= $"{Page}/" If Page > 1 Then url &= $"{Page}/"
Case SiteModes.Search Case SiteModes.Search
If Not Arguments.IsEmptyString Then If Not Arguments.IsEmptyString Then
url = $"https://thisvid.com/{Arguments}/" url = $"https://thisvid.com/{Arguments}/"
If Page > 1 Then url &= $"{Page}/" If Page > 1 Then url &= $"{Page}/"
url &= $"?q={TrueName}/" url &= $"?q={NameTrue}/"
End If End If
End Select End Select
Return url Return url
@@ -473,35 +471,47 @@ Namespace API.ThisVid
Dim u As UserMedia Dim u As UserMedia
Dim n$, r$ Dim n$, r$
Dim c% = 0 Dim c% = 0
Dim ii As Byte
Dim repeat As Boolean
Progress.Maximum += _TempMediaList.Count Progress.Maximum += _TempMediaList.Count
For i% = _TempMediaList.Count - 1 To 0 Step -1 For i% = _TempMediaList.Count - 1 To 0 Step -1
Progress.Perform() Progress.Perform()
u = _TempMediaList(i) u = _TempMediaList(i)
If u.Type = UserMedia.Types.VideoPre Then If u.Type = UserMedia.Types.VideoPre Then
If Not DownloadTopCount.HasValue OrElse c <= DownloadTopCount.Value Then If Not DownloadTopCount.HasValue OrElse c <= DownloadTopCount.Value Then
ThrowAny(Token) repeat = False
r = Responser.GetResponse(u.URL,, EDP.ReturnValue) For ii = 0 To 1
If Not r.IsEmptyString Then ThrowAny(Token)
n = TitleHtmlConverter(RegexReplace(r, RegExVideoTitle)) r = Responser.GetResponse(u.URL,, EDP.ReturnValue)
u.Post.ID = u.URL If Not r.IsEmptyString Then
If Not n.IsEmptyString Then n = n.Replace("ThisVid.com", String.Empty).StringTrim.StringTrimEnd("-").StringTrim n = TitleHtmlConverter(RegexReplace(r, RegExVideoTitle))
If n.IsEmptyString Then n = TitleHtmlConverter(u.URL.Replace("https://thisvid.com/videos/", String.Empty).StringTrim.StringTrimEnd("-").StringTrim) u.Post.ID = u.URL
If n.IsEmptyString Then n = "VideoFile" If Not n.IsEmptyString Then n = n.Replace("ThisVid.com", String.Empty).StringTrim.StringTrimEnd("-").StringTrim
u.File = $"{n}.mp4" If n.IsEmptyString Then n = TitleHtmlConverter(u.URL.Replace("https://thisvid.com/videos/", String.Empty).StringTrim.StringTrimEnd("-").StringTrim)
u.PictureOption = n If n.IsEmptyString Then n = "VideoFile"
u.URL = RegexReplace(r, Regex_VideosThumb_OG_IMAGE) u.File = $"{n}.mp4"
If u.URL.IsEmptyString Then u.URL = RegexReplace(r, RegExVideosThumb1) u.PictureOption = n
If u.URL.IsEmptyString Then u.URL = RegexReplace(r, RegExVideosThumb2) u.URL = RegexReplace(r, Regex_VideosThumb_OG_IMAGE)
If Not u.URL.IsEmptyString Then If u.URL.IsEmptyString And Not repeat And ii = 0 Then
u.URL = LinkFormatterSecure(u.URL) Thread.Sleep(250)
u.Type = UserMedia.Types.Video u = _TempMediaList(i)
_TempPostsList.Add(u.Post.ID) repeat = True
_TempMediaList(i) = u Continue For
c += 1 End If
Else If u.URL.IsEmptyString Then u.URL = RegexReplace(r, RegExVideosThumb1)
_TempMediaList.RemoveAt(i) If u.URL.IsEmptyString Then u.URL = RegexReplace(r, RegExVideosThumb2)
If Not u.URL.IsEmptyString Then
u.URL = LinkFormatterSecure(u.URL)
u.Type = UserMedia.Types.Video
_TempPostsList.Add(u.Post.ID)
_TempMediaList(i) = u
c += 1
Else
_TempMediaList.RemoveAt(i)
End If
End If End If
End If If Not repeat Then Exit For
Next
Else Else
_TempMediaList.RemoveAt(i) _TempMediaList.RemoveAt(i)
End If End If

View File

@@ -15,7 +15,7 @@ Imports PersonalUtilities.Functions.RegularExpressions
Imports IG = SCrawler.API.Instagram.SiteSettings Imports IG = SCrawler.API.Instagram.SiteSettings
Imports DN = SCrawler.API.Base.DeclaredNames Imports DN = SCrawler.API.Base.DeclaredNames
Namespace API.ThreadsNet Namespace API.ThreadsNet
<Manifest("AndyProgram_ThreadsNet"), SavedPosts, SeparatedTasks(1)> <Manifest("AndyProgram_ThreadsNet"), SavedPosts, SeparatedTasks(1), SpecialForm(False)>
Friend Class SiteSettings : Inherits SiteSettingsBase Friend Class SiteSettings : Inherits SiteSettingsBase
#Region "Declarations" #Region "Declarations"
#Region "Authorization" #Region "Authorization"
@@ -155,6 +155,7 @@ Namespace API.ThreadsNet
UrlPatternUser = "https://www.threads.net/@{0}" UrlPatternUser = "https://www.threads.net/@{0}"
UserRegex = RParams.DMS(String.Format(UserRegexDefaultPattern, "threads.net/@"), 1) UserRegex = RParams.DMS(String.Format(UserRegexDefaultPattern, "threads.net/@"), 1)
ImageVideoContains = "threads.net" ImageVideoContains = "threads.net"
UserOptionsType = GetType(EditorExchangeOptionsBase)
End Sub End Sub
#End Region #End Region
#Region "UpdateResponserData" #Region "UpdateResponserData"
@@ -180,9 +181,6 @@ Namespace API.ThreadsNet
Friend Overrides Function BaseAuthExists() As Boolean Friend Overrides Function BaseAuthExists() As Boolean
Return Responser.CookiesExists And {HH_CSRF_TOKEN, HH_IG_APP_ID}.All(Function(v) ACheck(Of String)(v.Value)) And CBool(DownloadData_Impl.Value) Return Responser.CookiesExists And {HH_CSRF_TOKEN, HH_IG_APP_ID}.All(Function(v) ACheck(Of String)(v.Value)) And CBool(DownloadData_Impl.Value)
End Function End Function
Friend Overrides Function GetUserUrl(ByVal User As IPluginContentProvider) As String
Return String.Format(UrlPatternUser, DirectCast(User, UserData).NameTrue)
End Function
Friend Overrides Function GetUserPostUrl(ByVal User As UserDataBase, ByVal Media As UserMedia) As String Friend Overrides Function GetUserPostUrl(ByVal User As UserDataBase, ByVal Media As UserMedia) As String
Try Try
Dim code$ = DirectCast(User, UserData).GetPostCodeById(Media.Post.ID) Dim code$ = DirectCast(User, UserData).GetPostCodeById(Media.Post.ID)

View File

@@ -7,6 +7,7 @@
' This program is distributed in the hope that it will be useful, ' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY ' but WITHOUT ANY WARRANTY
Imports System.Threading Imports System.Threading
Imports SCrawler.Plugin
Imports SCrawler.API.Base Imports SCrawler.API.Base
Imports SCrawler.API.YouTube.Objects Imports SCrawler.API.YouTube.Objects
Imports PersonalUtilities.Functions.XML Imports PersonalUtilities.Functions.XML
@@ -51,9 +52,10 @@ Namespace API.ThreadsNet
#End Region #End Region
#Region "Exchange" #Region "Exchange"
Friend Overrides Function ExchangeOptionsGet() As Object Friend Overrides Function ExchangeOptionsGet() As Object
Return Nothing Return New EditorExchangeOptionsBase(Me)
End Function End Function
Friend Overrides Sub ExchangeOptionsSet(ByVal Obj As Object) Friend Overrides Sub ExchangeOptionsSet(ByVal Obj As Object)
If Not Obj Is Nothing AndAlso TypeOf Obj Is EditorExchangeOptionsBase Then NameTrue = DirectCast(Obj, EditorExchangeOptionsBase).UserName
End Sub End Sub
#End Region #End Region
#Region "Initializer" #Region "Initializer"
@@ -73,12 +75,13 @@ Namespace API.ThreadsNet
End Sub End Sub
Private Sub DisableDownload() Private Sub DisableDownload()
MySettings.DownloadData_Impl.Value = False MySettings.DownloadData_Impl.Value = False
MyMainLOG = $"{Site} downloading is disabled until you update your credentials" LogError(Nothing, $"{Site} downloading is disabled until you update your credentials")
End Sub End Sub
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken) Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
If CBool(MySettings.DownloadData_Impl.Value) Then If CBool(MySettings.DownloadData_Impl.Value) Then
Dim errorFound As Boolean = False Dim errorFound As Boolean = False
Try Try
_IdChanged = False
Responser.Method = "POST" Responser.Method = "POST"
LoadSavePostsKV(True) LoadSavePostsKV(True)
ResetBaseTokens() ResetBaseTokens()
@@ -103,9 +106,9 @@ Namespace API.ThreadsNet
End If End If
If IsSavedPosts Then If IsSavedPosts Then
DefaultParser_ElemNode = {"node", "thread_items", 0, "post"} DefaultParser_ElemNode = {"node", "thread_items", 0, "post"}
DownloadSavedPosts(String.Empty, Token) DownloadSavedPosts(String.Empty, 0, Token)
Else Else
DownloadData(String.Empty, Token) DownloadData(String.Empty, 0, Token)
End If End If
If _TempMediaList.Count > 0 Then FirstLoadingDone = True : setMaxPostDate.Invoke(_TempMediaList) If _TempMediaList.Count > 0 Then FirstLoadingDone = True : setMaxPostDate.Invoke(_TempMediaList)
Catch ex As Exception Catch ex As Exception
@@ -115,6 +118,7 @@ Namespace API.ThreadsNet
Responser.Method = "POST" Responser.Method = "POST"
UpdateResponser() UpdateResponser()
MySettings.UpdateResponserData(Responser) MySettings.UpdateResponserData(Responser)
ValidateExtension()
If Not errorFound Then LoadSavePostsKV(False) If Not errorFound Then LoadSavePostsKV(False)
End Try End Try
End If End If
@@ -150,12 +154,14 @@ Namespace API.ThreadsNet
Responser.Headers.Add(IGS.Header_CSRF_TOKEN, csrf) Responser.Headers.Add(IGS.Header_CSRF_TOKEN, csrf)
End If End If
End Sub End Sub
Private Const GQL_Q As String = "https://www.threads.net/api/graphql?lsd={0}&fb_dtsg={1}&doc_id={2}&fb_api_req_friendly_name={3}&server_timestamps=true&variables={4}" 'Private Const GQL_Q As String = "https://www.threads.net/api/graphql?lsd={0}&fb_dtsg={1}&doc_id={2}&fb_api_req_friendly_name={3}&server_timestamps=true&variables={4}"
Private Const GQL_P_DOC_ID As String = "6371597506283707" Private Const GQL_Q2 As String = "https://www.threads.net/graphql/query"
Private Const GQL_P_NAME As String = "BarcelonaProfileThreadsTabRefetchableQuery" Private Const PayloadData As String = "lsd={0}&fb_dtsg={1}&doc_id={2}&fb_api_req_friendly_name={3}&server_timestamps=true&variables={4}"
Private Const GQL_S_DOC_ID_1 As String = "7758166704280174" Private Const GQL_P_DOC_ID As String = "9039187972876777" '"8779269398849532" '"6371597506283707"
Private Const GQL_S_NAME_1 As String = "BarcelonaSavedPageViewerQuery" Private Const GQL_P_NAME As String = "BarcelonaProfileThreadsTabRefetchableDirectQuery" '"BarcelonaProfileThreadsTabRefetchableQuery"
Private Const GQL_S_DOC_ID_2 As String = "8617275414954442" 'Private Const GQL_S_DOC_ID_1 As String = "9227844190587889" '"7758166704280174"
'Private Const GQL_S_NAME_1 As String = "BarcelonaSavedPageViewerQuery"
Private Const GQL_S_DOC_ID_2 As String = "9116629201788321" '"8617275414954442"
Private Const GQL_S_NAME_2 As String = "BarcelonaSavedPageRefetchableQuery" Private Const GQL_S_NAME_2 As String = "BarcelonaSavedPageRefetchableQuery"
Private Sub DownloadCheckCredentials() Private Sub DownloadCheckCredentials()
If Not Valid Then If Not Valid Then
@@ -165,106 +171,189 @@ Namespace API.ThreadsNet
End If End If
If Not Valid Then DisableDownload() : Throw New Plugin.ExitException("Some credentials are missing") If Not Valid Then DisableDownload() : Throw New Plugin.ExitException("Some credentials are missing")
End Sub End Sub
Private Overloads Sub DownloadData(ByVal Cursor As String, ByVal Token As CancellationToken) Private Function CheckErrors(ByVal e As EContainer) As Boolean
Const var_init$ = """userID"":""{0}""" Return e.ListExists AndAlso Not JsonErrorMessage(e).IsEmptyString
Const var_cursor$ = """after"":""{1}"",""before"":null,""first"":25,""last"":null,""userID"":""{0}"",""__relay_internal__pv__BarcelonaIsLoggedInrelayprovider"":true,""__relay_internal__pv__BarcelonaIsFeedbackHubEnabledrelayprovider"":false" End Function
Dim URL$ = String.Empty Private Function JsonErrorMessage(ByVal e As EContainer) As String
Return e.ItemF({"errors", 0, "summary"})?.Value
End Function
Private Sub ProcessJsonErrorException(ByVal uex As JsonErrorException, Optional ByVal ThrowEx As Boolean = True)
If uex.UserNotFound Then
UserExists = False
_ForceSaveUserInfo = True
_ForceSaveUserInfoOnException = True
ElseIf ThrowEx Then
Throw New ExitException(uex.ErrMessage) With {.SimpleLogLine = True}
Else
LogError(Nothing, uex.ErrMessage)
End If
End Sub
Private Class JsonErrorException : Inherits Exception
Friend Property UserNotFound As Boolean = False
Private _ErrMessage As String = String.Empty
Public Property ErrMessage As String
Get
Return _ErrMessage
End Get
Set(ByVal m As String)
_ErrMessage = m
UserNotFound = _ErrMessage.StringToLower = "not found"
End Set
End Property
Public Overrides ReadOnly Property Message As String
Get
Return _ErrMessage
End Get
End Property
Friend Sub New()
End Sub
Friend Sub New(ByVal Message As String)
ErrMessage = Message
End Sub
End Class
Private Overloads Sub DownloadData(ByVal Cursor As String, ByVal Round As Integer, ByVal Token As CancellationToken)
'Const var_init$ = """userID"":""{0}"""
'Const var_cursor$ = """after"":""{1}"",""before"":null,""first"":25,""last"":null,""userID"":""{0}"",""__relay_internal__pv__BarcelonaIsLoggedInrelayprovider"":true,""__relay_internal__pv__BarcelonaIsFeedbackHubEnabledrelayprovider"":false"
Const var_cursor2$ = """after"":{1},""before"":null,""first"":10,""last"":null,""userID"":""{0}"",""__relay_internal__pv__BarcelonaIsLoggedInrelayprovider"":true,""__relay_internal__pv__BarcelonaHasSelfReplyContextrelayprovider"":false,""__relay_internal__pv__BarcelonaShareableListsrelayprovider"":true,""__relay_internal__pv__BarcelonaIsSearchDiscoveryEnabledrelayprovider"":false,""__relay_internal__pv__BarcelonaOptionalCookiesEnabledrelayprovider"":true,""__relay_internal__pv__BarcelonaQuotedPostUFIEnabledrelayprovider"":false,""__relay_internal__pv__BarcelonaIsCrawlerrelayprovider"":false,""__relay_internal__pv__BarcelonaHasDisplayNamesrelayprovider"":false,""__relay_internal__pv__BarcelonaCanSeeSponsoredContentrelayprovider"":false,""__relay_internal__pv__BarcelonaShouldShowFediverseM075Featuresrelayprovider"":true,""__relay_internal__pv__BarcelonaShouldShowTagRedesignrelayprovider"":false,""__relay_internal__pv__BarcelonaIsInternalUserrelayprovider"":false"
Try Try
DownloadCheckCredentials() DownloadCheckCredentials()
Responser.Method = "POST" With Responser
Responser.Referer = $"https://www.threads.net/@{NameTrue}" .Method = "POST"
Responser.Headers.Add(GQL_HEADER_FB_LSD, Token_lsd) .Referer = $"https://www.threads.net/@{NameTrue}"
Responser.Headers.Add(GQL_HEADER_FB_FRINDLY_NAME, GQL_P_NAME) .ContentType = "application/x-www-form-urlencoded"
With .Headers
.Add(GQL_HEADER_FB_LSD, Token_lsd)
.Add(GQL_HEADER_FB_FRINDLY_NAME, GQL_P_NAME)
End With
End With
Dim nextCursor$ = String.Empty Dim nextCursor$ = String.Empty
Dim dataFound As Boolean = False Dim dataFound As Boolean = False
Dim vars$ Dim vars$ = String.Format(var_cursor2, ID, IIf(Cursor.IsEmptyString, "null", $"""{Cursor}"""))
If Cursor.IsEmptyString Then 'If Cursor.IsEmptyString Then
vars = String.Format(var_init, ID) ' vars = String.Format(var_init, ID)
Else 'Else
vars = String.Format(var_cursor, ID, Cursor) ' vars = String.Format(var_cursor, ID, Cursor)
End If 'End If
vars = SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & vars & "}") vars = String.Format(PayloadData, Token_lsd, Token_dtsg_Var, GQL_P_DOC_ID, GQL_P_NAME,
SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & vars & "}"))
URL = String.Format(GQL_Q, Token_lsd, Token_dtsg_Var, GQL_P_DOC_ID, GQL_P_NAME, vars) 'URL = String.Format(GQL_Q, Token_lsd, Token_dtsg_Var, GQL_P_DOC_ID, GQL_P_NAME, vars)
Using j As EContainer = GetDocument(URL, Token) Using j As EContainer = GetDocument(GQL_Q2, vars, Token)
If j.ListExists Then If Not CheckErrors(j) Then
With j({"data", "mediaData"}) If j.ListExists Then
If .ListExists Then With j({"data", "mediaData"})
nextCursor = .Value({"page_info"}, "end_cursor") If .ListExists Then
With .Item({"edges"}) nextCursor = .Value({"page_info"}, "end_cursor")
If .ListExists Then dataFound = DefaultParser(.Self, Sections.Timeline, Token) With .Item({"edges"})
End With If .ListExists Then dataFound = DefaultParser(.Self, Sections.Timeline, Token)
End If End With
End With End If
End With
End If
Else
Throw New JsonErrorException(JsonErrorMessage(j))
End If End If
End Using End Using
If dataFound And Not nextCursor.IsEmptyString Then DownloadData(nextCursor, Token) If dataFound And Not nextCursor.IsEmptyString Then DownloadData(nextCursor, 0, Token)
Catch uex As JsonErrorException
If Round > 0 Then
ProcessJsonErrorException(uex)
ElseIf Not _IdChanged AndAlso UpdateCredentials() Then
DownloadData(Cursor, Round + 1, Token)
Else
ProcessJsonErrorException(uex)
End If
Catch eex As ExitException
Throw eex
Catch ex As Exception Catch ex As Exception
ProcessException(ex, Token, $"data downloading error [{URL}]") ProcessException(ex, Token, "data downloading error")
End Try End Try
End Sub End Sub
Private Sub DownloadSavedPosts(ByVal Cursor As String, ByVal Token As CancellationToken) Private Sub DownloadSavedPosts(ByVal Cursor As String, ByVal Round As Integer, ByVal Token As CancellationToken)
Const var_init$ = """__relay_internal__pv__BarcelonaIsLoggedInrelayprovider"":true,""__relay_internal__pv__BarcelonaIsInlineReelsEnabledrelayprovider"":false,""__relay_internal__pv__BarcelonaUseCometVideoPlaybackEnginerelayprovider"":false,""__relay_internal__pv__BarcelonaOptionalCookiesEnabledrelayprovider"":true,""__relay_internal__pv__BarcelonaIsTextFragmentsEnabledForPostCaptionsrelayprovider"":true,""__relay_internal__pv__BarcelonaShouldShowFediverseM075Featuresrelayprovider"":true" 'Const var_init$ = """__relay_internal__pv__BarcelonaIsLoggedInrelayprovider"":true,""__relay_internal__pv__BarcelonaIsInlineReelsEnabledrelayprovider"":false,""__relay_internal__pv__BarcelonaUseCometVideoPlaybackEnginerelayprovider"":false,""__relay_internal__pv__BarcelonaOptionalCookiesEnabledrelayprovider"":true,""__relay_internal__pv__BarcelonaIsTextFragmentsEnabledForPostCaptionsrelayprovider"":true,""__relay_internal__pv__BarcelonaShouldShowFediverseM075Featuresrelayprovider"":true"
Const var_cursor$ = """after"":""{0}"",""first"":25,""__relay_internal__pv__BarcelonaIsLoggedInrelayprovider"":true,""__relay_internal__pv__BarcelonaIsInlineReelsEnabledrelayprovider"":false,""__relay_internal__pv__BarcelonaUseCometVideoPlaybackEnginerelayprovider"":false,""__relay_internal__pv__BarcelonaOptionalCookiesEnabledrelayprovider"":true,""__relay_internal__pv__BarcelonaIsTextFragmentsEnabledForPostCaptionsrelayprovider"":true,""__relay_internal__pv__BarcelonaShouldShowFediverseM075Featuresrelayprovider"":true" 'Const var_cursor$ = """after"":""{0}"",""first"":25,""__relay_internal__pv__BarcelonaIsLoggedInrelayprovider"":true,""__relay_internal__pv__BarcelonaIsInlineReelsEnabledrelayprovider"":false,""__relay_internal__pv__BarcelonaUseCometVideoPlaybackEnginerelayprovider"":false,""__relay_internal__pv__BarcelonaOptionalCookiesEnabledrelayprovider"":true,""__relay_internal__pv__BarcelonaIsTextFragmentsEnabledForPostCaptionsrelayprovider"":true,""__relay_internal__pv__BarcelonaShouldShowFediverseM075Featuresrelayprovider"":true"
Dim URL$ = String.Empty Const var_cursor2$ = """after"":{0},""first"":25,""__relay_internal__pv__BarcelonaQuotedPostUFIEnabledrelayprovider"":false,""__relay_internal__pv__BarcelonaIsLoggedInrelayprovider"":true,""__relay_internal__pv__BarcelonaHasSelfReplyContextrelayprovider"":false,""__relay_internal__pv__BarcelonaShareableListsrelayprovider"":true,""__relay_internal__pv__BarcelonaIsSearchDiscoveryEnabledrelayprovider"":false,""__relay_internal__pv__BarcelonaOptionalCookiesEnabledrelayprovider"":true,""__relay_internal__pv__BarcelonaIsCrawlerrelayprovider"":false,""__relay_internal__pv__BarcelonaHasDisplayNamesrelayprovider"":false,""__relay_internal__pv__BarcelonaCanSeeSponsoredContentrelayprovider"":false,""__relay_internal__pv__BarcelonaShouldShowFediverseM075Featuresrelayprovider"":true,""__relay_internal__pv__BarcelonaShouldShowTagRedesignrelayprovider"":false,""__relay_internal__pv__BarcelonaIsInternalUserrelayprovider"":false"
Try Try
DownloadCheckCredentials() DownloadCheckCredentials()
Responser.Method = "POST" With Responser
Responser.Referer = "https://www.threads.net/" .Method = "POST"
Responser.Headers.Add(GQL_HEADER_FB_LSD, Token_lsd) .Referer = "https://www.threads.net/"
Responser.Headers.Add(GQL_HEADER_FB_FRINDLY_NAME, If(Cursor.IsEmptyString, GQL_S_NAME_1, GQL_S_NAME_2)) .ContentType = "application/x-www-form-urlencoded"
With .Headers
.Add(GQL_HEADER_FB_LSD, Token_lsd)
'.Add(GQL_HEADER_FB_FRINDLY_NAME, If(Cursor.IsEmptyString, GQL_S_NAME_1, GQL_S_NAME_2))
.Add(GQL_HEADER_FB_FRINDLY_NAME, GQL_S_NAME_2)
End With
End With
Dim nextCursor$ = String.Empty Dim nextCursor$ = String.Empty
Dim dataFound As Boolean = False Dim dataFound As Boolean = False
Dim vars$ = SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & If(Cursor.IsEmptyString, var_init, String.Format(var_cursor, Cursor)) & "}") 'Dim vars$ = SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & If(Cursor.IsEmptyString, var_init, String.Format(var_cursor, Cursor)) & "}")
Dim vars$ = String.Format(PayloadData, Token_lsd, Token_dtsg_Var, GQL_S_DOC_ID_2, GQL_S_NAME_2,
SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & String.Format(var_cursor2, IIf(Cursor.IsEmptyString, "null", $"""{Cursor}""")) & "}"))
If Cursor.IsEmptyString Then 'If Cursor.IsEmptyString Then
URL = String.Format(GQL_Q, Token_lsd, Token_dtsg_Var, GQL_S_DOC_ID_1, GQL_S_NAME_1, vars) ' URL = String.Format(GQL_Q, Token_lsd, Token_dtsg_Var, GQL_S_DOC_ID_1, GQL_S_NAME_1, vars)
Else 'Else
URL = String.Format(GQL_Q, Token_lsd, Token_dtsg_Var, GQL_S_DOC_ID_2, GQL_S_NAME_2, vars) ' URL = String.Format(GQL_Q, Token_lsd, Token_dtsg_Var, GQL_S_DOC_ID_2, GQL_S_NAME_2, vars)
End If 'End If
Using j As EContainer = GetDocument(URL, Token) Using j As EContainer = GetDocument(GQL_Q2, vars, Token)
If j.ListExists Then If Not CheckErrors(j) Then
With j({"data", "xdt_viewer", "text_app_saved_media"}) If j.ListExists Then
If .ListExists Then With j({"data", "xdt_text_app_viewer", "saved_media"})
nextCursor = .Value({"page_info"}, "end_cursor") If .ListExists Then
With .Item({"edges"}) nextCursor = .Value({"page_info"}, "end_cursor")
If .ListExists Then dataFound = DefaultParser(.Self, Sections.Timeline, Token) With .Item({"edges"})
End With If .ListExists Then dataFound = DefaultParser(.Self, Sections.Timeline, Token)
End If End With
End With End If
End With
End If
Else
Throw New JsonErrorException(JsonErrorMessage(j))
End If End If
End Using End Using
If dataFound And Not nextCursor.IsEmptyString Then DownloadSavedPosts(nextCursor, Token) If dataFound And Not nextCursor.IsEmptyString Then DownloadSavedPosts(nextCursor, 0, Token)
Catch uex As JsonErrorException
If Round > 0 Then
ProcessJsonErrorException(uex)
ElseIf Not _IdChanged AndAlso UpdateCredentials() Then
DownloadSavedPosts(Cursor, Round + 1, Token)
Else
ProcessJsonErrorException(uex)
End If
Catch eex As ExitException
Throw eex
Catch ex As Exception Catch ex As Exception
ProcessException(ex, Token, $"saved posts downloading error [{URL}]") ProcessException(ex, Token, "saved posts downloading error")
End Try End Try
End Sub End Sub
Private Function GetDocument(ByVal URL As String, ByVal Token As CancellationToken, Optional ByVal Round As Integer = 0) As EContainer Private Function GetDocument(ByVal URL As String, ByVal PayLoad As String, ByVal Token As CancellationToken, Optional ByVal Round As Integer = 0) As EContainer
Try Try
ThrowAny(Token) ThrowAny(Token)
If Round > 0 AndAlso Not UpdateCredentials() Then DisableDownload() : Throw New Exception("Failed to update credentials") If Round > 0 AndAlso Not UpdateCredentials() Then DisableDownload() : Throw New Exception("Failed to update credentials")
ThrowAny(Token) ThrowAny(Token)
WaitTimer() WaitTimer()
Dim r$ = Responser.GetResponse(URL) Dim r$ = Responser.GetResponse(URL, PayLoad)
If Not r.IsEmptyString Then Return JsonDocument.Parse(r) Else Throw New Exception("Failed to get a response") If Not r.IsEmptyString Then Return JsonDocument.Parse(r) Else Throw New Exception("Failed to get a response")
Catch ex As Exception Catch ex As Exception
If Round = 0 Then If Round = 0 Then
Return GetDocument(URL, Token, Round + 1) Return GetDocument(URL, PayLoad, Token, Round + 1)
Else Else
Throw ex Throw ex
End If End If
End Try End Try
End Function End Function
Private _IdChanged As Boolean = False
Private Function UpdateCredentials(Optional ByVal e As ErrorsDescriber = Nothing) As Boolean Private Function UpdateCredentials(Optional ByVal e As ErrorsDescriber = Nothing) As Boolean
Dim URL$ = If(IsSavedPosts, "https://www.threads.net/", $"https://www.threads.net/@{NameTrue}") Dim URL$ = If(IsSavedPosts, "https://www.threads.net/", $"https://www.threads.net/@{NameTrue}")
ResetBaseTokens() ResetBaseTokens()
@@ -293,9 +382,25 @@ Namespace API.ThreadsNet
End With End With
WaitTimer() WaitTimer()
Dim r$ = Responser.GetResponse(URL,, EDP.ThrowException) Dim r$ = Responser.GetResponse(URL,, EDP.ThrowException)
Dim newID$
Dim idStr$ = String.Empty
If Not r.IsEmptyString Then If Not r.IsEmptyString Then
ParseTokens(r, 0) ParseTokens(r, 0)
If ID.IsEmptyString Then ID = RegexReplace(r, RParams.DMS("""props"":\{""user_id"":""(\d+)""", 1, EDP.ReturnValue)) newID = RegexReplace(r, RParams.DMS("""props"":\{[^\{\}]*?""user_id"":""(\d+)""", 1, EDP.ReturnValue))
If ID.IsEmptyString OrElse ID = newID Then
_IdChanged = ID.IsEmptyString
ID = newID
Else
_IdChanged = True
idStr = $"user ID changed from {ID} to {newID}"
LogError(Nothing, idStr)
ID = newID
End If
If _IdChanged Then
If Not idStr.IsEmptyString Then UserDescriptionUpdate(idStr, True, True, True)
_ForceSaveUserInfo = True
_ForceSaveUserInfoOnException = True
End If
End If End If
Return Valid Return Valid
Catch ex As Exception Catch ex As Exception
@@ -321,20 +426,22 @@ Namespace API.ThreadsNet
#End Region #End Region
#Region "ReparseMissing" #Region "ReparseMissing"
Protected Overrides Sub ReparseMissing(ByVal Token As CancellationToken) Protected Overrides Sub ReparseMissing(ByVal Token As CancellationToken)
Const varsPattern$ = """postID"":""{0}"",""userID"":""{1}"",""__relay_internal__pv__BarcelonaIsLoggedInrelayprovider"":true,""__relay_internal__pv__BarcelonaIsFeedbackHubEnabledrelayprovider"":false" 'Const varsPattern$ = """postID"":""{0}"",""userID"":""{1}"",""__relay_internal__pv__BarcelonaIsLoggedInrelayprovider"":true,""__relay_internal__pv__BarcelonaIsFeedbackHubEnabledrelayprovider"":false"
Const varsPattern$ = """postID"":""{0}"",""__relay_internal__pv__BarcelonaIsLoggedInrelayprovider"":true,""__relay_internal__pv__BarcelonaShouldShowFediverseM1Featuresrelayprovider"":true,""__relay_internal__pv__BarcelonaShareableListsrelayprovider"":true,""__relay_internal__pv__BarcelonaIsSearchDiscoveryEnabledrelayprovider"":false,""__relay_internal__pv__BarcelonaOptionalCookiesEnabledrelayprovider"":true,""__relay_internal__pv__BarcelonaQuotedPostUFIEnabledrelayprovider"":false,""__relay_internal__pv__BarcelonaIsCrawlerrelayprovider"":false,""__relay_internal__pv__BarcelonaHasDisplayNamesrelayprovider"":false,""__relay_internal__pv__BarcelonaCanSeeSponsoredContentrelayprovider"":false,""__relay_internal__pv__BarcelonaShouldShowFediverseM075Featuresrelayprovider"":true,""__relay_internal__pv__BarcelonaShouldShowTagRedesignrelayprovider"":false,""__relay_internal__pv__BarcelonaIsInternalUserrelayprovider"":false,""__relay_internal__pv__BarcelonaInlineComposerEnabledrelayprovider"":false"
'Const varsPattern$ = "{""postID"":""{0}"",""__relay_internal__pv__BarcelonaIsLoggedInrelayprovider"":true,""__relay_internal__pv__BarcelonaIsFeedbackHubEnabledrelayprovider"":false}" 'Const varsPattern$ = "{""postID"":""{0}"",""__relay_internal__pv__BarcelonaIsLoggedInrelayprovider"":true,""__relay_internal__pv__BarcelonaIsFeedbackHubEnabledrelayprovider"":false}"
Const urlPattern$ = "https://www.threads.net/api/graphql?lsd={0}&variables={1}&fb_api_req_friendly_name=BarcelonaPostPageQuery&server_timestamps=true&fb_dtsg={2}&doc_id=25460088156920903" 'Const urlPattern$ = "https://www.threads.net/api/graphql?lsd={0}&variables={1}&fb_api_req_friendly_name=BarcelonaPostPageQuery&server_timestamps=true&fb_dtsg={2}&doc_id=25460088156920903"
Dim rList As New List(Of Integer) Dim rList As New List(Of Integer)
Dim URL$ = String.Empty
DefaultParser_ElemNode = Nothing DefaultParser_ElemNode = Nothing
DefaultParser_IgnorePass = True DefaultParser_IgnorePass = True
Try Try
If ContentMissingExists Then If ContentMissingExists Then
Responser.Method = "POST" Responser.Method = "POST"
Responser.ContentType = "application/x-www-form-urlencoded"
Responser.Referer = $"https://www.threads.net/@{NameTrue}" Responser.Referer = $"https://www.threads.net/@{NameTrue}"
If Not IsSingleObjectDownload AndAlso Not UpdateCredentials() Then Throw New Exception("Failed to update credentials") If Not IsSingleObjectDownload AndAlso Not UpdateCredentials() Then Throw New Exception("Failed to update credentials")
Dim m As UserMedia Dim m As UserMedia
Dim vars$ Dim vars$
Dim r As Byte
Dim j As EContainer Dim j As EContainer
ProgressPre.ChangeMax(_ContentList.Count) ProgressPre.ChangeMax(_ContentList.Count)
For i% = 0 To _ContentList.Count - 1 For i% = 0 To _ContentList.Count - 1
@@ -342,21 +449,39 @@ Namespace API.ThreadsNet
m = _ContentList(i) m = _ContentList(i)
If m.State = UserMedia.States.Missing And Not m.Post.ID.IsEmptyString Then If m.State = UserMedia.States.Missing And Not m.Post.ID.IsEmptyString Then
ThrowAny(Token) ThrowAny(Token)
vars = SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & String.Format(varsPattern, m.Post.ID.Split("_").FirstOrDefault, ID) & "}") 'vars = SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & String.Format(varsPattern, m.Post.ID.Split("_").FirstOrDefault, ID) & "}")
URL = String.Format(urlPattern, Token_lsd, vars, Token_dtsg_Var) 'URL = String.Format(urlPattern, Token_lsd, vars, Token_dtsg_Var)
j = GetDocument(URL, Token) vars = String.Format(PayloadData, Token_lsd, Token_dtsg_Var, "9094233770675261", "BarcelonaPostPageDirectQuery",
If j.ListExists Then SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & String.Format(varsPattern, m.Post.ID) & "}"))
With j.ItemF({"data", "data", "edges", 0, "node", "thread_items", 0, "post"})
If .ListExists AndAlso DefaultParser({ .Self}, Sections.Timeline, Token) Then rList.Add(i) For r = 0 To 1
End With j = GetDocument(GQL_Q2, vars, Token)
j.Dispose() If Not CheckErrors(j) Then
End If If j.ListExists Then
With j.ItemF({"data", "data", "edges", 0, "node", "thread_items", 0, "post"})
If .ListExists AndAlso DefaultParser({ .Self}, Sections.Timeline, Token) Then rList.Add(i)
End With
j.Dispose()
End If
Else
j.DisposeIfReady(False)
If r > 0 Then
ProcessJsonErrorException(New JsonErrorException(JsonErrorMessage(j)))
ElseIf Not _IdChanged AndAlso UpdateCredentials() Then
Continue For
Else
ProcessJsonErrorException(New JsonErrorException(JsonErrorMessage(j)))
End If
End If
Next
End If End If
Next Next
End If End If
Catch eex As ExitException
Throw eex
Catch ex As Exception Catch ex As Exception
ProcessException(ex, Token, $"ReparseMissing error [{URL}]") ProcessException(ex, Token, "reparseMissing error")
Finally Finally
DefaultParser_ElemNode = DefaultParser_ElemNode_Default DefaultParser_ElemNode = DefaultParser_ElemNode_Default
DefaultParser_IgnorePass = False DefaultParser_IgnorePass = False
@@ -374,9 +499,9 @@ Namespace API.ThreadsNet
If Not postCode.IsEmptyString Then If Not postCode.IsEmptyString Then
Dim postId$ = CodeToID(postCode) Dim postId$ = CodeToID(postCode)
If Not postId.IsEmptyString Then If Not postId.IsEmptyString Then
_NameTrue = MySettings.IsMyUser(url).UserName NameTrue = MySettings.IsMyUser(url).UserName
DefaultParser_PostUrlCreator = Function(post) url DefaultParser_PostUrlCreator = Function(post) url
If Not _NameTrue.IsEmptyString AndAlso UpdateCredentials(EDP.ReturnValue) Then If Not NameTrue(True).IsEmptyString AndAlso UpdateCredentials(EDP.ReturnValue) Then
_ContentList.Add(New UserMedia(url) With {.State = UserMedia.States.Missing, .Post = postId}) _ContentList.Add(New UserMedia(url) With {.State = UserMedia.States.Missing, .Post = postId})
ReparseMissing(Token) ReparseMissing(Token)
End If End If

View File

@@ -62,8 +62,5 @@ Namespace API.TikTok
Using f As New InternalSettingsForm(Options, Me, False) : f.ShowDialog() : End Using Using f As New InternalSettingsForm(Options, Me, False) : f.ShowDialog() : End Using
End If End If
End Sub End Sub
Friend Overrides Function GetUserUrl(ByVal User As IPluginContentProvider) As String
Return String.Format(UrlPatternUser, DirectCast(User, UserData).TrueName)
End Function
End Class End Class
End Namespace End Namespace

View File

@@ -62,15 +62,6 @@ Namespace API.TikTok
Friend Property TitleUseRegexForTitle_Value As String = String.Empty Friend Property TitleUseRegexForTitle_Value As String = String.Empty
Friend Property TitleUseGlobalRegexOptions As Boolean = True Friend Property TitleUseGlobalRegexOptions As Boolean = True
Private Property LastDownloadDate As Date? = Nothing Private Property LastDownloadDate As Date? = Nothing
Private _TrueName As String = String.Empty
Friend Property TrueName As String
Get
Return _TrueName.IfNullOrEmpty(Name)
End Get
Set(ByVal NewName As String)
_TrueName = NewName
End Set
End Property
#End Region #End Region
#Region "Exchange" #Region "Exchange"
Friend Overrides Function ExchangeOptionsGet() As Object Friend Overrides Function ExchangeOptionsGet() As Object
@@ -98,7 +89,6 @@ Namespace API.TikTok
TitleAddVideoID = .Value(Name_TitleAddVideoID).FromXML(Of Boolean)(True) TitleAddVideoID = .Value(Name_TitleAddVideoID).FromXML(Of Boolean)(True)
LastDownloadDate = AConvert(Of Date)(.Value(Name_LastDownloadDate), ADateTime.Formats.BaseDateTime, Nothing) LastDownloadDate = AConvert(Of Date)(.Value(Name_LastDownloadDate), ADateTime.Formats.BaseDateTime, Nothing)
If Not LastDownloadDate.HasValue Then LastDownloadDate = LastUpdated If Not LastDownloadDate.HasValue Then LastDownloadDate = LastUpdated
_TrueName = .Value(Name_TrueName)
TitleUseRegexForTitle = .Value(Name_TitleUseRegexForTitle).FromXML(Of Boolean)(False) TitleUseRegexForTitle = .Value(Name_TitleUseRegexForTitle).FromXML(Of Boolean)(False)
TitleUseRegexForTitle_Value = .Value(Name_TitleUseRegexForTitle_Value) TitleUseRegexForTitle_Value = .Value(Name_TitleUseRegexForTitle_Value)
TitleUseGlobalRegexOptions = .Value(Name_TitleUseGlobalRegexOptions).FromXML(Of Boolean)(True) TitleUseGlobalRegexOptions = .Value(Name_TitleUseGlobalRegexOptions).FromXML(Of Boolean)(True)
@@ -107,7 +97,6 @@ Namespace API.TikTok
.Add(Name_TitleUseNative, TitleUseNative.BoolToInteger) .Add(Name_TitleUseNative, TitleUseNative.BoolToInteger)
.Add(Name_TitleAddVideoID, TitleAddVideoID.BoolToInteger) .Add(Name_TitleAddVideoID, TitleAddVideoID.BoolToInteger)
.Add(Name_LastDownloadDate, AConvert(Of String)(LastDownloadDate, AModes.XML, ADateTime.Formats.BaseDateTime, String.Empty)) .Add(Name_LastDownloadDate, AConvert(Of String)(LastDownloadDate, AModes.XML, ADateTime.Formats.BaseDateTime, String.Empty))
.Add(Name_TrueName, _TrueName)
.Add(Name_TitleUseRegexForTitle, TitleUseRegexForTitle.BoolToInteger) .Add(Name_TitleUseRegexForTitle, TitleUseRegexForTitle.BoolToInteger)
.Add(Name_TitleUseRegexForTitle_Value, TitleUseRegexForTitle_Value) .Add(Name_TitleUseRegexForTitle_Value, TitleUseRegexForTitle_Value)
.Add(Name_TitleUseGlobalRegexOptions, TitleUseGlobalRegexOptions.BoolToInteger) .Add(Name_TitleUseGlobalRegexOptions, TitleUseGlobalRegexOptions.BoolToInteger)
@@ -174,7 +163,7 @@ Namespace API.TikTok
UserCache = Nothing UserCache = Nothing
End Sub End Sub
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken) Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
Dim URL$ = $"https://www.tiktok.com/@{TrueName}" Dim URL$ = $"https://www.tiktok.com/@{NameTrue}"
UserCache = CreateCache() UserCache = CreateCache()
Try Try
Dim postID$, title$, postUrl$, newName$ Dim postID$, title$, postUrl$, newName$
@@ -232,10 +221,7 @@ Namespace API.TikTok
If Not ID.IsEmptyString Then _ForceSaveUserInfo = True If Not ID.IsEmptyString Then _ForceSaveUserInfo = True
End If End If
newName = j.Value("uploader") newName = j.Value("uploader")
If Not newName.IsEmptyString Then If Not newName.IsEmptyString Then NameTrue = newName
If Not _TrueName = newName Then _ForceSaveUserInfo = True
_TrueName = newName
End If
newName = j.Value("creator") newName = j.Value("creator")
If Not newName.IsEmptyString Then UserSiteName = newName If Not newName.IsEmptyString Then UserSiteName = newName
End If End If

View File

@@ -7,7 +7,7 @@
' This program is distributed in the hope that it will be useful, ' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY ' but WITHOUT ANY WARRANTY
Imports System.Globalization Imports System.Globalization
Imports PersonalUtilities.Functions.XML.Base Imports System.Text.RegularExpressions
Imports PersonalUtilities.Functions.RegularExpressions Imports PersonalUtilities.Functions.RegularExpressions
Namespace API.Twitter Namespace API.Twitter
Friend Module Declarations Friend Module Declarations
@@ -16,6 +16,9 @@ Namespace API.Twitter
Friend ReadOnly DateProvider As ADateTime = GetDateProvider() Friend ReadOnly DateProvider As ADateTime = GetDateProvider()
Friend ReadOnly VideoSizeRegEx As RParams = RParams.DMS("\d+x(\d+)", 1, EDP.ReturnValue) Friend ReadOnly VideoSizeRegEx As RParams = RParams.DMS("\d+x(\d+)", 1, EDP.ReturnValue)
Friend ReadOnly StatusRegEx As RParams = RParams.DM(".*?(twitter|x)\.com/\S+/status/\d+", 0, EDP.ReturnValue) Friend ReadOnly StatusRegEx As RParams = RParams.DM(".*?(twitter|x)\.com/\S+/status/\d+", 0, EDP.ReturnValue)
Friend ReadOnly BroadcastsUrls As Object() = {"entities", "urls", 0, "expanded_url"}
Friend ReadOnly GdlLimitRegEx As RParams = RParams.DM("Waiting until[\s\W\d\:]+\(rate limit\)", 0, RegexOptions.IgnoreCase, EDP.ReturnValue)
Friend ReadOnly GdlPostCoutNumberNodes As String() = {"data", "user", "result", "legacy", "statuses_count"}
Private Function GetDateProvider() As ADateTime Private Function GetDateProvider() As ADateTime
Dim n As DateTimeFormatInfo = CultureInfo.GetCultureInfo("en-us").DateTimeFormat.Clone Dim n As DateTimeFormatInfo = CultureInfo.GetCultureInfo("en-us").DateTimeFormat.Clone
n.FullDateTimePattern = "ddd MMM dd HH:mm:ss +ffff yyyy" n.FullDateTimePattern = "ddd MMM dd HH:mm:ss +ffff yyyy"

View File

@@ -8,11 +8,10 @@
' but WITHOUT ANY WARRANTY ' but WITHOUT ANY WARRANTY
Imports SCrawler.Plugin.Attributes Imports SCrawler.Plugin.Attributes
Imports DModels = SCrawler.API.Twitter.UserData.DownloadModels Imports DModels = SCrawler.API.Twitter.UserData.DownloadModels
Imports DN = SCrawler.API.Base.DeclaredNames
Namespace API.Twitter Namespace API.Twitter
Friend Class EditorExchangeOptions Friend Class EditorExchangeOptions : Inherits Base.EditorExchangeOptionsBase
Private Const DefaultOffset As Integer = 100 Private Const DefaultOffset As Integer = 100
Friend Property SiteKey As String = TwitterSiteKey Friend Overrides Property SiteKey As String = TwitterSiteKey
<PSetting(NameOf(SiteSettings.GifsDownload), NameOf(MySettings), LeftOffset:=DefaultOffset)> <PSetting(NameOf(SiteSettings.GifsDownload), NameOf(MySettings), LeftOffset:=DefaultOffset)>
Friend Property GifsDownload As Boolean Friend Property GifsDownload As Boolean
<PSetting(NameOf(SiteSettings.GifsSpecialFolder), NameOf(MySettings), LeftOffset:=DefaultOffset)> <PSetting(NameOf(SiteSettings.GifsSpecialFolder), NameOf(MySettings), LeftOffset:=DefaultOffset)>
@@ -43,12 +42,14 @@ Namespace API.Twitter
Caption:="Download model 'Likes'", Caption:="Download model 'Likes'",
ToolTip:="Download the data using the 'https://x.com/UserName/likes' command.", LeftOffset:=DefaultOffset)> ToolTip:="Download the data using the 'https://x.com/UserName/likes' command.", LeftOffset:=DefaultOffset)>
Friend Overridable Property DownloadModelLikes As Boolean = False Friend Overridable Property DownloadModelLikes As Boolean = False
<PSetting(Address:=SettingAddress.User,
Caption:="Download 'Broadcasts'",
ToolTip:="Download broadcasts posted by the user using the 'https://x.com/i/broadcasts/abcdef1234567' URLs", LeftOffset:=DefaultOffset)>
Friend Overridable Property DownloadBroadcasts As Boolean = False
<PSetting(Address:=SettingAddress.User, <PSetting(Address:=SettingAddress.User,
Caption:="Force apply", Caption:="Force apply",
ToolTip:="Force overrides the default parameters for the first download." & vbCr & "Applies to first download only.", LeftOffset:=DefaultOffset)> ToolTip:="Force overrides the default parameters for the first download." & vbCr & "Applies to first download only.", LeftOffset:=DefaultOffset)>
Friend Overridable Property DownloadModelForceApply As Boolean = False Friend Overridable Property DownloadModelForceApply As Boolean = False
<PSetting(Address:=SettingAddress.User, Caption:=DN.UserNameChangeCaption, ToolTip:=DN.UserNameChangeToolTip, LeftOffset:=DefaultOffset)>
Friend Overridable Property UserName As String = String.Empty
Private ReadOnly Property MySettings As Object Private ReadOnly Property MySettings As Object
Friend Sub New(ByVal s As SiteSettings) Friend Sub New(ByVal s As SiteSettings)
GifsDownload = s.GifsDownload.Value GifsDownload = s.GifsDownload.Value
@@ -67,6 +68,7 @@ Namespace API.Twitter
MySettings = s MySettings = s
End Sub End Sub
Friend Sub New(ByVal u As UserData) Friend Sub New(ByVal u As UserData)
MyBase.New(u)
GifsDownload = u.GifsDownload GifsDownload = u.GifsDownload
GifsSpecialFolder = u.GifsSpecialFolder GifsSpecialFolder = u.GifsSpecialFolder
GifsPrefix = u.GifsPrefix GifsPrefix = u.GifsPrefix
@@ -75,6 +77,7 @@ Namespace API.Twitter
MediaModelAllowNonUserTweets = u.MediaModelAllowNonUserTweets MediaModelAllowNonUserTweets = u.MediaModelAllowNonUserTweets
If Not TypeOf u Is Mastodon.UserData Then If Not TypeOf u Is Mastodon.UserData Then
DownloadModelForceApply = u.DownloadModelForceApply DownloadModelForceApply = u.DownloadModelForceApply
DownloadBroadcasts = u.DownloadBroadcasts
Dim dm As DModels() = EnumExtract(Of DModels)(u.DownloadModel) Dim dm As DModels() = EnumExtract(Of DModels)(u.DownloadModel)
If dm.ListExists Then If dm.ListExists Then
DownloadModelMedia = dm.Contains(DModels.Media) DownloadModelMedia = dm.Contains(DModels.Media)
@@ -83,7 +86,6 @@ Namespace API.Twitter
DownloadModelLikes = dm.Contains(DModels.Likes) DownloadModelLikes = dm.Contains(DModels.Likes)
End If End If
End If End If
UserName = u.NameTrue(True)
MySettings = u.HOST.Source MySettings = u.HOST.Source
End Sub End Sub
End Class End Class

View File

@@ -11,14 +11,50 @@ Imports SCrawler.Plugin
Imports SCrawler.Plugin.Attributes Imports SCrawler.Plugin.Attributes
Imports PersonalUtilities.Functions.RegularExpressions Imports PersonalUtilities.Functions.RegularExpressions
Imports PersonalUtilities.Tools.Web.Clients Imports PersonalUtilities.Tools.Web.Clients
Imports PersonalUtilities.Tools.Web.Cookies
Imports DN = SCrawler.API.Base.DeclaredNames Imports DN = SCrawler.API.Base.DeclaredNames
Imports IG = SCrawler.API.Instagram.SiteSettings
Namespace API.Twitter Namespace API.Twitter
<Manifest(TwitterSiteKey), SavedPosts, SeparatedTasks, SpecialForm(False)> <Manifest(TwitterSiteKey), SavedPosts, SeparatedTasks, SpecialForm(False)>
Friend Class SiteSettings : Inherits SiteSettingsBase Friend Class SiteSettings : Inherits SiteSettingsBase
#Region "Declarations" #Region "Declarations"
#Region "Icon"
<PXML("UseNewIcon")> Private ReadOnly Property UseNewIconXML As PropertyValue
<PropertyOption(ControlText:="Use the new Twitter icon (X)", ControlToolTip:="Restart SCrawler to take effect")>
Private ReadOnly Property UseNewIcon As PropertyValue
Get
If Not DefaultInstance Is Nothing Then
Return DirectCast(DefaultInstance, SiteSettings).UseNewIconXML
Else
Return UseNewIconXML
End If
End Get
End Property
Private Sub UpdateIcon()
If CBool(UseNewIcon.Value) Then _Icon = My.Resources.SiteResources.TwitterIconNew_32 : _Image = _Icon.ToBitmap
End Sub
#End Region
#Region "Categories" #Region "Categories"
Private Const CAT_DOWN As String = "Downloading" Private Const CAT_DOWN As String = "Downloading"
#End Region #End Region
#Region "Auth"
<PropertyOption(ControlText:="Update cookies", ControlToolTip:="Update cookies during requests", IsAuth:=True), PXML, PClonable, HiddenControl>
Friend ReadOnly Property CookiesUpdate As PropertyValue
<PropertyOption(ControlText:="Use UserAgent", ControlToolTip:="Use UserAgent in requests", IsAuth:=True), PXML, PClonable>
Friend ReadOnly Property UserAgentUse As PropertyValue
<PropertyOption(ControlText:="UserAgent", IsAuth:=True, AllowNull:=True, InheritanceName:=SettingsCLS.HEADER_DEF_UserAgent),
PXML("UserAgent", OnlyForChecked:=True), PClonable>
Private ReadOnly Property UserAgentXML As PropertyValue
Friend ReadOnly Property UserAgent As String
Get
If CBool(UserAgentUse.Value) AndAlso Not CStr(UserAgentXML.Value).IsEmptyString Then
Return UserAgentXML.Value
Else
Return String.Empty
End If
End Get
End Property
#End Region
#Region "Other properties" #Region "Other properties"
<PropertyOption(ControlText:="Use the appropriate model", <PropertyOption(ControlText:="Use the appropriate model",
ControlToolTip:="Use the appropriate model for new users." & vbCr & ControlToolTip:="Use the appropriate model for new users." & vbCr &
@@ -35,10 +71,30 @@ Namespace API.Twitter
Friend Property UseNewEndPointProfiles As PropertyValue Friend Property UseNewEndPointProfiles As PropertyValue
#End Region #End Region
#Region "Limits" #Region "Limits"
Friend Const TimerDisabled As Integer = -1
Friend Const TimerFirstUseTheSame As Integer = -2
<PropertyOption(ControlText:="Abort on limit", ControlToolTip:="Abort twitter downloading when limit is reached", Category:=CAT_DOWN), PXML, PClonable> <PropertyOption(ControlText:="Abort on limit", ControlToolTip:="Abort twitter downloading when limit is reached", Category:=CAT_DOWN), PXML, PClonable>
Friend Property AbortOnLimit As PropertyValue Friend Property AbortOnLimit As PropertyValue
<PropertyOption(ControlText:="Download already parsed", ControlToolTip:="Download already parsed content on abort", Category:=CAT_DOWN), PXML, PClonable> <PropertyOption(ControlText:="Download already parsed", ControlToolTip:="Download already parsed content on abort", Category:=CAT_DOWN), PXML, PClonable>
Friend Property DownloadAlreadyParsed As PropertyValue Friend Property DownloadAlreadyParsed As PropertyValue
#End Region
#Region "Timers"
<PropertyOption(ControlText:="Sleep timer",
ControlToolTip:="Use sleep timer in requests." & vbCr &
"You can set a timer value (in seconds) to wait before each subsequent request." & vbCr &
"-1 to disable and use the default algorithm." & vbCr &
"Default: 20", Category:=CAT_DOWN), PXML, PClonable>
Friend ReadOnly Property SleepTimer As PropertyValue
<Provider(NameOf(SleepTimer), FieldsChecker:=True)>
Private ReadOnly Property SleepTimerProvider As IFormatProvider
<PropertyOption(ControlText:="Sleep timer at start",
ControlToolTip:="Set a sleep timer (in seconds) before the first request." & vbCr &
"-1 to disable" & vbCr &
"-2 to use the 'Sleep timer' value" & vbCr &
"Default: -2", Category:=CAT_DOWN), PXML, PClonable>
Friend ReadOnly Property SleepTimerBeforeFirst As PropertyValue
<Provider(NameOf(SleepTimerBeforeFirst), FieldsChecker:=True)>
Private ReadOnly Property SleepTimerBeforeFirstProvider As IFormatProvider
#End Region #End Region
<PropertyOption(ControlText:="Media Model: allow non-user tweets", ControlToolTip:="Allow downloading non-user tweets in the media-model.", Category:=DN.CAT_UserDefs), PXML, PClonable> <PropertyOption(ControlText:="Media Model: allow non-user tweets", ControlToolTip:="Allow downloading non-user tweets in the media-model.", Category:=DN.CAT_UserDefs), PXML, PClonable>
Friend ReadOnly Property MediaModelAllowNonUserTweets As PropertyValue Friend ReadOnly Property MediaModelAllowNonUserTweets As PropertyValue
@@ -76,7 +132,17 @@ Namespace API.Twitter
<Provider(NameOf(ConcurrentDownloads), FieldsChecker:=True)> <Provider(NameOf(ConcurrentDownloads), FieldsChecker:=True)>
Private ReadOnly Property MyConcurrentDownloadsProvider As IFormatProvider Private ReadOnly Property MyConcurrentDownloadsProvider As IFormatProvider
#End Region #End Region
Friend Overrides Property DefaultInstance As ISiteSettings
Get
Return MyBase.DefaultInstance
End Get
Set(ByVal Instance As ISiteSettings)
MyBase.DefaultInstance = Instance
If Not Instance Is Nothing Then UpdateIcon()
End Set
End Property
#End Region #End Region
#Region "Initializer"
Friend Sub New(ByVal AccName As String, ByVal Temp As Boolean) Friend Sub New(ByVal AccName As String, ByVal Temp As Boolean)
MyBase.New(TwitterSite, "x.com", AccName, Temp, My.Resources.SiteResources.TwitterIcon_32, My.Resources.SiteResources.TwitterIcon_32.ToBitmap) MyBase.New(TwitterSite, "x.com", AccName, Temp, My.Resources.SiteResources.TwitterIcon_32, My.Resources.SiteResources.TwitterIcon_32.ToBitmap)
@@ -87,11 +153,28 @@ Namespace API.Twitter
.Cookies.Changed = False .Cookies.Changed = False
End With End With
UseNewIconXML = New PropertyValue(False)
CookiesUpdate = New PropertyValue(False)
UserAgentUse = New PropertyValue(True)
UserAgentXML = New PropertyValue(If(Responser.UserAgentExists, Responser.UserAgent, String.Empty), GetType(String),
Sub(ByVal Value As Object)
Responser.UserAgent = CStr(Value)
Responser.SaveSettings(, EDP.ReturnValue)
End Sub)
UseAppropriateModel = New PropertyValue(True) UseAppropriateModel = New PropertyValue(True)
UseNewEndPointSearch = New PropertyValue(True) UseNewEndPointSearch = New PropertyValue(True)
UseNewEndPointProfiles = New PropertyValue(True) UseNewEndPointProfiles = New PropertyValue(True)
AbortOnLimit = New PropertyValue(True) AbortOnLimit = New PropertyValue(True)
DownloadAlreadyParsed = New PropertyValue(True) DownloadAlreadyParsed = New PropertyValue(True)
SleepTimer = New PropertyValue(TimerDisabled)
SleepTimerProvider = New IG.TimersChecker(TimerDisabled)
SleepTimerBeforeFirst = New PropertyValue(TimerFirstUseTheSame)
SleepTimerBeforeFirstProvider = New IG.TimersChecker(TimerFirstUseTheSame)
MediaModelAllowNonUserTweets = New PropertyValue(False) MediaModelAllowNonUserTweets = New PropertyValue(False)
GifsDownload = New PropertyValue(True) GifsDownload = New PropertyValue(True)
GifsSpecialFolder = New PropertyValue(String.Empty, GetType(String)) GifsSpecialFolder = New PropertyValue(String.Empty, GetType(String))
@@ -101,28 +184,60 @@ Namespace API.Twitter
ConcurrentDownloads = New PropertyValue(1) ConcurrentDownloads = New PropertyValue(1)
MyConcurrentDownloadsProvider = New ConcurrentDownloadsProvider MyConcurrentDownloadsProvider = New ConcurrentDownloadsProvider
_AllowUserAgentUpdate = False
UserRegex = RParams.DMS(String.Format(UserRegexDefaultPattern, $"/(twitter|x).com({CommunitiesUser}|)/"), 3) UserRegex = RParams.DMS(String.Format(UserRegexDefaultPattern, $"/(twitter|x).com({CommunitiesUser}|)/"), 3)
UrlPatternUser = "https://x.com/{0}" UrlPatternUser = "https://x.com/{0}"
ImageVideoContains = "twitter" ImageVideoContains = "twitter"
CheckNetscapeCookiesOnEndInit = True CheckNetscapeCookiesOnEndInit = True
UseNetscapeCookies = True UseNetscapeCookies = True
End Sub End Sub
Friend Overrides Sub EndInit()
UpdateIcon()
MyBase.EndInit()
End Sub
#End Region
#Region "GetInstance"
Friend Overrides Function GetInstance(ByVal What As ISiteSettings.Download) As IPluginContentProvider Friend Overrides Function GetInstance(ByVal What As ISiteSettings.Download) As IPluginContentProvider
Return New UserData Return New UserData
End Function End Function
Friend Const SinglePostPattern As String = "https://x.com/i/web/status/{0}" #End Region
Friend Overrides Function GetUserPostUrl(ByVal User As UserDataBase, ByVal Media As UserMedia) As String #Region "BaseAuthExists, Available"
Return String.Format(SinglePostPattern, Media.Post.ID)
End Function
Friend Overrides Function BaseAuthExists() As Boolean Friend Overrides Function BaseAuthExists() As Boolean
Return Responser.CookiesExists Return Responser.CookiesExists
End Function End Function
Friend Overrides Function Available(ByVal What As ISiteSettings.Download, ByVal Silent As Boolean) As Boolean Friend Overrides Function Available(ByVal What As ISiteSettings.Download, ByVal Silent As Boolean) As Boolean
Return Settings.GalleryDLFile.Exists And BaseAuthExists() Return Settings.GalleryDLFile.Exists And BaseAuthExists()
End Function End Function
#End Region
#Region "Download"
Friend Property LIMIT_ABORT As Boolean = False Friend Property LIMIT_ABORT As Boolean = False
Friend ReadOnly Property LimitSkippedUsers As List(Of UserDataBase) Friend ReadOnly Property LimitSkippedUsers As List(Of UserDataBase)
Friend Property UserNumber As Integer = -1
Friend Overrides Sub DownloadStarted(ByVal What As ISiteSettings.Download)
UserNumber = 0
MyBase.DownloadStarted(What)
End Sub
Friend Overrides Sub AfterDownload(ByVal User As Object, ByVal What As ISiteSettings.Download)
If Not User Is Nothing AndAlso DirectCast(User, UserData).GDL_REQUESTS_COUNT > 0 Then UserNumber += 1
MyBase.AfterDownload(User, What)
End Sub
Friend Overrides Sub DownloadDone(ByVal What As ISiteSettings.Download) Friend Overrides Sub DownloadDone(ByVal What As ISiteSettings.Download)
If UserNumber > 0 Then
If CBool(CookiesUpdate.Value) Then
With CookieKeeper.ParseNetscapeText(CookiesNetscapeFile.GetText(EDP.ReturnValue), EDP.ReturnValue)
If .ListExists Then
Responser.Cookies.Clear()
Responser.Cookies.AddRange(.Self,, EDP.ReturnValue)
Responser.SaveCookies(EDP.ReturnValue)
Else
Update_SaveCookiesNetscape(True)
End If
End With
Else
Update_SaveCookiesNetscape(True)
End If
End If
UserNumber = -1
If LimitSkippedUsers.Count > 0 Then If LimitSkippedUsers.Count > 0 Then
With LimitSkippedUsers With LimitSkippedUsers
If .Count = 1 Then If .Count = 1 Then
@@ -137,6 +252,8 @@ Namespace API.Twitter
LIMIT_ABORT = False LIMIT_ABORT = False
MyBase.DownloadDone(What) MyBase.DownloadDone(What)
End Sub End Sub
#End Region
#Region "UserOptions, Update"
Friend Overrides Sub UserOptions(ByRef Options As Object, ByVal OpenForm As Boolean) Friend Overrides Sub UserOptions(ByRef Options As Object, ByVal OpenForm As Boolean)
If Options Is Nothing OrElse (Not TypeOf Options Is EditorExchangeOptions OrElse If Options Is Nothing OrElse (Not TypeOf Options Is EditorExchangeOptions OrElse
Not DirectCast(Options, EditorExchangeOptions).SiteKey = TwitterSiteKey) Then _ Not DirectCast(Options, EditorExchangeOptions).SiteKey = TwitterSiteKey) Then _
@@ -152,6 +269,8 @@ Namespace API.Twitter
End If End If
MyBase.Update() MyBase.Update()
End Sub End Sub
#End Region
#Region "IsMyUser, IsMyImageVideo, GetUserPostUrl, GetUserUrl"
Friend Const CommunitiesUser As String = "/i/communities" Friend Const CommunitiesUser As String = "/i/communities"
Friend Overrides Function IsMyUser(ByVal UserURL As String) As ExchangeOptions Friend Overrides Function IsMyUser(ByVal UserURL As String) As ExchangeOptions
Dim e As ExchangeOptions = MyBase.IsMyUser(UserURL) Dim e As ExchangeOptions = MyBase.IsMyUser(UserURL)
@@ -162,8 +281,20 @@ Namespace API.Twitter
Return Nothing Return Nothing
End If End If
End Function End Function
Friend Overrides Function IsMyImageVideo(ByVal URL As String) As ExchangeOptions
If Not URL.IsEmptyString AndAlso (URL.Contains("twitter") Or URL.Contains("x.com")) Then
Return New ExchangeOptions(Site, String.Empty) With {.Exists = True}
Else
Return Nothing
End If
End Function
Friend Const SinglePostPattern As String = "https://x.com/i/web/status/{0}"
Friend Overrides Function GetUserPostUrl(ByVal User As UserDataBase, ByVal Media As UserMedia) As String
Return String.Format(SinglePostPattern, Media.Post.ID)
End Function
Friend Overrides Function GetUserUrl(ByVal User As IPluginContentProvider) As String Friend Overrides Function GetUserUrl(ByVal User As IPluginContentProvider) As String
Return DirectCast(User, UserData).GetUserUrl Return DirectCast(User, UserData).GetUserUrl
End Function End Function
#End Region
End Class End Class
End Namespace End Namespace

View File

@@ -7,6 +7,7 @@
' This program is distributed in the hope that it will be useful, ' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY ' but WITHOUT ANY WARRANTY
Imports System.Threading Imports System.Threading
Imports System.Text.RegularExpressions
Imports SCrawler.API.Base Imports SCrawler.API.Base
Imports SCrawler.API.YouTube.Objects Imports SCrawler.API.YouTube.Objects
Imports PersonalUtilities.Functions.XML Imports PersonalUtilities.Functions.XML
@@ -16,6 +17,7 @@ Imports PersonalUtilities.Tools.Web.Documents
Imports PersonalUtilities.Tools.Web.Documents.JSON Imports PersonalUtilities.Tools.Web.Documents.JSON
Imports UStates = SCrawler.API.Base.UserMedia.States Imports UStates = SCrawler.API.Base.UserMedia.States
Imports UTypes = SCrawler.API.Base.UserMedia.Types Imports UTypes = SCrawler.API.Base.UserMedia.Types
Imports PKV = SCrawler.API.Instagram.UserData.PostKV
Namespace API.Twitter Namespace API.Twitter
Friend Class UserData : Inherits UserDataBase Friend Class UserData : Inherits UserDataBase
#Region "XML names" #Region "XML names"
@@ -23,22 +25,16 @@ Namespace API.Twitter
Private Const Name_DownloadModel As String = "DownloadModel" Private Const Name_DownloadModel As String = "DownloadModel"
Private Const Name_DownloadModelForceApply As String = "DownloadModelForceApply" Private Const Name_DownloadModelForceApply As String = "DownloadModelForceApply"
Private Const Name_MediaModelAllowNonUserTweets As String = "MediaModelAllowNonUserTweets" Private Const Name_MediaModelAllowNonUserTweets As String = "MediaModelAllowNonUserTweets"
Private Const Name_DownloadBroadcasts As String = "DownloadBroadcasts"
Private Const Name_GifsDownload As String = "GifsDownload" Private Const Name_GifsDownload As String = "GifsDownload"
Private Const Name_GifsSpecialFolder As String = "GifsSpecialFolder" Private Const Name_GifsSpecialFolder As String = "GifsSpecialFolder"
Private Const Name_GifsPrefix As String = "GifsPrefix" Private Const Name_GifsPrefix As String = "GifsPrefix"
Private Const Name_IsCommunity As String = "IsCommunity" Private Const Name_IsCommunity As String = "IsCommunity"
Private Const Name_DownloadModelChanged As String = "DownloadModelChanged"
#End Region #End Region
#Region "Declarations" #Region "Declarations"
Private Const BroadCastPartUrl As String = "i/broadcasts"
Private Const Label_Community As String = "Community" Private Const Label_Community As String = "Community"
Private _NameTrue As String = String.Empty
Friend Property NameTrue(Optional ByVal Exact As Boolean = False) As String
Get
Return If(Exact, _NameTrue, _NameTrue.IfNullOrEmpty(Name))
End Get
Set(ByVal NewName As String)
_NameTrue = NewName
End Set
End Property
Friend Overrides ReadOnly Property SpecialLabels As IEnumerable(Of String) Friend Overrides ReadOnly Property SpecialLabels As IEnumerable(Of String)
Get Get
Return {Label_Community} Return {Label_Community}
@@ -54,12 +50,20 @@ Namespace API.Twitter
Private FirstDownloadComplete As Boolean = False Private FirstDownloadComplete As Boolean = False
Friend Property DownloadModelForceApply As Boolean = False Friend Property DownloadModelForceApply As Boolean = False
Friend Property DownloadModel As DownloadModels = DownloadModels.Undefined Friend Property DownloadModel As DownloadModels = DownloadModels.Undefined
Private ReadOnly Property IsMultiMode As Boolean
Get
Return EnumExtract(Of DownloadModels)(DownloadModel).ListIfNothing.Count > 1
End Get
End Property
Private Property DownloadModelChanged As Boolean = False
Friend Property MediaModelAllowNonUserTweets As Boolean = False Friend Property MediaModelAllowNonUserTweets As Boolean = False
Friend Property DownloadBroadcasts As Boolean = False
Friend Property GifsDownload As Boolean = True Friend Property GifsDownload As Boolean = True
Friend Property GifsSpecialFolder As String = String.Empty Friend Property GifsSpecialFolder As String = String.Empty
Friend Property GifsPrefix As String = String.Empty Friend Property GifsPrefix As String = String.Empty
Friend Property IsCommunity As Boolean = False Friend Property IsCommunity As Boolean = False
Private ReadOnly LikesPosts As List(Of String) Private ReadOnly LikesPosts As List(Of String)
Private ReadOnly PostsKV As List(Of PKV)
Private ReadOnly _DataNames As List(Of String) Private ReadOnly _DataNames As List(Of String)
Private ReadOnly Property MySettings As SiteSettings Private ReadOnly Property MySettings As SiteSettings
Get Get
@@ -94,11 +98,14 @@ Namespace API.Twitter
DownloadModel = DownloadModels.Undefined DownloadModel = DownloadModels.Undefined
DownloadModelForceApply = .DownloadModelForceApply DownloadModelForceApply = .DownloadModelForceApply
MediaModelAllowNonUserTweets = .MediaModelAllowNonUserTweets MediaModelAllowNonUserTweets = .MediaModelAllowNonUserTweets
DownloadBroadcasts = .DownloadBroadcasts
Dim dModel As DownloadModels = DownloadModel
If .DownloadModelMedia Then DownloadModel += DownloadModels.Media If .DownloadModelMedia Then DownloadModel += DownloadModels.Media
If .DownloadModelProfile Then DownloadModel += DownloadModels.Profile If .DownloadModelProfile Or .DownloadBroadcasts Then DownloadModel += DownloadModels.Profile
If .DownloadModelSearch Then DownloadModel += DownloadModels.Search If .DownloadModelSearch Then DownloadModel += DownloadModels.Search
If .DownloadModelLikes Then DownloadModel += DownloadModels.Likes If .DownloadModelLikes Then DownloadModel += DownloadModels.Likes
_NameTrue = .UserName If Not dModel = DownloadModel Then DownloadModelChanged = True
NameTrue = .UserName
End With End With
End If End If
End Sub End Sub
@@ -107,11 +114,15 @@ Namespace API.Twitter
Friend Sub New() Friend Sub New()
_DataNames = New List(Of String) _DataNames = New List(Of String)
LikesPosts = New List(Of String) LikesPosts = New List(Of String)
PostsKV = New List(Of PKV)
UseInternalM3U8Function = True
End Sub End Sub
Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean) Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean)
With Container With Container
If Loading Then If Loading Then
DownloadBroadcasts = .Value(Name_DownloadBroadcasts).FromXML(Of Boolean)(False)
DownloadModelForceApply = .Value(Name_DownloadModelForceApply).FromXML(Of Boolean)(False) DownloadModelForceApply = .Value(Name_DownloadModelForceApply).FromXML(Of Boolean)(False)
DownloadModelChanged = .Value(Name_DownloadModelChanged).FromXML(Of Boolean)(False)
If .Contains(Name_FirstDownloadComplete) Then If .Contains(Name_FirstDownloadComplete) Then
FirstDownloadComplete = .Value(Name_FirstDownloadComplete).FromXML(Of Boolean)(False) FirstDownloadComplete = .Value(Name_FirstDownloadComplete).FromXML(Of Boolean)(False)
DownloadModel = .Value(Name_DownloadModel).FromXML(Of Integer)(DownloadModels.Undefined) DownloadModel = .Value(Name_DownloadModel).FromXML(Of Integer)(DownloadModels.Undefined)
@@ -144,12 +155,11 @@ Namespace API.Twitter
StartMD5Checked = .Value(Name_StartMD5Checked).FromXML(Of Boolean)(False) StartMD5Checked = .Value(Name_StartMD5Checked).FromXML(Of Boolean)(False)
MediaModelAllowNonUserTweets = .Value(Name_MediaModelAllowNonUserTweets).FromXML(Of Boolean)(False) MediaModelAllowNonUserTweets = .Value(Name_MediaModelAllowNonUserTweets).FromXML(Of Boolean)(False)
IsCommunity = .Value(Name_IsCommunity).FromXML(Of Boolean)(False) IsCommunity = .Value(Name_IsCommunity).FromXML(Of Boolean)(False)
_NameTrue = .Value(Name_TrueName)
Else Else
If Name.Contains("@") And Not IsCommunity Then If Name.Contains("@") And Not IsCommunity Then
IsCommunity = True IsCommunity = True
_NameTrue = Name.Split("@")(0) NameTrue = Name.Split("@")(0)
ID = _NameTrue ID = NameTrue
ParseUserMediaOnly = False ParseUserMediaOnly = False
Labels.ListAddValue(Label_Community, LNC) Labels.ListAddValue(Label_Community, LNC)
Labels.Sort() Labels.Sort()
@@ -159,7 +169,9 @@ Namespace API.Twitter
End If End If
.Add(Name_FirstDownloadComplete, FirstDownloadComplete.BoolToInteger) .Add(Name_FirstDownloadComplete, FirstDownloadComplete.BoolToInteger)
.Add(Name_DownloadModelForceApply, DownloadModelForceApply.BoolToInteger) .Add(Name_DownloadModelForceApply, DownloadModelForceApply.BoolToInteger)
.Add(Name_DownloadModelChanged, DownloadModelChanged.BoolToInteger)
.Add(Name_DownloadModel, CInt(DownloadModel)) .Add(Name_DownloadModel, CInt(DownloadModel))
.Add(Name_DownloadBroadcasts, DownloadBroadcasts.BoolToInteger)
.Add(Name_GifsDownload, GifsDownload.BoolToInteger) .Add(Name_GifsDownload, GifsDownload.BoolToInteger)
.Add(Name_GifsSpecialFolder, GifsSpecialFolder) .Add(Name_GifsSpecialFolder, GifsSpecialFolder)
.Add(Name_GifsPrefix, GifsPrefix) .Add(Name_GifsPrefix, GifsPrefix)
@@ -168,7 +180,7 @@ Namespace API.Twitter
.Add(Name_StartMD5Checked, StartMD5Checked.BoolToInteger) .Add(Name_StartMD5Checked, StartMD5Checked.BoolToInteger)
.Add(Name_MediaModelAllowNonUserTweets, MediaModelAllowNonUserTweets.BoolToInteger) .Add(Name_MediaModelAllowNonUserTweets, MediaModelAllowNonUserTweets.BoolToInteger)
.Add(Name_IsCommunity, IsCommunity.BoolToInteger) .Add(Name_IsCommunity, IsCommunity.BoolToInteger)
.Add(Name_TrueName, _NameTrue) .Add(Name_TrueName, NameTrue(True))
End If End If
End With End With
End Sub End Sub
@@ -182,8 +194,72 @@ Namespace API.Twitter
{{"item", "itemContent", "tweet_results", "result", "tweet", "legacy"}} {{"item", "itemContent", "tweet_results", "result", "tweet", "legacy"}}
} }
End Function End Function
Private Function ExtractBroadcast(ByVal e As EContainer, Optional ByVal PostID As String = Nothing, Optional ByVal PostDate As String = Nothing,
Optional ByVal Nodes As List(Of String()) = Nothing,
Optional ByVal IgnoreNodes As Boolean = False) As UserMedia
If e.ListExists Then
Dim __nodes As List(Of String()) = If(Nodes, GetContainerSubnodes())
Dim urlValue$
Dim m As UserMedia = Nothing
Dim __parseContainer As Func(Of EContainer, Boolean) =
Function(ByVal ee As EContainer) As Boolean
With ee
If .ListExists Then
urlValue = .ItemF(BroadcastsUrls, EDP.ReturnValue).XmlIfNothingValue
If Not urlValue.IsEmptyString AndAlso urlValue.Contains(BroadCastPartUrl) Then
m = MediaFromData(urlValue, PostID, PostDate,,, UTypes.m3u8)
If Not IsSingleObjectDownload Then m.SpecialFolder = "Broadcasts*"
Return True
End If
End If
End With
Return False
End Function
If IgnoreNodes Then
If __parseContainer(e) Then Return m
Else
For Each n As String() In __nodes
If __parseContainer(e(n)) Then Return m
Next
End If
m = ExtractBroadcast(e.ItemF(Of Object)({0}), PostID, PostDate, Nodes)
If Not m.URL.IsEmptyString Then Return m
End If
Return Nothing
End Function
Private ReadOnly Property MyFilePostsKV As SFile
Get
Dim f As SFile = MyFilePosts
If Not f.IsEmptyString Then
f.Name &= "_KV"
f.Extension = "xml"
Return f
Else
Return Nothing
End If
End Get
End Property
Protected Sub LoadSavePostsKV(ByVal Load As Boolean)
Instagram.UserData.LoadSavePostsKV(Load, MyFilePostsKV, PostsKV)
End Sub
Private Function PostKVExists(ByVal PID As String, ByVal Model As DownloadModels,
ByVal MultiMode As Boolean, ByVal IgnorePKV As Boolean, ByVal AutoAdd As Boolean) As Boolean
Dim result As Boolean
If IgnorePKV Or PostsKV.Count = 0 Then
result = _TempPostsList.Contains(PID)
Else
result = PostsKV.Contains(New PKV(PID, PID, Model)) Or (Not MultiMode AndAlso _TempPostsList.Contains(PID))
End If
If Not result And AutoAdd Then
PostsKV.ListAddValue(New PKV(PID, PID, Model), LNC)
_TempPostsList.ListAddValue(PID, LNC)
End If
Return result
End Function
Friend Property GDL_REQUESTS_COUNT As Integer = 0
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken) Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
Try Try
GDL_REQUESTS_COUNT = 0
If MySettings.LIMIT_ABORT Then If MySettings.LIMIT_ABORT Then
Throw New TwitterLimitException(Me) Throw New TwitterLimitException(Me)
Else Else
@@ -191,9 +267,17 @@ Namespace API.Twitter
If _ContentList.Count > 0 Then _DataNames.ListAddList(_ContentList.Select(Function(c) c.Post.ID), LAP.ClearBeforeAdd, LAP.NotContainsOnly) If _ContentList.Count > 0 Then _DataNames.ListAddList(_ContentList.Select(Function(c) c.Post.ID), LAP.ClearBeforeAdd, LAP.NotContainsOnly)
DownloadData_SavedPosts(Token) DownloadData_SavedPosts(Token)
Else Else
LoadSavePostsKV(True)
If PostsKV.Count = 0 And (_ContentList.Count > 0 Or _TempPostsList.Count > 0) Then
Dim m As DownloadModels = IIf(IsMultiMode, DownloadModels.Media, DownloadModel)
PostsKV.ListAddList(_TempPostsList.Select(Function(p) New PKV(p, p, m)), LNC)
PostsKV.ListAddList(_ContentList.Select(Function(p) New PKV(p.Post.ID, p.Post.ID, m)), LNC)
_ForceSaveUserData = True
End If
LikesPosts.Clear() LikesPosts.Clear()
If _ContentList.Count > 0 Then _DataNames.ListAddList(_ContentList.Select(Function(c) c.File.File), LAP.ClearBeforeAdd, LAP.NotContainsOnly) If _ContentList.Count > 0 Then _DataNames.ListAddList(_ContentList.Select(Function(c) c.File.File), LAP.ClearBeforeAdd, LAP.NotContainsOnly)
DownloadData_Timeline(Token) DownloadData_Timeline(Token)
LoadSavePostsKV(False)
If LikesPosts.Count > 0 Then If LikesPosts.Count > 0 Then
_ReparseLikes = True _ReparseLikes = True
ReparseMissing(Token) ReparseMissing(Token)
@@ -228,6 +312,8 @@ Namespace API.Twitter
Dim indxChanged As Boolean = False Dim indxChanged As Boolean = False
Dim isOneNode As Boolean, isPins As Boolean, ExistsDetected As Boolean, userInfoParsed As Boolean = False Dim isOneNode As Boolean, isPins As Boolean, ExistsDetected As Boolean, userInfoParsed As Boolean = False
Dim j As EContainer, rootNode As EContainer, optionalNode As EContainer, workingNode As EContainer, tmpNode As EContainer, nn As EContainer = Nothing Dim j As EContainer, rootNode As EContainer, optionalNode As EContainer, workingNode As EContainer, tmpNode As EContainer, nn As EContainer = Nothing
Dim multiMode As Boolean = IsMultiMode
Dim currentModel As DownloadModels = DownloadModels.Undefined
Dim __parseContainer As Func(Of EContainer, Boolean) = Dim __parseContainer As Func(Of EContainer, Boolean) =
Function(ByVal ee As EContainer) As Boolean Function(ByVal ee As EContainer) As Boolean
@@ -250,14 +336,13 @@ Namespace API.Twitter
Case DateResult.Skip, DateResult.Exit : Return False Case DateResult.Skip, DateResult.Exit : Return False
End Select End Select
If Not _TempPostsList.Contains(PostID) Then If Not PostKVExists(PostID, currentModel, multiMode, False, True) Then
_TempPostsList.Add(PostID)
ElseIf dirIndx = 3 Then ElseIf dirIndx = 3 Then
ElseIf isPins Then ElseIf isPins Then
Return False Return False
Else Else
ExistsDetected = True ExistsDetected = Not multiMode
Return False Return multiMode
End If End If
tmpUserId = nn({"retweeted_status_result", "result", "legacy", "user_id_str"}).XmlIfNothingValue tmpUserId = nn({"retweeted_status_result", "result", "legacy", "user_id_str"}).XmlIfNothingValue
@@ -268,12 +353,21 @@ Namespace API.Twitter
If (Not ParseUserMediaOnly Or dirIndx = 3) OrElse If (Not ParseUserMediaOnly Or dirIndx = 3) OrElse
(dirIndx = 0 AndAlso MediaModelAllowNonUserTweets) OrElse (dirIndx = 0 AndAlso MediaModelAllowNonUserTweets) OrElse
(Not ID.IsEmptyString AndAlso tmpUserId = ID) Then (Not ID.IsEmptyString AndAlso tmpUserId = ID) Then
If dirIndx = 1 And DownloadBroadcasts Then
Dim m As UserMedia = ExtractBroadcast(nn, PostID, PostDate, nodes)
If Not m.URL.IsEmptyString Then
_TempMediaList.ListAddValue(m, LNC)
Else
m = ExtractBroadcast(ee, PostID, PostDate, nodes)
If Not m.URL.IsEmptyString Then _TempMediaList.ListAddValue(m, LNC)
End If
End If
If dirIndx = 3 Then If dirIndx = 3 Then
Dim lUrl$ = nn.ItemF({"content", "itemContent", "tweet_results", "result", "legacy", "entities", "media", 0}, "expanded_url").XmlIfNothingValue Dim lUrl$ = nn.ItemF({"content", "itemContent", "tweet_results", "result", "legacy", "entities", "media", 0}, "expanded_url").XmlIfNothingValue
If Not lUrl.IsEmptyString Then If Not lUrl.IsEmptyString Then
lUrl = RegexReplace(lUrl, StatusRegEx) lUrl = RegexReplace(lUrl, StatusRegEx)
If Not lUrl.IsEmptyString Then If Not lUrl.IsEmptyString Then
If Not _TempPostsList.Contains(lUrl) Then _TempPostsList.Add(lUrl) Else Return False If PostKVExists(lUrl, currentModel, multiMode, False, True) Then Return multiMode
LikesPosts.ListAddValue(lUrl, LNC) LikesPosts.ListAddValue(lUrl, LNC)
End If End If
End If End If
@@ -287,11 +381,23 @@ Namespace API.Twitter
tCache = CreateCache() tCache = CreateCache()
'0 - media
'1 - profile
'2 - search
'3 - likes
Dim dirs As List(Of SFile) = GetTimelineFromGalleryDL(tCache, Token) Dim dirs As List(Of SFile) = GetTimelineFromGalleryDL(tCache, Token)
If dirs.ListExists Then If dirs.ListExists Then
For Each dir As SFile In dirs For Each dir As SFile In dirs
dirIndx += 1 dirIndx += 1
Select Case dirIndx
Case 0 : currentModel = DownloadModels.Media
Case 1 : currentModel = DownloadModels.Profile
Case 2 : currentModel = DownloadModels.Search
Case 3 : currentModel = DownloadModels.Likes
Case Else : currentModel = DownloadModels.Undefined
End Select
If dirIndx = 3 Then likesDetected = True If dirIndx = 3 Then likesDetected = True
ExistsDetected = False ExistsDetected = False
@@ -312,17 +418,7 @@ Namespace API.Twitter
userInfoParsed = True userInfoParsed = True
Dim resValue$ = j.Value({"data", IIf(IsCommunity, "communityResults", "user"), "result"}, "__typename").StringTrim.StringToLower Dim resValue$ = j.Value({"data", IIf(IsCommunity, "communityResults", "user"), "result"}, "__typename").StringTrim.StringToLower
Dim icon$ Dim icon$
Dim __getImage As Action(Of String) = Sub(ByVal img As String) Dim fileCrFunc As Func(Of String, SFile) = Function(img) UrlFile(img, True)
If Not img.IsEmptyString Then
Dim __imgFile As SFile = UrlFile(img, True)
If Not __imgFile.Name.IsEmptyString Then
If __imgFile.Extension.IsEmptyString Then __imgFile.Extension = "jpg"
__imgFile.Path = MyFile.CutPath.Path
If Not __imgFile.Exists Then GetWebFile(img, __imgFile, EDP.None)
If __imgFile.Exists Then IconBannerDownloaded = True
End If
End If
End Sub
If resValue.IsEmptyString Then If resValue.IsEmptyString Then
UserExists = False UserExists = False
j.Dispose() j.Dispose()
@@ -344,7 +440,7 @@ Namespace API.Twitter
icon = .Value({"custom_banner_media", "media_info"}, "original_img_url"). icon = .Value({"custom_banner_media", "media_info"}, "original_img_url").
IfNullOrEmpty(.Value({"default_banner_media", "media_info"}, "original_img_url")) IfNullOrEmpty(.Value({"default_banner_media", "media_info"}, "original_img_url"))
If Not icon.IsEmptyString And DownloadIconBanner Then __getImage.Invoke(icon) If Not icon.IsEmptyString And DownloadIconBanner Then SimpleDownloadAvatar(icon, fileCrFunc)
End If End If
End If End If
End With End With
@@ -370,8 +466,8 @@ Namespace API.Twitter
icon = .Value("profile_image_url_https") icon = .Value("profile_image_url_https")
If Not icon.IsEmptyString Then icon = icon.Replace("_normal", String.Empty) If Not icon.IsEmptyString Then icon = icon.Replace("_normal", String.Empty)
If DownloadIconBanner Then If DownloadIconBanner Then
__getImage.Invoke(.Value("profile_banner_url")) SimpleDownloadAvatar(.Value("profile_banner_url"), fileCrFunc)
__getImage.Invoke(icon) SimpleDownloadAvatar(icon, fileCrFunc)
End If End If
End If End If
End If End If
@@ -431,7 +527,7 @@ Namespace API.Twitter
ProgressPre.ChangeMax(If(isOneNode, 1, .Count)) ProgressPre.ChangeMax(If(isOneNode, 1, .Count))
If isOneNode Then If isOneNode Then
ProgressPre.Perform() ProgressPre.Perform()
If Not __parseContainer(.Self) Then Exit For If Not __parseContainer(.Self) Then Continue For 'Exit For
Else Else
For nodeIndx = 0 To 1 For nodeIndx = 0 To 1
If nodeIndx = 0 Then If nodeIndx = 0 Then
@@ -446,14 +542,19 @@ Namespace API.Twitter
.ItemF(newTwitterNodes), .ItemF(newTwitterNodes),
.Self) .Self)
ProgressPre.Perform() ProgressPre.Perform()
If Not __parseContainer(tmpNode) Then Exit For If Not __parseContainer(tmpNode) Then
If isPins Then GoTo nextpIndx
Exit For
End If
Next Next
End With End With
End If End If
nextNodeIndx:
Next Next
End If End If
End With End With
End If End If
nextpIndx:
Next Next
If ExistsDetected And i = 1 Then Exit For Else ExistsDetected = False If ExistsDetected And i = 1 Then Exit For Else ExistsDetected = False
@@ -473,6 +574,7 @@ Namespace API.Twitter
If DownloadModel = DownloadModels.Undefined Then If DownloadModel = DownloadModels.Undefined Then
If ParseUserMediaOnly Then If ParseUserMediaOnly Then
DownloadModel = DownloadModels.Media DownloadModel = DownloadModels.Media
If DownloadBroadcasts Then DownloadModel += DownloadModels.Profile
Else Else
DownloadModel = DownloadModels.Media + DownloadModels.Profile + DownloadModels.Search DownloadModel = DownloadModels.Media + DownloadModels.Profile + DownloadModels.Search
End If End If
@@ -679,19 +781,87 @@ Namespace API.Twitter
End Sub End Sub
Private Sub CheckForLimit(ByVal Value As String) Private Sub CheckForLimit(ByVal Value As String)
If Token.IsCancellationRequested Or (KillOnLimit AndAlso Not ProcessKilled AndAlso If Token.IsCancellationRequested Or (KillOnLimit AndAlso Not ProcessKilled AndAlso
Not Value.IsEmptyString AndAlso Value.ToLower.Contains("for rate limit reset")) Then Not Value.IsEmptyString AndAlso (Value.ToLower.Contains("for rate limit reset") OrElse
Not CStr(RegexReplace(Value, GdlLimitRegEx)).IsEmptyString)) Then
LimitReached = True LimitReached = True
Kill() Kill()
End If End If
End Sub End Sub
End Class End Class
Private ReadOnly Property SleepTimerValue(ByVal First As Boolean) As Integer
Get
Dim fTimer% = If(First, MySettings.SleepTimerBeforeFirst, MySettings.SleepTimer).Value
If First And fTimer = SiteSettings.TimerFirstUseTheSame Then fTimer = MySettings.SleepTimer.Value
Return fTimer
End Get
End Property
Private ReadOnly Property SleepRequest As String
Get
Dim s% = SleepTimerValue(False)
Return If(s = SiteSettings.TimerDisabled, String.Empty, $" --sleep-request {s}")
End Get
End Property
Private Sub GdlWaitFirstTimer(ByVal fTimer As Integer)
If GDL_REQUESTS_COUNT = 0 And Not fTimer = SiteSettings.TimerDisabled And MySettings.UserNumber > 0 Then Thread.Sleep(fTimer * 1000)
GDL_REQUESTS_COUNT += 1
End Sub
Private _GdlPreProgressPerformEnabled As Boolean = False
Private Sub GdlPreProgressPerform(ByVal Path As SFile, ByVal UpDir As SFile)
Dim f As SFile = Nothing
Try
Dim c% = -1, lb% = -1, cc%
Dim e As New ErrorsDescriber(EDP.ReturnValue)
While _GdlPreProgressPerformEnabled
Thread.Sleep(100)
If c > 0 Then
cc = If(SFile.GetFiles(Path,,, e)?.Count, 0)
If cc > c Then
Exit Sub
ElseIf cc > 0 And (lb = -1 Or cc > lb) Then
ProgressPre.Perform(cc - IIf(lb = -1, 0, lb))
lb = cc
End If
ElseIf Path.Exists(SFO.Path, False) Then
Dim files As List(Of SFile) = SFile.GetFiles(Path,,, e)
If files.ListExists Then
f = files.FirstOrDefault(Function(ff) Not ff.Name.IsEmptyString AndAlso ff.Name.StartsWith("01_"))
If UpDir.Exists(SFO.Path, False) Then
Dim fNew As SFile = $"{UpDir.PathWithSeparator}00.txt"
If f.Copy(fNew,, True, SFODelete.DeletePermanently,, e) Then
f = fNew
Using j As EContainer = JsonDocument.Parse(f.GetText(e), e)
If j.ListExists Then
c = j(GdlPostCoutNumberNodes).XmlIfNothingValue.FromXML(Of Integer)(-1)
If c > 0 Then c /= 30 : ProgressPre.ChangeMax(c) : Continue While
End If
End Using
End If
End If
Exit Sub
End If
End If
End While
Catch ex As Exception
ErrorsDescriber.Execute(EDP.SendToLog, ex, $"{ToStringForLog()}:{vbCr}Path: '{Path.Path}'{vbCr}Path 2: '{UpDir.Path}'")
Finally
If f.Exists Then f.Delete(SFO.File, SFODelete.DeletePermanently, EDP.ReturnValue)
End Try
End Sub
Private Sub GdlPreProgressPerformWait(ByVal t As Task)
_GdlPreProgressPerformEnabled = False
Try
While t.Status = TaskStatus.Running Or t.Status = TaskStatus.WaitingToRun : Thread.Sleep(100) : End While
Catch
End Try
End Sub
Private Function GetDataFromGalleryDL(ByVal URL As String, ByVal Cache As CacheKeeper, ByVal UseTempPostList As Boolean, Private Function GetDataFromGalleryDL(ByVal URL As String, ByVal Cache As CacheKeeper, ByVal UseTempPostList As Boolean,
Optional ByVal Token As CancellationToken = Nothing) As SFile Optional ByVal Token As CancellationToken = Nothing) As SFile
Dim command$ = String.Empty Dim command$ = String.Empty
Try Try
Dim conf As SFile = GdlCreateConf(Cache.NewPath) Dim conf As SFile = GdlCreateConf(Cache.NewPath)
command = $"""{Settings.GalleryDLFile}"" --verbose --no-download --no-skip --config ""{conf}"" --write-pages " Dim fTimer% = SleepTimerValue(True)
command &= GdlGetIdFilterString() command = $"""{Settings.GalleryDLFile}"" --verbose --no-download --no-skip{SleepRequest} --config ""{conf}"" --write-pages "
'command &= GdlGetIdFilterString()
Dim dir As SFile = Cache.NewPath Dim dir As SFile = Cache.NewPath
If dir.Exists(SFO.Path,, EDP.ThrowException) Then If dir.Exists(SFO.Path,, EDP.ThrowException) Then
Using batch As New TwitterGDL(dir, Token, MySettings.AbortOnLimit.Value) Using batch As New TwitterGDL(dir, Token, MySettings.AbortOnLimit.Value)
@@ -703,6 +873,7 @@ Namespace API.Twitter
'#If DEBUG Then '#If DEBUG Then
'Debug.WriteLine(command) 'Debug.WriteLine(command)
'#End If '#End If
GdlWaitFirstTimer(fTimer)
batch.Execute(command) batch.Execute(command)
If batch.LimitReached Then If batch.LimitReached Then
If CBool(MySettings.DownloadAlreadyParsed.Value) And If CBool(MySettings.DownloadAlreadyParsed.Value) And
@@ -731,7 +902,9 @@ Namespace API.Twitter
Dim conf As SFile = GdlCreateConf(confCache.RootDirectory) Dim conf As SFile = GdlCreateConf(confCache.RootDirectory)
If DownloadModel = DownloadModels.Undefined And Not FirstDownloadComplete And DownloadModelForceApply Then If DownloadModel = DownloadModels.Undefined And Not FirstDownloadComplete And DownloadModelForceApply Then
If ParseUserMediaOnly Then If ParseUserMediaOnly And DownloadBroadcasts Then
DownloadModel = DownloadModels.Media + DownloadModels.Profile
ElseIf ParseUserMediaOnly Then
DownloadModel = DownloadModels.Media DownloadModel = DownloadModels.Media
Else Else
DownloadModel = DownloadModels.Media + DownloadModels.Profile + DownloadModels.Search DownloadModel = DownloadModels.Media + DownloadModels.Profile + DownloadModels.Search
@@ -742,11 +915,17 @@ Namespace API.Twitter
Dim rootDir As CacheKeeper = Cache.NewInstance Dim rootDir As CacheKeeper = Cache.NewInstance
Dim dir As SFile Dim dir As SFile
Dim dm As List(Of DownloadModels) = EnumExtract(Of DownloadModels)(DownloadModel).ListIfNothing Dim dm As List(Of DownloadModels) = EnumExtract(Of DownloadModels)(DownloadModel).ListIfNothing
Dim process As Boolean Dim process As Boolean, multiMode As Boolean
Dim currentModel As DownloadModels
Dim urlPrePattern$ = $"https://x.com{IIf(IsCommunity, SiteSettings.CommunitiesUser, String.Empty)}/" Dim urlPrePattern$ = $"https://x.com{IIf(IsCommunity, SiteSettings.CommunitiesUser, String.Empty)}/"
Dim fTimer% = SleepTimerValue(True)
Dim t As Task
If DownloadBroadcasts AndAlso Not dm.Contains(DownloadModels.Profile) Then dm.Add(DownloadModels.Profile)
multiMode = dm.Count > 1
Using tgdl As New TwitterGDL(Nothing, Token, MySettings.AbortOnLimit.Value) With { Using tgdl As New TwitterGDL(Nothing, Token, MySettings.AbortOnLimit.Value) With {
.TempPostsList = _TempPostsList,
.AutoClear = True, .AutoClear = True,
.AutoReset = True, .AutoReset = True,
.CommandPermanent = $"chcp {BatchExecutor.UnicodeEncoding}", .CommandPermanent = $"chcp {BatchExecutor.UnicodeEncoding}",
@@ -759,13 +938,17 @@ Namespace API.Twitter
dir.Exists(SFO.Path, True, EDP.ThrowException) dir.Exists(SFO.Path, True, EDP.ThrowException)
outList.Add(dir) outList.Add(dir)
tgdl.ChangeDirectory(dir) tgdl.ChangeDirectory(dir)
command = $"""{Settings.GalleryDLFile}"" --verbose --no-download --no-skip --config ""{conf}"" --write-pages " command = $"""{Settings.GalleryDLFile}"" --verbose --no-download --no-skip{SleepRequest} --config ""{conf}"" --write-pages "
command &= GdlGetIdFilterString() If multiMode Then
command &= "{0}"
Else
command &= GdlGetIdFilterString()
End If
Select Case i Select Case i
Case 0 : command &= $"{urlPrePattern}{NameTrue}/media" : process = dm.Contains(DownloadModels.Media) Or IsCommunity Case 0 : command &= $"{urlPrePattern}{NameTrue}/media" : currentModel = DownloadModels.Media : process = dm.Contains(currentModel) Or IsCommunity
Case 1 : command &= $"{urlPrePattern}{NameTrue}" : process = dm.Contains(DownloadModels.Profile) Case 1 : command &= $"{urlPrePattern}{NameTrue}" : currentModel = DownloadModels.Profile : process = dm.Contains(currentModel)
Case 2 : command &= $"-o search-endpoint=graphql https://x.com/search?q=from:{NameTrue}+include:nativeretweets" : process = dm.Contains(DownloadModels.Search) And Not IsCommunity Case 2 : command &= $"-o search-endpoint=graphql https://x.com/search?q=from:{NameTrue}+include:nativeretweets" : currentModel = DownloadModels.Search : process = dm.Contains(currentModel) And Not IsCommunity
Case 3 : command &= $"{urlPrePattern}{NameTrue}/likes" : process = dm.Contains(DownloadModels.Likes) Case 3 : command &= $"{urlPrePattern}{NameTrue}/likes" : currentModel = DownloadModels.Likes : process = dm.Contains(currentModel)
Case Else : process = False Case Else : process = False
End Select End Select
'#If DEBUG Then '#If DEBUG Then
@@ -773,7 +956,28 @@ Namespace API.Twitter
'#End If '#End If
ThrowAny(Token) ThrowAny(Token)
If process Then If process Then
If multiMode Then
If PostsKV.Count = 0 Then
tgdl.TempPostsList = New List(Of String)
Else
tgdl.TempPostsList = (From p As PKV In PostsKV Where p.Section = currentModel Select p.ID).ListIfNothing
End If
command = String.Format(command, GdlGetIdFilterString(tgdl.TempPostsList))
Else
tgdl.TempPostsList = _TempPostsList
End If
GdlWaitFirstTimer(fTimer)
_GdlPreProgressPerformEnabled = True
t = New Task(Sub() GdlPreProgressPerform(dir, conf))
t.Start()
tgdl.Execute(command) tgdl.Execute(command)
_GdlPreProgressPerformEnabled = False
GdlPreProgressPerformWait(t)
If tgdl.LimitReached Then If tgdl.LimitReached Then
If CBool(MySettings.DownloadAlreadyParsed.Value) And If CBool(MySettings.DownloadAlreadyParsed.Value) And
SFile.GetFiles(rootDir, "*.txt", IO.SearchOption.AllDirectories, EDP.ReturnValue).Count > 0 Then SFile.GetFiles(rootDir, "*.txt", IO.SearchOption.AllDirectories, EDP.ReturnValue).Count > 0 Then
@@ -796,16 +1000,22 @@ Namespace API.Twitter
Catch ex As Exception Catch ex As Exception
ProcessException(ex, Token, $"{ToStringForLog()}: GetTimelineFromGalleryDL({command})") ProcessException(ex, Token, $"{ToStringForLog()}: GetTimelineFromGalleryDL({command})")
Return Nothing Return Nothing
Finally
_GdlPreProgressPerformEnabled = False
End Try End Try
End Function End Function
Private Function GdlGetIdFilterString() As String Private Function GdlGetIdFilterString(Optional ByVal TL As List(Of String) = Nothing) As String
Return If(_TempPostsList.Count > 0, $"--filter ""int(tweet_id) > {_TempPostsList.Last} or abort()"" ", String.Empty) If TL.ListExists Then TL.Sort()
With If(TL, _TempPostsList) : Return If(.Count > 0, $"--filter ""int(tweet_id) > { .Last} or abort()"" ", String.Empty) : End With
End Function End Function
Private Function GdlCreateConf(ByVal Path As SFile) As SFile Private Function GdlCreateConf(ByVal Path As SFile) As SFile
Try Try
Dim conf As SFile = $"{Path.PathWithSeparator}TwitterGdlConfig.conf" Dim conf As SFile = $"{Path.PathWithSeparator}TwitterGdlConfig.conf"
Dim __userAgent$ = MySettings.UserAgent
If Not __userAgent.IsEmptyString Then __userAgent = $"""user-agent"": ""{__userAgent}"","
Dim confText$ = "{""extractor"":{""cookies"": """ & MySettings.CookiesNetscapeFile.ToString.Replace("\", "/") & Dim confText$ = "{""extractor"":{""cookies"": """ & MySettings.CookiesNetscapeFile.ToString.Replace("\", "/") &
""",""cookies-update"": false,""twitter"":{""tweet-endpoint"": ""detail"",""cards"": false,""conversations"": true,""pinned"": false,""quoted"": false,""replies"": true,""retweets"": true,""strategy"": null,""text-tweets"": false,""twitpic"": false,""unique"": true,""users"": ""timeline"",""videos"": true}}}" $""",""cookies-update"": {IIf(CBool(MySettings.CookiesUpdate.Value), "true", "false")}," & __userAgent &
"""twitter"":{""tweet-endpoint"": ""detail"",""cards"": false,""conversations"": true,""pinned"": false,""quoted"": false,""replies"": true,""retweets"": true,""strategy"": null,""text-tweets"": false,""twitpic"": false,""unique"": true,""users"": ""timeline"",""videos"": true}}}"
If conf.Exists(SFO.Path, True, EDP.ThrowException) Then TextSaver.SaveTextToFile(confText, conf) If conf.Exists(SFO.Path, True, EDP.ThrowException) Then TextSaver.SaveTextToFile(confText, conf)
If Not conf.Exists Then Throw New IO.FileNotFoundException("Can't find Twitter GDL config file", conf) If Not conf.Exists Then Throw New IO.FileNotFoundException("Can't find Twitter GDL config file", conf)
Return conf Return conf
@@ -824,7 +1034,7 @@ Namespace API.Twitter
Dim cache As CacheKeeper = Nothing Dim cache As CacheKeeper = Nothing
Try Try
If ContentMissingExists Or (_ReparseLikes And LikesPosts.Count > 0) Then If ContentMissingExists Or (_ReparseLikes And LikesPosts.Count > 0) Then
Dim m As UserMedia Dim m As UserMedia, mTmp As UserMedia
Dim PostDate$ Dim PostDate$
Dim nodes As List(Of String()) = GetContainerSubnodes() Dim nodes As List(Of String()) = GetContainerSubnodes()
Dim node$() Dim node$()
@@ -844,7 +1054,11 @@ Namespace API.Twitter
m = If(_ReparseLikes, Nothing, _ContentList(i)) m = If(_ReparseLikes, Nothing, _ContentList(i))
If Not m.Post.ID.IsEmptyString Or (IsSingleObjectDownload And Not m.URL_BASE.IsEmptyString) Or _ReparseLikes Then If Not m.Post.ID.IsEmptyString Or (IsSingleObjectDownload And Not m.URL_BASE.IsEmptyString) Or _ReparseLikes Then
ThrowAny(Token) ThrowAny(Token)
If IsSingleObjectDownload Then If m.Type = UTypes.m3u8 Then
_TempMediaList.Add(m)
rList.ListAddValue(i, LNC)
Continue For
ElseIf IsSingleObjectDownload Then
URL = m.URL_BASE URL = m.URL_BASE
ElseIf _ReparseLikes Then ElseIf _ReparseLikes Then
URL = LikesPosts(i) URL = LikesPosts(i)
@@ -861,6 +1075,13 @@ Namespace API.Twitter
If Not j Is Nothing Then If Not j Is Nothing Then
With j.ItemF({"data", 0, "instructions", 0, "entries"}) With j.ItemF({"data", 0, "instructions", 0, "entries"})
If .ListExists Then If .ListExists Then
If IsSingleObjectDownload Or DownloadBroadcasts Then
mTmp = ExtractBroadcast(.Self, m.Post.ID, String.Empty, nodes)
If Not mTmp.URL.IsEmptyString Then
_TempMediaList.ListAddValue(mTmp, LNC)
rList.ListAddValue(i, LNC)
End If
End If
For Each n In .Self For Each n In .Self
For Each node In nodes For Each node In nodes
With n(node) With n(node)
@@ -898,7 +1119,10 @@ Namespace API.Twitter
#End Region #End Region
#Region "DownloadSingleObject" #Region "DownloadSingleObject"
Protected Overrides Sub DownloadSingleObject_GetPosts(ByVal Data As IYouTubeMediaContainer, ByVal Token As CancellationToken) Protected Overrides Sub DownloadSingleObject_GetPosts(ByVal Data As IYouTubeMediaContainer, ByVal Token As CancellationToken)
_ContentList.Add(New UserMedia(Data.URL) With {.State = UStates.Missing}) GDL_REQUESTS_COUNT = 0
Dim um As New UserMedia(Data.URL) With {.State = UStates.Missing}
If Not Data.URL.IsEmptyString AndAlso Data.URL.Contains(BroadCastPartUrl) Then um.Type = UTypes.m3u8
_ContentList.Add(um)
ReparseMissing(Token) ReparseMissing(Token)
End Sub End Sub
#End Region #End Region
@@ -955,6 +1179,13 @@ Namespace API.Twitter
End Try End Try
End Function End Function
#End Region #End Region
#Region "Clear"
Protected Overrides Sub EraseData_AdditionalDataFiles()
MyFilePostsKV.Delete(SFO.File, SFODelete.DeleteToRecycleBin, EDP.SendToLog + EDP.ReturnValue)
_DataNames.Clear()
MyBase.EraseData_AdditionalDataFiles()
End Sub
#End Region
#Region "Create media" #Region "Create media"
Private Function MediaFromData(ByVal _URL As String, ByVal PostID As String, ByVal PostDate As String, Private Function MediaFromData(ByVal _URL As String, ByVal PostID As String, ByVal PostDate As String,
Optional ByVal _PictureOption As String = Nothing, Optional ByVal _PictureOption As String = Nothing,
@@ -977,6 +1208,31 @@ Namespace API.Twitter
Protected Overrides Sub DownloadContent(ByVal Token As CancellationToken) Protected Overrides Sub DownloadContent(ByVal Token As CancellationToken)
DownloadContentDefault(Token) DownloadContentDefault(Token)
End Sub End Sub
Protected Overrides Function DownloadM3U8(ByVal URL As String, ByVal Media As UserMedia, ByVal DestinationFile As SFile, ByVal Token As CancellationToken) As SFile
Const ytDest$ = "[download] destination"
Dim f As SFile = Nothing
If MySettings.CookiesNetscapeFile.Exists And Settings.YtdlpFile.Exists And (Not URL.IsEmptyString AndAlso URL.Contains(BroadCastPartUrl)) Then
Dim destPath$ = DestinationFile.PathWithSeparator.Replace("\", "\\")
Dim rr As RParams = RParams.DM($"{destPath}.+mp4", 0, RegexOptions.IgnoreCase, EDP.ReturnValue)
Dim cmd$ = $"""{Settings.YtdlpFile.File}"" --no-cookies-from-browser --cookies ""{MySettings.CookiesNetscapeFile}"" "
cmd &= $"{URL} -P ""{destPath}"" --no-mtime"
Using ytdlp As New YTDLP.YTDLPBatch(Token)
With ytdlp
.Execute(cmd)
If .OutputData.Count > 0 Then
For Each outStr$ In .OutputData
If Not outStr.IsEmptyString AndAlso outStr.ToLower.Trim.StartsWith(ytDest) Then
f = CStr(RegexReplace(outStr, rr))
If Not f.Exists Then f = Nothing
Exit For
End If
Next
End If
End With
End Using
End If
Return f
End Function
#End Region #End Region
#Region "Exception" #Region "Exception"
Protected Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False, Protected Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False,
@@ -986,7 +1242,7 @@ Namespace API.Twitter
#End Region #End Region
#Region "IDisposable support" #Region "IDisposable support"
Protected Overrides Sub Dispose(ByVal disposing As Boolean) Protected Overrides Sub Dispose(ByVal disposing As Boolean)
If Not disposedValue And disposing Then _DataNames.Clear() : LikesPosts.Clear() If Not disposedValue And disposing Then _DataNames.Clear() : LikesPosts.Clear() : PostsKV.Clear()
MyBase.Dispose(disposing) MyBase.Dispose(disposing)
End Sub End Sub
#End Region #End Region

View File

@@ -299,6 +299,24 @@ Namespace API
End If End If
End Get End Get
End Property End Property
Friend ReadOnly Property ContextDownLimit As ToolStripMenuItem()
Get
If Count > 0 Then
Return Collections.Select(Function(c) DirectCast(c, UserDataBase).BTT_CONTEXT_DOWN_LIMIT).ToArray
Else
Return New ToolStripMenuItem() {}
End If
End Get
End Property
Friend ReadOnly Property ContextDownDate As ToolStripMenuItem()
Get
If Count > 0 Then
Return Collections.Select(Function(c) DirectCast(c, UserDataBase).BTT_CONTEXT_DOWN_DATE).ToArray
Else
Return New ToolStripMenuItem() {}
End If
End Get
End Property
Friend ReadOnly Property ContextEdit As ToolStripMenuItem() Friend ReadOnly Property ContextEdit As ToolStripMenuItem()
Get Get
If Count > 0 Then If Count > 0 Then

View File

@@ -45,7 +45,6 @@ Namespace API.XVIDEOS
End Get End Get
End Property End Property
Private Property SiteMode As SiteModes = SiteModes.User Private Property SiteMode As SiteModes = SiteModes.User
Private Property TrueName As String = String.Empty
Private Property Arguments As String = String.Empty Private Property Arguments As String = String.Empty
Private Property PersonType As String = String.Empty Private Property PersonType As String = String.Empty
Friend Overrides ReadOnly Property IsUser As Boolean Friend Overrides ReadOnly Property IsUser As Boolean
@@ -92,14 +91,14 @@ Namespace API.XVIDEOS
If Not Force OrElse (Not SiteMode = SiteModes.User AndAlso Not NewUrl.IsEmptyString AndAlso MyFileSettings.Exists) Then If Not Force OrElse (Not SiteMode = SiteModes.User AndAlso Not NewUrl.IsEmptyString AndAlso MyFileSettings.Exists) Then
Dim eObj As Plugin.ExchangeOptions = Nothing Dim eObj As Plugin.ExchangeOptions = Nothing
If Force Then eObj = MySettings.IsMyUser(NewUrl) If Force Then eObj = MySettings.IsMyUser(NewUrl)
If (Force And Not eObj.UserName.IsEmptyString) Or (Not Force And TrueName.IsEmptyString) Then If (Force And Not eObj.UserName.IsEmptyString) Or (Not Force And NameTrue(True).IsEmptyString) Then
Dim n$() = If(Force, eObj.UserName, Name).Split("@") Dim n$() = If(Force, eObj.UserName, Name).Split("@")
If n.ListExists(2) Then If n.ListExists(2) Then
Dim opt$ = If(Force, eObj.Options, Options) Dim opt$ = If(Force, eObj.Options, Options)
If opt.IsEmptyString AndAlso Not IsNumeric(n(0)) Then If opt.IsEmptyString AndAlso Not IsNumeric(n(0)) Then
If Not Force Then If Not Force Then
PersonType = n(0) PersonType = n(0)
TrueName = If(Force, eObj.UserName, Name).Replace($"{PersonType}@", String.Empty) NameTrue = If(Force, eObj.UserName, Name).Replace($"{PersonType}@", String.Empty)
End If End If
ElseIf Not opt.IsEmptyString Then ElseIf Not opt.IsEmptyString Then
Dim n2$() = opt.Split("@") Dim n2$() = opt.Split("@")
@@ -108,8 +107,8 @@ Namespace API.XVIDEOS
Dim __Arguments$ = opt.Replace($"{__TrueName}@", String.Empty) Dim __Arguments$ = opt.Replace($"{__TrueName}@", String.Empty)
Dim __ForceApply As Boolean = False Dim __ForceApply As Boolean = False
If Force AndAlso (Not TrueName = __TrueName Or Not SiteMode = __SiteMode) Then If Force AndAlso (Not NameTrue(True) = __TrueName Or Not SiteMode = __SiteMode) Then
If ValidateChangeSearchOptions(ToStringForLog, $"{__SiteMode}: {__TrueName}", $"{SiteMode}: {TrueName}") Then If ValidateChangeSearchOptions(ToStringForLog, $"{__SiteMode}: {__TrueName}", $"{SiteMode}: {NameTrue(True)}") Then
__ForceApply = True __ForceApply = True
Else Else
Return False Return False
@@ -120,15 +119,15 @@ Namespace API.XVIDEOS
Options = opt Options = opt
If Not Force Then If Not Force Then
SiteMode = __SiteMode SiteMode = __SiteMode
TrueName = __TrueName NameTrue = __TrueName
UserSiteName = $"{SiteMode}: {TrueName}" UserSiteName = $"{SiteMode}: {NameTrue}"
If FriendlyName.IsEmptyString Then FriendlyName = UserSiteName If FriendlyName.IsEmptyString Then FriendlyName = UserSiteName
Settings.Labels.Add(SearchRequestLabelName) Settings.Labels.Add(SearchRequestLabelName)
Labels.ListAddValue(SearchRequestLabelName, LNC) Labels.ListAddValue(SearchRequestLabelName, LNC)
Labels.Sort() Labels.Sort()
ElseIf Force And __ForceApply Then ElseIf Force And __ForceApply Then
SiteMode = __SiteMode SiteMode = __SiteMode
TrueName = __TrueName NameTrue = __TrueName
End If End If
Return True Return True
@@ -142,14 +141,13 @@ Namespace API.XVIDEOS
With Container With Container
If Loading Then If Loading Then
SiteMode = .Value(Name_SiteMode).FromXML(Of Integer)(SiteModes.User) SiteMode = .Value(Name_SiteMode).FromXML(Of Integer)(SiteModes.User)
TrueName = .Value(Name_TrueName)
Arguments = .Value(Name_Arguments) Arguments = .Value(Name_Arguments)
PersonType = .Value(Name_PersonType) PersonType = .Value(Name_PersonType)
If PersonType.IsEmptyString And TrueName.IsEmptyString And Not Name.IsEmptyString Then If PersonType.IsEmptyString And NameTrue(True).IsEmptyString And Not Name.IsEmptyString Then
If Not Name.Contains("@") Then If Not Name.Contains("@") Then
Dim n$() = Name.Split("_") Dim n$() = Name.Split("_")
PersonType = n(0) PersonType = n(0)
TrueName = Name.Replace($"{PersonType}_", String.Empty) NameTrue = Name.Replace($"{PersonType}_", String.Empty)
End If End If
End If End If
UpdateUserOptions() UpdateUserOptions()
@@ -160,7 +158,7 @@ Namespace API.XVIDEOS
.Value(Name_FriendlyName) = FriendlyName .Value(Name_FriendlyName) = FriendlyName
End If End If
.Add(Name_SiteMode, CInt(SiteMode)) .Add(Name_SiteMode, CInt(SiteMode))
.Add(Name_TrueName, TrueName) .Add(Name_TrueName, NameTrue(True))
.Add(Name_Arguments, Arguments) .Add(Name_Arguments, Arguments)
.Add(Name_PersonType, PersonType) .Add(Name_PersonType, PersonType)
@@ -181,19 +179,19 @@ Namespace API.XVIDEOS
Friend Function GetUserUrl(ByVal Page As Integer) As String Friend Function GetUserUrl(ByVal Page As Integer) As String
Dim url$ = String.Empty Dim url$ = String.Empty
If SiteMode = SiteModes.User Then If SiteMode = SiteModes.User Then
url = $"https://xvideos.com/{PersonType}/{TrueName}" url = $"https://xvideos.com/{PersonType}/{NameTrue}"
ElseIf SiteMode = SiteModes.Categories Then ElseIf SiteMode = SiteModes.Categories Then
url = "https://xvideos.com/c/" url = "https://xvideos.com/c/"
If Not Arguments.IsEmptyString Then url &= $"{Arguments}/" If Not Arguments.IsEmptyString Then url &= $"{Arguments}/"
url &= TrueName url &= NameTrue
If Page > 1 Then url &= $"/{Page - 1}" If Page > 1 Then url &= $"/{Page - 1}"
ElseIf SiteMode = SiteModes.Tags Then ElseIf SiteMode = SiteModes.Tags Then
url = "https://www.xvideos.com/tags/" url = "https://www.xvideos.com/tags/"
If Not Arguments.IsEmptyString Then url &= $"{Arguments}/" If Not Arguments.IsEmptyString Then url &= $"{Arguments}/"
url &= $"{TrueName}/" url &= $"{NameTrue}/"
If Page > 1 Then url &= Page - 1 If Page > 1 Then url &= Page - 1
ElseIf SiteMode = SiteModes.Search Then ElseIf SiteMode = SiteModes.Search Then
url = $"https://www.xvideos.com/?k={TrueName}" url = $"https://www.xvideos.com/?k={NameTrue}"
If Not Arguments.IsEmptyString Then url &= $"&{Arguments}" If Not Arguments.IsEmptyString Then url &= $"&{Arguments}"
If Page > 1 Then url &= $"&p={Page - 1}" If Page > 1 Then url &= $"&p={Page - 1}"
End If End If

View File

@@ -93,7 +93,11 @@ Namespace API.Xhamster
Dim position% = InStr(URL, sk) Dim position% = InStr(URL, sk)
If position > 0 Then appender = URL.Remove(position - 1) If position > 0 Then appender = URL.Remove(position - 1)
End If End If
URL = M3U8Base.CreateUrl(appender, file) If file.StartsWith("//") Then
URL = LinkFormatterSecure(file.TrimStart("/"))
Else
URL = M3U8Base.CreateUrl(appender, file)
End If
Dim l As List(Of M3U8URL) = ParseSecondM3U8(URL, Responser, appender) Dim l As List(Of M3U8URL) = ParseSecondM3U8(URL, Responser, appender)
If l.ListExists Then Return l If l.ListExists Then Return l
End If End If

View File

@@ -86,7 +86,7 @@ Namespace API.Xhamster
If Not .SiteMode = SiteModes.User Then If Not .SiteMode = SiteModes.User Then
Return .GetNonUserUrl(0) Return .GetNonUserUrl(0)
Else Else
Return String.Format(UrlPatternUser, IIf(.IsChannel, ChannelOption, UserOption), .TrueName) Return String.Format(UrlPatternUser, IIf(.IsChannel, ChannelOption, UserOption), .NameTrue)
End If End If
End With End With
End Function End Function

View File

@@ -29,7 +29,6 @@ Namespace API.Xhamster
End Property End Property
Friend Property IsChannel As Boolean = False Friend Property IsChannel As Boolean = False
Friend Property IsCreator As Boolean = False Friend Property IsCreator As Boolean = False
Friend Property TrueName As String = String.Empty
Friend Property Gender As String = String.Empty Friend Property Gender As String = String.Empty
Friend Property SiteMode As SiteModes = SiteModes.User Friend Property SiteMode As SiteModes = SiteModes.User
Friend Property Arguments As String = String.Empty Friend Property Arguments As String = String.Empty
@@ -73,17 +72,17 @@ Namespace API.Xhamster
If Not Force OrElse (Not SiteMode = SiteModes.User AndAlso Not NewUrl.IsEmptyString AndAlso MyFileSettings.Exists) Then If Not Force OrElse (Not SiteMode = SiteModes.User AndAlso Not NewUrl.IsEmptyString AndAlso MyFileSettings.Exists) Then
Dim eObj As Plugin.ExchangeOptions = Nothing Dim eObj As Plugin.ExchangeOptions = Nothing
If Force Then eObj = MySettings.IsMyUser(NewUrl) If Force Then eObj = MySettings.IsMyUser(NewUrl)
If (Force And Not eObj.UserName.IsEmptyString) Or (Not Force And TrueName.IsEmptyString) Then If (Force And Not eObj.UserName.IsEmptyString) Or (Not Force And NameTrue(True).IsEmptyString) Then
Dim n$() = If(Force, eObj.UserName, Name).Split("@") Dim n$() = If(Force, eObj.UserName, Name).Split("@")
If n.ListExists Then If n.ListExists Then
If n.Length = 2 And If(Force, eObj.Options, Options).IsEmptyString Then If n.Length = 2 And If(Force, eObj.Options, Options).IsEmptyString Then
If Force Then Return False If Force Then Return False
TrueName = n(0) NameTrue = n(0)
IsChannel = n(1) = SiteSettings.ChannelOption IsChannel = n(1) = SiteSettings.ChannelOption
IsCreator = n(1) = SiteSettings.P_Creators IsCreator = n(1) = SiteSettings.P_Creators
ElseIf IsChannel Then ElseIf IsChannel Then
If Force Then Return False If Force Then Return False
TrueName = Name NameTrue = Name
ElseIf Not If(Force, eObj.Options, Options).IsEmptyString Then ElseIf Not If(Force, eObj.Options, Options).IsEmptyString Then
Dim __TrueName$, __Arguments$, __Gender$ Dim __TrueName$, __Arguments$, __Gender$
Dim __Mode As SiteModes Dim __Mode As SiteModes
@@ -97,10 +96,10 @@ Namespace API.Xhamster
__Arguments = n2(3) __Arguments = n2(3)
__TrueName = n2.ListTake(3, 100, EDP.ReturnValue).ListToString(String.Empty) __TrueName = n2.ListTake(3, 100, EDP.ReturnValue).ListToString(String.Empty)
If Force AndAlso (Not TrueName = __TrueName Or Not SiteMode = __Mode Or Not Gender = __Gender) Then If Force AndAlso (Not NameTrue(True) = __TrueName Or Not SiteMode = __Mode Or Not Gender = __Gender) Then
If ValidateChangeSearchOptions(ToStringForLog, If ValidateChangeSearchOptions(ToStringForLog,
$"{__Mode}{IIf(__Gender.IsEmptyString, String.Empty, $" ({__Gender})")}: {__TrueName}", $"{__Mode}{IIf(__Gender.IsEmptyString, String.Empty, $" ({__Gender})")}: {__TrueName}",
$"{SiteMode}{IIf(Gender.IsEmptyString, String.Empty, $" ({Gender})")}: {TrueName}") Then $"{SiteMode}{IIf(Gender.IsEmptyString, String.Empty, $" ({Gender})")}: {NameTrue(True)}") Then
__ForceApply = True __ForceApply = True
Else Else
Return False Return False
@@ -110,17 +109,17 @@ Namespace API.Xhamster
Arguments = __Arguments Arguments = __Arguments
Options = If(Force, eObj.Options, Options) Options = If(Force, eObj.Options, Options)
If Not Force Then If Not Force Then
TrueName = __TrueName NameTrue = __TrueName
SiteMode = __Mode SiteMode = __Mode
Gender = __Gender Gender = __Gender
UserSiteName = $"{SiteMode}: {TrueName}" UserSiteName = $"{SiteMode}: {NameTrue}"
If FriendlyName.IsEmptyString Then FriendlyName = UserSiteName If FriendlyName.IsEmptyString Then FriendlyName = UserSiteName
Settings.Labels.Add(SearchRequestLabelName) Settings.Labels.Add(SearchRequestLabelName)
Labels.ListAddValue(SearchRequestLabelName, LNC) Labels.ListAddValue(SearchRequestLabelName, LNC)
Labels.Sort() Labels.Sort()
ElseIf Force And __ForceApply Then ElseIf Force And __ForceApply Then
TrueName = __TrueName NameTrue = __TrueName
SiteMode = __Mode SiteMode = __Mode
Gender = __Gender Gender = __Gender
End If End If
@@ -132,7 +131,7 @@ Namespace API.Xhamster
End If End If
Else Else
If Force Then Return False If Force Then Return False
TrueName = n(0) NameTrue = n(0)
End If End If
End If End If
End If End If
@@ -144,7 +143,6 @@ Namespace API.Xhamster
If Loading Then If Loading Then
IsChannel = .Value(Name_IsChannel).FromXML(Of Boolean)(False) IsChannel = .Value(Name_IsChannel).FromXML(Of Boolean)(False)
IsCreator = .Value(Name_IsCreator).FromXML(Of Boolean)(False) IsCreator = .Value(Name_IsCreator).FromXML(Of Boolean)(False)
TrueName = .Value(Name_TrueName)
Gender = .Value(Name_Gender) Gender = .Value(Name_Gender)
SiteMode = .Value(Name_SiteMode).FromXML(Of Integer)(SiteModes.User) SiteMode = .Value(Name_SiteMode).FromXML(Of Integer)(SiteModes.User)
Arguments = .Value(Name_Arguments) Arguments = .Value(Name_Arguments)
@@ -157,7 +155,7 @@ Namespace API.Xhamster
End If End If
.Add(Name_IsChannel, IsChannel.BoolToInteger) .Add(Name_IsChannel, IsChannel.BoolToInteger)
.Add(Name_IsCreator, IsCreator.BoolToInteger) .Add(Name_IsCreator, IsCreator.BoolToInteger)
.Add(Name_TrueName, TrueName) .Add(Name_TrueName, NameTrue(True))
.Add(Name_Gender, Gender) .Add(Name_Gender, Gender)
.Add(Name_SiteMode, CInt(SiteMode)) .Add(Name_SiteMode, CInt(SiteMode))
.Add(Name_Arguments, Arguments) .Add(Name_Arguments, Arguments)
@@ -198,7 +196,7 @@ Namespace API.Xhamster
Case SiteModes.User : url &= SiteSettings.P_Creators Case SiteModes.User : url &= SiteSettings.P_Creators
Case Else : Return String.Empty Case Else : Return String.Empty
End Select End Select
url &= $"/{TrueName}" url &= $"/{NameTrue}"
Dim args$ = Arguments Dim args$ = Arguments
If (args.IsEmptyString OrElse Not args.Contains(newest)) And Not SiteMode = SiteModes.Search Then url &= newest If (args.IsEmptyString OrElse Not args.Contains(newest)) And Not SiteMode = SiteModes.Search Then url &= newest
@@ -289,14 +287,14 @@ Namespace API.Xhamster
URL = $"https://xhamster.com/my/favorites/{IIf(IsVideo, "videos", "photos-and-galleries")}{IIf(Page = 1, String.Empty, $"/{Page}")}" URL = $"https://xhamster.com/my/favorites/{IIf(IsVideo, "videos", "photos-and-galleries")}{IIf(Page = 1, String.Empty, $"/{Page}")}"
containerNodes.Add(If(IsVideo, {"favoriteVideoListComponent", "models"}, {"favoritesGalleriesAndPhotosCollection"})) containerNodes.Add(If(IsVideo, {"favoriteVideoListComponent", "models"}, {"favoritesGalleriesAndPhotosCollection"}))
ElseIf IsChannel Then ElseIf IsChannel Then
URL = $"https://xhamster.com/channels/{TrueName}/newest{IIf(Page = 1, String.Empty, $"/{Page}")}" URL = $"https://xhamster.com/channels/{NameTrue}/newest{IIf(Page = 1, String.Empty, $"/{Page}")}"
ElseIf SiteMode = SiteModes.Search Then ElseIf SiteMode = SiteModes.Search Then
URL = GetNonUserUrl(Page) URL = GetNonUserUrl(Page)
containerNodes.Add({"searchResult", "models"}) containerNodes.Add({"searchResult", "models"})
ElseIf IsCreator Or SiteMode = SiteModes.Tags Or SiteMode = SiteModes.Categories Or SiteMode = SiteModes.Pornstars Then ElseIf IsCreator Or SiteMode = SiteModes.Tags Or SiteMode = SiteModes.Categories Or SiteMode = SiteModes.Pornstars Then
URL = GetNonUserUrl(Page) URL = GetNonUserUrl(Page)
Else Else
URL = $"https://xhamster.com/users/{TrueName}/{IIf(IsVideo, "videos", "photos")}{IIf(Page = 1, String.Empty, $"/{Page}")}" URL = $"https://xhamster.com/users/{NameTrue}/{IIf(IsVideo, "videos", "photos")}{IIf(Page = 1, String.Empty, $"/{Page}")}"
End If End If
ThrowAny(Token) ThrowAny(Token)

View File

@@ -10,36 +10,52 @@ Imports SCrawler.Plugin
Imports SCrawler.Plugin.Attributes Imports SCrawler.Plugin.Attributes
Imports SCrawler.API.Base Imports SCrawler.API.Base
Imports SCrawler.API.YouTube.Base Imports SCrawler.API.YouTube.Base
Imports DN = SCrawler.API.Base.DeclaredNames
Namespace API.YouTube Namespace API.YouTube
<Manifest(YouTubeSiteKey), SpecialForm(True), SpecialForm(False), SeparatedTasks(1)> <Manifest(YouTubeSiteKey), SpecialForm(True), SpecialForm(False), SeparatedTasks(1)>
Friend Class SiteSettings : Inherits SiteSettingsBase Friend Class SiteSettings : Inherits SiteSettingsBase
#Region "Categories"
Private Const CAT_COMMUNITY As String = "Communities"
#End Region
#Region "Declarations" #Region "Declarations"
<PXML, PropertyOption(ControlText:="Download user videos"), PClonable> <PXML, PropertyOption(ControlText:="Use cookies", ControlToolTip:="Default value for new users." & vbCr & "Use cookies when downloading data.", IsAuth:=True), PClonable>
Friend ReadOnly Property DownloadVideos As PropertyValue
<PXML, PropertyOption(ControlText:="Download user shorts"), PClonable>
Friend ReadOnly Property DownloadShorts As PropertyValue
<PXML, PropertyOption(ControlText:="Download user playlists"), PClonable>
Friend ReadOnly Property DownloadPlaylists As PropertyValue
<PXML, PropertyOption(ControlText:="Download user community: images"), PClonable>
Friend ReadOnly Property DownloadCommunityImages As PropertyValue
<PXML, PropertyOption(ControlText:="Download user community: videos"), PClonable>
Friend ReadOnly Property DownloadCommunityVideos As PropertyValue
<PXML, PropertyOption(ControlText:="Ignore community errors", ControlToolTip:="If true, community errors will not be added to the log."), PClonable>
Friend ReadOnly Property IgnoreCommunityErrors As PropertyValue
<PXML, PropertyOption(ControlText:="Use cookies", ControlToolTip:="Default value for new users." & vbCr & "Use cookies when downloading data."), PClonable>
Friend ReadOnly Property UseCookies As PropertyValue Friend ReadOnly Property UseCookies As PropertyValue
#Region "New user defaults"
<PXML, PropertyOption(ControlText:="Download user videos", Category:=DN.CAT_UserDefs), PClonable>
Friend ReadOnly Property DownloadVideos As PropertyValue
<PXML, PropertyOption(ControlText:="Download user shorts", Category:=DN.CAT_UserDefs), PClonable>
Friend ReadOnly Property DownloadShorts As PropertyValue
<PXML, PropertyOption(ControlText:="Download user playlists", Category:=DN.CAT_UserDefs), PClonable>
Friend ReadOnly Property DownloadPlaylists As PropertyValue
<PXML, PropertyOption(ControlText:="Download user community: images", Category:=DN.CAT_UserDefs), PClonable>
Friend ReadOnly Property DownloadCommunityImages As PropertyValue
<PXML, PropertyOption(ControlText:="Download user community: videos", Category:=DN.CAT_UserDefs), PClonable>
Friend ReadOnly Property DownloadCommunityVideos As PropertyValue
#End Region
#Region "Communities"
<PXML, PropertyOption(ControlText:="YouTube API host",
ControlToolTip:="YouTube API instance host (YouTube-operational-API). Example: 'localhost/YouTube-operational-API', 'http://localhost/YouTube-operational-API'.",
Category:=CAT_COMMUNITY), PClonable>
Friend ReadOnly Property CommunityHost As PropertyValue
<PXML, PropertyOption(ControlText:="YouTube API key", ControlToolTip:="YouTube Data API v3 developer key", Category:=CAT_COMMUNITY), PClonable>
Friend ReadOnly Property YouTubeAPIKey As PropertyValue
<PXML, PropertyOption(ControlText:="Ignore community errors", ControlToolTip:="If true, community errors will not be added to the log.", Category:=CAT_COMMUNITY), PClonable>
Friend ReadOnly Property IgnoreCommunityErrors As PropertyValue
#End Region
#End Region #End Region
#Region "Initializer" #Region "Initializer"
Friend Sub New(ByVal AccName As String, ByVal Temp As Boolean) Friend Sub New(ByVal AccName As String, ByVal Temp As Boolean)
MyBase.New(YouTubeSite, "youtube.com", AccName, Temp, My.Resources.SiteYouTube.YouTubeIcon_32, My.Resources.SiteYouTube.YouTubePic_96) MyBase.New(YouTubeSite, "youtube.com", AccName, Temp, My.Resources.SiteYouTube.YouTubeIcon_32, My.Resources.SiteYouTube.YouTubePic_96)
Responser.Cookies.ChangedAllowInternalDrop = False Responser.Cookies.ChangedAllowInternalDrop = False
UseCookies = New PropertyValue(False)
DownloadVideos = New PropertyValue(True) DownloadVideos = New PropertyValue(True)
DownloadShorts = New PropertyValue(False) DownloadShorts = New PropertyValue(False)
DownloadPlaylists = New PropertyValue(False) DownloadPlaylists = New PropertyValue(False)
DownloadCommunityImages = New PropertyValue(False) DownloadCommunityImages = New PropertyValue(False)
DownloadCommunityVideos = New PropertyValue(False) DownloadCommunityVideos = New PropertyValue(False)
CommunityHost = New PropertyValue(String.Empty, GetType(String))
YouTubeAPIKey = New PropertyValue(String.Empty, GetType(String))
IgnoreCommunityErrors = New PropertyValue(False) IgnoreCommunityErrors = New PropertyValue(False)
UseCookies = New PropertyValue(False)
_SubscriptionsAllowed = True _SubscriptionsAllowed = True
UseNetscapeCookies = True UseNetscapeCookies = True
End Sub End Sub

View File

@@ -33,6 +33,11 @@ Namespace API.YouTube
Private Const Name_LastDownloadDatePlaylist As String = "YTLastDownloadDatePlaylist" Private Const Name_LastDownloadDatePlaylist As String = "YTLastDownloadDatePlaylist"
#End Region #End Region
#Region "Declarations" #Region "Declarations"
Private ReadOnly Property MySettings As SiteSettings
Get
Return HOST.Source
End Get
End Property
Friend Property DownloadYTVideos As Boolean = True Friend Property DownloadYTVideos As Boolean = True
Friend Property DownloadYTShorts As Boolean = False Friend Property DownloadYTShorts As Boolean = False
Friend Property DownloadYTPlaylists As Boolean = False Friend Property DownloadYTPlaylists As Boolean = False
@@ -263,7 +268,17 @@ Namespace API.YouTube
If ChannelID.IsEmptyString Then GetChannelID() If ChannelID.IsEmptyString Then GetChannelID()
If ChannelID.IsEmptyString Then Throw New ArgumentNullException("ChannelID", "Channel ID cannot be null") If ChannelID.IsEmptyString Then Throw New ArgumentNullException("ChannelID", "Channel ID cannot be null")
URL = $"https://yt.lemnoslife.com/channels?part=community&id={ChannelID}" URL = MySettings.CommunityHost.Value
If URL.IsEmptyString Then
If Not CBool(MySettings.IgnoreCommunityErrors.Value) Then _
MyMainLOG = $"{ToStringForLog()}: YouTube API instance host is not specified for downloading communities"
Exit Sub
Else
URL = LinkFormatterSecure(URL.Trim, "http").TrimEnd("/")
End If
URL = $"{URL}/channels?part=community&id={ChannelID}"
If Not CStr(MySettings.YouTubeAPIKey.Value).IsEmptyString Then URL &= $"&key={CStr(MySettings.YouTubeAPIKey.Value).Trim}"
If Not Cursor.IsEmptyString Then URL &= $"&pageToken={Cursor}" If Not Cursor.IsEmptyString Then URL &= $"&pageToken={Cursor}"
ProgressPre.ChangeMax(1) ProgressPre.ChangeMax(1)
@@ -345,6 +360,8 @@ Namespace API.YouTube
If resp.Status = Net.WebExceptionStatus.ConnectFailure And Round < 2 Then If resp.Status = Net.WebExceptionStatus.ConnectFailure And Round < 2 Then
Thread.Sleep(1000) Thread.Sleep(1000)
DownloadCommunity(Cursor, Token, Round + 1) DownloadCommunity(Cursor, Token, Round + 1)
ElseIf resp.StatusCode = Net.HttpStatusCode.NotFound Then
MyMainLOG = $"{ToStringForLog()} {errMsg} (not found)"
Else Else
Throw resp.ErrorException Throw resp.ErrorException
End If End If

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -61,6 +61,7 @@ Namespace DownloadObjects
Me.BTT_FEED_DELETE_DAILY_LIST = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_FEED_DELETE_DAILY_LIST = New System.Windows.Forms.ToolStripMenuItem()
Me.BTT_FEED_DELETE_DAILY_DATE = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_FEED_DELETE_DAILY_DATE = New System.Windows.Forms.ToolStripMenuItem()
Me.BTT_CURR_SESSION_SET = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_CURR_SESSION_SET = New System.Windows.Forms.ToolStripMenuItem()
Me.BTT_CURR_SESSION_SET_LAST = New System.Windows.Forms.ToolStripMenuItem()
Me.BTT_MERGE_SESSIONS = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_MERGE_SESSIONS = New System.Windows.Forms.ToolStripMenuItem()
Me.BTT_CLEAR_DAILY = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_CLEAR_DAILY = New System.Windows.Forms.ToolStripMenuItem()
Me.BTT_RESET_DAILY = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_RESET_DAILY = New System.Windows.Forms.ToolStripMenuItem()
@@ -75,7 +76,7 @@ Namespace DownloadObjects
Me.BTT_DOWN_SELECTED = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_DOWN_SELECTED = New System.Windows.Forms.ToolStripMenuItem()
Me.BTT_REFRESH = New System.Windows.Forms.ToolStripButton() Me.BTT_REFRESH = New System.Windows.Forms.ToolStripButton()
Me.TP_DATA = New System.Windows.Forms.TableLayoutPanel() Me.TP_DATA = New System.Windows.Forms.TableLayoutPanel()
Me.BTT_CURR_SESSION_SET_LAST = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_CHECK_INVERT = New System.Windows.Forms.ToolStripMenuItem()
SEP_1 = New System.Windows.Forms.ToolStripSeparator() SEP_1 = New System.Windows.Forms.ToolStripSeparator()
SEP_2 = New System.Windows.Forms.ToolStripSeparator() SEP_2 = New System.Windows.Forms.ToolStripSeparator()
MENU_VIEW = New System.Windows.Forms.ToolStripDropDownButton() MENU_VIEW = New System.Windows.Forms.ToolStripDropDownButton()
@@ -187,7 +188,7 @@ Namespace DownloadObjects
' '
Me.MENU_LOAD_SESSION.AutoToolTip = False Me.MENU_LOAD_SESSION.AutoToolTip = False
Me.MENU_LOAD_SESSION.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image Me.MENU_LOAD_SESSION.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image
Me.MENU_LOAD_SESSION.DropDownItems.AddRange(New System.Windows.Forms.ToolStripItem() {Me.BTT_LOAD_SESSION_CURRENT, Me.BTT_LOAD_SESSION_LAST, Me.BTT_LOAD_SESSION_CHOOSE, MENU_LOAD_SEP_0, Me.BTT_COPY_TO, Me.BTT_MOVE_TO, MENU_LOAD_SEP_1, Me.BTT_COPY_SPEC_TO, Me.BTT_MOVE_SPEC_TO, MENU_LOAD_SEP_2, Me.BTT_LOAD_FAV, Me.BTT_LOAD_SPEC, MENU_LOAD_SEP_3, Me.BTT_FEED_ADD_FAV, Me.BTT_FEED_ADD_FAV_REMOVE, Me.BTT_FEED_REMOVE_FAV, MENU_LOAD_SEP_4, Me.BTT_FEED_ADD_SPEC, Me.BTT_FEED_ADD_SPEC_REMOVE, Me.BTT_FEED_REMOVE_SPEC, MENU_LOAD_SEP_5, Me.BTT_FEED_CLEAR_FAV, Me.BTT_FEED_CLEAR_SPEC, Me.BTT_FEED_DELETE_SPEC, Me.BTT_FEED_DELETE_DAILY_LIST, Me.BTT_FEED_DELETE_DAILY_DATE, MENU_LOAD_SEP_6, Me.BTT_CURR_SESSION_SET, Me.BTT_CURR_SESSION_SET_LAST, Me.BTT_MERGE_SESSIONS, Me.BTT_CLEAR_DAILY, Me.BTT_RESET_DAILY, MENU_LOAD_SEP_7, Me.BTT_MERGE_FEEDS, MENU_LOAD_SEP_8, Me.BTT_CHECK_ALL, Me.BTT_CHECK_NONE, MENU_LOAD_SEP_9, Me.BTT_VIEW_SAVE, Me.BTT_VIEW_LOAD}) Me.MENU_LOAD_SESSION.DropDownItems.AddRange(New System.Windows.Forms.ToolStripItem() {Me.BTT_LOAD_SESSION_CURRENT, Me.BTT_LOAD_SESSION_LAST, Me.BTT_LOAD_SESSION_CHOOSE, MENU_LOAD_SEP_0, Me.BTT_COPY_TO, Me.BTT_MOVE_TO, MENU_LOAD_SEP_1, Me.BTT_COPY_SPEC_TO, Me.BTT_MOVE_SPEC_TO, MENU_LOAD_SEP_2, Me.BTT_LOAD_FAV, Me.BTT_LOAD_SPEC, MENU_LOAD_SEP_3, Me.BTT_FEED_ADD_FAV, Me.BTT_FEED_ADD_FAV_REMOVE, Me.BTT_FEED_REMOVE_FAV, MENU_LOAD_SEP_4, Me.BTT_FEED_ADD_SPEC, Me.BTT_FEED_ADD_SPEC_REMOVE, Me.BTT_FEED_REMOVE_SPEC, MENU_LOAD_SEP_5, Me.BTT_FEED_CLEAR_FAV, Me.BTT_FEED_CLEAR_SPEC, Me.BTT_FEED_DELETE_SPEC, Me.BTT_FEED_DELETE_DAILY_LIST, Me.BTT_FEED_DELETE_DAILY_DATE, MENU_LOAD_SEP_6, Me.BTT_CURR_SESSION_SET, Me.BTT_CURR_SESSION_SET_LAST, Me.BTT_MERGE_SESSIONS, Me.BTT_CLEAR_DAILY, Me.BTT_RESET_DAILY, MENU_LOAD_SEP_7, Me.BTT_MERGE_FEEDS, MENU_LOAD_SEP_8, Me.BTT_CHECK_ALL, Me.BTT_CHECK_NONE, Me.BTT_CHECK_INVERT, MENU_LOAD_SEP_9, Me.BTT_VIEW_SAVE, Me.BTT_VIEW_LOAD})
Me.MENU_LOAD_SESSION.Image = Global.SCrawler.My.Resources.Resources.ArrowDownPic_Blue_24 Me.MENU_LOAD_SESSION.Image = Global.SCrawler.My.Resources.Resources.ArrowDownPic_Blue_24
Me.MENU_LOAD_SESSION.ImageTransparentColor = System.Drawing.Color.Magenta Me.MENU_LOAD_SESSION.ImageTransparentColor = System.Drawing.Color.Magenta
Me.MENU_LOAD_SESSION.Name = "MENU_LOAD_SESSION" Me.MENU_LOAD_SESSION.Name = "MENU_LOAD_SESSION"
@@ -347,6 +348,13 @@ Namespace DownloadObjects
Me.BTT_CURR_SESSION_SET.Text = "Set current session..." Me.BTT_CURR_SESSION_SET.Text = "Set current session..."
Me.BTT_CURR_SESSION_SET.ToolTipText = "Select one of the download sessions and set it as the current session" Me.BTT_CURR_SESSION_SET.ToolTipText = "Select one of the download sessions and set it as the current session"
' '
'BTT_CURR_SESSION_SET_LAST
'
Me.BTT_CURR_SESSION_SET_LAST.Image = Global.SCrawler.My.Resources.Resources.ArrowDownPic_Blue_24
Me.BTT_CURR_SESSION_SET_LAST.Name = "BTT_CURR_SESSION_SET_LAST"
Me.BTT_CURR_SESSION_SET_LAST.Size = New System.Drawing.Size(352, 22)
Me.BTT_CURR_SESSION_SET_LAST.Text = "Set last download session as current session"
'
'BTT_MERGE_SESSIONS 'BTT_MERGE_SESSIONS
' '
Me.BTT_MERGE_SESSIONS.AutoToolTip = True Me.BTT_MERGE_SESSIONS.AutoToolTip = True
@@ -476,12 +484,11 @@ Namespace DownloadObjects
Me.TP_DATA.Size = New System.Drawing.Size(484, 436) Me.TP_DATA.Size = New System.Drawing.Size(484, 436)
Me.TP_DATA.TabIndex = 1 Me.TP_DATA.TabIndex = 1
' '
'BTT_CURR_SESSION_SET_LAST 'BTT_CHECK_INVERT
' '
Me.BTT_CURR_SESSION_SET_LAST.Image = Global.SCrawler.My.Resources.Resources.ArrowDownPic_Blue_24 Me.BTT_CHECK_INVERT.Name = "BTT_CHECK_INVERT"
Me.BTT_CURR_SESSION_SET_LAST.Name = "BTT_CURR_SESSION_SET_LAST" Me.BTT_CHECK_INVERT.Size = New System.Drawing.Size(352, 22)
Me.BTT_CURR_SESSION_SET_LAST.Size = New System.Drawing.Size(352, 22) Me.BTT_CHECK_INVERT.Text = "Invert selection"
Me.BTT_CURR_SESSION_SET_LAST.Text = "Set last download session as current session"
' '
'DownloadFeedForm 'DownloadFeedForm
' '
@@ -543,5 +550,6 @@ Namespace DownloadObjects
Private WithEvents BTT_MOVE_SPEC_TO As ToolStripMenuItem Private WithEvents BTT_MOVE_SPEC_TO As ToolStripMenuItem
Private WithEvents BTT_RESET_DAILY As ToolStripMenuItem Private WithEvents BTT_RESET_DAILY As ToolStripMenuItem
Private WithEvents BTT_CURR_SESSION_SET_LAST As ToolStripMenuItem Private WithEvents BTT_CURR_SESSION_SET_LAST As ToolStripMenuItem
Private WithEvents BTT_CHECK_INVERT As ToolStripMenuItem
End Class End Class
End Namespace End Namespace

View File

@@ -1081,13 +1081,14 @@ Namespace DownloadObjects
End Try End Try
End Sub End Sub
#End Region #End Region
Private Sub BTT_CHECK_ALL_NONE_Click(sender As Object, e As EventArgs) Handles BTT_CHECK_ALL.Click, BTT_CHECK_NONE.Click Private Sub BTT_CHECK_ALL_NONE_Click(sender As Object, e As EventArgs) Handles BTT_CHECK_ALL.Click, BTT_CHECK_NONE.Click, BTT_CHECK_INVERT.Click
Try Try
Dim checked As Boolean = sender Is BTT_CHECK_ALL Dim checked As Boolean = sender Is BTT_CHECK_ALL
Dim isInvert As Boolean = sender Is BTT_CHECK_INVERT
ControlInvokeFast(TP_DATA, Sub() ControlInvokeFast(TP_DATA, Sub()
With TP_DATA With TP_DATA
If .Controls.Count > 0 Then If .Controls.Count > 0 Then
For Each cnt As FeedMedia In .Controls : cnt.Checked = checked : Next For Each cnt As FeedMedia In .Controls : cnt.Checked = If(isInvert, Not cnt.Checked, checked) : Next
End If End If
End With End With
End Sub, EDP.None) End Sub, EDP.None)

View File

@@ -407,7 +407,7 @@ Namespace DownloadObjects
#End Region #End Region
#Region "Picture / Video objects" #Region "Picture / Video objects"
Private Sub MyPicture_DoubleClick(sender As Object, e As EventArgs) Handles MyPicture.DoubleClick Private Sub MyPicture_DoubleClick(sender As Object, e As EventArgs) Handles MyPicture.DoubleClick
Try : Process.Start(File) : Catch : End Try Try : Process.Start(IIf(IsSubscription, Post.URL_BASE, File.ToString)) : Catch : End Try
End Sub End Sub
#End Region #End Region
#Region "Context" #Region "Context"

View File

@@ -55,8 +55,10 @@ Namespace DownloadObjects
#Region "FeedsComparer" #Region "FeedsComparer"
Private Class FeedsComparer : Implements IComparer(Of FeedSpecial) Private Class FeedsComparer : Implements IComparer(Of FeedSpecial)
Friend Function Compare(ByVal x As FeedSpecial, ByVal y As FeedSpecial) As Integer Implements IComparer(Of FeedSpecial).Compare Friend Function Compare(ByVal x As FeedSpecial, ByVal y As FeedSpecial) As Integer Implements IComparer(Of FeedSpecial).Compare
If x.IsFavorite Then If x.IsFavorite And Not y.IsFavorite Then
Return -1 Return -1
ElseIf Not x.IsFavorite And y.IsFavorite Then
Return 1
Else Else
Return x.Name.CompareTo(y.Name) Return x.Name.CompareTo(y.Name)
End If End If

File diff suppressed because it is too large Load Diff

View File

@@ -120,11 +120,8 @@
<metadata name="TP_BASIS.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <metadata name="TP_BASIS.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value> <value>False</value>
</metadata> </metadata>
<metadata name="TP_IMAGES.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value>
</metadata>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" /> <assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="ActionButton39.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ActionButton1.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP
@@ -135,7 +132,7 @@
cMaRN0UdBBkAAAAASUVORK5CYII= cMaRN0UdBBkAAAAASUVORK5CYII=
</value> </value>
</data> </data>
<data name="ActionButton40.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ActionButton2.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
@@ -146,7 +143,7 @@
<metadata name="TP_IMAGES.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <metadata name="TP_IMAGES.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value> <value>False</value>
</metadata> </metadata>
<data name="ActionButton41.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ActionButton3.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
@@ -154,7 +151,7 @@
AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC
</value> </value>
</data> </data>
<data name="ActionButton42.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ActionButton4.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6 iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6
JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAACOElE JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAACOElE
@@ -170,7 +167,7 @@
VnR1MIwhwMTCyqEQ37qEmZVDFF0OE/9nAACtFF4Ey6OP+wAAAABJRU5ErkJggg== VnR1MIwhwMTCyqEQ37qEmZVDFF0OE/9nAACtFF4Ey6OP+wAAAABJRU5ErkJggg==
</value> </value>
</data> </data>
<data name="ActionButton43.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ActionButton5.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6 iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6
JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAACOElE JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAACOElE
@@ -186,31 +183,7 @@
VnR1MIwhwMTCyqEQ37qEmZVDFF0OE/9nAACtFF4Ey6OP+wAAAABJRU5ErkJggg== VnR1MIwhwMTCyqEQ37qEmZVDFF0OE/9nAACtFF4Ey6OP+wAAAABJRU5ErkJggg==
</value> </value>
</data> </data>
<data name="ActionButton44.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ActionButton6.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
tbbB43rK5xSAQq1VYFtmeQBoqZTSreVZvgTknM8yyyjA/qodsDF9gspD2Bj6B+DH+NqzhQQAG+POMnSX
AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC
</value>
</data>
<data name="ActionButton45.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6
JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAACOElE
QVQ4T2P4//8/QczOJyyqHpzfiE0OQwAZC8iqszAzs7CJ69o4BR768V/W2jcGXQ0KB4aFNS3dDQtnrbCb
ePCK48wTN1wXXXzge/jXf/clV55zC4hIIatF0cjIyMikElzc57z0wX+XHd/+2+//99/ywP//xlu//tdb
+eK/4Zp3/1WTOhYzARViNUAluKjTdf37/0ZTTn9TbdhwXblhwwW1/qOP1Ja9+K8w+95/6cm3/6v2Xvkv
qKjniGGAoIqRpW3/4e8S9uGdzFz82gwMDFxAzCxm4ZegtuLDf+VJ1/8rZM25IqLvnM/CximCYYCic1QN
v7x2JIwPwyrJ3XNUylddE9G2TWNmZOBDl4czmJiZMSRBmFdSyYyJgUEQmxwIYxWEYXZBCUls4sgYq6CA
prWNbtG8nXKeaVPR5XiVjSxEzf0yYXy4BBMLO6eQjoOXZvrkbbazrv53Xf/2v4CSbjBMXkhBl1/CMyNZ
qWnvGy5pNQ+YONwAfjXzAOupl/47LLr333L50/96q9/8l23YdES6cO5KuYqVW+R7Tj6SnfP0v4hryjyY
HhQDmFjYeHVKFp7WX/Xuv9Kq9/+Vd/z7r7rv/3+l7f//y676DEwDN/9L+BVvYkKLCTgDhNkkVUyVlr74
qbbz73/VOTc/qsy89kWx+9h7qbQpJwS1bbOAscGGrB6EUTggLOqf16C55ft/HlnNAFZOXgVWdi4FRgYG
VnR1MIwhwMTCyqEQ37qEmZVDFF0OE/9nAACtFF4Ey6OP+wAAAABJRU5ErkJggg==
</value>
</data>
<data name="ActionButton46.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
@@ -227,9 +200,6 @@
<metadata name="LBL_DATE_POS.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <metadata name="LBL_DATE_POS.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value> <value>False</value>
</metadata> </metadata>
<metadata name="LBL_DATE_POS.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value>
</metadata>
<metadata name="TT_MAIN.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <metadata name="TT_MAIN.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value> <value>False</value>
</metadata> </metadata>
@@ -263,37 +233,19 @@ You can find more detailed information about the missing posts in the form that
<metadata name="TP_DEFS.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <metadata name="TP_DEFS.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value> <value>False</value>
</metadata> </metadata>
<metadata name="TP_DEFS.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value>
</metadata>
<metadata name="TAB_DEFS_CHANNELS.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <metadata name="TAB_DEFS_CHANNELS.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value> <value>False</value>
</metadata> </metadata>
<metadata name="TP_CHANNELS.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <metadata name="TP_CHANNELS.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value> <value>False</value>
</metadata> </metadata>
<metadata name="TP_CHANNELS.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value>
</metadata>
<metadata name="TAB_BEHAVIOR.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <metadata name="TAB_BEHAVIOR.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value> <value>False</value>
</metadata> </metadata>
<metadata name="TP_BEHAVIOR.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <metadata name="TP_BEHAVIOR.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value> <value>False</value>
</metadata> </metadata>
<metadata name="TP_BEHAVIOR.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <data name="ActionButton7.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>False</value>
</metadata>
<metadata name="TP_OPEN_INFO.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value>
</metadata>
<metadata name="TP_OPEN_PROGRESS.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value>
</metadata>
<metadata name="TP_BEHAVIOR_F6.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value>
</metadata>
<data name="ActionButton47.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
@@ -301,7 +253,7 @@ You can find more detailed information about the missing posts in the form that
AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC
</value> </value>
</data> </data>
<data name="ActionButton48.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ActionButton8.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
@@ -324,13 +276,7 @@ You can find more detailed information about the missing posts in the form that
<metadata name="TP_DOWNLOADING.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <metadata name="TP_DOWNLOADING.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value> <value>False</value>
</metadata> </metadata>
<metadata name="TP_DOWNLOADING.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <data name="ActionButton9.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>False</value>
</metadata>
<metadata name="TP_MISSING_DATA.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value>
</metadata>
<data name="ActionButton49.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP
@@ -341,7 +287,7 @@ You can find more detailed information about the missing posts in the form that
cMaRN0UdBBkAAAAASUVORK5CYII= cMaRN0UdBBkAAAAASUVORK5CYII=
</value> </value>
</data> </data>
<data name="ActionButton50.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ActionButton10.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
@@ -364,10 +310,7 @@ You can find more detailed information about the missing posts in the form that
<metadata name="TP_FEED_SES.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <metadata name="TP_FEED_SES.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value> <value>False</value>
</metadata> </metadata>
<metadata name="TP_FEED_SPEC_SEARCH.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <data name="ActionButton11.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>False</value>
</metadata>
<data name="ActionButton51.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6 iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6
JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAACOElE JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAACOElE
@@ -383,7 +326,7 @@ You can find more detailed information about the missing posts in the form that
VnR1MIwhwMTCyqEQ37qEmZVDFF0OE/9nAACtFF4Ey6OP+wAAAABJRU5ErkJggg== VnR1MIwhwMTCyqEQ37qEmZVDFF0OE/9nAACtFF4Ey6OP+wAAAABJRU5ErkJggg==
</value> </value>
</data> </data>
<data name="ActionButton52.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ActionButton12.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
@@ -397,10 +340,10 @@ You can find more detailed information about the missing posts in the form that
0 - only the session of the current day. 0 - only the session of the current day.
&gt;0 - the value (in minutes) that must elapse since last file download in a session for that session to be considered current.</value> &gt;0 - the value (in minutes) that must elapse since last file download in a session for that session to be considered current.</value>
</data> </data>
<metadata name="TAB_NOTIFY.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <metadata name="TP_FEED_SPEC_SEARCH.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value> <value>False</value>
</metadata> </metadata>
<metadata name="TP_NOTIFY_MAIN.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <metadata name="TAB_NOTIFY.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value> <value>False</value>
</metadata> </metadata>
<metadata name="TP_NOTIFY_MAIN.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <metadata name="TP_NOTIFY_MAIN.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
@@ -409,7 +352,7 @@ You can find more detailed information about the missing posts in the form that
<metadata name="TP_ENVIR.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <metadata name="TP_ENVIR.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value> <value>False</value>
</metadata> </metadata>
<data name="ActionButton53.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ActionButton13.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP
@@ -420,7 +363,7 @@ You can find more detailed information about the missing posts in the form that
cMaRN0UdBBkAAAAASUVORK5CYII= cMaRN0UdBBkAAAAASUVORK5CYII=
</value> </value>
</data> </data>
<data name="ActionButton54.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ActionButton14.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6 iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6
JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAACOElE JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAACOElE
@@ -436,7 +379,7 @@ You can find more detailed information about the missing posts in the form that
VnR1MIwhwMTCyqEQ37qEmZVDFF0OE/9nAACtFF4Ey6OP+wAAAABJRU5ErkJggg== VnR1MIwhwMTCyqEQ37qEmZVDFF0OE/9nAACtFF4Ey6OP+wAAAABJRU5ErkJggg==
</value> </value>
</data> </data>
<data name="ActionButton55.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ActionButton15.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
@@ -444,7 +387,7 @@ You can find more detailed information about the missing posts in the form that
AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC
</value> </value>
</data> </data>
<data name="ActionButton56.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ActionButton16.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP
@@ -455,7 +398,7 @@ You can find more detailed information about the missing posts in the form that
cMaRN0UdBBkAAAAASUVORK5CYII= cMaRN0UdBBkAAAAASUVORK5CYII=
</value> </value>
</data> </data>
<data name="ActionButton57.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ActionButton17.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6 iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6
JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAACOElE JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAACOElE
@@ -471,7 +414,7 @@ You can find more detailed information about the missing posts in the form that
VnR1MIwhwMTCyqEQ37qEmZVDFF0OE/9nAACtFF4Ey6OP+wAAAABJRU5ErkJggg== VnR1MIwhwMTCyqEQ37qEmZVDFF0OE/9nAACtFF4Ey6OP+wAAAABJRU5ErkJggg==
</value> </value>
</data> </data>
<data name="ActionButton58.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ActionButton18.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
@@ -479,7 +422,7 @@ You can find more detailed information about the missing posts in the form that
AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC
</value> </value>
</data> </data>
<data name="ActionButton59.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ActionButton19.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP
@@ -490,7 +433,7 @@ You can find more detailed information about the missing posts in the form that
cMaRN0UdBBkAAAAASUVORK5CYII= cMaRN0UdBBkAAAAASUVORK5CYII=
</value> </value>
</data> </data>
<data name="ActionButton60.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ActionButton20.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6 iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6
JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAACOElE JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAACOElE
@@ -506,7 +449,7 @@ You can find more detailed information about the missing posts in the form that
VnR1MIwhwMTCyqEQ37qEmZVDFF0OE/9nAACtFF4Ey6OP+wAAAABJRU5ErkJggg== VnR1MIwhwMTCyqEQ37qEmZVDFF0OE/9nAACtFF4Ey6OP+wAAAABJRU5ErkJggg==
</value> </value>
</data> </data>
<data name="ActionButton61.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ActionButton21.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
@@ -514,7 +457,7 @@ You can find more detailed information about the missing posts in the form that
AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC
</value> </value>
</data> </data>
<data name="ActionButton62.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ActionButton22.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP
@@ -525,7 +468,7 @@ You can find more detailed information about the missing posts in the form that
cMaRN0UdBBkAAAAASUVORK5CYII= cMaRN0UdBBkAAAAASUVORK5CYII=
</value> </value>
</data> </data>
<data name="ActionButton63.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ActionButton23.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6 iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6
JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAACOElE JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAACOElE
@@ -541,7 +484,7 @@ You can find more detailed information about the missing posts in the form that
VnR1MIwhwMTCyqEQ37qEmZVDFF0OE/9nAACtFF4Ey6OP+wAAAABJRU5ErkJggg== VnR1MIwhwMTCyqEQ37qEmZVDFF0OE/9nAACtFF4Ey6OP+wAAAABJRU5ErkJggg==
</value> </value>
</data> </data>
<data name="ActionButton64.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ActionButton24.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
@@ -549,7 +492,7 @@ You can find more detailed information about the missing posts in the form that
AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC
</value> </value>
</data> </data>
<data name="ActionButton65.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ActionButton25.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6 iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6
JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAACOElE JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAACOElE
@@ -565,7 +508,7 @@ You can find more detailed information about the missing posts in the form that
VnR1MIwhwMTCyqEQ37qEmZVDFF0OE/9nAACtFF4Ey6OP+wAAAABJRU5ErkJggg== VnR1MIwhwMTCyqEQ37qEmZVDFF0OE/9nAACtFF4Ey6OP+wAAAABJRU5ErkJggg==
</value> </value>
</data> </data>
<data name="ActionButton66.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ActionButton26.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
@@ -579,10 +522,7 @@ You can find more detailed information about the missing posts in the form that
<metadata name="TP_STD.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <metadata name="TP_STD.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value> <value>False</value>
</metadata> </metadata>
<metadata name="TP_STD.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <data name="ActionButton27.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>False</value>
</metadata>
<data name="ActionButton67.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAABGdBTUEAALGPC/xhBQAAE65JREFUeF7t iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAABGdBTUEAALGPC/xhBQAAE65JREFUeF7t
3X2sJWddB/DdLi2lQG2hdOHuvfM887J7Cxca4ELTQMDWKigIFpBAEAgi9g+CJpJo9Q8NJhgBiYZIYspL 3X2sJWddB/DdLi2lQG2hdOHuvfM887J7Cxca4ELTQMDWKigIFpBAEAgi9g+CJpJo9Q8NJhgBiYZIYspL
@@ -678,10 +618,7 @@ You can find more detailed information about the missing posts in the form that
<metadata name="TP_DESIGN.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <metadata name="TP_DESIGN.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value> <value>False</value>
</metadata> </metadata>
<metadata name="TP_DESIGN.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <data name="ActionButton28.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>False</value>
</metadata>
<data name="ActionButton68.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
@@ -689,7 +626,7 @@ You can find more detailed information about the missing posts in the form that
AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC
</value> </value>
</data> </data>
<data name="ActionButton69.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ActionButton29.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
@@ -697,7 +634,7 @@ You can find more detailed information about the missing posts in the form that
AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC
</value> </value>
</data> </data>
<data name="ActionButton70.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ActionButton30.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP
@@ -708,7 +645,7 @@ You can find more detailed information about the missing posts in the form that
cMaRN0UdBBkAAAAASUVORK5CYII= cMaRN0UdBBkAAAAASUVORK5CYII=
</value> </value>
</data> </data>
<data name="ActionButton71.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ActionButton31.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
@@ -719,7 +656,7 @@ You can find more detailed information about the missing posts in the form that
<metadata name="TP_HEADERS_DEF.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <metadata name="TP_HEADERS_DEF.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value> <value>False</value>
</metadata> </metadata>
<data name="ActionButton72.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ActionButton32.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
@@ -727,7 +664,7 @@ You can find more detailed information about the missing posts in the form that
AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC
</value> </value>
</data> </data>
<data name="ActionButton73.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ActionButton33.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
@@ -735,7 +672,7 @@ You can find more detailed information about the missing posts in the form that
AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC
</value> </value>
</data> </data>
<data name="ActionButton74.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ActionButton34.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
@@ -743,7 +680,7 @@ You can find more detailed information about the missing posts in the form that
AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC
</value> </value>
</data> </data>
<data name="ActionButton75.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ActionButton35.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
@@ -751,7 +688,7 @@ You can find more detailed information about the missing posts in the form that
AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC
</value> </value>
</data> </data>
<data name="ActionButton76.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ActionButton36.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go

View File

@@ -38,7 +38,6 @@ Namespace Editors
TXT_MAX_JOBS_USERS.Value = .MaxUsersJobsCount.Value TXT_MAX_JOBS_USERS.Value = .MaxUsersJobsCount.Value
TXT_MAX_JOBS_CHANNELS.Value = .ChannelsMaxJobsCount.Value TXT_MAX_JOBS_CHANNELS.Value = .ChannelsMaxJobsCount.Value
CH_CHECK_VER_START.Checked = .CheckUpdatesAtStart CH_CHECK_VER_START.Checked = .CheckUpdatesAtStart
TXT_USER_AGENT.Text = .UserAgent
TXT_IMGUR_CLIENT_ID.Text = .ImgurClientID TXT_IMGUR_CLIENT_ID.Text = .ImgurClientID
'Design 'Design
TXT_PRG_TITLE.Text = .ProgramText TXT_PRG_TITLE.Text = .ProgramText
@@ -80,6 +79,7 @@ Namespace Editors
TXT_SCHEDULER_SCRIPT.Text = .AutomationScript TXT_SCHEDULER_SCRIPT.Text = .AutomationScript
TXT_SCHEDULER_SCRIPT.Checked = .AutomationScript.Use TXT_SCHEDULER_SCRIPT.Checked = .AutomationScript.Use
CH_SCHEDULER_SCRIPT_EX_MANUAL.Checked = .AutomationScript_ExcludeManual CH_SCHEDULER_SCRIPT_EX_MANUAL.Checked = .AutomationScript_ExcludeManual
CH_FEED_OPEN_CTRLF.Checked = .FeedOpenCtrlF
'Notifications 'Notifications
CH_NOTIFY_SILENT.Checked = .NotificationsSilentMode CH_NOTIFY_SILENT.Checked = .NotificationsSilentMode
CH_NOTIFY_SHOW_BASE.Checked = .ShowNotifications CH_NOTIFY_SHOW_BASE.Checked = .ShowNotifications
@@ -254,8 +254,6 @@ Namespace Editors
.MaxUsersJobsCount.Value = CInt(TXT_MAX_JOBS_USERS.Value) .MaxUsersJobsCount.Value = CInt(TXT_MAX_JOBS_USERS.Value)
.ChannelsMaxJobsCount.Value = TXT_MAX_JOBS_CHANNELS.Value .ChannelsMaxJobsCount.Value = TXT_MAX_JOBS_CHANNELS.Value
.CheckUpdatesAtStart.Value = CH_CHECK_VER_START.Checked .CheckUpdatesAtStart.Value = CH_CHECK_VER_START.Checked
.UserAgent.Value = TXT_USER_AGENT.Text
UserAgentChanged = .UserAgent.ChangesDetected
.ImgurClientID.Value = TXT_IMGUR_CLIENT_ID.Text .ImgurClientID.Value = TXT_IMGUR_CLIENT_ID.Text
'Design 'Design
.ProgramText.Value = TXT_PRG_TITLE.Text .ProgramText.Value = TXT_PRG_TITLE.Text
@@ -275,6 +273,7 @@ Namespace Editors
.CMDEncoding.ChangesDetected .CMDEncoding.ChangesDetected
'Headers 'Headers
.HEADER_UserAgent.Value = TXT_H_DEF_UserAgent.Text .HEADER_UserAgent.Value = TXT_H_DEF_UserAgent.Text
UserAgentChanged = .HEADER_UserAgent.ChangesDetected
.HEADER_sec_ch_ua.Value = TXT_H_DEF_sec_ch_ua.Text .HEADER_sec_ch_ua.Value = TXT_H_DEF_sec_ch_ua.Text
.HEADER_sec_ch_ua_full_version_list.Value = TXT_H_DEF_sec_ch_ua_full_version_list.Text .HEADER_sec_ch_ua_full_version_list.Value = TXT_H_DEF_sec_ch_ua_full_version_list.Text
.HEADER_sec_ch_ua_platform.Value = TXT_H_DEF_sec_ch_ua_platform.Text .HEADER_sec_ch_ua_platform.Value = TXT_H_DEF_sec_ch_ua_platform.Text
@@ -300,6 +299,7 @@ Namespace Editors
.AutomationScript.Value = TXT_SCHEDULER_SCRIPT.Text .AutomationScript.Value = TXT_SCHEDULER_SCRIPT.Text
.AutomationScript.Use = TXT_SCHEDULER_SCRIPT.Checked .AutomationScript.Use = TXT_SCHEDULER_SCRIPT.Checked
.AutomationScript_ExcludeManual.Value = CH_SCHEDULER_SCRIPT_EX_MANUAL.Checked .AutomationScript_ExcludeManual.Value = CH_SCHEDULER_SCRIPT_EX_MANUAL.Checked
.FeedOpenCtrlF.Value = CH_FEED_OPEN_CTRLF.Checked
'Notifications 'Notifications
.NotificationsSilentMode = CH_NOTIFY_SILENT.Checked .NotificationsSilentMode = CH_NOTIFY_SILENT.Checked
.ShowNotifications.Value = CH_NOTIFY_SHOW_BASE.Checked .ShowNotifications.Value = CH_NOTIFY_SHOW_BASE.Checked
@@ -405,9 +405,6 @@ Namespace Editors
Private Sub TXT_MAX_JOBS_CHANNELS_ActionOnButtonClick(ByVal Sender As ActionButton, ByVal e As EventArgs) Handles TXT_MAX_JOBS_CHANNELS.ActionOnButtonClick Private Sub TXT_MAX_JOBS_CHANNELS_ActionOnButtonClick(ByVal Sender As ActionButton, ByVal e As EventArgs) Handles TXT_MAX_JOBS_CHANNELS.ActionOnButtonClick
If Sender.DefaultButton = ADB.Refresh Then TXT_MAX_JOBS_CHANNELS.Value = SettingsCLS.DefaultMaxDownloadingTasks If Sender.DefaultButton = ADB.Refresh Then TXT_MAX_JOBS_CHANNELS.Value = SettingsCLS.DefaultMaxDownloadingTasks
End Sub End Sub
Private Sub TXT_USER_AGENT_ActionOnButtonClick(ByVal Sender As ActionButton, ByVal e As ActionButtonEventArgs) Handles TXT_USER_AGENT.ActionOnButtonClick
If Sender.DefaultButton = ADB.Refresh Then TXT_USER_AGENT.Text = Settings.UserAgent.Value
End Sub
Private Sub ChangePositionControlsEnabling() Handles OPT_FILE_NAME_REPLACE.CheckedChanged, OPT_FILE_NAME_ADD_DATE.CheckedChanged Private Sub ChangePositionControlsEnabling() Handles OPT_FILE_NAME_REPLACE.CheckedChanged, OPT_FILE_NAME_ADD_DATE.CheckedChanged
Dim b As Boolean = OPT_FILE_NAME_ADD_DATE.Checked And OPT_FILE_NAME_ADD_DATE.Enabled Dim b As Boolean = OPT_FILE_NAME_ADD_DATE.Checked And OPT_FILE_NAME_ADD_DATE.Enabled
OPT_FILE_DATE_START.Enabled = b OPT_FILE_DATE_START.Enabled = b

View File

@@ -357,6 +357,9 @@ Namespace Editors
MyDefs.InvokeLoaderError(ex) MyDefs.InvokeLoaderError(ex)
End Try End Try
End Sub End Sub
Private Sub SiteEditorForm_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown
If e.Control And e.KeyCode = Keys.Enter Then MyDefs_ButtonOkClick(sender, New KeyHandleEventArgs With {.KeyEventArgs = e})
End Sub
Private Sub SiteEditorForm_Disposed(sender As Object, e As EventArgs) Handles Me.Disposed 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 Host.PropList.Count > 0 Then Host.PropList.ForEach(Sub(p) p.DisposeControl())
If Not SpecialButton Is Nothing Then SpecialButton.Dispose() If Not SpecialButton Is Nothing Then SpecialButton.Dispose()
@@ -366,7 +369,11 @@ Namespace Editors
If Not Cookies Is Nothing Then Cookies.Dispose() If Not Cookies Is Nothing Then Cookies.Dispose()
End Sub End Sub
Private Sub MyDefs_ButtonOkClick(ByVal Sender As Object, ByVal e As KeyHandleEventArgs) Handles MyDefs.ButtonOkClick Private Sub MyDefs_ButtonOkClick(ByVal Sender As Object, ByVal e As KeyHandleEventArgs) Handles MyDefs.ButtonOkClick
If MyDefs.MyFieldsChecker.AllParamsOK Then Dim ctrl As Boolean = Not e Is Nothing AndAlso (If(e.KeyEventArgs?.Control, False) OrElse e.Key.Control)
If ctrl OrElse MyDefs.MyFieldsChecker.AllParamsOK Then
If (Not MyDefs.MyFieldsCheckerE.AllParamsOK(EDP.ReturnValue) And ctrl) AndAlso
MsgBoxE({$"Some required fields are not filled in!{vbCr}{vbCr}{MyDefs.MyFieldsChecker.ComparisonInformation}
{vbCr}{vbCr}Are you sure you want to process?", "Required fields are missing"}, vbCritical,,, {"Process", "Cancel"}) = 1 Then Exit Sub
Dim i%, ii% Dim i%, ii%
With Host With Host
Dim indxList As New List(Of Integer) Dim indxList As New List(Of Integer)
@@ -376,6 +383,7 @@ Namespace Editors
If indxList.Count > 0 Then If indxList.Count > 0 Then
Dim pList As New List(Of PropertyData) Dim pList As New List(Of PropertyData)
Dim n$() Dim n$()
Dim errorsDetected As Boolean = False
For i = 0 To indxList.Count - 1 For i = 0 To indxList.Count - 1
n = .PropList(indxList(i)).PropertiesChecking n = .PropList(indxList(i)).PropertiesChecking
For ii = 0 To .PropList.Count - 1 For ii = 0 To .PropList.Count - 1
@@ -383,8 +391,13 @@ Namespace Editors
If n.Contains(.Name) Then pList.Add(New PropertyData(.Name, .GetControlValue)) If n.Contains(.Name) Then pList.Add(New PropertyData(.Name, .GetControlValue))
End With End With
Next Next
If pList.Count > 0 AndAlso Not CBool(.PropList(indxList(i)).PropertiesCheckingMethod.Invoke(.Source, {pList})) Then Exit Sub If pList.Count > 0 AndAlso Not CBool(.PropList(indxList(i)).PropertiesCheckingMethod.Invoke(.Source, {pList})) Then
If ctrl Then errorsDetected = True Else Exit Sub
End If
Next Next
If (ctrl And errorsDetected) AndAlso MsgBoxE({$"Some settings may be incorrect. Do you still want to save?",
"Incorrect settings detected"},
vbCritical,,, {"Process", "Cancel"}) = 1 Then Exit Sub
End If End If
If TXT_PATH.Text.IsEmptyString Then TXT_PATH.Text = .PathGenerate.CSFilePS If TXT_PATH.Text.IsEmptyString Then TXT_PATH.Text = .PathGenerate.CSFilePS

View File

@@ -103,7 +103,7 @@ Namespace Editors
#End Region #End Region
#Region "Exchange, Path, Labels" #Region "Exchange, Path, Labels"
Friend Property MyExchangeOptions As Object = Nothing Friend Property MyExchangeOptions As Object = Nothing
Private ReadOnly _SpecPathPattern As RParams = RParams.DM("\w:\\.*", 0, EDP.ReturnValue) Private ReadOnly _SpecPathPattern As RParams = RParams.DM("(\w:\\|\\\\).*", 0, EDP.ReturnValue)
Private ReadOnly Property SpecialPath(ByVal s As SettingsHost) As SFile Private ReadOnly Property SpecialPath(ByVal s As SettingsHost) As SFile
Get Get
If TXT_SPEC_FOLDER.IsEmptyString Then If TXT_SPEC_FOLDER.IsEmptyString Then

View File

@@ -679,7 +679,7 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form
Me.BTT_CONTEXT_DOWN.Image = Global.SCrawler.My.Resources.Resources.StartPic_Green_16 Me.BTT_CONTEXT_DOWN.Image = Global.SCrawler.My.Resources.Resources.StartPic_Green_16
Me.BTT_CONTEXT_DOWN.Name = "BTT_CONTEXT_DOWN" Me.BTT_CONTEXT_DOWN.Name = "BTT_CONTEXT_DOWN"
Me.BTT_CONTEXT_DOWN.Size = New System.Drawing.Size(221, 22) Me.BTT_CONTEXT_DOWN.Size = New System.Drawing.Size(221, 22)
Me.BTT_CONTEXT_DOWN.Text = "Download data" Me.BTT_CONTEXT_DOWN.Text = "Download"
Me.BTT_CONTEXT_DOWN.ToolTipText = "" Me.BTT_CONTEXT_DOWN.ToolTipText = ""
' '
'BTT_CONTEXT_DOWN_LIMITED 'BTT_CONTEXT_DOWN_LIMITED
@@ -688,7 +688,7 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form
Me.BTT_CONTEXT_DOWN_LIMITED.Image = Global.SCrawler.My.Resources.Resources.StartPic_Green_16 Me.BTT_CONTEXT_DOWN_LIMITED.Image = Global.SCrawler.My.Resources.Resources.StartPic_Green_16
Me.BTT_CONTEXT_DOWN_LIMITED.Name = "BTT_CONTEXT_DOWN_LIMITED" Me.BTT_CONTEXT_DOWN_LIMITED.Name = "BTT_CONTEXT_DOWN_LIMITED"
Me.BTT_CONTEXT_DOWN_LIMITED.Size = New System.Drawing.Size(221, 22) Me.BTT_CONTEXT_DOWN_LIMITED.Size = New System.Drawing.Size(221, 22)
Me.BTT_CONTEXT_DOWN_LIMITED.Text = "Download data limited" Me.BTT_CONTEXT_DOWN_LIMITED.Text = "Download (limited)"
Me.BTT_CONTEXT_DOWN_LIMITED.ToolTipText = "Download top ... posts" Me.BTT_CONTEXT_DOWN_LIMITED.ToolTipText = "Download top ... posts"
' '
'BTT_CONTEXT_DOWN_DATE_LIMIT 'BTT_CONTEXT_DOWN_DATE_LIMIT
@@ -696,7 +696,7 @@ Partial Public Class MainFrame : Inherits System.Windows.Forms.Form
Me.BTT_CONTEXT_DOWN_DATE_LIMIT.Image = Global.SCrawler.My.Resources.Resources.StartPic_Green_16 Me.BTT_CONTEXT_DOWN_DATE_LIMIT.Image = Global.SCrawler.My.Resources.Resources.StartPic_Green_16
Me.BTT_CONTEXT_DOWN_DATE_LIMIT.Name = "BTT_CONTEXT_DOWN_DATE_LIMIT" Me.BTT_CONTEXT_DOWN_DATE_LIMIT.Name = "BTT_CONTEXT_DOWN_DATE_LIMIT"
Me.BTT_CONTEXT_DOWN_DATE_LIMIT.Size = New System.Drawing.Size(221, 22) Me.BTT_CONTEXT_DOWN_DATE_LIMIT.Size = New System.Drawing.Size(221, 22)
Me.BTT_CONTEXT_DOWN_DATE_LIMIT.Text = "Download data to the date" Me.BTT_CONTEXT_DOWN_DATE_LIMIT.Text = "Download (to the date)"
Me.BTT_CONTEXT_DOWN_DATE_LIMIT.ToolTipText = "" Me.BTT_CONTEXT_DOWN_DATE_LIMIT.ToolTipText = ""
' '
'BTT_CONTEXT_EDIT 'BTT_CONTEXT_EDIT

View File

@@ -241,13 +241,13 @@ CloseResume:
If Not b Then If Not b Then
b = True b = True
If e.Control And e.KeyCode = Keys.F Then If Settings.ShortcutOpenFeed = e Then
BTT_FEED.PerformClick() BTT_FEED.PerformClick()
ElseIf e.Alt And e.KeyCode = Keys.A Then ElseIf e.Alt And e.KeyCode = Keys.A Then
BTT_DOWN_AUTOMATION.PerformClick() BTT_DOWN_AUTOMATION.PerformClick()
ElseIf e.Alt And e.KeyCode = Keys.P Then ElseIf e.Alt And e.KeyCode = Keys.P Then
BTT_PR_INFO.PerformClick() BTT_PR_INFO.PerformClick()
ElseIf (e.Alt And (e.KeyCode = Keys.F Or e.KeyCode = Keys.U)) Or (e.Control And e.KeyCode = Keys.U) Then ElseIf Settings.ShortcutOpenSearch = e Or (e.Alt And e.KeyCode = Keys.U) Or (e.Control And e.KeyCode = Keys.U) Then
MySearch.FormShow() MySearch.FormShow()
Else Else
b = False b = False
@@ -1520,6 +1520,8 @@ CloseResume:
If Not user Is Nothing AndAlso user.IsCollection Then If Not user Is Nothing AndAlso user.IsCollection Then
With DirectCast(user, UserDataBind) With DirectCast(user, UserDataBind)
BTT_CONTEXT_DOWN.DropDownItems.AddRange(.ContextDown) BTT_CONTEXT_DOWN.DropDownItems.AddRange(.ContextDown)
BTT_CONTEXT_DOWN_LIMITED.DropDownItems.AddRange(.ContextDownLimit)
BTT_CONTEXT_DOWN_DATE_LIMIT.DropDownItems.AddRange(.ContextDownDate)
BTT_CONTEXT_EDIT.DropDownItems.AddRange(.ContextEdit) BTT_CONTEXT_EDIT.DropDownItems.AddRange(.ContextEdit)
BTT_CONTEXT_DELETE.DropDownItems.AddRange(.ContextDelete) BTT_CONTEXT_DELETE.DropDownItems.AddRange(.ContextDelete)
BTT_CONTEXT_ERASE.DropDownItems.AddRange(.ContextErase) BTT_CONTEXT_ERASE.DropDownItems.AddRange(.ContextErase)
@@ -1529,6 +1531,8 @@ CloseResume:
End If End If
Else Else
BTT_CONTEXT_DOWN.DropDownItems.Clear() BTT_CONTEXT_DOWN.DropDownItems.Clear()
BTT_CONTEXT_DOWN_LIMITED.DropDownItems.Clear()
BTT_CONTEXT_DOWN_DATE_LIMIT.DropDownItems.Clear()
BTT_CONTEXT_EDIT.DropDownItems.Clear() BTT_CONTEXT_EDIT.DropDownItems.Clear()
BTT_CONTEXT_DELETE.DropDownItems.Clear() BTT_CONTEXT_DELETE.DropDownItems.Clear()
BTT_CONTEXT_ERASE.DropDownItems.Clear() BTT_CONTEXT_ERASE.DropDownItems.Clear()
@@ -1634,10 +1638,11 @@ CloseResume:
Return ErrorsDescriber.Execute(EDP.SendToLog + EDP.ReturnValue, ex, "[MainFrame.GetSelectedUserArray]", New List(Of IUserData)) Return ErrorsDescriber.Execute(EDP.SendToLog + EDP.ReturnValue, ex, "[MainFrame.GetSelectedUserArray]", New List(Of IUserData))
End Try End Try
End Function End Function
Private Enum DownUserLimits : None : Number : [Date] : End Enum Friend Enum DownUserLimits : None : Number : [Date] : End Enum
Private Sub DownloadSelectedUser(ByVal UseLimits As DownUserLimits, Optional ByVal IncludeInTheFeed As Boolean = True) Friend Sub DownloadSelectedUser(ByVal UseLimits As DownUserLimits, Optional ByVal IncludeInTheFeed As Boolean = True,
Optional ByVal SUser As IUserData = Nothing)
Const MsgTitle$ = "Download limit" Const MsgTitle$ = "Download limit"
Dim users As List(Of IUserData) = GetSelectedUserArray() Dim users As List(Of IUserData) = If(SUser Is Nothing, GetSelectedUserArray(), New List(Of IUserData) From {SUser})
If users.ListExists Then If users.ListExists Then
Dim limit%? = Nothing Dim limit%? = Nothing
Dim _from As Date? = Nothing Dim _from As Date? = Nothing
@@ -1704,7 +1709,7 @@ ResumeDownloadingOperation:
$"Do you want to download them all{fStr}?{vbNewLine.StringDup(2)}" & $"Do you want to download them all{fStr}?{vbNewLine.StringDup(2)}" &
$"Selected users:{vbNewLine}{uStr}", "Multiple users selected"}, $"Selected users:{vbNewLine}{uStr}", "Multiple users selected"},
MsgBoxStyle.Question + MsgBoxStyle.YesNo) = MsgBoxResult.Yes Then MsgBoxStyle.Question + MsgBoxStyle.YesNo) = MsgBoxResult.Yes Then
users.ForEach(Sub(u) users.ForEach(Sub(ByVal u As IUserData)
u.DownloadTopCount = limit u.DownloadTopCount = limit
u.DownloadDateFrom = _from u.DownloadDateFrom = _from
u.DownloadDateTo = _to u.DownloadDateTo = _to

View File

@@ -13,7 +13,7 @@ Imports System.Runtime.InteropServices
<Assembly: AssemblyDescription("Social networks media downloader")> <Assembly: AssemblyDescription("Social networks media downloader")>
<Assembly: AssemblyCompany("AndyProgram")> <Assembly: AssemblyCompany("AndyProgram")>
<Assembly: AssemblyProduct("SCrawler")> <Assembly: AssemblyProduct("SCrawler")>
<Assembly: AssemblyCopyright("Copyright © 2024")> <Assembly: AssemblyCopyright("Copyright © 2025")>
<Assembly: AssemblyTrademark("AndyProgram")> <Assembly: AssemblyTrademark("AndyProgram")>
<Assembly: ComVisible(False)> <Assembly: ComVisible(False)>
@@ -32,6 +32,6 @@ Imports System.Runtime.InteropServices
' by using the '*' as shown below: ' by using the '*' as shown below:
' <Assembly: AssemblyVersion("1.0.*")> ' <Assembly: AssemblyVersion("1.0.*")>
<Assembly: AssemblyVersion("2024.10.24.0")> <Assembly: AssemblyVersion("2025.2.25.0")>
<Assembly: AssemblyFileVersion("2024.10.24.0")> <Assembly: AssemblyFileVersion("2025.2.25.0")>
<Assembly: NeutralResourcesLanguage("en")> <Assembly: NeutralResourcesLanguage("en")>

View File

@@ -60,4 +60,7 @@ Namespace Plugin.Attributes
PropertyName = _PropertyName PropertyName = _PropertyName
End Sub End Sub
End Class End Class
<AttributeUsage(AttributeTargets.Class, AllowMultiple:=False)>
Public Class UseDownDetectorAttribute : Inherits Attribute
End Class
End Namespace End Namespace

View File

@@ -88,6 +88,7 @@ Namespace Plugin.Hosts
ByRef _Vids As XMLValue(Of Boolean)) As IEnumerable(Of PluginHost) ByRef _Vids As XMLValue(Of Boolean)) As IEnumerable(Of PluginHost)
Return {New PluginHost(GetType(API.Reddit.SiteSettings), _XML, GlobalPath, _Temp, _Imgs, _Vids), Return {New PluginHost(GetType(API.Reddit.SiteSettings), _XML, GlobalPath, _Temp, _Imgs, _Vids),
New PluginHost(GetType(API.Twitter.SiteSettings), _XML, GlobalPath, _Temp, _Imgs, _Vids), New PluginHost(GetType(API.Twitter.SiteSettings), _XML, GlobalPath, _Temp, _Imgs, _Vids),
New PluginHost(GetType(API.Bluesky.SiteSettings), _XML, GlobalPath, _Temp, _Imgs, _Vids),
New PluginHost(GetType(API.Mastodon.SiteSettings), _XML, GlobalPath, _Temp, _Imgs, _Vids), New PluginHost(GetType(API.Mastodon.SiteSettings), _XML, GlobalPath, _Temp, _Imgs, _Vids),
New PluginHost(GetType(API.Instagram.SiteSettings), _XML, GlobalPath, _Temp, _Imgs, _Vids), New PluginHost(GetType(API.Instagram.SiteSettings), _XML, GlobalPath, _Temp, _Imgs, _Vids),
New PluginHost(GetType(API.ThreadsNet.SiteSettings), _XML, GlobalPath, _Temp, _Imgs, _Vids), New PluginHost(GetType(API.ThreadsNet.SiteSettings), _XML, GlobalPath, _Temp, _Imgs, _Vids),

View File

@@ -184,6 +184,16 @@ Namespace Plugin.Hosts
End Sub End Sub
Friend ReadOnly Property IsSeparatedTasks As Boolean = False Friend ReadOnly Property IsSeparatedTasks As Boolean = False
Friend ReadOnly Property IsSavedPostsCompatible As Boolean = False Friend ReadOnly Property IsSavedPostsCompatible As Boolean = False
Friend ReadOnly Property IsDownDetectorCompatible As Boolean = False
Friend ReadOnly Property DownDetectorValue As Integer
Get
If IsDownDetectorCompatible Then
Return DirectCast(Source, DownDetector.IDownDetector).Value
Else
Return -1
End If
End Get
End Property
Private ReadOnly _TaskCountDefined As Integer? = Nothing Private ReadOnly _TaskCountDefined As Integer? = Nothing
Friend ReadOnly Property TaskCount As Integer Friend ReadOnly Property TaskCount As Integer
Get Get
@@ -296,6 +306,8 @@ Namespace Plugin.Hosts
End With End With
ElseIf TypeOf a Is ReplaceInternalPluginAttribute Then ElseIf TypeOf a Is ReplaceInternalPluginAttribute Then
Replacer = a Replacer = a
ElseIf TypeOf a Is UseDownDetectorAttribute Then
IsDownDetectorCompatible = True
End If End If
Next Next
End If End If
@@ -521,18 +533,34 @@ Namespace Plugin.Hosts
Friend Function GetUserPostUrl(ByVal User As IPluginContentProvider, ByVal Media As IUserMedia) As String Friend Function GetUserPostUrl(ByVal User As IPluginContentProvider, ByVal Media As IUserMedia) As String
Return Source.GetUserPostUrl(User, Media) Return Source.GetUserPostUrl(User, Media)
End Function End Function
Private _AvailableValue As Boolean = True Friend Property AvailableValue As Boolean = True
Private _AvailableAsked As Boolean = False Friend Property AvailableAsked As Boolean = False
Friend Property AvailableDownDetectorAsked As Boolean = False
Private _ActiveTaskCount As Integer = 0 Private _ActiveTaskCount As Integer = 0
Friend Property AvailableText As String = String.Empty Friend Property AvailableText As String = String.Empty
Friend Function AvailableDownDetector(ByVal What As Download, ByVal Silent As Boolean) As Boolean
If Not AvailableDownDetectorAsked Then
AvailableDownDetectorAsked = True
If IsDownDetectorCompatible Then
AvailableValue = DirectCast(Source, DownDetector.IDownDetector).Available(What, Silent)
If Not AvailableValue Then AvailableText = Source.AvailableText : AvailableAsked = True
Return AvailableValue
Else
Return True
End If
Else
Return AvailableValue
End If
End Function
Friend Function Available(ByVal What As Download, ByVal Silent As Boolean) As Boolean Friend Function Available(ByVal What As Download, ByVal Silent As Boolean) As Boolean
If DownloadSiteData Then If DownloadSiteData Then
If Not _AvailableAsked Then If Not AvailableDownDetectorAsked AndAlso Not AvailableDownDetector(What, Silent) Then Return AvailableValue
_AvailableValue = Source.Available(What, Silent) If Not AvailableAsked Then
AvailableValue = Source.Available(What, Silent)
AvailableText = Source.AvailableText AvailableText = Source.AvailableText
_AvailableAsked = True AvailableAsked = True
End If End If
Return _AvailableValue Return AvailableValue
Else Else
AvailableText = $"Downloading data for the site {Name} - {AccountName.IfNullOrEmpty(NameAccountNameDefault)} has been disabled by you." AvailableText = $"Downloading data for the site {Name} - {AccountName.IfNullOrEmpty(NameAccountNameDefault)} has been disabled by you."
If Not Silent Then MsgBoxE({AvailableText, $"{Name} downloading disabled"}, vbExclamation) If Not Silent Then MsgBoxE({AvailableText, $"{Name} downloading disabled"}, vbExclamation)
@@ -551,7 +579,7 @@ Namespace Plugin.Hosts
End Sub End Sub
Friend Sub DownloadDone(ByVal What As Download) Friend Sub DownloadDone(ByVal What As Download)
_ActiveTaskCount -= 1 _ActiveTaskCount -= 1
If _ActiveTaskCount = 0 Then _AvailableAsked = False : AvailableText = String.Empty If _ActiveTaskCount = 0 Then AvailableAsked = False : AvailableDownDetectorAsked = False : AvailableText = String.Empty
Source.DownloadDone(What) Source.DownloadDone(What)
End Sub End Sub
Private Function ConvertUser(ByVal User As IUserData) As Object Private Function ConvertUser(ByVal User As IUserData) As Object

View File

@@ -469,37 +469,59 @@ Namespace Plugin.Hosts
Return False Return False
End If End If
Else Else
Dim a As Boolean = False, n As Boolean = False Dim a As Boolean = False, n As Boolean = False, aDown As Boolean = True
Dim t$ = String.Empty Dim t$ = String.Empty
Dim tExists As Boolean = False Dim tExists As Boolean = False
Dim singleHost As Boolean = hnExists AndAlso HostNames.Count = 1 Dim singleHost As Boolean = hnExists AndAlso HostNames.Count = 1
Dim m As New MMessage("", "Some of the hosts are unavailable",, vbExclamation) Dim m As New MMessage("", "Some of the hosts are unavailable",, vbExclamation)
For i% = 0 To Count - 1 If [Default].IsDownDetectorCompatible Then
If Not hnExists OrElse HostNames.Contains(Hosts(i).AccountName) Then Dim sh As SettingsHost = Nothing
If Hosts(i).Available(What, True) Then Dim defdvalue% = [Default].DownDetectorValue
a = True If hnExists AndAlso Not Hosts.All(Function(h) h.DownDetectorValue = defdvalue) Then _
Else sh = Hosts.Find(Function(h) h.AccountName = HostNames(0))
n = True If sh Is Nothing Then sh = [Default]
If Not Hosts(i).AvailableText.IsEmptyString Then aDown = sh.AvailableDownDetector(What, Silent)
t &= vbCr Hosts.ForEach(Sub(ByVal h As SettingsHost)
t.StringAppendLine($"{Name} - {Hosts(i).AccountName.IfNullOrEmpty(SettingsHost.NameAccountNameDefault)}:") h.AvailableDownDetectorAsked = True
t.StringAppendLine(Hosts(i).AvailableText) If Not aDown And Not Silent Then h.AvailableValue = False : h.AvailableAsked = True
tExists = True End Sub)
End If
If aDown Then
For i% = 0 To Count - 1
If Not hnExists OrElse HostNames.Contains(Hosts(i).AccountName) Then
If Hosts(i).Available(What, True) Then
a = True
Else Else
t.StringAppendLine($"{Name} - {Hosts(i).AccountName.IfNullOrEmpty(SettingsHost.NameAccountNameDefault)}") n = True
If Not Hosts(i).AvailableText.IsEmptyString Then
t &= vbCr
t.StringAppendLine($"{Name} - {Hosts(i).AccountName.IfNullOrEmpty(SettingsHost.NameAccountNameDefault)}:")
t.StringAppendLine(Hosts(i).AvailableText)
tExists = True
Else
t.StringAppendLine($"{Name} - {Hosts(i).AccountName.IfNullOrEmpty(SettingsHost.NameAccountNameDefault)}")
End If
If FillIndexes Then HostsUnavailableIndexes.Add(i)
End If End If
If FillIndexes Then HostsUnavailableIndexes.Add(i)
End If End If
End If Next
Next Else
If Not Silent Then Silent = True
a = False
n = True
If Not [Default].AvailableText.IsEmptyString Then t = [Default].AvailableText : tExists = Not t.IsEmptyString
End If
t = t.StringTrim t = t.StringTrim
If singleHost Then If singleHost Then
m.Text = "The host is unavailable." m.Text = "The host is unavailable."
Else Else
m.Text = "Some of the hosts are unavailable." m.Text = "Some of the hosts are unavailable."
End If End If
If HostNamesPassed And Not hnExists Then Silent = True If HostNamesPassed And Not hnExists And aDown Then Silent = True
If a And Not n Then If Not aDown And Not Silent Then
Return False
ElseIf a And Not n Then
Return True Return True
ElseIf Not a And n Then ElseIf Not a And n Then
If Not Silent And tExists Then m.Text &= $"{vbCr}{vbCr}{t}" : m.Show() If Not Silent And tExists Then m.Text &= $"{vbCr}{vbCr}{t}" : m.Show()

View File

@@ -48,6 +48,7 @@ Namespace Plugin.Hosts
.Thrower = Me .Thrower = Me
.LogProvider = LogConnector .LogProvider = LogConnector
.Name = Name .Name = Name
.NameTrue = NameTrue(True)
.ID = ID .ID = ID
.Options = Options .Options = Options
.ParseUserMediaOnly = ParseUserMediaOnly .ParseUserMediaOnly = ParseUserMediaOnly
@@ -78,6 +79,7 @@ Namespace Plugin.Hosts
UserDescriptionUpdate(.UserDescription) UserDescriptionUpdate(.UserDescription)
UserExists = .UserExists UserExists = .UserExists
UserSuspended = .UserSuspended UserSuspended = .UserSuspended
NameTrue = .NameTrue
End With End With
End Sub End Sub
Friend Overrides Sub DownloadSingleObject(ByVal Data As IYouTubeMediaContainer, ByVal Token As CancellationToken) Friend Overrides Sub DownloadSingleObject(ByVal Data As IYouTubeMediaContainer, ByVal Token As CancellationToken)

View File

@@ -168,6 +168,7 @@
<Compile Include="API\Base\Declarations.vb" /> <Compile Include="API\Base\Declarations.vb" />
<Compile Include="API\Base\DeclaredNames.vb" /> <Compile Include="API\Base\DeclaredNames.vb" />
<Compile Include="API\Base\DownDetector.vb" /> <Compile Include="API\Base\DownDetector.vb" />
<Compile Include="API\Base\EditorExchangeOptionsBase.vb" />
<Compile Include="API\Base\GDL.vb" /> <Compile Include="API\Base\GDL.vb" />
<Compile Include="API\Base\IUserData.vb" /> <Compile Include="API\Base\IUserData.vb" />
<Compile Include="API\Base\M3U8Base.vb" /> <Compile Include="API\Base\M3U8Base.vb" />
@@ -189,6 +190,10 @@
<Compile Include="API\Base\Structures.vb" /> <Compile Include="API\Base\Structures.vb" />
<Compile Include="API\Base\TokenBatch.vb" /> <Compile Include="API\Base\TokenBatch.vb" />
<Compile Include="API\Base\YTDLP.vb" /> <Compile Include="API\Base\YTDLP.vb" />
<Compile Include="API\Bluesky\Declarations.vb" />
<Compile Include="API\Bluesky\M3U8.vb" />
<Compile Include="API\Bluesky\SiteSettings.vb" />
<Compile Include="API\Bluesky\UserData.vb" />
<Compile Include="API\Facebook\Declarations.vb" /> <Compile Include="API\Facebook\Declarations.vb" />
<Compile Include="API\Facebook\SiteSettings.vb" /> <Compile Include="API\Facebook\SiteSettings.vb" />
<Compile Include="API\Facebook\UserData.vb" /> <Compile Include="API\Facebook\UserData.vb" />
@@ -227,6 +232,7 @@
<Compile Include="API\PathPlugin\SiteSettings.vb" /> <Compile Include="API\PathPlugin\SiteSettings.vb" />
<Compile Include="API\PathPlugin\UserData.vb" /> <Compile Include="API\PathPlugin\UserData.vb" />
<Compile Include="API\Pinterest\Declarations.vb" /> <Compile Include="API\Pinterest\Declarations.vb" />
<Compile Include="API\Pinterest\EditorExchangeOptions.vb" />
<Compile Include="API\Pinterest\SiteSettings.vb" /> <Compile Include="API\Pinterest\SiteSettings.vb" />
<Compile Include="API\Pinterest\UserData.vb" /> <Compile Include="API\Pinterest\UserData.vb" />
<Compile Include="API\PornHub\Declarations.vb" /> <Compile Include="API\PornHub\Declarations.vb" />
@@ -660,6 +666,7 @@
<ItemGroup> <ItemGroup>
<None Include=".editorconfig" /> <None Include=".editorconfig" />
<None Include="API\OnlyFans\OFScraperConfigPattern.json" /> <None Include="API\OnlyFans\OFScraperConfigPattern.json" />
<None Include="Content\Icons\SiteIcons\TwitterIconNew_32.ico" />
<None Include="Content\Pictures\ApplicationPic_16.png" /> <None Include="Content\Pictures\ApplicationPic_16.png" />
<None Include="Content\Pictures\BookmarkBlack_16.png" /> <None Include="Content\Pictures\BookmarkBlack_16.png" />
<None Include="Content\Pictures\DBPic_32.png" /> <None Include="Content\Pictures\DBPic_32.png" />
@@ -797,6 +804,8 @@
<Content Include="API\OnlyFans\DynamicRules.txt" /> <Content Include="API\OnlyFans\DynamicRules.txt" />
<Content Include="API\OnlyFans\DynamicRulesAll.txt" /> <Content Include="API\OnlyFans\DynamicRulesAll.txt" />
<Content Include="API\OnlyFans\OFScraperConfigPatternConstants.txt" /> <Content Include="API\OnlyFans\OFScraperConfigPatternConstants.txt" />
<Content Include="Content\Icons\SiteIcons\BlueskyIcon_32.ico" />
<Content Include="Content\Pictures\SitePictures\BlueskyPic_32.png" />
<Content Include="Content\Pictures\CutPic_48.png" /> <Content Include="Content\Pictures\CutPic_48.png" />
<Content Include="Content\Pictures\FindPic_16.png" /> <Content Include="Content\Pictures\FindPic_16.png" />
</ItemGroup> </ItemGroup>

View File

@@ -10,6 +10,7 @@ Imports PersonalUtilities.Functions.Messaging
Imports PersonalUtilities.Functions.XML Imports PersonalUtilities.Functions.XML
Imports PersonalUtilities.Functions.XML.Base Imports PersonalUtilities.Functions.XML.Base
Imports PersonalUtilities.Functions.XML.Objects Imports PersonalUtilities.Functions.XML.Objects
Imports PersonalUtilities.Forms
Imports PersonalUtilities.Forms.Controls Imports PersonalUtilities.Forms.Controls
Imports PersonalUtilities.Forms.Controls.Base Imports PersonalUtilities.Forms.Controls.Base
Imports PersonalUtilities.Tools Imports PersonalUtilities.Tools
@@ -172,6 +173,7 @@ Friend Class SettingsCLS : Implements IDownloaderSettings, IDisposable
Friend ReadOnly Plugins As List(Of PluginHost) Friend ReadOnly Plugins As List(Of PluginHost)
Friend ReadOnly Property Users As List(Of IUserData) Friend ReadOnly Property Users As List(Of IUserData)
Friend ReadOnly Property UsersList As List(Of UserInfo) Friend ReadOnly Property UsersList As List(Of UserInfo)
Private ReadOnly Property UsersListProtected As Boolean = False
Friend Property Channels As Reddit.ChannelsCollection Friend Property Channels As Reddit.ChannelsCollection
Friend ReadOnly Property Labels As LabelsKeeper Friend ReadOnly Property Labels As LabelsKeeper
Friend ReadOnly Property Groups As Groups.DownloadGroupCollection Friend ReadOnly Property Groups As Groups.DownloadGroupCollection
@@ -192,7 +194,18 @@ Friend Class SettingsCLS : Implements IDownloaderSettings, IDisposable
Private ReadOnly BlackListFile As SFile = $"{SettingsFolderName}\BlackList.txt" Private ReadOnly BlackListFile As SFile = $"{SettingsFolderName}\BlackList.txt"
Private ReadOnly UsersSettingsFile As SFile = $"{SettingsFolderName}\Users.xml" Private ReadOnly UsersSettingsFile As SFile = $"{SettingsFolderName}\Users.xml"
Private ReadOnly Property SettingsVersion As XMLValue(Of Integer) Private ReadOnly Property SettingsVersion As XMLValue(Of Integer)
Private Const SettingsVersionCurrent As Integer = 1 Private Const SettingsVersionCurrent As Integer = 2
Friend ShortcutOpenFeed As New ButtonKey(Keys.F, True)
Friend ShortcutOpenSearch As New ButtonKey(Keys.F,, True)
Private Sub ChangeFeedOpenMode()
If FeedOpenCtrlF Then
ShortcutOpenFeed = New ButtonKey(Keys.F, True)
ShortcutOpenSearch = New ButtonKey(Keys.F,, True)
Else
ShortcutOpenFeed = New ButtonKey(Keys.F,, True)
ShortcutOpenSearch = New ButtonKey(Keys.F, True)
End If
End Sub
#End Region #End Region
#Region "Initializer" #Region "Initializer"
Friend Sub New() Friend Sub New()
@@ -216,10 +229,10 @@ Friend Class SettingsCLS : Implements IDownloaderSettings, IDisposable
EnvironmentProgramsList = New List(Of String) EnvironmentProgramsList = New List(Of String)
SettingsVersion = New XMLValue(Of Integer)("SettingsVersion", 0, MyXML) SettingsVersion = New XMLValue(Of Integer)("SettingsVersion", 0, MyXML)
UsersListProtected = MyXML.Value("UsersListProtected").FromXML(Of Boolean)(False)
Dim n() As String = {"Scheduler"} Dim n() As String = {"Scheduler"}
AutomationFile = New XMLValue(Of String)("File",, MyXML, n) AutomationFile = New XMLValue(Of String)("File",, MyXML, n)
If SettingsVersion.Value = 0 AndAlso MyXML.Contains(AutomationFile.Name) Then AutomationFile.Value = MyXML.Value(AutomationFile.Name)
AutomationScript = New XMLValueUse(Of String)("Script", String.Empty,, MyXML, n) AutomationScript = New XMLValueUse(Of String)("Script", String.Empty,, MyXML, n)
AutomationScript_ExcludeManual = New XMLValue(Of Boolean)("ScriptExcludeManual", True, MyXML, n) AutomationScript_ExcludeManual = New XMLValue(Of Boolean)("ScriptExcludeManual", True, MyXML, n)
@@ -252,7 +265,6 @@ Friend Class SettingsCLS : Implements IDownloaderSettings, IDisposable
MaxSmallImageHeight = New XMLValue(Of Integer)("MaxSmallImageHeight", 15, MyXML, n) MaxSmallImageHeight = New XMLValue(Of Integer)("MaxSmallImageHeight", 15, MyXML, n)
CollectionsPath = New XMLValue(Of String)("CollectionsPath", CollectionsFolderName, MyXML, n) CollectionsPath = New XMLValue(Of String)("CollectionsPath", CollectionsFolderName, MyXML, n)
MaxUsersJobsCount = New XMLValue(Of Integer)("MaxJobsCount", DefaultMaxDownloadingTasks, MyXML, n) MaxUsersJobsCount = New XMLValue(Of Integer)("MaxJobsCount", DefaultMaxDownloadingTasks, MyXML, n)
UserAgent = New XMLValue(Of String)("UserAgent",, MyXML, n)
ImgurClientID = New XMLValue(Of String)("ImgurClientID", String.Empty, MyXML, {Name_Node_Sites}) ImgurClientID = New XMLValue(Of String)("ImgurClientID", String.Empty, MyXML, {Name_Node_Sites})
'Basis: new version 'Basis: new version
@@ -299,6 +311,9 @@ Friend Class SettingsCLS : Implements IDownloaderSettings, IDisposable
DownloadAll_UseF6 = New XMLValue(Of Boolean)("DownloadAll_UseF6", True, MyXML, n) DownloadAll_UseF6 = New XMLValue(Of Boolean)("DownloadAll_UseF6", True, MyXML, n)
DownloadAll_UseF6_Confirm = New XMLValue(Of Boolean)("DownloadAll_UseF6_Confirm", False, MyXML, n) DownloadAll_UseF6_Confirm = New XMLValue(Of Boolean)("DownloadAll_UseF6_Confirm", False, MyXML, n)
DownloadAll_Confirm = New XMLValue(Of Boolean)("DownloadAll_Confirm", False, MyXML, n) DownloadAll_Confirm = New XMLValue(Of Boolean)("DownloadAll_Confirm", False, MyXML, n)
FeedOpenCtrlF = New XMLValue(Of Boolean)("FeedOpenCtrlF", True, MyXML, n)
AddHandler FeedOpenCtrlF.ValueChanged, AddressOf ChangeFeedOpenMode
ChangeFeedOpenMode()
'Notifications 'Notifications
n = {"Notifications"} n = {"Notifications"}
@@ -318,7 +333,7 @@ Friend Class SettingsCLS : Implements IDownloaderSettings, IDisposable
DefaultDownloadImages = New XMLValue(Of Boolean)("DownloadImages", True, MyXML, n) DefaultDownloadImages = New XMLValue(Of Boolean)("DownloadImages", True, MyXML, n)
DefaultDownloadVideos = New XMLValue(Of Boolean)("DownloadVideos", True, MyXML, n) DefaultDownloadVideos = New XMLValue(Of Boolean)("DownloadVideos", True, MyXML, n)
DownloadNativeImageFormat = New XMLValue(Of Boolean)("DownloadNativeImageFormat", True, MyXML, n) DownloadNativeImageFormat = New XMLValue(Of Boolean)("DownloadNativeImageFormat", True, MyXML, n)
UserSiteNameAsFriendly = New XMLValue(Of Boolean)("UserSiteNameAsFriendly", False, MyXML, n) UserSiteNameAsFriendly = New XMLValue(Of Boolean)("UserSiteNameAsFriendly", True, MyXML, n)
'STDownloader 'STDownloader
n = {"Downloader"} n = {"Downloader"}
@@ -351,6 +366,9 @@ Friend Class SettingsCLS : Implements IDownloaderSettings, IDisposable
ReparseMissingInTheRoutine = New XMLValue(Of Boolean)("ReparseMissingInTheRoutine", False, MyXML, n) ReparseMissingInTheRoutine = New XMLValue(Of Boolean)("ReparseMissingInTheRoutine", False, MyXML, n)
UseDefaultAccountIfMissing = New XMLValue(Of Boolean)("UseDefaultAccountIfMissing", True, MyXML, n) UseDefaultAccountIfMissing = New XMLValue(Of Boolean)("UseDefaultAccountIfMissing", True, MyXML, n)
AutomationBrushUndownloadedPlansMinutes = New XMLValue(Of Integer)("AutomationBrushUndownloadedPlansMinutes", 10080, MyXML, n) AutomationBrushUndownloadedPlansMinutes = New XMLValue(Of Integer)("AutomationBrushUndownloadedPlansMinutes", 10080, MyXML, n)
DownDetectorEnabled = New XMLValue(Of Boolean)("DownDetectorEnabled", True, MyXML, n)
'TODELETE: DownDetectorEnabled change
If SettingsVersion.Value < SettingsVersionCurrent Then DownDetectorEnabled.Value = False 'SettingsVersionCurrent = 2
'Downloading: file naming 'Downloading: file naming
n = {"Downloading", "FileName"} n = {"Downloading", "FileName"}
@@ -500,6 +518,8 @@ Friend Class SettingsCLS : Implements IDownloaderSettings, IDisposable
Dim NeedUpdate As Boolean = False Dim NeedUpdate As Boolean = False
Dim i%, indx% Dim i%, indx%
Dim errStr As Func(Of Date, String) = Function(d) IIf(UsersListProtected, String.Empty,
$"It will be removed from SCrawler on {d.ToStringDate(DateTimeDefaultProvider)}.")
Dim UsersListInitialCount% = UsersList.Count Dim UsersListInitialCount% = UsersList.Count
Dim iUser As UserInfo Dim iUser As UserInfo
Dim userFileExists As Boolean, pluginFound As Boolean Dim userFileExists As Boolean, pluginFound As Boolean
@@ -515,7 +535,7 @@ Friend Class SettingsCLS : Implements IDownloaderSettings, IDisposable
If .Plugin.IsEmptyString Then If .Plugin.IsEmptyString Then
pluginFound = False pluginFound = False
If .Site.IsEmptyString Then If .Site.IsEmptyString Then
MyMainLOG = $"The corresponding plugin was not found for the user [{ .Name}]. The user was removed from SCrawler." MyMainLOG = $"The corresponding plugin was not found for the user [{ .Name}].{IIf(UsersListProtected, String.Empty, " The user has been removed from SCrawler.")}"
Else Else
indx = __plugins.FindIndex(Function(p) p.Value.ToLower = .Site.ToLower) indx = __plugins.FindIndex(Function(p) p.Value.ToLower = .Site.ToLower)
If indx >= 0 Then If indx >= 0 Then
@@ -545,19 +565,17 @@ Friend Class SettingsCLS : Implements IDownloaderSettings, IDisposable
If .LastSeen.HasValue Then .LastSeen = Nothing : NeedUpdate = True If .LastSeen.HasValue Then .LastSeen = Nothing : NeedUpdate = True
Else Else
.LastSeen = Now .LastSeen = Now
MyMainLOG = $"The user [{ .Site}: { .Name}] was not found. " & MyMainLOG = $"The user [{ .Site}: { .Name}] was not found. " & errStr(.LastSeen.Value)
$"It will be removed from SCrawler on { .LastSeen.Value.ToStringDate(DateTimeDefaultProvider)}."
NeedUpdate = True NeedUpdate = True
End If End If
ElseIf userFileExists Then ElseIf userFileExists Then
If .Protected Then If .Protected Then
If Not .LastSeen.HasValue Then .LastSeen = Now : NeedUpdate = True If Not .LastSeen.HasValue Then .LastSeen = Now : NeedUpdate = True
MyMainLOG = $"The corresponding plugin was not found for the user [{ .Site}: { .Name}]. " & MyMainLOG = $"The corresponding plugin was not found for the user [{ .Site}: { .Name}]. " & errStr(.LastSeen.Value)
$"It will be removed from SCrawler on { .LastSeen.Value.ToStringDate(DateTimeDefaultProvider)}."
Else Else
If .LastSeen.HasValue Then .LastSeen = Nothing : NeedUpdate = True If .LastSeen.HasValue Then .LastSeen = Nothing : NeedUpdate = True
End If End If
ElseIf If(.LastSeen, Now).AddDays(30) < Now Then ElseIf If(.LastSeen, Now).AddDays(30) < Now And Not UsersListProtected Then
UsersList.RemoveAt(i) UsersList.RemoveAt(i)
MyMainLOG = $"The user [{ .Site}: { .Name}] was not found and was removed from SCrawler." MyMainLOG = $"The user [{ .Site}: { .Name}] was not found and was removed from SCrawler."
NeedUpdate = True NeedUpdate = True
@@ -617,8 +635,7 @@ Friend Class SettingsCLS : Implements IDownloaderSettings, IDisposable
uu.LastSeen = Now uu.LastSeen = Now
NeedUpdate = True NeedUpdate = True
UsersList(uIndex) = uu UsersList(uIndex) = uu
MyMainLOG = $"The user [{uu.Site}: {uu.Name}] was not found. " & MyMainLOG = $"The user [{uu.Site}: {uu.Name}] was not found. " & errStr(uu.LastSeen.Value)
$"It will be removed from SCrawler on {uu.LastSeen.Value.ToStringDate(DateTimeDefaultProvider)}."
End If End If
End If End If
Return uIndex >= 0 Return uIndex >= 0
@@ -633,7 +650,7 @@ Friend Class SettingsCLS : Implements IDownloaderSettings, IDisposable
__del = False __del = False
If Not .Item(i).FileExists Then If Not .Item(i).FileExists Then
With DirectCast(.Item(i), UserDataBase).User With DirectCast(.Item(i), UserDataBase).User
If Not findWrongUser(.Self) Then If Not findWrongUser(.Self) And Not UsersListProtected Then
__del = True __del = True
MyMainLOG = $"The user [{ .Site}: { .Name}] was not found and was removed from SCrawler." MyMainLOG = $"The user [{ .Site}: { .Name}] was not found and was removed from SCrawler."
End If End If
@@ -859,7 +876,11 @@ Friend Class SettingsCLS : Implements IDownloaderSettings, IDisposable
End Get End Get
End Property End Property
Friend ReadOnly Property MaxUsersJobsCount As XMLValue(Of Integer) Friend ReadOnly Property MaxUsersJobsCount As XMLValue(Of Integer)
Friend ReadOnly Property UserAgent As XMLValue(Of String) Friend ReadOnly Property UserAgent As String
Get
Return HEADER_UserAgent
End Get
End Property
Friend ReadOnly Property ImgurClientID As XMLValue(Of String) Friend ReadOnly Property ImgurClientID As XMLValue(Of String)
#End Region #End Region
#Region "Basis: new version" #Region "Basis: new version"
@@ -925,6 +946,7 @@ Friend Class SettingsCLS : Implements IDownloaderSettings, IDisposable
Friend ReadOnly Property DownloadAll_UseF6 As XMLValue(Of Boolean) Friend ReadOnly Property DownloadAll_UseF6 As XMLValue(Of Boolean)
Friend ReadOnly Property DownloadAll_UseF6_Confirm As XMLValue(Of Boolean) Friend ReadOnly Property DownloadAll_UseF6_Confirm As XMLValue(Of Boolean)
Friend ReadOnly Property DownloadAll_Confirm As XMLValue(Of Boolean) Friend ReadOnly Property DownloadAll_Confirm As XMLValue(Of Boolean)
Friend ReadOnly Property FeedOpenCtrlF As XMLValue(Of Boolean)
#End Region #End Region
#Region "Notifications" #Region "Notifications"
Friend Enum NotificationObjects Friend Enum NotificationObjects
@@ -1048,6 +1070,7 @@ Friend Class SettingsCLS : Implements IDownloaderSettings, IDisposable
Friend ReadOnly Property ReparseMissingInTheRoutine As XMLValue(Of Boolean) Friend ReadOnly Property ReparseMissingInTheRoutine As XMLValue(Of Boolean)
Friend ReadOnly Property UseDefaultAccountIfMissing As XMLValue(Of Boolean) Friend ReadOnly Property UseDefaultAccountIfMissing As XMLValue(Of Boolean)
Friend ReadOnly Property AutomationBrushUndownloadedPlansMinutes As XMLValue(Of Integer) Friend ReadOnly Property AutomationBrushUndownloadedPlansMinutes As XMLValue(Of Integer)
Friend ReadOnly Property DownDetectorEnabled As XMLValue(Of Boolean)
#End Region #End Region
#Region "Downloading: file naming" #Region "Downloading: file naming"
Friend ReadOnly Property FileAddDateToFileName As XMLValue(Of Boolean) Friend ReadOnly Property FileAddDateToFileName As XMLValue(Of Boolean)

View File

@@ -64,6 +64,26 @@ Namespace My.Resources
End Set End Set
End Property End Property
'''<summary>
''' Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
'''</summary>
Friend Shared ReadOnly Property BlueskyIcon_32() As System.Drawing.Icon
Get
Dim obj As Object = ResourceManager.GetObject("BlueskyIcon_32", resourceCulture)
Return CType(obj,System.Drawing.Icon)
End Get
End Property
'''<summary>
''' Looks up a localized resource of type System.Drawing.Bitmap.
'''</summary>
Friend Shared ReadOnly Property BlueskyPic_32() As System.Drawing.Bitmap
Get
Dim obj As Object = ResourceManager.GetObject("BlueskyPic_32", resourceCulture)
Return CType(obj,System.Drawing.Bitmap)
End Get
End Property
'''<summary> '''<summary>
''' Looks up a localized resource of type System.Drawing.Icon similar to (Icon). ''' Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
'''</summary> '''</summary>
@@ -324,6 +344,16 @@ Namespace My.Resources
End Get End Get
End Property End Property
'''<summary>
''' Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
'''</summary>
Friend Shared ReadOnly Property TwitterIconNew_32() As System.Drawing.Icon
Get
Dim obj As Object = ResourceManager.GetObject("TwitterIconNew_32", resourceCulture)
Return CType(obj,System.Drawing.Icon)
End Get
End Property
'''<summary> '''<summary>
''' Looks up a localized resource of type System.Drawing.Icon similar to (Icon). ''' Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
'''</summary> '''</summary>

View File

@@ -118,6 +118,12 @@
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="BlueskyIcon_32" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Content\Icons\SiteIcons\BlueskyIcon_32.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="BlueskyPic_32" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Content\Pictures\SitePictures\BlueskyPic_32.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="FacebookIcon_32" type="System.Resources.ResXFileRef, System.Windows.Forms"> <data name="FacebookIcon_32" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Content\Icons\SiteIcons\FacebookIcon_32.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> <value>Content\Icons\SiteIcons\FacebookIcon_32.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data> </data>
@@ -193,6 +199,9 @@
<data name="TikTokPic_192" type="System.Resources.ResXFileRef, System.Windows.Forms"> <data name="TikTokPic_192" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Content\Pictures\SitePictures\TikTokPic_192.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> <value>Content\Pictures\SitePictures\TikTokPic_192.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data> </data>
<data name="TwitterIconNew_32" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Content\Icons\SiteIcons\TwitterIconNew_32.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="TwitterIcon_32" type="System.Resources.ResXFileRef, System.Windows.Forms"> <data name="TwitterIcon_32" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Content\Icons\SiteIcons\TwitterIcon_32.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> <value>Content\Icons\SiteIcons\TwitterIcon_32.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data> </data>