Compare commits

...

11 Commits

Author SHA1 Message Date
Andy
d0d8e5470e 2026.2.14.0
TokenBatch: add 'MyWorkingDirectory' property
API.Instagram: update 'ID' extraction; reduce the number of downloaded stories (GQL) to 5
API.Twitter: get a new username based on the user ID
API.XHamster: videos aren't downloading
Feed: fix a bug when removing from favorites
2026-02-14 14:24:03 +03:00
Andy
164b999de7 2026.1.24.0
Instagram: update settings; fix a bug in line 1437
OnlyFans: update the URLs that open posts
2026-01-24 18:17:28 +03:00
Andy
e6d5fc2b95 2026.1.17.0
UserDataBase: move GLD functions from 'Twitter'
Instagram: add 'Reposts' and 'Likes' to the 'Sections' enum
OnlyFans: update the regex in 'DynamicRulesEnv'; handling error 502
PornHub: fix videos aren't downloading
ThreadsNet: add user name and description extraction
TikTok: fix downloading new videos; add downloading 'Stories' and 'Reposts'
Twitter: move GLD functions to 'UserDataBase'
Xhamster: fix a bug when adding new users; fix incorrect cache location
Download groups: add excluded groups
MainFrame: fix the 'Feed' tooltip
2026-01-17 20:06:37 +03:00
Andy
6d4380ccac 2025.11.25.0
YT
YouTubeSettings: add property 'DefaultSubtitlesEmbed'
YouTubeMediaContainerBase: improve trim options; update command for 'DefaultSubtitlesEmbed'

SCrawler
TokenBatch, GDLBatch, YTDLPBatch: optimize code; add default encoding
UserMedia: add property 'IsPhotoType'
API.TikTok: optimize code; download descriptions
API.Twitter: optimize code; fix downloading of site name, description and avatar
API.Xhamster: add the properties 'UseYTDLPJSON', 'UseYTDLPDownload' and 'UseYTDLPForceDisableInternal'; add yt-dlp support; update parsing functions
Groups, automation: delete 'Modes'; add groups downloading among other options; optimize code
DownloadSavedPostsForm, DownloadProgress: add hotkey 'Esc'
MainFrame: update 'BTT_VIEW_FILTER_LOAD_Click' function
2025-11-25 12:57:37 +03:00
Andy
8dfd4e8bd1 2025.10.4.0
YT
YouTubeSettings: add property 'ParseLongUserTitle'
YouTubeMediaContainerBase: concatenate artists

SCrawler
Bluesky: add saved posts downloading
xHamster: temporarily disable the plugin
2025-10-04 15:43:40 +03:00
Andy
1404afdfa3 2025.9.1.0
API.PornHub: update regex and data parsing
2025-09-01 16:41:44 +03:00
Andy
5857fcfae3 2025.8.30.0
YT
Add video trim
Fix downloading error
Add artist name when downloading audio
Embed chapters

SCrawler
Add correct handling of 'webp' files
API.Redgifs: hide 'Responser.Save' error
2025-08-30 14:36:36 +03:00
Andy
e09752a2d5 2025.8.1.0
YT
Update 'ReplaceModificationDate'

SCrawler
API.Instagram: fix 'LastCursor' issue
API.Reddit: add OAuth validation; add default credentials; hide unused controls; add 'SeparatedTasks'; bypass 429 error; fix crossposts downloading
API.Redgifs: force delete cookies if user added them
API.TikTok: yt-dlp modification (date change)
API.Twitter: simplify large profiles download
SettingsCLS: change default max value for channel downloads
2025-08-01 21:49:40 +03:00
Andy
05772a9fc4 2025.7.18.0
API.Instagram: fix special folder issue
API.OnlyFans: bypass unpurchased videos; add support for GIF files
API.Reddit: add OAuth credentials validation; add extended 429 error handling
API.Xhamster: remove 'UserOptions' function ('SiteSettings'); add support for downloading 'moments'
API.XVIDEOS: remove 'UserOptions' function ('SiteSettings'); remove 'UserExchangeOptions' class
Add 'EditorExchangeOptionsBase_P' and update base classes for user options
2025-07-18 20:29:35 +03:00
Andy
24ad338c60 2025.6.12.0
YT
MainModShared: fix environment output
YouTubeMediaContainerBase: fix 'm3u8' audio formats

SCrawler
UserDataBase: text downloading with saved posts; update 'ID' property (handle '_ForceSaveUserInfo')
API.Bluesky: data is not downloaded
API.Reddit: update 'RedditViewExchange'; set base inheritance; inherit default settings for new users
API.ALL: update functions with property 'ID'
2025-06-12 20:29:59 +03:00
Andy
ff0c4587eb 2025.6.1.0
PluginProvider
IUserMedia, PluginUserMedia: add properties 'PostText', 'PostTextFile', 'PostTextFileSpecialFolder'

YT
YouTubeFunctions: update 'Info_GetUrlType' and 'StandardizeURL' functions: add youtu.be domain
YouTubeSettings: add 'FILTER' property
Add classes 'FilterForm', 'YTDataFilter'
VideoListForm: add filters; update 'LoadData' and 'RemoveControls' functions; add hotkey 'Ctrl+F5' for refresh
YouTubeMediaContainerBase: add support for new interface properties
Minor bugs

SCrawler
DeclaredNames: add new names
EditorExchangeOptionsBase, IUserData, SiteSettingsBase, UserMedia, UserDataBase: add support for text downloading

Sites Bluesky, Instagram, OnlyFans, Reddit, ThreadsNet, Twitter: add support for text downloading
Sites Facebook, JustForFans, LPSG, Mastodon, Pinterest, PornHub, Redgifs, ThisVid, TikTok, Xhamster, XVIDEOS, YouTube (STD): disable text downloading

UserDataBase: add 'ToStringExt' functions

API.Instagram: add 'SleepTimerRequestsNextProfile' property
API.OnlyFans: update 'DynamicRules'; fix incorrect posts opening (update 'GetUserPostUrl' function); fix limited download ('DownloadTopCount')
API.Reddit: fix post date provider; add 'Best' and 'Rising' view modes; fix request (data is not downloading); set 'BearerTokenUseCurl' to 'False' by default
API.ThreadsNet: change domain from 'net' to 'com'; fix data downloading
API.TikTok: add downloading of avatar, site name and description
API.Twitter: fix JSON error; add debug options; fix downloading
API.Xhamster: add folder 'Photo' for albums

Feed: add filters; update move/copy algo; add the ability to show test posts; update table rendering; add new 'MediaItem' handlers
FeedMedia: add text options; update 'DeleteFile' function
FeedMoveCopyTo: add text option

VideoDownloaderForm: disable filter button

GlobalSettingsForm: add 'FeedShowTextPosts' and 'FeedShowTextPostsAlwaysMove' options
SettingsCLS: add feed text properties
UserImage: add 'CreateImageFromText' function
UserInfo: update 'Equals' function

Add classes: 'FeedFilter', 'FeedFilterCollection', 'FeedFilterForm'

Minor bugs and improvements
2025-06-01 19:01:26 +03:00
155 changed files with 8222 additions and 2460 deletions

View File

@@ -1,4 +1,218 @@
# 2025.3.17.0
# Program versions
- [ffmpeg](https://github.com/AAndyProgram/SCrawler/wiki/Settings#ffmpeg)
- x64 version - [release](https://github.com/GyanD/codexffmpeg/releases/tag/5.1.2); [zip](https://github.com/GyanD/codexffmpeg/releases/download/5.1.2/ffmpeg-5.1.2-full_build.zip); **version `5.1.2-full_build-www.gyan.dev`**
- x86 version - [release](https://github.com/yt-dlp/FFmpeg-Builds/releases/tag/autobuild-2022-11-30-12-57); [zip](https://github.com/yt-dlp/FFmpeg-Builds/releases/download/autobuild-2022-11-30-12-57/ffmpeg-N-109274-gd7a5f068c2-win32-gpl.zip); **version `N-109457-geeb280f351-20221226`**
- [Gallery-dl](https://github.com/AAndyProgram/SCrawler/wiki/Settings#gallery-dl) - **1.31.6**
- [YT-DLP](https://github.com/AAndyProgram/SCrawler/wiki/Settings#yt-dlp) - **2026.02.04.233607**
- [Deno](https://github.com/AAndyProgram/SCrawler/wiki/Settings#deno) - latest *(`2.0.0` or higher)*
- [OF-Scraper](https://github.com/AAndyProgram/SCrawler/wiki/Settings#of-scraper) - **3.12.9** ([release](https://github.com/datawhores/OF-Scraper/releases/tag/3.12.9))
# 2026
## 2026.2.14.0
*2026-02-14*
- Added
- Sites:
- Twitter: get a new username based on the user ID
- Minor improvements
- Updated
- gallery-dl up to version **1.31.6**
- yt-dlp up to version **2026.02.04.233607**
- Fixed
- Sites:
- **Instagram: some profiles aren't downloading**
- xHamster: videos aren't downloading
- Minor bugs
## 2026.1.24.0
*2026-01-24*
- Updated
- gallery-dl up to version **1.31.4**
- Fixed
- Minor bugs
## 2026.1.17.0
*2026-01-17*
- Added
- Sites:
- OnlyFans: handling error `502`
- Threads: user name and description extraction
- TikTok: **downloading `Stories` and `Reposts`**
- Download groups: excluded groups
- Updated
- yt-dlp up to version **2025.12.08**
- gallery-dl up to version **1.31.3**
- Fixed
- Sites:
- PornHub: videos aren't downloading
- TikTok: new videos aren't downloading
- xHamster: new users aren't added in some cases
# 2025
## 2025.11.25.0
*2025-11-25*
**ATTENTION!**
**An external JavaScript runtime is now required for full YouTube support**
yt-dlp now requires users to have an external JavaScript runtime ([Deno](https://github.com/AAndyProgram/SCrawler/wiki/Settings#deno)) installed in order to solve the JavaScript challenges presented by YouTube.
**xHamster is back.** 🥳🎉 xHamster's downloading algorithms now partially utilize yt-dlp, so yt-dlp is now required for this site.
- Added
- Sites:
- TikTok: **download descriptions**
- Download groups: groups downloading among other options (more [here](https://github.com/AAndyProgram/SCrawler/wiki/Settings#download-groups))
- Saved posts: hotkey `Esc` to close the form
- Minor improvements
- Updated
- yt-dlp up to version **2025.11.12**
- gallery-dl up to version **1.30.10**
- AutoDownloader: the modes are now removed. Now you can only enable/disable the auto download plan. All options can now be combined.
- Fixed
- Sites:
- Twitter: the site name, description and avatar are not downloading
- xHamster: **data is not downloading**
- Minor bugs
## 2025.10.4.0
*2025-10-04*
**xHamster downloads are temporarily disabled**
- Added
- Sites:
- Bluesky: **saved posts downloading**
- xHamster: **temporarily disable the plugin**
- Minor improvements
- Updated
- yt-dlp up to version **2025.09.26**
- gallery-dl up to version **1.30.9**
## 2025.9.1.0
*2025-09-01*
- Fixed
- PornHub: data is not downloading
## 2025.8.30.0
*2025-08-30*
- Added
- YouTube:
- **video trim** *(button `Trim`)*
- embed chapters into video file
- add artist name when downloading audio
- Correct handling of `webp` files
- Minor improvements
- Updated
- yt-dlp up to version **2025.08.27**
- gallery-dl up to version **1.30.5**
- Fixed
- **YouTube: downloading error**
- Minor bugs
## 2025.8.1.0
*2025-08-01*
- Added
- Sites:
- Reddit: **bypass error `429`**
- Twitter: **[large profile option](https://github.com/AAndyProgram/SCrawler/wiki/Settings#twitter-user-settings) in user settings**
- Minor improvements
- Updated
- yt-dlp up to version **2025.07.21**
- gallery-dl up to version **1.30.2**
- Fixed
- Reddit: in some cases crossposts don't download
- Minor bugs
## 2025.7.18.0
*2025-07-18*
- Added
- Sites:
- OnlyFans: support for GIF files
- Reddit: extended `429` error handling
- Xhamster: support for downloading 'moments'
- Minor improvements
- Updated
- yt-dlp up to version **2025.06.30**
- gallery-dl up to version **1.30.0**
- Fixed
- OnlyFans: **hanging on purchased content**
- Minor bugs
## 2025.6.12.0
*2025-06-12*
- Updated
- yt-dlp up to version **2025.06.09**
- Fixed
- Sites:
- YouTube: audio formats of protocol `m3u8` are not handled correctly
- BlueSky: data is not downloaded in some cases
- Reddit: new users do not inherit default text settings
- Saved posts: text downloading with saved posts
- Environment incorrect output
## 2025.6.1.0
*2025-06-01*
- Added
- Sites:
- YouTube (standalone app):
- support for **youtu.be** domain
- **filters**
- hotkey `Ctrl+F5` for refresh
- **Bluesky, Instagram, OnlyFans, Reddit, Threads, Twitter: the ability to download text posts**
- OnlyFans:
- updated `DynamicRules`
- `backend` option *(`aio` & `httpx`)*
- Reddit: add `Best` and `Rising` view modes
- TikTok: downloading of avatar, site name and description
- Feed:
- **filters**
- add the ability to show test posts
- Minor improvements
- Updated
- yt-dlp up to version **2025.05.22**
- gallery-dl up to version **1.29.7**
- PluginProvider
- IUserMedia: properties `PostText`, `PostTextFile`, `PostTextFileSpecialFolder`
- Fixed
- Sites:
- OnlyFans:
- DRM videos are not downloading in some cases *(only if you can't download video try changing `backend` option to `httpx`)*
- incorrect open posts
- while limited downloading, the first time, the profile still loads completely
- Reddit:
- **data is not downloading**
- post date is incorrect
- Threads
- change domain from `net` to `com`
- data is not downloading
- Twitter
- fix JSON error
- data is not downloaded in some cases
- Minor bugs
## 2025.3.17.0
*2025-03-17*
@@ -12,7 +226,7 @@
- PornHub: newly added users aren't downloading
- Threads: users aren't updated if there is a pinned post
# 2025.2.25.0
## 2025.2.25.0
*2025-02-25*
@@ -44,7 +258,7 @@
- Threads: **data is not downloading**
- Minor bugs
# 2025.1.12.0
## 2025.1.12.0
*2025-01-12*
@@ -69,7 +283,8 @@
- YouTube: **communities are not downloading** *(see settings in wiki)*
- Minor bugs
# 2024.11.21.0
# 2024
## 2024.11.21.0
*2024-11-21*
@@ -92,7 +307,7 @@
- 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*
@@ -117,7 +332,7 @@
- Can't change data path (issue #206)
- Minor bugs
# 2024.9.2.0
## 2024.9.2.0
*2024-09-02*
@@ -133,7 +348,7 @@
- YouTube (SCrawler): incorrect parsing of video page
- Minor bugs
# 2024.8.10.0
## 2024.8.10.0
*2024-08-10*
@@ -145,7 +360,7 @@
- Fixed
- YouTube (standalone app): **video is being parsed using cookies but is not downloading** *(Issue #205)*
# 2024.8.1.0
## 2024.8.1.0
*2024-08-01*
@@ -154,7 +369,7 @@
- Updated
- yt-dlp up to version **2024.08.01**
# 2024.7.24.0
## 2024.7.24.0
*2024-07-24*
@@ -177,7 +392,7 @@
- OnlyFans: rules parsing bug
- Minor bugs
# 2024.6.25.0
## 2024.6.25.0
*2024-06-25*
@@ -191,7 +406,7 @@
- Fixed
- Minor bugs
# 2024.6.10.0
## 2024.6.10.0
*2024-06-10*
@@ -204,7 +419,7 @@
- Fixed
- Minor bugs
# 2024.6.6.0
## 2024.6.6.0
*2024-06-06*
@@ -220,7 +435,7 @@
- OnlyFans: **data is not downloading**
- Minor bugs
# 2024.6.4.0
## 2024.6.4.0
*2024-06-04*
@@ -239,7 +454,7 @@
- Twitter: deleting user directory when redownloading missing posts
- Minor bugs
# 2024.5.19.0
## 2024.5.19.0
*2024-05-19*
@@ -248,7 +463,7 @@
- Fixed
- YouTube (SCrawler): advanced settings are not saved when changed
# 2024.5.18.0
## 2024.5.18.0
*2024-05-18*
@@ -275,7 +490,7 @@
- Twitter: **data is not downloading due to domain change from twitter.com to x.com**
- Minor bugs
# 2024.5.4.0
## 2024.5.4.0
*2024-05-04*
@@ -290,7 +505,7 @@
- Reddit: token update error
- Threads: unable to obtain credentials (`ID`)
# 2024.4.26.0
## 2024.4.26.0
*2024-04-26*
@@ -303,14 +518,14 @@
- Fixed
- xHamster: **saved posts aren't downloading**
# 2024.4.14.0
## 2024.4.14.0
*2024-04-14*
- Fixed
- Facebook: can't get tokens
# 2024.4.13.0
## 2024.4.13.0
*2024-04-13*
@@ -324,7 +539,7 @@
- YouTube: remove last download date when erasing history data
- Instagram: **saved posts aren't downloading**
# 2024.4.10.0
## 2024.4.10.0
*2024-04-10*
@@ -392,7 +607,7 @@
- Feed: a scrolling bug where the feed scrolls up after returning to it
- Minor bugs
# 2024.2.25.0
## 2024.2.25.0
*2024-02-25*
@@ -426,7 +641,7 @@
- TikTok: files with long names aren't downloaded
- Minor bugs
# 2024.1.26.0
## 2024.1.26.0
*2024-01-26*
@@ -438,7 +653,7 @@
- Instagram: stories (user) downloading with the wrong aspect ratio for some users
- Minor bugs
# 2024.1.20.0
## 2024.1.20.0
*2024-01-20*
@@ -446,7 +661,7 @@
- Instagram: **the ability to download reels**
- LPSG: handle 404 error
# 2024.1.18.0
## 2024.1.18.0
*2024-01-18*
@@ -456,7 +671,7 @@
- YouTube (standalone app): URL array form doesn't show scrollbars
- Minor bugs
# 2024.1.12.1
## 2024.1.12.1
*2024-01-12*
@@ -469,7 +684,7 @@
- YouTube: incorrect opening of a post from the feed
- YouTube: wrong date to data parsing
# 2024.1.12.0
## 2024.1.12.0
*2024-01-12*
@@ -485,7 +700,8 @@
- xHamster: profiles are not downloading
- Minor bugs
# 2023.12.27.0
# 2023
## 2023.12.27.0
*2023-12-27*
@@ -499,7 +715,7 @@
- Saved posts: session file is not updated when new data is added
- Minor bugs
# 2023.12.15.0
## 2023.12.15.0
*2023-12-15*
@@ -507,7 +723,7 @@
- Twitter: some twitter profiles don't download completely
- Minor bugs
# 2023.12.14.0
## 2023.12.14.0
*2023-12-14*
@@ -515,7 +731,7 @@
- YouTube: options `Create thumbnail files (video)` and `Create thumbnail files (music)`
- YouTube: `Select all` and `Select none` buttons
# 2023.12.13.0
## 2023.12.13.0
*2023-12-13*
@@ -527,7 +743,7 @@
- Feed: saved posts are added to the end of the feed
- xHamster: some videos won't download
# 2023.12.10.0
## 2023.12.10.0
*2023-12-10*
@@ -536,7 +752,7 @@
- Fixed
- Twitter: data is not downloading
# 2023.12.7.0
## 2023.12.7.0
*2023-12-07*
@@ -552,14 +768,14 @@
- Standalone downloader: URL files are not deleted along with the file
- Minor bugs
# 2023.11.25.0
## 2023.11.25.0
*2023-11-25*
- Fixed
- Reddit: missing refresh token button in the settings form
# 2023.11.24.0
## 2023.11.24.0
*2023-11-24*
@@ -583,7 +799,7 @@ For those of you who use TikTok, I recommend updating [TikTok plugin](https://gi
- YouTube: path not set when adding array to download
- Minor bugs
# 2023.11.17.0
## 2023.11.17.0
*2023-11-17*
@@ -613,7 +829,7 @@ For those of you who use TikTok, I recommend updating [TikTok plugin](https://gi
- Automation: handle automation start error (in some cases) when changing scheduler
- Minor bugs
# 2023.10.10.0
## 2023.10.10.0
*2023-10-10*
@@ -639,7 +855,7 @@ For those of you who use TikTok, I recommend updating [TikTok plugin](https://gi
- Standalone downloader: cached thumbnail is not removed when item is removed from the list
- Minor bugs
# 2023.10.1.0
## 2023.10.1.0
*2023-10-01*
@@ -654,14 +870,14 @@ For those of you who use TikTok, I recommend updating [TikTok plugin](https://gi
- JustForFans: some profiles won't download
- Minor bugs
# 2023.9.21.0
## 2023.9.21.0
*2023-09-21*
- Fixed
- PornHub: videos are not downloading
# 2023.9.20.0
## 2023.9.20.0
*2023-09-20*
@@ -677,7 +893,7 @@ For those of you who use TikTok, I recommend updating [TikTok plugin](https://gi
- Instagram: handle error 500
- Collections: update labels only for the added user
# 2023.8.27.0
## 2023.8.27.0
*2023-08-27*
@@ -696,7 +912,7 @@ For those of you who use TikTok, I recommend updating [TikTok plugin](https://gi
- Auto downloader: downloading stuck
- Minor bugs
# 2023.8.6.0
## 2023.8.6.0
*2023-08-06*
@@ -784,7 +1000,7 @@ For those of you who use TikTok, I recommend updating [TikTok plugin](https://gi
- YouTube: a bug that caused the video to redownload
- Minor bugs
# 2023.6.19.0
## 2023.6.19.0
*2023-06-19*
@@ -804,7 +1020,7 @@ For those of you who use TikTok, I recommend updating [TikTok plugin](https://gi
- Progress bar bugs
- Minor bugs
# 2023.6.9.0
## 2023.6.9.0
*2023-06-09*
@@ -813,7 +1029,7 @@ For those of you who use TikTok, I recommend updating [TikTok plugin](https://gi
- Twitter: make the algorithm faster
- Make progress more informative
# 2023.6.8.0
## 2023.6.8.0
*2023-06-08*
@@ -825,7 +1041,7 @@ For those of you who use TikTok, I recommend updating [TikTok plugin](https://gi
- Twitter: profile not fully downloaded
- Corrected form size for small monitors (Issue #136)
# 2023.6.5.0
## 2023.6.5.0
*2023-06-05*
@@ -844,7 +1060,7 @@ For those of you who use TikTok, I recommend updating [TikTok plugin](https://gi
- Saved posts: remove main progress perform when downloading saved posts
- Minor bugs
# 2023.5.12.0
## 2023.5.12.0
*2023-05-12*
@@ -862,7 +1078,7 @@ For those of you who use TikTok, I recommend updating [TikTok plugin](https://gi
- Reddit: missing & broken images bug
- Main window: collection pointing bug
# 2023.4.28.0
## 2023.4.28.0
*2023-04-28*
@@ -906,7 +1122,7 @@ For those of you who use TikTok, I recommend updating [TikTok plugin](https://gi
- PornHub: photo galleries bug (Issue #115)
- Minor bugs
# 2023.3.5.0
## 2023.3.5.0
*2023-03-05*
@@ -915,7 +1131,7 @@ For those of you who use TikTok, I recommend updating [TikTok plugin](https://gi
- An error that could occur during Twitter MD5 comparison.
- A bug in the ffmpeg file parts concatenation algorithm that could occur in some cases.
# 2023.3.1.0
## 2023.3.1.0
*2023-03-01*
@@ -937,7 +1153,7 @@ For those of you who use TikTok, I recommend updating [TikTok plugin](https://gi
- (Issue #106) problem with non-Latin characters
- ffmpeg: maximum input length error when merging parts of files
# 2023.2.5.0
## 2023.2.5.0
*2023-02-05*
@@ -946,7 +1162,7 @@ For those of you who use TikTok, I recommend updating [TikTok plugin](https://gi
- Fixed
- (Issue #101) Failed download Gfycat video in some cases
# 2023.1.27.0
## 2023.1.27.0
*2023-01-27*
@@ -960,7 +1176,7 @@ For those of you who use TikTok, I recommend updating [TikTok plugin](https://gi
- `Interaction` option to the `Provider` attribute
- `IPropertyProvider` interface
# 2023.1.24.1
## 2023.1.24.1
*2023-01-24*
@@ -969,7 +1185,7 @@ For those of you who use TikTok, I recommend updating [TikTok plugin](https://gi
- Fixed
- (Issue #100) some Imgur albums won't download
# 2023.1.24.0
## 2023.1.24.0
*2023-01-24*
@@ -977,7 +1193,7 @@ For those of you who use TikTok, I recommend updating [TikTok plugin](https://gi
- (Issue #100) Imgur albums not downloading
- When deleting a collection with the 'ban' option, users in the collection are not banned
# 2023.1.2.0
## 2023.1.2.0
*2023-01-02*
@@ -992,7 +1208,8 @@ For those of you who use TikTok, I recommend updating [TikTok plugin](https://gi
- Fixed a bug in the user list loading algorithm
- Notifications: pressing any button opens SCrawler
# 2022.12.27.0
# 2022
## 2022.12.27.0
*2022-12-27*
@@ -1002,7 +1219,7 @@ For those of you who use TikTok, I recommend updating [TikTok plugin](https://gi
- Fixed
- XVideos not downloading (sorry, I broke it in a previous release)
# 2022.12.26.0
## 2022.12.26.0
*2022-12-26*
@@ -1025,7 +1242,7 @@ For those of you who use TikTok, I recommend updating [TikTok plugin](https://gi
- (Issue #69) **RedGifs data is not downloading**. Again.
- Minor bugs
# 2022.11.16.0
## 2022.11.16.0
*2022-11-16*
@@ -1059,7 +1276,7 @@ For those of you who use TikTok, I recommend updating [TikTok plugin](https://gi
- Users search form doesn't remember last size
- Minor bugs
# 2022.10.23.0
## 2022.10.23.0
*2022-10-23*
@@ -1078,7 +1295,7 @@ For those of you who use TikTok, I recommend updating [TikTok plugin](https://gi
- (Issue #69) **RedGifs data is not downloading**. Requires token.
- Minor bugs
# 2022.10.18.0
## 2022.10.18.0
*2022-10-18*
@@ -1113,7 +1330,7 @@ For those of you who use TikTok, I recommend updating [TikTok plugin](https://gi
- When trying to delete multiple collections, each collection asked for confirmation to delete
- Minor bugs
# 2022.9.24.0
## 2022.9.24.0
*2022-09-24*
@@ -1131,7 +1348,7 @@ For those of you who use TikTok, I recommend updating [TikTok plugin](https://gi
- Bug in the XVIDEOS downloader
- Minor bugs
# 2022.9.17.0
## 2022.9.17.0
*2022-09-17*
@@ -1147,7 +1364,7 @@ For those of you who use TikTok, I recommend updating [TikTok plugin](https://gi
- Incorrect feed sorting algorithm
- Minor bugs
# 2022.9.16.0
## 2022.9.16.0
*2022-09-16*
@@ -1156,7 +1373,7 @@ For those of you who use TikTok, I recommend updating [TikTok plugin](https://gi
- Incorrect rendering of the 'Feed' table when the number of columns is more than one
- Minor design bugs
# 2022.9.13.0
## 2022.9.13.0
*2022-09-13*
@@ -1166,21 +1383,21 @@ For those of you who use TikTok, I recommend updating [TikTok plugin](https://gi
- (Issue #70) Instagram posts not downloading if there are pinned posts that have already been downloaded
- Minor bugs
# 2022.9.10.0
## 2022.9.10.0
*2022-09-10*
- Fixed
- The memory is still leaking. This time because of the video. *Using WMP was not the best choice.*
# 2022.9.8.1
## 2022.9.8.1
*2022-09-08*
- Fixed
- Unexpected memory leak when using the 'Feed' form
# 2022.9.8.0
## 2022.9.8.0
*2022-09-08*
@@ -1191,7 +1408,7 @@ For those of you who use TikTok, I recommend updating [TikTok plugin](https://gi
- Fixed
- (Issue #67) Saved Instagram posts not downloading
# 2022.8.28.0
## 2022.8.28.0
*2022-08-28*
@@ -1200,7 +1417,7 @@ For those of you who use TikTok, I recommend updating [TikTok plugin](https://gi
- Fixed
- Incorrect number of posts displayed in the Reddit channels downloader.
# 2022.8.22.0
## 2022.8.22.0
*2022-08-22*
@@ -1214,7 +1431,7 @@ For those of you who use TikTok, I recommend updating [TikTok plugin](https://gi
- AutoDownloader option ```Show notifications``` not saved
- Minor bugs
# 2022.7.7.0
## 2022.7.7.0
*2022-07-07*
@@ -1232,7 +1449,7 @@ For those of you who use TikTok, I recommend updating [TikTok plugin](https://gi
- In some cases, Twitter image is not downloading
- Minor bugs
# 2022.6.10.0
## 2022.6.10.0
*2022-06-10*
@@ -1241,7 +1458,7 @@ For those of you who use TikTok, I recommend updating [TikTok plugin](https://gi
- Fixed
- Can't get Instagram user ID
# 2022.6.6.0
## 2022.6.6.0
*2022-06-06*
@@ -1251,7 +1468,7 @@ For those of you who use TikTok, I recommend updating [TikTok plugin](https://gi
- GIFs from Twitter not downloading
- Not quite correct algorithm for stopping automation
# 2022.6.3.0
## 2022.6.3.0
*2022-06-03*
@@ -1266,7 +1483,7 @@ Changed version numbering method. From now on, new versions will be numbered by
- Videos hosted on Reddit that are downloaded via m3u8 playlists are missing an audio track.
- Instagram hash not able to be auto-filled from cookies
# 3.0.0.10
## 3.0.0.10
*2022-05-23*
@@ -1289,7 +1506,7 @@ Changed version numbering method. From now on, new versions will be numbered by
- In some cases, the date and time are not added to the filename
- Unable to download photos from Twitter in full resolution (4K)
# 3.0.0.9
## 3.0.0.9
*2022-04-24*
@@ -1301,7 +1518,7 @@ Changed version numbering method. From now on, new versions will be numbered by
- Removed adding "No Parsed" internal label when not needed
- Redownloading Instagram Stories
# 3.0.0.8
## 3.0.0.8
*2022-04-19*
@@ -1311,7 +1528,7 @@ Changed version numbering method. From now on, new versions will be numbered by
- Fixed
- The script does not run after the user download is complete
# 3.0.0.7
## 3.0.0.7
*2022-04-14*
@@ -1323,7 +1540,7 @@ Changed version numbering method. From now on, new versions will be numbered by
- (Issue #33) Instagram Stories downloading error
- LPSG downloader does not download all content
# 3.0.0.6
## 3.0.0.6
*2022-04-04*
@@ -1336,14 +1553,14 @@ Changed version numbering method. From now on, new versions will be numbered by
- Incorrect behavior of the main progress bar when downloading saved posts
- (Issue #25) Date and Time not added for Stories and Tagged Photos
# 3.0.0.5
## 3.0.0.5
*2022-04-02*
- Added
- ```New```, ```Hot```, ```Top``` Reddit channel and user download modes
# 3.0.0.4
## 3.0.0.4
*2022-03-26*
@@ -1351,7 +1568,7 @@ Changed version numbering method. From now on, new versions will be numbered by
- External plugins do not save information about downloaded files
- The user cannot be added to the collection if a special path has been specified.
# 3.0.0.3
## 3.0.0.3
*2022-03-24*
@@ -1364,7 +1581,7 @@ Changed version numbering method. From now on, new versions will be numbered by
- Typo when applying "Download UHD" in XVIDEOS plugin
- The sites filter does not work unless the "Fast profiles loading" option is enabled.
# 3.0.0.2
## 3.0.0.2
*2022-03-22*
@@ -1376,7 +1593,7 @@ Changed version numbering method. From now on, new versions will be numbered by
- Fixed
- Minor bugs
# 3.0.0.1
## 3.0.0.1
*2022-03-20*
@@ -1391,7 +1608,7 @@ Changed version numbering method. From now on, new versions will be numbered by
- Some design fixes
- Minor bugs
# 3.0.0.0
## 3.0.0.0
*2022-03-17*
@@ -1433,7 +1650,7 @@ Changed version numbering method. From now on, new versions will be numbered by
At the requests of some users, I added [screenshots](ProgramScreenshots) of the program and added screenshots to [ReadMe](README.md) and the [guide](https://github.com/AAndyProgram/SCrawler/wiki).
# 2.0.0.4
## 2.0.0.4
*2022-02-07*
@@ -1449,7 +1666,7 @@ At the requests of some users, I added [screenshots](ProgramScreenshots) of the
- **Error when specifying network paths**
- Minor bugs
# 2.0.0.3
## 2.0.0.3
*2022-02-02*
@@ -1466,7 +1683,7 @@ At the requests of some users, I added [screenshots](ProgramScreenshots) of the
- Collection ignored when validated when creating a new user
- Incorrect number of Instagram profiles downloads per session
# 2.0.0.2
## 2.0.0.2
*2022-01-23*
@@ -1490,7 +1707,8 @@ At the requests of some users, I added [screenshots](ProgramScreenshots) of the
- Fixed
- The program was showing incorrect information about the total numbers of images and videos downloaded when a Reddit user was created from a channel
# 2.0.0.1
# 2021
## 2.0.0.1
*2021-12-29*
@@ -1500,7 +1718,7 @@ At the requests of some users, I added [screenshots](ProgramScreenshots) of the
- Incorrect filling of user parameters in the user creation form
- In some cases, the global settings cannot be saved.
# 2.0.0.0
## 2.0.0.0
*2021-12-27*
@@ -1519,7 +1737,7 @@ At the requests of some users, I added [screenshots](ProgramScreenshots) of the
- Suspended profiles do not change status if the profile is no longer suspended
- Limited download for Twitter not implemented
# 1.0.1.0
## 1.0.1.0
*2021-12-20*
@@ -1542,7 +1760,7 @@ At the requests of some users, I added [screenshots](ProgramScreenshots) of the
- Users in the main window are not refreshed if new users are added by a list that includes banned and/or unrecognized users.
- Minor bugs
# 1.0.0.4
## 1.0.0.4
*2021-12-12*
@@ -1552,7 +1770,7 @@ At the requests of some users, I added [screenshots](ProgramScreenshots) of the
- Fixed
- Images hosted on Imgur won't download
# 1.0.0.3
## 1.0.0.3
*2021-12-11*
@@ -1560,7 +1778,7 @@ At the requests of some users, I added [screenshots](ProgramScreenshots) of the
- Custom "Download videos" option is not saved
- The "Download all" button is not activated after changing modes
# 1.0.0.2
## 1.0.0.2
*2021-12-10*
@@ -1570,7 +1788,7 @@ At the requests of some users, I added [screenshots](ProgramScreenshots) of the
- Fixed
- In some cases, the "Stop" button is not activated after download start
# 1.0.0.1
## 1.0.0.1
*2021-12-09*
@@ -1594,7 +1812,7 @@ At the requests of some users, I added [screenshots](ProgramScreenshots) of the
- Wrong some Reddit videos parsing
- Wrong some Reddit images parsing
# 1.0.0.0
## 1.0.0.0
*2021-12-07*

1
FAQ.md
View File

@@ -33,6 +33,7 @@ I strongly recommend you to **regularly** create backup copies of the settings f
## General questions
- **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)!
- :exclamation: **Try to avoid Chinese/Japanese symbols in the paths.**
- 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 **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.

View File

@@ -8,3 +8,4 @@ List of available plugins:
Tools:
- [image2post](https://github.com/unknown81311/SCrawler-image2post) by @unknown81311: **get reddit post URL from file.**
- [Update-SCrawler-Tools](https://github.com/cjb900/Update-SCrawler-Tools) by @cjb900: **a tool for updating third-party programs such as yt-dlp and gallery-dl**

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

@@ -1,5 +1,6 @@
<!--
# 🏳️‍🌈 Happy LGBT Pride Month 🎉
-->
# 🏳️‍🌈 Social networks crawler 🏳️‍🌈
[![GitHub release (latest by date)](https://img.shields.io/github/v/release/AAndyProgram/SCrawler)](https://github.com/AAndyProgram/SCrawler/releases/latest)
@@ -35,15 +36,15 @@ A program to download photo and video from [any site](#supported-sites) (e.g. Yo
# What can program do:
- Download pictures and videos from user profiles:
- YouTube videos, shorts, community feeds, users, artists, playlists, music, tracks;
- Reddit images, galleries of images, videos, saved posts;
- Reddit images, galleries of images, videos, text, saved posts;
- Redgifs images and videos (https://www.redgifs.com/);
- Twitter images and videos, saved (bookmarked) posts, likes, communities;
- Bluesky images and videos;
- OnlyFans images and videos, saved (bookmarked) posts, stories;
- Twitter images and videos, text, saved (bookmarked) posts, likes, communities;
- Bluesky images and videos, text;
- OnlyFans images and videos, text, saved (bookmarked) posts, stories;
- JustForFans images and videos, saved (bookmarked) posts;
- Mastodon images and videos, saved (bookmarked) posts;
- Instagram images and videos, tagged posts, stories, saved posts;
- Threads images and videos, saved posts;
- Instagram images and videos, text, tagged posts, stories, saved posts;
- Threads images and videos, text, saved posts;
- Facebook images and videos, stories, saved posts;
- TikTok images and videos;
- Pinterest boards, users, saved posts;

View File

@@ -32,6 +32,6 @@ Imports System.Runtime.InteropServices
' by using the '*' as shown below:
' <Assembly: AssemblyVersion("1.0.*")>
<Assembly: AssemblyVersion("2025.2.25.0")>
<Assembly: AssemblyFileVersion("2025.2.25.0")>
<Assembly: AssemblyVersion("2025.6.1.0")>
<Assembly: AssemblyFileVersion("2025.6.1.0")>
<Assembly: NeutralResourcesLanguage("en")>

View File

@@ -35,6 +35,9 @@ Namespace Plugin
Public Property DownloadState As UserMediaStates Implements IUserMedia.DownloadState
Public Property PostID As String Implements IUserMedia.PostID
Public Property PostDate As Date? Implements IUserMedia.PostDate
Public Property PostText As String Implements IUserMedia.PostText
Public Property PostTextFile As String Implements IUserMedia.PostTextFile
Public Property PostTextFileSpecialFolder As Boolean Implements IUserMedia.PostTextFileSpecialFolder
Public Property SpecialFolder As String Implements IUserMedia.SpecialFolder
Public Property Attempts As Integer Implements IUserMedia.Attempts
Public Property [Object] As Object Implements IUserMedia.Object
@@ -48,6 +51,9 @@ Namespace Plugin
Property DownloadState As UserMediaStates
Property PostID As String
Property PostDate As Date?
Property PostText As String
Property PostTextFile As String
Property PostTextFileSpecialFolder As Boolean
Property SpecialFolder As String
Property Attempts As Integer
Property [Object] As Object

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 320 B

View File

@@ -32,6 +32,6 @@ Imports System.Runtime.InteropServices
' by using the '*' as shown below:
' <Assembly: AssemblyVersion("1.0.*")>
<Assembly: AssemblyVersion("2023.12.15.0")>
<Assembly: AssemblyFileVersion("2023.12.15.0")>
<Assembly: AssemblyVersion("2025.6.1.0")>
<Assembly: AssemblyFileVersion("2025.6.1.0")>
<Assembly: NeutralResourcesLanguage("en")>

View File

@@ -26,7 +26,7 @@ Namespace My.Resources
Global.System.Diagnostics.DebuggerNonUserCodeAttribute(), _
Global.System.Runtime.CompilerServices.CompilerGeneratedAttribute(), _
Global.Microsoft.VisualBasic.HideModuleNameAttribute()> _
Friend Module Resources
Public Module Resources
Private resourceMan As Global.System.Resources.ResourceManager
@@ -36,7 +36,7 @@ Namespace My.Resources
''' Returns the cached ResourceManager instance used by this class.
'''</summary>
<Global.System.ComponentModel.EditorBrowsableAttribute(Global.System.ComponentModel.EditorBrowsableState.Advanced)> _
Friend ReadOnly Property ResourceManager() As Global.System.Resources.ResourceManager
Public ReadOnly Property ResourceManager() As Global.System.Resources.ResourceManager
Get
If Object.ReferenceEquals(resourceMan, Nothing) Then
Dim temp As Global.System.Resources.ResourceManager = New Global.System.Resources.ResourceManager("SCrawler.Resources", GetType(Resources).Assembly)
@@ -51,7 +51,7 @@ Namespace My.Resources
''' resource lookups using this strongly typed resource class.
'''</summary>
<Global.System.ComponentModel.EditorBrowsableAttribute(Global.System.ComponentModel.EditorBrowsableState.Advanced)> _
Friend Property Culture() As Global.System.Globalization.CultureInfo
Public Property Culture() As Global.System.Globalization.CultureInfo
Get
Return resourceCulture
End Get
@@ -59,5 +59,25 @@ Namespace My.Resources
resourceCulture = value
End Set
End Property
'''<summary>
''' Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
'''</summary>
Public ReadOnly Property FilterIcon() As System.Drawing.Icon
Get
Dim obj As Object = ResourceManager.GetObject("FilterIcon", resourceCulture)
Return CType(obj,System.Drawing.Icon)
End Get
End Property
'''<summary>
''' Looks up a localized resource of type System.Drawing.Bitmap.
'''</summary>
Public ReadOnly Property FilterPic() As System.Drawing.Bitmap
Get
Dim obj As Object = ResourceManager.GetObject("FilterPic", resourceCulture)
Return CType(obj,System.Drawing.Bitmap)
End Get
End Property
End Module
End Namespace

View File

@@ -46,7 +46,7 @@
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
@@ -60,6 +60,7 @@
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
@@ -68,9 +69,10 @@
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
@@ -85,9 +87,10 @@
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
@@ -109,9 +112,16 @@
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.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>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="FilterIcon" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Content\Icons\FilterIcon.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="FilterPic" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Content\Images\FilterPic.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

View File

@@ -82,6 +82,7 @@
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
<Reference Include="System.Core" />
@@ -123,7 +124,7 @@
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="My Project\Resources.resx">
<Generator>VbMyResourcesResXFileCodeGenerator</Generator>
<Generator>PublicVbMyResourcesResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.vb</LastGenOutput>
<CustomToolNamespace>My.Resources</CustomToolNamespace>
<SubType>Designer</SubType>
@@ -147,5 +148,9 @@
<Name>PersonalUtilities</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Content Include="Content\Icons\FilterIcon.ico" />
<Content Include="Content\Images\FilterPic.png" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" />
</Project>

View File

@@ -136,4 +136,34 @@ Namespace API.YouTube.Base
End If
End Function
End Structure
Public Structure TrimOption : Implements IComparable(Of TrimOption)
Public Name As String
Public Start As Integer
Public ReadOnly Property StartTime As TimeSpan
Get
Return TimeSpan.FromSeconds(Start)
End Get
End Property
Public [End] As Integer
Public ReadOnly Property EndTime As TimeSpan
Get
Return TimeSpan.FromSeconds([End])
End Get
End Property
Public Shared Widening Operator CType(ByVal t As TrimOption) As String
Return t.ToString
End Operator
Public Overrides Function ToString() As String
Dim v$ = $"{Start} - {[End]}"
If Not Name.IsEmptyString Then v = $"[{v}] - {Name}"
Return v
End Function
Public Overrides Function Equals(ByVal Obj As Object) As Boolean
Try : With DirectCast(Obj, TrimOption) : Return Start = .Start And [End] = .End : End With : Catch : End Try
Return False
End Function
Private Function CompareTo(ByVal Other As TrimOption) As Integer Implements IComparable(Of TrimOption).CompareTo
Return Start.CompareTo(Other.Start)
End Function
End Structure
End Namespace

View File

@@ -18,6 +18,7 @@ Namespace API.YouTube.Base
Public Const TrueUrlPattern As String = "https?://[^/]*?youtube.com/[^\?/&]+((\??[^\?/&]+|/[^\?/&]+))"
'2 - type; 5 - id
Public Const UrlTypePattern As String = "(?<=https?://[^/]*?youtube.com/)((@|[^\?/&]+))([/\?]{0,1}(list=|v=|)([^\?/&]*))(?=(\S+|\Z|))"
Public Const UrlTypePattern_BE As String = "(?<=https?://[^/]*?youtu.be/)([^\?/&]+)"
Private Sub New()
End Sub
Public Shared Function StandardizeURL(ByVal URL As String) As String
@@ -36,6 +37,7 @@ Namespace API.YouTube.Base
Next
data.Clear()
End If
If val.IsEmptyString Then val = RegexReplace(URL, RParams.DMS(UrlTypePattern_BE, 0, EDP.ReturnValue))
If Not val.IsEmptyString Then Return $"https://www.youtube.com/watch?v={val}"
End If
End If
@@ -100,6 +102,9 @@ Namespace API.YouTube.Base
Return YouTubeMediaType.Channel
End Select
End If
Else
Dim v$ = RegexReplace(URL, RParams.DMS(UrlTypePattern_BE, 0, EDP.ReturnValue))
If Not v.IsEmptyString Then Return YouTubeMediaType.Single
End If
End If
Return YouTubeMediaType.Undefined

View File

@@ -33,6 +33,7 @@ Namespace API.YouTube.Base
#Region "Declarations"
<Browsable(False)> Private ReadOnly Property XML As XmlFile Implements IXMLValuesContainer.XML
<Browsable(False)> Friend ReadOnly Property DesignXml As XmlFile
<Browsable(False)> Friend ReadOnly Property FILTER As Controls.YTDataFilter
<Browsable(False)> Private Property Mode As GridUpdateModes = GridUpdateModes.OnConfirm Implements IGridValuesContainer.Mode
<Browsable(False), XMLVV(-1)> Friend ReadOnly Property PlaylistFormSplitterDistance As XMLValue(Of Integer)
<Browsable(False)> Friend ReadOnly Property DownloadLocations As DownloadLocationsCollection
@@ -267,6 +268,9 @@ Namespace API.YouTube.Base
<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)
<Browsable(True), GridVisible, XMLVN({"Defaults"}, True), Category("Defaults"), DisplayName("Parse long user titles"),
Description("Suitable for multiple artists")>
Public ReadOnly Property ParseLongUserTitle As XMLValue(Of Boolean)
#End Region
#Region "Defaults ChannelsDownload"
<Browsable(True), GridVisible, XMLVN({"Defaults", "Channels"}), Category("Defaults"), DisplayName("Default download tabs for channels"),
@@ -318,8 +322,11 @@ Namespace API.YouTube.Base
Description("Convert non-AVC codecs (eg 'VP9') to AVC. Not recommended due to high CPU usage!")>
Public ReadOnly Property DefaultVideoConvertNonAVC As XMLValue(Of Boolean)
<Browsable(True), GridVisible, XMLVN({"DefaultsVideo"}, False), Category("Defaults Video"), DisplayName("Embed thumbnail (video)"),
Description("Embed thumbnail in the video as cover art. Default: true.")>
Description("Embed thumbnail in the video as cover art. Default: false.")>
Public ReadOnly Property DefaultVideoEmbedThumbnail As XMLValue(Of Boolean)
<Browsable(True), GridVisible, XMLVN({"DefaultsVideo"}, True), Category("Defaults Video"), DisplayName("Embed chapters"),
Description("Embed chapters in the video. Default: true.")>
Public ReadOnly Property DefaultVideoEmbedChapters As XMLValue(Of Boolean)
<Browsable(True), GridVisible, XMLVN({"DefaultsVideo"}), Category("Defaults Video"), DisplayName("Include zero size formats"),
Description("Include formats with zero size (or undefined size).")>
Public ReadOnly Property DefaultVideoIncludeNullSize As XMLValue(Of Boolean)
@@ -494,6 +501,29 @@ Namespace API.YouTube.Base
TypeConverter(GetType(ValueCollectionConverter)),
Description("Additional format for downloading subtitles. This means that all subtitles will be converted to the formats you choose and saved as separate files.")>
Public ReadOnly Property DefaultSubtitlesFormatAddit As XMLValuesCollection(Of String)
<Browsable(True), GridVisible, XMLVN({"DefaultsSubtitles"}), Category("Defaults Subtitles"), DisplayName("Embed subtitles"),
Description("Embed subtitles in the video (only for mp4, webm and mkv videos)")>
Public ReadOnly Property DefaultSubtitlesEmbed As XMLValue(Of Boolean)
#End Region
#Region "Trim"
<Browsable(True), GridVisible, XMLVN({"DefaultsTrim"}, False), Category("Defaults Trimming"), DisplayName("Delete original file"),
Description("If true, the original file will be deleted after trimming")>
Public ReadOnly Property TrimDeleteOriginalFile As XMLValue(Of Boolean)
<Browsable(True), GridVisible, XMLVN({"DefaultsTrim"}, False), Category("Defaults Trimming"), DisplayName("Add to M3U8"),
Description("If true, the trimmed files will be added to the M3U8 playlist (if selected)")>
Public ReadOnly Property TrimAddTrimmedFilesToM3U8 As XMLValue(Of Boolean)
<Browsable(True), GridVisible, XMLVN({"DefaultsTrim"}, False), Category("Defaults Trimming"), DisplayName("Separate folder"),
Description("Place the trimmed files in a separate folder")>
Public ReadOnly Property TrimSeparateFolder As XMLValue(Of Boolean)
Friend Const TrimSeparateFolderNameDefault As String = "Trim"
<Browsable(True), GridVisible, XMLVN({"DefaultsTrim"}, TrimSeparateFolderNameDefault), Category("Defaults Trimming"), DisplayName("Separate folder name"),
Description("Name of a separate folder")>
Public ReadOnly Property TrimSeparateFolderName As XMLValue(Of String)
#End Region
#Region "Errors"
<Browsable(True), GridVisible, XMLVN({"Errors"}, True), Category("ERRORS"), DisplayName("Ignore downloading errors"),
Description("Ignore download errors if some files are missing (e.g. subtitles) and continue downloading")>
Public ReadOnly Property ErrorsIgnore As XMLValue(Of Boolean)
#End Region
#End Region
#Region "Initializer"
@@ -514,6 +544,7 @@ Namespace API.YouTube.Base
XML.LoadData(EDP.None)
DesignXml = New XmlFile("Settings\DesignDownloader.xml", Protector.Modes.All, False)
DesignXml.LoadData(EDP.None)
FILTER = New Controls.YTDataFilter
InitializeXMLValueProperties(Me)
AddHandler ShowNotificationsEveryDownload.TempValueChanged, AddressOf ShowNotificationsEveryDownload_TempValueChanged
Cookies = New CookieKeeper

View File

@@ -0,0 +1,158 @@
' 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
Namespace API.YouTube.Controls
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()>
Partial Friend Class ChaptersForm : Inherits System.Windows.Forms.Form
<System.Diagnostics.DebuggerNonUserCode()>
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
Try
If disposing AndAlso components IsNot Nothing Then
components.Dispose()
End If
Finally
MyBase.Dispose(disposing)
End Try
End Sub
Private components As System.ComponentModel.IContainer
<System.Diagnostics.DebuggerStepThrough()>
Private Sub InitializeComponent()
Dim CONTAINER_MAIN As System.Windows.Forms.ToolStripContainer
Dim TP_MAIN As System.Windows.Forms.TableLayoutPanel
Dim TP_BUTTONS As System.Windows.Forms.TableLayoutPanel
Me.BTT_ALL = New System.Windows.Forms.Button()
Me.BTT_NONE = New System.Windows.Forms.Button()
Me.BTT_INVERT = New System.Windows.Forms.Button()
Me.LIST_CHAPTERS = New System.Windows.Forms.CheckedListBox()
CONTAINER_MAIN = New System.Windows.Forms.ToolStripContainer()
TP_MAIN = New System.Windows.Forms.TableLayoutPanel()
TP_BUTTONS = New System.Windows.Forms.TableLayoutPanel()
CONTAINER_MAIN.ContentPanel.SuspendLayout()
CONTAINER_MAIN.SuspendLayout()
TP_MAIN.SuspendLayout()
TP_BUTTONS.SuspendLayout()
Me.SuspendLayout()
'
'CONTAINER_MAIN
'
'
'CONTAINER_MAIN.ContentPanel
'
CONTAINER_MAIN.ContentPanel.Controls.Add(TP_MAIN)
CONTAINER_MAIN.ContentPanel.Size = New System.Drawing.Size(384, 361)
CONTAINER_MAIN.Dock = System.Windows.Forms.DockStyle.Fill
CONTAINER_MAIN.LeftToolStripPanelVisible = False
CONTAINER_MAIN.Location = New System.Drawing.Point(0, 0)
CONTAINER_MAIN.Name = "CONTAINER_MAIN"
CONTAINER_MAIN.RightToolStripPanelVisible = False
CONTAINER_MAIN.Size = New System.Drawing.Size(384, 361)
CONTAINER_MAIN.TabIndex = 0
CONTAINER_MAIN.TopToolStripPanelVisible = False
'
'TP_MAIN
'
TP_MAIN.ColumnCount = 1
TP_MAIN.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
TP_MAIN.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20.0!))
TP_MAIN.Controls.Add(TP_BUTTONS, 0, 1)
TP_MAIN.Controls.Add(Me.LIST_CHAPTERS, 0, 0)
TP_MAIN.Dock = System.Windows.Forms.DockStyle.Fill
TP_MAIN.Location = New System.Drawing.Point(0, 0)
TP_MAIN.Name = "TP_MAIN"
TP_MAIN.RowCount = 2
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 30.0!))
TP_MAIN.Size = New System.Drawing.Size(384, 361)
TP_MAIN.TabIndex = 0
'
'TP_BUTTONS
'
TP_BUTTONS.ColumnCount = 3
TP_BUTTONS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33333!))
TP_BUTTONS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33333!))
TP_BUTTONS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33333!))
TP_BUTTONS.Controls.Add(Me.BTT_ALL, 0, 0)
TP_BUTTONS.Controls.Add(Me.BTT_NONE, 1, 0)
TP_BUTTONS.Controls.Add(Me.BTT_INVERT, 2, 0)
TP_BUTTONS.Dock = System.Windows.Forms.DockStyle.Fill
TP_BUTTONS.Location = New System.Drawing.Point(0, 331)
TP_BUTTONS.Margin = New System.Windows.Forms.Padding(0)
TP_BUTTONS.Name = "TP_BUTTONS"
TP_BUTTONS.RowCount = 1
TP_BUTTONS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
TP_BUTTONS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20.0!))
TP_BUTTONS.Size = New System.Drawing.Size(384, 30)
TP_BUTTONS.TabIndex = 1
'
'BTT_ALL
'
Me.BTT_ALL.Dock = System.Windows.Forms.DockStyle.Fill
Me.BTT_ALL.Location = New System.Drawing.Point(3, 3)
Me.BTT_ALL.Name = "BTT_ALL"
Me.BTT_ALL.Size = New System.Drawing.Size(122, 24)
Me.BTT_ALL.TabIndex = 0
Me.BTT_ALL.Text = "All"
Me.BTT_ALL.UseVisualStyleBackColor = True
'
'BTT_NONE
'
Me.BTT_NONE.Dock = System.Windows.Forms.DockStyle.Fill
Me.BTT_NONE.Location = New System.Drawing.Point(131, 3)
Me.BTT_NONE.Name = "BTT_NONE"
Me.BTT_NONE.Size = New System.Drawing.Size(122, 24)
Me.BTT_NONE.TabIndex = 1
Me.BTT_NONE.Text = "None"
Me.BTT_NONE.UseVisualStyleBackColor = True
'
'BTT_INVERT
'
Me.BTT_INVERT.Dock = System.Windows.Forms.DockStyle.Fill
Me.BTT_INVERT.Location = New System.Drawing.Point(259, 3)
Me.BTT_INVERT.Name = "BTT_INVERT"
Me.BTT_INVERT.Size = New System.Drawing.Size(122, 24)
Me.BTT_INVERT.TabIndex = 2
Me.BTT_INVERT.Text = "Invert"
Me.BTT_INVERT.UseVisualStyleBackColor = True
'
'LIST_CHAPTERS
'
Me.LIST_CHAPTERS.Dock = System.Windows.Forms.DockStyle.Fill
Me.LIST_CHAPTERS.FormattingEnabled = True
Me.LIST_CHAPTERS.Location = New System.Drawing.Point(3, 3)
Me.LIST_CHAPTERS.Name = "LIST_CHAPTERS"
Me.LIST_CHAPTERS.Size = New System.Drawing.Size(378, 325)
Me.LIST_CHAPTERS.TabIndex = 0
'
'ChaptersForm
'
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
Me.ClientSize = New System.Drawing.Size(384, 361)
Me.Controls.Add(CONTAINER_MAIN)
Me.KeyPreview = True
Me.MinimizeBox = False
Me.MinimumSize = New System.Drawing.Size(400, 400)
Me.Name = "ChaptersForm"
Me.ShowIcon = False
Me.ShowInTaskbar = False
Me.Text = "Chapters"
CONTAINER_MAIN.ContentPanel.ResumeLayout(False)
CONTAINER_MAIN.ResumeLayout(False)
CONTAINER_MAIN.PerformLayout()
TP_MAIN.ResumeLayout(False)
TP_BUTTONS.ResumeLayout(False)
Me.ResumeLayout(False)
End Sub
Private WithEvents BTT_ALL As Button
Private WithEvents BTT_NONE As Button
Private WithEvents BTT_INVERT As Button
Private WithEvents LIST_CHAPTERS As CheckedListBox
End Class
End Namespace

View File

@@ -0,0 +1,129 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="CONTAINER_MAIN.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value>
</metadata>
<metadata name="TP_MAIN.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value>
</metadata>
<metadata name="TP_BUTTONS.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value>
</metadata>
</root>

View File

@@ -0,0 +1,66 @@
' 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.Forms
Imports PersonalUtilities.Forms.Toolbars
Imports SCrawler.API.YouTube.Base
Imports SCrawler.API.YouTube.Objects
Namespace API.YouTube.Controls
Friend Class ChaptersForm
Private WithEvents MyDefs As DefaultFormOptions
Private ReadOnly Property MyData As YouTubeMediaContainerBase
Private ReadOnly Property MyDataSelected As List(Of TrimOption)
Friend ReadOnly Property MyResult As List(Of TrimOption)
Friend Sub New(ByRef data As YouTubeMediaContainerBase, ByVal selected As IEnumerable(Of TrimOption))
InitializeComponent()
MyDefs = New DefaultFormOptions(Me, MyYouTubeSettings.DesignXml)
MyData = data
MyDataSelected = New List(Of TrimOption)
MyDataSelected.ListAddList(selected)
MyResult = New List(Of TrimOption)
End Sub
Private Sub ChaptersForm_Load(sender As Object, e As EventArgs) Handles Me.Load
With MyDefs
.MyViewInitialize()
.AddOkCancelToolbar()
With LIST_CHAPTERS
.BeginUpdate()
.Items.AddRange(MyData.Chapters.Cast(Of Object).ToArray)
If MyDataSelected.Count > 0 Then
For i% = 0 To .Items.Count - 1 : .SetItemChecked(i, MyDataSelected.Contains(MyData.Chapters(i))) : Next
End If
.EndUpdate()
End With
.EndLoaderOperations()
End With
End Sub
Private Sub ChaptersForm_Disposed(sender As Object, e As EventArgs) Handles Me.Disposed
MyResult.Clear()
MyDataSelected.Clear()
End Sub
Private Sub MyDefs_ButtonOkClick(ByVal Sender As Object, ByVal e As KeyHandleEventArgs) Handles MyDefs.ButtonOkClick
With LIST_CHAPTERS
For i% = 0 To .Items.Count - 1
If .GetItemChecked(i) Then MyResult.Add(MyData.Chapters(i))
Next
End With
MyDefs.CloseForm()
End Sub
Private Sub BTT_ALL_NONE_INVERT_Click(sender As Object, e As EventArgs) Handles BTT_ALL.Click, BTT_NONE.Click, BTT_INVERT.Click
Dim v As Boolean = sender Is BTT_ALL
Dim isInvert As Boolean = sender Is BTT_INVERT
With LIST_CHAPTERS
.BeginUpdate()
For i% = 0 To .Items.Count - 1 : .SetItemChecked(i, If(isInvert, Not .GetItemChecked(i), v)) : Next
.EndUpdate()
End With
End Sub
End Class
End Namespace

View File

@@ -0,0 +1,324 @@
' 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
Namespace API.YouTube.Controls
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()>
Partial Friend Class FilterForm : Inherits System.Windows.Forms.Form
<System.Diagnostics.DebuggerNonUserCode()>
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
Try
If disposing AndAlso components IsNot Nothing Then
components.Dispose()
End If
Finally
MyBase.Dispose(disposing)
End Try
End Sub
Private components As System.ComponentModel.IContainer
<System.Diagnostics.DebuggerStepThrough()>
Private Sub InitializeComponent()
Dim CONTAINER_MAIN As System.Windows.Forms.ToolStripContainer
Dim TP_MAIN As System.Windows.Forms.TableLayoutPanel
Dim TP_TYPES As System.Windows.Forms.TableLayoutPanel
Dim TP_USERS As System.Windows.Forms.TableLayoutPanel
Dim TP_USERS_2 As System.Windows.Forms.TableLayoutPanel
Me.CH_FILTER_ALL = New System.Windows.Forms.CheckBox()
Me.CH_FILTER_SINGLE = New System.Windows.Forms.CheckBox()
Me.CH_FILTER_CHANNEL = New System.Windows.Forms.CheckBox()
Me.CH_FILTER_PLS = New System.Windows.Forms.CheckBox()
Me.TP_MUSIC = New System.Windows.Forms.TableLayoutPanel()
Me.CH_M_ALL = New System.Windows.Forms.CheckBox()
Me.CH_M_VIDEO = New System.Windows.Forms.CheckBox()
Me.CH_M_MUSIC = New System.Windows.Forms.CheckBox()
Me.LIST_USERS = New System.Windows.Forms.CheckedListBox()
Me.CH_USERS_USE = New System.Windows.Forms.CheckBox()
Me.BTT_SELECT_ALL = New System.Windows.Forms.Button()
Me.BTT_SELECT_NONE = New System.Windows.Forms.Button()
CONTAINER_MAIN = New System.Windows.Forms.ToolStripContainer()
TP_MAIN = New System.Windows.Forms.TableLayoutPanel()
TP_TYPES = New System.Windows.Forms.TableLayoutPanel()
TP_USERS = New System.Windows.Forms.TableLayoutPanel()
TP_USERS_2 = New System.Windows.Forms.TableLayoutPanel()
CONTAINER_MAIN.ContentPanel.SuspendLayout()
CONTAINER_MAIN.SuspendLayout()
TP_MAIN.SuspendLayout()
TP_TYPES.SuspendLayout()
Me.TP_MUSIC.SuspendLayout()
TP_USERS.SuspendLayout()
TP_USERS_2.SuspendLayout()
Me.SuspendLayout()
'
'CONTAINER_MAIN
'
'
'CONTAINER_MAIN.ContentPanel
'
CONTAINER_MAIN.ContentPanel.Controls.Add(TP_MAIN)
CONTAINER_MAIN.ContentPanel.Size = New System.Drawing.Size(384, 361)
CONTAINER_MAIN.Dock = System.Windows.Forms.DockStyle.Fill
CONTAINER_MAIN.LeftToolStripPanelVisible = False
CONTAINER_MAIN.Location = New System.Drawing.Point(0, 0)
CONTAINER_MAIN.Name = "CONTAINER_MAIN"
CONTAINER_MAIN.RightToolStripPanelVisible = False
CONTAINER_MAIN.Size = New System.Drawing.Size(384, 361)
CONTAINER_MAIN.TabIndex = 0
CONTAINER_MAIN.TopToolStripPanelVisible = False
'
'TP_MAIN
'
TP_MAIN.ColumnCount = 1
TP_MAIN.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
TP_MAIN.Controls.Add(TP_TYPES, 0, 0)
TP_MAIN.Controls.Add(Me.TP_MUSIC, 0, 1)
TP_MAIN.Controls.Add(TP_USERS, 0, 2)
TP_MAIN.Dock = System.Windows.Forms.DockStyle.Fill
TP_MAIN.Location = New System.Drawing.Point(0, 0)
TP_MAIN.Name = "TP_MAIN"
TP_MAIN.RowCount = 3
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20.0!))
TP_MAIN.Size = New System.Drawing.Size(384, 361)
TP_MAIN.TabIndex = 0
'
'TP_TYPES
'
TP_TYPES.ColumnCount = 4
TP_TYPES.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 25.0!))
TP_TYPES.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 25.0!))
TP_TYPES.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 25.0!))
TP_TYPES.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 25.0!))
TP_TYPES.Controls.Add(Me.CH_FILTER_ALL, 0, 0)
TP_TYPES.Controls.Add(Me.CH_FILTER_SINGLE, 1, 0)
TP_TYPES.Controls.Add(Me.CH_FILTER_CHANNEL, 2, 0)
TP_TYPES.Controls.Add(Me.CH_FILTER_PLS, 3, 0)
TP_TYPES.Dock = System.Windows.Forms.DockStyle.Fill
TP_TYPES.Location = New System.Drawing.Point(0, 0)
TP_TYPES.Margin = New System.Windows.Forms.Padding(0)
TP_TYPES.Name = "TP_TYPES"
TP_TYPES.RowCount = 1
TP_TYPES.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
TP_TYPES.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
TP_TYPES.Size = New System.Drawing.Size(384, 25)
TP_TYPES.TabIndex = 0
'
'CH_FILTER_ALL
'
Me.CH_FILTER_ALL.AutoSize = True
Me.CH_FILTER_ALL.Dock = System.Windows.Forms.DockStyle.Fill
Me.CH_FILTER_ALL.Location = New System.Drawing.Point(3, 3)
Me.CH_FILTER_ALL.Name = "CH_FILTER_ALL"
Me.CH_FILTER_ALL.Size = New System.Drawing.Size(90, 19)
Me.CH_FILTER_ALL.TabIndex = 0
Me.CH_FILTER_ALL.Text = "ALL"
Me.CH_FILTER_ALL.UseVisualStyleBackColor = True
'
'CH_FILTER_SINGLE
'
Me.CH_FILTER_SINGLE.AutoSize = True
Me.CH_FILTER_SINGLE.Dock = System.Windows.Forms.DockStyle.Fill
Me.CH_FILTER_SINGLE.Location = New System.Drawing.Point(99, 3)
Me.CH_FILTER_SINGLE.Name = "CH_FILTER_SINGLE"
Me.CH_FILTER_SINGLE.Size = New System.Drawing.Size(90, 19)
Me.CH_FILTER_SINGLE.TabIndex = 1
Me.CH_FILTER_SINGLE.Text = "Single"
Me.CH_FILTER_SINGLE.UseVisualStyleBackColor = True
'
'CH_FILTER_CHANNEL
'
Me.CH_FILTER_CHANNEL.AutoSize = True
Me.CH_FILTER_CHANNEL.Dock = System.Windows.Forms.DockStyle.Fill
Me.CH_FILTER_CHANNEL.Location = New System.Drawing.Point(195, 3)
Me.CH_FILTER_CHANNEL.Name = "CH_FILTER_CHANNEL"
Me.CH_FILTER_CHANNEL.Size = New System.Drawing.Size(90, 19)
Me.CH_FILTER_CHANNEL.TabIndex = 2
Me.CH_FILTER_CHANNEL.Text = "Channel"
Me.CH_FILTER_CHANNEL.UseVisualStyleBackColor = True
'
'CH_FILTER_PLS
'
Me.CH_FILTER_PLS.AutoSize = True
Me.CH_FILTER_PLS.Dock = System.Windows.Forms.DockStyle.Fill
Me.CH_FILTER_PLS.Location = New System.Drawing.Point(291, 3)
Me.CH_FILTER_PLS.Name = "CH_FILTER_PLS"
Me.CH_FILTER_PLS.Size = New System.Drawing.Size(90, 19)
Me.CH_FILTER_PLS.TabIndex = 3
Me.CH_FILTER_PLS.Text = "Playlist"
Me.CH_FILTER_PLS.UseVisualStyleBackColor = True
'
'TP_MUSIC
'
Me.TP_MUSIC.ColumnCount = 3
Me.TP_MUSIC.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33333!))
Me.TP_MUSIC.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33333!))
Me.TP_MUSIC.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33333!))
Me.TP_MUSIC.Controls.Add(Me.CH_M_ALL, 0, 0)
Me.TP_MUSIC.Controls.Add(Me.CH_M_VIDEO, 1, 0)
Me.TP_MUSIC.Controls.Add(Me.CH_M_MUSIC, 2, 0)
Me.TP_MUSIC.Dock = System.Windows.Forms.DockStyle.Fill
Me.TP_MUSIC.Location = New System.Drawing.Point(0, 25)
Me.TP_MUSIC.Margin = New System.Windows.Forms.Padding(0)
Me.TP_MUSIC.Name = "TP_MUSIC"
Me.TP_MUSIC.RowCount = 1
Me.TP_MUSIC.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
Me.TP_MUSIC.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
Me.TP_MUSIC.Size = New System.Drawing.Size(384, 25)
Me.TP_MUSIC.TabIndex = 1
'
'CH_M_ALL
'
Me.CH_M_ALL.AutoSize = True
Me.CH_M_ALL.Dock = System.Windows.Forms.DockStyle.Fill
Me.CH_M_ALL.Location = New System.Drawing.Point(3, 3)
Me.CH_M_ALL.Name = "CH_M_ALL"
Me.CH_M_ALL.Size = New System.Drawing.Size(122, 19)
Me.CH_M_ALL.TabIndex = 0
Me.CH_M_ALL.Text = "ALL"
Me.CH_M_ALL.UseVisualStyleBackColor = True
'
'CH_M_VIDEO
'
Me.CH_M_VIDEO.AutoSize = True
Me.CH_M_VIDEO.Dock = System.Windows.Forms.DockStyle.Fill
Me.CH_M_VIDEO.Location = New System.Drawing.Point(131, 3)
Me.CH_M_VIDEO.Name = "CH_M_VIDEO"
Me.CH_M_VIDEO.Size = New System.Drawing.Size(122, 19)
Me.CH_M_VIDEO.TabIndex = 1
Me.CH_M_VIDEO.Text = "Video"
Me.CH_M_VIDEO.UseVisualStyleBackColor = True
'
'CH_M_MUSIC
'
Me.CH_M_MUSIC.AutoSize = True
Me.CH_M_MUSIC.Dock = System.Windows.Forms.DockStyle.Fill
Me.CH_M_MUSIC.Location = New System.Drawing.Point(259, 3)
Me.CH_M_MUSIC.Name = "CH_M_MUSIC"
Me.CH_M_MUSIC.Size = New System.Drawing.Size(122, 19)
Me.CH_M_MUSIC.TabIndex = 2
Me.CH_M_MUSIC.Text = "Music"
Me.CH_M_MUSIC.UseVisualStyleBackColor = True
'
'TP_USERS
'
TP_USERS.ColumnCount = 1
TP_USERS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
TP_USERS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20.0!))
TP_USERS.Controls.Add(Me.LIST_USERS, 0, 1)
TP_USERS.Controls.Add(TP_USERS_2, 0, 0)
TP_USERS.Dock = System.Windows.Forms.DockStyle.Fill
TP_USERS.Location = New System.Drawing.Point(0, 50)
TP_USERS.Margin = New System.Windows.Forms.Padding(0)
TP_USERS.Name = "TP_USERS"
TP_USERS.RowCount = 2
TP_USERS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
TP_USERS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
TP_USERS.Size = New System.Drawing.Size(384, 311)
TP_USERS.TabIndex = 3
'
'LIST_USERS
'
Me.LIST_USERS.Dock = System.Windows.Forms.DockStyle.Fill
Me.LIST_USERS.FormattingEnabled = True
Me.LIST_USERS.Location = New System.Drawing.Point(3, 28)
Me.LIST_USERS.Name = "LIST_USERS"
Me.LIST_USERS.Size = New System.Drawing.Size(378, 280)
Me.LIST_USERS.TabIndex = 1
'
'TP_USERS_2
'
TP_USERS_2.ColumnCount = 3
TP_USERS_2.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33333!))
TP_USERS_2.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33333!))
TP_USERS_2.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33333!))
TP_USERS_2.Controls.Add(Me.CH_USERS_USE, 0, 0)
TP_USERS_2.Controls.Add(Me.BTT_SELECT_ALL, 1, 0)
TP_USERS_2.Controls.Add(Me.BTT_SELECT_NONE, 2, 0)
TP_USERS_2.Dock = System.Windows.Forms.DockStyle.Fill
TP_USERS_2.Location = New System.Drawing.Point(0, 0)
TP_USERS_2.Margin = New System.Windows.Forms.Padding(0)
TP_USERS_2.Name = "TP_USERS_2"
TP_USERS_2.RowCount = 1
TP_USERS_2.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
TP_USERS_2.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
TP_USERS_2.Size = New System.Drawing.Size(384, 25)
TP_USERS_2.TabIndex = 0
'
'CH_USERS_USE
'
Me.CH_USERS_USE.AutoSize = True
Me.CH_USERS_USE.Dock = System.Windows.Forms.DockStyle.Fill
Me.CH_USERS_USE.Location = New System.Drawing.Point(3, 3)
Me.CH_USERS_USE.Name = "CH_USERS_USE"
Me.CH_USERS_USE.Size = New System.Drawing.Size(122, 19)
Me.CH_USERS_USE.TabIndex = 0
Me.CH_USERS_USE.Text = "Filter users"
Me.CH_USERS_USE.UseVisualStyleBackColor = True
'
'BTT_SELECT_ALL
'
Me.BTT_SELECT_ALL.Dock = System.Windows.Forms.DockStyle.Fill
Me.BTT_SELECT_ALL.Location = New System.Drawing.Point(129, 1)
Me.BTT_SELECT_ALL.Margin = New System.Windows.Forms.Padding(1)
Me.BTT_SELECT_ALL.Name = "BTT_SELECT_ALL"
Me.BTT_SELECT_ALL.Size = New System.Drawing.Size(126, 23)
Me.BTT_SELECT_ALL.TabIndex = 1
Me.BTT_SELECT_ALL.Text = "All"
Me.BTT_SELECT_ALL.UseVisualStyleBackColor = True
'
'BTT_SELECT_NONE
'
Me.BTT_SELECT_NONE.Dock = System.Windows.Forms.DockStyle.Fill
Me.BTT_SELECT_NONE.Location = New System.Drawing.Point(257, 1)
Me.BTT_SELECT_NONE.Margin = New System.Windows.Forms.Padding(1)
Me.BTT_SELECT_NONE.Name = "BTT_SELECT_NONE"
Me.BTT_SELECT_NONE.Size = New System.Drawing.Size(126, 23)
Me.BTT_SELECT_NONE.TabIndex = 2
Me.BTT_SELECT_NONE.Text = "None"
Me.BTT_SELECT_NONE.UseVisualStyleBackColor = True
'
'FilterForm
'
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
Me.ClientSize = New System.Drawing.Size(384, 361)
Me.Controls.Add(CONTAINER_MAIN)
Me.MinimizeBox = False
Me.MinimumSize = New System.Drawing.Size(400, 400)
Me.Name = "FilterForm"
Me.ShowInTaskbar = False
Me.Text = "Filter"
CONTAINER_MAIN.ContentPanel.ResumeLayout(False)
CONTAINER_MAIN.ResumeLayout(False)
CONTAINER_MAIN.PerformLayout()
TP_MAIN.ResumeLayout(False)
TP_TYPES.ResumeLayout(False)
TP_TYPES.PerformLayout()
Me.TP_MUSIC.ResumeLayout(False)
Me.TP_MUSIC.PerformLayout()
TP_USERS.ResumeLayout(False)
TP_USERS_2.ResumeLayout(False)
TP_USERS_2.PerformLayout()
Me.ResumeLayout(False)
End Sub
Private WithEvents CH_FILTER_ALL As CheckBox
Private WithEvents CH_FILTER_SINGLE As CheckBox
Private WithEvents CH_FILTER_CHANNEL As CheckBox
Private WithEvents CH_FILTER_PLS As CheckBox
Private WithEvents TP_MUSIC As TableLayoutPanel
Private WithEvents CH_M_ALL As CheckBox
Private WithEvents CH_M_VIDEO As CheckBox
Private WithEvents CH_M_MUSIC As CheckBox
Private WithEvents LIST_USERS As CheckedListBox
Private WithEvents CH_USERS_USE As CheckBox
Private WithEvents BTT_SELECT_ALL As Button
Private WithEvents BTT_SELECT_NONE As Button
End Class
End Namespace

View File

@@ -0,0 +1,135 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="CONTAINER_MAIN.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value>
</metadata>
<metadata name="TP_MAIN.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value>
</metadata>
<metadata name="TP_TYPES.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value>
</metadata>
<metadata name="TP_USERS.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value>
</metadata>
<metadata name="TP_USERS_2.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value>
</metadata>
</root>

View File

@@ -0,0 +1,181 @@
' 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.YouTube.Base
Imports SCrawler.API.YouTube.Objects
Imports PersonalUtilities.Forms
Imports SimpleUser = SCrawler.API.YouTube.Controls.YTDataFilter.SimpleUser
Namespace API.YouTube.Controls
Friend Class FilterForm
Private WithEvents MyDefs As DefaultFormOptions
Friend ReadOnly Property DATA As List(Of IYouTubeMediaContainer)
Private ReadOnly Property DataTMP As List(Of IYouTubeMediaContainer)
Private ReadOnly Property MyFilterTmp As YTDataFilter
Private ReadOnly Property MyTmpUsers As List(Of SimpleUser)
Friend Sub New(ByVal d As IEnumerable(Of IYouTubeMediaContainer))
InitializeComponent()
MyDefs = New DefaultFormOptions(Me, MyYouTubeSettings.DesignXml)
DATA = New List(Of IYouTubeMediaContainer)
DATA.ListAddList(d)
DataTMP = New List(Of IYouTubeMediaContainer)
DataTMP.ListAddList(d)
MyFilterTmp = New YTDataFilter(MyYouTubeSettings.FILTER)
MyTmpUsers = New List(Of SimpleUser)
Icon = My.Resources.FilterIcon
End Sub
Private Sub FilterForm_Load(sender As Object, e As EventArgs) Handles Me.Load
Try
With MyDefs
.MyViewInitialize()
.AddOkCancelToolbar(True)
With MyYouTubeSettings.FILTER
With .Types
If .Count = 0 Or (.Count = 1 AndAlso .Item(0) = YouTubeMediaType.Undefined) Then
CH_FILTER_ALL.Checked = True
Else
If .Contains(YouTubeMediaType.Single) Then CH_FILTER_SINGLE.Checked = True
If .Contains(YouTubeMediaType.Channel) Then CH_FILTER_CHANNEL.Checked = True
If .Contains(YouTubeMediaType.PlayList) Then CH_FILTER_PLS.Checked = True
End If
End With
If .IsMusic And .IsVideo Then
CH_M_ALL.Checked = True
Else
CH_M_MUSIC.Checked = .IsMusic
CH_M_VIDEO.Checked = .IsVideo
End If
CH_USERS_USE.Checked = .UserList.Count > 0
End With
.EndLoaderOperations()
_RefillEnabled = True
RefillList(True)
CH_USERS_USE_CheckedChanged()
End With
Catch ex As Exception
MyDefs.InvokeLoaderError(ex)
End Try
End Sub
Private Sub FilterForm_Disposed(sender As Object, e As EventArgs) Handles Me.Disposed
DATA.ListClearDispose
DataTMP.ListClearDispose
MyTmpUsers.ListClearDispose
End Sub
Private _RefillEnabled As Boolean = False
Private Sub RefillList(Optional ByVal Init As Boolean = False)
If Not MyDefs.Initializing And _RefillEnabled Then
ApplyFilter(MyFilterTmp, Init)
MyFilterTmp.Populate(DATA, DataTMP, CH_USERS_USE.Checked)
With DataTMP
If .Count > 0 Then
Dim tmpUsers As New List(Of SimpleUser)
tmpUsers.ListAddList(DataTMP, LAP.NotContainsOnly, CType(Function(obj As YouTubeMediaContainerBase) CType(obj, SimpleUser), Func(Of Object, Object)))
If tmpUsers.Count > 0 Then
tmpUsers.Sort()
LIST_USERS.BeginUpdate()
LIST_USERS.Items.Clear()
tmpUsers.ForEach(Sub(u) LIST_USERS.Items.Add(u, True))
If CH_USERS_USE.Checked And MyFilterTmp.UserList.Count > 0 Then
For i% = 0 To LIST_USERS.Items.Count - 1
LIST_USERS.SetItemChecked(i, MyFilterTmp.UserList.Contains(LIST_USERS.Items(i)))
Next
End If
LIST_USERS.EndUpdate()
End If
End If
End With
End If
End Sub
Private Sub ApplyFilter(ByRef Filter As YTDataFilter, ByVal Init As Boolean)
With Filter
.Reset(False, Not Init)
With .Types
If CH_FILTER_ALL.Checked Then
.Add(YouTubeMediaType.Undefined)
Else
If CH_FILTER_SINGLE.Checked Then .Add(YouTubeMediaType.Single)
If CH_FILTER_CHANNEL.Checked Then .Add(YouTubeMediaType.Channel)
If CH_FILTER_PLS.Checked Then .Add(YouTubeMediaType.PlayList)
End If
If .Count = 0 Then .Add(YouTubeMediaType.Undefined)
End With
If CH_M_ALL.Checked Then
.IsMusic = True
.IsVideo = True
Else
If CH_M_MUSIC.Checked Then .IsMusic = True
If CH_M_VIDEO.Checked Then .IsVideo = True
End If
If Not .IsVideo And Not .IsMusic Then .IsMusic = True : .IsVideo = True
If CH_USERS_USE.Checked And Not Init Then
.UserList.Clear()
If LIST_USERS.CheckedItems.Count > 0 Then
For Each item In LIST_USERS.CheckedItems : .UserList.Add(item) : Next
End If
End If
End With
End Sub
Private Sub MyDefs_ButtonOkClick(ByVal Sender As Object, ByVal e As KeyHandleEventArgs) Handles MyDefs.ButtonOkClick
ApplyFilter(MyYouTubeSettings.FILTER, False)
MyYouTubeSettings.FILTER.RemoveAll(DATA)
MyYouTubeSettings.FILTER.Update()
MyDefs.CloseForm()
End Sub
Private Sub MyDefs_ButtonDeleteClickOC(ByVal Sender As Object, ByVal e As KeyHandleEventArgs) Handles MyDefs.ButtonDeleteClickOC
With MyYouTubeSettings.FILTER : .Reset() : .Update() : End With
MyDefs.CloseForm()
End Sub
Private Sub UpdateControlOptions(ByRef CNT As CheckBox, ByVal v As Boolean)
If v Then CNT.Checked = True
CNT.Enabled = Not v
End Sub
Private Sub CH_FILTER_ALL_CheckedChanged(sender As Object, e As EventArgs) Handles CH_FILTER_ALL.CheckedChanged
_RefillEnabled = False
Dim v As Boolean = CH_FILTER_ALL.Checked
UpdateControlOptions(CH_FILTER_SINGLE, v)
UpdateControlOptions(CH_FILTER_CHANNEL, v)
UpdateControlOptions(CH_FILTER_PLS, v)
_RefillEnabled = True
RefillList()
End Sub
Private Sub CH_FILTER_SINGLE_CHANNEL_PLS_CheckedChanged(sender As Object, e As EventArgs) Handles CH_FILTER_SINGLE.CheckedChanged,
CH_FILTER_CHANNEL.CheckedChanged,
CH_FILTER_PLS.CheckedChanged
RefillList()
End Sub
Private Sub CH_M_ALL_CheckedChanged(sender As Object, e As EventArgs) Handles CH_M_ALL.CheckedChanged
_RefillEnabled = False
Dim v As Boolean = CH_M_ALL.Checked
UpdateControlOptions(CH_M_VIDEO, v)
UpdateControlOptions(CH_M_MUSIC, v)
_RefillEnabled = True
RefillList()
End Sub
Private Sub CH_M_VIDEO_MUSIC_CheckedChanged(sender As Object, e As EventArgs) Handles CH_M_VIDEO.CheckedChanged, CH_M_MUSIC.CheckedChanged
RefillList()
End Sub
Private Sub CH_USERS_USE_CheckedChanged() Handles CH_USERS_USE.CheckedChanged
LIST_USERS.Enabled = CH_USERS_USE.Checked
BTT_SELECT_ALL.Enabled = CH_USERS_USE.Checked
BTT_SELECT_NONE.Enabled = CH_USERS_USE.Checked
End Sub
Private Sub BTT_SELECT_ALL_NONE_Click(sender As Object, e As EventArgs) Handles BTT_SELECT_ALL.Click, BTT_SELECT_NONE.Click
Dim checked As Boolean = sender Is BTT_SELECT_ALL
With LIST_USERS
If .Items.Count > 0 Then
For i% = 0 To .Items.Count - 1 : .SetItemChecked(i, checked) : Next
End If
End With
End Sub
End Class
End Namespace

View File

@@ -155,7 +155,7 @@ Namespace API.YouTube.Controls
With DirectCast(MyContainer, YouTubeMediaContainerBase)
Select Case Sender.DefaultButton
Case ADB.Open
Using f As New SimpleListForm(Of String)(IIf(isLyrics, AvailableSubtitlesFormats, AvailableAudioFormats)) With {
Using f As New SimpleListForm(Of String)(If(isLyrics, AvailableSubtitlesFormats, AvailableAudioFormats)) With {
.DesignXML = DesignXML,
.DesignXMLNodeName = SimpleArraysFormNode,
.FormText = DirectCast(e.AssociatedControl, TextBoxExtended).CaptionText,

View File

@@ -0,0 +1,274 @@
' 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
Namespace API.YouTube.Controls
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()>
Partial Friend Class TrimOptionForm : Inherits System.Windows.Forms.Form
<System.Diagnostics.DebuggerNonUserCode()>
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
Try
If disposing AndAlso components IsNot Nothing Then
components.Dispose()
End If
Finally
MyBase.Dispose(disposing)
End Try
End Sub
Private components As System.ComponentModel.IContainer
<System.Diagnostics.DebuggerStepThrough()>
Private Sub InitializeComponent()
Dim CONTAINER_MAIN As System.Windows.Forms.ToolStripContainer
Dim TP_MAIN As System.Windows.Forms.TableLayoutPanel
Dim ActionButton1 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(TrimOptionForm))
Dim TP_OPTIONS As System.Windows.Forms.TableLayoutPanel
Me.TXT_NAME = New PersonalUtilities.Forms.Controls.TextBoxExtended()
Me.TP_TIME_TIME = New System.Windows.Forms.TableLayoutPanel()
Me.TXT_FROM_DATE = New PersonalUtilities.Forms.Controls.TextBoxExtended()
Me.TXT_TO_DATE = New PersonalUtilities.Forms.Controls.TextBoxExtended()
Me.OPT_INT = New System.Windows.Forms.RadioButton()
Me.OPT_TIME = New System.Windows.Forms.RadioButton()
Me.TP_TIME_INT = New System.Windows.Forms.TableLayoutPanel()
Me.TXT_FROM_INT = New PersonalUtilities.Forms.Controls.TextBoxExtended()
Me.TXT_TO_INT = New PersonalUtilities.Forms.Controls.TextBoxExtended()
CONTAINER_MAIN = New System.Windows.Forms.ToolStripContainer()
TP_MAIN = New System.Windows.Forms.TableLayoutPanel()
TP_OPTIONS = New System.Windows.Forms.TableLayoutPanel()
CONTAINER_MAIN.ContentPanel.SuspendLayout()
CONTAINER_MAIN.SuspendLayout()
TP_MAIN.SuspendLayout()
CType(Me.TXT_NAME, System.ComponentModel.ISupportInitialize).BeginInit()
TP_OPTIONS.SuspendLayout()
Me.TP_TIME_TIME.SuspendLayout()
CType(Me.TXT_FROM_DATE, System.ComponentModel.ISupportInitialize).BeginInit()
CType(Me.TXT_TO_DATE, System.ComponentModel.ISupportInitialize).BeginInit()
Me.TP_TIME_INT.SuspendLayout()
CType(Me.TXT_FROM_INT, System.ComponentModel.ISupportInitialize).BeginInit()
CType(Me.TXT_TO_INT, System.ComponentModel.ISupportInitialize).BeginInit()
Me.SuspendLayout()
'
'CONTAINER_MAIN
'
'
'CONTAINER_MAIN.ContentPanel
'
CONTAINER_MAIN.ContentPanel.Controls.Add(TP_MAIN)
CONTAINER_MAIN.ContentPanel.Size = New System.Drawing.Size(334, 112)
CONTAINER_MAIN.Dock = System.Windows.Forms.DockStyle.Fill
CONTAINER_MAIN.LeftToolStripPanelVisible = False
CONTAINER_MAIN.Location = New System.Drawing.Point(0, 0)
CONTAINER_MAIN.Name = "CONTAINER_MAIN"
CONTAINER_MAIN.RightToolStripPanelVisible = False
CONTAINER_MAIN.Size = New System.Drawing.Size(334, 112)
CONTAINER_MAIN.TabIndex = 0
CONTAINER_MAIN.TopToolStripPanelVisible = False
'
'TP_MAIN
'
TP_MAIN.ColumnCount = 1
TP_MAIN.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
TP_MAIN.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20.0!))
TP_MAIN.Controls.Add(Me.TXT_NAME, 0, 0)
TP_MAIN.Controls.Add(TP_OPTIONS, 0, 1)
TP_MAIN.Dock = System.Windows.Forms.DockStyle.Fill
TP_MAIN.Location = New System.Drawing.Point(0, 0)
TP_MAIN.Name = "TP_MAIN"
TP_MAIN.RowCount = 2
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
TP_MAIN.Size = New System.Drawing.Size(334, 112)
TP_MAIN.TabIndex = 0
'
'TXT_NAME
'
ActionButton1.BackgroundImage = CType(resources.GetObject("ActionButton1.BackgroundImage"), System.Drawing.Image)
ActionButton1.Name = "Clear"
ActionButton1.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
Me.TXT_NAME.Buttons.Add(ActionButton1)
Me.TXT_NAME.CaptionText = "Name"
Me.TXT_NAME.CaptionToolTipText = "Section name"
Me.TXT_NAME.CaptionWidth = 50.0R
Me.TXT_NAME.Dock = System.Windows.Forms.DockStyle.Fill
Me.TXT_NAME.Location = New System.Drawing.Point(3, 3)
Me.TXT_NAME.Name = "TXT_NAME"
Me.TXT_NAME.Size = New System.Drawing.Size(328, 22)
Me.TXT_NAME.TabIndex = 0
'
'TP_OPTIONS
'
TP_OPTIONS.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.[Single]
TP_OPTIONS.ColumnCount = 2
TP_OPTIONS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 30.0!))
TP_OPTIONS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
TP_OPTIONS.Controls.Add(Me.TP_TIME_TIME, 1, 1)
TP_OPTIONS.Controls.Add(Me.OPT_INT, 0, 0)
TP_OPTIONS.Controls.Add(Me.OPT_TIME, 0, 1)
TP_OPTIONS.Controls.Add(Me.TP_TIME_INT, 1, 0)
TP_OPTIONS.Dock = System.Windows.Forms.DockStyle.Fill
TP_OPTIONS.Location = New System.Drawing.Point(3, 31)
TP_OPTIONS.Name = "TP_OPTIONS"
TP_OPTIONS.RowCount = 3
TP_OPTIONS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
TP_OPTIONS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
TP_OPTIONS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
TP_OPTIONS.Size = New System.Drawing.Size(328, 78)
TP_OPTIONS.TabIndex = 1
'
'TP_TIME_TIME
'
Me.TP_TIME_TIME.ColumnCount = 2
Me.TP_TIME_TIME.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50.0!))
Me.TP_TIME_TIME.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50.0!))
Me.TP_TIME_TIME.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20.0!))
Me.TP_TIME_TIME.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20.0!))
Me.TP_TIME_TIME.Controls.Add(Me.TXT_FROM_DATE, 0, 0)
Me.TP_TIME_TIME.Controls.Add(Me.TXT_TO_DATE, 1, 0)
Me.TP_TIME_TIME.Dock = System.Windows.Forms.DockStyle.Fill
Me.TP_TIME_TIME.Location = New System.Drawing.Point(32, 27)
Me.TP_TIME_TIME.Margin = New System.Windows.Forms.Padding(0)
Me.TP_TIME_TIME.Name = "TP_TIME_TIME"
Me.TP_TIME_TIME.RowCount = 1
Me.TP_TIME_TIME.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
Me.TP_TIME_TIME.Size = New System.Drawing.Size(295, 25)
Me.TP_TIME_TIME.TabIndex = 3
'
'TXT_FROM_DATE
'
Me.TXT_FROM_DATE.CaptionPadding = New System.Windows.Forms.Padding(0, 0, 6, 0)
Me.TXT_FROM_DATE.CaptionText = "From"
Me.TXT_FROM_DATE.CaptionWidth = 40.0R
Me.TXT_FROM_DATE.Dock = System.Windows.Forms.DockStyle.Fill
Me.TXT_FROM_DATE.Location = New System.Drawing.Point(3, 3)
Me.TXT_FROM_DATE.Name = "TXT_FROM_DATE"
Me.TXT_FROM_DATE.PlaceholderEnabled = True
Me.TXT_FROM_DATE.PlaceholderText = "h:mm:ss"
Me.TXT_FROM_DATE.Size = New System.Drawing.Size(141, 22)
Me.TXT_FROM_DATE.TabIndex = 0
'
'TXT_TO_DATE
'
Me.TXT_TO_DATE.CaptionPadding = New System.Windows.Forms.Padding(0, 0, 6, 0)
Me.TXT_TO_DATE.CaptionText = "To"
Me.TXT_TO_DATE.CaptionWidth = 40.0R
Me.TXT_TO_DATE.Dock = System.Windows.Forms.DockStyle.Fill
Me.TXT_TO_DATE.Location = New System.Drawing.Point(150, 3)
Me.TXT_TO_DATE.Name = "TXT_TO_DATE"
Me.TXT_TO_DATE.PlaceholderEnabled = True
Me.TXT_TO_DATE.PlaceholderText = "h:mm:ss"
Me.TXT_TO_DATE.Size = New System.Drawing.Size(142, 22)
Me.TXT_TO_DATE.TabIndex = 1
'
'OPT_INT
'
Me.OPT_INT.AutoSize = True
Me.OPT_INT.CheckAlign = System.Drawing.ContentAlignment.MiddleCenter
Me.OPT_INT.Dock = System.Windows.Forms.DockStyle.Fill
Me.OPT_INT.Location = New System.Drawing.Point(4, 4)
Me.OPT_INT.Name = "OPT_INT"
Me.OPT_INT.Size = New System.Drawing.Size(24, 19)
Me.OPT_INT.TabIndex = 0
Me.OPT_INT.TabStop = True
Me.OPT_INT.UseVisualStyleBackColor = True
'
'OPT_TIME
'
Me.OPT_TIME.AutoSize = True
Me.OPT_TIME.CheckAlign = System.Drawing.ContentAlignment.MiddleCenter
Me.OPT_TIME.Dock = System.Windows.Forms.DockStyle.Fill
Me.OPT_TIME.Location = New System.Drawing.Point(4, 30)
Me.OPT_TIME.Name = "OPT_TIME"
Me.OPT_TIME.Size = New System.Drawing.Size(24, 19)
Me.OPT_TIME.TabIndex = 1
Me.OPT_TIME.TabStop = True
Me.OPT_TIME.UseVisualStyleBackColor = True
'
'TP_TIME_INT
'
Me.TP_TIME_INT.ColumnCount = 2
Me.TP_TIME_INT.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50.0!))
Me.TP_TIME_INT.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50.0!))
Me.TP_TIME_INT.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20.0!))
Me.TP_TIME_INT.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20.0!))
Me.TP_TIME_INT.Controls.Add(Me.TXT_FROM_INT, 0, 0)
Me.TP_TIME_INT.Controls.Add(Me.TXT_TO_INT, 1, 0)
Me.TP_TIME_INT.Dock = System.Windows.Forms.DockStyle.Fill
Me.TP_TIME_INT.Location = New System.Drawing.Point(32, 1)
Me.TP_TIME_INT.Margin = New System.Windows.Forms.Padding(0)
Me.TP_TIME_INT.Name = "TP_TIME_INT"
Me.TP_TIME_INT.RowCount = 1
Me.TP_TIME_INT.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
Me.TP_TIME_INT.Size = New System.Drawing.Size(295, 25)
Me.TP_TIME_INT.TabIndex = 2
'
'TXT_FROM_INT
'
Me.TXT_FROM_INT.CaptionPadding = New System.Windows.Forms.Padding(0, 0, 6, 0)
Me.TXT_FROM_INT.CaptionText = "From"
Me.TXT_FROM_INT.CaptionWidth = 40.0R
Me.TXT_FROM_INT.Dock = System.Windows.Forms.DockStyle.Fill
Me.TXT_FROM_INT.Location = New System.Drawing.Point(3, 3)
Me.TXT_FROM_INT.Name = "TXT_FROM_INT"
Me.TXT_FROM_INT.Size = New System.Drawing.Size(141, 22)
Me.TXT_FROM_INT.TabIndex = 0
'
'TXT_TO_INT
'
Me.TXT_TO_INT.CaptionPadding = New System.Windows.Forms.Padding(0, 0, 6, 0)
Me.TXT_TO_INT.CaptionText = "To"
Me.TXT_TO_INT.CaptionWidth = 40.0R
Me.TXT_TO_INT.Dock = System.Windows.Forms.DockStyle.Fill
Me.TXT_TO_INT.Location = New System.Drawing.Point(150, 3)
Me.TXT_TO_INT.Name = "TXT_TO_INT"
Me.TXT_TO_INT.Size = New System.Drawing.Size(142, 22)
Me.TXT_TO_INT.TabIndex = 1
'
'TrimOptionForm
'
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
Me.ClientSize = New System.Drawing.Size(334, 112)
Me.Controls.Add(CONTAINER_MAIN)
Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle
Me.KeyPreview = True
Me.MaximizeBox = False
Me.MaximumSize = New System.Drawing.Size(350, 151)
Me.MinimizeBox = False
Me.MinimumSize = New System.Drawing.Size(350, 151)
Me.Name = "TrimOptionForm"
Me.ShowIcon = False
Me.ShowInTaskbar = False
Me.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide
Me.Text = "Trim option"
CONTAINER_MAIN.ContentPanel.ResumeLayout(False)
CONTAINER_MAIN.ResumeLayout(False)
CONTAINER_MAIN.PerformLayout()
TP_MAIN.ResumeLayout(False)
CType(Me.TXT_NAME, System.ComponentModel.ISupportInitialize).EndInit()
TP_OPTIONS.ResumeLayout(False)
TP_OPTIONS.PerformLayout()
Me.TP_TIME_TIME.ResumeLayout(False)
CType(Me.TXT_FROM_DATE, System.ComponentModel.ISupportInitialize).EndInit()
CType(Me.TXT_TO_DATE, System.ComponentModel.ISupportInitialize).EndInit()
Me.TP_TIME_INT.ResumeLayout(False)
CType(Me.TXT_FROM_INT, System.ComponentModel.ISupportInitialize).EndInit()
CType(Me.TXT_TO_INT, System.ComponentModel.ISupportInitialize).EndInit()
Me.ResumeLayout(False)
End Sub
Private WithEvents TXT_NAME As PersonalUtilities.Forms.Controls.TextBoxExtended
Private WithEvents OPT_INT As RadioButton
Private WithEvents OPT_TIME As RadioButton
Private WithEvents TP_TIME_INT As TableLayoutPanel
Private WithEvents TP_TIME_TIME As TableLayoutPanel
Private WithEvents TXT_FROM_DATE As PersonalUtilities.Forms.Controls.TextBoxExtended
Private WithEvents TXT_TO_DATE As PersonalUtilities.Forms.Controls.TextBoxExtended
Private WithEvents TXT_FROM_INT As PersonalUtilities.Forms.Controls.TextBoxExtended
Private WithEvents TXT_TO_INT As PersonalUtilities.Forms.Controls.TextBoxExtended
End Class
End Namespace

View File

@@ -0,0 +1,138 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="CONTAINER_MAIN.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value>
</metadata>
<metadata name="TP_MAIN.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" />
<data name="ActionButton1.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
vgAADr4B6kKxwAAAAHpJREFUOE+1kVEKgDAMQ3e2/e+MXqpn6W/HxM7SpkIVB4HxSMKiTUTaFwVQ1X25
DjMfSxskHBYsAxHJkjUjHgrUNMY4peaMPxb03rcZMVhgn2oDKAwn+L0aROH/Cny4NAGFSx8x+10ZDwV2
gt+LOCxQsw1nPBS8VQBVTTzyhrdZSUm7AAAAAElFTkSuQmCC
</value>
</data>
<metadata name="TP_OPTIONS.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value>
</metadata>
</root>

View File

@@ -0,0 +1,79 @@
' 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.Forms
Imports PersonalUtilities.Forms.Toolbars
Imports SCrawler.API.YouTube.Base
Namespace API.YouTube.Controls
Friend Class TrimOptionForm
Private WithEvents MyDefs As DefaultFormOptions
Friend Property MyTrimOption As TrimOption
Private ReadOnly FieldsDateProvider As New CustomProvider(Function(v) AConvert(Of TimeSpan)(v, Nothing, EDP.ReturnValue))
Private ReadOnly TCE As New ErrorsDescriber(False, False, False, New TimeSpan)
Friend Sub New(Optional ByVal Opt As TrimOption = Nothing)
InitializeComponent()
MyDefs = New DefaultFormOptions(Me, MyYouTubeSettings.DesignXml)
MyTrimOption = Opt
End Sub
Private Sub TrimOptionForm_Load(sender As Object, e As EventArgs) Handles Me.Load
Try
With MyDefs
.MyViewInitialize()
.AddOkCancelToolbar()
TXT_NAME.Text = MyTrimOption.Name
TXT_FROM_INT.Text = MyTrimOption.Start
TXT_TO_INT.Text = MyTrimOption.End
OPT_INT.Checked = True
.MyFieldsCheckerE = New FieldsChecker
With .MyFieldsCheckerE
.AddControl(Of Integer)(TXT_FROM_INT, "From")
.AddControl(Of Integer)(TXT_TO_INT, "To")
.AddControl(Of String)(TXT_FROM_DATE, "From (time)",, FieldsDateProvider)
.AddControl(Of String)(TXT_TO_DATE, "To (time)",, FieldsDateProvider)
.EndLoaderOperations()
End With
.EndLoaderOperations()
End With
Catch ex As Exception
MyDefs.InvokeLoaderError(ex)
End Try
End Sub
Private Sub MyDefs_ButtonOkClick(ByVal Sender As Object, ByVal e As KeyHandleEventArgs) Handles MyDefs.ButtonOkClick
If MyDefs.MyFieldsChecker.AllParamsOK Then
MyTrimOption = New TrimOption With {
.Name = TXT_NAME.Text,
.Start = AConvert(Of Integer)(TXT_FROM_INT.Text, 0),
.[End] = AConvert(Of Integer)(TXT_TO_INT.Text, 0)
}
MyDefs.CloseForm()
End If
End Sub
Private Sub OPT_INT_TIME_CheckedChanged(sender As Object, e As EventArgs) Handles OPT_INT.CheckedChanged, OPT_TIME.CheckedChanged
TP_TIME_INT.Enabled = OPT_INT.Checked
TP_TIME_TIME.Enabled = OPT_TIME.Checked
End Sub
Private _TextHandlersEnabled As Boolean = True
Private Sub TXT_FROM_TO_TextChanged(sender As Object, e As EventArgs) Handles TXT_FROM_INT.ActionOnTextChanged, TXT_TO_INT.ActionOnTextChanged,
TXT_FROM_DATE.ActionOnTextChanged, TXT_TO_DATE.ActionOnTextChanged
If _TextHandlersEnabled Then
_TextHandlersEnabled = False
Select Case DirectCast(sender, Control).Name
Case TXT_FROM_INT.Name : TXT_FROM_DATE.Value = AConvert(Of String)(TimeSpan.FromSeconds(AConvert(Of Integer)(TXT_FROM_INT.Text, 0)), TimeToStringProviderH)
Case TXT_TO_INT.Name : TXT_TO_DATE.Value = AConvert(Of String)(TimeSpan.FromSeconds(AConvert(Of Integer)(TXT_TO_INT.Text, 0)), TimeToStringProviderH)
Case TXT_FROM_DATE.Name : TXT_FROM_INT.Text = CInt(CType(AConvert(Of TimeSpan)(TXT_FROM_DATE.Text, TCE), TimeSpan).TotalSeconds)
Case TXT_TO_DATE.Name : TXT_TO_INT.Text = CInt(CType(AConvert(Of TimeSpan)(TXT_TO_DATE.Text, TCE), TimeSpan).TotalSeconds)
End Select
_TextHandlersEnabled = True
End If
End Sub
End Class
End Namespace

View File

@@ -73,6 +73,7 @@ Namespace API.YouTube.Controls
Me.OPT_VIDEO = New System.Windows.Forms.RadioButton()
Me.OPT_AUDIO = New System.Windows.Forms.RadioButton()
Me.LBL_AUDIO_CODEC = New System.Windows.Forms.Label()
Me.BTT_TRIM = New System.Windows.Forms.Button()
Me.TXT_FPS = New PersonalUtilities.Forms.Controls.TextBoxExtended()
Me.TXT_AUDIO_BITRATE = New PersonalUtilities.Forms.Controls.TextBoxExtended()
Me.TP_HEADER_BASE = New System.Windows.Forms.TableLayoutPanel()
@@ -545,20 +546,32 @@ Namespace API.YouTube.Controls
Me.LBL_AUDIO_CODEC.TextAlign = System.Drawing.ContentAlignment.MiddleRight
TT_MAIN.SetToolTip(Me.LBL_AUDIO_CODEC, "Output Audio Codec")
'
'BTT_TRIM
'
Me.BTT_TRIM.Dock = System.Windows.Forms.DockStyle.Fill
Me.BTT_TRIM.Location = New System.Drawing.Point(541, 3)
Me.BTT_TRIM.Name = "BTT_TRIM"
Me.BTT_TRIM.Size = New System.Drawing.Size(45, 22)
Me.BTT_TRIM.TabIndex = 2
Me.BTT_TRIM.Text = "Trim"
TT_MAIN.SetToolTip(Me.BTT_TRIM, "Trim the file by setting trimming options and/or selecting chapters")
Me.BTT_TRIM.UseVisualStyleBackColor = True
'
'TP_FPS_BITRATE
'
TP_FPS_BITRATE.ColumnCount = 2
TP_FPS_BITRATE.ColumnCount = 3
TP_FPS_BITRATE.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50.0!))
TP_FPS_BITRATE.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50.0!))
TP_FPS_BITRATE.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 51.0!))
TP_FPS_BITRATE.Controls.Add(Me.TXT_FPS, 0, 0)
TP_FPS_BITRATE.Controls.Add(Me.TXT_AUDIO_BITRATE, 1, 0)
TP_FPS_BITRATE.Controls.Add(Me.BTT_TRIM, 2, 0)
TP_FPS_BITRATE.Dock = System.Windows.Forms.DockStyle.Fill
TP_FPS_BITRATE.Location = New System.Drawing.Point(6, 93)
TP_FPS_BITRATE.Margin = New System.Windows.Forms.Padding(6, 0, 6, 0)
TP_FPS_BITRATE.Name = "TP_FPS_BITRATE"
TP_FPS_BITRATE.RowCount = 1
TP_FPS_BITRATE.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
TP_FPS_BITRATE.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
TP_FPS_BITRATE.Size = New System.Drawing.Size(589, 28)
TP_FPS_BITRATE.TabIndex = 6
'
@@ -577,7 +590,7 @@ Namespace API.YouTube.Controls
Me.TXT_FPS.Location = New System.Drawing.Point(3, 2)
Me.TXT_FPS.Margin = New System.Windows.Forms.Padding(3, 2, 3, 3)
Me.TXT_FPS.Name = "TXT_FPS"
Me.TXT_FPS.Size = New System.Drawing.Size(288, 22)
Me.TXT_FPS.Size = New System.Drawing.Size(263, 22)
Me.TXT_FPS.TabIndex = 0
Me.TXT_FPS.TextBoxWidthMinimal = 30
'
@@ -593,9 +606,9 @@ Namespace API.YouTube.Controls
" to change."
Me.TXT_AUDIO_BITRATE.CaptionWidth = 75.0R
Me.TXT_AUDIO_BITRATE.Dock = System.Windows.Forms.DockStyle.Fill
Me.TXT_AUDIO_BITRATE.Location = New System.Drawing.Point(297, 3)
Me.TXT_AUDIO_BITRATE.Location = New System.Drawing.Point(272, 3)
Me.TXT_AUDIO_BITRATE.Name = "TXT_AUDIO_BITRATE"
Me.TXT_AUDIO_BITRATE.Size = New System.Drawing.Size(289, 22)
Me.TXT_AUDIO_BITRATE.Size = New System.Drawing.Size(263, 22)
Me.TXT_AUDIO_BITRATE.TabIndex = 1
'
'TP_HEADER_BASE
@@ -920,5 +933,6 @@ Namespace API.YouTube.Controls
Private WithEvents CMB_PLS As PersonalUtilities.Forms.Controls.ComboBoxExtended
Private WithEvents BTT_PLS_BROWSE As SCrawler.API.YouTube.Controls.ButtonRC
Private WithEvents TXT_AUDIO_BITRATE As PersonalUtilities.Forms.Controls.TextBoxExtended
Private WithEvents BTT_TRIM As Button
End Class
End Namespace

View File

@@ -139,98 +139,96 @@
<data name="ActionButton1.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wwAADsMBx2+oZAAAAFFJREFUOE9joAr49u3bf1Lw169f50O1QgBI0MnJCY4/vP8Ix8hiILqtrQ3TEFIM
AGGYIVDtpBsAwkQbgIyR1dDWAGLwqAGD0gByMFQ7JYCBAQChNviRiQ8ETwAAAABJRU5ErkJggg==
vAAADrwBlbxySQAAAEhJREFUOE9jYKAG+Pbt239S8NevX+djGODk5ATHH95/hGNkMRDd1taGaQgpBiAb
QrYBIEy0AdgMo70BxOBRAwalAeRguAGUAAChNviRcuLCdwAAAABJRU5ErkJggg==
</value>
</data>
<data name="ActionButton2.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
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
U1wTEVFuCACwydqqfmZO8Z3yxXrc6T7f73o/AEyQiDgtp/QXG/EpgTbFF3JKv1D+TABgQnQfx2ur+POc
4kflC/kx5u6c4l+blF7YXWoofw4AMIG6W/G2i/Wzckp/lVP896EX9PJFvkh3M5/6o03E7+acF8vvCQBM
mQO/EFTVctfezxEXtlX9vLaqX9z9s6mqC5YWF+fLrwEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6LEtW/4flgYiLD1qeX0A
AAAASUVORK5CYII=
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAYIj+H5YGIizEb/aEAAAAAElFTkSuQmCC
</value>
</data>
<metadata name="TT_MAIN.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
@@ -248,115 +246,113 @@
<data name="ActionButton3.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wwAADsMBx2+oZAAAAFFJREFUOE9joAr49u3bf1Lw169f50O1QgBI0MnJCY4/vP8Ix8hiILqtrQ3TEFIM
AGGYIVDtpBsAwkQbgIyR1dDWAGLwqAGD0gByMFQ7JYCBAQChNviRiQ8ETwAAAABJRU5ErkJggg==
vAAADrwBlbxySQAAAEhJREFUOE9jYKAG+Pbt239S8NevX+djGODk5ATHH95/hGNkMRDd1taGaQgpBiAb
QrYBIEy0AdgMo70BxOBRAwalAeRguAGUAAChNviRcuLCdwAAAABJRU5ErkJggg==
</value>
</data>
<data name="ActionButton4.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6
JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAeElE
QVQ4T2P4//8/RRhMFHQfKgDi/yAaXQEhDCZAmkNbnvyXta4CciESLEws//FhmDqYAQUgzUBMngsowVgF
ScFgYjQQsUsQi8FEYsXyAiD+D6LRFRDCYAKk2bPo6H9J40wgFyKBLeCQMUwdzIACkGYgHnKB+J8BAD5Q
tqhi4tzWAAAAAElFTkSuQmCC
JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsMAAALDAE/QCLIAAAAd0lE
QVQ4T2P4//8/AyUYTBR0Hyoo6D70H0SjKyCEYQb8D2158l/Wuuo/TIKFieU/PoxuQAFIs6x1FXkuoARj
CJCKwcRoIGIKkoLBRGLF8oLEiuX/QTS6AkIYZsB/z6Kj/yWNM8kLRJDNIM2SxpnkuYASjCFAKgYAPlC2
qCS4LQgAAAAASUVORK5CYII=
</value>
</data>
<data name="ActionButton5.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
vAAADrwBlbxySQAAAHpJREFUOE+1kVEKgDAMQ3e2/e+MXqpn6W/HxM7SpkIVB4HxSMKiTUTaFwVQ1X25
DjMfSxskHBYsAxHJkjUjHgrUNMY4peaMPxb03rcZMVhgn2oDKAwn+L0aROH/Cny4NAGFSx8x+10ZDwV2
gt+LOCxQsw1nPBS8VQBVTTzyhrdZSUm7AAAAAElFTkSuQmCC
</value>
</data>
<data name="ActionButton6.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
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
U1wTEVFuCACwydqqfmZO8Z3yxXrc6T7f73o/AEyQiDgtp/QXG/EpgTbFF3JKv1D+TABgQnQfx2ur+POc
4kflC/kx5u6c4l+blF7YXWoofw4AMIG6W/G2i/Wzckp/lVP896EX9PJFvkh3M5/6o03E7+acF8vvCQBM
mQO/EFTVctfezxEXtlX9vLaqX9z9s6mqC5YWF+fLrwEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6LEtW/4flgYiLD1qeX0A
AAAASUVORK5CYII=
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAYIj+H5YGIizEb/aEAAAAAElFTkSuQmCC
</value>
</data>
<metadata name="LB_SEP_1.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
@@ -383,122 +379,122 @@
<data name="ActionButton7.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
vAAADrwBlbxySQAAAHpJREFUOE+1kVEKgDAMQ3e2/e+MXqpn6W/HxM7SpkIVB4HxSMKiTUTaFwVQ1X25
DjMfSxskHBYsAxHJkjUjHgrUNMY4peaMPxb03rcZMVhgn2oDKAwn+L0aROH/Cny4NAGFSx8x+10ZDwV2
gt+LOCxQsw1nPBS8VQBVTTzyhrdZSUm7AAAAAElFTkSuQmCC
</value>
</data>
<data name="ActionButton8.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
vAAADrwBlbxySQAAAHpJREFUOE+1kVEKgDAMQ3e2/e+MXqpn6W/HxM7SpkIVB4HxSMKiTUTaFwVQ1X25
DjMfSxskHBYsAxHJkjUjHgrUNMY4peaMPxb03rcZMVhgn2oDKAwn+L0aROH/Cny4NAGFSx8x+10ZDwV2
gt+LOCxQsw1nPBS8VQBVTTzyhrdZSUm7AAAAAElFTkSuQmCC
</value>
</data>
<data name="ActionButton9.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP
WQwhyWIyJIUW5NqyPb7oCVtIlhVTwYf8nv7/t2zJagel9KmqKsIACYL9RjI8UHz5zshougZr/AEvbxEP
aZCDBY3VslixaJvX3wzkkDiOwbZtDRGA5vdNAg+TL27qgmt5XkBG/gTdAG7Gt+3PP9oOaEGFCVEC6rp+
5g9MfM/c5e4OsEZMZkQEtGL5H2DdZ5JRArDwPA+iKII0TfkC9vroC9j5vq8JTWw3WzWgLMtZGIaa0MR8
vlAD8PYlSaIJTTiOowY0p0Bc19XEJo6HE59FAPuMzyAINKGJ1XLFZxHALtMrnkBXOIQIIIQ8YvF/KrgB
cMaRN0UdBBkAAAAASUVORK5CYII=
vAAADrwBlbxySQAAARlJREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbWujg3dATZPKYZC6BQhvw1AMkg3dP
XQyl7WIyJIEW5CbS0/jKE5GwpCghgg9s6/8/y5Kj6DA45zcAwAAAezB6rjNnB4XX244NHt8wGs7wblop
yRGxwZQBYKIfbn477EvqusY4jj2MgMpPiwav7l9UyYXmdrs9duzP4ApUmd72sfrxVsD33JQISyClvFUX
w9nJssvJFei9CJUtgQ7394Du3YKLJaCbLMuwqips21ZNuDve/35X8J7nuRcMsVwsbYEQYlSWpRcMMR5P
bAH9fU3TeMEQSZLYgsMpsDRNvXCIr89vWyCEeC6KwguGmL/ObYGU8oFOwA2ewwgYY9f6f7iUf3DGkTcu
khP7AAAAAElFTkSuQmCC
</value>
</data>
<data name="ActionButton10.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==
JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsMAAALDAE/QCLIAAACM0lE
QVQ4T2P4//8/AyHMzicsqh6c34guDsIYAshYQFadhZmZhU1c18Yp8NCP/7LWvjHoajA0gbCwpqW7YeGs
FXYTD15xnHnihuuiiw98D//6777kynNuAREpnAYwMjIyqQQX9zkvffDfZce3//b7//23PPD/v/HWr//1
Vr74b7jm3X/VpI7FTIyMjFgNUAku6nRd//6/0ZTT31QbNlxXbthwQa3/6CO1ZS/+K8y+91968u3/qr1X
/gsq6jliGCCoYmRp23/4u4R9eCczF782AwMDFwMDA7OYhV+C2ooP/5UnXf+vkDXnioi+cz4LG6cIhgGK
zlE1/PLakcguArsquXuOSvmqayLatmnMjAx86PJwBhMzM4YkCPNKKpkxMTAIootjGIANswtKSKKLoWMM
ARAW0LS20S2at1POM20quhyvspGFqLlfJoYBTCzsnEI6Dl6a6ZO32c66+t91/dv/Akq6wTB5IQVdfgnP
jGSlpr1vuKTVPDAM4FczD7Ceeum/w6J7/y2XP/2vt/rNf9mGTUekC+eulKtYuUW+5+Qj2TlP/4u4psxD
dhGSC9h4dUoWntZf9e6/0qr3/5V3/Puvuu//f6Xt///Lrvr8X3ryzf8SfsWbmNBiAsV/bJIqpkpLX/xU
2/n3v+qcmx9VZl77oth97L1U2pQTgtq2WUwMDGzI6jEMAGFR/7wGzS3f//PIagawcvIqsLJzKTAyMLCi
q8NpABMLK4dCfOsSZlYOUXQ5bBgArRReBMoH61gAAAAASUVORK5CYII=
</value>
</data>
<data name="ActionButton11.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
vAAADrwBlbxySQAAAHpJREFUOE+1kVEKgDAMQ3e2/e+MXqpn6W/HxM7SpkIVB4HxSMKiTUTaFwVQ1X25
DjMfSxskHBYsAxHJkjUjHgrUNMY4peaMPxb03rcZMVhgn2oDKAwn+L0aROH/Cny4NAGFSx8x+10ZDwV2
gt+LOCxQsw1nPBS8VQBVTTzyhrdZSUm7AAAAAElFTkSuQmCC
</value>
</data>
<data name="ActionButton12.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP
WQwhyWIyJIUW5NqyPb7oCVtIlhVTwYf8nv7/t2zJagel9KmqKsIACYL9RjI8UHz5zshougZr/AEvbxEP
aZCDBY3VslixaJvX3wzkkDiOwbZtDRGA5vdNAg+TL27qgmt5XkBG/gTdAG7Gt+3PP9oOaEGFCVEC6rp+
5g9MfM/c5e4OsEZMZkQEtGL5H2DdZ5JRArDwPA+iKII0TfkC9vroC9j5vq8JTWw3WzWgLMtZGIaa0MR8
vlAD8PYlSaIJTTiOowY0p0Bc19XEJo6HE59FAPuMzyAINKGJ1XLFZxHALtMrnkBXOIQIIIQ8YvF/KrgB
cMaRN0UdBBkAAAAASUVORK5CYII=
vAAADrwBlbxySQAAARlJREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbWujg3dATZPKYZC6BQhvw1AMkg3dP
XQyl7WIyJIEW5CbS0/jKE5GwpCghgg9s6/8/y5Kj6DA45zcAwAAAezB6rjNnB4XX244NHt8wGs7wblop
yRGxwZQBYKIfbn477EvqusY4jj2MgMpPiwav7l9UyYXmdrs9duzP4ApUmd72sfrxVsD33JQISyClvFUX
w9nJssvJFei9CJUtgQ7394Du3YKLJaCbLMuwqips21ZNuDve/35X8J7nuRcMsVwsbYEQYlSWpRcMMR5P
bAH9fU3TeMEQSZLYgsMpsDRNvXCIr89vWyCEeC6KwguGmL/ObYGU8oFOwA2ewwgYY9f6f7iUf3DGkTcu
khP7AAAAAElFTkSuQmCC
</value>
</data>
<data name="ActionButton13.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==
JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsMAAALDAE/QCLIAAACM0lE
QVQ4T2P4//8/AyHMzicsqh6c34guDsIYAshYQFadhZmZhU1c18Yp8NCP/7LWvjHoajA0gbCwpqW7YeGs
FXYTD15xnHnihuuiiw98D//6777kynNuAREpnAYwMjIyqQQX9zkvffDfZce3//b7//23PPD/v/HWr//1
Vr74b7jm3X/VpI7FTIyMjFgNUAku6nRd//6/0ZTT31QbNlxXbthwQa3/6CO1ZS/+K8y+91968u3/qr1X
/gsq6jliGCCoYmRp23/4u4R9eCczF782AwMDFwMDA7OYhV+C2ooP/5UnXf+vkDXnioi+cz4LG6cIhgGK
zlE1/PLakcguArsquXuOSvmqayLatmnMjAx86PJwBhMzM4YkCPNKKpkxMTAIootjGIANswtKSKKLoWMM
ARAW0LS20S2at1POM20quhyvspGFqLlfJoYBTCzsnEI6Dl6a6ZO32c66+t91/dv/Akq6wTB5IQVdfgnP
jGSlpr1vuKTVPDAM4FczD7Ceeum/w6J7/y2XP/2vt/rNf9mGTUekC+eulKtYuUW+5+Qj2TlP/4u4psxD
dhGSC9h4dUoWntZf9e6/0qr3/5V3/Puvuu//f6Xt///Lrvr8X3ryzf8SfsWbmNBiAsV/bJIqpkpLX/xU
2/n3v+qcmx9VZl77oth97L1U2pQTgtq2WUwMDGzI6jEMAGFR/7wGzS3f//PIagawcvIqsLJzKTAyMLCi
q8NpABMLK4dCfOsSZlYOUXQ5bBgArRReBMoH61gAAAAASUVORK5CYII=
</value>
</data>
<data name="ActionButton14.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
vAAADrwBlbxySQAAAHpJREFUOE+1kVEKgDAMQ3e2/e+MXqpn6W/HxM7SpkIVB4HxSMKiTUTaFwVQ1X25
DjMfSxskHBYsAxHJkjUjHgrUNMY4peaMPxb03rcZMVhgn2oDKAwn+L0aROH/Cny4NAGFSx8x+10ZDwV2
gt+LOCxQsw1nPBS8VQBVTTzyhrdZSUm7AAAAAElFTkSuQmCC
</value>
</data>
<data name="ActionButton15.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP
WQwhyWIyJIUW5NqyPb7oCVtIlhVTwYf8nv7/t2zJagel9KmqKsIACYL9RjI8UHz5zshougZr/AEvbxEP
aZCDBY3VslixaJvX3wzkkDiOwbZtDRGA5vdNAg+TL27qgmt5XkBG/gTdAG7Gt+3PP9oOaEGFCVEC6rp+
5g9MfM/c5e4OsEZMZkQEtGL5H2DdZ5JRArDwPA+iKII0TfkC9vroC9j5vq8JTWw3WzWgLMtZGIaa0MR8
vlAD8PYlSaIJTTiOowY0p0Bc19XEJo6HE59FAPuMzyAINKGJ1XLFZxHALtMrnkBXOIQIIIQ8YvF/KrgB
cMaRN0UdBBkAAAAASUVORK5CYII=
vAAADrwBlbxySQAAARlJREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbWujg3dATZPKYZC6BQhvw1AMkg3dP
XQyl7WIyJIEW5CbS0/jKE5GwpCghgg9s6/8/y5Kj6DA45zcAwAAAezB6rjNnB4XX244NHt8wGs7wblop
yRGxwZQBYKIfbn477EvqusY4jj2MgMpPiwav7l9UyYXmdrs9duzP4ApUmd72sfrxVsD33JQISyClvFUX
w9nJssvJFei9CJUtgQ7394Du3YKLJaCbLMuwqips21ZNuDve/35X8J7nuRcMsVwsbYEQYlSWpRcMMR5P
bAH9fU3TeMEQSZLYgsMpsDRNvXCIr89vWyCEeC6KwguGmL/ObYGU8oFOwA2ewwgYY9f6f7iUf3DGkTcu
khP7AAAAAElFTkSuQmCC
</value>
</data>
<data name="ActionButton16.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==
JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsMAAALDAE/QCLIAAACM0lE
QVQ4T2P4//8/AyHMzicsqh6c34guDsIYAshYQFadhZmZhU1c18Yp8NCP/7LWvjHoajA0gbCwpqW7YeGs
FXYTD15xnHnihuuiiw98D//6777kynNuAREpnAYwMjIyqQQX9zkvffDfZce3//b7//23PPD/v/HWr//1
Vr74b7jm3X/VpI7FTIyMjFgNUAku6nRd//6/0ZTT31QbNlxXbthwQa3/6CO1ZS/+K8y+91968u3/qr1X
/gsq6jliGCCoYmRp23/4u4R9eCczF782AwMDFwMDA7OYhV+C2ooP/5UnXf+vkDXnioi+cz4LG6cIhgGK
zlE1/PLakcguArsquXuOSvmqayLatmnMjAx86PJwBhMzM4YkCPNKKpkxMTAIootjGIANswtKSKKLoWMM
ARAW0LS20S2at1POM20quhyvspGFqLlfJoYBTCzsnEI6Dl6a6ZO32c66+t91/dv/Akq6wTB5IQVdfgnP
jGSlpr1vuKTVPDAM4FczD7Ceeum/w6J7/y2XP/2vt/rNf9mGTUekC+eulKtYuUW+5+Qj2TlP/4u4psxD
dhGSC9h4dUoWntZf9e6/0qr3/5V3/Puvuu//f6Xt///Lrvr8X3ryzf8SfsWbmNBiAsV/bJIqpkpLX/xU
2/n3v+qcmx9VZl77oth97L1U2pQTgtq2WUwMDGzI6jEMAGFR/7wGzS3f//PIagawcvIqsLJzKTAyMLCi
q8NpABMLK4dCfOsSZlYOUXQ5bBgArRReBMoH61gAAAAASUVORK5CYII=
</value>
</data>
<data name="ActionButton17.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
vAAADrwBlbxySQAAAHpJREFUOE+1kVEKgDAMQ3e2/e+MXqpn6W/HxM7SpkIVB4HxSMKiTUTaFwVQ1X25
DjMfSxskHBYsAxHJkjUjHgrUNMY4peaMPxb03rcZMVhgn2oDKAwn+L0aROH/Cny4NAGFSx8x+10ZDwV2
gt+LOCxQsw1nPBS8VQBVTTzyhrdZSUm7AAAAAElFTkSuQmCC
</value>
</data>
</root>

View File

@@ -465,6 +465,9 @@ Namespace API.YouTube.Controls
TXT_FILE.Text = f
End If
End Sub
Private Sub BTT_TRIM_Click(sender As Object, e As EventArgs) Handles BTT_TRIM.Click
Using f As New VideoOptionsTrimForm(MyContainer) : f.ShowDialog() : End Using
End Sub
Private Sub TXT_SUBS_ActionOnButtonClick(ByVal Sender As ActionButton, ByVal e As ActionButtonEventArgs) Handles TXT_SUBS.ActionOnButtonClick
Select Case Sender.DefaultButton
Case ADB.Open

View File

@@ -0,0 +1,166 @@
' 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
Namespace API.YouTube.Controls
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()>
Partial Friend Class VideoOptionsTrimForm : Inherits System.Windows.Forms.Form
<System.Diagnostics.DebuggerNonUserCode()>
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
Try
If disposing AndAlso components IsNot Nothing Then
components.Dispose()
End If
Finally
MyBase.Dispose(disposing)
End Try
End Sub
Private components As System.ComponentModel.IContainer
<System.Diagnostics.DebuggerStepThrough()>
Private Sub InitializeComponent()
Me.components = New System.ComponentModel.Container()
Dim CONTAINER_MAIN As System.Windows.Forms.ToolStripContainer
Dim TP_MAIN As System.Windows.Forms.TableLayoutPanel
Dim TP_OPTIONS As System.Windows.Forms.TableLayoutPanel
Dim TT_MAIN As System.Windows.Forms.ToolTip
Me.LIST_TRIM = New System.Windows.Forms.ListBox()
Me.CH_DEL_ORIG = New System.Windows.Forms.CheckBox()
Me.CH_ADD_M3U8 = New System.Windows.Forms.CheckBox()
Me.CH_SEP_FOLDER = New System.Windows.Forms.CheckBox()
CONTAINER_MAIN = New System.Windows.Forms.ToolStripContainer()
TP_MAIN = New System.Windows.Forms.TableLayoutPanel()
TP_OPTIONS = New System.Windows.Forms.TableLayoutPanel()
TT_MAIN = New System.Windows.Forms.ToolTip(Me.components)
CONTAINER_MAIN.ContentPanel.SuspendLayout()
CONTAINER_MAIN.SuspendLayout()
TP_MAIN.SuspendLayout()
TP_OPTIONS.SuspendLayout()
Me.SuspendLayout()
'
'CONTAINER_MAIN
'
'
'CONTAINER_MAIN.ContentPanel
'
CONTAINER_MAIN.ContentPanel.Controls.Add(TP_MAIN)
CONTAINER_MAIN.ContentPanel.Size = New System.Drawing.Size(434, 236)
CONTAINER_MAIN.Dock = System.Windows.Forms.DockStyle.Fill
CONTAINER_MAIN.LeftToolStripPanelVisible = False
CONTAINER_MAIN.Location = New System.Drawing.Point(0, 0)
CONTAINER_MAIN.Name = "CONTAINER_MAIN"
CONTAINER_MAIN.RightToolStripPanelVisible = False
CONTAINER_MAIN.Size = New System.Drawing.Size(434, 261)
CONTAINER_MAIN.TabIndex = 0
'
'TP_MAIN
'
TP_MAIN.ColumnCount = 1
TP_MAIN.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
TP_MAIN.Controls.Add(Me.LIST_TRIM, 0, 1)
TP_MAIN.Controls.Add(TP_OPTIONS, 0, 0)
TP_MAIN.Dock = System.Windows.Forms.DockStyle.Fill
TP_MAIN.Location = New System.Drawing.Point(0, 0)
TP_MAIN.Name = "TP_MAIN"
TP_MAIN.RowCount = 2
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 30.0!))
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
TP_MAIN.Size = New System.Drawing.Size(434, 236)
TP_MAIN.TabIndex = 0
'
'LIST_TRIM
'
Me.LIST_TRIM.Dock = System.Windows.Forms.DockStyle.Fill
Me.LIST_TRIM.FormattingEnabled = True
Me.LIST_TRIM.Location = New System.Drawing.Point(3, 33)
Me.LIST_TRIM.Name = "LIST_TRIM"
Me.LIST_TRIM.Size = New System.Drawing.Size(428, 200)
Me.LIST_TRIM.TabIndex = 0
'
'TP_OPTIONS
'
TP_OPTIONS.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.[Single]
TP_OPTIONS.ColumnCount = 3
TP_OPTIONS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33333!))
TP_OPTIONS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33334!))
TP_OPTIONS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33334!))
TP_OPTIONS.Controls.Add(Me.CH_DEL_ORIG, 0, 0)
TP_OPTIONS.Controls.Add(Me.CH_ADD_M3U8, 1, 0)
TP_OPTIONS.Controls.Add(Me.CH_SEP_FOLDER, 2, 0)
TP_OPTIONS.Dock = System.Windows.Forms.DockStyle.Fill
TP_OPTIONS.Location = New System.Drawing.Point(3, 3)
TP_OPTIONS.Name = "TP_OPTIONS"
TP_OPTIONS.RowCount = 1
TP_OPTIONS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
TP_OPTIONS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 23.0!))
TP_OPTIONS.Size = New System.Drawing.Size(428, 24)
TP_OPTIONS.TabIndex = 1
'
'CH_DEL_ORIG
'
Me.CH_DEL_ORIG.AutoSize = True
Me.CH_DEL_ORIG.Dock = System.Windows.Forms.DockStyle.Fill
Me.CH_DEL_ORIG.Location = New System.Drawing.Point(4, 4)
Me.CH_DEL_ORIG.Name = "CH_DEL_ORIG"
Me.CH_DEL_ORIG.Size = New System.Drawing.Size(135, 16)
Me.CH_DEL_ORIG.TabIndex = 0
Me.CH_DEL_ORIG.Text = "Delete original file"
TT_MAIN.SetToolTip(Me.CH_DEL_ORIG, "If checked, the original file will be deleted after trimming")
Me.CH_DEL_ORIG.UseVisualStyleBackColor = True
'
'CH_ADD_M3U8
'
Me.CH_ADD_M3U8.AutoSize = True
Me.CH_ADD_M3U8.Dock = System.Windows.Forms.DockStyle.Fill
Me.CH_ADD_M3U8.Location = New System.Drawing.Point(146, 4)
Me.CH_ADD_M3U8.Name = "CH_ADD_M3U8"
Me.CH_ADD_M3U8.Size = New System.Drawing.Size(135, 16)
Me.CH_ADD_M3U8.TabIndex = 1
Me.CH_ADD_M3U8.Text = "Add to M3U8"
TT_MAIN.SetToolTip(Me.CH_ADD_M3U8, "If checked, the trimmed files will be added to the M3U8 playlist (if selected)")
Me.CH_ADD_M3U8.UseVisualStyleBackColor = True
'
'CH_SEP_FOLDER
'
Me.CH_SEP_FOLDER.AutoSize = True
Me.CH_SEP_FOLDER.Dock = System.Windows.Forms.DockStyle.Fill
Me.CH_SEP_FOLDER.Location = New System.Drawing.Point(288, 4)
Me.CH_SEP_FOLDER.Name = "CH_SEP_FOLDER"
Me.CH_SEP_FOLDER.Size = New System.Drawing.Size(136, 16)
Me.CH_SEP_FOLDER.TabIndex = 2
Me.CH_SEP_FOLDER.Text = "Separate folder"
TT_MAIN.SetToolTip(Me.CH_SEP_FOLDER, "Place the trimmed files in a separate folder")
Me.CH_SEP_FOLDER.UseVisualStyleBackColor = True
'
'VideoOptionsTrimForm
'
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
Me.ClientSize = New System.Drawing.Size(434, 261)
Me.Controls.Add(CONTAINER_MAIN)
Me.KeyPreview = True
Me.MinimizeBox = False
Me.MinimumSize = New System.Drawing.Size(450, 300)
Me.Name = "VideoOptionsTrimForm"
Me.ShowIcon = False
Me.ShowInTaskbar = False
Me.Text = "Trimming options"
CONTAINER_MAIN.ContentPanel.ResumeLayout(False)
CONTAINER_MAIN.ResumeLayout(False)
CONTAINER_MAIN.PerformLayout()
TP_MAIN.ResumeLayout(False)
TP_OPTIONS.ResumeLayout(False)
TP_OPTIONS.PerformLayout()
Me.ResumeLayout(False)
End Sub
Private WithEvents LIST_TRIM As ListBox
Private WithEvents CH_DEL_ORIG As CheckBox
Private WithEvents CH_ADD_M3U8 As CheckBox
Private WithEvents CH_SEP_FOLDER As CheckBox
End Class
End Namespace

View File

@@ -0,0 +1,135 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="CONTAINER_MAIN.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value>
</metadata>
<metadata name="TP_MAIN.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value>
</metadata>
<metadata name="TP_OPTIONS.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">
<value>False</value>
</metadata>
<metadata name="TT_MAIN.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
</root>

View File

@@ -0,0 +1,167 @@
' 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.Forms
Imports PersonalUtilities.Forms.Toolbars
Imports SCrawler.API.YouTube.Objects
Imports SCrawler.API.YouTube.Base
Imports ETC = PersonalUtilities.Forms.Toolbars.EditToolbar.ControlItem
Namespace API.YouTube.Controls
Friend Class VideoOptionsTrimForm
#Region "Declarations"
Private WithEvents MyDefs As DefaultFormOptions
Private ReadOnly Property MyMedia As YouTubeMediaContainerBase
Private ReadOnly Property TrimData As List(Of TrimOption)
Private WithEvents BTT_CLEAR_ALL As ToolStripButton
Private WithEvents BTT_CHAPTERS As ToolStripButton
#End Region
#Region "Initializer"
Friend Sub New(ByRef Media As YouTubeMediaContainerBase)
InitializeComponent()
MyDefs = New DefaultFormOptions(Me, MyYouTubeSettings.DesignXml)
MyMedia = Media
TrimData = New List(Of TrimOption)
TrimData.ListAddList(Media.TrimOptions)
BTT_CLEAR_ALL = New ToolStripButton("Clear", PersonalUtilities.My.Resources.DeletePic_Red_24) With {
.DisplayStyle = ToolStripItemDisplayStyle.ImageAndText,
.BackColor = MyColor.DeleteBack,
.ForeColor = MyColor.DeleteFore
}
BTT_CHAPTERS = New ToolStripButton("Chapters") With {.DisplayStyle = ToolStripItemDisplayStyle.Text, .Enabled = Media.Chapters.Count > 0}
End Sub
#End Region
#Region "Form handlers"
Private Sub VideoOptionsTrimForm_Load(sender As Object, e As EventArgs) Handles Me.Load
With MyDefs
.MyViewInitialize()
.AddOkCancelToolbar()
.AddEditToolbar({ETC.Add, ETC.Edit, ETC.Delete, BTT_CLEAR_ALL, ETC.Separator, BTT_CHAPTERS})
Refill()
With MyMedia
If Not .TrimOptionsSet Then
With MyYouTubeSettings
CH_DEL_ORIG.Checked = .TrimDeleteOriginalFile
CH_ADD_M3U8.Checked = .TrimAddTrimmedFilesToM3U8
CH_SEP_FOLDER.Checked = .TrimSeparateFolder
End With
Else
CH_DEL_ORIG.Checked = .TrimDeleteOriginalFile
CH_ADD_M3U8.Checked = .TrimAddTrimmedFilesToM3U8
CH_SEP_FOLDER.Checked = .TrimSeparateFolder
End If
End With
.EndLoaderOperations()
End With
End Sub
Private Sub VideoOptionsTrimForm_Disposed(sender As Object, e As EventArgs) Handles Me.Disposed
TrimData.Clear()
End Sub
#End Region
#Region "Refill"
Private Sub Refill(Optional ByVal DEL As Boolean = False)
Dim indx% = _CurrentIndex
With LIST_TRIM
.BeginUpdate()
.Items.Clear()
If TrimData.Count > 0 Then
TrimData.Sort()
.Items.AddRange(TrimData.Cast(Of Object).ToArray)
If DEL Then indx -= IIf(indx - 1 < 0, 0, 1)
If indx.ValueBetween(0, TrimData.Count - 1) Then
.SelectedIndex = indx
ElseIf (indx - 1).ValueBetween(0, TrimData.Count - 1) Then
.SelectedIndex = indx - 1
ElseIf (indx + 1).ValueBetween(0, TrimData.Count - 1) Then
.SelectedIndex = indx + 1
End If
End If
.EndUpdate()
End With
End Sub
#End Region
#Region "Ok"
Private Sub MyDefs_ButtonOkClick(ByVal Sender As Object, ByVal e As KeyHandleEventArgs) Handles MyDefs.ButtonOkClick
With MyMedia
.TrimOptions.Clear()
.TrimDeleteOriginalFile = CH_DEL_ORIG.Checked
.TrimAddTrimmedFilesToM3U8 = CH_ADD_M3U8.Checked
.TrimSeparateFolder = CH_SEP_FOLDER.Checked
.TrimOptionsSet = True
End With
If TrimData.Count > 0 Then
TrimData.Sort()
Dim indx% = 0
Dim c%
Dim opt As TrimOption
Dim dic As New Dictionary(Of String, Integer)
For i% = 0 To TrimData.Count - 1
opt = TrimData(i)
If opt.Name.IsEmptyString Then
indx += 1
opt.Name = indx
Else
c = TrimData.LongCount(Function(d) Not d.Name.IsEmptyString AndAlso d.Name = opt.Name)
If c > 1 Then
If Not dic.ContainsKey(opt.Name) Then dic.Add(opt.Name, 0)
dic(opt.Name) += 1
opt.Name &= $"_{dic(opt.Name)}"
End If
End If
TrimData(i) = opt
Next
dic.Clear()
MyMedia.TrimOptions.AddRange(TrimData)
End If
MyDefs.CloseForm()
End Sub
#End Region
#Region "Edit"
Private Sub MyDefs_ButtonAddClick(ByVal Sender As Object, ByVal e As EditToolbarEventArgs) Handles MyDefs.ButtonAddClick
Using f As New TrimOptionForm
f.ShowDialog()
If f.DialogResult = DialogResult.OK AndAlso
(TrimData.Count = 0 OrElse Not TrimData.Any(Function(t) t.End = f.MyTrimOption.End And t.Start = f.MyTrimOption.Start)) Then _
TrimData.Add(f.MyTrimOption) : Refill()
End Using
End Sub
Private Sub MyDefs_ButtonEditClick(ByVal Sender As Object, ByVal e As EditToolbarEventArgs) Handles MyDefs.ButtonEditClick
If _CurrentIndex.ValueBetween(0, TrimData.Count - 1) Then
Using f As New TrimOptionForm(TrimData(_CurrentIndex))
f.ShowDialog()
If f.DialogResult = DialogResult.OK Then TrimData(_CurrentIndex) = f.MyTrimOption : Refill()
End Using
End If
End Sub
Private Sub MyDefs_ButtonDeleteClickE(ByVal Sender As Object, ByVal e As EditToolbarEventArgs) Handles MyDefs.ButtonDeleteClickE
If _CurrentIndex.ValueBetween(0, TrimData.Count - 1) AndAlso
MsgBoxE({$"Are you sure you want to remove the following trim option?{vbCr}{vbCr}{TrimData(_CurrentIndex)}", "Remove trim option"}, vbYesNo + vbExclamation) = vbYes Then _
TrimData.RemoveAt(_CurrentIndex) : Refill(True)
End Sub
Private Sub BTT_CLEAR_ALL_Click(sender As Object, e As EventArgs) Handles BTT_CLEAR_ALL.Click
If MsgBoxE({$"Are you sure you want to remove ALL trim options?", "Remove trim option"}, vbYesNo + vbCritical) = vbYes Then TrimData.Clear() : Refill()
End Sub
Private Sub BTT_CHAPTERS_Click(sender As Object, e As EventArgs) Handles BTT_CHAPTERS.Click
Using f As New ChaptersForm(MyMedia, TrimData)
f.ShowDialog()
If f.DialogResult = DialogResult.OK AndAlso f.MyResult.Count > 0 Then TrimData.ListAddList(f.MyResult, LAP.NotContainsOnly) : Refill()
End Using
End Sub
#End Region
#Region "List"
Private _CurrentIndex As Integer = -1
Private Sub LIST_TRIM_SelectedIndexChanged(sender As Object, e As EventArgs) Handles LIST_TRIM.SelectedIndexChanged
_CurrentIndex = LIST_TRIM.SelectedIndex
End Sub
#End Region
End Class
End Namespace

View File

@@ -0,0 +1,122 @@
' 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.YouTube.Base
Imports SCrawler.API.YouTube.Objects
Imports PersonalUtilities.Functions.XML
Imports PersonalUtilities.Functions.XML.Base
Namespace API.YouTube.Controls
Friend Class YTDataFilter
Private Const Name_Types As String = "Types"
Private Const Name_IsMusic As String = "IsMusic"
Private Const Name_IsVideo As String = "IsVideo"
Private Const Name_UserList As String = "UserList"
Friend Structure SimpleUser : Implements IEContainerProvider, IComparable(Of SimpleUser)
Private Const Name_UID As String = "UID"
Friend Title As String
Friend ID As String
Friend Sub New(ByVal _Title As String, ByVal _ID As String)
Title = _Title
ID = _ID
End Sub
Private Sub New(ByVal u As YouTubeMediaContainerBase)
Title = u.Title
ID = u.ID
End Sub
Public Shared Widening Operator CType(ByVal u As YouTubeMediaContainerBase) As SimpleUser
Return New SimpleUser(u.UserTitle, u.UserID)
End Operator
Public Shared Widening Operator CType(ByVal e As EContainer) As SimpleUser
Return New SimpleUser(e.Value, e.Attribute(Name_UID).Value)
End Operator
Public Overrides Function ToString() As String
Return String.Format(CStr(Interaction.Switch(Title.IsEmptyString, "{1}", ID.IsEmptyString, "{0}", True, "{0} ({1})")), Title, ID)
End Function
Public Overrides Function Equals(ByVal Obj As Object) As Boolean
Return Not IsDBNull(Obj) AndAlso ToString() = DirectCast(Obj, SimpleUser).ToString
End Function
Private Function CompareTo(ByVal Other As SimpleUser) As Integer Implements IComparable(Of SimpleUser).CompareTo
Return ToString.CompareTo(Other.ToString)
End Function
Private Function ToEContainer(Optional ByVal e As ErrorsDescriber = Nothing) As EContainer Implements IEContainerProvider.ToEContainer
Return New EContainer("User", Title, {New EAttribute(Name_UID, ID)})
End Function
End Structure
Friend ReadOnly Property Types As List(Of YouTubeMediaType)
Friend Property IsMusic As Boolean = True
Friend Property IsVideo As Boolean = True
Friend ReadOnly Property UserList As List(Of SimpleUser)
Private ReadOnly File As New SFile("Settings\YouTubeFilter.xml")
Friend Sub New(Optional ByVal LoadFromFile As Boolean = True)
Types = New List(Of YouTubeMediaType) From {YouTubeMediaType.Undefined}
UserList = New List(Of SimpleUser)
If LoadFromFile AndAlso File.Exists Then
Using e As New XmlFile(File, Protector.Modes.All, False) With {.AllowSameNames = True}
e.LoadData()
If e.Count > 0 Then
Types.Clear()
Types.ListAddList(e.Value(Name_Types).StringToList(Of Integer)(","), LAP.NotContainsOnly)
IsMusic = e.Value(Name_IsMusic).FromXML(Of Boolean)(True)
IsVideo = e.Value(Name_IsVideo).FromXML(Of Boolean)(True)
UserList.ListAddList(e(Name_UserList), LAP.IgnoreICopier)
End If
End Using
End If
End Sub
Friend Sub New(ByVal f As YTDataFilter)
Me.New(False)
With f
Types.ListAddList(.Types, LAP.NotContainsOnly)
IsMusic = .IsMusic
IsVideo = .IsVideo
UserList.ListAddList(.UserList)
End With
End Sub
Friend Sub Reset(Optional ByVal AddDefType As Boolean = True, Optional ByVal ClearUserList As Boolean = True)
Types.Clear()
If AddDefType Then Types.Add(YouTubeMediaType.Undefined)
IsMusic = True
IsVideo = True
If ClearUserList Then UserList.Clear()
End Sub
Friend Sub Update()
Using x As New XmlFile With {.AllowSameNames = True}
With x
.Add(Name_Types, Types.ListToStringE(","))
.Add(Name_IsMusic, IsMusic.BoolToInteger)
.Add(Name_IsVideo, IsVideo.BoolToInteger)
.Add(Name_UserList, String.Empty)
.Self()(Name_UserList).AddRange(UserList)
.Name = "FILTER"
.Save(File)
End With
End Using
End Sub
Friend Function Ready(ByVal Item As YouTubeMediaContainerBase, Optional ByVal IgnoreUserList As Boolean = False) As Boolean
With Item
If Not IsMusic Or Not IsVideo Then
If .IsMusic And Not IsMusic Then Return False
If Not .IsMusic And Not IsVideo Then Return False
End If
If Not Types.Contains(YouTubeMediaType.Undefined) AndAlso Not Types.Contains(.ObjectType) Then Return False
If Not IgnoreUserList AndAlso UserList.Count > 0 AndAlso Not UserList.Contains(Item) Then Return False
End With
Return True
End Function
Friend Overloads Sub RemoveAll(ByRef Data As List(Of IYouTubeMediaContainer))
Data.RemoveAll(Function(item) Not Ready(item))
End Sub
Friend Overloads Sub RemoveAll(ByRef Data As List(Of YouTubeMediaContainerBase))
Data.RemoveAll(Function(item) Not Ready(item))
End Sub
Friend Sub Populate(ByVal InitList As List(Of IYouTubeMediaContainer), ByVal DestList As List(Of IYouTubeMediaContainer), ByVal IgnoreUserList As Boolean)
DestList.Clear()
If InitList.Count > 0 Then InitList.ForEach(Sub(item) If Ready(item, IgnoreUserList) Then DestList.ListAddValue(item))
End Sub
End Class
End Namespace

View File

@@ -54,6 +54,7 @@ Namespace API.YouTube
Friend ReadOnly DateBaseProvider As New ADateTime(ADateTime.Formats.BaseDateTime)
Friend ReadOnly DateAddedProvider As New ADateTime(ADateTime.Formats.yyyymmdd) With {.DateSeparator = String.Empty}
Friend ReadOnly TimeToStringProvider As IFormatProvider = New TimeToStringConverter
Friend ReadOnly TimeToStringProviderH As IFormatProvider = New TimeToStringConverter(True)
Friend ReadOnly TitleHtmlConverter As Func(Of String, String) = Function(Input) Input.StringRemoveWinForbiddenSymbols().StringTrim()
Friend ReadOnly ProgressProvider As IMyProgressNumberProvider = MyProgressNumberProvider.Percentage
Public ReadOnly TrueUrlRegEx As RParams = RParams.DM(Base.YouTubeFunctions.TrueUrlPattern, 0, EDP.ReturnValue)
@@ -80,11 +81,15 @@ Namespace API.YouTube
Private Class TimeToStringConverter : Implements ICustomProvider
Private ReadOnly _Provider As New ADateTime("mm\:ss") With {.TimeParseMode = ADateTime.TimeModes.TimeSpan}
Private ReadOnly _ProviderWithHours As New ADateTime("h\:mm\:ss") With {.TimeParseMode = ADateTime.TimeModes.TimeSpan}
Private ReadOnly ForceHours As Boolean
Private ReadOnly Property Provider(ByVal t As TimeSpan) As IFormatProvider
Get
Return If(t.Hours > 0, _ProviderWithHours, _Provider)
Return If(t.Hours > 0 Or ForceHours, _ProviderWithHours, _Provider)
End Get
End Property
Friend Sub New(Optional ByVal ForceHours As Boolean = False)
Me.ForceHours = ForceHours
End Sub
Private Function Convert(ByVal Value As Object, ByVal DestinationType As Type, ByVal Provider As IFormatProvider,
Optional ByVal NothingArg As Object = Nothing, Optional ByVal e As ErrorsDescriber = Nothing) As Object Implements ICustomProvider.Convert
If Not IsNothing(Value) Then

View File

@@ -33,6 +33,7 @@ Namespace DownloadObjects.STDownloader
Me.BTT_CLEAR_DONE = New System.Windows.Forms.ToolStripMenuItem()
Me.BTT_CLEAR_ALL = New System.Windows.Forms.ToolStripMenuItem()
Me.BTT_SELECT_ALL = New System.Windows.Forms.ToolStripMenuItem()
Me.BTT_SELECT_NONE = New System.Windows.Forms.ToolStripMenuItem()
Me.TOOLBAR_BOTTOM = New System.Windows.Forms.StatusStrip()
Me.PR_MAIN = New System.Windows.Forms.ToolStripProgressBar()
Me.LBL_INFO = New System.Windows.Forms.ToolStripStatusLabel()
@@ -43,6 +44,7 @@ Namespace DownloadObjects.STDownloader
Me.MENU_ADD = New System.Windows.Forms.ToolStripDropDownButton()
Me.BTT_ADD = New PersonalUtilities.Forms.Controls.KeyClick.ToolStripMenuItemKeyClick()
Me.BTT_ADD_PLS_ARR = New PersonalUtilities.Forms.Controls.KeyClick.ToolStripMenuItemKeyClick()
Me.BTT_FILTER = New System.Windows.Forms.ToolStripButton()
Me.BTT_DOWN = New System.Windows.Forms.ToolStripButton()
Me.BTT_STOP = New System.Windows.Forms.ToolStripButton()
Me.SEP_LOG = New System.Windows.Forms.ToolStripSeparator()
@@ -50,7 +52,7 @@ Namespace DownloadObjects.STDownloader
Me.BTT_INFO = New System.Windows.Forms.ToolStripButton()
Me.BTT_DONATE = New System.Windows.Forms.ToolStripButton()
Me.BTT_BUG_REPORT = New System.Windows.Forms.ToolStripButton()
Me.BTT_SELECT_NONE = New System.Windows.Forms.ToolStripMenuItem()
Me.SEP_FILTER = New System.Windows.Forms.ToolStripSeparator()
SEP_2 = New System.Windows.Forms.ToolStripSeparator()
SEP_3 = New System.Windows.Forms.ToolStripSeparator()
MENU_DEL_CLEAR = New System.Windows.Forms.ToolStripDropDownButton()
@@ -134,6 +136,12 @@ Namespace DownloadObjects.STDownloader
Me.BTT_SELECT_ALL.Size = New System.Drawing.Size(185, 22)
Me.BTT_SELECT_ALL.Text = "Select all"
'
'BTT_SELECT_NONE
'
Me.BTT_SELECT_NONE.Name = "BTT_SELECT_NONE"
Me.BTT_SELECT_NONE.Size = New System.Drawing.Size(185, 22)
Me.BTT_SELECT_NONE.Text = "Select none"
'
'TOOLBAR_BOTTOM
'
Me.TOOLBAR_BOTTOM.Items.AddRange(New System.Windows.Forms.ToolStripItem() {Me.PR_MAIN, Me.LBL_INFO})
@@ -169,7 +177,7 @@ Namespace DownloadObjects.STDownloader
'TOOLBAR_TOP
'
Me.TOOLBAR_TOP.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden
Me.TOOLBAR_TOP.Items.AddRange(New System.Windows.Forms.ToolStripItem() {Me.BTT_SETTINGS, Me.SEP_1, Me.MENU_ADD, SEP_2, Me.BTT_DOWN, Me.BTT_STOP, SEP_3, MENU_DEL_CLEAR, Me.SEP_LOG, Me.BTT_LOG, Me.BTT_INFO, Me.BTT_DONATE, Me.BTT_BUG_REPORT})
Me.TOOLBAR_TOP.Items.AddRange(New System.Windows.Forms.ToolStripItem() {Me.BTT_SETTINGS, Me.SEP_1, Me.MENU_ADD, Me.SEP_FILTER, Me.BTT_FILTER, SEP_2, Me.BTT_DOWN, Me.BTT_STOP, SEP_3, MENU_DEL_CLEAR, Me.SEP_LOG, Me.BTT_LOG, Me.BTT_INFO, Me.BTT_DONATE, Me.BTT_BUG_REPORT})
Me.TOOLBAR_TOP.Location = New System.Drawing.Point(0, 0)
Me.TOOLBAR_TOP.Name = "TOOLBAR_TOP"
Me.TOOLBAR_TOP.Size = New System.Drawing.Size(584, 25)
@@ -222,6 +230,15 @@ Namespace DownloadObjects.STDownloader
Me.BTT_ADD_PLS_ARR.Text = "Add URL array"
Me.BTT_ADD_PLS_ARR.ToolTipText = "Click to add." & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "Ctrl+click to use cookies for download (if supported)." & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "Shift to a" &
"dd without downloading."
'
'BTT_FILTER
'
Me.BTT_FILTER.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image
Me.BTT_FILTER.Image = CType(resources.GetObject("BTT_FILTER.Image"), System.Drawing.Image)
Me.BTT_FILTER.ImageTransparentColor = System.Drawing.Color.Magenta
Me.BTT_FILTER.Name = "BTT_FILTER"
Me.BTT_FILTER.Size = New System.Drawing.Size(23, 22)
Me.BTT_FILTER.Text = "Filter"
'
'BTT_DOWN
'
@@ -286,11 +303,10 @@ Namespace DownloadObjects.STDownloader
Me.BTT_BUG_REPORT.Size = New System.Drawing.Size(23, 22)
Me.BTT_BUG_REPORT.Text = "Bug report"
'
'BTT_SELECT_NONE
'SEP_FILTER
'
Me.BTT_SELECT_NONE.Name = "BTT_SELECT_NONE"
Me.BTT_SELECT_NONE.Size = New System.Drawing.Size(185, 22)
Me.BTT_SELECT_NONE.Text = "Select none"
Me.SEP_FILTER.Name = "SEP_FILTER"
Me.SEP_FILTER.Size = New System.Drawing.Size(6, 25)
'
'VideoListForm
'
@@ -337,5 +353,7 @@ Namespace DownloadObjects.STDownloader
Private WithEvents BTT_CLEAR_SELECTED As ToolStripMenuItem
Private WithEvents BTT_SELECT_ALL As ToolStripMenuItem
Private WithEvents BTT_SELECT_NONE As ToolStripMenuItem
Public WithEvents BTT_FILTER As ToolStripButton
Public WithEvents SEP_FILTER As ToolStripSeparator
End Class
End Namespace

View File

@@ -130,30 +130,31 @@
<data name="BTT_DELETE.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAVDSURBVEhLjZVtTFNXGMcLQmdHO6AdarLSOcTxXqC3lls3
FeVFihZsqaBjRM2ouILiC0oUozGaaBSTJfuwfdg+bJnJnFuiyzKZMOp8ATJl4IYLAjqspfS9cNlLskjP
/qe0OmPZeJJfbu85z/k//z7n3Ht5NPqLimLMSUkfXV20aPiKTPbhOqn0FQxHBSb/J4ZUKvY3mez6yMKF
fReTkw0YigERgUkaAyaT8FZR0ZdDzc1k9OxZ0tHU5L/Csp15ItFSTEfPZIWPQYZ507Z580Pu9Gky1dpK
hisrPe+nptbLYmPjMT1TpHPJkg/u7d1LHhw7RkaPHiVWJFsOHCBtLHtdLhK9jpSwRUbU6jUQt3BnzhAO
a7mWFjJ15Ai5p9V6v5ZK30OKGETxrkokVir+oKmJPDx0iFiRNH7yJLnW3Oy/olLdyBKJUpD4TBG0pdhW
VWWlzqn4FMS5/fvJFIz5amqIOSHhLtJYIOJ1JCaeG66tJaMoYAHWgweJDQscJ06QG2hXB8t2ZwqFqUgO
FBlSKIrHKyutk6dOEQ5mpmCKw7qpPXvIxI4dpE2lcm2RSC4gtRSIefrFi19tUyqvt2/b5n+EpDEwDjcO
7IkbLXPu20euokiuUJj2E8MUjRkMtsnjxwMt4eCYQ3u5xkYyCXFzbq4nRSD4FMJGIAd0w3nRjESSiiLd
li1biG3nTmJvaCCO3buJC8W8EOpuaPD3qNV37peXW7nDhwOOOcxxyOXq65+IpwkE56BXCzKC4pEgEPxc
sTjtO4Wi64fqar+9ro44gdtkIh4I+FDMt2sXmYTbSSqMew73AXG0t0up9MqFQipOnaeD+eDpUQ0GXyUW
p3dkZ3fbNm4kzq1biRt4jUbig8MJFJzABk6UlxPf6tXEp1YTD+jKy/PliETnsX47oM4F4DnxUPCXxcdn
dMjl3QMZGY89mzYRt0ZD3AxDXFIpcYlExBUdTVzz5pFuodDfnpTkZePi6IbWgUzwIphVPBSBdt2Sy/vH
IWiHmAM4ARUOEBVF+rKz/zKkpn6L/HdBFpiTeCBsNTUmm1ptsQsEz4sDOjaQnPzYrNXeT0tMLMeSWDA3
cZ/RuM9VUOCyw/1s4mPgIbBkZJA+ne7uW8uX52Dpf75WAuE2Gluca9e67TExz4k70XvaLio+CoYjI8kA
uJyT4++vqPjVwLK0TbMX8W7ffshRUuIJ59wpkZBHJSV/30hJ8VPxoaB4L+gCZoaZ7qf/ZLYiOIot9oIC
r10oDAg/05YFC8hgaenvx1eu7Oldv37ckpZGfoHobXATXKMFKMuWTf9oMNwpkMvpC/Lpq95TW9tMxR3h
xBMSyOCqVX/UM0wHUhtKMjPfvltWdn8QvQ+Jd4J20EaLqFTTPRUVvZ9otXTj+SCC59mwweqIiwvvHOJ1
CkU7Ek2AbqR4d37+G316/cidrKwn4pfBN+AS6E9Pf3xbr7+H3MWAz7PrdIMemSyscxPDfI+k+qB46EvF
31NYyPZVVIxcyMry/1v8q4gIchsFzBrNGPKKQDyvp7p687hG43ZDNOR8oLj4z53hxUPBb8zPz8PGDt/K
ziYXg+LmvLzpzzWaR6VLl36MnJkCiPntlZWHrWVl3h6W9VPnjUplJ8ZnEw8F37hihYK24wusM6vV0xfg
/CWBoBVzOjDTIkQEk5QU26vXf/agrMx5vrAQpy+yEeMMENJ5mjRL8C9VVb3zs043dlOrdZWkpNBvgR5I
AT2uT9bSH3FACdYFr3N9/F8A9GjSk7MevAbCPnDzAHVMP9b0Su/nEtQEff+/HIQWDBrj8f4B7zPYbtFn
HR8AAAAASUVORK5CYII=
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAV6SURBVEhLjZVrTFNnGMcfT4Wto2dcOtRkyBzigALlcmo5
dVNwXKRowRYEGSNiRi3jXiwQqYEQTVxQkyX7sH3YPmyZicYt0WUXBEaJjkumDNxwQUCHWErpFQ5zSxbo
s7xd200Fxz95c9qcc37//3me9wIAAKOZmQGGiIiP+7ZsmewKD/9of1jYywCwkdz7P01Ipexv4eE3pjZv
HrkSGVkAAAEAsMH3wFhlpeBmZuYXE83NOH3+PHbpdK4ulu1NoekdAOD3GO0JjTPMG6bi4gdcRwcunTuH
k4WF9g+io6vCAwODfSa927d/eLehAe+3t+N0WxsaOzpwpqkJO1n2hpimX1vLZEome9NUXDzDnT2LXHs7
cno9LrW24l2FwvFVWNj7ABDirkKfUGgk8Ps6HT5oaUFjayvOnTmDfc3Nri6p9Id4mo560mRCKs0yFRUZ
SXICX9LrkWtsxKWmJnSWlqIhNPQOALAAQEPP1q0XJsvLcVqnwxmdDo0nTqBJr8f506fxuk7n6mHZwTiB
INprMpGcnDVXWGhcfO895FpbcamlBTmdDpe0WlyoqMBOqdR6RCi8DAA57q9Qbdv2SqdEcuPa0aOuh1ot
zmq1ONfYiPPNzWhra0PL8ePYx7KDSQJBzE8MkzlbUGBaPHXKXRKuqQm5hgbk6upwsaICDUlJ9ig+/zMA
UAOA2NNw8GOEwuhOiWRw5sgRNNXUoLm6Gufr69Gq1aJDr8f+6mrXkEx2+15enpE7edKdmNNqkaupQa6q
ygeP4fMvAEA5AMR64JS3rP5JISEx15KTBwwlJS6zRoMWjQZtlZVor6pCZ309OmtrcbGhARcJuL4eudra
f+Dl5TggkTjEAgGBk+QiAHj+sanqNZGGhIh6EhIGTYcOoaWsDG1lZehQq9FZUYELGg0ulJbiQl4eOvfu
RadMhnaZDAdSUpyJNH0JAI55kvNXg3vlvzM4OLZHLB4cE4mW7YcPo00uRxvDoDUsDK00jVY/P7TyeNgv
ELi6IyIcbFAQaagGAOIA4IVnwb1yl+umWDw6R9No5vFwnsdDC4/nBrvHxo04kpDwZ0F09LcA8C4AxK8X
7paptLTSJJPNmPn8p+Eew7HIyGWDQnEvZuvWPAAIXDfcqVYft6anW800vSZ8lsfDBzwezsTG4ohSeeet
XbsSn1yMq8qmVust+/bZzAEBT8Etfn7uchH4NI+HkxSFYxSF3yQmukbz838tYFlSprVNHMeOtcxnZ9tX
S24RCvFhdvZf16OiXAQ+4YEPUxQOUBQaGGZllHzJWiYOtVpvTk93mAUCN/ixsmzahOM5Ob+f2rNnaPjA
gbmZmBj8haLwFkVhP0XhdWJAxs6dKz8WFNxOF4vJBvnvVm8vL28m8PnV4KGhOJ6a+qiKYXoAoDo7Lu7t
O7m598ZjY33wXorCborCTmIila4M5ecPf6pQkMb7uxtvP3jQOB8U9DScJE9NfaRJTu4GgEoAII0MqU9L
e31EpZq6HR/vg39HUfg1ReFVisJRkWj5lkp1FwC2uU3MSuW4PTx81eSVDPM9AFR54N6Tyl+bkcGO5OdP
XYqPd/0X/uWGDXhLJFo2yOWzAJAJAMEwVFJSPCeX22yhob7kY1lZf9SsDvfKvy4tLWVUqZy8mZCAVzxw
Q0rKykW5/GHOjh2f+AzIxtRdWHjSmJvrGGBZF0leJ5H0PgPulb969+5kUo6LLOsyyGQrl+Xy2Rf5/HMA
oPSViLzMREQEDqtUn9/PzbVcysgYpiiqDgAYABCsAffK/2pR0Ts/K5Wz/QqFNTsqipwFKgAI80xX37vk
RxAASABgv+e63uX/HACQqUlmzgEAeHXVtQAAPE9icliTK/m/HpEQZP9/yTOIoS/Y32gy2FQGa9GfAAAA
AElFTkSuQmCC
</value>
</data>
<metadata name="MENU_DEL_SEP_1.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
@@ -162,88 +163,91 @@
<data name="BTT_CLEAR_SELECTED.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAABl0RVh0U29m
dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAVoSURBVEhLhZVrTJNXGMdfrtNSQIoadKRz2o0CorU3
WkDIVBRaaGNbwAteh+AARRQlitEYTTRekiX7sH3YPmyZH9wtziybigLRCWTaCW5sCBWhlrb0Ci9zSxbo
2f+UliGX7SS/tO85z/k9T57zXhhCCPO7Wh3VIhB83JKQ0Nu4bNlHm5YseZ1hmHC69n+Y5HLFcz7/ft/S
pY+vr1hhwL4oEBJcZ0x793If5uZ+1VNfT/qvXCHP6+p8tzMymqRxcW8hMGKqbDo9MlmWddu2AfbiRTJ6
+TIZKC52fyAUVi2JiYkLJmGaBYIPnx4+TPrOnCH9p08TC4LNx46RWwrF/ZXR0W/PleRZZuY669atZvbS
JcJiL9vQQEZPnSKmwkLPjcTE97GPB8KZlvh4C5X31dWRgRMniAVBtvPnyWB9ve+2XP7jmtjYpOlJTOnp
G60lJRZaOZWPQs4ePUpGUZh3xw7SnJDQhT0KEM3c5fOv9paVkX4kMAPL8ePEig1D584RG9rVpFS2rY6J
EQaTmKTSjbbiYsvIhQuERTGjKIrFvtHaWjK8fz9plsudexYu/BLxKsBj9ALBGzel0vt9e/b4XiBoENhQ
zRDOxIWWOY4cIS0KRZs4Nja5QyLJtRoM1pGzZ/0tYVExi/ayNTVkBPJ76enuJA7nM4j3gVWAHjgTIYqL
E96SStvMu3YR64EDxF5dTYYOHSJOJPNA5Kiu9rUrlZ1mrdbCnjzpr5jFGotYtqpqQi6TuVM4nKvwlYHU
gDzU31OMSGl8fPJtsbjVsn27z15RQRzAVVlJ3BB4kcx78CAZQbUjVIxrFtd+OdrbmpHhEXG5VE4rTwHz
wMRdFDw4jEgFj5dyRyRqsxYVEcfu3cQFPPv2ES8qHEbCYRzgsFZLvO+8Q7xKJXGDVoXCK46Ovob95YBW
Ph/8+xwE/wSTyHi81OZVq9qsGs2Ye8sW4srPJy6JhDgTE4kzOpo4IyKIMyyMOLhcX9Py5R4lj0cPtAKs
BBwwKfc7p174J5BEhHY9FIk6bBDaIRuiQkDFfsLDSbdU+pdBKPwe8e+BNDBD7vdNn6BYd+6stK5da7bP
nz9TDujcoEAw1lJY+CyFz9dCHDubnDJjwltRccS5fr3TjurnlIMBYE5NJY8Nhq7SrCwREsz6xL9y4S4v
b3Bt2uSyR0XNkDvQe9ouKu8HvaGh5FfQIxL5OgyG30qUStqmGUkm/3jKy0+48vLcs1XuiI8nL/Ly/rYl
JfmovCcgN4JW+l8iGe8oKuoqzcyckSQob3CpVB47l+sXv9KWxYtJt0r1x9ns7HZjQYHNnJxMfoH0EXgA
7oFm0CmTjRsNhs6Na9bQF+Tkq57xlJXVu9Rqz9Bs8kWLSG9BwcsqieQONlXnpaaWdul0z7rR+6C8CTSC
m8Aol4+36/XGT7VaevCRIIRx6/WWoQULZq2cyveLxY0IrAT0IHm1OTmZT3Q6U2da2qT8B/Ad+BZ05OSM
GXW6p4hdBiIZZ1FRt5vPn6vyuwiqCsj9Xyq6qXbDBkWnXm/6OS3NN1X+dUgIeZSdPXZPoxlEXC6IY9pL
S7faNBqXC9Iplf95YBb5ZF+RpGbdunQcbO/D1avJ9YC8LT19/Iv8/BeqpKRPEDORAGNeY3HxSYtG43Eq
FL5etfpljUzWhPlZ5VOTlGVliR+hHUbs+0mpHP9GpRqM5XAuY20zmGgRRohYKIx9rNd/3qfTOa7l5uLu
C63BvARw6fp0eRCMyBslJe8+2bx58EFhoVMlFNJvgQ4kgggQEgykvV0ApEAd+J3z8Z8KxmuA3pr0zikA
b4LJZ2FqYBigFdOPNf0NC679Fxi0OPr+XxiAJgwURph/AJfOQQebMR8TAAAAAElFTkSuQmCC
dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAWXSURBVEhLhZVrTFN3FMCPt+ImtCCtEHSkc9qNQkVr
X7SAGB8oFCixD/ABiq5SxrtYIFIDIZpoUJMl+7B92D5smR/cK84sm4oC0QlkygQ3NgREqKWFPqHMLVmg
Z/nX0lQo2YeTe5N77+937jnn//8DIsKfOTkRXRzOp11xcSPtmzZ9cmDDhrcAYDV59n8xKpFIn7PZ98c2
bnx8fcsWNQBEAMCqxecwevIk/WFm5jfDjY04fuUKPjcYvLdTUztE0dHvAkDYUmBwDIvF6ZYjRyY8bW04
d/kyThQUOD/icis2REZGL0qgk8P5+GldHY61tuJ4Swua29rQ1NCAt6TS+1sZjPdWkjxLS9tjOXzY5Ll0
CT2tregxGnGuuRlH8/JcN+LjPwQAJqkCdLFYZgIfMxhwoqkJzc3NaL1wAScbG723JZKfd0RFJSyVjKak
7LcUFppJ5gQ+ZzSip74e5xoa0F1cjJ1xcYMAIAUABtxls6+OaLU4bjCgyWBA85kzaDEacfr8ebQaDN4O
maxne2Qkd1EyKhLttxYUmGcvXkRPczPONTWhx2DAOb0eZ8rKsFMisZ9Yv/5rAJD7/kLF4bx9UyS6P3bi
hPeFXo+Tej1a6+txurERHS0taDt9Gruk0h5BVFRiv1CYaVGrLbPnzvlK4mloQE9dHXpqanC2rAzvpaQ4
E8LDvwCAUwCwzd9wCONHR3NviUQ9puPH0VJVhVOVlThdW4t2vR5dRiPaKiu9vTLZgCk/3+w5e9aXsUev
R09VFXoqKl7BxWJnUnj4VQDQAgDPD6dejRLAGhGLlXhbIOg2Hz3qndLp0KbToaO8HJ0VFeiurUV3dTXO
1tXhLAHX1qKnuvoVXKvF7tRUF59OJ3CSeRIAvBmYosXGEYmUyUy6w+f3WDQatJWUoKOkBF2nTqG7rAxn
dDqcKS7Gmfx8dO/ejW6ZDJ0yGXZLpW4Bg3ENAEr9ma99bR0ETweRiJlMXue2bT0WhWLeeegQOrKz0SEU
oj0+Hu0MBtrDwtBOo6GNTvd2bN7skjGZpKE6ANgKAOHB8GWCRQmfxUp8yOf3WxkMnKLRcJoAaTQf2Ber
V+OQSPSPmsv9EQA+AIDkUPCQAhKWY8fKLTt3mqbWrl0O9wsnOZz5rry8Z0lsdj4ARIWChxS4dbrT9r17
7VMMxspwGg0naDQ08Xj4WK0eLEpP5y9djCEFztJSo+PAAcdURMQyuC0szFcuAh+n0XCEovB3isJhPt/b
r1b/USiTkTItkwRuXKWlTY6sLGeozG0sFr7IyvrXmpDgJfBhP7yPorCb3AuFC/0azWBRWtoyySLc6JDL
XVN0ug/8WlliY3FILv/rXEZGb19urtWUmIi/URQ+oih8QFF4j6Kwk6JwQCxe6FOrB/bv2EE2yMBWDy6t
ttGRk+OaDgWPicGR3NyXFULhHQCozOLxigaVymdDPF4A3kFR2E5ReJP8kUSy0KtS9X2en08av4Y0Hpwq
lXl63brl8NhYH7xMIGgHgHIAII1k6nftSnuiVI4OJCcH4D9RFP5AUfg9RWH/rl3zfUrlUwDY5JPYNZoh
J5u9UuZ3AaDCD/edVOQj/b590gGVavTX5GRvMPzbVavwUUbG/D2FYhIAMgEgGnqLig5bFQqHIyYmOPO/
q0LAA3UFWFOzZ09Kv0Yz8nD7drzuh/ekpCx8lZ39Qp6Q8FlAQDam9oKCs2aFwmWXSr0jOTkva8TijpXg
wRJterrgkVL5tE8q9f4iky18J5dPRoWHXwaAg4ESkY8FXG7UY5XqyzGl0nYtM7OPoqgaABACAD0UPFhy
o7Dw/ScHD04+yMuzy7lcchYoASCejKuvpP4XSW3XAYAIAHL81xWX/xLJGwBARpNMTi4AvBO8FoJfpPkz
Joc1udKWwkKFPzmy/6/3BxEGEvsPl85BBwUq8igAAAAASUVORK5CYII=
</value>
</data>
<data name="BTT_CLEAR_DONE.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAVDSURBVEhLjZVtTFNXGMcLQmdHO6AdarLSOcTxXqC3lls3
FeVFihZsqaBjRM2ouILiC0oUozGaaBSTJfuwfdg+bJnJnFuiyzKZMOp8ATJl4IYLAjqspfS9cNlLskjP
/qe0OmPZeJJfbu85z/k//z7n3Ht5NPqLimLMSUkfXV20aPiKTPbhOqn0FQxHBSb/J4ZUKvY3mez6yMKF
fReTkw0YigERgUkaAyaT8FZR0ZdDzc1k9OxZ0tHU5L/Csp15ItFSTEfPZIWPQYZ507Z580Pu9Gky1dpK
hisrPe+nptbLYmPjMT1TpHPJkg/u7d1LHhw7RkaPHiVWJFsOHCBtLHtdLhK9jpSwRUbU6jUQt3BnzhAO
a7mWFjJ15Ai5p9V6v5ZK30OKGETxrkokVir+oKmJPDx0iFiRNH7yJLnW3Oy/olLdyBKJUpD4TBG0pdhW
VWWlzqn4FMS5/fvJFIz5amqIOSHhLtJYIOJ1JCaeG66tJaMoYAHWgweJDQscJ06QG2hXB8t2ZwqFqUgO
FBlSKIrHKyutk6dOEQ5mpmCKw7qpPXvIxI4dpE2lcm2RSC4gtRSIefrFi19tUyqvt2/b5n+EpDEwDjcO
7IkbLXPu20euokiuUJj2E8MUjRkMtsnjxwMt4eCYQ3u5xkYyCXFzbq4nRSD4FMJGIAd0w3nRjESSiiLd
li1biG3nTmJvaCCO3buJC8W8EOpuaPD3qNV37peXW7nDhwOOOcxxyOXq65+IpwkE56BXCzKC4pEgEPxc
sTjtO4Wi64fqar+9ro44gdtkIh4I+FDMt2sXmYTbSSqMew73AXG0t0up9MqFQipOnaeD+eDpUQ0GXyUW
p3dkZ3fbNm4kzq1biRt4jUbig8MJFJzABk6UlxPf6tXEp1YTD+jKy/PliETnsX47oM4F4DnxUPCXxcdn
dMjl3QMZGY89mzYRt0ZD3AxDXFIpcYlExBUdTVzz5pFuodDfnpTkZePi6IbWgUzwIphVPBSBdt2Sy/vH
IWiHmAM4ARUOEBVF+rKz/zKkpn6L/HdBFpiTeCBsNTUmm1ptsQsEz4sDOjaQnPzYrNXeT0tMLMeSWDA3
cZ/RuM9VUOCyw/1s4mPgIbBkZJA+ne7uW8uX52Dpf75WAuE2Gluca9e67TExz4k70XvaLio+CoYjI8kA
uJyT4++vqPjVwLK0TbMX8W7ffshRUuIJ59wpkZBHJSV/30hJ8VPxoaB4L+gCZoaZ7qf/ZLYiOIot9oIC
r10oDAg/05YFC8hgaenvx1eu7Oldv37ckpZGfoHobXATXKMFKMuWTf9oMNwpkMvpC/Lpq95TW9tMxR3h
xBMSyOCqVX/UM0wHUhtKMjPfvltWdn8QvQ+Jd4J20EaLqFTTPRUVvZ9otXTj+SCC59mwweqIiwvvHOJ1
CkU7Ek2AbqR4d37+G316/cidrKwn4pfBN+AS6E9Pf3xbr7+H3MWAz7PrdIMemSyscxPDfI+k+qB46EvF
31NYyPZVVIxcyMry/1v8q4gIchsFzBrNGPKKQDyvp7p687hG43ZDNOR8oLj4z53hxUPBb8zPz8PGDt/K
ziYXg+LmvLzpzzWaR6VLl36MnJkCiPntlZWHrWVl3h6W9VPnjUplJ8ZnEw8F37hihYK24wusM6vV0xfg
/CWBoBVzOjDTIkQEk5QU26vXf/agrMx5vrAQpy+yEeMMENJ5mjRL8C9VVb3zs043dlOrdZWkpNBvgR5I
AT2uT9bSH3FACdYFr3N9/F8A9GjSk7MevAbCPnDzAHVMP9b0Su/nEtQEff+/HIQWDBrj8f4B7zPYbtFn
HR8AAAAASUVORK5CYII=
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAV6SURBVEhLjZVrTFNnGMcfT4Wto2dcOtRkyBzigALlcmo5
dVNwXKRowRYEGSNiRi3jXiwQqYEQTVxQkyX7sH3YPmyZicYt0WUXBEaJjkumDNxwQUCHWErpFQ5zSxbo
s7xd200Fxz95c9qcc37//3me9wIAAKOZmQGGiIiP+7ZsmewKD/9of1jYywCwkdz7P01Ipexv4eE3pjZv
HrkSGVkAAAEAsMH3wFhlpeBmZuYXE83NOH3+PHbpdK4ulu1NoekdAOD3GO0JjTPMG6bi4gdcRwcunTuH
k4WF9g+io6vCAwODfSa927d/eLehAe+3t+N0WxsaOzpwpqkJO1n2hpimX1vLZEome9NUXDzDnT2LXHs7
cno9LrW24l2FwvFVWNj7ABDirkKfUGgk8Ps6HT5oaUFjayvOnTmDfc3Nri6p9Id4mo560mRCKs0yFRUZ
SXICX9LrkWtsxKWmJnSWlqIhNPQOALAAQEPP1q0XJsvLcVqnwxmdDo0nTqBJr8f506fxuk7n6mHZwTiB
INprMpGcnDVXWGhcfO895FpbcamlBTmdDpe0WlyoqMBOqdR6RCi8DAA57q9Qbdv2SqdEcuPa0aOuh1ot
zmq1ONfYiPPNzWhra0PL8ePYx7KDSQJBzE8MkzlbUGBaPHXKXRKuqQm5hgbk6upwsaICDUlJ9ig+/zMA
UAOA2NNw8GOEwuhOiWRw5sgRNNXUoLm6Gufr69Gq1aJDr8f+6mrXkEx2+15enpE7edKdmNNqkaupQa6q
ygeP4fMvAEA5AMR64JS3rP5JISEx15KTBwwlJS6zRoMWjQZtlZVor6pCZ309OmtrcbGhARcJuL4eudra
f+Dl5TggkTjEAgGBk+QiAHj+sanqNZGGhIh6EhIGTYcOoaWsDG1lZehQq9FZUYELGg0ulJbiQl4eOvfu
RadMhnaZDAdSUpyJNH0JAI55kvNXg3vlvzM4OLZHLB4cE4mW7YcPo00uRxvDoDUsDK00jVY/P7TyeNgv
ELi6IyIcbFAQaagGAOIA4IVnwb1yl+umWDw6R9No5vFwnsdDC4/nBrvHxo04kpDwZ0F09LcA8C4AxK8X
7paptLTSJJPNmPn8p+Eew7HIyGWDQnEvZuvWPAAIXDfcqVYft6anW800vSZ8lsfDBzwezsTG4ohSeeet
XbsSn1yMq8qmVust+/bZzAEBT8Etfn7uchH4NI+HkxSFYxSF3yQmukbz838tYFlSprVNHMeOtcxnZ9tX
S24RCvFhdvZf16OiXAQ+4YEPUxQOUBQaGGZllHzJWiYOtVpvTk93mAUCN/ixsmzahOM5Ob+f2rNnaPjA
gbmZmBj8haLwFkVhP0XhdWJAxs6dKz8WFNxOF4vJBvnvVm8vL28m8PnV4KGhOJ6a+qiKYXoAoDo7Lu7t
O7m598ZjY33wXorCborCTmIila4M5ecPf6pQkMb7uxtvP3jQOB8U9DScJE9NfaRJTu4GgEoAII0MqU9L
e31EpZq6HR/vg39HUfg1ReFVisJRkWj5lkp1FwC2uU3MSuW4PTx81eSVDPM9AFR54N6Tyl+bkcGO5OdP
XYqPd/0X/uWGDXhLJFo2yOWzAJAJAMEwVFJSPCeX22yhob7kY1lZf9SsDvfKvy4tLWVUqZy8mZCAVzxw
Q0rKykW5/GHOjh2f+AzIxtRdWHjSmJvrGGBZF0leJ5H0PgPulb969+5kUo6LLOsyyGQrl+Xy2Rf5/HMA
oPSViLzMREQEDqtUn9/PzbVcysgYpiiqDgAYABCsAffK/2pR0Ts/K5Wz/QqFNTsqipwFKgAI80xX37vk
RxAASABgv+e63uX/HACQqUlmzgEAeHXVtQAAPE9icliTK/m/HpEQZP9/yTOIoS/Y32gy2FQGa9GfAAAA
AElFTkSuQmCC
</value>
</data>
<data name="BTT_CLEAR_ALL.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAVDSURBVEhLjZVtTFNXGMcLQmdHO6AdarLSOcTxXqC3lls3
FeVFihZsqaBjRM2ouILiC0oUozGaaBSTJfuwfdg+bJnJnFuiyzKZMOp8ATJl4IYLAjqspfS9cNlLskjP
/qe0OmPZeJJfbu85z/k//z7n3Ht5NPqLimLMSUkfXV20aPiKTPbhOqn0FQxHBSb/J4ZUKvY3mez6yMKF
fReTkw0YigERgUkaAyaT8FZR0ZdDzc1k9OxZ0tHU5L/Csp15ItFSTEfPZIWPQYZ507Z580Pu9Gky1dpK
hisrPe+nptbLYmPjMT1TpHPJkg/u7d1LHhw7RkaPHiVWJFsOHCBtLHtdLhK9jpSwRUbU6jUQt3BnzhAO
a7mWFjJ15Ai5p9V6v5ZK30OKGETxrkokVir+oKmJPDx0iFiRNH7yJLnW3Oy/olLdyBKJUpD4TBG0pdhW
VWWlzqn4FMS5/fvJFIz5amqIOSHhLtJYIOJ1JCaeG66tJaMoYAHWgweJDQscJ06QG2hXB8t2ZwqFqUgO
FBlSKIrHKyutk6dOEQ5mpmCKw7qpPXvIxI4dpE2lcm2RSC4gtRSIefrFi19tUyqvt2/b5n+EpDEwDjcO
7IkbLXPu20euokiuUJj2E8MUjRkMtsnjxwMt4eCYQ3u5xkYyCXFzbq4nRSD4FMJGIAd0w3nRjESSiiLd
li1biG3nTmJvaCCO3buJC8W8EOpuaPD3qNV37peXW7nDhwOOOcxxyOXq65+IpwkE56BXCzKC4pEgEPxc
sTjtO4Wi64fqar+9ro44gdtkIh4I+FDMt2sXmYTbSSqMew73AXG0t0up9MqFQipOnaeD+eDpUQ0GXyUW
p3dkZ3fbNm4kzq1biRt4jUbig8MJFJzABk6UlxPf6tXEp1YTD+jKy/PliETnsX47oM4F4DnxUPCXxcdn
dMjl3QMZGY89mzYRt0ZD3AxDXFIpcYlExBUdTVzz5pFuodDfnpTkZePi6IbWgUzwIphVPBSBdt2Sy/vH
IWiHmAM4ARUOEBVF+rKz/zKkpn6L/HdBFpiTeCBsNTUmm1ptsQsEz4sDOjaQnPzYrNXeT0tMLMeSWDA3
cZ/RuM9VUOCyw/1s4mPgIbBkZJA+ne7uW8uX52Dpf75WAuE2Gluca9e67TExz4k70XvaLio+CoYjI8kA
uJyT4++vqPjVwLK0TbMX8W7ffshRUuIJ59wpkZBHJSV/30hJ8VPxoaB4L+gCZoaZ7qf/ZLYiOIot9oIC
r10oDAg/05YFC8hgaenvx1eu7Oldv37ckpZGfoHobXATXKMFKMuWTf9oMNwpkMvpC/Lpq95TW9tMxR3h
xBMSyOCqVX/UM0wHUhtKMjPfvltWdn8QvQ+Jd4J20EaLqFTTPRUVvZ9otXTj+SCC59mwweqIiwvvHOJ1
CkU7Ek2AbqR4d37+G316/cidrKwn4pfBN+AS6E9Pf3xbr7+H3MWAz7PrdIMemSyscxPDfI+k+qB46EvF
31NYyPZVVIxcyMry/1v8q4gIchsFzBrNGPKKQDyvp7p687hG43ZDNOR8oLj4z53hxUPBb8zPz8PGDt/K
ziYXg+LmvLzpzzWaR6VLl36MnJkCiPntlZWHrWVl3h6W9VPnjUplJ8ZnEw8F37hihYK24wusM6vV0xfg
/CWBoBVzOjDTIkQEk5QU26vXf/agrMx5vrAQpy+yEeMMENJ5mjRL8C9VVb3zs043dlOrdZWkpNBvgR5I
AT2uT9bSH3FACdYFr3N9/F8A9GjSk7MevAbCPnDzAHVMP9b0Su/nEtQEff+/HIQWDBrj8f4B7zPYbtFn
HR8AAAAASUVORK5CYII=
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAV6SURBVEhLjZVrTFNnGMcfT4Wto2dcOtRkyBzigALlcmo5
dVNwXKRowRYEGSNiRi3jXiwQqYEQTVxQkyX7sH3YPmyZicYt0WUXBEaJjkumDNxwQUCHWErpFQ5zSxbo
s7xd200Fxz95c9qcc37//3me9wIAAKOZmQGGiIiP+7ZsmewKD/9of1jYywCwkdz7P01Ipexv4eE3pjZv
HrkSGVkAAAEAsMH3wFhlpeBmZuYXE83NOH3+PHbpdK4ulu1NoekdAOD3GO0JjTPMG6bi4gdcRwcunTuH
k4WF9g+io6vCAwODfSa927d/eLehAe+3t+N0WxsaOzpwpqkJO1n2hpimX1vLZEome9NUXDzDnT2LXHs7
cno9LrW24l2FwvFVWNj7ABDirkKfUGgk8Ps6HT5oaUFjayvOnTmDfc3Nri6p9Id4mo560mRCKs0yFRUZ
SXICX9LrkWtsxKWmJnSWlqIhNPQOALAAQEPP1q0XJsvLcVqnwxmdDo0nTqBJr8f506fxuk7n6mHZwTiB
INprMpGcnDVXWGhcfO895FpbcamlBTmdDpe0WlyoqMBOqdR6RCi8DAA57q9Qbdv2SqdEcuPa0aOuh1ot
zmq1ONfYiPPNzWhra0PL8ePYx7KDSQJBzE8MkzlbUGBaPHXKXRKuqQm5hgbk6upwsaICDUlJ9ig+/zMA
UAOA2NNw8GOEwuhOiWRw5sgRNNXUoLm6Gufr69Gq1aJDr8f+6mrXkEx2+15enpE7edKdmNNqkaupQa6q
ygeP4fMvAEA5AMR64JS3rP5JISEx15KTBwwlJS6zRoMWjQZtlZVor6pCZ309OmtrcbGhARcJuL4eudra
f+Dl5TggkTjEAgGBk+QiAHj+sanqNZGGhIh6EhIGTYcOoaWsDG1lZehQq9FZUYELGg0ulJbiQl4eOvfu
RadMhnaZDAdSUpyJNH0JAI55kvNXg3vlvzM4OLZHLB4cE4mW7YcPo00uRxvDoDUsDK00jVY/P7TyeNgv
ELi6IyIcbFAQaagGAOIA4IVnwb1yl+umWDw6R9No5vFwnsdDC4/nBrvHxo04kpDwZ0F09LcA8C4AxK8X
7paptLTSJJPNmPn8p+Eew7HIyGWDQnEvZuvWPAAIXDfcqVYft6anW800vSZ8lsfDBzwezsTG4ohSeeet
XbsSn1yMq8qmVust+/bZzAEBT8Etfn7uchH4NI+HkxSFYxSF3yQmukbz838tYFlSprVNHMeOtcxnZ9tX
S24RCvFhdvZf16OiXAQ+4YEPUxQOUBQaGGZllHzJWiYOtVpvTk93mAUCN/ixsmzahOM5Ob+f2rNnaPjA
gbmZmBj8haLwFkVhP0XhdWJAxs6dKz8WFNxOF4vJBvnvVm8vL28m8PnV4KGhOJ6a+qiKYXoAoDo7Lu7t
O7m598ZjY33wXorCborCTmIila4M5ecPf6pQkMb7uxtvP3jQOB8U9DScJE9NfaRJTu4GgEoAII0MqU9L
e31EpZq6HR/vg39HUfg1ReFVisJRkWj5lkp1FwC2uU3MSuW4PTx81eSVDPM9AFR54N6Tyl+bkcGO5OdP
XYqPd/0X/uWGDXhLJFo2yOWzAJAJAMEwVFJSPCeX22yhob7kY1lZf9SsDvfKvy4tLWVUqZy8mZCAVzxw
Q0rKykW5/GHOjh2f+AzIxtRdWHjSmJvrGGBZF0leJ5H0PgPulb969+5kUo6LLOsyyGQrl+Xy2Rf5/HMA
oPSViLzMREQEDqtUn9/PzbVcysgYpiiqDgAYABCsAffK/2pR0Ts/K5Wz/QqFNTsqipwFKgAI80xX37vk
RxAASABgv+e63uX/HACQqUlmzgEAeHXVtQAAPE9icliTK/m/HpEQZP9/yTOIoS/Y32gy2FQGa9GfAAAA
AElFTkSuQmCC
</value>
</data>
<metadata name="MENU_DEL_SEP_2.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
@@ -252,30 +256,31 @@
<data name="MENU_DEL_CLEAR.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAVGSURBVEhLjZVtTFNXGMcLQmdHO6AdarLSOcQBAgV6a1/c
NGgBKVqwpYKOETWj4gqKLyhRjMZoovElWbIP24ftw5aZzLgluiwbE0aNjpdNGejUIW9iKaX0FS5zSxbo
2f+UVmcsG0/yy+095zn/59/nnHsvh0ZPfn6MJSnpk+tLlvRfk0g+3iAWv4bhqMDk/0SfQqF6JJHcHFi8
uPtKcrIRQzEgIjBJ457ZzL+Vn/9VX0MDGb5wgfxeX++/plK1KgWC5ZiOns0KH70M87Z969bH7NmzZOr8
edJfVub5MDW1RhIbG4/p2SKty5Z99HD/fjJ04gQZPn6c2JBsPXSINKlUN6UCwZtICVtkQK1eB3Ere+4c
YbGWbWwkU8eOkYc6nfcbsfgDpAhBFOe6SGSj4kP19eTxkSPEhqSx06fJYEOD/5pC8VOmQJCCxOeKoC0F
9vJyG3VOxacgzh48SKZgzFdZSSwJCfeRpgICTkti4sX+qioyjAJWYDt8mNixYPzUKfII7WpRqToy+PxU
JAeK9MlkBWNlZbbJM2cICzNTMMVi3dS+fWRi1y7SpFC4tolEl5FaBIQcw9KlrzfJ5Tcf7NjhH0HSKBiD
m3HsiRstcx44QK6jSA6fn/Yrw+SPGo32yZMnAy1h4ZhFe9m6OjIJcUtOjieFx/scwiYgBXTDOdGMSJSK
Ih3WbduIffdu4qitJeN79xIXinkhZK2t9Xeq1XcGS0ps7NGjAccs5ljksjU1T8XTeLyL0KsC6UHxSBAI
bo5QmPaDTNY+UFHhd1RXEydwm83EAwEfivn27CGTcDtJhXHP4j4gjva2y+VeKZ9PxanzFWAheHZUg8FV
CIUrWrKyOuybNxPn9u3EDbwmE/HB4QQKTmADJ0pKiG/tWuJTq4kHtCuVvmyB4BLW7wTUOQ+8IB4K7sr4
+PQWqbRjqKBg2rNlC3FrtcTNMMQlFhOXQEBc0dHEtWABsfL5/uakJK8qLo5uaDXIAC+DOcVDEWjXLam0
ZwyCDoiNAyegwgGiokhPVtZfxtTU75D/PsgE8xIPhL2y0mxXq60OHu9FcUDHBpOTpy063WBaYmIJlsSC
+Yn7TKYDLo3G5YD7ucRHwWNgTU8n3Xr9/XdWrcrG0v98rQTCbTI1OtevdztiYl4Qd6L3tF1UfBj0R0aS
e+Budra/p7T0gVGlom2au4h3584j44WFnnDOnSIRGSks/PtRSoqfivcFxbtAO7jNMDM99J/MVQRHsdGh
0XgdfH5A+Lm2LFpEeouK/ji5Zk1n18aNY9a0NPIbFQVt4AawgJ9Xrpz5xWi8o5FK6Qvy2aveU1XVQMXH
w4knJJDevLwnNQzTgtTawoyMd+8XFw/2ovch8VbQDJpAm0Ix01la2vWZTkc3ngsiOJ5Nm2zjcXHhnUO8
WiZrRqIZ0I0U7s3NfavbYBi4k5n5VPx78C24CnqUyunbBsND5C4FXI5Dr+/1SCRhnZsZ5kck1QTFQ18q
7r68PFV3aelAe2am/9/iX0dEkNsKxbRFqx1FXj6I53RWVGwd02rdboiGnN8rKPhzd3jxUHDrcnOV2Nj+
W1lZ5EpQ3KJUznyp1Y4ULV/+KXJmCyAWNpeVHbUVF3tHVCp/r0bzpE4ub8X4XOKh4JpWr5bRdrRh3Q21
euYynL/C453HnB7MtggRwSQlxXYZDF8MFRc7L+Xl4fRF1mGcAXw6T5PmCO7V8vL37ur1o206naswJYV+
CwxADOhxfbqW/ogDcrAheJ3v4/8SoEeTnpyN4A0Q9oFbAKhj+rGmV3o/n6Am6Pv/1SC0YNAYh/MPME3a
dCWdzmEAAAAASUVORK5CYII=
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAV8SURBVEhLjZVrTFNnGMcf3wpbR8+4dKjJgDnEAQLl0lJO
3TTcpWjBFgQZI2JGLeNeLBCpgRBNNIjJkn3YPmwftsxkxi3RZdmYMCBDgagM3GBBLg6wlNIrlLklC/RZ
3q7tpoLjn5ycNuec3///Ps97AQCA0YwMn97Q0I/7du2auhkS8tHhoKBXAWA7ffZ/mhSL2d9CQvqnd+4c
uR4Wlg8APgCwzfPCWEUF725GxpeTTU04e/kyjms0jpss25PEMHsBwOsJ2lOaEArf0hcVzdnb23G1owOn
CgosH0REVIb4+vp7THr27PnwQX09Pmxrw9nWVtS1t+N8YyN2smy/gGHe2MxkWiJJ1RcVzdsvXUJ7Wxva
tVpcbWnBBzKZ9eugoPcBIMBZhT4+X0fhDzUanGtuRl1LCy5euIBTTU2Om2LxrRiGCX/aZFIsztQXFupo
cgpf1WrR3tCAq42NaCspwd7AwHEAYAGAge7g4CtTZWU4q9HgvEaDujNnUK/V4tL58zij0Ti6WXYwmseL
cJtMJiRkLhYU6FYuXkR7SwuuNjejXaPBVbUal8vLsVMsNp3g868BQLZzFIrdu1/rFIn6x06edDxSq3FB
rcbFhgZcampCc2srGk+fxj6WHYzn8SJ/EgozFvLz9SvnzjlLYm9sRHt9Pdpra3GlvBx74+Mt4VzuZwCg
BACBq+HgJeTzIzpFosH5EydQX12NhqoqXKqrQ5NajVatFmerqhxDEsn9mdxcnf3sWWdiu1qN9upqtFdW
euCRXO4VACgDgCgXnLjL6h0fEBD5fULCwGRxscOgUqFRpUJzRQVaKivRVleHtpoaXKmvxxUKrqtDe03N
P/CyMhwQiawCHo/CafJ9APDiE1PVbSIOCNjXHRs7qD92DI2lpWguLUWrUom28nJcVqlwuaQEl3Nz0ZaS
gjaJBC0SCQ4kJdniGOYqAJxyJeduBHfLO9HfP6pbIBicTk9fsxw/jmapFM1CIZqCgtDEMGjy8kITh4Oz
PJ6jKzTUyvr50YaqACAaAF56HtwtZ7nuCgSjiwyDBg4HlzgcNHI4TrDz2r4dR2Jj/8yPiPgWAN4DgJit
wp3Sl5RU6CWSeQOX+yzcZTgVFrbWK5PNRAYH5wKA75bhNqXytCktzWRgmE3hCxwOznE4OB8VhSNy+fjb
+/fHPb0YN5RZqdQaDx0yG3x8noEbvbyc5aLwWToCQnCMEByNi3OM5uX9ms+ytEybm1hPnWpeysqybJTc
yOfjo6ysv2bCwx0UPumCDxOCA4TgHaFwfZSOZDMTq1KpNaSlWQ08nhP8RFl27MCJ7Ozfzx08ODR85Mji
fGQk/kII3iMEbxOCPxKCvYTgYGLi+p38/PtpAgHdIP/d6i1lZU0UvrQRPDAQJ1JTH1cKhd0AUJUVHf3O
eE7OzERUlAfeQwh2EYKdhGC/WLw+lJc3/KlMRhvv7Wy85ehR3ZKf37Nwmjw19bEqIaELACoAgDYyoC45
+c0RhWL6fkyMB/4dIfgNIXiD9iQxce2eQvEAAHY7TQxy+YQlJGTD5BVC4Q8AUOmCu08qb3V6OjuSlzd9
KybG8V/4V9u24T2RaK1XKl0AgAwA8Ieh4uKiRanUbA4M9CQfy8z8o3pjuFvetcnJSaNy+dTd2Fi87oL3
JiWtfyGVPsreu/cTjwHdmLoKCs7qcnKscyzrmEhJeVwrEvU8B+6Wt/LAgQRajn6WdfRJJOvXpNKFl7nc
DgCQe0pEPxaGhvoOKxSfP8zJMV5NTx8mhNQCgBAAeJvA3fK+UVj47s9y+cJtmcyUFR5OzwIFAAS5pqvn
W/rDDwBEAHDYdd/q8n8BAOjUpDPnCAC8vuFaAACOKzE9rOmd/t+KaAi6/7/iuqihJ9jfOenaM1O34/0A
AAAASUVORK5CYII=
</value>
</data>
<metadata name="TOOLBAR_BOTTOM.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
@@ -287,105 +292,123 @@
<data name="BTT_ADD.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAANoSURBVEhLrZVrSFNhGMePTbwkzoVlzUuUZdkizzIvFE5r
K51a0bzU8kYXp2W4El1SGdL1axh9iz5IRPQljD51gUyitIvOOjNdytxNZ4Hh9xP/nvc0yQ+iYeeBPwze
l//veZ/3/55xi1VYh6Iz/LYC84mtBbctvZiRxq4R9QM67BvUwSiQHDqUuwwSJLht6SUBPmvE5T0clO84
xH7gkPApFBZfkYwAQSPG9HJY9SEE6wbCoRWUOB8olRfAOk+2hyN9KAa7h1fj+o8q+QA8AZLtEcgcUqHA
GU/z34iO6Tr5ACcIkOFQociZgIrxzahza3FvxiofoFlIE43fElHt3oIGTzqavbvwYMb2b4CFcj6r0td5
v6rcqWj0ZKB1QocrgXx0/mzByoeRC0r9OKpT6tAwm3HKd/GwDiVOPWpcBWj0HsQFfzluBGrodwYu+nNx
NVCAa1NGdE634P60TQLdnbbi1o9aXJ2qxHl/GY2wGA3uI1B3RUECMHPlW8p4H4fEgTC6VKWUFNNYMo67
t+KsNxuXJvaQgRE3p4okwJXJfDLToc6jxeGxFBhG1oB3KJHUHwYV+RxxmP4CWOfMnC3yjmjarEZp0Pyc
Lxttfj2uBYwEyMflST1s/hy6ix04Nq7BodH1yBuOwzYhGvEDoVC95xBJj7J0cA6geEiHtWSupQ4MTrUU
Q4uHR5MvCxcndqONTC/482Dz5eCcNxP1tFbt1uDA6Drk0kkl8/5QsMfIzEOeczC9nwM46tyLnUOxFMNE
VLpSYaXLbKGktJIhM2327ZLURKOqp5GwNO0fXYucr3HY8iUK6o8KRNOII7o5cM9IXQR4MwdwmnJuE7Ri
u1BNOkmqJ9WRTpHOiHe+nybzLFjcPMzjm1A0moRWbw0KX+X9qhW2i1UCL5pJWhL/mfSJ1MeLEoDFdL6I
zYptahdaRYsnDWZXCgrplNl02gaPWVpbUCymixXb2C40iYfpXgpGErCDXvQGeySOuUokk+C2pdcfQIOY
PxIvfejY1zSWklLhDM74f+sPoFbc7ohBIiVlBcWZ/TeUzeb8f4uZWIVKMak/HCqK4fLXHBQvKCV2GQEn
KGHMPII6X8Zi+JQAvTICKil+kdQ5Rw+Ie0J6RIAeGQFlX0zSSEx9JHpApm7SS7kAlGVmNK8WzTnH/Qao
hKygM1JCJAAAAABJRU5ErkJggg==
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAOLSURBVEhLrZVfTFNXHMePQvhjQ+nSTVcoxjH8d7dLO0XJ
CFdc62hX1OwKKJM/cQ7K7AKTQCE6TSM6X43L3pY9NMuy7MWw7GlqohLjKM5S9dwOOkzpvS20M2Hx/Zjv
cm9FkRjUcL/J7+mcfD8nJ59zLyEvSd7FnGD+dzl40ahrS/e/dtQiLsIxx4SAj+8KcFMBbklAc9ypQZbu
f+1ogHscWzNKYPyTwHyboPROLrqSHh0BlGPFYwRv3V6FDRP5sFMjBtON+gLUk5dH8rEtWozdk+tw7mGb
fgCecqw8UoAdURNcsRI0xytwcd6rH+Ao5ViVZIInVorDM5vhTdjx46Ne/QD99H3m/seK9sRW+ORt6Fdq
8PMj/6sBlvN8YcRrdY/bElvQI1dhaFbAmXQ9gv8N4M1fCpcdyyVDUDuhc8FxSUDDpIADMQc64i70KPtx
ItWMb9Md6FGqcDK1C8NpF85m3AjOD+Cneb8G+mG+FxcedmI404rBVBO8iQb4EodgGTFAA6jlxlsE5hCB
dSIPNmrUTBEflOPzxHv4WqnGN7MfYTjjxvmMRwOcmavHYEqAV7bj4IONcE69DZtkRFk4D6YQwSFJfAZQ
T66Wq4s2qQjOKQsan5QfT1bjVMqBs2k3hjP1OD3ngD9VC5+8HUdmOHw6/Q7qJteCp0UomciFaZygcJSg
8e4iQENUwPpwHuySEc6YRdOwS7ahL7kTJ2d349ScAydSdfAna3Fc2YFu2Yb2BId90xuwa3JdtjycC/Ux
quWrLhOI44sAn8X24MOoGZ6YFa3xLeiVqzCg1GAoWauV9idrtOlTqtEt2zWb9k6vR+3fa7H1vgGWv3JQ
dIug4DoB+YOAjBCINxcBjlGO+WklC9B2FqBfsADtZgHqZQH6JQvQr9j3/x5Dn7ITXQkbWmY2wTNdhiGl
A65rdY87qY21UZ61UJ5VUp7x93jG3+EZH+KZBlA1XarXc6qNGBCgQ6xLrkRLfCM+iVlRHTXDJ7doa8uO
qunLkgX0sYPxCrimSrFdMuHdSCGOxA9kr2ClyQJ8rH6qRPvQqV9T8zjB4diTO15psoBO9oFUDGs4F2+E
CNR/Q9OC5yuNWtJLW1lZOB+mMYI1NwhyrhCIER0BR2klU8sLRglWqxr+TiCO6QhopTwrvEFALhOQ3wjI
rwTiqI6ApvuidiViSNQekHhdhHhVL8AlQ/Cp10vnFTz/HxZurHotmkteAAAAAElFTkSuQmCC
</value>
</data>
<data name="BTT_ADD_PLS_ARR.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAANqSURBVEhLrZVbSJNhGMe/mjhN1IWdpjPMssOitqklidPS
cksrmufyQAcPZWSKzqEZUla3UXQXXYyI6CaMrjpcmEV5KF31zXQt5k42Cxbdf/Hveb8meSEa+j3wh8H7
8v897/P+32/cQhV+U2aV35JhLrG10LbFFzNS29RC7qgeBz7oYeRJdj1KXXkiJLRt8SUCPqqFFf0cYt5y
iBvmkPA+DHW+AgkBvFqIHeCwengZkkbl0PIxaA8USwfYTgDWebJNjtSxWOwdX4urP6qkA+gIkGyLwK4x
BQyOeJr/JtwM1ksHOEWAdLsCBY4EHJ/cgnq3Fnd/NUkHaOW1gvGLCtXubWj0pKLVm4n7v8z/B5gv5zMq
fZXzu8q9Fec96bBM6XE5kA/rzzasehA5r5SPoqxih3kzGad8F47rUeTIRY3LgPPeI+jwl+JaoIZ+p6PT
n40rAQN6po2wBttwL2gWQXeCTbjxoxZXpivR7i+hERai0V0OZW8URAAzj3lDGR/koBoNh4ZiyJJi+pqM
k+7tuODNwMWpfWRgxPXpAhFw+Vs+melR79Gi7GsK8ibWQWOPQeJIOBTkU243/QOwzpk5W9TYo2mzEsUh
82ZfBrr8uegJGAmQj0vfcmH2Z9FdpOHEpBpHnRuQM74GO/hoxI+GQTHEIZIeZfGHWYDCMT3Wk7mWOshz
KMUY1nk0aPHtRufUXnSRaYc/B2ZfFpq9u9BAa9VuNQ47k5BNJxXNR8LAHiMzX/aMg2loFuCYYz/2jMVR
DFWodG1FE11mGyXFQobMtNWXKaqFRtVAI2FpOuRcj6zPa7DtUxSU72SIphFH9HHgnpJ6CfB6FuAs5dzM
pwrdfDXpNKmBVE86Qzon3P5+lsx3o86tQcXkZhQ4E2Hx1qCwL+d3LZ8mVPE6oYKUStJ9JL0nDeoEEcBi
OlfEZsQ2dfMWoc6zExWuFBykU2bQaRs9FeLavGIxXajYxm6+RSijezFMJCCNXvRGWyROuIpEk9C2xddf
QKOQPxEvfujY1zSOknLcEZrxUusvoFbQ2WOhoqSspDiz/4aSmZwvtZhJE18pJI7IoaAYrnjJQfacUmKT
EHCKEsbMI6jz5SyGTwgwICGgkuIXSZ1z9IC4x6SHBOiXEFDyySSOxDRIogdk6iO9kApAWWZGc2rBnHPc
H0WkrMjY2947AAAAAElFTkSuQmCC
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAOKSURBVEhLrZVfTFNXHMePg/DHhlLTqSsU41A3vUZPVZSM
cMW1G9SyLV4BZfIn6qBoF5gECtG5NOK218Vlb8seGrMsvhjMnuaWTMiiFLVUPbdChym9t4V2Jiy+H/M1
91Y2RhbUcL/J7+mcfD8nJ59zLyEvSN6lnGD+tzn4v9HWlu5/5WhFQkTgzgkR798T4WYi3LKIprhLhyzd
/8rRAfcFvnqUwHyLwHqboPRuLjqTHgMBTODFYwRrb6/Cxol8OJgZA+kGYwHaycsj+dgdLcaByfX48nGr
cQDKBF4eKcDeqAV1sRI0xTfj0rzXOMBJJvAK2QJPrBTHZt6GN+HAD096jAP0sZ3c/acdbYlt8Cm70adW
4ccn/pcDLOf5wjSM1DxtTWxFt1KBwVkRF9K1CP7dj9d/Klx2bFdNQf2ErgXHZRH1kyIOx5xoj9ehW/0I
Z1NN+Crdjm61AudS+zGUrsPFjBvB+X5cnvfroO/ne/DN4w4MZVowkGqEN1EPX+IobMMm6ACt3HyTwBoi
sE/kgTKzbor0qBwnEtvxmVqJz2ffxVDGja8zHh1wYa4WAykRXsWBI4+2wDX1BqhsRlk4D5YQwVFZ+heg
nVwr1xapXATXlA0Nz8vPJCtxPuXExbQbQ5lafDHnhD9VDZ+yB8dnBByafhM1k+uwgxWhZCIXlnGCwlGC
hnuLAPVRERvCeXDIZrhiNl3DToWiN7kP52YP4PycE2dTNfAnq3FG3YsuhaItIeDD6Y3YP7k+Wx7OhfYY
tfJV1wmk8UWAj2Pv4Z2oFZ6YHS3xrehRKtCvVmEwWa2X9iWr9OlVK9GlOHSbPpjegOqH67DtgQm2Ozko
uklQcIOA/EJAhgmkPxYBTjOB+5mDB1gbD7BPeIB18QDz8gA7xQPsU/7dX6fRq+5DZ4KieeYteKbLMKi2
4+DvNU872C7eyihvZpQ7GOX0PuX0LuU0RLkO0DRdqtd/VBs2IcAGeaeyE83xLTgYs6MyaoVPadbXlh1N
0xclC+jlR+KbUTdVij2yBZsihTgeP5y9gpUmC/Dx2qkS/UOnfU2t4wTHYs/veKXJAjr4LrkY9nAu1oQI
tH9D44LnK41W0sNaeFk4H5YxgtUjBDm/EkgRAwEnmYNr5QWjBK9pGv5MII0ZCGhhlBeOEJDrBOQaAblC
II0aCGh8IOlXIoUk/QFJNyRIvxkFuGoK/uP10nkJz58BqISsoFjBRioAAAAASUVORK5CYII=
</value>
</data>
<data name="MENU_ADD.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
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
QmCC
</value>
</data>
<data name="BTT_FILTER.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIFSURBVDhPpZLtS1NhGMbPPxJmmlYSgqHiKzGU1EDxg4iK
YKyG2WBogqMYJQOtCEVRFBGdTBCJfRnkS4VaaWNT5sqx1BUxRXxDHYxAJLvkusEeBaPAB+5z4Jzn+t3X
/aLhnEfjo8m+dCoa+7/C3O2Hqe0zDC+8KG+cRZHZhdzaaWTVTCLDMIY0vfM04Nfh77/G/sEhwpEDbO3t
I7TxE8urEVy99fT/AL5gWDLrTB/hnF4XsW0khCu5ln8DmJliT2AXrcNBsU1gj/MH4nMeKwBrPktM28xM
cX79DFKrHHD5d9D26hvicx4pABt2lpg10zYzU0zr7+e3xXGcrkEB2O2TNec9nJFwB3alZn5jZorfeDZh
6Q3g8s06BeCoKF4MRURoH1+BY2oNCbeb0TIclIYxOhzf8frTOuo7FxCbbVIAzpni0iceEc8vhzEwGkJD
lx83ymxifejdKjRNk/8PWnyIyTQqAJek0jqHwfEVscu31baIu8+90sTE4nY025dQ2/5FIPpnXlzKuK8A
HBUzHot52djqQ6HZhfR7IwK4mKpHtvEDMqvfCiQ6zaAAXM8x94aIWTNrLLG4kVUzgaTSPlzLtyJOZxbb
1wtfyg4Q+AfA3aZlButjSfxGcUJBk4g5tuP3haQKRKXcUQDOmbvNTpPOJeFFjordZmbWTNvMTHFUcpUC
nOccAdABIDXXE1nzAAAAAElFTkSuQmCC
</value>
</data>
<data name="BTT_STOP.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAVCSURBVEhLjZVtTFNXGMcLQmdHO6AdarLCHOJ4L9Bby62b
iuNFihZsqaBjRM2ouILiC0oUozGauPiSLNmH7cP2YctMZtwSdcvGhFEzFJjCihsuCOiwltL3wmUvySI9
+5/S6oxl40l+ub3nPOf//Pucc+/l0RgoLo4xJSd/dG3JkpGrSUkfrpdKX8JwVGDyf2JYqWR/S0rqGl28
2HwpJUWPoRgQEZikMWg0Cm8VF38x3NJCxs6dI9ebm/1XWbYzXyRajuno2azwMcQwr9u2bHnAnT5Nps+e
JSNVVZ7309IakmJj4zE9W6Rz2bIP7u7bR+4fP07Gjh0jViRbDh4kbSzbJROJXkVK2CKjKtUbELdwZ84Q
Dmu51lYyffQouavReK9Ipe8hRQyieNckEisVv9/cTB4cPkysSJo4dYrcbGnxX1Uqr2eLRKlIfKoI2lJi
q662UudUfBri3IEDZBrGfLW1xJSQcAdpLBDxOhITz4/U1ZExFLAA66FDxIYFjpMnSR/a1cGyPVlCYRqS
A0WG5fKSiaoq69S77xIOZqZhisO66b17yeTOnaRNqXRtlUguIrUMiHm6pUtfblMourq2b/c/RNI4mIAb
B/bEjZY59+8n11AkTyhM/4lhisf1etvUiROBlnBwzKG9XFMTmYK4KS/PkyoQfAphA5ABuuG8aEYiSUOR
HsvWrcS2axexNzYSx549xIViXgiZGxv9vSrV7XsVFVbuyJGAYw5zHHK5hobH4ukCwXno1YHMoHgkCAQ/
TyxO/04u7/6xpsZvr68nTuA2GokHAj4U8+3eTabgdooK457DfUAc7e1WKLwyoZCKU+cZYCF4clSDwVeK
xRkdOTk9tk2biHPbNuIGXoOB+OBwEgUnsYGTFRXEt3Yt8alUxAO68/N9uSLRBazfAahzAXhGPBT8FfHx
mR0yWc8gwzzybN5M3Go1cTMMcUmlxCUSEVd0NHEtWEDMQqG/PTnZy8bF0Q2tB1ngeTCneCgC7bolkw1M
QNAOMQdwAiocICqKmHNy/tKnpX2D/HdANpiXeCBstbVGm0plsQsEz4oDOjaYkvLIpNHcS09MrMCSWDA/
cZ/BsN9VWOiyw/1c4uPgAbBkZhKzVnvnzZUrc7H0P18rgXAbDK3Odevc9piYZ8Sd6D1tFxUfAyORkWQQ
mHJz/QOVlb/qWZa2ae4i3h07DjtKSz3hnDslEvKwtPTvvtRUPxUfDor3g25ahGFmBug/masIjmKrvbDQ
axcKA8JPtWXRIjJUVvb7idWre/s3bJiwpKeTXyDaB26AH2gByooVMzf1+tuFMhl9QT551Xvq6lqouCOc
eEICGVqz5o8GhulAamNpVtZbd8rL7w2h9yHxTtAO2mgRpXKmt7Ky/xONhm48H0TwPBs3Wh1xceGdQ7xe
Lm9HohHQjRTvKSh4zazTjd7Ozn4s/i34GlwGAxkZj/p0urvIXQr4PLtWO+RJSgrr3Mgw3yOpISge+lLx
9xYVsebKytGvsrP9/xb/MiKC9KGASa0eR14xiOf11tRsmVCr3W6IhpwPlpT8uSu8eCj4TQUF+djYkVs5
OeRSUNyUnz/zuVr9sGz58o+RM1sAsbC9quqItbzcO8Cyfuq8SaHoxPhc4qHgG1atktN2XME6k0o1cxHO
XxAIzmJOC2ZbhIhgkpNj+3W6z+6XlzsvFBXh9EU2YZwBQjpPk+YI/uXq6rd/1mrHb2g0rtLUVPot0AEp
oMf18Vr6Iw4owPrgdb6P/3OAHk16cjaAV0DYB24BoI7px5pe6f18gpqg7/8Xg9CCQWM83j84CNjeVkuE
bAAAAABJRU5ErkJggg==
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAV6SURBVEhLjZVrTFNnGMcfzxG2jp5x6VCTIXOI414up5ZT
NwXHRYoWbEGQMSJm1DLuxQKRGgjRRIOaLNmH7cP2YctMZtwSXZaNCVKnE4iK4IYLAjrEUkqvcJhbskCf
5e3abio4/smb0+ac8/v/z/O8FwAAGM7KCjBERHxyZcOG8Uvh4R/vDgt7FQDWknv/pzGplPstPPzaxPr1
QxciIwsBIAAA1vgeGKmqEt7MyvpqrKUFJ8+cwas6nesSx/WmMswWAPB7gvaURln2LVNJyUO+sxMXTp/G
8aIi+4fR0dXhgYHBPpPezZs/utfYiA86OnCyvR2NnZ041dyMXRx3Tcwwb6xkMiGTvW0qKZniT51CvqMD
eb0eF9ra8J5C4fgmLOwDAAhxV+GKSGQk8Ac6HT5sbUVjWxvOnDiBAy0trktS6U8JDBP1tMmYVJptKi42
kuQEvqDXI9/UhAvNzegsK0NDaOhdAOAAgIGejRvPjldU4KROh1M6HRqPHEGTXo+zx4/jDZ3O1cNx/fFC
YbTXZCwlJXumqMg4f/Ik8m1tuNDairxOhwtaLc5VVmKXVGo9IBKdB4Bc91eoNm16rUsiufbjwYOuR1ot
Tmu1ONPUhLMtLWhrb0fL4cN4heP6k4XCmNssmzVdWGiaP3bMXRK+uRn5xkbk6+txvrISDcnJ9iiB4HMA
UAOA2NNw8GNFouguiaR/6sABNNXWormmBmcbGtCq1aJDr8fBmhrXgEx2535+vpE/etSdmNdqka+tRb66
2gePEQjOAkAFAMR54JS3rP7JISExP6Sk9PWXlrrMGg1aNBq0VVWhvboanQ0N6Kyrw/nGRpwn4IYG5Ovq
/oFXVGCfROIQC4UETpLHAsCLT0xVr4k0JCS2JzGx37RvH1rKy9FWXo4OtRqdlZU4p9HgXFkZzuXno3Pn
TnTKZGiXybAvNdWZxDDnAOCQJ7lgObhX/luDg+N6xOL+keTkRfv+/WiTy9HGsmgNC0Mrw6DVzw+tNI2D
QqGrOyLCwQUFkYZqACAeAF56Htwrd7luisXDMwyDZprGWZpGC027we6xdi0OJSb+WRgd/R0AvA8ACauF
u2UqK6syyWRTZoHgWbjHcCQyctGgUNyP2bgxHwACVw13qtWHrRkZVjPDrAifpml8SNM4FReHQ0rl3Xe2
bUt6ejEuK5tarbfs2mUzBwQ8A7f4+bnLReCTNI3jFIUjFIWXk5JcwwUFvxZyHCnTyiaOQ4daZ3Ny7Msl
t4hE+Cgn568bUVEuAh/zwAcpCvsoCg0suzRMvmQlE4darTdnZDjMQqEb/ERZ1q3D0dzc34/t2DEwuGfP
zFRMDP5CUXiLovA6ReFVYkDG1q1LNwoL72SIxWSD/Hert1dUtBD47HLw0FAcTUt7XM2yPQBQkxMf/+7d
vLz7o3FxPngvRWE3RWEXMZFKlwYKCgY/UyhI4/3djbfv3WucDQp6Fk6Sp6U91qSkdANAFQCQRoY0pKe/
OaRSTdxJSPDBv6co/Jai8CJF4XBs7OItleoeAGxym5iVylF7ePiyyatY9jIAVHvg3pPKX5uZyQ0VFExc
TEhw/Rf+9Zo1eCs2dtEgl08DQBYABMNAaWnJjFxus4WG+pKPZGf/Ubs83Cv/+vT01GGlcvxmYiJe8MAN
qalLX8rlj3K3bPnUZ0A2pu6ioqPGvDzHbY5zkeT1Eknvc+Be+au3b08h5bjAcS6DTLZ0Xi6fflkgOA0A
Sl+JyMtsRETgoEr1xYO8PMu5zMxBiqLqAYAFAOEKcK/8LxYXv/ezUjl9XaGw5kRFkbNABQBhnunqe5f8
CAIACQDs9lxXu/xfAAAyNcnM2QMAry+7FgCA9iQmhzW5kv+rEQlB9v9XPIMY+oL9DaXP2MIw0VMVAAAA
AElFTkSuQmCC
</value>
</data>
<data name="BTT_LOG.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAFmSURBVFhH1dc/K4VhHMbxJ5EFEQbFiERKCotIrMJIiYEi
pbwCZcOqJC9AikUWiqRkJYtSRDbESMT3V07dna7zHHru+9T51me+Ts//E+V7LRjFFAZRiZzUhDVc4/vX
B47Rh6D14Aqp4XQ36ECQ2nALNezaQjG8Vo5DqMF0bxiA1+bwCTWoLMFbNTiDGsrkABXw0jDsKldDmdyj
HokrwCrUSBz7wXbRJs4eLkdQI9m0I3ENeIAaiGN3QjMSZ4fxv+ffnKIKibOnmhqI84V5eMleOHY41VAm
9k7wdgtW4wRqSHlCP7y2AjWmbMB7Y7DzqgZdz2iF9zrxCDXq2oU9uLz31+tgAcHahhp1DSFY9pGhRl29
CFYXxrMoQ7BmsZfFPkoRpHWow+56hX26BWkRatR1gRIEaQLvUMMpOyhCkBpxBzWcMoOgLUMNm0vUIWj2
ebaJF7jj5+hGTiqE/f+bxDRGUIt8LIp+AC/GHt3tQnwvAAAAAElFTkSuQmCC
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGTSURBVFhH1ZfPK21RGIaf5GaCCAPF0JVISWEiElNhSIkB
RUr5C5QZpkryB0gxkQlFUjKlO1HKjcwQQyJatdXp7TtnH3t/Z+CpZ7be7z3tH2uvA7+cFmAUmAIGgSpd
UCiagDXgCviMfAOOgT5d7E0P8C+jWL0GOjTkRRtwY5SqW0CJhtNSARwaZZYvwIAOSMsc8G6UZXNJB6Sh
FjgzSnJ5AFTqoKQMR0+5luTyFmjQQUkoAlaNgjjDDw4PbWrC5nJkFORjuw5Lwl/gzhgeZ3gTmnVYEsJl
/On9D54C1TosCWFX0+FxfgDzOigp4YMTLqeW5DJ8E9xewRrgxCjJ5gPQr0PSsmIUZXNDwx6MRfdVy9RH
oFXDHnQC90ahuhttXO7k+xwsaNCTbaNQHdKQJ+GQoYVqr4Y86QLGYyzXkCezwF6M+0CZBr1YNy65+hwd
3QrColGoXgClGvRiAng1SjPdAf5o0ItG4L9RmumMhrxZNkq/vQTqNeBNOJ5tAk9Sfg506+JCURz9/5sE
poERoE4X/Rq+AC/GHt09Rk0KAAAAAElFTkSuQmCC
</value>
</data>
</root>

View File

@@ -39,6 +39,7 @@ Namespace DownloadObjects.STDownloader
MyView = New FormView(Me)
MyProgress = New MyProgress(TOOLBAR_BOTTOM, PR_MAIN, LBL_INFO)
MyJob = New JobThread(Of MediaItem)
BTT_FILTER.Image = My.Resources.FilterPic
End Sub
#End Region
#Region "Form handlers"
@@ -95,15 +96,18 @@ Namespace DownloadObjects.STDownloader
Dim b As Boolean = True
Select Case e.KeyCode
Case Keys.Insert : BTT_ADD.PerformClick()
Case Keys.F5 : BTT_DOWN.PerformClick()
Case Keys.F5
If e.Control Then LoadData(True) Else BTT_DOWN.PerformClick()
Case Else : b = False
End Select
If b Then e.Handled = True
End Sub
#End Region
#Region "Refill, save list"
Protected Sub LoadData()
Protected Sub LoadData(Optional ByVal ClearTable As Boolean = False)
If ClearTable Then RemoveControls(,, False)
Dim c As List(Of IYouTubeMediaContainer) = LoadData_GetFiles()
If c.ListExists Then MyYouTubeSettings.FILTER.RemoveAll(c)
If c.ListExists Then
c.Sort(New ContainerDateComparer)
SuspendLayout()
@@ -332,6 +336,7 @@ Namespace DownloadObjects.STDownloader
pForm.Dispose()
End If
If Not c Is Nothing Then
If Not c.HasElements Then DirectCast(c, YouTubeMediaContainerBase).FileForceArtist()
Dim f As Form
Select Case c.ObjectType
Case YouTubeMediaType.Single : f = New VideoOptionsForm(c) With {.UseCookies = useCookies}
@@ -415,6 +420,12 @@ Namespace DownloadObjects.STDownloader
Return ErrorsDescriber.Execute(EDP.SendToLog + EDP.ReturnValue, ex, "[VideoListForm.ValidateContainerURL]", True)
End Try
End Function
Private Sub BTT_FILTER_Click(sender As Object, e As EventArgs) Handles BTT_FILTER.Click
Using f As New FilterForm(LoadData_GetFiles)
f.ShowDialog()
If f.DialogResult = DialogResult.OK Or f.DialogResult = DialogResult.Abort Then LoadData(True)
End Using
End Sub
Private Sub BTT_DOWN_Click(sender As Object, e As EventArgs) Handles BTT_DOWN.Click
With TP_CONTROLS
If .Controls.Count > 0 Then
@@ -494,7 +505,8 @@ Namespace DownloadObjects.STDownloader
Protected Sub CheckVersionImpl(ByVal Force As Boolean)
CheckVersion(Force)
End Sub
Protected Overloads Sub RemoveControls(Optional ByVal Predicate As Predicate(Of MediaItem) = Nothing, Optional ByVal RemoveFiles As Boolean = False)
Protected Overloads Sub RemoveControls(Optional ByVal Predicate As Predicate(Of MediaItem) = Nothing, Optional ByVal RemoveFiles As Boolean = False,
Optional ByVal ProcessDelete As Boolean = True)
ControlInvokeFast(TP_CONTROLS, Sub()
With TP_CONTROLS
If .Controls.Count > 0 Then
@@ -509,7 +521,10 @@ Namespace DownloadObjects.STDownloader
For i = rCnt.Count - 1 To 0 Step -1
cnt = .Controls(rCnt(i))
.Controls.RemoveAt(rCnt(i))
If Not cnt.MyContainer Is Nothing Then cnt.MyContainer.Delete(RemoveFiles) : cnt.MyContainer.Dispose()
If Not cnt.MyContainer Is Nothing Then
If ProcessDelete Then cnt.MyContainer.Delete(RemoveFiles)
cnt.MyContainer.Dispose()
End If
cnt.Dispose()
Next
End If

View File

@@ -24,7 +24,6 @@ Namespace Editors
Private Sub InitializeComponent()
Me.components = New System.ComponentModel.Container()
Dim TP_MAIN As System.Windows.Forms.TableLayoutPanel
Dim TP_BUTTONS As System.Windows.Forms.TableLayoutPanel
Dim ActionButton1 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(BugReporterForm))
Dim ActionButton2 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
@@ -33,31 +32,32 @@ Namespace Editors
Dim ActionButton5 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim ActionButton6 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim ActionButton7 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim TP_BUTTONS As System.Windows.Forms.TableLayoutPanel
Dim ActionButton8 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim ActionButton9 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Me.BTT_EMAIL = New System.Windows.Forms.Button()
Me.BTT_GITHUB = New System.Windows.Forms.Button()
Me.BTT_COPY = New System.Windows.Forms.Button()
Me.BTT_CANCEL = New System.Windows.Forms.Button()
Me.BTT_ANON = New System.Windows.Forms.Button()
Me.TT_MAIN = New System.Windows.Forms.ToolTip(Me.components)
Me.TXT_DESCR = New PersonalUtilities.Forms.Controls.TextBoxExtended()
Me.TXT_URL_PROFILE = New PersonalUtilities.Forms.Controls.TextBoxExtended()
Me.TXT_URL_POST = New PersonalUtilities.Forms.Controls.TextBoxExtended()
Me.TXT_REPRODUCE = New PersonalUtilities.Forms.Controls.TextBoxExtended()
Me.TXT_EXPECT = New PersonalUtilities.Forms.Controls.TextBoxExtended()
Me.TXT_LOG = New PersonalUtilities.Forms.Controls.TextBoxExtended()
Me.BTT_EMAIL = New System.Windows.Forms.Button()
Me.BTT_GITHUB = New System.Windows.Forms.Button()
Me.BTT_COPY = New System.Windows.Forms.Button()
Me.BTT_CANCEL = New System.Windows.Forms.Button()
Me.BTT_ANON = New System.Windows.Forms.Button()
Me.TXT_FILES = New PersonalUtilities.Forms.Controls.TextBoxExtended()
Me.TT_MAIN = New System.Windows.Forms.ToolTip(Me.components)
TP_MAIN = New System.Windows.Forms.TableLayoutPanel()
TP_BUTTONS = New System.Windows.Forms.TableLayoutPanel()
TP_MAIN.SuspendLayout()
TP_BUTTONS.SuspendLayout()
CType(Me.TXT_DESCR, System.ComponentModel.ISupportInitialize).BeginInit()
CType(Me.TXT_URL_PROFILE, System.ComponentModel.ISupportInitialize).BeginInit()
CType(Me.TXT_URL_POST, System.ComponentModel.ISupportInitialize).BeginInit()
CType(Me.TXT_REPRODUCE, System.ComponentModel.ISupportInitialize).BeginInit()
CType(Me.TXT_EXPECT, System.ComponentModel.ISupportInitialize).BeginInit()
CType(Me.TXT_LOG, System.ComponentModel.ISupportInitialize).BeginInit()
TP_BUTTONS.SuspendLayout()
CType(Me.TXT_FILES, System.ComponentModel.ISupportInitialize).BeginInit()
Me.SuspendLayout()
'
@@ -88,6 +88,123 @@ Namespace Editors
TP_MAIN.Size = New System.Drawing.Size(584, 461)
TP_MAIN.TabIndex = 0
'
'TXT_DESCR
'
ActionButton1.BackgroundImage = CType(resources.GetObject("ActionButton1.BackgroundImage"), System.Drawing.Image)
ActionButton1.Dock = System.Windows.Forms.DockStyle.Top
ActionButton1.Name = "Clear"
ActionButton1.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
Me.TXT_DESCR.Buttons.Add(ActionButton1)
Me.TXT_DESCR.CaptionDock = System.Windows.Forms.DockStyle.Top
Me.TXT_DESCR.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.None
Me.TXT_DESCR.CaptionVisible = False
Me.TXT_DESCR.Dock = System.Windows.Forms.DockStyle.Fill
Me.TXT_DESCR.GroupBoxed = True
Me.TXT_DESCR.GroupBoxText = "Describe the bug or write your message"
Me.TXT_DESCR.Location = New System.Drawing.Point(3, 3)
Me.TXT_DESCR.Multiline = True
Me.TXT_DESCR.Name = "TXT_DESCR"
Me.TXT_DESCR.Size = New System.Drawing.Size(578, 69)
Me.TXT_DESCR.TabIndex = 0
Me.TXT_DESCR.TextToolTip = "A clear and concise description of what the bug is"
Me.TXT_DESCR.TextToolTipEnabled = True
'
'TXT_URL_PROFILE
'
ActionButton2.BackgroundImage = CType(resources.GetObject("ActionButton2.BackgroundImage"), System.Drawing.Image)
ActionButton2.Name = "Clear"
ActionButton2.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
Me.TXT_URL_PROFILE.Buttons.Add(ActionButton2)
Me.TXT_URL_PROFILE.CaptionText = "Profile URL"
Me.TXT_URL_PROFILE.CaptionWidth = 75.0R
Me.TXT_URL_PROFILE.Dock = System.Windows.Forms.DockStyle.Fill
Me.TXT_URL_PROFILE.Location = New System.Drawing.Point(3, 78)
Me.TXT_URL_PROFILE.Name = "TXT_URL_PROFILE"
Me.TXT_URL_PROFILE.Size = New System.Drawing.Size(578, 22)
Me.TXT_URL_PROFILE.TabIndex = 1
'
'TXT_URL_POST
'
ActionButton3.BackgroundImage = CType(resources.GetObject("ActionButton3.BackgroundImage"), System.Drawing.Image)
ActionButton3.Name = "Clear"
ActionButton3.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
Me.TXT_URL_POST.Buttons.Add(ActionButton3)
Me.TXT_URL_POST.CaptionText = "Post URL"
Me.TXT_URL_POST.CaptionWidth = 75.0R
Me.TXT_URL_POST.Dock = System.Windows.Forms.DockStyle.Fill
Me.TXT_URL_POST.Location = New System.Drawing.Point(3, 106)
Me.TXT_URL_POST.Name = "TXT_URL_POST"
Me.TXT_URL_POST.Size = New System.Drawing.Size(578, 22)
Me.TXT_URL_POST.TabIndex = 2
'
'TXT_REPRODUCE
'
ActionButton4.BackgroundImage = CType(resources.GetObject("ActionButton4.BackgroundImage"), System.Drawing.Image)
ActionButton4.Dock = System.Windows.Forms.DockStyle.Top
ActionButton4.Name = "Clear"
ActionButton4.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
Me.TXT_REPRODUCE.Buttons.Add(ActionButton4)
Me.TXT_REPRODUCE.CaptionDock = System.Windows.Forms.DockStyle.Top
Me.TXT_REPRODUCE.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.None
Me.TXT_REPRODUCE.CaptionVisible = False
Me.TXT_REPRODUCE.Dock = System.Windows.Forms.DockStyle.Fill
Me.TXT_REPRODUCE.GroupBoxed = True
Me.TXT_REPRODUCE.GroupBoxText = "Steps to reproduce"
Me.TXT_REPRODUCE.Location = New System.Drawing.Point(3, 134)
Me.TXT_REPRODUCE.Multiline = True
Me.TXT_REPRODUCE.Name = "TXT_REPRODUCE"
Me.TXT_REPRODUCE.Size = New System.Drawing.Size(578, 69)
Me.TXT_REPRODUCE.TabIndex = 3
Me.TXT_REPRODUCE.TextToolTip = "Steps to reproduce the behavior:" & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "1. Do something" & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "2. See error"
Me.TXT_REPRODUCE.TextToolTipEnabled = True
'
'TXT_EXPECT
'
ActionButton5.BackgroundImage = CType(resources.GetObject("ActionButton5.BackgroundImage"), System.Drawing.Image)
ActionButton5.Dock = System.Windows.Forms.DockStyle.Top
ActionButton5.Name = "Clear"
ActionButton5.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
Me.TXT_EXPECT.Buttons.Add(ActionButton5)
Me.TXT_EXPECT.CaptionDock = System.Windows.Forms.DockStyle.Top
Me.TXT_EXPECT.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.None
Me.TXT_EXPECT.CaptionVisible = False
Me.TXT_EXPECT.Dock = System.Windows.Forms.DockStyle.Fill
Me.TXT_EXPECT.GroupBoxed = True
Me.TXT_EXPECT.GroupBoxText = "Expected behavior"
Me.TXT_EXPECT.Location = New System.Drawing.Point(3, 209)
Me.TXT_EXPECT.Multiline = True
Me.TXT_EXPECT.Name = "TXT_EXPECT"
Me.TXT_EXPECT.Size = New System.Drawing.Size(578, 69)
Me.TXT_EXPECT.TabIndex = 4
Me.TXT_EXPECT.TextToolTip = "A clear and concise description of what you expected to happen."
Me.TXT_EXPECT.TextToolTipEnabled = True
'
'TXT_LOG
'
ActionButton6.BackgroundImage = CType(resources.GetObject("ActionButton6.BackgroundImage"), System.Drawing.Image)
ActionButton6.Dock = System.Windows.Forms.DockStyle.Top
ActionButton6.Name = "Open"
ActionButton6.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open
ActionButton6.ToolTipText = "Select log files to add their text to the message"
ActionButton7.BackgroundImage = CType(resources.GetObject("ActionButton7.BackgroundImage"), System.Drawing.Image)
ActionButton7.Dock = System.Windows.Forms.DockStyle.Top
ActionButton7.Name = "Clear"
ActionButton7.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
ActionButton7.ToolTipText = "Empty"
Me.TXT_LOG.Buttons.Add(ActionButton6)
Me.TXT_LOG.Buttons.Add(ActionButton7)
Me.TXT_LOG.CaptionDock = System.Windows.Forms.DockStyle.Top
Me.TXT_LOG.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.None
Me.TXT_LOG.CaptionVisible = False
Me.TXT_LOG.Dock = System.Windows.Forms.DockStyle.Fill
Me.TXT_LOG.GroupBoxed = True
Me.TXT_LOG.GroupBoxText = "Log data"
Me.TXT_LOG.Location = New System.Drawing.Point(3, 284)
Me.TXT_LOG.Multiline = True
Me.TXT_LOG.Name = "TXT_LOG"
Me.TXT_LOG.Size = New System.Drawing.Size(578, 69)
Me.TXT_LOG.TabIndex = 5
'
'TP_BUTTONS
'
TP_BUTTONS.ColumnCount = 6
@@ -119,7 +236,7 @@ Namespace Editors
Me.BTT_EMAIL.Name = "BTT_EMAIL"
Me.BTT_EMAIL.Size = New System.Drawing.Size(94, 24)
Me.BTT_EMAIL.TabIndex = 1
Me.BTT_EMAIL.Text = "email"
Me.BTT_EMAIL.Text = "Email"
Me.TT_MAIN.SetToolTip(Me.BTT_EMAIL, "Create a message to send via email.")
Me.BTT_EMAIL.UseVisualStyleBackColor = True
'
@@ -167,129 +284,6 @@ Namespace Editors
Me.TT_MAIN.SetToolTip(Me.BTT_ANON, resources.GetString("BTT_ANON.ToolTip"))
Me.BTT_ANON.UseVisualStyleBackColor = True
'
'TXT_DESCR
'
ActionButton1.BackgroundImage = CType(resources.GetObject("ActionButton1.BackgroundImage"), System.Drawing.Image)
ActionButton1.Dock = System.Windows.Forms.DockStyle.Top
ActionButton1.Name = "Clear"
ActionButton1.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
Me.TXT_DESCR.Buttons.Add(ActionButton1)
Me.TXT_DESCR.CaptionDock = System.Windows.Forms.DockStyle.Top
Me.TXT_DESCR.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.None
Me.TXT_DESCR.CaptionVisible = False
Me.TXT_DESCR.Dock = System.Windows.Forms.DockStyle.Fill
Me.TXT_DESCR.GroupBoxed = True
Me.TXT_DESCR.GroupBoxText = "Describe the bug or write your message"
Me.TXT_DESCR.Lines = New String(-1) {}
Me.TXT_DESCR.Location = New System.Drawing.Point(3, 3)
Me.TXT_DESCR.Multiline = True
Me.TXT_DESCR.Name = "TXT_DESCR"
Me.TXT_DESCR.Size = New System.Drawing.Size(578, 69)
Me.TXT_DESCR.TabIndex = 0
Me.TXT_DESCR.TextToolTip = "A clear and concise description of what the bug is"
Me.TXT_DESCR.TextToolTipEnabled = True
'
'TXT_URL_PROFILE
'
ActionButton2.BackgroundImage = CType(resources.GetObject("ActionButton2.BackgroundImage"), System.Drawing.Image)
ActionButton2.Name = "Clear"
ActionButton2.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
Me.TXT_URL_PROFILE.Buttons.Add(ActionButton2)
Me.TXT_URL_PROFILE.CaptionText = "Profile URL"
Me.TXT_URL_PROFILE.CaptionWidth = 75.0R
Me.TXT_URL_PROFILE.Dock = System.Windows.Forms.DockStyle.Fill
Me.TXT_URL_PROFILE.Lines = New String(-1) {}
Me.TXT_URL_PROFILE.Location = New System.Drawing.Point(3, 78)
Me.TXT_URL_PROFILE.Name = "TXT_URL_PROFILE"
Me.TXT_URL_PROFILE.Size = New System.Drawing.Size(578, 22)
Me.TXT_URL_PROFILE.TabIndex = 1
'
'TXT_URL_POST
'
ActionButton3.BackgroundImage = CType(resources.GetObject("ActionButton3.BackgroundImage"), System.Drawing.Image)
ActionButton3.Name = "Clear"
ActionButton3.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
Me.TXT_URL_POST.Buttons.Add(ActionButton3)
Me.TXT_URL_POST.CaptionText = "Post URL"
Me.TXT_URL_POST.CaptionWidth = 75.0R
Me.TXT_URL_POST.Dock = System.Windows.Forms.DockStyle.Fill
Me.TXT_URL_POST.Lines = New String(-1) {}
Me.TXT_URL_POST.Location = New System.Drawing.Point(3, 106)
Me.TXT_URL_POST.Name = "TXT_URL_POST"
Me.TXT_URL_POST.Size = New System.Drawing.Size(578, 22)
Me.TXT_URL_POST.TabIndex = 2
'
'TXT_REPRODUCE
'
ActionButton4.BackgroundImage = CType(resources.GetObject("ActionButton4.BackgroundImage"), System.Drawing.Image)
ActionButton4.Dock = System.Windows.Forms.DockStyle.Top
ActionButton4.Name = "Clear"
ActionButton4.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
Me.TXT_REPRODUCE.Buttons.Add(ActionButton4)
Me.TXT_REPRODUCE.CaptionDock = System.Windows.Forms.DockStyle.Top
Me.TXT_REPRODUCE.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.None
Me.TXT_REPRODUCE.CaptionVisible = False
Me.TXT_REPRODUCE.Dock = System.Windows.Forms.DockStyle.Fill
Me.TXT_REPRODUCE.GroupBoxed = True
Me.TXT_REPRODUCE.GroupBoxText = "To Reproduce"
Me.TXT_REPRODUCE.Lines = New String(-1) {}
Me.TXT_REPRODUCE.Location = New System.Drawing.Point(3, 134)
Me.TXT_REPRODUCE.Multiline = True
Me.TXT_REPRODUCE.Name = "TXT_REPRODUCE"
Me.TXT_REPRODUCE.Size = New System.Drawing.Size(578, 69)
Me.TXT_REPRODUCE.TabIndex = 3
Me.TXT_REPRODUCE.TextToolTip = "Steps to reproduce the behavior:" & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "1. Do something" & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "2. See error"
Me.TXT_REPRODUCE.TextToolTipEnabled = True
'
'TXT_EXPECT
'
ActionButton5.BackgroundImage = CType(resources.GetObject("ActionButton5.BackgroundImage"), System.Drawing.Image)
ActionButton5.Dock = System.Windows.Forms.DockStyle.Top
ActionButton5.Name = "Clear"
ActionButton5.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
Me.TXT_EXPECT.Buttons.Add(ActionButton5)
Me.TXT_EXPECT.CaptionDock = System.Windows.Forms.DockStyle.Top
Me.TXT_EXPECT.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.None
Me.TXT_EXPECT.CaptionVisible = False
Me.TXT_EXPECT.Dock = System.Windows.Forms.DockStyle.Fill
Me.TXT_EXPECT.GroupBoxed = True
Me.TXT_EXPECT.GroupBoxText = "Expected behavior"
Me.TXT_EXPECT.Lines = New String(-1) {}
Me.TXT_EXPECT.Location = New System.Drawing.Point(3, 209)
Me.TXT_EXPECT.Multiline = True
Me.TXT_EXPECT.Name = "TXT_EXPECT"
Me.TXT_EXPECT.Size = New System.Drawing.Size(578, 69)
Me.TXT_EXPECT.TabIndex = 4
Me.TXT_EXPECT.TextToolTip = "A clear and concise description of what you expected to happen."
Me.TXT_EXPECT.TextToolTipEnabled = True
'
'TXT_LOG
'
ActionButton6.BackgroundImage = CType(resources.GetObject("ActionButton6.BackgroundImage"), System.Drawing.Image)
ActionButton6.Dock = System.Windows.Forms.DockStyle.Top
ActionButton6.Name = "Open"
ActionButton6.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open
ActionButton6.ToolTipText = "Select log files to add their text to the message"
ActionButton7.BackgroundImage = CType(resources.GetObject("ActionButton7.BackgroundImage"), System.Drawing.Image)
ActionButton7.Dock = System.Windows.Forms.DockStyle.Top
ActionButton7.Name = "Clear"
ActionButton7.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
ActionButton7.ToolTipText = "Empty"
Me.TXT_LOG.Buttons.Add(ActionButton6)
Me.TXT_LOG.Buttons.Add(ActionButton7)
Me.TXT_LOG.CaptionDock = System.Windows.Forms.DockStyle.Top
Me.TXT_LOG.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.None
Me.TXT_LOG.CaptionVisible = False
Me.TXT_LOG.Dock = System.Windows.Forms.DockStyle.Fill
Me.TXT_LOG.GroupBoxed = True
Me.TXT_LOG.GroupBoxText = "Log data"
Me.TXT_LOG.Lines = New String(-1) {}
Me.TXT_LOG.Location = New System.Drawing.Point(3, 284)
Me.TXT_LOG.Multiline = True
Me.TXT_LOG.Name = "TXT_LOG"
Me.TXT_LOG.Size = New System.Drawing.Size(578, 69)
Me.TXT_LOG.TabIndex = 5
'
'TXT_FILES
'
ActionButton8.BackgroundImage = CType(resources.GetObject("ActionButton8.BackgroundImage"), System.Drawing.Image)
@@ -310,7 +304,6 @@ Namespace Editors
Me.TXT_FILES.Dock = System.Windows.Forms.DockStyle.Fill
Me.TXT_FILES.GroupBoxed = True
Me.TXT_FILES.GroupBoxText = "Files"
Me.TXT_FILES.Lines = New String(-1) {}
Me.TXT_FILES.Location = New System.Drawing.Point(3, 359)
Me.TXT_FILES.Multiline = True
Me.TXT_FILES.Name = "TXT_FILES"
@@ -332,13 +325,13 @@ Namespace Editors
Me.Name = "BugReporterForm"
Me.Text = "New message"
TP_MAIN.ResumeLayout(False)
TP_BUTTONS.ResumeLayout(False)
CType(Me.TXT_DESCR, System.ComponentModel.ISupportInitialize).EndInit()
CType(Me.TXT_URL_PROFILE, System.ComponentModel.ISupportInitialize).EndInit()
CType(Me.TXT_URL_POST, System.ComponentModel.ISupportInitialize).EndInit()
CType(Me.TXT_REPRODUCE, System.ComponentModel.ISupportInitialize).EndInit()
CType(Me.TXT_EXPECT, System.ComponentModel.ISupportInitialize).EndInit()
CType(Me.TXT_LOG, System.ComponentModel.ISupportInitialize).EndInit()
TP_BUTTONS.ResumeLayout(False)
CType(Me.TXT_FILES, System.ComponentModel.ISupportInitialize).EndInit()
Me.ResumeLayout(False)

View File

@@ -124,60 +124,60 @@
<data name="ActionButton1.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
xAAADsQBlSsOGwAAAHpJREFUOE+1kVEKgDAMQ3e2/e+MXqpn6W/HxM7SpkIVB4HxSMKiTUTaFwVQ1X25
DjMfSxskHBYsAxHJkjUjHgrUNMY4peaMPxb03rcZMVhgn2oDKAwn+L0aROH/Cny4NAGFSx8x+10ZDwV2
gt+LOCxQsw1nPBS8VQBVTTzyhrdZSUm7AAAAAElFTkSuQmCC
</value>
</data>
<data name="ActionButton2.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
xAAADsQBlSsOGwAAAHpJREFUOE+1kVEKgDAMQ3e2/e+MXqpn6W/HxM7SpkIVB4HxSMKiTUTaFwVQ1X25
DjMfSxskHBYsAxHJkjUjHgrUNMY4peaMPxb03rcZMVhgn2oDKAwn+L0aROH/Cny4NAGFSx8x+10ZDwV2
gt+LOCxQsw1nPBS8VQBVTTzyhrdZSUm7AAAAAElFTkSuQmCC
</value>
</data>
<data name="ActionButton3.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
xAAADsQBlSsOGwAAAHpJREFUOE+1kVEKgDAMQ3e2/e+MXqpn6W/HxM7SpkIVB4HxSMKiTUTaFwVQ1X25
DjMfSxskHBYsAxHJkjUjHgrUNMY4peaMPxb03rcZMVhgn2oDKAwn+L0aROH/Cny4NAGFSx8x+10ZDwV2
gt+LOCxQsw1nPBS8VQBVTTzyhrdZSUm7AAAAAElFTkSuQmCC
</value>
</data>
<data name="ActionButton4.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
xAAADsQBlSsOGwAAAHpJREFUOE+1kVEKgDAMQ3e2/e+MXqpn6W/HxM7SpkIVB4HxSMKiTUTaFwVQ1X25
DjMfSxskHBYsAxHJkjUjHgrUNMY4peaMPxb03rcZMVhgn2oDKAwn+L0aROH/Cny4NAGFSx8x+10ZDwV2
gt+LOCxQsw1nPBS8VQBVTTzyhrdZSUm7AAAAAElFTkSuQmCC
</value>
</data>
<data name="ActionButton5.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
xAAADsQBlSsOGwAAAHpJREFUOE+1kVEKgDAMQ3e2/e+MXqpn6W/HxM7SpkIVB4HxSMKiTUTaFwVQ1X25
DjMfSxskHBYsAxHJkjUjHgrUNMY4peaMPxb03rcZMVhgn2oDKAwn+L0aROH/Cny4NAGFSx8x+10ZDwV2
gt+LOCxQsw1nPBS8VQBVTTzyhrdZSUm7AAAAAElFTkSuQmCC
</value>
</data>
<data name="ActionButton6.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP
WQwhyWIyJIUW5NqyPb7oCVtIlhVTwYf8nv7/t2zJagel9KmqKsIACYL9RjI8UHz5zshougZr/AEvbxEP
aZCDBY3VslixaJvX3wzkkDiOwbZtDRGA5vdNAg+TL27qgmt5XkBG/gTdAG7Gt+3PP9oOaEGFCVEC6rp+
5g9MfM/c5e4OsEZMZkQEtGL5H2DdZ5JRArDwPA+iKII0TfkC9vroC9j5vq8JTWw3WzWgLMtZGIaa0MR8
vlAD8PYlSaIJTTiOowY0p0Bc19XEJo6HE59FAPuMzyAINKGJ1XLFZxHALtMrnkBXOIQIIIQ8YvF/KrgB
cMaRN0UdBBkAAAAASUVORK5CYII=
wwAADsMBx2+oZAAAARlJREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbWujg3dATZPKYZC6BQhvw1AMkg3dP
XQyl7WIyJIEW5CbS0/jKE5GwpCghgg9s6/8/y5Kj6DA45zcAwAAAezB6rjNnB4XX244NHt8wGs7wblop
yRGxwZQBYKIfbn477EvqusY4jj2MgMpPiwav7l9UyYXmdrs9duzP4ApUmd72sfrxVsD33JQISyClvFUX
w9nJssvJFei9CJUtgQ7394Du3YKLJaCbLMuwqips21ZNuDve/35X8J7nuRcMsVwsbYEQYlSWpRcMMR5P
bAH9fU3TeMEQSZLYgsMpsDRNvXCIr89vWyCEeC6KwguGmL/ObYGU8oFOwA2ewwgYY9f6f7iUf3DGkTcu
khP7AAAAAElFTkSuQmCC
</value>
</data>
<data name="ActionButton7.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
xAAADsQBlSsOGwAAAHpJREFUOE+1kVEKgDAMQ3e2/e+MXqpn6W/HxM7SpkIVB4HxSMKiTUTaFwVQ1X25
DjMfSxskHBYsAxHJkjUjHgrUNMY4peaMPxb03rcZMVhgn2oDKAwn+L0aROH/Cny4NAGFSx8x+10ZDwV2
gt+LOCxQsw1nPBS8VQBVTTzyhrdZSUm7AAAAAElFTkSuQmCC
</value>
</data>
<metadata name="TP_BUTTONS.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
@@ -195,31 +195,31 @@ If you would like a response from the developer, response, please add your conta
<data name="ActionButton8.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
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
</value>
</data>
<data name="ActionButton9.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
xAAADsQBlSsOGwAAAHpJREFUOE+1kVEKgDAMQ3e2/e+MXqpn6W/HxM7SpkIVB4HxSMKiTUTaFwVQ1X25
DjMfSxskHBYsAxHJkjUjHgrUNMY4peaMPxb03rcZMVhgn2oDKAwn+L0aROH/Cny4NAGFSx8x+10ZDwV2
gt+LOCxQsw1nPBS8VQBVTTzyhrdZSUm7AAAAAElFTkSuQmCC
</value>
</data>
</root>

View File

@@ -10,6 +10,7 @@ Imports System.Threading
Imports PersonalUtilities.Tools
Imports PersonalUtilities.Tools.Web
Imports PersonalUtilities.Functions.Messaging
Imports PersonalUtilities.Functions.RegularExpressions
Imports SCrawler.DownloadObjects.STDownloader
Public Module MainModShared
Public Property BATCH As BatchExecutor
@@ -135,9 +136,11 @@ Namespace Editors
Public Shared Function GetProgramEnvirText(ByVal EnvirData As IDownloaderSettings, ByVal IsYouTube As Boolean) As String
Try
Dim output$ = String.Empty
Dim verAfter As RParams = RParams.DM("\A\w\:\\.*", 0, EDP.ReturnValue)
Using b As New BatchExecutor(True)
Dim f As SFile
Dim cmd$, ff$, vText$
Dim ii%
For i% = 0 To IIf(IsYouTube, 1, 3)
cmd = "--version"
@@ -154,7 +157,17 @@ Namespace Editors
Else
b.Reset()
b.Execute($"""{f}"" {cmd}", EDP.None)
If b.OutputData.Count > 3 Then vText = b.OutputData(3) Else vText = "undefined"
'If b.OutputData.Count > 3 Then vText = b.OutputData(3) Else vText = "undefined"
vText = String.Empty
With b.OutputData
If .Count > 0 Then
ii = .FindIndex(Function(bb) Not CStr(RegexReplace(bb, verAfter)).IsEmptyString)
If ii >= 0 And ii + 1 <= .Count - 1 Then vText = .Item(ii + 1)
End If
End With
If vText.IsEmptyString Then vText = "undefined"
output.StringAppendLine($"{ff} version: {vText}")
End If
End If

View File

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

View File

@@ -30,6 +30,11 @@ Namespace API.YouTube.Objects
_File = CleanFileName(_File)
End If
End Sub
Protected Friend Overrides Sub FileForceArtist()
Dim __artistName$ = UserTitle.IfNullOrEmpty(AccountName)
If Not _File.Name.IsEmptyString AndAlso Not _File.Name.ToLower.Contains(__artistName.ToLower) Then _
_File.Name = $"{__artistName} - {_File.Name}"
End Sub
Public Overrides Function ToString(ByVal ForMediaItem As Boolean) As String
Dim s$ = SizeStr
If Not s.IsEmptyString Then s = $" [{s}]"

View File

@@ -116,7 +116,9 @@ Namespace API.YouTube.Objects
<XMLEC> Public Property IsShorts As Boolean = False Implements IYouTubeMediaContainer.IsShorts
<XMLEC> Public Property ID As String Implements IYouTubeMediaContainer.ID, IUserMedia.PostID
<XMLEC> Public Property Title As String Implements IDownloadableMedia.Title
<XMLEC> Public Property Description As String Implements IYouTubeMediaContainer.Description
<XMLEC> Public Property Description As String Implements IYouTubeMediaContainer.Description, IUserMedia.PostText
Private Property IUserMedia_PostTextFile As String Implements IUserMedia.PostTextFile
Private Property IUserMedia_PostTextFileSpecialFolder As Boolean Implements IUserMedia.PostTextFileSpecialFolder
<XMLEC> Public Property PlaylistID As String Implements IYouTubeMediaContainer.PlaylistID
<XMLEC> Public Property PlaylistTitle As String Implements IYouTubeMediaContainer.PlaylistTitle
<XMLEC> Public Property UserID As String Implements IYouTubeMediaContainer.UserID
@@ -354,6 +356,14 @@ Namespace API.YouTube.Objects
End If
End Sub
#End Region
#Region "Chapters, Trimming"
Friend ReadOnly Property Chapters As List(Of TrimOption)
Friend ReadOnly Property TrimOptions As List(Of TrimOption)
Friend Property TrimDeleteOriginalFile As Boolean = False
Friend Property TrimAddTrimmedFilesToM3U8 As Boolean = False
Friend Property TrimSeparateFolder As Boolean = False
Friend Property TrimOptionsSet As Boolean = False
#End Region
#Region "IUserMedia Support"
<XMLEC> Private Property Attempts As Integer Implements IUserMedia.Attempts
Private _Object As Object = Nothing
@@ -682,6 +692,8 @@ Namespace API.YouTube.Objects
End If
End Set
End Property
Protected Friend Overridable Sub FileForceArtist()
End Sub
Friend Sub FileDateUpdate()
Dim n$ = _File.Name.StringTrim
Dim s$ = IIf(n.IsEmptyString, String.Empty, " ")
@@ -736,6 +748,7 @@ Namespace API.YouTube.Objects
#Region "Command"
<XMLEC> Public Property UseCookies As Boolean = MyYouTubeSettings.DefaultUseCookies Implements IYouTubeMediaContainer.UseCookies
Protected Const mp3 As String = "mp3"
Private Const mp4 As String = "mp4"
Private Const aac As String = "aac"
Private Const ac3 As String = "ac3"
Protected PostProcessing_AudioAC3 As Boolean = False
@@ -771,7 +784,12 @@ Namespace API.YouTube.Objects
'2023.3.4 -> 2023.7.6
'cmd.StringAppend($"ba*[format_id={SelectedAudio.ID}]", "+")
cmd.StringAppend(SelectedAudio.ID, "+")
If OutputAudioCodec.StringToLower = ac3 Then
If SelectedVideoIndex >= 0 And SelectedAudio.ProtocolType = Protocols.m3u8 And
(SelectedAudio.Codec.StringToLower = mp4 Or OutputAudioCodec.StringToLower = mp4) Then
PostProcessing_AudioAC3 = True
formats.StringAppend($"--merge-output-format ""{mp4}{IIf(OutputVideoExtension.IsEmptyString, String.Empty, $"/{OutputVideoExtension.StringToLower}")}""", " ")
atCodec = aac
ElseIf OutputAudioCodec.StringToLower = ac3 Then
PostProcessing_AudioAC3 = True
formats.StringAppend($"--audio-format {aac}", " ")
atCodec = aac
@@ -809,15 +827,20 @@ Namespace API.YouTube.Objects
subs = ListAddList(Nothing, Subtitles.Select(Function(s, i) If(SubtitlesSelectedIndexes.Contains(i), s.FullID, String.Empty)),
LAP.NotContainsOnly, EDP.ReturnValue).ListToString(",")
subs = $"--write-subs --write-auto-subs --sub-format {OutputSubtitlesFormat.StringToLower} --sub-langs ""{subs}"" --convert-subs {OutputSubtitlesFormat.StringToLower}"
If MyYouTubeSettings.DefaultSubtitlesEmbed Then subs = $"--write-auto-sub --embed-subs {subs}"
End If
If Not cmd.IsEmptyString Then
'2023.3.4 -> 2023.7.6
'cmd = $"yt-dlp -f ""{cmd}"""
'cmd = $"yt-dlp -f {cmd}"
cmd = $"{YTDLP_NAME} -f {cmd}"
If Not MyYouTubeSettings.ReplaceModificationDate Then cmd &= " --no-mtime"
'yt-dlp 2025.07.21
'If Not MyYouTubeSettings.ReplaceModificationDate Then cmd &= " --no-mtime"
cmd &= $" --{IIf(MyYouTubeSettings.ReplaceModificationDate.Value, String.Empty, "no-")}mtime"
If MyYouTubeSettings.DefaultVideoEmbedChapters Then cmd &= " --embed-chapters --add-chapters"
cmd.StringAppend(formats, " ")
cmd.StringAppend(subs, " ")
If MyYouTubeSettings.ErrorsIgnore Then cmd &= " --no-abort-on-error --ignore-errors"
cmd.StringAppend(YouTubeFunctions.GetCookiesCommand(WithCookies, YouTubeCookieNetscapeFile), " ")
cmd &= $" {URL} -o ""{File.PathWithSeparator}{File.Name}"""
File.Exists(SFO.Path, True)
@@ -835,6 +858,8 @@ Namespace API.YouTube.Objects
_Subtitles = New List(Of Subtitles)
_SubtitlesDelegated = New List(Of Subtitles)
SubtitlesSelectedIndexes = New List(Of Integer)
Chapters = New List(Of TrimOption)
TrimOptions = New List(Of TrimOption)
MediaObjects = New List(Of MediaObject)
_Files = New List(Of SFile)
@@ -1257,6 +1282,7 @@ Namespace API.YouTube.Objects
Dim fPatternFiles$ = $"{File.Name}*." & "{0}"
Dim fAacAudio As New TempFileConversion(New SFile(String.Format(fPattern, aac)), Me)
Dim mp3ThumbEmbedded As Boolean = False
Dim audioFiles As New List(Of SFile)
Dim tempFilesList As New List(Of TempFileConversion)
Dim ttFile As TempFileConversion
@@ -1364,13 +1390,17 @@ Namespace API.YouTube.Objects
format = format.StringToLower
f = String.Format(fPattern, format)
AddFile(f)
audioFiles.Add(f)
If Not f.Exists Then
tryToConvert.Invoke(format, f)
updateBitrate(f)
If f.Exists Then
If format = mp3 And Not mp3ThumbEmbedded And MyYouTubeSettings.DefaultAudioEmbedThumbnail_ExtractedFiles Then _
embedThumbTo.Invoke(f) : mp3ThumbEmbedded = True
If Not M3U8_PlaylistFiles.ListExists AndAlso f.Exists Then M3U8_Append(f)
If format = mp3 AndAlso f.Exists AndAlso MyYouTubeSettings.VideoPlaylist_AddExtractedMP3.Value Then M3U8_Append(f)
If M3U8_PlaylistFiles.ListExists OrElse
(format = mp3 AndAlso MyYouTubeSettings.VideoPlaylist_AddExtractedMP3.Value) Then _
M3U8_Append(f)
End If
End If
Next
End If
@@ -1423,6 +1453,69 @@ Namespace API.YouTube.Objects
If OutputVideoFPS > 0 AndAlso SelectedVideo.Bitrate <> OutputVideoFPS Then _
reencodeFile("ffmpeg -i ""{0}"" -filter:v fps=" & OutputVideoFPS.ToString.Replace(", ", ".") & " -c:a copy ""{1}""")
End If
'Trimming
If TrimOptions.Count > 0 AndAlso File.Exists Then
Const trimCommand$ = "ffmpeg -i ""{0}"" -ss {1} -to {2} -c:v copy -c:a copy ""{3}"""
Dim trimFirstFile As SFile = Nothing
Dim trimFirstAdded As Boolean = False
Dim trimSingleReplace As Boolean = TrimOptions.Count = 1 And TrimDeleteOriginalFile
Dim audioReplace As New Dictionary(Of SFile, SFile)
Dim processTrim As Action(Of TrimOption, SFile, Boolean) =
Sub(ByVal opt As TrimOption, ByVal pFile As SFile, ByVal isAudio As Boolean)
Dim fNew As SFile = pFile
fNew.Name &= $"_{opt.Name}"
If TrimSeparateFolder Then fNew = $"{fNew.PathNoSeparator}\{MyYouTubeSettings.TrimSeparateFolderName.Value.IfNullOrEmpty(YouTubeSettings.TrimSeparateFolderNameDefault)}\{fNew.File}" : fNew.Exists(SFO.Path)
format = fNew.Extension.StringToLower
.Execute(String.Format(trimCommand,
pFile,
AConvert(Of String)(opt.StartTime, TimeToStringProvider),
AConvert(Of String)(opt.EndTime, TimeToStringProvider),
fNew))
If fNew.Exists Then
If trimFirstFile.IsEmptyString Then trimFirstFile = fNew
If Not trimSingleReplace Then AddFile(fNew)
If isAudio And trimSingleReplace And Not audioReplace.ContainsKey(pFile) Then audioReplace.Add(pFile, fNew)
If format = mp3 And MyYouTubeSettings.DefaultAudioEmbedThumbnail_ExtractedFiles Then _
embedThumbTo.Invoke(fNew) : mp3ThumbEmbedded = True
If Not trimSingleReplace AndAlso
(TrimAddTrimmedFilesToM3U8 Or (TrimDeleteOriginalFile And Not trimFirstAdded)) AndAlso
(M3U8_PlaylistFiles.ListExists OrElse
(format = mp3 AndAlso MyYouTubeSettings.VideoPlaylist_AddExtractedMP3.Value)) Then _
M3U8_Append(fNew) : trimFirstAdded = True
End If
End Sub
For Each tr As TrimOption In TrimOptions
processTrim(tr, File, False)
If audioFiles.Count > 0 Then
For Each f In audioFiles : processTrim(tr, f, True) : Next
End If
Next
If TrimDeleteOriginalFile Then
Dim silentEDP As New ErrorsDescriber(EDP.ReturnValue)
File.Delete(,, silentEDP)
If trimSingleReplace Then
File = SFile.Rename(trimFirstFile, File,, New ErrorsDescriber(False, False, False, trimFirstFile))
If audioReplace.Count > 0 Then
For Each kvf As KeyValuePair(Of SFile, SFile) In audioReplace
If kvf.Key.Exists And kvf.Value.Exists Then
kvf.Key.Delete(,, silentEDP)
SFile.Rename(kvf.Value, kvf.Key,, silentEDP)
End If
Next
audioReplace.Clear()
End If
Else
File = trimFirstFile
If audioFiles.Count > 0 Then
For Each f In audioFiles
If Not f = File Then f.Delete(,, silentEDP)
Next
End If
End If
End If
End If
End If
End If
End With
@@ -1650,12 +1743,20 @@ Namespace API.YouTube.Objects
If Not tmpPls.IsEmptyString Then PlaylistTitle = tmpPls
End If
Dim tmpTitle$
UserID = .Value("uploader_id")
UserTitle = TitleHtmlConverter.Invoke(.Value("uploader"))
If Not UserTitle.IsEmptyString Then
Dim tmpTitle$ = UserTitle.Replace("Topic", String.Empty).StringTrimEnd(" ", "-")
tmpTitle = UserTitle.Replace("Topic", String.Empty).StringTrimEnd(" ", "-")
If Not tmpTitle.IsEmptyString Then UserTitle = tmpTitle
End If
If MyYouTubeSettings.ParseLongUserTitle Or UserTitle.IsEmptyString Then
tmpTitle = TitleHtmlConverter.Invoke(.Value("artist"))
If Not tmpTitle.IsEmptyString Then
If Not UserTitle.IsEmptyString AndAlso Not tmpTitle.Contains(UserTitle) Then tmpTitle = $"{UserTitle}, {tmpTitle}"
UserTitle = ListAddList(Nothing, tmpTitle.Split(","), CType(Function(v$) v.StringTrim, Func(Of Object, Object)), EDP.ReturnValue).ListToString(" & ").IfNullOrEmpty(UserTitle)
End If
End If
Dim ext$ = IIf(IsMusic,
MyYouTubeSettings.DefaultAudioCodecMusic.Value.StringToLower,
@@ -1682,6 +1783,8 @@ Namespace API.YouTube.Objects
ParseThumbnails(.Self)
ParseSubtitles(.Self)
ParseChapters(.Self)
End With
Return True
End If
@@ -1751,9 +1854,12 @@ Namespace API.YouTube.Objects
If If(e({"formats"})?.Count, 0) > 0 Then
Dim obj As MediaObject
Dim nValue#
Dim sValue$
Dim sValue$ = String.Empty
Dim allowWebm As Boolean = MyYouTubeSettings.DefaultVideoAllowWebm
Dim validCodecValue As Func(Of String, Boolean) = Function(codec) Not codec.IsEmptyString AndAlso Not codec = "none"
Dim validCodecValue As Func(Of String, Boolean) = Function(ByVal codec As String) As Boolean
sValue = codec
Return Not codec.IsEmptyString AndAlso Not codec = "none"
End Function
For Each ee In e({"formats"})
obj = New MediaObject With {
@@ -1777,19 +1883,30 @@ Namespace API.YouTube.Objects
If obj.Size <= 0 And obj.Bitrate > 0 And Duration.TotalSeconds > 0 Then _
obj.Size = (obj.Bitrate / 8 * Duration.TotalSeconds).RoundVal(2)
sValue = ee.Value("vcodec")
If validCodecValue(sValue) Then
'sValue = ee.Value("vcodec")
If validCodecValue(ee.Value("vcodec")) Then
obj.Type = UMTypes.Video
obj.Codec = sValue.Split(".").First
If validCodecValue(ee.Value("acodec")) Then obj.Type = av
Else
sValue = ee.Value("acodec")
If validCodecValue(sValue) Then
ElseIf validCodecValue(ee.Value("acodec")) Then
obj.Type = UMTypes.Audio
obj.Codec = sValue.Split(".").First
Else
Continue For
Dim fd As Boolean = False
sValue = ee.Value("format_note")
If Not sValue.IsEmptyString Then
With ListAddList(Nothing, sValue.Split(","), CType(Function(v) CStr(v).StringToLower.StringTrim, Func(Of Object, Object)), EDP.ReturnValue)
If .ListContains({"high", "low"}) Then
obj.Type = UMTypes.Audio
obj.Codec = ee.Value("ext")
If obj.Protocol.StringToLower.StartsWith("m3u8") Then obj.Protocol = "m3u8"
If obj.Bitrate <= 0 Then obj.Bitrate = IIf(.Contains("high"), 129, 53)
If obj.Size <= 0 Then obj.Size = 1
fd = True
End If
End With
End If
If Not fd Then Continue For
End If
MediaObjects.Add(obj)
Next
@@ -1801,8 +1918,9 @@ Namespace API.YouTube.Objects
Dim data As New List(Of MediaObject)(MediaObjects.Where(Function(mo) mo.Type = t And mo.Extension = webm))
If data.Count > 0 Then
Dim d As MediaObject = Nothing
Dim expWebm As Predicate(Of MediaObject) = Function(mo) mo.Extension = webm
Dim expAVC As Predicate(Of MediaObject) = Function(mo) mo.Codec.IfNullOrEmpty("/").ToLower.StartsWith(avc)
Dim allWebm As Boolean = False, allAVC As Boolean = False
Dim expWebm As Predicate(Of MediaObject) = Function(mo) Not allWebm And mo.Extension = webm
Dim expAVC As Predicate(Of MediaObject) = Function(mo) Not allAVC And mo.Codec.IfNullOrEmpty("/").ToLower.StartsWith(avc)
Dim comp As Func(Of MediaObject, Predicate(Of MediaObject), Boolean, Boolean, Boolean) =
Function(mo, exp, isTrue, checkHttp) mo.Type = t And exp.Invoke(mo) = isTrue And mo.Width = d.Width And
(Not checkHttp OrElse mo.ProtocolType = Protocols.https)
@@ -1810,6 +1928,8 @@ Namespace API.YouTube.Objects
Dim RemoveWebm As Predicate(Of MediaObject) = Function(mo) comp.Invoke(mo, expWebm, True, allowWebm)
Dim CountAVC As Func(Of MediaObject, Boolean) = Function(mo) comp.Invoke(mo, expAVC, True, False)
Dim RemoveAVC As Predicate(Of MediaObject) = Function(mo) comp.Invoke(mo, expAVC, False, False)
allWebm = data.All(FPredicate(Of MediaObject).ToFunc(expWebm))
allAVC = data.All(FPredicate(Of MediaObject).ToFunc(expAVC))
For Each d In data
If MediaObjects.Count = 0 Then Exit For
If MediaObjects.LongCount(CountWebm) > 0 Then MediaObjects.RemoveAll(RemoveWebm)
@@ -1930,6 +2050,15 @@ Namespace API.YouTube.Objects
End With
End If
End Sub
Protected Sub ParseChapters(ByVal e As EContainer)
With e({"chapters"})
If .ListExists Then Chapters.AddRange(.Select(Function(ee) New TrimOption With {
.Start = CInt(AConvert(Of Double)(ee.Value("start_time"), 0, EDP.ReturnValue)),
.[End] = CInt(AConvert(Of Double)(ee.Value("end_time"), 0, EDP.ReturnValue)),
.Name = CleanFileName(New SFile With {.Name = ee.Value("title")}).Name
}))
End With
End Sub
#End Region
#Region "IEContainerProvider Support"
Private Function GetElementsChecked() As IEnumerable(Of EContainer)
@@ -2004,6 +2133,8 @@ Namespace API.YouTube.Objects
_Subtitles.Clear()
_SubtitlesDelegated.Clear()
SubtitlesSelectedIndexes.Clear()
Chapters.Clear()
TrimOptions.Clear()
MediaObjects.Clear()
_Files.Clear()
PostProcessing_OutputAudioFormats.Clear()

View File

@@ -124,12 +124,37 @@
<Compile Include="Controls\ChannelTabsChooserForm.vb">
<SubType>Form</SubType>
</Compile>
<Compile Include="Controls\ChaptersForm.Designer.vb">
<DependentUpon>ChaptersForm.vb</DependentUpon>
</Compile>
<Compile Include="Controls\ChaptersForm.vb">
<SubType>Form</SubType>
</Compile>
<Compile Include="Controls\FilterForm.Designer.vb">
<DependentUpon>FilterForm.vb</DependentUpon>
</Compile>
<Compile Include="Controls\FilterForm.vb">
<SubType>Form</SubType>
</Compile>
<Compile Include="Controls\PlayListParserForm.Designer.vb">
<DependentUpon>PlayListParserForm.vb</DependentUpon>
</Compile>
<Compile Include="Controls\PlayListParserForm.vb">
<SubType>Form</SubType>
</Compile>
<Compile Include="Controls\TrimOptionForm.Designer.vb">
<DependentUpon>TrimOptionForm.vb</DependentUpon>
</Compile>
<Compile Include="Controls\TrimOptionForm.vb">
<SubType>Form</SubType>
</Compile>
<Compile Include="Controls\VideoOptionsTrimForm.Designer.vb">
<DependentUpon>VideoOptionsTrimForm.vb</DependentUpon>
</Compile>
<Compile Include="Controls\VideoOptionsTrimForm.vb">
<SubType>Form</SubType>
</Compile>
<Compile Include="Controls\YTDataFilter.vb" />
<Compile Include="Downloader\DownloadLocationsCollection.vb" />
<Compile Include="Downloader\IDownloaderSettings.vb" />
<Compile Include="Downloader\Notificator.vb" />
@@ -220,9 +245,21 @@
<EmbeddedResource Include="Controls\ChannelTabsChooserForm.resx">
<DependentUpon>ChannelTabsChooserForm.vb</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Controls\ChaptersForm.resx">
<DependentUpon>ChaptersForm.vb</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Controls\FilterForm.resx">
<DependentUpon>FilterForm.vb</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Controls\PlayListParserForm.resx">
<DependentUpon>PlayListParserForm.vb</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Controls\TrimOptionForm.resx">
<DependentUpon>TrimOptionForm.vb</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Controls\VideoOptionsTrimForm.resx">
<DependentUpon>VideoOptionsTrimForm.vb</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Downloader\MediaItem.resx">
<DependentUpon>MediaItem.vb</DependentUpon>
</EmbeddedResource>

View File

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

View File

@@ -29,6 +29,8 @@ Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "SCrawler.Updater", "SCrawle
EndProject
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "SCrawler.Shared", "SCrawler.Shared\SCrawler.Shared.vbproj", "{DC634700-24C7-42DD-BF8F-87E6CC54E625}"
EndProject
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "PersonalUtilities.Images", "..\..\MyUtilities\PersonalUtilities.Images\PersonalUtilities.Images.vbproj", "{B7EF76A9-96F3-4C53-B252-0AB5F79B67B3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -135,6 +137,18 @@ Global
{DC634700-24C7-42DD-BF8F-87E6CC54E625}.Release|x64.Build.0 = Release|x64
{DC634700-24C7-42DD-BF8F-87E6CC54E625}.Release|x86.ActiveCfg = Release|x86
{DC634700-24C7-42DD-BF8F-87E6CC54E625}.Release|x86.Build.0 = Release|x86
{B7EF76A9-96F3-4C53-B252-0AB5F79B67B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B7EF76A9-96F3-4C53-B252-0AB5F79B67B3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B7EF76A9-96F3-4C53-B252-0AB5F79B67B3}.Debug|x64.ActiveCfg = Debug|x64
{B7EF76A9-96F3-4C53-B252-0AB5F79B67B3}.Debug|x64.Build.0 = Debug|x64
{B7EF76A9-96F3-4C53-B252-0AB5F79B67B3}.Debug|x86.ActiveCfg = Debug|x86
{B7EF76A9-96F3-4C53-B252-0AB5F79B67B3}.Debug|x86.Build.0 = Debug|x86
{B7EF76A9-96F3-4C53-B252-0AB5F79B67B3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B7EF76A9-96F3-4C53-B252-0AB5F79B67B3}.Release|Any CPU.Build.0 = Release|Any CPU
{B7EF76A9-96F3-4C53-B252-0AB5F79B67B3}.Release|x64.ActiveCfg = Release|x64
{B7EF76A9-96F3-4C53-B252-0AB5F79B67B3}.Release|x64.Build.0 = Release|x64
{B7EF76A9-96F3-4C53-B252-0AB5F79B67B3}.Release|x86.ActiveCfg = Release|x86
{B7EF76A9-96F3-4C53-B252-0AB5F79B67B3}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@@ -30,6 +30,13 @@ Namespace API.Base
Friend Const UseMD5ComparisonToolTip As String = "Each image will be checked for existence using MD5"
Friend Const UserNameChangeCaption As String = "UserName"
Friend Const UserNameChangeToolTip As String = "If the user has changed their UserName, you can set a new name here. Not required for new users."
Friend Const DownloadTextCaption As String = "Download text"
Friend Const DownloadTextTip As String = "Download text (if available) for posts with image and video" & vbCr & "If this checkbox is checked, the post text will be downloaded along with the file and saved under the same name but with the 'txt' extension."
Friend Const DownloadTextPostsCaption As String = "Download text posts"
Friend Const DownloadTextPostsTip As String = "Download text (if available) for text posts (no image and video)"
Friend Const DownloadTextSpecialFolderCaption As String = "Text special folder"
Friend Const DownloadTextSpecialFolderTip As String = "If checked, text files will be saved to a separate folder"
Private Sub New()
End Sub
End Class

View File

@@ -13,10 +13,34 @@ Namespace API.Base
Friend Overridable Property SiteKey As String
<PSetting(Address:=SettingAddress.User, Caption:=DN.UserNameChangeCaption, ToolTip:=DN.UserNameChangeToolTip)>
Friend Overridable Property UserName As String = String.Empty
<PSetting(Address:=SettingAddress.User, Caption:=DN.DownloadTextCaption, ToolTip:=DN.DownloadTextTip)>
Friend Overridable Property DownloadText As Boolean = False
<PSetting(Address:=SettingAddress.User, Caption:=DN.DownloadTextPostsCaption, ToolTip:=DN.DownloadTextPostsTip)>
Friend Overridable Property DownloadTextPosts As Boolean = False
<PSetting(Address:=SettingAddress.User, Caption:=DN.DownloadTextSpecialFolderCaption, ToolTip:=DN.DownloadTextSpecialFolderTip)>
Friend Overridable Property DownloadTextSpecialFolder As Boolean = False
Friend Sub New(ByVal u As UserDataBase)
UserName = u.NameTrue(True)
DownloadText = u.DownloadText
DownloadTextPosts = u.DownloadTextPosts
DownloadTextSpecialFolder = u.DownloadTextSpecialFolder
End Sub
Friend Sub New(ByVal s As SiteSettingsBase)
DownloadText = s.DownloadText.Value
DownloadTextPosts = s.DownloadTextPosts.Value
DownloadTextSpecialFolder = s.DownloadTextSpecialFolder.Value
End Sub
Friend Sub New()
End Sub
Protected _ApplyBase_Name As Boolean = True
Protected _ApplyBase_Text As Boolean = True
Friend Sub ApplyBase(ByRef u As UserDataBase)
If _ApplyBase_Name Then u.NameTrue = UserName
If _ApplyBase_Text Then
u.DownloadText = DownloadText
u.DownloadTextPosts = DownloadTextPosts
u.DownloadTextSpecialFolder = DownloadTextSpecialFolder
End If
End Sub
End Class
End Namespace

View File

@@ -0,0 +1,43 @@
' 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.Base
Friend Interface IPSite
Property QueryString As String
End Interface
Friend Class EditorExchangeOptionsBase_P : Inherits EditorExchangeOptionsBase : Implements IPSite
<PSetting(Address:=SettingAddress.None)> Friend Overrides Property UserName As String
<PSetting(Address:=SettingAddress.None)> Friend Overrides Property DownloadText As Boolean
<PSetting(Address:=SettingAddress.None)> Friend Overrides Property DownloadTextPosts As Boolean
<PSetting(Address:=SettingAddress.None)> Friend Overrides Property DownloadTextSpecialFolder As Boolean
<PSetting(Address:=SettingAddress.User, Caption:="Query",
ToolTip:="Query string. Don't change this field when creating a user! Change it only for the same request.")>
Friend Property QueryString As String Implements IPSite.QueryString
Friend Sub New()
DisableBase()
End Sub
Friend Sub New(ByVal u As UserDataBase)
MyBase.New(u)
DisableBase()
If TypeOf u Is IPSite Then QueryString = DirectCast(u, IPSite).QueryString
End Sub
Friend Sub New(ByVal s As SiteSettingsBase)
MyBase.New(s)
DisableBase()
End Sub
Friend Overridable Sub Apply(ByRef u As IPSite)
ApplyBase(u)
u.QueryString = QueryString
End Sub
Protected Overridable Sub DisableBase()
_ApplyBase_Name = False
_ApplyBase_Text = False
End Sub
End Class
End Namespace

View File

@@ -10,10 +10,8 @@ Namespace API.Base.GDL
Friend Class GDLBatch : Inherits TokenBatch
Friend Const UrlLibStart As String = "[urllib3.connectionpool][debug]"
Friend Const UrlTextStart As String = UrlLibStart & " https"
Friend Sub New(ByVal _Token As Threading.CancellationToken)
MyBase.New(_Token)
MainProcessName = "gallery-dl"
ChangeDirectory(Settings.GalleryDLFile.File)
Friend Sub New(ByVal _Token As Threading.CancellationToken, Optional ByVal __MainProcessName As String = Nothing, Optional ByVal WorkingDir As SFile = Nothing)
MyBase.New(_Token, __MainProcessName.IfNullOrEmpty(Settings.GalleryDLFile.File.Name), WorkingDir.IfNullOrEmpty(Settings.GalleryDLFile.File))
End Sub
Protected Overrides Async Sub OutputDataReceiver(ByVal Sender As Object, ByVal e As DataReceivedEventArgs)
If Not ProcessKilled Then

View File

@@ -57,6 +57,7 @@ Namespace API.Base
Property FileExists As Boolean
Property DownloadedPictures(ByVal Total As Boolean) As Integer
Property DownloadedVideos(ByVal Total As Boolean) As Integer
Property DownloadedTexts(ByVal Total As Boolean) As Integer
ReadOnly Property DownloadedTotal(Optional ByVal Total As Boolean = True) As Integer
ReadOnly Property DownloadedInformation As String
Property HasError As Boolean

View File

@@ -14,6 +14,7 @@ Imports PersonalUtilities.Tools.Web.Cookies
Imports PersonalUtilities.Tools.Web.Clients
Imports PersonalUtilities.Functions.RegularExpressions
Imports Download = SCrawler.Plugin.ISiteSettings.Download
Imports DN = SCrawler.API.Base.DeclaredNames
Namespace API.Base
Friend MustInherit Class SiteSettingsBase : Implements ISiteSettings, IResponserContainer
#Region "Declarations"
@@ -69,6 +70,14 @@ Namespace API.Base
_UserOptionsExists = Not t Is Nothing
End Set
End Property
#Region "New user defaults"
<PropertyOption(ControlText:=DN.DownloadTextCaption, ControlToolTip:=DN.DownloadTextTip, Category:=DN.CAT_UserDefs), PXML, PClonable>
Friend Overridable Property DownloadText As PropertyValue
<PropertyOption(ControlText:=DN.DownloadTextPostsCaption, ControlToolTip:=DN.DownloadTextPostsTip, Category:=DN.CAT_UserDefs), PXML, PClonable>
Friend Overridable Property DownloadTextPosts As PropertyValue
<PropertyOption(ControlText:=DN.DownloadTextSpecialFolderCaption, ControlToolTip:=DN.DownloadTextSpecialFolderTip, Category:=DN.CAT_UserDefs), PXML, PClonable>
Friend Overridable Property DownloadTextSpecialFolder As PropertyValue
#End Region
#End Region
#Region "EnvironmentPrograms"
Private Property CMDEncoding As String Implements ISiteSettings.CMDEncoding
@@ -124,6 +133,9 @@ Namespace API.Base
_Image = __Image
Responser = New Responser With {.DeclaredError = EDP.ThrowException}
SettingsVersion = New PropertyValue(0)
DownloadText = New PropertyValue(False)
DownloadTextPosts = New PropertyValue(False)
DownloadTextSpecialFolder = New PropertyValue(True)
UpdateResponserFile()
End Sub
Friend Sub New(ByVal SiteName As String, ByVal CookiesDomain As String, ByVal AccName As String, ByVal Temp As Boolean,

View File

@@ -32,6 +32,8 @@ Namespace API.Base
Private Const Name_MediaPostID As String = "ID"
Private Const Name_MediaPostDate As String = "Date"
Private Const Name_SpecialFolder As String = "SpecialFolder"
Private Const Name_PostTextFile As String = "PostTextFile"
Private Const Name_PostTextFileSpecialFolder As String = "PostTextFileSpecialFolder"
#End Region
Friend Enum Types As Integer
Undefined = 0
@@ -46,12 +48,30 @@ Namespace API.Base
End Enum
Friend Enum States As Integer : Unknown = 0 : Tried = 1 : Downloaded = 2 : Skipped = 3 : Missing = 4 : End Enum
Friend [Type] As Types
Friend ReadOnly Property IsVideoType As Boolean
Get
Return Type = Types.m3u8 Or Type = Types.Video Or Type = Types.VideoPre
End Get
End Property
Friend ReadOnly Property IsAudioType As Boolean
Get
Return Type = Types.Audio Or Type = Types.AudioPre
End Get
End Property
Friend ReadOnly Property IsPhotoType As Boolean
Get
Return Type = Types.Picture Or Type = Types.GIF
End Get
End Property
Friend URL_BASE As String
Friend URL As String
Friend MD5 As String
Friend [File] As SFile
Friend Post As UserPost
Friend PictureOption As String
Friend PostText As String
Friend PostTextFile As SFile
Friend PostTextFileSpecialFolder As Boolean
Friend State As States
Friend Attempts As Integer
''' <summary>
@@ -125,6 +145,30 @@ Namespace API.Base
Post = New UserPost(Post.ID, PostDate)
End Set
End Property
Private Property IUserMedia_PostText As String Implements IUserMedia.PostText
Get
Return PostText
End Get
Set(ByVal NewText As String)
PostText = NewText
End Set
End Property
Private Property IUserMedia_PostTextFile As String Implements IUserMedia.PostTextFile
Get
Return PostTextFile.ToString
End Get
Set(ByVal NewPostFile As String)
If Not NewPostFile.IsEmptyString Then PostTextFile = New SFile(NewPostFile) Else PostTextFile = Nothing
End Set
End Property
Private Property IUserMedia_PostTextFileSpecialFolder As Boolean Implements IUserMedia.PostTextFileSpecialFolder
Get
Return PostTextFileSpecialFolder
End Get
Set(ByVal IsSpecialFolder As Boolean)
PostTextFileSpecialFolder = IsSpecialFolder
End Set
End Property
Private Property IUserMedia_SpecialFolder As String Implements IUserMedia.SpecialFolder
Get
Return SpecialFolder
@@ -171,6 +215,9 @@ Namespace API.Base
SpecialFolder = m.SpecialFolder
Attempts = m.Attempts
Me.Object = m.Object
PostText = m.PostText
PostTextFile = m.PostTextFile
PostTextFileSpecialFolder = m.PostTextFileSpecialFolder
End Sub
Friend Sub New(ByVal e As EContainer, ByVal UserInstance As IUserData)
Type = e.Attribute(Name_MediaType).Value.FromXML(Of Integer)(CInt(Types.Undefined))
@@ -180,6 +227,8 @@ Namespace API.Base
URL_BASE = e.Value
MD5 = e.Attribute(Name_MediaHash).Value
File = e.Attribute(Name_MediaFile).Value
PostTextFile = e.Attribute(Name_PostTextFile).Value
PostTextFileSpecialFolder = e.Attribute(Name_PostTextFileSpecialFolder).Value.FromXML(Of Boolean)(False)
Dim vp As Boolean? = Nothing
Dim upath$ = String.Empty
@@ -194,7 +243,13 @@ Namespace API.Base
SpecialFolder = e.Attribute(Name_SpecialFolder).Value
If Not SpecialFolder.IsEmptyString Then upath &= $"{SpecialFolder}\"
If vp.HasValue AndAlso vp.Value Then upath &= $"Video\"
If Not upath.IsEmptyString Then File = $"{upath.CSFilePS}{File.File}"
If Not upath.IsEmptyString Then
File = $"{upath.CSFilePS}{File.File}"
If Not PostTextFile.IsEmptyString Then
PostTextFile = $"{upath.CSFilePS}{IIf(PostTextFileSpecialFolder, $"{UserDataBase.PostTextSpecialFolderDefault}\", String.Empty)}{PostTextFile.File}"
If Type = Types.Text Then File = PostTextFile
End If
End If
Post = New UserPost With {
.ID = e.Attribute(Name_MediaPostID).Value,
@@ -234,7 +289,9 @@ Namespace API.Base
New EAttribute(Name_MediaURL, URL),
New EAttribute(Name_MediaHash, MD5),
New EAttribute(Name_MediaFile, File.File),
New EAttribute(Name_PostTextFile, PostTextFile.File),
New EAttribute(Name_SpecialFolder, SpecialFolder),
New EAttribute(Name_PostTextFileSpecialFolder, PostTextFileSpecialFolder.BoolToInteger),
New EAttribute(Name_MediaPostID, Post.ID),
New EAttribute(Name_MediaPostDate, AConvert(Of String)(Post.Date, DateTimeDefaultProvider, String.Empty))
}

View File

@@ -13,10 +13,21 @@ Namespace API.Base
Friend Property TempPostsList As List(Of String)
Protected ReadOnly Token As CancellationToken
Friend Property DebugMode As Boolean = False
Friend Sub New(ByVal _Token As CancellationToken)
Friend Overridable Property MyWorkingDirectory As SFile = Nothing
Friend Sub New(ByVal _Token As CancellationToken, Optional ByVal _MainProcessName As String = Nothing, Optional ByVal WorkingDir As SFile = Nothing)
MyBase.New(True)
Token = _Token
MainProcessName = _MainProcessName
If Not WorkingDir.IsEmptyString Then ChangeDirectory(WorkingDir)
End Sub
Public Overrides Sub ChangeDirectory(ByVal Directory As SFile)
MyBase.ChangeDirectory(Directory)
If Not Directory.IsEmptyString Then MyWorkingDirectory = Directory
End Sub
Protected Overrides Function Internal_Execute(ByVal Commands As IEnumerable(Of String), ByVal e As ErrorsDescriber) As Boolean
If Not Encoding.HasValue Then Encoding = UnicodeEncoding
Return MyBase.Internal_Execute(Commands, e)
End Function
Public Overrides Sub Create()
If TempPostsList Is Nothing Then TempPostsList = New List(Of String)
MyBase.Create()

View File

@@ -27,6 +27,7 @@ Imports CookieUpdateModes = PersonalUtilities.Tools.Web.Cookies.CookieKeeper.Upd
Namespace API.Base
Friend MustInherit Class UserDataBase : Implements IUserData, IPluginContentProvider, IThrower
Friend Const UserFileAppender As String = "User"
Friend Const PostTextSpecialFolderDefault As String = "txt"
#Region "Events"
Private ReadOnly UserUpdatedEventHandlers As List(Of IUserData.UserUpdatedEventHandler)
Friend Custom Event UserUpdated As IUserData.UserUpdatedEventHandler Implements IUserData.UserUpdated
@@ -152,6 +153,9 @@ Namespace API.Base
Private Const Name_IsSubscription As String = UserInfo.Name_IsSubscription
Private Const Name_Temporary As String = "Temporary"
Private Const Name_Favorite As String = "Favorite"
Private Const Name_DownloadText As String = "DownloadText"
Private Const Name_DownloadTextPosts As String = "DownloadTextPosts"
Private Const Name_DownloadTextSpecialFolder As String = "DownloadTextSpecialFolder"
Private Const Name_BackColor As String = "BackColor"
Private Const Name_ForeColor As String = "ForeColor"
Private Const Name_CreatedByChannel As String = "CreatedByChannel"
@@ -167,6 +171,7 @@ Namespace API.Base
Private Const Name_VideoCount As String = "VideoCount"
Private Const Name_PicturesCount As String = "PicturesCount"
Private Const Name_TextCount As String = "TextCount"
Private Const Name_LastUpdated As String = "LastUpdated"
Private Const Name_ScriptUse As String = "ScriptUse"
@@ -247,7 +252,20 @@ Namespace API.Base
#End Region
#Region "User name, ID, exist, suspend, options"
Friend User As UserInfo
Private _IsSavedPosts As Boolean = False
Friend Property IsSavedPosts As Boolean Implements IPluginContentProvider.IsSavedPosts
Get
Return _IsSavedPosts
End Get
Set(ByVal __IsSavedPosts As Boolean)
_IsSavedPosts = __IsSavedPosts
If _IsSavedPosts Then
DownloadText = True
DownloadTextPosts = True
DownloadTextSpecialFolder = True
End If
End Set
End Property
Private _UserExists As Boolean = True
Friend Overridable Property UserExists As Boolean Implements IUserData.Exists, IPluginContentProvider.UserExists
Get
@@ -295,7 +313,16 @@ Namespace API.Base
Return If(Exact, _NameTrue, _NameTrue.IfNullOrEmpty(Name))
End Get
End Property
Friend Overridable Property ID As String = String.Empty Implements IUserData.ID, IPluginContentProvider.ID
Private _ID As String = String.Empty
Friend Property ID As String Implements IUserData.ID, IPluginContentProvider.ID
Get
Return _ID
End Get
Set(ByVal NewId As String)
If Not _ID = NewId Then EnvirChanged(NewId)
_ID = NewId
End Set
End Property
Protected _FriendlyName As String = String.Empty
Friend Overridable Property FriendlyName As String Implements IUserData.FriendlyName
Get
@@ -639,6 +666,9 @@ BlockNullPicture:
Friend Overridable Property ReadyForDownload As Boolean = True Implements IUserData.ReadyForDownload
Friend Property DownloadImages As Boolean = True Implements IUserData.DownloadImages
Friend Property DownloadVideos As Boolean = True Implements IUserData.DownloadVideos
Friend Overridable Property DownloadText As Boolean = False
Friend Overridable Property DownloadTextPosts As Boolean = False
Friend Overridable Property DownloadTextSpecialFolder As Boolean = True
Friend Property DownloadMissingOnly As Boolean = False Implements IUserData.DownloadMissingOnly
Private _IconBannerDownloaded As Boolean = False
Friend WriteOnly Property IconBannerDownloaded As Boolean
@@ -750,9 +780,23 @@ BlockNullPicture:
End If
End Set
End Property
Private _DownloadedTextsTotal As Integer = 0
Private _DownloadedTextsSession As Integer = 0
Friend Property DownloadedTexts(ByVal Total As Boolean) As Integer Implements IUserData.DownloadedTexts
Get
Return IIf(Total, _DownloadedTextsTotal, _DownloadedTextsSession)
End Get
Set(ByVal NewValue As Integer)
If Total Then
_DownloadedTextsTotal = NewValue
Else
_DownloadedTextsSession = NewValue
End If
End Set
End Property
Friend Overridable ReadOnly Property DownloadedTotal(Optional ByVal Total As Boolean = True) As Integer Implements IUserData.DownloadedTotal
Get
Return DownloadedPictures(Total) + DownloadedVideos(Total)
Return DownloadedPictures(Total) + DownloadedVideos(Total) + DownloadedTexts(Total)
End Get
End Property
Friend ReadOnly Property DownloadedInformation As String Implements IUserData.DownloadedInformation
@@ -963,9 +1007,13 @@ BlockNullPicture:
ReadyForDownload = x.Value(Name_ReadyForDownload).FromXML(Of Boolean)(True)
DownloadImages = x.Value(Name_DownloadImages).FromXML(Of Boolean)(True)
DownloadVideos = x.Value(Name_DownloadVideos).FromXML(Of Boolean)(True)
DownloadText = x.Value(Name_DownloadText).FromXML(Of Boolean)(IsSavedPosts)
DownloadTextPosts = x.Value(Name_DownloadTextPosts).FromXML(Of Boolean)(IsSavedPosts)
DownloadTextSpecialFolder = x.Value(Name_DownloadTextSpecialFolder).FromXML(Of Boolean)(True)
_IconBannerDownloaded = x.Value(Name_IconBannerDownloaded).FromXML(Of Boolean)(False)
DownloadedVideos(True) = x.Value(Name_VideoCount).FromXML(Of Integer)(0)
DownloadedPictures(True) = x.Value(Name_PicturesCount).FromXML(Of Integer)(0)
DownloadedTexts(True) = x.Value(Name_TextCount).FromXML(Of Integer)(0)
LastUpdated = AConvert(Of Date)(x.Value(Name_LastUpdated), ADateTime.Formats.BaseDateTime, Nothing)
ScriptUse = x.Value(Name_ScriptUse).FromXML(Of Boolean)(False)
ScriptData = x.Value(Name_ScriptData)
@@ -1025,9 +1073,13 @@ BlockNullPicture:
x.Add(Name_ReadyForDownload, ReadyForDownload.BoolToInteger)
x.Add(Name_DownloadImages, DownloadImages.BoolToInteger)
x.Add(Name_DownloadVideos, DownloadVideos.BoolToInteger)
x.Add(Name_DownloadText, DownloadText.BoolToInteger)
x.Add(Name_DownloadTextPosts, DownloadTextPosts.BoolToInteger)
x.Add(Name_DownloadTextSpecialFolder, DownloadTextSpecialFolder.BoolToInteger)
x.Add(Name_IconBannerDownloaded, _IconBannerDownloaded.BoolToInteger)
x.Add(Name_VideoCount, DownloadedVideos(True))
x.Add(Name_PicturesCount, DownloadedPictures(True))
x.Add(Name_TextCount, DownloadedTexts(True))
x.Add(Name_LastUpdated, AConvert(Of String)(LastUpdated, ADateTime.Formats.BaseDateTime, String.Empty))
x.Add(Name_ScriptUse, ScriptUse.BoolToInteger)
x.Add(Name_ScriptData, ScriptData)
@@ -1192,7 +1244,8 @@ BlockNullPicture:
Select Case Caller
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(NameTrue) : _EnvirChanged = True : _EnvirInvokeUserUpdated = True : _ForceSaveUserInfo = True : _ForceSaveUserInfoOnException = True
Case NameOf(NameTrue) : _EnvirChanged = True : _ForceSaveUserInfo = True : _ForceSaveUserInfoOnException = True
Case NameOf(ID) : _EnvirChanged = True : _ForceSaveUserInfo = True : _ForceSaveUserInfoOnException = True
Case Else : _EnvirChanged = True
End Select
End If
@@ -1246,6 +1299,7 @@ BlockNullPicture:
If Not DownloadImages Then _TempMediaList.RemoveAll(Function(m) m.Type = UTypes.GIF Or m.Type = UTypes.Picture)
If Not DownloadVideos Then _TempMediaList.RemoveAll(Function(m) m.Type = UTypes.Video Or
m.Type = UTypes.VideoPre Or m.Type = UTypes.m3u8)
If Not DownloadTextPosts Then _TempMediaList.RemoveAll(Function(m) m.Type = UTypes.Text)
If DownloadMissingOnly Then _TempMediaList.RemoveAll(Function(m) Not m.State = UStates.Missing)
End If
@@ -1385,6 +1439,16 @@ BlockNullPicture:
Cache.Validate()
Return Cache
End Function
#Region "GDL File Names"
Protected GDLFileNameProvider As ANumbers = Nothing
Protected Sub GDLResetFileNameProvider(Optional ByVal GroupSize As Integer? = Nothing)
GDLFileNameProvider = New ANumbers With {.FormatOptions = ANumbers.Options.FormatNumberGroup + ANumbers.Options.Groups}
GDLFileNameProvider.GroupSize = If(GroupSize, 3)
End Sub
Protected Function GDLRenameFile(ByVal Input As SFile, ByVal i As Integer) As SFile
Return SFile.Rename(Input, $"{Input.PathWithSeparator}{i.NumToString(GDLFileNameProvider)}.{Input.Extension}",, EDP.ThrowException)
End Function
#End Region
#Region "DownloadSingleObject"
Protected IsSingleObjectDownload As Boolean = False
Friend Overridable Sub DownloadSingleObject(ByVal Data As YouTube.Objects.IYouTubeMediaContainer, ByVal Token As CancellationToken) Implements IUserData.DownloadSingleObject
@@ -1659,7 +1723,8 @@ BlockNullPicture:
Dim dCount% = 0, dTotal% = 0
ThrowAny(Token)
If _ContentNew.Count > 0 Then
_ContentNew.RemoveAll(Function(c) c.URL.IsEmptyString)
_ContentNew.RemoveAll(Function(c) Not c.Type = UTypes.Text And c.URL.IsEmptyString)
If Not DownloadText Or Not DownloadTextPosts Then _ContentNew.RemoveAll(Function(c) c.Type = UTypes.Text)
If _ContentNew.Count > 0 Then
If UseMD5Comparison Then LoadMD5()
MyFile.Exists(SFO.Path)
@@ -1668,7 +1733,8 @@ BlockNullPicture:
Dim vsf As Boolean = SeparateVideoFolderF
Dim __isVideo As Boolean
Dim __interrupt As Boolean
Dim f As SFile
Dim postProcessWebp As Boolean
Dim f As SFile, fTxt As SFile
Dim v As UserMedia
Dim __fileDeleted As Boolean
Dim fileNumProvider As SFileNumbers = SFileNumbers.Default
@@ -1682,9 +1748,11 @@ BlockNullPicture:
ErrorsDescriber.Execute(EDP.SendToLog, file_del_ex)
End Try
End Sub
Dim updateDownCount As Action = Sub()
Dim __n% = IIf(__fileDeleted, -1, 1)
If __isVideo Then
Dim updateDownCount As Action(Of Boolean) = Sub(ByVal forceText As Boolean)
Dim __n% = IIf(__fileDeleted And Not forceText, -1, 1)
If v.Type = UTypes.Text Or forceText Then
DownloadedTexts(False) += __n
ElseIf __isVideo Then
v.Type = UTypes.Video
DownloadedVideos(False) += __n
ElseIf v.Type = UTypes.GIF Then
@@ -1725,9 +1793,11 @@ BlockNullPicture:
If v.URL_BASE.IsEmptyString Then v.URL_BASE = v.URL
__fileDeleted = False
postProcessWebp = False
If Not f.IsEmptyString And Not v.URL.IsEmptyString Then
If (v.Type = UTypes.Text And DownloadText) Or (Not f.IsEmptyString And Not v.URL.IsEmptyString) Then
Try
If v.Type = UTypes.Text Then GoTo stxt
__isVideo = v.Type = UTypes.Video Or f.Extension = "mp4" Or v.Type = UTypes.m3u8
If f.Extension.IsEmptyString Then
@@ -1736,8 +1806,9 @@ BlockNullPicture:
Case UTypes.Video, UTypes.m3u8 : f.Extension = "mp4"
Case UTypes.GIF : f.Extension = "gif"
End Select
ElseIf f.Extension = "webp" And Settings.DownloadNativeImageFormat Then
f.Extension = "jpg"
ElseIf f.Extension = UserImage.ExtWebp And Settings.DownloadNativeImageFormat And Settings.FfmpegFile.Exists Then
'f.Extension = "jpg"
postProcessWebp = True
End If
If Not v.SpecialFolder.IsEmptyString Then
@@ -1770,9 +1841,9 @@ BlockNullPicture:
End If
End If
updateDownCount()
updateDownCount(False)
v.File = ChangeFileNameByProvider(f, v)
v.File = DownloadContentDefault_ConvertWebp(ChangeFileNameByProvider(f, v), postProcessWebp)
v.State = UStates.Downloaded
DownloadContentDefault_PostProcessing(v, f, Token)
If UseMD5Comparison And (v.Type = UTypes.GIF Or v.Type = UTypes.Picture) Then
@@ -1781,7 +1852,7 @@ BlockNullPicture:
If Not v.MD5.IsEmptyString Then
If _MD5List.Contains(v.MD5) Then
__fileDeleted = v.File.Delete(SFO.File, SFODelete.DeletePermanently, EDP.ReturnValue)
If __fileDeleted Then dCount -= 1 : updateDownCount()
If __fileDeleted Then dCount -= 1 : updateDownCount(False)
Else
_MD5List.Add(v.MD5)
End If
@@ -1790,7 +1861,33 @@ BlockNullPicture:
dCount -= 1
End If
End If
stxt:
If DownloadText And Not v.PostText.IsEmptyString And (v.Type = UTypes.Text Or v.File.Exists) Then
fTxt = v.File
If fTxt.IsEmptyString Then
If DownloadTextPosts And Not f.IsEmptyString Then
fTxt = f
If v.Type = UTypes.Text Then fTxt.Name &= IIf(fTxt.Name.IsEmptyString, String.Empty, "_") &
v.Post.ID.StringRemoveWinForbiddenSymbols
If fTxt.IsEmptyString Then Throw New ArgumentNullException("Text", "Error downloading text") With {.HelpLink = 10}
Else
Continue For
End If
End If
v.PostTextFileSpecialFolder = DownloadTextSpecialFolder
If DownloadTextSpecialFolder Then fTxt.Path = $"{fTxt.Path.StringTrimEnd("\")}\{PostTextSpecialFolderDefault}"
fTxt.Extension = "txt"
v.PostTextFile = TextSaver.SaveTextToFile(v.PostText, fTxt,,, Settings.FeedShowTextPosts_LogErrors_E)
If Not v.PostTextFile.Exists Then Throw New ArgumentNullException("Text", "Error downloading text") With {.HelpLink = 10}
If v.Type = UTypes.Text Then v.File = v.PostTextFile
v.State = UStates.Downloaded
updateDownCount(Not v.Type = UTypes.Text)
If v.URL.IsEmptyString Then v.URL = v.PostTextFile.File
If v.URL_BASE.IsEmptyString Then v.URL_BASE = v.URL
End If
dCount += 1
Catch anex As ArgumentNullException When anex.HelpLink = 10
LogError(anex, anex.Message, Settings.FeedShowTextPosts_LogErrors_E)
Catch woex As OperationCanceledException When Token.IsCancellationRequested
__deleteFile.Invoke(f, v.URL_BASE)
v.State = UStates.Missing
@@ -1798,7 +1895,7 @@ BlockNullPicture:
_ContentNew(i) = v
Throw woex
Catch wex As Exception
If DownloadContentDefault_ProcessDownloadException() Then
If Not v.Type = UTypes.Text AndAlso DownloadContentDefault_ProcessDownloadException() Then
v.Attempts += 1
v.State = UStates.Missing
If MissingErrorsAdd Then ErrorDownloading(f, v.URL)
@@ -1846,6 +1943,22 @@ BlockNullPicture:
End Function
Protected Overridable Sub DownloadContentDefault_PostProcessing(ByRef m As UserMedia, ByVal File As SFile, ByVal Token As CancellationToken)
End Sub
Protected Overridable Function DownloadContentDefault_ConvertWebp(ByVal WebpFile As SFile, ByVal Process As Boolean) As SFile
Dim f As SFile = WebpFile
If Process AndAlso f.Exists Then
f.Path = $"{f.PathWithSeparator}Sources"
f.Exists(SFO.Path)
If WebpFile.Copy(f) Then
Dim newFile As SFile = WebpFile
newFile.Extension = UserImage.ExtJpg
f = UserImage.ConvertWebp(f, newFile)
If f.Exists Then WebpFile.Delete(SFO.File, SFODelete.DeletePermanently, EDP.ReturnValue)
Else
f = WebpFile
End If
End If
Return f
End Function
Protected Overridable Function DownloadContentDefault_ProcessDownloadException() As Boolean
Return True
End Function
@@ -1991,7 +2104,7 @@ BlockNullPicture:
End If
If m.Contains(IUserData.EraseMode.Data) Then
Dim files As List(Of SFile) = SFile.GetFiles(DownloadContentDefault_GetRootDir.CSFileP,, SearchOption.AllDirectories, e)
If files.ListExists Then files.RemoveAll(Function(f) Not f.Extension.IsEmptyString AndAlso (f.Extension = "txt" Or f.Extension = "xml"))
If files.ListExists Then files.RemoveAll(Function(f) Not f.Extension.IsEmptyString AndAlso ((f.Path.EndsWith(SettingsFolderName) And f.Extension = "txt") Or f.Extension = "xml"))
If files.ListExists Then files.ForEach(Sub(f) f.Delete(SFO.File, Settings.DeleteMode, e))
LatestData.Clear()
result = True
@@ -2204,6 +2317,7 @@ BlockNullPicture:
End Function
#End Region
#Region "Errors functions"
''' <summary>ToStringForLog(): Message</summary>
Protected Sub LogError(ByVal ex As Exception, ByVal Message As String, Optional ByVal e As ErrorsDescriber = Nothing)
ErrorsDescriber.Execute(If(e.Exists, e, New ErrorsDescriber(EDP.SendToLog)), ex, $"{ToStringForLog()}: {Message}")
End Sub
@@ -2234,6 +2348,17 @@ BlockNullPicture:
Friend Function ToStringForLog() As String
Return $"{IIf(IncludedInCollection, $"[{CollectionName}] - ", String.Empty)}[{Site}] - {Name}"
End Function
Friend Overloads Function ToStringExt(ByVal UseFriendlyName As Boolean) As String
Return $"{IIf(IncludedInCollection, $"[{CollectionName}] - ", String.Empty)}[{Site}] - {String.Format(CStr(IIf(Not FriendlyName.IsEmptyString And
UseFriendlyName, "{1} ({0})", "{0}")), Name, FriendlyName)}"
End Function
Friend Overloads Shared Function ToStringExt(ByVal User As UserInfo) As String
If Not IsDBNull(User) Then
With User : Return $"{IIf(.IncludedInCollection, $"[{ .CollectionName}] - ", String.Empty)}[{ .Site}] - { .Name}" : End With
Else
Return String.Empty
End If
End Function
Public Overrides Function ToString() As String
If IsCollection Then
Return CollectionName
@@ -2338,6 +2463,7 @@ BlockNullPicture:
_TempPostsList.Clear()
_MD5List.Clear()
TokenPersonal = Nothing
GDLFileNameProvider = Nothing
If Not ProgressPre Is Nothing Then ProgressPre.Reset() : ProgressPre.Dispose()
If Not Responser Is Nothing Then Responser.Dispose()
If Not BTT_CONTEXT_DOWN Is Nothing Then BTT_CONTEXT_DOWN.Dispose()

View File

@@ -8,11 +8,8 @@
' but WITHOUT ANY WARRANTY
Namespace API.Base.YTDLP
Friend Class YTDLPBatch : Inherits GDL.GDLBatch
Friend Sub New(ByVal _Token As Threading.CancellationToken)
MyBase.New(_Token)
Commands.Clear()
MainProcessName = Settings.YtdlpFile.File.Name '"yt-dlp"
ChangeDirectory(Settings.YtdlpFile.File)
Friend Sub New(ByVal _Token As Threading.CancellationToken, Optional ByVal __MainProcessName As String = Nothing, Optional ByVal WorkingDir As SFile = Nothing)
MyBase.New(_Token, __MainProcessName.IfNullOrEmpty(Settings.YtdlpFile.File.Name), WorkingDir.IfNullOrEmpty(Settings.YtdlpFile.File))
End Sub
End Class
End Namespace

View File

@@ -14,7 +14,7 @@ Imports PersonalUtilities.Functions.RegularExpressions
Imports PersonalUtilities.Tools.Web.Clients
Imports PersonalUtilities.Tools.Web.Documents.JSON
Namespace API.Bluesky
<Manifest(BlueskySiteKey), SpecialForm(False)>
<Manifest(BlueskySiteKey), SpecialForm(False), SavedPosts>
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

View File

@@ -27,6 +27,7 @@ Namespace API.Bluesky
Return If(ID.IsEmptyString, String.Empty, SymbolsConverter.ASCII.EncodeSymbolsOnly(ID))
End Get
End Property
Private ReadOnly _TmpPosts2 As List(Of String)
#End Region
#Region "Loader"
Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean)
@@ -36,12 +37,13 @@ Namespace API.Bluesky
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
DirectCast(Obj, EditorExchangeOptionsBase).SiteKey = BlueskySiteKey Then DirectCast(Obj, EditorExchangeOptionsBase).ApplyBase(Me)
End Sub
#End Region
#Region "Initializer"
Friend Sub New()
UseInternalM3U8Function = True
_TmpPosts2 = New List(Of String)
End Sub
#End Region
#Region "Token"
@@ -62,31 +64,48 @@ Namespace API.Bluesky
#Region "Download"
Private _PostCount As Integer = 0
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
_TmpPosts2.Clear()
Try
If Not CBool(MySettings.CookiesEnabled.Value) Then Responser.Cookies.Clear()
UpdateToken(, True)
_TokenUpdateCount = 0
_PostCount = 0
DownloadData(String.Empty, Token)
Finally
_TempPostsList.ListAddList(_TmpPosts2, LNC)
_TmpPosts2.Clear()
End Try
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 Not IsSavedPosts And ID.IsEmptyString Then GetProfileInfo(Token)
If Not IsSavedPosts And ID.IsEmptyString Then Throw New ArgumentNullException("ID", "ID is null")
If UpdateToken() Then
Dim nextCursor$ = String.Empty
Dim c%
Dim n$(), p$()
If IsSavedPosts Then
URL = "https://bsky.social/xrpc/app.bsky.bookmark.getBookmarks"
If Not Cursor.IsEmptyString Then URL &= $"?cursor={Cursor}"
n = {"bookmarks"}
p = {"item"}
Else
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)}"
n = {"feed"}
p = {"post"}
End If
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")
nextCursor = j.Value("cursor")
With j(n)
If .ListExists Then
For Each post As EContainer In .Self
With post({"post"})
With post(p)
c = DefaultParser(.Self,, nextCursor)
Select Case c
Case CInt(DateResult.Skip) * -1 : Continue For
@@ -96,6 +115,8 @@ Namespace API.Bluesky
If DownloadTopCount.HasValue AndAlso DownloadTopCount.Value <= _PostCount Then Exit Sub
End With
Next
ElseIf IsSavedPosts Then
nextCursor = String.Empty
End If
End With
End If
@@ -117,7 +138,8 @@ Namespace API.Bluesky
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$
Const skipReturn% = CInt(DateResult.Skip) * -1
Dim postID$, postDate$, __url$, __urlBase$, __txt$, __userId$, __postAuthor$
Dim updateUrl As Boolean
Dim c% = 0
Dim m As UserMedia
@@ -127,22 +149,30 @@ Namespace API.Bluesky
postID = GetPostID(.Value("uri"))
postDate = String.Empty
__urlBase = String.Empty
__txt = String.Empty
__userId = .Value({"author"}, "did")
__postAuthor = String.Empty
With .Item({"record"})
If .ListExists Then
'2025-01-28T02:42:12.415Z
postDate = .Value("createdAt")
NextCursor = postDate
If Not IsSavedPosts Then NextCursor = postDate
If CheckDateLimits Then
Select Case CheckDatesLimit(postDate, DateProvider)
Case DateResult.Skip : Return CInt(DateResult.Skip) * -1 'Continue For
Case DateResult.Skip : Return skipReturn '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)
'If _TempPostsList.Contains(postID) Then Return exitReturn Else _TempPostsList.Add(postID)
If _TempPostsList.Contains(postID) Then Return exitReturn Else _TmpPosts2.Add(postID)
End If
__urlBase = $"https://bsky.app/profile/{NameTrue}/post/{postID}"
If ParseUserMediaOnly And Not IsSavedPosts And Not ID.IsEmptyString And Not __userId.IsEmptyString And Not ID = __userId Then Return skipReturn
__postAuthor = e.Value({"author"}, "did")
__urlBase = $"https://bsky.app/profile/{If(IsSavedPosts, __postAuthor, NameTrue)}/post/{postID}"
End If
End With
@@ -152,13 +182,18 @@ Namespace API.Bluesky
.URL_BASE = __urlBase,
.File = CreateFileFromUrl(url, type),
.Post = New UserPost(postID, If(AConvert(Of Date)(postDate, DateProvider, Nothing, EDP.ReturnValue), Nothing)),
.State = State
.State = State,
.PostText = __txt,
.PostTextFileSpecialFolder = DownloadTextSpecialFolder
}
If type = UTypes.Text Then m.PostTextFile = $"{postID}.txt"
_TempMediaList.ListAddValue(m, LNC)
c += 1
Return m
End Function
__txt = .Value({"record"}, "text").IfNullOrEmpty(__txt)
For Each SecondExtraction As Boolean In {False, True}
With If(SecondExtraction, .Item({"record", "embed"}), .Item("embed"))
If .ListExists Then
@@ -170,15 +205,26 @@ Namespace API.Bluesky
__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)
If Not __url.IsEmptyString Then
If updateUrl AndAlso Not __url.StartsWith("http") Then _
__url = $"https://cdn.bsky.app/img/feed_fullsize/plain/{__postAuthor}/{__url}@jpeg"
createMedia(__url, UTypes.Picture)
End If
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)
If If(.Item("external")?.Count, 0) > 0 Then
__txt = .Value({"external"}, "title").IfNullOrEmpty(__txt)
createMedia(.Value({"external"}, "uri"), UTypes.GIF)
End If
If If(.Item({"media"}, "external")?.Count, 0) > 0 Then
__txt = .Value({"media", "external"}, "title").IfNullOrEmpty(__txt)
createMedia(.Value({"media", "external"}, "uri"), UTypes.GIF)
End If
End If
End With
@@ -325,6 +371,12 @@ Namespace API.Bluesky
Return 0
End If
End Function
#End Region
#Region "IDisposable Support"
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then _TmpPosts2.Clear()
MyBase.Dispose(disposing)
End Sub
#End Region
End Class
End Namespace

View File

@@ -40,6 +40,9 @@ Namespace API.Facebook
Friend ReadOnly Property ParseReelsBlock As PropertyValue
<PropertyOption(ControlText:="Download stories", IsAuth:=False, Category:=DN.CAT_UserDefs), PXML, PClonable>
Friend ReadOnly Property ParseStoriesBlock As PropertyValue
<DoNotUse> Friend Overrides Property DownloadText As PropertyValue
<DoNotUse> Friend Overrides Property DownloadTextPosts As PropertyValue
<DoNotUse> Friend Overrides Property DownloadTextSpecialFolder As PropertyValue
#End Region
#End Region
#Region "Initializer"

View File

@@ -676,10 +676,7 @@ Namespace API.Facebook
End If
Token_Photosby = RegexReplace(r, Regex_Photos_by)
If StoryBucket.IsEmptyString Then StoryBucket = RegexReplace(r, Regex_StoryBucket)
If ID.IsEmptyString Then
ID = RegexReplace(r, Regex_UserID)
If Not ID.IsEmptyString Then _ForceSaveUserInfo = True
End If
If ID.IsEmptyString Then ID = RegexReplace(r, Regex_UserID)
End If
Catch ex As Exception
ProcessException(ex, Token, "get user token",, resp)

View File

@@ -20,6 +20,7 @@ Namespace API.Instagram
Friend Const PageTokenRegexPatternDefault As String = "\[\],{""token"":""(.*?)""},\d+\]"
Friend ReadOnly Regex_UserToken_dtsg As RParams = RParams.DMS("DTSGInitialData["":,.\[\]]*?{\s*.token.:\s*""([^""]+)", 1, EDP.ReturnValue)
Friend ReadOnly Regex_UserToken_lsd As RParams = RParams.DMS("LSD["":,.\[\]]*?{\s*.token.:\s*""([^""]+)", 1, EDP.ReturnValue)
Friend ReadOnly Regex_ProfileID As RParams = RParams.DMS("profilePage_(\d+)", 1, EDP.ReturnValue)
Friend Sub UpdateResponser(ByVal Source As IResponse, ByRef Destination As Responser, ByVal UpdateWwwClaim As Boolean)
Const r_wwwClaimName$ = "x-ig-set-www-claim"
Const r_tokenName$ = SiteSettings.Header_CSRF_TOKEN_COOKIE

View File

@@ -36,6 +36,8 @@ Namespace API.Instagram
<PSetting(Caption:="Place the extracted image into the video folder")>
Friend Property PutImageVideoFolder As Boolean
Friend Overrides Property UserName As String
<PSetting(Address:=SettingAddress.User, Caption:="Verified profile", ToolTip:="This profile has a verified mark")>
Friend Property IsVerifiedProfile As Boolean = False
<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")>
@@ -57,11 +59,14 @@ Namespace API.Instagram
PutImageVideoFolder = .PutImageVideoFolder
IsVerifiedProfile = .IsVerifiedProfile
ForceUpdateUserName = .ForceUpdateUserName
ForceUpdateUserInfo = .ForceUpdateUserInfo
End With
End Sub
Friend Sub New(ByVal s As SiteSettings)
MyBase.New(s)
With s
GetTimeline = CBool(.GetTimeline.Value)
GetReels = CBool(.GetReels.Value)

View File

@@ -148,6 +148,8 @@ Namespace API.Instagram
#End Region
<PropertyOption(ControlText:="Use GraphQL to download", IsAuth:=True), PXML, PClonable>
Friend ReadOnly Property USE_GQL As PropertyValue
<PropertyOption(ControlText:="Use GraphQL to download user data", IsAuth:=True), PXML, PClonable, HiddenControl>
Friend ReadOnly Property USE_GQL_UserData As PropertyValue
#End Region
#Region "Download data"
<PropertyOption(ControlText:="Download timeline", Category:=CAT_DOWN), PXML, PClonable>
@@ -165,6 +167,14 @@ Namespace API.Instagram
<PropertyOption(ControlText:="Download tagged posts", Category:=CAT_DOWN), PXML, PClonable>
Friend ReadOnly Property DownloadTagged As PropertyValue
<PXML> Private ReadOnly Property DownloadTagged_Def As PropertyValue
<PropertyOption(ControlText:="Number of posts (verified)", ControlToolTip:="The number of posts received per request if the profile has a verified mark", Category:=CAT_DOWN), PXML, PClonable, HiddenControl>
Friend ReadOnly Property PostNumberVerified As PropertyValue
<Provider(NameOf(PostNumberVerified), FieldsChecker:=True)>
Private ReadOnly Property PostNumberVerifiedProvider As IFormatProvider
<PropertyOption(ControlText:="Number of posts (unverified)", ControlToolTip:="The number of posts received per request if the profile doesn't have a verified mark", Category:=CAT_DOWN), PXML, PClonable, HiddenControl>
Friend ReadOnly Property PostNumberVerifiedNot As PropertyValue
<Provider(NameOf(PostNumberVerifiedNot), FieldsChecker:=True)>
Private ReadOnly Property PostNumberVerifiedNotProvider As IFormatProvider
#End Region
#Region "Timers"
Friend Const TimersUrgentTip As String = vbCr & "It is highly recommended not to change the default value."
@@ -196,6 +206,18 @@ Namespace API.Instagram
Friend ReadOnly Property SleepTimerOnPostsLimit As PropertyValue
<Provider(NameOf(SleepTimerOnPostsLimit), FieldsChecker:=True)>
Private ReadOnly Property SleepTimerOnPostsLimitProvider As IFormatProvider
<PropertyOption(ControlText:="Next profile timer",
ControlToolTip:="The time value (in milliseconds) the program will wait before processing the next profile." &
vbCr & "-2 to use max timer." & vbCr & "-1 to disable." & vbCr & "The default value is -2" & TimersUrgentTip,
AllowNull:=False, Category:=DN.CAT_Timers), PXML, PClonable>
Friend ReadOnly Property SleepTimerRequestsNextProfile As PropertyValue
Friend ReadOnly Property SleepTimerRequestsNextProfileMax As Integer
Get
Return {RequestsWaitTimer_Any, RequestsWaitTimer, SleepTimerOnPostsLimit}.Max(Function(obj) CInt(obj.Value))
End Get
End Property
<Provider(NameOf(SleepTimerRequestsNextProfile), FieldsChecker:=True)>
Private ReadOnly Property SleepTimerRequestsNextProfileProvider As IFormatProvider
#End Region
#Region "New user defaults"
<PropertyOption(ControlText:="Get timeline", ControlToolTip:="Default value for new users", Category:=DN.CAT_UserDefs), PXML, PClonable>
@@ -473,6 +495,7 @@ Namespace API.Instagram
HH_IG_WWW_CLAIM_USE_DEFAULT_ALGO = New PropertyValue(True)
TokenUpdateIntervalProvider = New TokenRefreshIntervalProvider
USE_GQL = New PropertyValue(False)
USE_GQL_UserData = New PropertyValue(True)
DownloadTimeline = New PropertyValue(True)
DownloadTimeline_Def = New PropertyValue(DownloadTimeline.Value, GetType(Boolean))
@@ -484,6 +507,10 @@ Namespace API.Instagram
DownloadStoriesUser_Def = New PropertyValue(DownloadStoriesUser.Value, GetType(Boolean))
DownloadTagged = New PropertyValue(False)
DownloadTagged_Def = New PropertyValue(DownloadTagged.Value, GetType(Boolean))
PostNumberVerified = New PropertyValue(50)
PostNumberVerifiedProvider = New TimersChecker(12)
PostNumberVerifiedNot = New PropertyValue(12)
PostNumberVerifiedNotProvider = New TimersChecker(12)
RequestsWaitTimer_Any = New PropertyValue(1000)
RequestsWaitTimer_AnyProvider = New TimersChecker(0)
@@ -493,6 +520,8 @@ Namespace API.Instagram
RequestsWaitTimerTaskCountProvider = New TimersChecker(1)
SleepTimerOnPostsLimit = New PropertyValue(60000)
SleepTimerOnPostsLimitProvider = New TimersChecker(10000)
SleepTimerRequestsNextProfile = New PropertyValue(-2)
SleepTimerRequestsNextProfileProvider = New TimersChecker(-2)
GetTimeline = New PropertyValue(True)
GetTimeline_VideoPic = New PropertyValue(True)
@@ -531,18 +560,17 @@ Namespace API.Instagram
UserRegex = RParams.DMS(String.Format(UserRegexDefaultPattern, "instagram.com/"), 1)
ImageVideoContains = "instagram.com"
End Sub
Private Const SettingsVersionCurrent As Integer = 2
Private Const SettingsVersionCurrent As Integer = 3
Friend Overrides Sub EndInit()
Try : MyLastRequests.Add(LastDownloadDate.Value, LastRequestsCount.Value) : Catch : End Try
If Not CBool(HH_IG_WWW_CLAIM_USE.Value) Then Responser.Headers.Remove(Header_IG_WWW_CLAIM)
If CInt(SettingsVersion.Value) < SettingsVersionCurrent Then
SettingsVersion.Value = SettingsVersionCurrent
HH_IG_WWW_CLAIM_UPDATE_INTERVAL.Value = 120
HH_IG_WWW_CLAIM_ALWAYS_ZERO.Value = False
HH_IG_WWW_CLAIM_RESET_EACH_SESSION.Value = True
HH_IG_WWW_CLAIM_RESET_EACH_TARGET.Value = True
HH_IG_WWW_CLAIM_USE.Value = True
HH_IG_WWW_CLAIM_USE_DEFAULT_ALGO.Value = True
HH_IG_WWW_CLAIM_RESET_EACH_TARGET.Value = False
RequestsWaitTimer_Any.Value = 5000
TaggedNotifyLimit.Value = 50
DownDetectorValue.Value = 30
DownDetectorValueAddToLog.Value = True
End If
MyBase.EndInit()
End Sub
@@ -579,6 +607,7 @@ Namespace API.Instagram
MDD.Reset()
If ActiveSessionRequestsExists Then RefreshMyLastRequests(Now)
ActiveSessionRequestsExists = False
ActiveSessionLastProfileRequests = False
_NextWNM = UserData.WNM.Notify
_NextTagged = True
SkipUntilNextSession = False
@@ -596,6 +625,7 @@ Namespace API.Instagram
Private ActiveJobs As Integer = 0
Private ActiveSessionDate As Date
Private ActiveSessionRequestsExists As Boolean = False
Friend ActiveSessionLastProfileRequests As Boolean = False
Private _NextWNM As UserData.WNM = UserData.WNM.Notify
Private _NextTagged As Boolean = True
Friend Overrides Sub DownloadStarted(ByVal What As Download)
@@ -632,6 +662,7 @@ Namespace API.Instagram
_NextWNM = .WaitNotificationMode
If _NextWNM = UserData.WNM.SkipTemp Or _NextWNM = UserData.WNM.SkipCurrent Then _NextWNM = UserData.WNM.Notify
_NextTagged = .TaggedCheckSession
If MyLastRequestsCount <> .RequestsCountSession Then ActiveSessionLastProfileRequests = True
MyLastRequestsCount = .RequestsCountSession
If .RequestsCountSession > 0 Then ActiveSessionRequestsExists = True
_FieldsChangerSuspended = True

View File

@@ -6,12 +6,13 @@
'
' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY
Imports System.Security.Cryptography
Imports System.Threading
Imports SCrawler.API.Base
Imports PersonalUtilities.Functions.XML
Imports PersonalUtilities.Functions.RegularExpressions
Imports PersonalUtilities.Functions.XML
Imports PersonalUtilities.Tools.Web.Clients
Imports PersonalUtilities.Tools.Web.Documents.JSON
Imports SCrawler.API.Base
Namespace API.Instagram
Partial Friend Class UserData
#Region "Tokens"
@@ -43,9 +44,9 @@ Namespace API.Instagram
Private Const GQL_UserStories_DocId As String = "25231722019806941"
Private Const GQL_UserStories_FbFriendlyName As String = "PolarisStoriesV3ReelPageStandaloneQuery"
Private Const GQL_Timeline_DocId As String = "7268577773270422"
Private Const GQL_Timeline_DocId As String = "7268577773270422" '"34579740524958711" '"7268577773270422"
Private Const GQL_Timeline_FbFriendlyName As String = "PolarisProfilePostsQuery"
Private Const GQL_Timeline_DocId_Second As String = "7286316061475375"
Private Const GQL_Timeline_DocId_Second As String = "7286316061475375" '"33944389991841132" '"7286316061475375"
Private Const GQL_Timeline_FbFriendlyName_Second As String = "PolarisProfilePostsTabContentQuery_connection"
Private Const GQL_Reels_DocId As String = "7191572580905225"
@@ -64,33 +65,42 @@ Namespace API.Instagram
Responser.Headers.Add(GQL_HEADER_FB_FRINDLY_NAME, HeaderValue)
Responser.Headers.Add(GQL_HEADER_FB_LSD, Token_lsd)
End Sub
<Obsolete("Use 'GET' function: 'GetUserData'", False)>
Private Sub GetUserDataGQL(ByVal Token As CancellationToken)
'<Obsolete("Use 'GET' function: 'GetUserData'", False)>
Private Function GetUserDataGQL(ByVal Token As CancellationToken) As String
Dim vars$ = String.Format(GQL_URL_PATTERN_VARS, GQL_UserData_DocId, Token_lsd, Token_dtsg_Var, GQL_UserData_FbFriendlyName,
SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & $"""id"":""{ID}"",""relay_header"":false,""render_surface"":""PROFILE""" & "}"))
UpdateRequestNumber()
ChangeResponserMode(True)
UpdateHeadersGQL(GQL_UserData_FbFriendlyName)
Dim r$ = Responser.GetResponse(GQL_URL, vars)
If Not r.IsEmptyString Then
Using j As EContainer = JsonDocument.Parse(r)
If j.ListExists Then
With j({"data", "user"})
If .ListExists Then
UserSiteName = .Value("full_name").IfNullOrEmpty(UserSiteName)
Dim f As New SFile With {.Path = DownloadContentDefault_GetRootDir(), .Name = "ProfilePicture", .Extension = "jpg"}
Dim pic$ = .Value({"hd_profile_pic_url_info"}, "url").IfNullOrEmpty(.Value("profile_pic_url"))
If Not pic.IsEmptyString Then GetWebFile(pic, f, EDP.ReturnValue)
UserDescriptionUpdate(.Value("biography"))
End If
End With
End If
End Using
End If
End Sub
Return r
'If Not r.IsEmptyString Then
' Using j As EContainer = JsonDocument.Parse(r)
' If j.ListExists Then
' With j({"data", "user"})
' If .ListExists Then
' UserSiteName = .Value("full_name").IfNullOrEmpty(UserSiteName)
' IsVerifiedProfile = .Value("is_verified").FromXML(Of Boolean)(False)
' IsVerifiedProfile_Checked = True
' Dim descr$ = .Value("biography")
' If If(.Item("bio_links")?.Count, 0) > 0 Then descr.StringAppend(.Item("bio_links").Select(Function(bl) bl.Value("url")).ListToString(vbNewLine), vbNewLine)
' Dim eUrl$ = .Value("external_url")
' If Not eUrl.IsEmptyString AndAlso (descr.IsEmptyString OrElse Not descr.Contains(eUrl)) Then descr.StringAppendLine(eUrl)
' UserDescriptionUpdate(descr)
' Dim f As New SFile With {.Path = DownloadContentDefault_GetRootDir(), .Name = "ProfilePicture", .Extension = "jpg"}
' Dim pic$ = .Value({"hd_profile_pic_url_info"}, "url").IfNullOrEmpty(.Value("profile_pic_url"))
' If Not pic.IsEmptyString Then GetWebFile(pic, f, EDP.ReturnValue)
' End If
' End With
' End If
' End Using
'End If
End Function
Private Function GetTimelineGQL(ByVal Cursor As String, ByVal Token As CancellationToken) As String
Const none_cursor$ = "none"
Dim nextCursor$ = String.Empty, hasNextPage$ = String.Empty
Dim nextCursor$ = String.Empty
Dim hasNextPage As Boolean = False
Dim vars$
ThrowAny(Token)
@@ -98,14 +108,18 @@ Namespace API.Instagram
ChangeResponserMode(True)
If Cursor.IsEmptyString Then
vars = "{""data"":{""count"":50,""include_relationship_info"":true,""latest_besties_reel_media"":true,""latest_reel_media"":true},""username"":""" &
vars = "{""data"":{""count"":" & PostNumberPerRequest & ",""include_relationship_info"":true,""latest_besties_reel_media"":true,""latest_reel_media"":true},""username"":""" &
NameTrue & """,""__relay_internal__pv__PolarisShareMenurelayprovider"":false}"
'vars = "{""data"":{""count"":" & PostNumberPerRequest & ",""include_reel_media_seen_timestamp"":true,""include_relationship_info"":true,""latest_besties_reel_media"":true,""latest_reel_media"":true},""username"":""" &
' NameTrue & """,""__relay_internal__pv__PolarisShareMenurelayprovider"":false}"
vars = String.Format(GQL_URL_PATTERN_VARS, GQL_Timeline_DocId, Token_lsd, Token_dtsg_Var, GQL_Timeline_FbFriendlyName,
SymbolsConverter.ASCII.EncodeSymbolsOnly(vars))
UpdateHeadersGQL(GQL_Timeline_FbFriendlyName)
Else
vars = "{""after"":""" & Cursor & """,""before"":null,""data"":{""count"":50,""include_relationship_info"":true,""latest_besties_reel_media"":true,""latest_reel_media"":true},""first"":50,""last"":null,""username"":""" &
vars = "{""after"":""" & Cursor & """,""before"":null,""data"":{""count"":" & PostNumberPerRequest & ",""include_relationship_info"":true,""latest_besties_reel_media"":true,""latest_reel_media"":true},""first"":" & PostNumberPerRequest & ",""last"":null,""username"":""" &
NameTrue & """,""__relay_internal__pv__PolarisShareMenurelayprovider"":false}"
'vars = "{""after"":""" & Cursor & """,""before"":null,""data"":{""count"":" & PostNumberPerRequest & ",""include_reel_media_seen_timestamp"":true,""include_relationship_info"":true,""latest_besties_reel_media"":true,""latest_reel_media"":true},""first"":" & PostNumberPerRequest & ",""last"":null,""username"":""" &
' NameTrue & """}"
vars = String.Format(GQL_URL_PATTERN_VARS, GQL_Timeline_DocId_Second, Token_lsd, Token_dtsg_Var, GQL_Timeline_FbFriendlyName_Second,
SymbolsConverter.ASCII.EncodeSymbolsOnly(vars))
UpdateHeadersGQL(GQL_Timeline_FbFriendlyName_Second)
@@ -140,7 +154,8 @@ Namespace API.Instagram
End Function
Private Function GetHighlightsGQL_List() As List(Of String)
Dim nextCursor$ = String.Empty, hasNextPage$ = String.Empty
Dim nextCursor$ = String.Empty
Dim hasNextPage As Boolean = False
Dim i% = -1
Dim hList As New List(Of String)
Dim tmpList As New List(Of String)
@@ -178,7 +193,9 @@ Namespace API.Instagram
Dim tmpList As New List(Of String)
Dim i% = -1
If StoriesList.ListExists Then
tmpList.AddRange(StoriesList.Take(10))
'TODO: 5 Instagram stories
'tmpList.AddRange(StoriesList.Take(10))
tmpList.AddRange(StoriesList.Take(5))
StoriesList.RemoveRange(0, tmpList.Count)
Dim vars$ = String.Format(GQL_URL_PATTERN_VARS, GQL_Highlights_DocId_Second, Token_lsd, Token_dtsg_Var, GQL_Highlights_FbFriendlyName_Second,
@@ -238,11 +255,9 @@ Namespace API.Instagram
Private Function GetReelsGQL(ByVal Cursor As String) As String
GetReelsGQL_SetEnvir = True
Dim errData$ = String.Empty
If Cursor.IsEmptyString And Not ValidateBaseTokens() Then GetPageTokens()
If Cursor.IsEmptyString And Not ValidateBaseTokens(errData) Then ValidateBaseTokens_Error(errData)
UpdateTokens(Cursor.IsEmptyString)
Dim vars$ = """data"":{""include_feed_video"":true,""page_size"":50,""target_user_id"":""" & ID & """}"
Dim vars$ = """data"":{""include_feed_video"":true,""page_size"":" & PostNumberPerRequest & ",""target_user_id"":""" & ID & """}"
If Not Cursor.IsEmptyString Then vars = $"""after"":""{Cursor}"",""before"":null,{vars},""first"":4,""last"":null"
vars = String.Format(GQL_URL_PATTERN_VARS, GQL_Reels_DocId, Token_lsd, Token_dtsg_Var, GQL_Reels_FbFriendlyName,
SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & vars & "}"))
@@ -258,10 +273,10 @@ Namespace API.Instagram
Dim vars$
If Cursor.IsEmptyString Then
vars = String.Format(GQL_URL_PATTERN_VARS, GQL_Tagged_DocId, Token_lsd, Token_dtsg_Var, GQL_Tagged_FbFriendlyName,
SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & $"""count"":50,""user_id"":""{ID}""" & "}"))
SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & $"""count"":{PostNumberPerRequest},""user_id"":""{ID}""" & "}"))
Else
vars = String.Format(GQL_URL_PATTERN_VARS, GQL_Tagged_DocId, Token_lsd, Token_dtsg_Var, GQL_Tagged_FbFriendlyName,
SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & $"""after"":""{Cursor}"",""before"":null,""count"":50,""first"":50,""last"":null,""user_id"":""{ID}""" & "}"))
SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & $"""after"":""{Cursor}"",""before"":null,""count"":{PostNumberPerRequest},""first"":{PostNumberPerRequest},""last"":null,""user_id"":""{ID}""" & "}"))
End If
UpdateRequestNumber()
ChangeResponserMode(True)
@@ -270,6 +285,13 @@ Namespace API.Instagram
End Function
#End Region
#Region "ValidateBaseTokens"
Private Sub UpdateTokens(ByVal process As Boolean)
If process Then
Dim TokensErrData$ = String.Empty
If Not ValidateBaseTokens() Then GetPageTokens()
If Not ValidateBaseTokens(TokensErrData) Then ValidateBaseTokens_Error(TokensErrData)
End If
End Sub
Protected Overridable Overloads Function ValidateBaseTokens() As Boolean
Return ValidateBaseTokens(Nothing)
End Function
@@ -307,6 +329,10 @@ Namespace API.Instagram
Try
If Not r.IsEmptyString Then
ResetBaseTokens()
If ID.IsEmptyString Then
Dim __id$ = RegexReplace(r, Regex_ProfileID)
If CLng(AConvert(Of Long)(__id, 0, EDP.ReturnValue)) <> 0 Then ID = __id
End If
Select Case Attempt
Case 0
Dim rr As RParams = RParams.DM(PageTokenRegexPatternDefault, 0, RegexReturn.List, EDP.ReturnValue)

View File

@@ -39,6 +39,8 @@ Namespace API.Instagram
Private Const Name_TaggedChecked As String = "TaggedChecked"
Private Const Name_ForceUpdateUserName As String = "ForceUpdateUserName"
Private Const Name_ForceUpdateUserInfo As String = "ForceUpdateUserInfo"
Private Const Name_IsVerifiedProfile As String = "IsVerifiedProfile"
Private Const Name_IsVerifiedProfile_Checked As String = "IsVerifiedProfile_Checked"
#End Region
#Region "Declarations"
Friend Structure PostKV : Implements IEContainerProvider
@@ -115,6 +117,13 @@ Namespace API.Instagram
Private UserNameRequested As Boolean = False
Friend Property ForceUpdateUserName As Boolean = False
Friend Property ForceUpdateUserInfo As Boolean = False
Friend Property IsVerifiedProfile As Boolean = False
Friend Property IsVerifiedProfile_Checked As Boolean = False
Private ReadOnly Property PostNumberPerRequest As Integer
Get
With MySiteSettings : Return If(IsVerifiedProfile, .PostNumberVerified, .PostNumberVerifiedNot).Value : End With
End Get
End Property
#End Region
#Region "Loader"
Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean)
@@ -136,6 +145,8 @@ Namespace API.Instagram
TaggedChecked = .Value(Name_TaggedChecked).FromXML(Of Boolean)(False)
ForceUpdateUserName = .Value(Name_ForceUpdateUserName).FromXML(Of Boolean)(False)
ForceUpdateUserInfo = .Value(Name_ForceUpdateUserInfo).FromXML(Of Boolean)(False)
IsVerifiedProfile = .Value(Name_IsVerifiedProfile).FromXML(Of Boolean)(False)
IsVerifiedProfile_Checked = .Value(Name_IsVerifiedProfile_Checked).FromXML(Of Boolean)(False)
Else
.Add(Name_LastCursor, LastCursor)
.Add(Name_FirstLoadingDone, FirstLoadingDone.BoolToInteger)
@@ -153,6 +164,8 @@ Namespace API.Instagram
.Add(Name_TaggedChecked, TaggedChecked.BoolToInteger)
.Add(Name_ForceUpdateUserName, ForceUpdateUserName.BoolToInteger)
.Add(Name_ForceUpdateUserInfo, ForceUpdateUserInfo.BoolToInteger)
.Add(Name_IsVerifiedProfile, IsVerifiedProfile.BoolToInteger)
.Add(Name_IsVerifiedProfile_Checked, IsVerifiedProfile_Checked.BoolToInteger)
End If
End With
End Sub
@@ -164,6 +177,7 @@ Namespace API.Instagram
Friend Overrides Sub ExchangeOptionsSet(ByVal Obj As Object)
If Not Obj Is Nothing AndAlso TypeOf Obj Is EditorExchangeOptions Then
With DirectCast(Obj, EditorExchangeOptions)
.ApplyBase(Me)
GetTimeline = .GetTimeline
GetReels = .GetReels
GetStories = .GetStories
@@ -178,7 +192,9 @@ Namespace API.Instagram
PutImageVideoFolder = .PutImageVideoFolder
NameTrue = .UserName
IsVerifiedProfile = .IsVerifiedProfile
If IsVerifiedProfile Then IsVerifiedProfile_Checked = True
ForceUpdateUserName = .ForceUpdateUserName
ForceUpdateUserInfo = .ForceUpdateUserInfo
End With
@@ -385,6 +401,20 @@ Namespace API.Instagram
Dim s As Sections = Sections.Timeline
Dim errorFound As Boolean = False
Dim firstWait As Boolean = False
Dim __firstWait As Action = Sub()
With MySiteSettings
If Not firstWait And .ActiveSessionLastProfileRequests And CInt(.SleepTimerRequestsNextProfile.Value) <> -1 Then
Dim ____v% = 0
If CInt(.SleepTimerRequestsNextProfile.Value) = -2 Then
____v = .SleepTimerRequestsNextProfileMax
Else
____v = CInt(.SleepTimerRequestsNextProfile.Value)
End If
If ____v > 0 Then firstWait = True : Thread.Sleep(____v)
End If
End With
End Sub
Try
Err5xx = -1
ErrHandling = -1
@@ -398,9 +428,11 @@ Namespace API.Instagram
ThrowAny(Token)
HasError = False
Dim dt As Func(Of Boolean) = Function() (CBool(MySiteSettings.DownloadTimeline.Value) And GetTimeline) Or IsSavedPosts
If FirstLoadingDone Then LastCursor = String.Empty
If dt.Invoke And Not LastCursor.IsEmptyString Then
s = IIf(IsSavedPosts, Sections.SavedPosts, Sections.Timeline)
upClaimRequest.Invoke
__firstWait.Invoke
DownloadData(LastCursor, s, Token)
ProgressPre.Done()
ThrowAny(Token)
@@ -410,6 +442,7 @@ Namespace API.Instagram
s = IIf(IsSavedPosts, Sections.SavedPosts, Sections.Timeline)
upClaimRequest.Invoke
ChangeResponserMode(_UseGQL)
__firstWait.Invoke
DownloadData(String.Empty, s, Token)
ProgressPre.Done()
ThrowAny(Token)
@@ -425,6 +458,7 @@ Namespace API.Instagram
DefaultParser_ElemNode = {"node", "media"}
upClaimRequest.Invoke
ChangeResponserMode(True)
__firstWait.Invoke
DownloadData(String.Empty, s, Token)
GetReelsGQL_SetEnvir = False
ProgressPre.Done()
@@ -434,6 +468,7 @@ Namespace API.Instagram
If CBool(MySiteSettings.DownloadStories.Value) And GetStories Then
s = Sections.Stories
upClaimRequest.Invoke
__firstWait.Invoke
DownloadData(String.Empty, s, Token)
ProgressPre.Done()
End If
@@ -442,6 +477,7 @@ Namespace API.Instagram
If CBool(MySiteSettings.DownloadStoriesUser.Value) And GetStoriesUser Then
s = Sections.UserStories
upClaimRequest.Invoke
__firstWait.Invoke
DownloadData(String.Empty, s, Token)
ProgressPre.Done()
End If
@@ -450,6 +486,7 @@ Namespace API.Instagram
If CBool(MySiteSettings.DownloadTagged.Value) And GetTaggedData Then
s = Sections.Tagged
upClaimRequest.Invoke
__firstWait.Invoke
DownloadData(String.Empty, s, Token)
ProgressPre.Done()
DefaultParser_ElemNode = Nothing
@@ -504,7 +541,7 @@ Namespace API.Instagram
Protected Overrides Sub Responser_ResponseReceived(ByVal Sender As Object, ByVal e As EventArguments.WebDataResponse)
Declarations.UpdateResponser(e, Responser, WwwClaimUpdate)
End Sub
Friend Enum Sections : Timeline : Reels : Tagged : Stories : UserStories : SavedPosts : End Enum
Friend Enum Sections : Timeline : Reels : Tagged : Stories : UserStories : SavedPosts : Reposts : Likes : End Enum
Protected Const StoriesFolder As String = "Stories"
Private Const TaggedFolder As String = "Tagged"
#Region "429 bypass"
@@ -642,6 +679,7 @@ Namespace API.Instagram
Dim StoriesList As List(Of String) = Nothing
Dim StoriesRequested As Boolean = False
Dim dValue% = 1
Dim __idIsEmpty As Boolean = ID.IsEmptyString
LastCursor = Cursor
Try
Do While dValue = 1
@@ -655,7 +693,6 @@ Namespace API.Instagram
Dim HasNextPage As Boolean = False
Dim EndCursor$ = String.Empty
Dim PostID$ = String.Empty, PostDate$ = String.Empty, SpecFolder$ = String.Empty
Dim TokensErrData$ = String.Empty
Dim PostIDKV As PostKV
Dim ENode() As Object = Nothing
Dim processGetResponse As Boolean = True
@@ -663,14 +700,11 @@ Namespace API.Instagram
'Check environment
If Not IsSavedPosts Then
If ID.IsEmptyString Then GetUserData()
If _UseGQL And Cursor.IsEmptyString And Not Section = Sections.SavedPosts Then UpdateTokens(True)
If ID.IsEmptyString Or __idIsEmpty Or Not IsVerifiedProfile_Checked Then GetUserData(Token)
If ID.IsEmptyString Then UserExists = False : _ForceSaveUserInfoOnException = True : Throw New Plugin.ExitException("can't get user ID")
If _UseGQL And Cursor.IsEmptyString And Not Section = Sections.SavedPosts Then
If Not ValidateBaseTokens() Then GetPageTokens()
If Not ValidateBaseTokens(TokensErrData) Then ValidateBaseTokens_Error(TokensErrData)
End If
If ForceUpdateUserName Then GetUserNameById()
If ForceUpdateUserInfo Then GetUserData()
If ForceUpdateUserInfo Then GetUserData(Token)
End If
'Create query
@@ -682,7 +716,7 @@ Namespace API.Instagram
MySiteSettings.TooManyRequests(False)
GoTo NextPageBlock
Else
URL = $"https://www.instagram.com/api/v1/feed/user/{NameTrue}/username/?count=50" &
URL = $"https://www.instagram.com/api/v1/feed/user/{NameTrue}/username/?count={PostNumberPerRequest}" &
If(Cursor.IsEmptyString, String.Empty, $"&max_id={Cursor}")
ENode = Nothing
End If
@@ -705,7 +739,7 @@ Namespace API.Instagram
ENode = {"data", "xdt_api__v1__usertags__user_id__feed_connection"}
processGetResponse = False
Else
Dim vars$ = "{""id"":" & ID & ",""first"":50,""after"":""" & Cursor & """}"
Dim vars$ = "{""id"":" & ID & $",""first"":{PostNumberPerRequest},""after"":""" & Cursor & """}"
vars = SymbolsConverter.ASCII.EncodeSymbolsOnly(vars)
URL = $"https://www.instagram.com/graphql/query/?doc_id=17946422347485809&variables={vars}"
ENode = {"data", "user", "edge_user_to_photos_of_you"}
@@ -968,10 +1002,12 @@ NextPageBlock:
Dim PostIDKV As PostKV
Dim Pinned As Boolean
Dim PostDate$, PostOriginUrl$
Dim PostText$ = String.Empty
Dim i%, before%
Dim usePinFunc As Boolean = Not DefaultParser_Pinned Is Nothing
Dim skipPostFuncExists As Boolean = Not DefaultParser_SkipPost Is Nothing
Dim nn As EContainer
Dim textMedia As UserMedia
If SpecFolder.IsEmptyString Then
Select Case Section
Case Sections.Tagged : SpecFolder = TaggedFolder
@@ -1006,9 +1042,17 @@ NextPageBlock:
Case DateResult.Exit : If Not Pinned Then Return False
End Select
End If
If DownloadTextPosts Then PostText = DefaultParser_GetCaption(.Self)
before = _TempMediaList.Count
ObtainMedia(.Self, PostIDKV.ID, SpecFolder, PostDate,, PostOriginUrl, State, Attempts,, Section)
If Not before = _TempMediaList.Count Then _TotalPostsParsed += 1
ObtainMedia(.Self, PostIDKV.ID, SpecFolder, PostDate,, PostOriginUrl, State, Attempts,, Section, PostText)
If Not before = _TempMediaList.Count Then
_TotalPostsParsed += 1
ElseIf DownloadTextPosts And DownloadText And Not PostText.IsEmptyString Then
textMedia = MediaFromData(UTypes.Text, PostIDKV.ID, PostIDKV.ID, PostDate, SpecFolder, PostOriginUrl, State, Attempts, PostText)
textMedia.URL = PostIDKV.ID
textMedia.PostTextFile = $"{PostIDKV.ID}.txt"
_TempMediaList.ListAddValue(textMedia, LNC)
End If
If _Limit > 0 And _TotalPostsParsed >= _Limit Then Return False
End If
Else
@@ -1021,6 +1065,9 @@ NextPageBlock:
Return False
End If
End Function
Protected Overridable Function DefaultParser_GetCaption(ByVal e As EContainer) As String
Return e.Value({"caption"}, "text")
End Function
#End Region
#Region "Code ID converters"
Protected Function CodeToID(ByVal Code As String) As String
@@ -1069,7 +1116,8 @@ NextPageBlock:
Optional ByVal PostOriginUrl As String = Nothing,
Optional ByVal State As UStates = UStates.Unknown, Optional ByVal Attempts As Integer = 0,
Optional ByVal TryExtractImage As Boolean = False,
Optional ByVal Section As Sections = ObtainMedia_NoSection)
Optional ByVal Section As Sections = ObtainMedia_NoSection,
Optional ByVal Text As String = Nothing)
Try
Dim maxSize As Func(Of EContainer, Integer) = Function(ByVal _ss As EContainer) As Integer
Dim w% = AConvert(Of Integer)(_ss.Value("width"), 0)
@@ -1104,6 +1152,7 @@ NextPageBlock:
End Function
If Not ObtainMedia_SizeFuncVid Is Nothing Then ssVid = ObtainMedia_SizeFuncVid
If Not ObtainMedia_SizeFuncPic Is Nothing Then ssPic = ObtainMedia_SizeFuncPic
If DownloadTextPosts And Text.IsEmptyString Then Text = DefaultParser_GetCaption(n)
If n.Count > 0 Then
Dim l As New List(Of Sizes)
Dim d As EContainer
@@ -1116,12 +1165,30 @@ NextPageBlock:
If TryExtractImage Then
t = 1
abstractDecision = True
If Not SpecialFolder.IsEmptyString AndAlso PutImageVideoFolder Then
Dim endsAbs As Boolean = SpecialFolder.EndsWith("*")
If endsAbs Then SpecialFolder = SpecialFolder.TrimEnd("*")
If Not SpecialFolder.IsEmptyString Then SpecialFolder = $"{SpecialFolder.TrimEnd("\")}\{VideoFolderName}{IIf(Not endsAbs, "*", String.Empty)}"
If endsAbs Then SpecialFolder &= "*"
Dim endsAbs As Boolean
Dim newFolderName$
If PutImageVideoFolder Then
If SpecialFolder.IsEmptyString Then
newFolderName = $"{VideoFolderName}\*"
Else
endsAbs = SpecialFolder.EndsWith("*")
SpecialFolder = SpecialFolder.TrimEnd({CChar("\"), CChar("*")})
If Not endsAbs Then SpecialFolder = $"{SpecialFolder}\{VideoFolderName}"
newFolderName = $"{SpecialFolder}*"
End If
'Dim endsAbs As Boolean = SpecialFolder.EndsWith("*")
'If endsAbs Then SpecialFolder = SpecialFolder.TrimEnd("*")
'If Not SpecialFolder.IsEmptyString Then SpecialFolder = $"{SpecialFolder.TrimEnd("\")}\{VideoFolderName}{IIf(Not endsAbs, "*", String.Empty)}"
'If endsAbs Then SpecialFolder &= "*"
ElseIf Not SpecialFolder.IsEmptyString Then
endsAbs = SpecialFolder.EndsWith("*")
SpecialFolder = SpecialFolder.TrimEnd({CChar("\"), CChar("*")})
If endsAbs Then SpecialFolder = $"{SpecialFolder}\Photos"
newFolderName = $"{SpecialFolder}*"
Else
newFolderName = SpecialFolder
End If
SpecialFolder = newFolderName
ElseIf t = -1 And InitialType = 8 And ObtainMedia_AllowAbstract Then
If n.Contains(vid) Then
t = 2
@@ -1145,7 +1212,7 @@ NextPageBlock:
If l.Count > 0 Then l.RemoveAll(wrongData)
If l.Count > 0 Then
l.Sort()
_TempMediaList.ListAddValue(MediaFromData(UTypes.Picture, l.First.Data, PostID, DateObj, SpecialFolder, PostOriginUrl, State, Attempts), LNC)
_TempMediaList.ListAddValue(MediaFromData(UTypes.Picture, l.First.Data, PostID, DateObj, SpecialFolder, PostOriginUrl, State, Attempts, Text), LNC)
l.Clear()
End If
End If
@@ -1162,19 +1229,19 @@ NextPageBlock:
If l.Count > 0 Then l.RemoveAll(wrongData)
If l.Count > 0 Then
l.Sort()
_TempMediaList.ListAddValue(MediaFromData(UTypes.Video, l.First.Data, PostID, DateObj, SpecialFolder, PostOriginUrl, State, Attempts), LNC)
_TempMediaList.ListAddValue(MediaFromData(UTypes.Video, l.First.Data, PostID, DateObj, SpecialFolder, PostOriginUrl, State, Attempts, Text), LNC)
l.Clear()
End If
End If
End With
End If
If Not TryExtractImage And Not Section = ObtainMedia_NoSection And ExtractImageFrom(Section) Then _
ObtainMedia(n, PostID, SpecialFolder, DateObj, InitialType, PostOriginUrl, State, Attempts, True, Section)
ObtainMedia(n, PostID, SpecialFolder, DateObj, InitialType, PostOriginUrl, State, Attempts, True, Section, Text)
Case 8 'gallery
DateObj = mDate(n)
With n("carousel_media").XmlIfNothing
If .Count > 0 Then
For Each d In .Self : ObtainMedia(d, PostID, SpecialFolder, DateObj, 8, PostOriginUrl) : Next
For Each d In .Self : ObtainMedia(d, PostID, SpecialFolder, DateObj, 8, PostOriginUrl,,,,, Text) : Next
End If
End With
End Select
@@ -1187,26 +1254,37 @@ NextPageBlock:
End Sub
#End Region
#Region "GetUserId, GetUserName"
Private Sub GetUserData()
Private Sub GetUserData(ByVal Token As CancellationToken)
Dim __idFound As Boolean = False
If ForceUpdateUserInfo Then ForceUpdateUserInfo = False : _ForceSaveUserInfo = True
Try
Dim r$
Dim ____dataGql As Boolean = _UseGQL Or CBool(MySiteSettings.USE_GQL_UserData.Value)
If ____dataGql Then
UpdateTokens(True)
r = GetUserDataGQL(Token)
If Not _UseGQL Then ChangeResponserMode(_UseGQL)
Else
ChangeResponserMode(False)
UpdateRequestNumber()
Dim r$ = Responser.GetResponse($"https://i.instagram.com/api/v1/users/web_profile_info/?username={NameTrue}")
r = Responser.GetResponse($"https://i.instagram.com/api/v1/users/web_profile_info/?username={NameTrue}")
End If
If Not r.IsEmptyString Then
Using j As EContainer = JsonDocument.Parse(r)
If Not j Is Nothing AndAlso j.Contains({"data", "user"}) Then
With j({"data", "user"})
ID = .Value("id")
_ForceSaveUserData = True
If Not ____dataGql Or ID.IsEmptyString Then ID = .Value("id")
__idFound = True
UserSiteNameUpdate(.Value("full_name"))
IsVerifiedProfile = .Value("is_verified").FromXML(Of Boolean)(False)
IsVerifiedProfile_Checked = True
Dim descr$ = .Value("biography")
If If(.Item("bio_links")?.Count, 0) > 0 Then descr.StringAppend(.Item("bio_links").Select(Function(bl) bl.Value("url")).ListToString(vbNewLine), vbNewLine)
Dim eUrl$ = .Value("external_url")
If Not eUrl.IsEmptyString AndAlso (descr.IsEmptyString OrElse Not descr.Contains(eUrl)) Then descr.StringAppendLine(eUrl)
UserDescriptionUpdate(descr)
Dim f As New SFile With {.Path = DownloadContentDefault_GetRootDir(), .Name = "ProfilePicture", .Extension = "jpg"}
f = SFile.IndexReindex(f)
If Not f.Exists Then
@@ -1381,7 +1459,7 @@ NextPageBlock:
MyMainLOG = $"Number of requests before error 429: {RequestsCount}"
Return 1
ElseIf Responser.StatusCode = 560 Or Responser.StatusCode = HttpStatusCode.InternalServerError Then '560, 500
If Responser.StatusCode = 560 And s = Sections.Stories And MySiteSettings.IgnoreStoriesDownloadingErrors Then
If Responser.StatusCode = 560 And s = Sections.Stories And MySiteSettings.IgnoreStoriesDownloadingErrors.Value Then
MyMainLOG = $"{ToStringForLog()}: Stories downloading skipped (560)"
Return ErrHandlingValueStories
Else
@@ -1438,13 +1516,16 @@ NextPageBlock:
#Region "Create media"
Private Function MediaFromData(ByVal t As UTypes, ByVal _URL As String, ByVal PostID As String, ByVal PostDate As String,
Optional ByVal SpecialFolder As String = Nothing, Optional ByVal PostOriginUrl As String = Nothing,
Optional ByVal State As UStates = UStates.Unknown, Optional ByVal Attempts As Integer = 0) As UserMedia
Optional ByVal State As UStates = UStates.Unknown, Optional ByVal Attempts As Integer = 0,
Optional ByVal Text As String = Nothing) As UserMedia
_URL = LinkFormatterSecure(RegexReplace(_URL.Replace("\", String.Empty), LinkPattern))
Dim m As New UserMedia(_URL, t) With {.URL_BASE = PostOriginUrl.IfNullOrEmpty(_URL), .Post = New UserPost With {.ID = PostID}}
If Not m.URL.IsEmptyString Then m.File = CStr(RegexReplace(m.URL, FilesPattern))
If Not PostDate.IsEmptyString Then m.Post.Date = AConvert(Of Date)(PostDate, UnixDate32Provider, Nothing) Else m.Post.Date = Nothing
m.SpecialFolder = SpecialFolder
If State = UStates.Missing Then m.State = UStates.Missing : m.Attempts = Attempts
m.PostText = Text
m.PostTextFileSpecialFolder = DownloadTextSpecialFolder
Return m
End Function
#End Region

View File

@@ -34,6 +34,9 @@ Namespace API.JustForFans
Case NameOf(UserAgent) : If Not HeaderValue.IsEmptyString Then Responser.UserAgent = HeaderValue
End Select
End Sub
<DoNotUse> Friend Overrides Property DownloadText As PropertyValue
<DoNotUse> Friend Overrides Property DownloadTextPosts As PropertyValue
<DoNotUse> Friend Overrides Property DownloadTextSpecialFolder As PropertyValue
Friend Sub New(ByVal AccName As String, ByVal Temp As Boolean)
MyBase.New("JustForFans", "justfor.fans", AccName, Temp, My.Resources.SiteResources.JFFIcon_64, My.Resources.SiteResources.JFFPic_76)

View File

@@ -12,6 +12,9 @@ Imports PersonalUtilities.Functions.RegularExpressions
Namespace API.LPSG
<Manifest("AndyProgram_LPSG")>
Friend Class SiteSettings : Inherits Base.SiteSettingsBase
<DoNotUse> Friend Overrides Property DownloadText As PropertyValue
<DoNotUse> Friend Overrides Property DownloadTextPosts As PropertyValue
<DoNotUse> Friend Overrides Property DownloadTextSpecialFolder As PropertyValue
Friend Sub New(ByVal AccName As String, ByVal Temp As Boolean)
MyBase.New("LPSG", "www.lpsg.com", AccName, Temp, My.Resources.SiteResources.LPSGIcon_48, My.Resources.SiteResources.LPSGPic_32)
UrlPatternUser = "https://www.lpsg.com/threads/{0}/"

View File

@@ -76,6 +76,9 @@ Namespace API.Mastodon
<PropertyOption(IsAuth:=False, ControlText:="User related to my domain",
ControlToolTip:="Open user profiles and user posts through my domain."), PXML, PClonable>
Friend ReadOnly Property UserRelatedToMyDomain As PropertyValue
<DoNotUse> Friend Overrides Property DownloadText As PropertyValue
<DoNotUse> Friend Overrides Property DownloadTextPosts As PropertyValue
<DoNotUse> Friend Overrides Property DownloadTextSpecialFolder As PropertyValue
#End Region
#End Region
#Region "Initializer"

View File

@@ -3,5 +3,4 @@ https://github.com/riley-access-labs/onlyfans-dynamic-rules-1/blob/main/dynamicR
https://github.com/riley-access-labs/onlyfans-dynamic-rules-1/blob/patch-1/dynamicRules.json
https://github.com/DATAHOARDERS/dynamic-rules/blob/main/onlyfans.json
https://github.com/DIGITALCRIMINAL/dynamic-rules/blob/main/onlyfans.json
https://github.com/deviint/onlyfans-dynamic-rules/blob/main/dynamicRules.json
https://github.com/rafa-9/dynamic-rules/blob/main/rules.json

View File

@@ -4,8 +4,8 @@ https://github.com/riley-access-labs/onlyfans-dynamic-rules-1/blob/main/dynamicR
https://github.com/riley-access-labs/onlyfans-dynamic-rules-1/blob/patch-1/dynamicRules.json
https://github.com/DATAHOARDERS/dynamic-rules/blob/main/onlyfans.json
https://github.com/DIGITALCRIMINAL/dynamic-rules/blob/main/onlyfans.json
https://github.com/deviint/onlyfans-dynamic-rules/blob/main/dynamicRules.json
https://github.com/rafa-9/dynamic-rules/blob/main/rules.json
https://github.com/SneakyOvis/onlyfans-dynamic-rules/blob/main/rules.json
https://github.com/Growik/onlyfans-dynamic-rules/blob/main/rules.json
https://github.com/deviint/onlyfans-dynamic-rules/blob/main/dynamicRules.json

View File

@@ -219,12 +219,12 @@ Namespace API.OnlyFans
DynamicRulesXml.Extension = "xml"
ReplacePattern_RepoToRaw = New RParams("(.*github.com/([^/]+)/([^/]+)/blob/(.+))", Nothing, 0,
RegexReturn.ReplaceChangeListMatch, EDP.ReturnValue) With {
.PatternReplacement = "https://raw.githubusercontent.com/{2}/{3}/{4}"}
.PatternReplacement = "https://raw.githubusercontent.com/{2}/{3}/refs/heads/{4}"}
ReplacePattern_JsonInfo = ReplacePattern_RepoToRaw.Copy
ReplacePattern_JsonInfo.PatternReplacement = "https://github.com/{2}/{3}/latest-commit/{4}"
ReplacePattern_RawToRepo = ReplacePattern_RepoToRaw.Copy
ReplacePattern_RawToRepo.Pattern = "(.*raw.githubusercontent.com/([^/]+)/([^/]+)/([^/]+)/(.+))"
ReplacePattern_RawToRepo.PatternReplacement = "https://github.com/{2}/{3}/blob/{4}/{5}"
ReplacePattern_RawToRepo.Pattern = "(.*raw.githubusercontent.com/([^/]+)/([^/]+)(/refs/heads)?/([^/]+)/(.+))"
ReplacePattern_RawToRepo.PatternReplacement = "https://github.com/{2}/{3}/blob/{5}/{6}"
ConfigRulesExtract = RParams.DMS("DYNAMIC_RULE"":(\{.+?\}[\r\n]+)", 1, RegexOptions.Singleline, EDP.ReturnValue)
OFLOG = New TextSaver($"LOGs\OF_{Now:yyyyMMdd_HHmmss}.txt") With {.LogMode = True, .AutoSave = True, .AutoClear = True}
AddHandler OFLOG.TextSaved, AddressOf OFLOG_TextSaved

View File

@@ -37,6 +37,8 @@ Namespace API.OnlyFans
Friend Const HeaderXBC As String = "X-Bc"
Friend Const HeaderAppToken As String = "App-Token"
Private Const AppTokenDefault As String = "33d57ade8c02dbc5a333db99ff9ae26a"
Private Const BackendDefault As String = "aio"
Private Const Backendhttpx As String = "httpx"
<PropertyOption(ControlText:=HeaderUserID, AllowNull:=False, IsAuth:=True), PClonable(Clone:=False)>
Friend ReadOnly Property HH_USER_ID As PropertyValue
<PropertyOption(ControlText:=HeaderXBC, AllowNull:=False, IsAuth:=True), PClonable(Clone:=False)>
@@ -97,6 +99,19 @@ Namespace API.OnlyFans
End Get
End Property
#End Region
#Region "Other"
<PClonable, PXML("OpenPostsUsingID")> Private ReadOnly Property OpenPostsUsingID_XML As PropertyValue
<PropertyOption(ControlText:="Open posts using ID", ControlToolTip:="Open posts using the user ID instead of the user name"), HiddenControl>
Private ReadOnly Property OpenPostsUsingID As PropertyValue
Get
If Not DefaultInstance Is Nothing Then
Return DirectCast(DefaultInstance, SiteSettings).OpenPostsUsingID_XML
Else
Return OpenPostsUsingID_XML
End If
End Get
End Property
#End Region
#Region "OFScraper"
<PClonable, PXML("OFScraperPath")> Private ReadOnly Property OFScraperPath_XML As PropertyValue
<PropertyOption(ControlText:="OF-Scraper path", ControlToolTip:="The path to the 'ofscraper.exe'", Category:=CAT_OFS)>
@@ -199,6 +214,24 @@ Namespace API.OnlyFans
End If
Return False
End Function
<PClonable, PXML("OFS_BACKEND")> Private ReadOnly Property OFS_BACKEND_XML As PropertyValue
<PropertyOption(ControlText:="backend", ControlToolTip:="The value of 'advanced_options' in the configuration" & vbCr &
"If you can't download the video, try using 'httpx'", AllowNull:=False, Category:=CAT_OFS)>
Friend ReadOnly Property OFS_BACKEND As PropertyValue
Get
If Not DefaultInstance Is Nothing Then
Return DirectCast(DefaultInstance, SiteSettings).OFS_BACKEND_XML
Else
Return OFS_BACKEND_XML
End If
End Get
End Property
<PropertyUpdater(NameOf(OFS_BACKEND))>
Private Function OFS_BACKEND_Update() As Boolean
DirectCast(If(DefaultInstance, Me), SiteSettings).OFS_BACKEND_XML.Value =
CStr(IIf(MsgBoxE({"Select a value for the 'backend' option", "'backend' value"}, vbQuestion,,, {BackendDefault, Backendhttpx}) = 0, BackendDefault, Backendhttpx))
Return True
End Function
#End Region
#End Region
#Region "Initializer"
@@ -261,9 +294,12 @@ Namespace API.OnlyFans
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))
OFS_BACKEND_XML = New PropertyValue(BackendDefault)
UpdateRules401_XML = New PropertyValue(False)
OpenPostsUsingID_XML = New PropertyValue(True)
UserRegex = RParams.DMS(String.Format(UserRegexDefaultPattern, "onlyfans.com/"), 1, EDP.ReturnValue)
UrlPatternUser = "https://onlyfans.com/{0}"
ImageVideoContains = "onlyfans.com"
@@ -342,8 +378,10 @@ Namespace API.OnlyFans
End If
If p.IsEmptyString Then
Return GetUserUrl(User)
ElseIf CBool(OpenPostsUsingID.Value) Then
Return String.Format(UserPostPattern, p, If(User.ID.IsEmptyString, User.NameTrue, $"u{User.ID}"))
Else
Return String.Format(UserPostPattern, p, If(User.ID.IsEmptyString, User.Name, $"u{User.ID}"))
Return String.Format(UserPostPattern, p, User.NameTrue)
End If
Else
Return String.Empty

View File

@@ -66,6 +66,7 @@ Namespace API.OnlyFans
Friend Overrides Sub ExchangeOptionsSet(ByVal Obj As Object)
If Not Obj Is Nothing AndAlso TypeOf Obj Is UserExchangeOptions Then
With DirectCast(Obj, UserExchangeOptions)
.ApplyBase(Me)
MediaDownloadTimeline = .DownloadTimeline
MediaDownloadStories = .DownloadStories
MediaDownloadHighlights = .DownloadHighlights
@@ -84,8 +85,10 @@ Namespace API.OnlyFans
Private _OFScraperExists As Boolean = False
Private OFSCache As CacheKeeper = Nothing
Private _AbsMediaIndex As Integer = 0
Private _DownloadedPostsSession As Integer = 0
Private FunctionErr As Integer = FunctionErrDef
Private Const FunctionErrDef As Integer = -100
Private _TimelineDownloading As Boolean = False
Private Sub ValidateOFScraper()
_OFScraperExists = ACheck(MySettings.OFScraperPath.Value) AndAlso CStr(MySettings.OFScraperPath.Value).CSFile.Exists
End Sub
@@ -95,6 +98,7 @@ Namespace API.OnlyFans
If Not MySettings.SessionAborted Then
ValidateOFScraper()
_AbsMediaIndex = 0
_DownloadedPostsSession = 0
FunctionErr = FunctionErrDef
If Not CCookie Is Nothing Then CCookie.Dispose()
CCookie = Responser.Cookies.Copy
@@ -107,12 +111,15 @@ Namespace API.OnlyFans
If ID.IsEmptyString Then Throw New ArgumentNullException("ID", "Unable to get user ID")
End If
_TimelineDownloading = True
If MediaDownloadTimeline Then DownloadTimeline(IIf(IsSavedPosts, 0, String.Empty), Token)
_TimelineDownloading = False
If Not IsSavedPosts Then
If MediaDownloadStories And FunctionErr = FunctionErrDef Then DownloadStories(Token)
If MediaDownloadHighlights And FunctionErr = FunctionErrDef Then DownloadHighlights(Token)
If MediaDownloadChatMedia And FunctionErr = FunctionErrDef Then DownloadChatMedia(0, Token)
End If
If _TempMediaList.Count > 0 And Not _NameUpdated Then GetUserID(True)
End If
Finally
Responser_ResponseReceived_RemoveHandler()
@@ -144,7 +151,7 @@ Namespace API.OnlyFans
Dim tmpCursor$ = String.Empty
Dim hasMore As Boolean = False
Dim path$ = String.Empty
Dim postDate$, postID$
Dim postDate$, postID$, txt$
Dim n As EContainer
Dim mediaList As List(Of UserMedia)
Dim mediaResult As Boolean
@@ -189,9 +196,21 @@ Namespace API.OnlyFans
Case DateResult.Exit : Exit Sub
End Select
txt = n.Value("text")
mediaResult = False
mediaList = TryCreateMedia(n, postID, postDate, mediaResult)
If mediaResult Then _TempMediaList.ListAddList(mediaList, LNC)
mediaList = TryCreateMedia(n, postID, postDate, mediaResult,,,,, txt)
If mediaResult Then
_TempMediaList.ListAddList(mediaList, LNC)
_DownloadedPostsSession += 1
ElseIf Not txt.IsEmptyString Then
_TempMediaList.ListAddValue(New UserMedia(postID, UTypes.Text) With {
.Post = New UserPost(postID, AConvert(Of Date)(postDate, DateProvider, Nothing)),
.PostText = txt,
.PostTextFileSpecialFolder = DownloadTextSpecialFolder,
.PostTextFile = $"{postID}.txt"
}, LNC)
End If
Next
Else
hasMore = False
@@ -202,7 +221,10 @@ Namespace API.OnlyFans
End If
End If
If hasMore Then
If DownloadTopCount.HasValue AndAlso DownloadTopCount.Value <= _DownloadedPostsSession Then
_complete = True
Exit Sub
ElseIf hasMore Then
If IsSavedPosts Then tmpCursor = CInt(Cursor.IfNullOrEmpty(0)) + 10
DownloadTimeline(tmpCursor, Token)
End If
@@ -405,14 +427,14 @@ Namespace API.OnlyFans
Private Function TryCreateMedia(ByVal n As EContainer, ByVal PostID As String, Optional ByVal PostDate As String = Nothing,
Optional ByRef Result As Boolean = False, Optional ByVal IsHL As Boolean = False,
Optional ByVal SpecFolder As String = Nothing, Optional ByVal PostUserID As String = Nothing,
Optional ByVal TryUseOFS As Boolean = True) As List(Of UserMedia)
Optional ByVal TryUseOFS As Boolean = True, Optional ByVal PostText As String = Nothing) As List(Of UserMedia)
Dim postUrl$, postUrlBase$, ext$
Dim t As UTypes
Dim mList As New List(Of UserMedia)
Result = False
With n("media")
If .ListExists Then
For Each m In .Self
For Each m As EContainer In .Self
postUrl = GetMediaURL(m)
'If IsHL Then
' 'postUrl = m.Value({"files", "source"}, "url")
@@ -421,10 +443,11 @@ Namespace API.OnlyFans
' 'postUrl = m.Value({"source"}, "source").IfNullOrEmpty(m.Value("full"))
' postUrl = GetMediaURL(m)
'End If
If m.Value("canView").FromXML(Of Boolean)(True) Then
postUrlBase = String.Empty
Select Case m.Value("type")
Case "photo" : t = UTypes.Picture : ext = "jpg"
Case "video"
Case "video", "gif"
t = UTypes.Video
ext = "mp4"
If postUrl.IsEmptyString And Not IsHL And TryUseOFS Then
@@ -438,7 +461,9 @@ Namespace API.OnlyFans
If Not t = UTypes.Undefined And (Not postUrl.IsEmptyString Or t = UTypes.VideoPre) Then
Dim media As New UserMedia(postUrl.IfNullOrEmpty(IIf(t = UTypes.VideoPre, $"{t}{_AbsMediaIndex}", String.Empty)), t) With {
.Post = New UserPost(PostID, AConvert(Of Date)(PostDate, DateProvider, Nothing)),
.SpecialFolder = SpecFolder
.SpecialFolder = SpecFolder,
.PostText = PostText,
.PostTextFileSpecialFolder = DownloadTextSpecialFolder
}
If postUrlBase.IsEmptyString And Not IsSingleObjectDownload Then postUrlBase = GetPostUrl(Me, media)
If Not postUrlBase.IsEmptyString Then media.URL_BASE = postUrlBase
@@ -446,23 +471,27 @@ Namespace API.OnlyFans
Result = True
mList.Add(media)
End If
End If
Next
End If
End With
Return mList
End Function
Private Sub GetUserID()
Private _NameUpdated As Boolean = False
Private Sub GetUserID(Optional ByVal UpdateNameOnly As Boolean = False)
Const brTag$ = "<br />"
Dim path$ = $"/api2/v2/users/{Name}"
Dim path$ = $"/api2/v2/users/{IIf(UpdateNameOnly, $"u{ID}", Name)}"
Dim url$ = String.Format(BaseUrlPattern, path)
Try
If ID.IsEmptyString AndAlso UpdateSignature(path) Then
If (ID.IsEmptyString Or UpdateNameOnly) AndAlso UpdateSignature(path) Then
Dim r$ = Responser.GetResponse(url)
If Not r.IsEmptyString Then
Using j As EContainer = JsonDocument.Parse(r)
If j.ListExists Then
NameTrue = j.Value("username")
_NameUpdated = True
If UpdateNameOnly Then Exit Sub
ID = j.Value("id")
If Not ID.IsEmptyString Then _ForceSaveUserInfo = True
UserSiteNameUpdate(j.Value("name"))
Dim descr$ = j.Value("about")
If Not descr.IsEmptyString Then descr = descr.Replace(brTag, String.Empty)
@@ -600,6 +629,7 @@ Namespace API.OnlyFans
Const requestPattern$ = """{0}"" manual --config ""{1}"" --url {2}"
Dim conf As SFile = OFS_CreateConfig()
If conf.Exists Then
If Not _NameUpdated Then GetUserID(True)
Dim command$ = String.Format(requestPattern, MySettings.OFScraperPath.Value, conf, URL)
'#If DEBUG Then
'Debug.WriteLine(command)
@@ -657,6 +687,7 @@ Namespace API.OnlyFans
updateConf("key-mode-default", CStr(MySettings.KeyModeDefault.Value).IfNullOrEmpty(SiteSettings.KeyModeDefault_Default), m1)
updateConf("keydb_api", CStr(MySettings.Keydb_Api.Value), m1)
updateConf("backend", CStr(MySettings.OFS_BACKEND.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)
@@ -730,6 +761,7 @@ Namespace API.OnlyFans
If IsSingleObjectDownload Then
URL = Media.URL_BASE
Else
If Not _NameUpdated Then GetUserID(True)
URL = GetPostUrl(Me, Media)
End If
If Not URL.IsEmptyString Then
@@ -798,6 +830,8 @@ Namespace API.OnlyFans
Return 3
ElseIf Responser.StatusCode = Net.HttpStatusCode.InternalServerError Then '500
Return 3
ElseIf Not _TimelineDownloading And Responser.StatusCode = Net.HttpStatusCode.BadGateway Then '502
Return 3
Else
Return 0
End If

View File

@@ -8,7 +8,7 @@
' but WITHOUT ANY WARRANTY
Imports SCrawler.Plugin.Attributes
Namespace API.OnlyFans
Friend Class UserExchangeOptions
Friend Class UserExchangeOptions : Inherits Base.EditorExchangeOptionsBase
<PSetting(NameOf(SiteSettings.DownloadTimeline), NameOf(MySettings))>
Friend Property DownloadTimeline As Boolean
<PSetting(NameOf(SiteSettings.DownloadStories), NameOf(MySettings))>
@@ -18,7 +18,12 @@ Namespace API.OnlyFans
<PSetting(NameOf(SiteSettings.DownloadChatMedia), NameOf(MySettings))>
Friend Property DownloadChatMedia As Boolean
Private ReadOnly MySettings As SiteSettings
<PSetting(Address:=SettingAddress.None)> Friend Overrides Property UserName As String
Private Sub New()
End Sub
Friend Sub New(ByVal u As UserData)
MyBase.New(u)
_ApplyBase_Name = False
DownloadTimeline = u.MediaDownloadTimeline
DownloadStories = u.MediaDownloadStories
DownloadHighlights = u.MediaDownloadHighlights
@@ -26,6 +31,8 @@ Namespace API.OnlyFans
MySettings = u.HOST.Source
End Sub
Friend Sub New(ByVal s As SiteSettings)
MyBase.New(s)
_ApplyBase_Name = False
DownloadTimeline = s.DownloadTimeline.Value
DownloadStories = s.DownloadStories.Value
DownloadHighlights = s.DownloadHighlights.Value

View File

@@ -21,6 +21,9 @@ Namespace API.Pinterest
Private ReadOnly Property MyConcurrentDownloadsProvider As IFormatProvider
<PropertyOption(ControlText:=DeclaredNames.SavedPostsUserNameCaption, ControlToolTip:=DeclaredNames.SavedPostsUserNameToolTip), PXML, PClonable(Clone:=False)>
Friend ReadOnly Property SavedPostsUserName As PropertyValue
<DoNotUse> Friend Overrides Property DownloadText As PropertyValue
<DoNotUse> Friend Overrides Property DownloadTextPosts As PropertyValue
<DoNotUse> Friend Overrides Property DownloadTextSpecialFolder As PropertyValue
#End Region
#Region "Initializer"
Friend Sub New(ByVal AccName As String, ByVal Temp As Boolean)

View File

@@ -20,7 +20,7 @@ Namespace API.PornHub
Friend ReadOnly RegexVideo_FlashVars_Vars As RParams = RParams.DM("var ([\w\d]{10,})=("".+?)(?=(;|\Z))", 0, RegexReturn.List)
Friend ReadOnly RegexVideo_FlashVars_Compiler As RParams = RParams.DM("(?<=\*/)([\w\d\S]{10,})", 0, RegexReturn.List)
Friend ReadOnly RegexVideo_FlashVars_UrlResolution As RParams = RParams.DMS("/(\d+)[^/]+\.mp4", 1, EDP.ReturnValue)
Friend ReadOnly RegexUserVideos As RParams = RParams.DM("(\<li class=""pcVideoListItem)((?:(?!/li\>).)*?)(\<div.class=.private-vid-title((?:(?!/li\>).)*?)|)(\<a.href=.([^""]+?)"".title=.([^""]*?)"")(((?:(?!/li\>).)+?)(\<div class=.videoUploaderBlock)|)((?:(?!/li\>).)*?)(\</li\>)",
Friend ReadOnly RegexUserVideos As RParams = RParams.DM("(\<li class=""pcVideoListItem)((?:(?!/li\>).)*?)(\<div.class=.private-vid-title((?:(?!/li\>).)*?)|)(\<a.href=.([^""]+?)"".title=.([^""]*?)"")(((?:(?!/li\>).)+?)(\<div class=.videoUploaderBlock.*?href=""([^""]+)"")|)((?:(?!/li\>).)*?)(\</li\>)",
0, RegexOptions.Singleline, RegexReturn.List, EDP.ReturnValue, UnicodeHexConverter)
Friend ReadOnly RegexVideo_Video_VideoKey As RParams = RParams.DMS("viewkey=([\w\d]+)", 1, EDP.ReturnValue)
Friend ReadOnly RegexVideoPageTitle As RParams = RParams.DMS("meta (property|name)=""[^:]+?:title"" content=""([^""]+)""", 2, EDP.ReturnValue)

View File

@@ -31,6 +31,9 @@ Namespace API.PornHub
Friend ReadOnly Property DownloadGifsAsMp4 As PropertyValue
<PropertyOption(ControlText:=DeclaredNames.SavedPostsUserNameCaption, ControlToolTip:=DeclaredNames.SavedPostsUserNameToolTip), PXML, PClonable(Clone:=False)>
Friend ReadOnly Property SavedPostsUserName As PropertyValue
<DoNotUse> Friend Overrides Property DownloadText As PropertyValue
<DoNotUse> Friend Overrides Property DownloadTextPosts As PropertyValue
<DoNotUse> Friend Overrides Property DownloadTextSpecialFolder As PropertyValue
#End Region
#Region "Initializer"
Friend Sub New(ByVal AccName As String, ByVal Temp As Boolean)

View File

@@ -15,7 +15,7 @@ Imports PersonalUtilities.Tools.Web.Clients
Imports PersonalUtilities.Tools.Web.Documents.JSON
Imports UTypes = SCrawler.API.Base.UserMedia.Types
Namespace API.PornHub
Friend Class UserData : Inherits UserDataBase
Friend Class UserData : Inherits UserDataBase : Implements IPSite
Private Const UrlPattern As String = "https://www.pornhub.com/{0}"
#Region "Declarations"
#Region "XML names"
@@ -50,6 +50,7 @@ Namespace API.PornHub
Friend URL As String
Friend ID As String
Friend Title As String
Friend UserRef As String
Friend Type As VideoTypes
Friend Function ToUserMedia(Optional ByVal SpecialFolder As String = Nothing) As UserMedia
Return New UserMedia(URL, UTypes.VideoPre) With {
@@ -66,14 +67,16 @@ Namespace API.PornHub
URL = String.Empty
Else
URL = String.Format(UrlPattern, URL.TrimStart("/"))
Title = TitleHtmlConverter(ParamsArray(1))
If Not ParamsArray(2).IsEmptyString Then
Type = VideoTypes.Private
ElseIf Not ParamsArray(3).IsEmptyString Then
Type = VideoTypes.Tagged
'ElseIf Not ParamsArray(3).IsEmptyString Then
' Type = VideoTypes.Tagged
Else
Type = VideoTypes.Uploaded
End If
If Not ParamsArray(3).IsEmptyString Then UserRef = ParamsArray(3).StringTrim
End If
End If
Return Me
@@ -140,7 +143,7 @@ Namespace API.PornHub
End Get
End Property
Friend Property SiteMode As SiteModes = SiteModes.User
Friend Property QueryString As String
Friend Property QueryString As String Implements IPSite.QueryString
Get
If IsUser Then
Return String.Empty
@@ -163,17 +166,7 @@ Namespace API.PornHub
Return New UserExchangeOptions(Me)
End Function
Friend Overrides Sub ExchangeOptionsSet(ByVal Obj As Object)
If Not Obj Is Nothing AndAlso TypeOf Obj Is UserExchangeOptions Then
With DirectCast(Obj, UserExchangeOptions)
DownloadUHD = .DownloadUHD
DownloadUploaded = .DownloadUploaded
DownloadTagged = .DownloadTagged
DownloadPrivate = .DownloadPrivate
DownloadFavorite = .DownloadFavorite
DownloadGifs = .DownloadGifs
QueryString = .QueryString
End With
End If
If Not Obj Is Nothing AndAlso TypeOf Obj Is UserExchangeOptions Then DirectCast(Obj, UserExchangeOptions).Apply(Me)
End Sub
#End Region
Private ReadOnly Property MySettings As SiteSettings
@@ -348,10 +341,13 @@ Namespace API.PornHub
Dim tryNextPage As Boolean = False
Dim limit% = If(DownloadTopCount, -1)
Dim cBefore% = _TempMediaList.Count
Dim usrRef$ = String.Empty
Dim npd$ = "?"
If IsUser Then
URL = $"https://www.pornhub.com/{PersonType}/{NameTrue}"
usrRef = $"/{PersonType}/{NameTrue}"
If Type = VideoTypes.Uploaded Then
URL &= "/videos/upload"
If Not PersonType = PersonTypeCannel Then URL &= "/videos/upload?o=mr" : npd = "&"
ElseIf Type = VideoTypes.Tagged Then
If Not SecondMode Then URL &= "/videos"
specFolder = "Tagged"
@@ -364,7 +360,7 @@ Namespace API.PornHub
Else
Throw New ArgumentException($"Type '{Type}' is not implemented in the video download function", "Type")
End If
If Page > 1 Then URL &= $"?page={Page}"
If Page > 1 Then URL &= $"{npd}page={Page}"
ElseIf SiteMode = SiteModes.Playlists Then
If PlaylistToken.IsEmptyString Then Throw New ArgumentNullException("PlaylistToken", "Unable to get 'PlaylistToken'")
URL = String.Format(PlayListUrlPattern, NameTrue, PlaylistToken, Page)
@@ -377,12 +373,21 @@ Namespace API.PornHub
'Debug.WriteLine(URL)
Dim r$ = Responser.GetResponse(URL)
If Not r.IsEmptyString Then
Dim l As List(Of UserVideo) = RegexFields(Of UserVideo)(r, {RegexUserVideos}, {6, 7, 3, 10})
Dim l As List(Of UserVideo) = RegexFields(Of UserVideo)(r, {RegexUserVideos}, {6, 7, 3, 11})
'If l.ListExists And Not SiteMode = SiteModes.Playlists Then l = l.ListTake(3, l.Count).ToList
If l.ListExists And Not SiteMode = SiteModes.Playlists Then l = l.ListTake(1, l.Count).ToList
If l.ListExists And Not SiteMode = SiteModes.Playlists And Not IsUser Then l = l.ListTake(1, l.Count).ToList
If l.ListExists Then
If IsUser Then
If Type = VideoTypes.Favorite Then
If Type = VideoTypes.Tagged Then
l = l.ListTake(4, l.Count)
If l.ListExists Then l.RemoveAll(Function(uv) Not uv.UserRef.IsEmptyString AndAlso uv.UserRef = usrRef)
ElseIf Type = VideoTypes.Uploaded Then
If PersonType = PersonTypeCannel Then
l = l.ListTake(4, l.Count)
Else
l.RemoveAll(Function(uv) Not uv.UserRef.IsEmptyString AndAlso Not uv.UserRef = usrRef)
End If
ElseIf Type = VideoTypes.Favorite Then
l.RemoveAll(Function(uv) uv.Type = VideoTypes.Private)
ElseIf Not PersonType = PersonTypeCannel Then
l.RemoveAll(Function(uv) Not uv.Type = Type)

View File

@@ -6,9 +6,10 @@
'
' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY
Imports SCrawler.API.Base
Imports SCrawler.Plugin.Attributes
Namespace API.PornHub
Friend Class UserExchangeOptions : Inherits Xhamster.UserExchangeOptions
Friend Class UserExchangeOptions : Inherits EditorExchangeOptionsBase_P
<PSetting(NameOf(SiteSettings.DownloadUHD), NameOf(MySettings))>
Friend Property DownloadUHD As Boolean
<PSetting(NameOf(SiteSettings.DownloadUploaded), NameOf(MySettings))>
@@ -23,16 +24,17 @@ Namespace API.PornHub
Friend Property DownloadGifs As Boolean
Private ReadOnly Property MySettings As SiteSettings
Friend Sub New(ByVal u As UserData)
MyBase.New(u)
DownloadUHD = u.DownloadUHD
DownloadUploaded = u.DownloadUploaded
DownloadTagged = u.DownloadTagged
DownloadPrivate = u.DownloadPrivate
DownloadFavorite = u.DownloadFavorite
DownloadGifs = u.DownloadGifs
QueryString = u.QueryString
MySettings = u.HOST.Source
End Sub
Friend Sub New(ByVal s As SiteSettings)
MyBase.New(s)
Dim v As CheckState = CInt(s.DownloadGifs.Value)
DownloadUHD = s.DownloadUHD.Value
DownloadUploaded = s.DownloadUploaded.Value
@@ -42,5 +44,16 @@ Namespace API.PornHub
DownloadGifs = Not v = CheckState.Unchecked
MySettings = s
End Sub
Friend Overrides Sub Apply(ByRef u As IPSite)
MyBase.Apply(u)
With DirectCast(u, UserData)
.DownloadUHD = DownloadUHD
.DownloadUploaded = DownloadUploaded
.DownloadTagged = DownloadTagged
.DownloadPrivate = DownloadPrivate
.DownloadFavorite = DownloadFavorite
.DownloadGifs = DownloadGifs
End With
End Sub
End Class
End Namespace

View File

@@ -88,14 +88,22 @@ Namespace API.Reddit
End Property
Friend Property ViewMode As View = View.New Implements IRedditView.ViewMode
Friend Property ViewPeriod As Period = Period.All Implements IRedditView.ViewPeriod
Friend Property DownloadText As Boolean = False Implements IRedditView.DownloadText
Friend Property DownloadTextPosts As Boolean = False Implements IRedditView.DownloadTextPosts
Friend Property DownloadTextSpecialFolder As Boolean = False Implements IRedditView.DownloadTextSpecialFolder
Friend Property RedGifsAccount As String = String.Empty Implements IRedditView.RedGifsAccount
Friend Property RedditAccount As String = String.Empty Implements IRedditView.RedditAccount
Friend Sub SetView(ByVal Options As IRedditView) Implements IRedditView.SetView
If Not Options Is Nothing Then
ViewMode = Options.ViewMode
ViewPeriod = Options.ViewPeriod
RedditAccount = Options.RedditAccount
RedGifsAccount = Options.RedGifsAccount
With Options
ViewMode = .ViewMode
ViewPeriod = .ViewPeriod
DownloadText = .DownloadText
DownloadTextPosts = .DownloadTextPosts
DownloadTextSpecialFolder = .DownloadTextSpecialFolder
RedditAccount = .RedditAccount
RedGifsAccount = .RedGifsAccount
End With
End If
End Sub
#Region "Statistics support"

View File

@@ -21,6 +21,6 @@ Namespace API.Reddit
Friend ReadOnly UrlBasePattern As RParams = RParams.DM("(?<=/)([^/]+?\.[\w]{3,4})(?=(\?|\Z))", 0)
Friend ReadOnly VideoRegEx As RParams = RParams.DM("http.{0,1}://[^" & Chr(34) & "]+?mp4", 0)
Private ReadOnly EUR_PROVIDER As New ANumbers(ANumbers.Cultures.EUR)
Friend ReadOnly UnixDate32ProviderReddit As New CustomProvider(Function(v, d, p, n, e) ADateTime.ParseUnix32(AConvert(Of Double)(v, EUR_PROVIDER, v), n, e))
<Obsolete("Use 'UnixDate32Provider'", True)> Friend ReadOnly UnixDate32ProviderReddit As New CustomProvider(Function(v, d, p, n, e) ADateTime.ParseUnix32(AConvert(Of Double)(v, EUR_PROVIDER, v), n, e))
End Module
End Namespace

View File

@@ -12,6 +12,8 @@ Namespace API.Reddit
[New] = 0
Hot = 1
Top = 2
Best = 3
Rising = 4
End Enum
Enum Period As Integer
All = 0
@@ -23,25 +25,45 @@ Namespace API.Reddit
End Enum
Property ViewMode As View
Property ViewPeriod As Period
Property DownloadText As Boolean
Property DownloadTextPosts As Boolean
Property DownloadTextSpecialFolder As Boolean
Property RedGifsAccount As String
Property RedditAccount As String
Sub SetView(ByVal Options As IRedditView)
End Interface
Friend Class RedditViewExchange : Implements IRedditView
Friend Class RedditViewExchange : Inherits Base.EditorExchangeOptionsBase : Implements IRedditView
Friend Const Name_ViewMode As String = "ViewMode"
Friend Const Name_ViewPeriod As String = "ViewPeriod"
Friend Const Name_RedGifsAccount As String = "RedGifsAccount"
Friend Const Name_RedditAccount As String = "RedditAccount"
Friend Property ViewMode As IRedditView.View Implements IRedditView.ViewMode
Friend Property ViewPeriod As IRedditView.Period Implements IRedditView.ViewPeriod
Friend Overrides Property DownloadText As Boolean Implements IRedditView.DownloadText
Friend Overrides Property DownloadTextPosts As Boolean Implements IRedditView.DownloadTextPosts
Friend Overrides Property DownloadTextSpecialFolder As Boolean Implements IRedditView.DownloadTextSpecialFolder
Friend Property RedGifsAccount As String Implements IRedditView.RedGifsAccount
Friend Property RedditAccount As String Implements IRedditView.RedditAccount
Friend Sub New(ByVal Options As IRedditView)
MyBase.New(DirectCast(Options, UserData))
SetView(Options)
_ApplyBase_Name = False
End Sub
Friend Sub New(ByVal s As SiteSettings)
MyBase.New(s)
_ApplyBase_Name = False
End Sub
Friend Sub SetView(ByVal Options As IRedditView) Implements IRedditView.SetView
If Not Options Is Nothing Then
ViewMode = Options.ViewMode
ViewPeriod = Options.ViewPeriod
RedGifsAccount = Options.RedGifsAccount
RedditAccount = Options.RedditAccount
With Options
ViewMode = .ViewMode
ViewPeriod = .ViewPeriod
DownloadText = .DownloadText
DownloadTextPosts = .DownloadTextPosts
DownloadTextSpecialFolder = .DownloadTextSpecialFolder
RedGifsAccount = .RedGifsAccount
RedditAccount = .RedditAccount
End With
End If
End Sub
End Class

View File

@@ -22,17 +22,21 @@ Namespace API.Reddit
Private components As System.ComponentModel.IContainer
<System.Diagnostics.DebuggerStepThrough()>
Private Sub InitializeComponent()
Me.components = New System.ComponentModel.Container()
Dim CONTAINER_MAIN As System.Windows.Forms.ToolStripContainer
Dim TP_VIEW_MODE As System.Windows.Forms.TableLayoutPanel
Dim LBL_VIEW_MODE As System.Windows.Forms.Label
Dim LBL_PERIOD As System.Windows.Forms.Label
Dim ActionButton1 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim ActionButton3 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(RedditViewSettingsForm))
Dim ActionButton2 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim ActionButton4 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim TP_TEXT As System.Windows.Forms.TableLayoutPanel
Me.TP_MAIN = New System.Windows.Forms.TableLayoutPanel()
Me.OPT_VIEW_MODE_NEW = New System.Windows.Forms.RadioButton()
Me.OPT_VIEW_MODE_HOT = New System.Windows.Forms.RadioButton()
Me.OPT_VIEW_MODE_TOP = New System.Windows.Forms.RadioButton()
Me.OPT_VIEW_MODE_BEST = New System.Windows.Forms.RadioButton()
Me.OPT_VIEW_MODE_RISING = New System.Windows.Forms.RadioButton()
Me.TP_PERIOD = New System.Windows.Forms.TableLayoutPanel()
Me.OPT_PERIOD_ALL = New System.Windows.Forms.RadioButton()
Me.OPT_PERIOD_HOUR = New System.Windows.Forms.RadioButton()
@@ -42,10 +46,15 @@ Namespace API.Reddit
Me.OPT_PERIOD_YEAR = New System.Windows.Forms.RadioButton()
Me.CMB_REDGIFS_ACC = New PersonalUtilities.Forms.Controls.ComboBoxExtended()
Me.CMB_REDDIT_ACC = New PersonalUtilities.Forms.Controls.ComboBoxExtended()
Me.CH_TXT_DOWN_TXT = New System.Windows.Forms.CheckBox()
Me.CH_TXT_DOWN_POSTS = New System.Windows.Forms.CheckBox()
Me.TT_MAIN = New System.Windows.Forms.ToolTip(Me.components)
Me.CH_TXT_DOWN_SPEC_FOLDER = New System.Windows.Forms.CheckBox()
CONTAINER_MAIN = New System.Windows.Forms.ToolStripContainer()
TP_VIEW_MODE = New System.Windows.Forms.TableLayoutPanel()
LBL_VIEW_MODE = New System.Windows.Forms.Label()
LBL_PERIOD = New System.Windows.Forms.Label()
TP_TEXT = New System.Windows.Forms.TableLayoutPanel()
CONTAINER_MAIN.ContentPanel.SuspendLayout()
CONTAINER_MAIN.SuspendLayout()
Me.TP_MAIN.SuspendLayout()
@@ -53,6 +62,7 @@ Namespace API.Reddit
Me.TP_PERIOD.SuspendLayout()
CType(Me.CMB_REDGIFS_ACC, System.ComponentModel.ISupportInitialize).BeginInit()
CType(Me.CMB_REDDIT_ACC, System.ComponentModel.ISupportInitialize).BeginInit()
TP_TEXT.SuspendLayout()
Me.SuspendLayout()
'
'CONTAINER_MAIN
@@ -61,13 +71,13 @@ Namespace API.Reddit
'CONTAINER_MAIN.ContentPanel
'
CONTAINER_MAIN.ContentPanel.Controls.Add(Me.TP_MAIN)
CONTAINER_MAIN.ContentPanel.Size = New System.Drawing.Size(477, 169)
CONTAINER_MAIN.ContentPanel.Size = New System.Drawing.Size(477, 222)
CONTAINER_MAIN.Dock = System.Windows.Forms.DockStyle.Fill
CONTAINER_MAIN.LeftToolStripPanelVisible = False
CONTAINER_MAIN.Location = New System.Drawing.Point(0, 0)
CONTAINER_MAIN.Name = "CONTAINER_MAIN"
CONTAINER_MAIN.RightToolStripPanelVisible = False
CONTAINER_MAIN.Size = New System.Drawing.Size(477, 169)
CONTAINER_MAIN.Size = New System.Drawing.Size(477, 222)
CONTAINER_MAIN.TabIndex = 0
CONTAINER_MAIN.TopToolStripPanelVisible = False
'
@@ -78,18 +88,20 @@ Namespace API.Reddit
Me.TP_MAIN.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
Me.TP_MAIN.Controls.Add(TP_VIEW_MODE, 0, 0)
Me.TP_MAIN.Controls.Add(Me.TP_PERIOD, 0, 1)
Me.TP_MAIN.Controls.Add(Me.CMB_REDGIFS_ACC, 0, 3)
Me.TP_MAIN.Controls.Add(Me.CMB_REDDIT_ACC, 0, 2)
Me.TP_MAIN.Controls.Add(Me.CMB_REDGIFS_ACC, 0, 4)
Me.TP_MAIN.Controls.Add(Me.CMB_REDDIT_ACC, 0, 3)
Me.TP_MAIN.Controls.Add(TP_TEXT, 0, 2)
Me.TP_MAIN.Dock = System.Windows.Forms.DockStyle.Fill
Me.TP_MAIN.Location = New System.Drawing.Point(0, 0)
Me.TP_MAIN.Name = "TP_MAIN"
Me.TP_MAIN.RowCount = 5
Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
Me.TP_MAIN.RowCount = 6
Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 56.0!))
Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 56.0!))
Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
Me.TP_MAIN.Size = New System.Drawing.Size(477, 169)
Me.TP_MAIN.Size = New System.Drawing.Size(477, 222)
Me.TP_MAIN.TabIndex = 0
'
'TP_VIEW_MODE
@@ -103,14 +115,16 @@ Namespace API.Reddit
TP_VIEW_MODE.Controls.Add(Me.OPT_VIEW_MODE_NEW, 1, 0)
TP_VIEW_MODE.Controls.Add(Me.OPT_VIEW_MODE_HOT, 2, 0)
TP_VIEW_MODE.Controls.Add(Me.OPT_VIEW_MODE_TOP, 3, 0)
TP_VIEW_MODE.Controls.Add(Me.OPT_VIEW_MODE_BEST, 1, 1)
TP_VIEW_MODE.Controls.Add(Me.OPT_VIEW_MODE_RISING, 2, 1)
TP_VIEW_MODE.Dock = System.Windows.Forms.DockStyle.Fill
TP_VIEW_MODE.Location = New System.Drawing.Point(1, 1)
TP_VIEW_MODE.Margin = New System.Windows.Forms.Padding(0)
TP_VIEW_MODE.Name = "TP_VIEW_MODE"
TP_VIEW_MODE.RowCount = 1
TP_VIEW_MODE.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
TP_VIEW_MODE.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
TP_VIEW_MODE.Size = New System.Drawing.Size(475, 28)
TP_VIEW_MODE.RowCount = 2
TP_VIEW_MODE.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50.0!))
TP_VIEW_MODE.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50.0!))
TP_VIEW_MODE.Size = New System.Drawing.Size(475, 56)
TP_VIEW_MODE.TabIndex = 0
'
'LBL_VIEW_MODE
@@ -160,6 +174,30 @@ Namespace API.Reddit
Me.OPT_VIEW_MODE_TOP.Text = "Top"
Me.OPT_VIEW_MODE_TOP.UseVisualStyleBackColor = True
'
'OPT_VIEW_MODE_BEST
'
Me.OPT_VIEW_MODE_BEST.AutoSize = True
Me.OPT_VIEW_MODE_BEST.Dock = System.Windows.Forms.DockStyle.Fill
Me.OPT_VIEW_MODE_BEST.Location = New System.Drawing.Point(121, 31)
Me.OPT_VIEW_MODE_BEST.Name = "OPT_VIEW_MODE_BEST"
Me.OPT_VIEW_MODE_BEST.Size = New System.Drawing.Size(112, 22)
Me.OPT_VIEW_MODE_BEST.TabIndex = 4
Me.OPT_VIEW_MODE_BEST.TabStop = True
Me.OPT_VIEW_MODE_BEST.Text = "Best"
Me.OPT_VIEW_MODE_BEST.UseVisualStyleBackColor = True
'
'OPT_VIEW_MODE_RISING
'
Me.OPT_VIEW_MODE_RISING.AutoSize = True
Me.OPT_VIEW_MODE_RISING.Dock = System.Windows.Forms.DockStyle.Fill
Me.OPT_VIEW_MODE_RISING.Location = New System.Drawing.Point(239, 31)
Me.OPT_VIEW_MODE_RISING.Name = "OPT_VIEW_MODE_RISING"
Me.OPT_VIEW_MODE_RISING.Size = New System.Drawing.Size(112, 22)
Me.OPT_VIEW_MODE_RISING.TabIndex = 5
Me.OPT_VIEW_MODE_RISING.TabStop = True
Me.OPT_VIEW_MODE_RISING.Text = "Rising"
Me.OPT_VIEW_MODE_RISING.UseVisualStyleBackColor = True
'
'TP_PERIOD
'
Me.TP_PERIOD.ColumnCount = 4
@@ -175,7 +213,7 @@ Namespace API.Reddit
Me.TP_PERIOD.Controls.Add(Me.OPT_PERIOD_MONTH, 2, 1)
Me.TP_PERIOD.Controls.Add(Me.OPT_PERIOD_YEAR, 3, 1)
Me.TP_PERIOD.Dock = System.Windows.Forms.DockStyle.Fill
Me.TP_PERIOD.Location = New System.Drawing.Point(1, 30)
Me.TP_PERIOD.Location = New System.Drawing.Point(1, 58)
Me.TP_PERIOD.Margin = New System.Windows.Forms.Padding(0)
Me.TP_PERIOD.Name = "TP_PERIOD"
Me.TP_PERIOD.RowCount = 2
@@ -269,17 +307,17 @@ Namespace API.Reddit
'
'CMB_REDGIFS_ACC
'
ActionButton1.BackgroundImage = CType(resources.GetObject("ActionButton1.BackgroundImage"), System.Drawing.Image)
ActionButton1.Name = "ArrowDown"
ActionButton1.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.ArrowDown
Me.CMB_REDGIFS_ACC.Buttons.Add(ActionButton1)
ActionButton3.BackgroundImage = CType(resources.GetObject("ActionButton3.BackgroundImage"), System.Drawing.Image)
ActionButton3.Name = "ArrowDown"
ActionButton3.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.ArrowDown
Me.CMB_REDGIFS_ACC.Buttons.Add(ActionButton3)
Me.CMB_REDGIFS_ACC.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.Label
Me.CMB_REDGIFS_ACC.CaptionSizeType = System.Windows.Forms.SizeType.Percent
Me.CMB_REDGIFS_ACC.CaptionText = "RedGifs account"
Me.CMB_REDGIFS_ACC.CaptionVisible = True
Me.CMB_REDGIFS_ACC.CaptionWidth = 26.0R
Me.CMB_REDGIFS_ACC.Dock = System.Windows.Forms.DockStyle.Fill
Me.CMB_REDGIFS_ACC.Location = New System.Drawing.Point(4, 119)
Me.CMB_REDGIFS_ACC.Location = New System.Drawing.Point(4, 173)
Me.CMB_REDGIFS_ACC.Name = "CMB_REDGIFS_ACC"
Me.CMB_REDGIFS_ACC.Size = New System.Drawing.Size(469, 22)
Me.CMB_REDGIFS_ACC.TabIndex = 4
@@ -287,35 +325,89 @@ Namespace API.Reddit
'
'CMB_REDDIT_ACC
'
ActionButton2.BackgroundImage = CType(resources.GetObject("ActionButton2.BackgroundImage"), System.Drawing.Image)
ActionButton2.Name = "ArrowDown"
ActionButton2.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.ArrowDown
Me.CMB_REDDIT_ACC.Buttons.Add(ActionButton2)
ActionButton4.BackgroundImage = CType(resources.GetObject("ActionButton4.BackgroundImage"), System.Drawing.Image)
ActionButton4.Name = "ArrowDown"
ActionButton4.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.ArrowDown
Me.CMB_REDDIT_ACC.Buttons.Add(ActionButton4)
Me.CMB_REDDIT_ACC.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.Label
Me.CMB_REDDIT_ACC.CaptionSizeType = System.Windows.Forms.SizeType.Percent
Me.CMB_REDDIT_ACC.CaptionText = "Reddit account"
Me.CMB_REDDIT_ACC.CaptionVisible = True
Me.CMB_REDDIT_ACC.CaptionWidth = 26.0R
Me.CMB_REDDIT_ACC.Dock = System.Windows.Forms.DockStyle.Fill
Me.CMB_REDDIT_ACC.Location = New System.Drawing.Point(4, 90)
Me.CMB_REDDIT_ACC.Location = New System.Drawing.Point(4, 144)
Me.CMB_REDDIT_ACC.Name = "CMB_REDDIT_ACC"
Me.CMB_REDDIT_ACC.Size = New System.Drawing.Size(469, 22)
Me.CMB_REDDIT_ACC.TabIndex = 3
Me.CMB_REDDIT_ACC.TextBoxBorderStyle = System.Windows.Forms.BorderStyle.FixedSingle
'
'TP_TEXT
'
TP_TEXT.ColumnCount = 3
TP_TEXT.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33333!))
TP_TEXT.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33333!))
TP_TEXT.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33333!))
TP_TEXT.Controls.Add(Me.CH_TXT_DOWN_TXT, 0, 0)
TP_TEXT.Controls.Add(Me.CH_TXT_DOWN_POSTS, 1, 0)
TP_TEXT.Controls.Add(Me.CH_TXT_DOWN_SPEC_FOLDER, 2, 0)
TP_TEXT.Dock = System.Windows.Forms.DockStyle.Fill
TP_TEXT.Location = New System.Drawing.Point(1, 115)
TP_TEXT.Margin = New System.Windows.Forms.Padding(0)
TP_TEXT.Name = "TP_TEXT"
TP_TEXT.RowCount = 1
TP_TEXT.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
TP_TEXT.Size = New System.Drawing.Size(475, 25)
TP_TEXT.TabIndex = 5
'
'CH_TXT_DOWN_TXT
'
Me.CH_TXT_DOWN_TXT.AutoSize = True
Me.CH_TXT_DOWN_TXT.Dock = System.Windows.Forms.DockStyle.Fill
Me.CH_TXT_DOWN_TXT.Location = New System.Drawing.Point(3, 3)
Me.CH_TXT_DOWN_TXT.Name = "CH_TXT_DOWN_TXT"
Me.CH_TXT_DOWN_TXT.Size = New System.Drawing.Size(152, 19)
Me.CH_TXT_DOWN_TXT.TabIndex = 0
Me.CH_TXT_DOWN_TXT.Text = "Download text"
Me.TT_MAIN.SetToolTip(Me.CH_TXT_DOWN_TXT, "Download text (if available) for posts with image and video")
Me.CH_TXT_DOWN_TXT.UseVisualStyleBackColor = True
'
'CH_TXT_DOWN_POSTS
'
Me.CH_TXT_DOWN_POSTS.AutoSize = True
Me.CH_TXT_DOWN_POSTS.Dock = System.Windows.Forms.DockStyle.Fill
Me.CH_TXT_DOWN_POSTS.Location = New System.Drawing.Point(161, 3)
Me.CH_TXT_DOWN_POSTS.Name = "CH_TXT_DOWN_POSTS"
Me.CH_TXT_DOWN_POSTS.Size = New System.Drawing.Size(152, 19)
Me.CH_TXT_DOWN_POSTS.TabIndex = 1
Me.CH_TXT_DOWN_POSTS.Text = "Download text posts"
Me.TT_MAIN.SetToolTip(Me.CH_TXT_DOWN_POSTS, "Download text (if available) for text posts (no image and video)")
Me.CH_TXT_DOWN_POSTS.UseVisualStyleBackColor = True
'
'CH_TXT_DOWN_SPEC_FOLDER
'
Me.CH_TXT_DOWN_SPEC_FOLDER.AutoSize = True
Me.CH_TXT_DOWN_SPEC_FOLDER.Dock = System.Windows.Forms.DockStyle.Fill
Me.CH_TXT_DOWN_SPEC_FOLDER.Location = New System.Drawing.Point(319, 3)
Me.CH_TXT_DOWN_SPEC_FOLDER.Name = "CH_TXT_DOWN_SPEC_FOLDER"
Me.CH_TXT_DOWN_SPEC_FOLDER.Size = New System.Drawing.Size(153, 19)
Me.CH_TXT_DOWN_SPEC_FOLDER.TabIndex = 2
Me.CH_TXT_DOWN_SPEC_FOLDER.Text = "Text special folder"
Me.TT_MAIN.SetToolTip(Me.CH_TXT_DOWN_SPEC_FOLDER, "If checked, text files will be saved to a separate folder")
Me.CH_TXT_DOWN_SPEC_FOLDER.UseVisualStyleBackColor = True
'
'RedditViewSettingsForm
'
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
Me.ClientSize = New System.Drawing.Size(477, 169)
Me.ClientSize = New System.Drawing.Size(477, 222)
Me.Controls.Add(CONTAINER_MAIN)
Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle
Me.Icon = Global.SCrawler.My.Resources.SiteResources.RedditIcon_128
Me.KeyPreview = True
Me.MaximizeBox = False
Me.MaximumSize = New System.Drawing.Size(493, 208)
Me.MaximumSize = New System.Drawing.Size(493, 261)
Me.MinimizeBox = False
Me.MinimumSize = New System.Drawing.Size(493, 208)
Me.MinimumSize = New System.Drawing.Size(493, 261)
Me.Name = "RedditViewSettingsForm"
Me.ShowInTaskbar = False
Me.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide
@@ -330,6 +422,8 @@ Namespace API.Reddit
Me.TP_PERIOD.PerformLayout()
CType(Me.CMB_REDGIFS_ACC, System.ComponentModel.ISupportInitialize).EndInit()
CType(Me.CMB_REDDIT_ACC, System.ComponentModel.ISupportInitialize).EndInit()
TP_TEXT.ResumeLayout(False)
TP_TEXT.PerformLayout()
Me.ResumeLayout(False)
End Sub
@@ -346,5 +440,11 @@ Namespace API.Reddit
Private WithEvents CMB_REDGIFS_ACC As PersonalUtilities.Forms.Controls.ComboBoxExtended
Private WithEvents CMB_REDDIT_ACC As PersonalUtilities.Forms.Controls.ComboBoxExtended
Private WithEvents TP_MAIN As TableLayoutPanel
Private WithEvents OPT_VIEW_MODE_BEST As RadioButton
Private WithEvents OPT_VIEW_MODE_RISING As RadioButton
Private WithEvents CH_TXT_DOWN_TXT As CheckBox
Private WithEvents TT_MAIN As ToolTip
Private WithEvents CH_TXT_DOWN_POSTS As CheckBox
Private WithEvents CH_TXT_DOWN_SPEC_FOLDER As CheckBox
End Class
End Namespace

View File

@@ -130,184 +130,195 @@
<value>False</value>
</metadata>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="ActionButton1.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>
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
U1wTEVFuCACwydqqfmZO8Z3yxXrc6T7f73o/AEyQiDgtp/QXG/EpgTbFF3JKv1D+TABgQnQfx2ur+POc
4kflC/kx5u6c4l+blF7YXWoofw4AMIG6W/G2i/Wzckp/lVP896EX9PJFvkh3M5/6o03E7+acF8vvCQBM
mQO/EFTVctfezxEXtlX9vLaqX9z9s6mqC5YWF+fLrwEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6LEtW/4flgYiLD1qeX0A
AAAASUVORK5CYII=
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAYIj+H5YGIizEb/aEAAAAAElFTkSuQmCC
</value>
</data>
<data name="ActionButton2.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>
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
U1wTEVFuCACwydqqfmZO8Z3yxXrc6T7f73o/AEyQiDgtp/QXG/EpgTbFF3JKv1D+TABgQnQfx2ur+POc
4kflC/kx5u6c4l+blF7YXWoofw4AMIG6W/G2i/Wzckp/lVP896EX9PJFvkh3M5/6o03E7+acF8vvCQBM
mQO/EFTVctfezxEXtlX9vLaqX9z9s6mqC5YWF+fLrwEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6LEtW/4flgYiLD1qeX0A
AAAASUVORK5CYII=
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAYIj+H5YGIizEb/aEAAAAAElFTkSuQmCC
</value>
</data>
<metadata name="TP_TEXT.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value>
</metadata>
<metadata name="TT_MAIN.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<metadata name="TT_MAIN.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<metadata name="LBL_VIEW_MODE.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value>
</metadata>
<metadata name="LBL_PERIOD.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value>
</metadata>
</root>

View File

@@ -38,6 +38,8 @@ Namespace API.Reddit
Select Case MyOptions.ViewMode
Case CView.Hot : OPT_VIEW_MODE_HOT.Checked = True
Case CView.Top : OPT_VIEW_MODE_TOP.Checked = True
Case CView.Best : OPT_VIEW_MODE_BEST.Checked = True
Case CView.Rising : OPT_VIEW_MODE_RISING.Checked = True
Case Else : OPT_VIEW_MODE_NEW.Checked = True
End Select
Select Case MyOptions.ViewPeriod
@@ -50,11 +52,15 @@ Namespace API.Reddit
End Select
ChangePeriodEnabled()
CH_TXT_DOWN_TXT.Checked = MyOptions.DownloadText
CH_TXT_DOWN_POSTS.Checked = MyOptions.DownloadTextPosts
CH_TXT_DOWN_SPEC_FOLDER.Checked = MyOptions.DownloadTextSpecialFolder
PopulateCMB(Settings(RedditSiteKey), CMB_REDDIT_ACC, MyOptions.RedditAccount)
PopulateCMB(Settings(RedGifs.RedGifsSiteKey), CMB_REDGIFS_ACC, MyOptions.RedGifsAccount)
If IsUserSettings Then
TP_MAIN.Controls.Remove(CMB_REDDIT_ACC)
TP_MAIN.RowStyles(2).Height = 0
TP_MAIN.RowStyles(3).Height = 0
TP_MAIN.Refresh()
Dim s As Size = Size
s.Height -= 28
@@ -100,6 +106,8 @@ Namespace API.Reddit
Select Case True
Case OPT_VIEW_MODE_HOT.Checked : .ViewMode = CView.Hot
Case OPT_VIEW_MODE_TOP.Checked : .ViewMode = CView.Top
Case OPT_VIEW_MODE_BEST.Checked : .ViewMode = CView.Best
Case OPT_VIEW_MODE_RISING.Checked : .ViewMode = CView.Rising
Case Else : .ViewMode = CView.New
End Select
Select Case True
@@ -110,6 +118,9 @@ Namespace API.Reddit
Case OPT_PERIOD_YEAR.Checked : .ViewPeriod = CPeriod.Year
Case Else : .ViewPeriod = CPeriod.All
End Select
.DownloadText = CH_TXT_DOWN_TXT.Checked
.DownloadTextPosts = CH_TXT_DOWN_POSTS.Checked
.DownloadTextSpecialFolder = CH_TXT_DOWN_SPEC_FOLDER.Checked
.RedGifsAccount = CMB_REDGIFS_ACC.Text
If Not IsUserSettings Then .RedditAccount = CMB_REDDIT_ACC.Text
End With

View File

@@ -9,26 +9,38 @@
Imports SCrawler.API.Base
Imports SCrawler.Plugin
Imports SCrawler.Plugin.Attributes
Imports System.Reflection
Imports PersonalUtilities.Tools.Web.Clients
Imports PersonalUtilities.Tools.Web.Clients.Base
Imports PersonalUtilities.Tools.Web.Documents.JSON
Imports PersonalUtilities.Functions.XML
Imports PersonalUtilities.Functions.RegularExpressions
Imports DN = SCrawler.API.Base.DeclaredNames
Imports DownDetector = SCrawler.API.Base.DownDetector
Imports Download = SCrawler.Plugin.ISiteSettings.Download
Namespace API.Reddit
<Manifest(RedditSiteKey), SavedPosts, SpecialForm(False), UseDownDetector>
<Manifest(RedditSiteKey), SavedPosts, SeparatedTasks, SpecialForm(False), UseDownDetector>
Friend Class SiteSettings : Inherits SiteSettingsBase : Implements DownDetector.IDownDetector
#Region "Declarations"
#Region "Authorization"
Private Const ApiClientID_Default As String = "dYctRA-SIJxyykHe27lGZg"
Private Const ApiClientSecret_Default As String = "_5D6KzplRPDga-es1YlpzDIe9hiFlg"
<PropertyOption(ControlText:="Login", ControlToolTip:="Your authorization username", IsAuth:=True), PXML, PClonable(Clone:=False)>
Friend ReadOnly Property AuthUserName As PropertyValue
<PropertyOption(ControlText:="Password", ControlToolTip:="Your authorization password", IsAuth:=True), PXML, PClonable(Clone:=False)>
Friend ReadOnly Property AuthPassword As PropertyValue
<PropertyOption(ControlText:="Client ID", ControlToolTip:="Your registered app client ID", IsAuth:=True), PXML, PClonable(Clone:=False)>
Friend ReadOnly Property ApiClientID As PropertyValue
<PropertyUpdater(NameOf(ApiClientID))> Private Function ApiClientID_SetDefault() As Boolean
ApiClientID.Value = ApiClientID_Default
Return True
End Function
<PropertyOption(ControlText:="Client Secret", ControlToolTip:="Your registered app client secret", IsAuth:=True), PXML, PClonable(Clone:=False)>
Friend ReadOnly Property ApiClientSecret As PropertyValue
<PropertyUpdater(NameOf(ApiClientSecret))> Private Function ApiClientSecret_SetDefault() As Boolean
ApiClientSecret.Value = ApiClientSecret_Default
Return True
End Function
<PropertyOption(ControlText:="Bearer token",
ControlToolTip:="Bearer token (can be null)." & vbCr &
"If you are using cookies to download the timeline, it is highly recommended that you add a token." & vbCr &
@@ -58,14 +70,59 @@ Namespace API.Reddit
Return {AuthUserName.Value, AuthPassword.Value, ApiClientID.Value, ApiClientSecret.Value}.All(Function(v$) Not v.IsEmptyString)
End Get
End Property
<PropertiesDataChecker({NameOf(AuthUserName), NameOf(AuthPassword), NameOf(ApiClientID), NameOf(ApiClientSecret),
NameOf(UseTokenForTimelines), NameOf(UseCookiesForTimelines)})>
Private Function OAuthCredentialsChecker(ByVal p As IEnumerable(Of PropertyData)) As Boolean
Const msgTitle$ = "OAuth credentials"
If p.ListExists Then
Dim useToken As Boolean = False, useCookies As Boolean = False
Dim d$ = String.Empty
Dim dCount As Byte = 0
Dim members As IEnumerable(Of MemberInfo) = GetObjectMembers(Me)
Dim getPropText As Func(Of String, String) = Function(name) members.First(Function(m) m.Name = name).GetCustomAttribute(Of PropertyOption).ControlText
Dim dataStr As Action(Of String, String) = Sub(dd, name) If dd.IsEmptyString Then d.StringAppendLine(getPropText(name)) : dCount += 1
For Each pp As PropertyData In p
Select Case pp.Name
Case NameOf(AuthUserName) : dataStr(pp.Value, NameOf(AuthUserName))
Case NameOf(AuthPassword) : dataStr(pp.Value, NameOf(AuthPassword))
Case NameOf(ApiClientID) : dataStr(pp.Value, NameOf(ApiClientID))
Case NameOf(ApiClientSecret) : dataStr(pp.Value, NameOf(ApiClientSecret))
Case NameOf(UseTokenForTimelines) : useToken = pp.Value
Case NameOf(UseCookiesForTimelines) : useCookies = pp.Value
Case Else : Throw New ArgumentException($"Property name '{pp.Name}' is not implemented", "Property Name")
End Select
Next
If d.IsEmptyString Then
If useToken And useCookies Then
Return True
Else
If Not useToken Then d.StringAppendLine(getPropText(NameOf(UseTokenForTimelines)))
If Not useCookies Then d.StringAppendLine(getPropText(NameOf(UseCookiesForTimelines)))
MsgBoxE({$"You need to check the following options:{vbCr}{d}", msgTitle}, vbCritical)
Return False
End If
ElseIf dCount = 4 Then
Return MsgBoxE({$"You haven't configured OAuth. It's highly recommended to use OAuth.{vbCr}Do you still want to continue?", msgTitle},
vbExclamation,,, {"Process", "Cancel"}) = 0
Else
MsgBoxE({$"You haven't filled in the following fields:{vbCr}{d}.{vbCr}{vbCr}" &
"To use OAuth authorization, you must fill in all authorization fields.", msgTitle}, vbCritical)
Return False
End If
End If
Return True
End Function
#End Region
#Region "Other"
<PropertyOption(ControlText:="Use M3U8", ControlToolTip:="Use M3U8 or mp4 for Reddit videos", IsAuth:=False), PXML, PClonable>
<PropertyOption(ControlText:="Use M3U8", ControlToolTip:="Use M3U8 or mp4 for Reddit videos"), PXML, PClonable, HiddenControl>
Friend ReadOnly Property UseM3U8 As PropertyValue
<PropertyOption(ControlText:="Check image", ControlToolTip:="Check the image if it exists before downloading (it makes downloading very slow)", IsAuth:=False), PXML, PClonable>
<PropertyOption(ControlText:="Check image", ControlToolTip:="Check the image if it exists before downloading (it makes downloading very slow)"), PXML, PClonable, HiddenControl>
Friend ReadOnly Property CheckImage As PropertyValue
<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"), PXML, PClonable, HiddenControl>
Friend ReadOnly Property CheckImageReturnOrig As PropertyValue
<PropertyOption(ControlText:=DN.ConcurrentDownloadsCaption,
ControlToolTip:=DN.ConcurrentDownloadsToolTip, AllowNull:=False), PXML, TaskCounter, PClonable>
Friend ReadOnly Property ConcurrentDownloads As PropertyValue
#End Region
#Region "IDownDetector Support"
Private ReadOnly Property IDownDetector_Value As Integer Implements DownDetector.IDownDetector.Value
@@ -96,6 +153,7 @@ Namespace API.Reddit
With Responser
Dim d% = .Decoders.Count
.Decoders.ListAddList({SymbolsConverter.Converters.Unicode, SymbolsConverter.Converters.HTML}, LAP.NotContainsOnly)
.Accept = "application/json"
token = .Headers.Value(DeclaredNames.Header_Authorization)
End With
@@ -104,7 +162,7 @@ Namespace API.Reddit
ApiClientID = New PropertyValue(String.Empty, GetType(String))
ApiClientSecret = New PropertyValue(String.Empty, GetType(String))
BearerToken = New PropertyValue(token, GetType(String), Sub(v) Responser.Headers.Add(DeclaredNames.Header_Authorization, v))
BearerTokenUseCurl = New PropertyValue(True)
BearerTokenUseCurl = New PropertyValue(False)
TokenUpdateInterval = New PropertyValue(360)
TokenUpdateIntervalProvider = New TokenRefreshIntervalProvider
BearerTokenDateUpdate = New PropertyValue(Now.AddYears(-1))
@@ -116,6 +174,7 @@ Namespace API.Reddit
UseM3U8 = New PropertyValue(True)
CheckImage = New PropertyValue(False)
CheckImageReturnOrig = New PropertyValue(True)
ConcurrentDownloads = New PropertyValue(1)
MDD = New MyDownDetector(Me)
@@ -123,11 +182,14 @@ Namespace API.Reddit
ImageVideoContains = "reddit.com"
UserRegex = RParams.DM("[htps:/]{7,8}.*?reddit.com/([user]{1,4})/([^/\?&]+)", 0, RegexReturn.ListByMatch, EDP.ReturnValue)
End Sub
Private Const SettingsVersionCurrent As Integer = 1
Private Const SettingsVersionCurrent As Integer = 3
Friend Overrides Sub EndInit()
If CInt(SettingsVersion.Value) < SettingsVersionCurrent Then
SettingsVersion.Value = SettingsVersionCurrent
TokenUpdateInterval.Value = 360
UseM3U8.Value = True
CheckImage.Value = False
CheckImageReturnOrig.Value = True
BearerTokenUseCurl.Value = False
End If
MyBase.EndInit()
End Sub
@@ -164,6 +226,7 @@ Namespace API.Reddit
End Sub
End Class
Friend Property SessionInterrupted As Boolean = False
Friend Property RequestCount As Integer = 0
Friend Overrides Function ReadyToDownload(ByVal What As Download) As Boolean
If What = Download.Main Then
Return Not SessionInterrupted
@@ -179,6 +242,7 @@ Namespace API.Reddit
End Function
Friend Overrides Sub DownloadDone(ByVal What As Download)
SessionInterrupted = False
RequestCount = 0
MDD.Reset()
MyBase.DownloadDone(What)
End Sub
@@ -211,7 +275,7 @@ Namespace API.Reddit
#End Region
#Region "UserOptions"
Friend Overrides Sub UserOptions(ByRef Options As Object, ByVal OpenForm As Boolean)
If Options Is Nothing OrElse Not TypeOf Options Is RedditViewExchange Then Options = New RedditViewExchange
If Options Is Nothing OrElse Not TypeOf Options Is RedditViewExchange Then Options = New RedditViewExchange(Me)
If OpenForm Then
Using f As New RedditViewSettingsForm(Options, True) : f.ShowDialog() : End Using
End If
@@ -232,23 +296,6 @@ Namespace API.Reddit
End Sub
#End Region
#Region "Token"
<PropertiesDataChecker({NameOf(AuthUserName), NameOf(AuthPassword), NameOf(ApiClientID), NameOf(ApiClientSecret)})>
Private Function TokenPropertiesChecker(ByVal p As IEnumerable(Of PropertyData)) As Boolean
If p.ListExists Then
Dim wrong As New List(Of String)
For i% = 0 To p.Count - 1
If CStr(p(i).Value).IsEmptyString Then wrong.Add(p(i).Name)
Next
If wrong.Count > 0 And wrong.Count <> 4 Then
MsgBoxE({$"You have not completed the following fields: {wrong.ListToString}." & vbCr &
"To use OAuth authorization, all authorization fields must be filled in.", "Validate token fields"}, vbCritical)
Return False
Else
Return True
End If
End If
Return False
End Function
Private Function UpdateTokenIfRequired() As Boolean
UpdateRedGifsToken()
If (CBool(UseTokenForTimelines.Value) Or CBool(UseTokenForSavedPosts.Value)) AndAlso CredentialsExists Then

View File

@@ -8,19 +8,20 @@
' but WITHOUT ANY WARRANTY
Imports System.Net
Imports System.Threading
Imports PersonalUtilities.Functions.RegularExpressions
Imports PersonalUtilities.Functions.XML
Imports PersonalUtilities.Tools.ImageRenderer
Imports PersonalUtilities.Tools.Web.Clients
Imports PersonalUtilities.Tools.Web.Clients.Base
Imports PersonalUtilities.Tools.Web.Documents.JSON
Imports SCrawler.API.Base
Imports SCrawler.API.Reddit.RedditViewExchange
Imports SCrawler.API.YouTube.Objects
Imports SCrawler.Plugin.Hosts
Imports PersonalUtilities.Functions.XML
Imports PersonalUtilities.Functions.RegularExpressions
Imports PersonalUtilities.Tools.ImageRenderer
Imports PersonalUtilities.Tools.Web.Clients
Imports PersonalUtilities.Tools.Web.Documents.JSON
Imports CPeriod = SCrawler.API.Reddit.IRedditView.Period
Imports CView = SCrawler.API.Reddit.IRedditView.View
Imports UStates = SCrawler.API.Base.UserMedia.States
Imports UTypes = SCrawler.API.Base.UserMedia.Types
Imports CView = SCrawler.API.Reddit.IRedditView.View
Imports CPeriod = SCrawler.API.Reddit.IRedditView.Period
Namespace API.Reddit
Friend Class UserData : Inherits UserDataBase : Implements IChannelLimits, IRedditView
#Region "Declarations"
@@ -33,7 +34,8 @@ Namespace API.Reddit
End Property
Private ReadOnly Property DateTrueProvider(ByVal IsChannel As Boolean) As IFormatProvider
Get
Return If(IsChannel, UnixDate32ProviderReddit, UnixDate64Provider)
Return UnixDate32Provider
'Return If(IsChannel, UnixDate32ProviderReddit, UnixDate64Provider)
End Get
End Property
Private ReadOnly Property UseM3U8 As Boolean
@@ -126,10 +128,16 @@ Namespace API.Reddit
Friend Property ViewPeriod As CPeriod Implements IRedditView.ViewPeriod
Friend Sub SetView(ByVal Options As IRedditView) Implements IRedditView.SetView
If Not Options Is Nothing Then
ViewMode = Options.ViewMode
ViewPeriod = Options.ViewPeriod
RedGifsAccount = Options.RedGifsAccount
RedditAccount = Options.RedditAccount
With Options
ViewMode = .ViewMode
ViewPeriod = .ViewPeriod
DownloadText = .DownloadText
DownloadTextPosts = .DownloadTextPosts
DownloadTextSpecialFolder = .DownloadTextSpecialFolder
RedGifsAccount = .RedGifsAccount
RedditAccount = .RedditAccount
If TypeOf Options Is RedditViewExchange Then DirectCast(Options, RedditViewExchange).ApplyBase(Me)
End With
End If
End Sub
Private ReadOnly Property View As String
@@ -137,6 +145,8 @@ Namespace API.Reddit
Select Case ViewMode
Case CView.Hot : Return "hot"
Case CView.Top : Return "top"
Case CView.Best : Return "best"
Case CView.Rising : Return "rising"
Case Else : Return "new"
End Select
End Get
@@ -157,6 +167,9 @@ Namespace API.Reddit
End If
End Get
End Property
Friend Overrides Property DownloadText As Boolean Implements IRedditView.DownloadText
Friend Overrides Property DownloadTextPosts As Boolean Implements IRedditView.DownloadTextPosts
Friend Overrides Property DownloadTextSpecialFolder As Boolean Implements IRedditView.DownloadTextSpecialFolder
#End Region
#Region "Initializer"
Friend Sub New()
@@ -215,7 +228,7 @@ Namespace API.Reddit
End With
End Sub
Friend Overrides Function ExchangeOptionsGet() As Object
Return New RedditViewExchange With {.ViewMode = ViewMode, .ViewPeriod = ViewPeriod, .RedGifsAccount = RedGifsAccount, .RedditAccount = RedditAccount}
Return New RedditViewExchange(Me)
End Function
Friend Overrides Sub ExchangeOptionsSet(ByVal Obj As Object)
If Not Obj Is Nothing AndAlso TypeOf Obj Is IRedditView Then SetView(DirectCast(Obj, IRedditView))
@@ -257,6 +270,8 @@ Namespace API.Reddit
End If
End With
Responser.ProcessExceptionDecision = AddressOf Err429Process
_TotalPostsDownloaded = 0
If IsSavedPosts Then
Responser.DecodersError = EDP.ReturnValue
@@ -274,8 +289,13 @@ Namespace API.Reddit
Else
GetUserInfo()
End If
If SaveToCache AndAlso Not Responser.Decoders.Contains(SymbolsConverter.Converters.HTML) Then _
If SaveToCache Then
DownloadText = False
DownloadTextPosts = False
DownloadTextSpecialFolder = False
If Not Responser.Decoders.Contains(SymbolsConverter.Converters.HTML) Then _
Responser.Decoders.Add(SymbolsConverter.Converters.HTML)
End If
DownloadDataChannel(String.Empty, Token)
If ChannelInfo Is Nothing Then _TempPostsList.ListAddList(_TempMediaList.Select(Function(m) m.Post.ID), LNC)
Else
@@ -287,6 +307,7 @@ Namespace API.Reddit
#End Region
#Region "Download Functions (User, Channel)"
Private Err429Count As Integer = 0
Private Err429TryAgain As Boolean = False
Private _TotalPostsDownloaded As Integer = 0
Private ReadOnly _CrossPosts As List(Of String)
Private Const SiteGfycatKey As String = "gfycat"
@@ -294,6 +315,28 @@ Namespace API.Reddit
Private Const Node_CrosspostRootId As String = "crosspostRootId"
Private Const Node_CrosspostParentId As String = "crosspostParentId"
Private Const Node_CrosspostParent As String = "crosspost_parent"
Private Sub Wait429()
With MySiteSettings
If Not Err429TryAgain Then .RequestCount += 1
Err429TryAgain = False
If (.RequestCount Mod 100) = 0 Then Thread.Sleep(60100)
End With
End Sub
Private Function Err429Process(ByVal Status As IResponserStatus, ByVal NullArg As Object, ByVal CurrErr As ErrorsDescriber) As ErrorsDescriber
If Not Status Is Nothing AndAlso Status.StatusCode = 429 Then
If Err429Count = 0 Then
Err429Count += 1
MySiteSettings.RequestCount = 100
Err429TryAgain = True
Return EDP.ReturnValue
End If
End If
Return CurrErr
End Function
Private Sub Err429Reset()
Err429Count = 0
Err429TryAgain = False
End Sub
Private Sub DownloadDataUser(ByVal POST As String, ByVal Token As CancellationToken)
Dim eObj% = 0
Dim round% = 0
@@ -311,31 +354,38 @@ Namespace API.Reddit
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)
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"
'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"
URL = $"https://oauth.reddit.com/user/{NameTrue}/submitted.json?rtj=only&allow_quarantined=true&allow_over18=1&include=identity&after={POST}&dist=25&sort={View}&t={Period}&layout=classic"
ThrowAny(Token)
Wait429()
Dim r$ = Responser.GetResponse(URL)
If Not r.IsEmptyString Then
Err429Reset()
Using w As EContainer = JsonDocument.Parse(r).XmlIfNothing
If w.Count > 0 Then
n = w.GetNode(JsonNodesJson)
If Not n Is Nothing AndAlso n.Count > 0 Then
'n = w.GetNode(JsonNodesJson)
n = w.GetNode(ChannelJsonNodes)
If n.ListExists Then
ProgressPre.ChangeMax(n.Count)
For Each nn In n
ProgressPre.Perform()
ThrowAny(Token)
If nn.Count > 0 Then
If CheckNode(nn) Then
With nn("data")
If .ListExists Then
If CheckNode(.Self) Then
'Obtain post ID
PostTmp = nn.Name
If PostTmp.IsEmptyString Then PostTmp = nn.Value("id")
PostID = String.Empty
PostTmp = .Value("name") '.Name
If PostTmp.IsEmptyString Then PostTmp = .Value("id")
If PostTmp.IsEmptyString Then Continue For
'Check for CrossPost
If IsCrossPost(nn) Then
_CrossPosts.ListAddList({nn.Value(Node_CrosspostRootId),
nn.Value(Node_CrosspostParentId),
nn.Value(Node_CrosspostParent)}, LNC)
Continue For
If IsCrossPost(.Self) Then
_CrossPosts.ListAddList({ .Value(Node_CrosspostRootId),
.Value(Node_CrosspostParentId),
.Value(Node_CrosspostParent),
PostTmp}, LNC)
If ParseUserMediaOnly Then Continue For
Else
If Not _CrossPosts.Contains(PostTmp) Then PostID = PostTmp : PostTmp = String.Empty
End If
@@ -348,21 +398,24 @@ Namespace API.Reddit
If Not _CrossPosts.Contains(_PostID()) Then ExistsDetected = True
Continue For
End If
If nn.Contains("created") Then PostDate = nn("created").Value Else PostDate = String.Empty
PostDate = If(.Item("created")?.Value, String.Empty)
Select Case CheckDatesLimit(PostDate, DateTrueProvider(IsChannel))
Case DateResult.Skip : Continue For
Case DateResult.Exit : Exit Sub
End Select
ParseContainer(nn, _PostID(), PostDate)
ParseContainer(.Self, _PostID(), PostDate,,, GetTextDocument(.Self))
End If
End If
End With
Next
End If
End If
End Using
If POST.IsEmptyString And ExistsDetected Then Exit Sub
If Not _PostID().IsEmptyString And NewPostDetected Then DownloadDataUser(_PostID(), Token)
ElseIf Err429TryAgain Then
Continue Do
End If
_completed = True
Catch ex As Exception
@@ -399,9 +452,11 @@ Namespace API.Reddit
End If
ThrowAny(Token)
Wait429()
Dim r$ = Responser.GetResponse(URL)
If IsSavedPosts Then Err429Count = 0
'If IsSavedPosts Then Err429Count = 0
If Not r.IsEmptyString Then
Err429Reset()
Using w As EContainer = JsonDocument.Parse(r).XmlIfNothing
If w.Count > 0 Then
n = w.GetNode(ChannelJsonNodes)
@@ -450,7 +505,7 @@ Namespace API.Reddit
Continue For
End If
ParseContainer(s, PostID, PostDate, _UserID)
ParseContainer(s, PostID, PostDate, _UserID,, If(Not SaveToCache, GetTextDocument(s), String.Empty))
End If
Next
End If
@@ -458,6 +513,8 @@ Namespace API.Reddit
End Using
If POST.IsEmptyString And ExistsDetected Then Exit Sub
If Not PostID.IsEmptyString And NewPostDetected Then DownloadDataChannel(PostID, Token)
ElseIf Err429TryAgain Then
Continue Do
End If
_completed = True
Catch ex As Exception
@@ -475,11 +532,13 @@ Namespace API.Reddit
End Sub
#End Region
#Region "GetUserInfo"
Private Sub GetUserInfo()
Private Sub GetUserInfo(Optional ByVal Round As Integer = 0)
Try
If Not IsSavedPosts And ChannelInfo Is Nothing Then
Wait429()
Dim r$ = Responser.GetResponse($"https://reddit.com/{IIf(IsChannel, "r", "user")}/{NameTrue}/about.json",, EDP.ReturnValue)
If Not r.IsEmptyString Then
Err429Reset()
Using j As EContainer = JsonDocument.Parse(r)
If Not j Is Nothing AndAlso j.Contains({"data", "subreddit"}) Then
If ID.IsEmptyString Then ID = j.Value({"data"}, "id")
@@ -495,6 +554,8 @@ Namespace API.Reddit
End With
End If
End Using
ElseIf Err429TryAgain And Round < 2 Then
GetUserInfo(Round + 1)
End If
End If
Catch ex As Exception
@@ -503,7 +564,7 @@ Namespace API.Reddit
#End Region
#Region "ParseContainer"
Private Function ParseContainer(ByVal e As EContainer, ByVal PostID As String, ByVal PostDate As String, Optional ByVal UserID As String = Nothing,
Optional ByVal AllowReparse As Boolean = True) As Boolean
Optional ByVal AllowReparse As Boolean = True, Optional ByVal PostText As String = Nothing) As Boolean
If Not e Is Nothing Then
Dim UPicType As Func(Of String, UTypes) = Function(input) IIf(input = "image", UTypes.Picture, UTypes.GIF)
Dim eCount As Predicate(Of EContainer) = Function(item) item.Count > 0
@@ -513,24 +574,24 @@ Namespace API.Reddit
If SaveToCache Then
tmpUrl = e.Value({"media", "oembed"}, "thumbnail_url")
If Not tmpUrl.IsEmptyString Then
_TempMediaList.ListAddValue(MediaFromData(UTypes.Picture, tmpUrl, PostID, PostDate, UserID), LNC)
_TempMediaList.ListAddValue(MediaFromData(UTypes.Picture, tmpUrl, PostID, PostDate, UserID,, PostText), LNC)
_TotalPostsDownloaded += 1
Else
added = False
End If
Else
_TempMediaList.ListAddValue(MediaFromData(UTypes.VideoPre, tmpUrl, PostID, PostDate, UserID), LNC)
_TempMediaList.ListAddValue(MediaFromData(UTypes.VideoPre, tmpUrl, PostID, PostDate, UserID,, PostText), LNC)
_TotalPostsDownloaded += 1
End If
ElseIf CreateImgurMedia(tmpUrl, PostID, PostDate, UserID, IsChannel) Then
ElseIf CreateImgurMedia(tmpUrl, PostID, PostDate, UserID, IsChannel, PostText) Then
_TotalPostsDownloaded += 1
ElseIf DownloadGallery(e, PostID, PostDate, UserID, SaveToCache) Then
ElseIf DownloadGallery(e, PostID, PostDate, UserID, SaveToCache, PostText) Then
_TotalPostsDownloaded += 1
ElseIf Not If(e({"media"}, "type")?.Value, String.Empty).IsEmptyString Then
With e("media")
Dim t$ = .Item("type").Value
Select Case t
Case "gallery" : If DownloadGallery(.Self, PostID, PostDate) Then _TotalPostsDownloaded += 1 Else added = False
Case "gallery" : If DownloadGallery(.Self, PostID, PostDate,,, PostText) Then _TotalPostsDownloaded += 1 Else added = False
Case "image", "gifvideo"
Dim resolution As Sizes = Nothing
@@ -565,17 +626,17 @@ Namespace API.Reddit
End If
If Not chosenVal.IsEmptyString Then
_TempMediaList.ListAddValue(MediaFromData(UPicType(t), chosenVal, PostID, PostDate, UserID), LNC)
_TempMediaList.ListAddValue(MediaFromData(UPicType(t), chosenVal, PostID, PostDate, UserID,, PostText), LNC)
_TotalPostsDownloaded += 1
Else
added = False
End If
Case "video"
If UseM3U8 AndAlso .Item("hlsUrl").XmlIfNothingValue("/").ToLower.Contains("m3u8") Then
_TempMediaList.ListAddValue(MediaFromData(UTypes.m3u8, .Value("hlsUrl"), PostID, PostDate, UserID), LNC)
_TempMediaList.ListAddValue(MediaFromData(UTypes.m3u8, .Value("hlsUrl"), PostID, PostDate, UserID,, PostText), LNC)
_TotalPostsDownloaded += 1
ElseIf Not UseM3U8 AndAlso .Item("fallback_url").XmlIfNothingValue("/").ToLower.Contains("mp4") Then
_TempMediaList.ListAddValue(MediaFromData(UTypes.Video, .Value("fallback_url"), PostID, PostDate, UserID), LNC)
_TempMediaList.ListAddValue(MediaFromData(UTypes.Video, .Value("fallback_url"), PostID, PostDate, UserID,, PostText), LNC)
_TotalPostsDownloaded += 1
Else
added = False
@@ -588,16 +649,16 @@ Namespace API.Reddit
If SaveToCache Then
tmpUrl = GetVideoRedditPreview(e)
If Not tmpUrl.IsEmptyString Then
_TempMediaList.ListAddValue(MediaFromData(UTypes.Picture, tmpUrl, PostID, PostDate, UserID, False), LNC)
_TempMediaList.ListAddValue(MediaFromData(UTypes.Picture, tmpUrl, PostID, PostDate, UserID, False, PostText), LNC)
_TotalPostsDownloaded += 1
Else
added = False
End If
ElseIf UseM3U8 AndAlso Not If(e({"media", "reddit_video"}, "hls_url")?.Value, String.Empty).IsEmptyString Then
_TempMediaList.ListAddValue(MediaFromData(UTypes.m3u8, e.Value({"media", "reddit_video"}, "hls_url"), PostID, PostDate, UserID), LNC)
_TempMediaList.ListAddValue(MediaFromData(UTypes.m3u8, e.Value({"media", "reddit_video"}, "hls_url"), PostID, PostDate, UserID,, PostText), LNC)
_TotalPostsDownloaded += 1
Else
_TempMediaList.ListAddValue(MediaFromData(UTypes.Video, tmpUrl, PostID, PostDate, UserID), LNC)
_TempMediaList.ListAddValue(MediaFromData(UTypes.Video, tmpUrl, PostID, PostDate, UserID,, PostText), LNC)
_TotalPostsDownloaded += 1
End If
Else
@@ -606,20 +667,25 @@ Namespace API.Reddit
If Not added Then
If AllowReparse Then
If If(e.ItemF({"crosspost_parent_list", 0})?.Count, 0) > 0 Then
added = ParseContainer(e.ItemF({"crosspost_parent_list", 0}), PostID, PostDate, UserID, True)
added = ParseContainer(e.ItemF({"crosspost_parent_list", 0}), PostID, PostDate, UserID, True, PostText)
Else
Dim tPostId$ = e.Value(Node_CrosspostParent).IfNullOrEmpty(e.Value(Node_CrosspostParentId)).IfNullOrEmpty(e.Value(Node_CrosspostRootId))
If Not PostID.IsEmptyString Then
For ri% = 0 To 1
Wait429()
Dim r$ = Responser.GetResponse($"https://www.reddit.com/comments/{tPostId.Split("_").LastOrDefault}/.json",, EDP.ReturnValue)
If Not r.IsEmptyString Then
Err429Reset()
Using j As EContainer = JsonDocument.Parse(r, EDP.ReturnValue)
If j.ListExists Then
With j.ItemF({0, "data", "children", 0, "data"})
If .ListExists Then added = ParseContainer(.Self, PostID, PostDate, UserID, False)
If .ListExists Then added = ParseContainer(.Self, PostID, PostDate, UserID, False, PostText)
End With
End If
End Using
Exit For
End If
Next
End If
End If
End If
@@ -637,7 +703,7 @@ Namespace API.Reddit
End Select
End With
If Not tmpType = UTypes.Undefined Then
_TempMediaList.ListAddValue(MediaFromData(tmpType, node.Value, PostID, PostDate, UserID), LNC)
_TempMediaList.ListAddValue(MediaFromData(tmpType, node.Value, PostID, PostDate, UserID,, PostText), LNC)
added = True
End If
End If
@@ -660,7 +726,7 @@ Namespace API.Reddit
If Not tmpUrl.IsEmptyString Then tmpType = UTypes.Picture
End If
If Not tmpUrl.IsEmptyString And Not tmpType = UTypes.Undefined Then
Dim m As UserMedia = MediaFromData(tmpType, tmpUrl, PostID, PostDate, UserID)
Dim m As UserMedia = MediaFromData(tmpType, tmpUrl, PostID, PostDate, UserID,, PostText)
If tmpType = UTypes.Video Then m.File.Extension = "mp4"
_TempMediaList.ListAddValue(m, LNC)
_TotalPostsDownloaded += 1
@@ -671,6 +737,7 @@ Namespace API.Reddit
End If
End If
End If
If Not added And Not PostText.IsEmptyString Then _TempMediaList.ListAddValue(MediaFromData(UTypes.Text, String.Empty, PostID, PostDate, UserID,, PostText))
Return added
Else
Return False
@@ -693,28 +760,45 @@ Namespace API.Reddit
Return False
End Try
End Function
Private Function GetTextDocument(ByVal e As EContainer) As String
Dim t$ = String.Empty
Try
t = e.Value("title")
With e({"rtjson", "document"})
If .ListExists Then
For Each tt As EContainer In .Self
t.StringAppendLine(vbCrLf,, False)
t.StringAppendLine(If(tt.ItemF({"c", 0, "t"})?.Value, String.Empty))
Next
End If
End With
Catch
End Try
Return t
End Function
#End Region
#Region "Download Base Functions"
Private Function CreateImgurMedia(ByVal _URL As String, ByVal PostID As String, ByVal PostDate As String,
Optional ByVal _UserID As String = "", Optional ByVal IsChannel As Boolean = False) As Boolean
Optional ByVal _UserID As String = "", Optional ByVal IsChannel As Boolean = False,
Optional ByVal PostText As String = Nothing) As Boolean
If Not _URL.IsEmptyString AndAlso _URL.Contains("imgur") Then
If _URL.StringContains({".jpg", ".png", ".jpeg"}) Then
_TempMediaList.ListAddValue(MediaFromData(UTypes.Picture, _URL, PostID, PostDate, _UserID), LNC)
_TempMediaList.ListAddValue(MediaFromData(UTypes.Picture, _URL, PostID, PostDate, _UserID,, PostText), LNC)
ElseIf _URL.Contains(".gifv") Then
If SaveToCache Then
_TempMediaList.ListAddValue(MediaFromData(UTypes.Picture, _URL.Replace(".gifv", ".gif"), PostID, PostDate, _UserID), LNC)
_TempMediaList.ListAddValue(MediaFromData(UTypes.Picture, _URL.Replace(".gifv", ".gif"), PostID, PostDate, _UserID,, PostText), LNC)
Else
_TempMediaList.ListAddValue(MediaFromData(UTypes.Video, _URL.Replace(".gifv", ".mp4"), PostID, PostDate, _UserID), LNC)
_TempMediaList.ListAddValue(MediaFromData(UTypes.Video, _URL.Replace(".gifv", ".mp4"), PostID, PostDate, _UserID,, PostText), LNC)
End If
ElseIf _URL.Contains(".mp4") Then
_TempMediaList.ListAddValue(MediaFromData(UTypes.Video, _URL, PostID, PostDate, _UserID), LNC)
_TempMediaList.ListAddValue(MediaFromData(UTypes.Video, _URL, PostID, PostDate, _UserID,, PostText), LNC)
ElseIf _URL.Contains(".gif") Then
_TempMediaList.ListAddValue(MediaFromData(UTypes.GIF, _URL, PostID, PostDate, _UserID), LNC)
_TempMediaList.ListAddValue(MediaFromData(UTypes.GIF, _URL, PostID, PostDate, _UserID,, PostText), LNC)
Else
Dim obj As IEnumerable(Of UserMedia) = Imgur.Envir.GetVideoInfo(_URL, EDP.ReturnValue)
If Not obj.ListExists Then
If Not TryFile(_URL) Then _URL &= ".jpg"
_TempMediaList.ListAddValue(MediaFromData(UTypes.Picture, _URL, PostID, PostDate, _UserID), LNC)
_TempMediaList.ListAddValue(MediaFromData(UTypes.Picture, _URL, PostID, PostDate, _UserID,, PostText), LNC)
Else
Dim ut As UTypes
Dim m As UserMedia
@@ -729,7 +813,7 @@ Namespace API.Reddit
Case "gif" : ut = UTypes.GIF
Case Else : ut = UTypes.Picture : .File.Extension = "jpg"
End Select
m = MediaFromData(ut, _URL, PostID, PostDate, _UserID)
m = MediaFromData(ut, _URL, PostID, PostDate, _UserID,, PostText)
m.URL = .URL
m.File = .File.File
_TempMediaList.ListAddValue(m, LNC)
@@ -745,7 +829,8 @@ Namespace API.Reddit
End If
End Function
Private Function DownloadGallery(ByVal e As EContainer, ByVal PostID As String, ByVal PostDate As String,
Optional ByVal _UserID As String = Nothing, Optional ByVal FirstOnly As Boolean = False) As Boolean
Optional ByVal _UserID As String = Nothing, Optional ByVal FirstOnly As Boolean = False,
Optional ByVal PostText As String = Nothing) As Boolean
Try
Dim added As Boolean = False
Dim node As EContainer = Nothing
@@ -759,7 +844,7 @@ Namespace API.Reddit
For Each n As EContainer In node
t = n.ItemF({"s", "u"})
If Not t Is Nothing AndAlso Not t.Value.IsEmptyString Then
_TempMediaList.ListAddValue(MediaFromData(UTypes.Picture, t.Value, PostID, PostDate, _UserID), LNC)
_TempMediaList.ListAddValue(MediaFromData(UTypes.Picture, t.Value, PostID, PostDate, _UserID,, PostText), LNC)
added = True
If FirstOnly Then Exit For
End If
@@ -866,7 +951,10 @@ Namespace API.Reddit
End If
Continue For
Else
Wait429()
r = Responser.GetResponse(m.URL,, e)
If r.IsEmptyString And Err429TryAgain Then _repeatForRedgifs = True
If Not r.IsEmptyString Then Err429Reset()
End If
Loop While _repeatForRedgifs
Else
@@ -876,7 +964,7 @@ Namespace API.Reddit
If Not r.IsEmptyString Then
v = RegexReplace(r, VideoRegEx)
If Not v.IsEmptyString Then
_TempMediaList(i) = New UserMedia With {.Type = UTypes.Video, .URL = v, .File = v, .Post = m.Post}
_TempMediaList(i) = New UserMedia With {.Type = UTypes.Video, .URL = v, .File = v, .Post = m.Post, .PostText = m.PostText, .PostTextFile = m.PostTextFile}
Else
_TempMediaList.RemoveAt(i)
End If
@@ -904,11 +992,13 @@ Namespace API.Reddit
RedGifsResponser = RedGifsHost.Responser.Copy
Dim respNoHeaders As Responser = Responser.Copy
Dim m As UserMedia, m2 As UserMedia
Dim r$, url$
Dim r$ = String.Empty, url$
Dim ri As Byte
Dim j As EContainer
Dim lastCount%, li%
Dim rv As New ErrorsDescriber(EDP.ReturnValue)
respNoHeaders.Headers.Clear()
respNoHeaders.ProcessExceptionDecision = AddressOf Err429Process
ProgressPre.ChangeMax(_ContentList.Count)
For i% = 0 To _ContentList.Count - 1
m = _ContentList(i)
@@ -916,15 +1006,20 @@ Namespace API.Reddit
If m.State = UStates.Missing AndAlso Not m.Post.ID.IsEmptyString Then
ThrowAny(Token)
url = $"https://www.reddit.com/comments/{m.Post.ID.Split("_").LastOrDefault}/.json"
For ri = 0 To 1
Wait429()
r = Responser.GetResponse(url,, rv)
If r.IsEmptyString Then r = respNoHeaders.GetResponse(url,, rv)
If r.IsEmptyString Then Wait429() : r = respNoHeaders.GetResponse(url,, rv)
If Not (r.IsEmptyString And Err429TryAgain) Then Exit For
Next
If Not r.IsEmptyString Then
Err429Reset()
j = JsonDocument.Parse(r, rv)
If Not j Is Nothing Then
If j.Count > 0 Then
lastCount = _TempMediaList.Count
With j.GetNode(SingleJsonNodes)
If .ListExists AndAlso ParseContainer(.Self, m.Post.ID, String.Empty) Then
If .ListExists AndAlso ParseContainer(.Self, m.Post.ID, String.Empty,,, GetTextDocument(.Self)) Then
If lastCount <> _TempMediaList.Count Then
For li = IIf(lastCount < 0, 0, lastCount) To _TempMediaList.Count - 1
m2 = _TempMediaList(i)
@@ -971,13 +1066,15 @@ Namespace API.Reddit
#End Region
#Region "Structure creator"
Private Function MediaFromData(ByVal t As UTypes, ByVal _URL As String, ByVal PostID As String, ByVal PostDate As String,
Optional ByVal _UserID As String = "", Optional ByVal ReplacePreview As Boolean = True) As UserMedia
Optional ByVal _UserID As String = "", Optional ByVal ReplacePreview As Boolean = True,
Optional ByVal PostText As String = Nothing) As UserMedia
If _URL.IsEmptyString And t = UTypes.Picture Then Return Nothing
_URL = LinkFormatterSecure(RegexReplace(_URL.Replace("\", String.Empty), LinkPattern))
Dim m As New UserMedia(_URL, t) With {.Post = New UserPost With {.ID = PostID, .UserID = _UserID}}
If t = UTypes.Picture Or t = UTypes.GIF Then m.File = CreateFileFromUrl(m.URL) Else m.File = Nothing
If ReplacePreview And m.URL.Contains("preview") And Not t = UTypes.Picture Then m.URL = $"https://i.redd.it/{m.File.File}"
If Not PostDate.IsEmptyString Then m.Post.Date = AConvert(Of Date)(PostDate, DateTrueProvider(IsChannel Or IsSavedPosts), Nothing) Else m.Post.Date = Nothing
If Not PostText.IsEmptyString Then m.PostText = PostText
Return m
End Function
Private Function TryFile(ByVal URL As String) As Boolean
@@ -1048,25 +1145,37 @@ Namespace API.Reddit
ElseIf .StatusCode = HttpStatusCode.Forbidden Then '403
UserSuspended = True
ElseIf .StatusCode = HttpStatusCode.BadGateway Or .StatusCode = HttpStatusCode.ServiceUnavailable Then '502, 503
MyMainLOG = $"{ToStringForLog()}: [{CInt(Responser.StatusCode)}] Reddit is currently unavailable"
LogError(Nothing, $"[{CInt(Responser.StatusCode)}] Reddit is currently unavailable")
Throw New Plugin.ExitException With {.Silent = True}
ElseIf .StatusCode = HttpStatusCode.GatewayTimeout Then '504
Return 1
ElseIf .StatusCode = HttpStatusCode.Unauthorized Then '401
MyMainLOG = $"{ToStringForLog()}: [{CInt(Responser.StatusCode)}] Reddit credentials expired"
LogError(Nothing, $"[{CInt(Responser.StatusCode)}] Reddit credentials expired")
MySiteSettings.SessionInterrupted = True
Throw New Plugin.ExitException With {.Silent = True}
ElseIf .StatusCode = HttpStatusCode.InternalServerError Then '500
If Not IsNothing(EObj) AndAlso IsNumeric(EObj) AndAlso CInt(EObj) = HttpStatusCode.InternalServerError Then Return 1
Return HttpStatusCode.InternalServerError
ElseIf .StatusCode = 429 And IsSavedPosts And Err429Count = 0 Then
Err429Count += 1
Return 429
ElseIf .StatusCode = 429 AndAlso
((Not IsSavedPosts And CBool(MySiteSettings.UseTokenForTimelines.Value)) Or (IsSavedPosts And CBool(MySiteSettings.UseTokenForSavedPosts.Value))) AndAlso
Not MySiteSettings.CredentialsExists Then '429
MyMainLOG = $"{ToStringForLog()}: [{CInt(Responser.StatusCode)}] You should use OAuth authorization or disable " &
IIf(IsSavedPosts, "token usage for downloading saved posts", "the use of token and cookies for downloading timelines")
'ElseIf .StatusCode = 429 And IsSavedPosts And Err429Count = 0 Then '429 (saved)
' Err429Count += 1
' Return 429
ElseIf .StatusCode = 429 Then '429 (all)
'If ((Not IsSavedPosts And CBool(MySiteSettings.UseTokenForTimelines.Value)) Or (IsSavedPosts And CBool(MySiteSettings.UseTokenForSavedPosts.Value))) AndAlso
' Not MySiteSettings.CredentialsExists Then
' LogError(Nothing, "[429] You should use OAuth authorization or disable " &
' IIf(IsSavedPosts, "token usage for downloading saved posts", "the use of token and cookies for downloading timelines"))
'Else
' LogError(Nothing, "Too many requests (429). Try again later!")
'End If
'MySiteSettings.SessionInterrupted = True
'Throw New Plugin.ExitException With {.Silent = True}
If ((Not IsSavedPosts And CBool(MySiteSettings.UseTokenForTimelines.Value)) Or (IsSavedPosts And CBool(MySiteSettings.UseTokenForSavedPosts.Value))) AndAlso
Not MySiteSettings.CredentialsExists Then
LogError(Nothing, "[429] You should use OAuth authorization or disable " &
IIf(IsSavedPosts, "token usage for downloading saved posts", "the use of token and cookies for downloading timelines"))
Else
LogError(Nothing, "Too many requests (429). Try again later!")
End If
MySiteSettings.SessionInterrupted = True
Throw New Plugin.ExitException With {.Silent = True}
Else

View File

@@ -22,6 +22,7 @@ Namespace API.RedGifs
Friend ReadOnly Property Token As PropertyValue
<PropertyOption, ControlNumber(2), PClonable, HiddenControl>
Private ReadOnly Property UserAgent As PropertyValue
<PXML> Friend ReadOnly Property UseCookies As PropertyValue
<PXML> Friend ReadOnly Property TokenLastDateUpdated As PropertyValue
Private Const TokenName As String = "authorization"
#Region "TokenUpdateInterval"
@@ -31,6 +32,9 @@ Namespace API.RedGifs
<Provider(NameOf(TokenUpdateInterval), FieldsChecker:=True)>
Private ReadOnly Property TokenUpdateIntervalProvider As IFormatProvider
#End Region
<DoNotUse> Friend Overrides Property DownloadText As PropertyValue
<DoNotUse> Friend Overrides Property DownloadTextPosts As PropertyValue
<DoNotUse> Friend Overrides Property DownloadTextSpecialFolder As PropertyValue
#End Region
#Region "Initializer"
Friend Sub New(ByVal AccName As String, ByVal Temp As Boolean)
@@ -44,6 +48,7 @@ Namespace API.RedGifs
End With
Token = New PropertyValue(t, GetType(String), Sub(v) UpdateResponse(NameOf(Token), v))
UserAgent = New PropertyValue(If(Responser.UserAgentExists, Responser.UserAgent, String.Empty), GetType(String), Sub(v) UpdateResponse(NameOf(UserAgent), v))
UseCookies = New PropertyValue(False)
TokenLastDateUpdated = New PropertyValue(Now.AddYears(-1), GetType(Date))
TokenUpdateInterval = New PropertyValue(60 * 12, GetType(Integer))
TokenUpdateIntervalProvider = New TokenRefreshIntervalProvider
@@ -59,11 +64,16 @@ Namespace API.RedGifs
Case NameOf(Token) : Responser.Headers.Add(TokenName, Value)
Case NameOf(UserAgent) : Responser.UserAgent = Value
End Select
Responser.SaveSettings()
Responser.SaveSettings(, New ErrorsDescriber(EDP.ReturnValue + If(_TokenUpdating, EDP.None, EDP.SendToLog)))
End Sub
#End Region
#Region "Token updaters"
Private _TokenUpdating As Boolean = False
Friend Function UpdateTokenIfRequired() As Boolean
While _TokenUpdating : Threading.Thread.Sleep(100) : End While
Return UpdateTokenIfRequired_Impl()
End Function
Private Function UpdateTokenIfRequired_Impl() As Boolean
Dim d As Date? = AConvert(Of Date)(TokenLastDateUpdated.Value, AModes.Var, Nothing)
If Not d.HasValue OrElse d.Value < Now.AddMinutes(-CInt(TokenUpdateInterval.Value)) Then
Return UpdateToken()
@@ -73,7 +83,12 @@ Namespace API.RedGifs
End Function
<PropertyUpdater(NameOf(Token))>
Friend Function UpdateToken() As Boolean
While _TokenUpdating : Threading.Thread.Sleep(100) : End While
Return UpdateToken_Impl()
End Function
Private Function UpdateToken_Impl() As Boolean
Try
_TokenUpdating = True
Dim r$
Dim NewToken$ = String.Empty, NewAgent$ = String.Empty
Using resp As New Responser : r = resp.GetResponse("https://api.redgifs.com/v2/auth/temporary",, EDP.ThrowException) : End Using
@@ -95,6 +110,8 @@ Namespace API.RedGifs
End If
Catch ex As Exception
Return ErrorsDescriber.Execute(EDP.SendToLog, ex, "[API.RedGifs.SiteSettings.UpdateToken]", False)
Finally
_TokenUpdating = False
End Try
End Function
#End Region

View File

@@ -36,6 +36,7 @@ Namespace API.RedGifs
#End Region
#Region "Download functions"
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
If Not MySettings.UseCookies.Value Then Responser.Cookies.Clear()
DownloadData(1, Token)
End Sub
Private Overloads Sub DownloadData(ByVal Page As Integer, ByVal Token As CancellationToken)

View File

@@ -27,6 +27,9 @@ Namespace API.ThisVid
"If true, then public videos will be stored in the 'Public' folder, private - in the 'Private' folder." & vbCr &
"If false, all videos will be stored in the 'Video' folder."), PClonable>
Friend ReadOnly Property DifferentFolders As PropertyValue
<DoNotUse> Friend Overrides Property DownloadText As PropertyValue
<DoNotUse> Friend Overrides Property DownloadTextPosts As PropertyValue
<DoNotUse> Friend Overrides Property DownloadTextSpecialFolder As PropertyValue
#End Region
#Region "Initializer"
Friend Sub New(ByVal AccName As String, ByVal Temp As Boolean)

Some files were not shown because too many files have changed in this diff Show More