Compare commits
35 Commits
2023.12.15
...
2024.5.4.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ec2266f1bf | ||
|
|
7d9255c916 | ||
|
|
5b5857e31d | ||
|
|
46372ec9fb | ||
|
|
7296fda977 | ||
|
|
5f90bf6a99 | ||
|
|
718eccc3c3 | ||
|
|
efa09fb457 | ||
|
|
b252d32a7e | ||
|
|
34cd510507 | ||
|
|
f5dd791941 | ||
|
|
2a2c12c651 | ||
|
|
2bacc17ac4 | ||
|
|
7a68067d77 | ||
|
|
d1eacc2db2 | ||
|
|
3f38803643 | ||
|
|
09760a6926 | ||
|
|
75039ac4d2 | ||
|
|
03e3a07947 | ||
|
|
d283d9c9f9 | ||
|
|
b9100bd3c1 | ||
|
|
0ea2156ada | ||
|
|
520280b038 | ||
|
|
10516f229b | ||
|
|
c3f0831768 | ||
|
|
74a0404670 | ||
|
|
52a43b9207 | ||
|
|
5bc559c448 | ||
|
|
b37f641582 | ||
|
|
e00dfec701 | ||
|
|
94edf23570 | ||
|
|
0246af9b69 | ||
|
|
c458f1cd1d | ||
|
|
e3da1bf1d3 | ||
|
|
dde024276b |
@@ -5,7 +5,13 @@ I welcome requests! Follow these steps to contribute:
|
|||||||
1. Find an [issue](https://github.com/AAndyProgram/SCrawler/issues) that needs assistance.
|
1. Find an [issue](https://github.com/AAndyProgram/SCrawler/issues) that needs assistance.
|
||||||
1. Let me know you are working on it by posting a comment on the issue.
|
1. Let me know you are working on it by posting a comment on the issue.
|
||||||
1. If you find an error in the code, please provide a link to the file and the line number.
|
1. If you find an error in the code, please provide a link to the file and the line number.
|
||||||
1. If you have a code change suggestion, you can post a replacement code block. I also accept pull requests.
|
1. If you have a code change suggestion, you can post a replacement code block.<!-- I also accept pull requests.-->
|
||||||
|
|
||||||
|
# How to report a problem
|
||||||
|
1. Attach the **profile URLs or links** that you cannot download.
|
||||||
|
1. Attach the **LOG** if it exists.
|
||||||
|
1. **Attach the environment information copied from SCrawler (click the top right info button in the main window, then the `Environment` button, then the `Copy` button, and paste the copied text into the issue).**
|
||||||
|
1. *Add screenshots to illustrate the problem (**optional**)*
|
||||||
|
|
||||||
# How to build from source
|
# How to build from source
|
||||||
1. Delete the `PersonalUtilities` project from the solution.
|
1. Delete the `PersonalUtilities` project from the solution.
|
||||||
|
|||||||
224
Changelog.md
@@ -1,3 +1,227 @@
|
|||||||
|
# 2024.5.4.0
|
||||||
|
|
||||||
|
*2024-05-04*
|
||||||
|
|
||||||
|
- Added
|
||||||
|
- YouTube (standalone app): setting to remove specific characters (`Defaults` - `Remove characters`)
|
||||||
|
- Instagram: simplify the `Connection closed` error
|
||||||
|
- Users search: add 'FriendlyName' to search results
|
||||||
|
- Fixed
|
||||||
|
- YouTube (standalone app): incorrect download processing when the file name ends with a dot (Issue #188)
|
||||||
|
- The program is freezes when editing users in some cases
|
||||||
|
- Sites
|
||||||
|
- Reddit: token update error
|
||||||
|
- Threads: unable to obtain credentials (ID)
|
||||||
|
|
||||||
|
# 2024.4.26.0
|
||||||
|
|
||||||
|
*2024-04-26*
|
||||||
|
|
||||||
|
- Added
|
||||||
|
- Site settings: the values that can be extracted from cookies immediately populate fields
|
||||||
|
- Feed: ability to load the last session of the current day (if it exists) as the current session after restarting SCrawler
|
||||||
|
- Users search: include friendly name matches in search result
|
||||||
|
- Updated
|
||||||
|
- gallery-dl up to version **1.26.9**
|
||||||
|
- Fixed
|
||||||
|
- xHamster: **saved posts aren't downloading**
|
||||||
|
|
||||||
|
# 2024.4.14.0
|
||||||
|
|
||||||
|
*2024-04-14*
|
||||||
|
|
||||||
|
- Fixed
|
||||||
|
- Facebook: can't get tokens
|
||||||
|
|
||||||
|
# 2024.4.13.0
|
||||||
|
|
||||||
|
*2024-04-13*
|
||||||
|
|
||||||
|
- Added
|
||||||
|
- Minor improvements
|
||||||
|
- PluginProvider
|
||||||
|
- IPluginContentProvider: added `ResetHistoryData` function
|
||||||
|
- Fixed
|
||||||
|
- Sites
|
||||||
|
- TikTok: remove last download date when erasing history data
|
||||||
|
- YouTube: remove last download date when erasing history data
|
||||||
|
- Instagram: **saved posts aren't downloading**
|
||||||
|
|
||||||
|
# 2024.4.10.0
|
||||||
|
|
||||||
|
*2024-04-10*
|
||||||
|
|
||||||
|
**For those of you who use TikTok, it is highly recommended to update TikTok plugin (using [these instructions](https://github.com/AAndyProgram/SCrawler/wiki/Settings#how-to-install-yt-dlp-ttuser-plugin)) to the latest version!**
|
||||||
|
|
||||||
|
**ATTENTION! This version includes changes to downloading groups (including the scheduler) and the settings file. Once you start using it, you won't be able to downgrade. I recommend making a backup of your SCrawler settings folder. It is also recommended to check all of your download groups settings and scheduler plans settings.**
|
||||||
|
|
||||||
|
- Added
|
||||||
|
- Sites
|
||||||
|
- TikTok: more settings for standalone downloader
|
||||||
|
- Instagram:
|
||||||
|
- automatically reset download options after updating credentials
|
||||||
|
- **ability to extract an image (if it exists) from a video that contains a static image**
|
||||||
|
- updated reels downloading function
|
||||||
|
- GraphQL support
|
||||||
|
- request timers (per request)
|
||||||
|
- OnlyFans:
|
||||||
|
- **download stories**
|
||||||
|
- option to disable timeline downloading
|
||||||
|
- Reddit: added `Check image` setting (unchecked by default) to make Reddit downloading faster
|
||||||
|
- **YouTube (standalone app)**:
|
||||||
|
- **the ability to add downloaded item(s) to a playlist**
|
||||||
|
- **the ability to create a playlist from downloaded music**
|
||||||
|
- calculation and display of the actual size of downloaded files
|
||||||
|
- **the ability to change audio bitrate** *(you can also set the default bitrate in `Defaults Audio` - `Bitrate`)*
|
||||||
|
- **embed thumbnail in the extracted audio (`mp3` only) as cover art** (Settings: `Defaults Audio` - `Embed thumbnail (extracted files)`)
|
||||||
|
- Exclude `drc` *(dynamic range compression)* from parsing results
|
||||||
|
- Standalone downloader:
|
||||||
|
- allow thumbnail to be saved with file
|
||||||
|
- calculation and display of the actual size of downloaded files
|
||||||
|
- Feed:
|
||||||
|
- add hotkeys: `Home`, `End`, `Up`, `Page Up`, `Down`, `Page Down`
|
||||||
|
- ability to save/load views
|
||||||
|
- Scheduler: **`All` and `Default` options removed.** *Only `Disabled`, `Specified` and `Groups` are available.*
|
||||||
|
- Download groups: **filter users who have been (not)downloaded in the last `x` days** *(download groups, advanced filter (main window), scheduler)*.
|
||||||
|
- Main window:
|
||||||
|
- ability to save/load views (`View` - `Save/Load view`)
|
||||||
|
- **all filter options from the `View` menu have now been moved to `Filter`**
|
||||||
|
- Settings:
|
||||||
|
- ability to confirm mass download using the `F6` key *(to protect against accidental pressing) (`Settings` - `Behavior`)*
|
||||||
|
- the ability to find the program environment
|
||||||
|
- default headers (`Settings` - `Headers`)
|
||||||
|
- Added the ability to display group users *(works in scheduler, scheduler plans, download groups)*
|
||||||
|
- Added exclusion of last 3 days from deleting session files
|
||||||
|
- Other improvements
|
||||||
|
- Updated
|
||||||
|
- **yt-dlp up to version 2024.04.09**
|
||||||
|
- PluginProvider
|
||||||
|
- Add `PropertyValueEventArgs` class
|
||||||
|
- PropertyValue:
|
||||||
|
- add `Checked` parameter
|
||||||
|
- add `OnCheckboxCheckedChange` handler
|
||||||
|
- ISiteSettings:
|
||||||
|
- add `CMDEncoding`, `EnvironmentPrograms` properties
|
||||||
|
- add `EnvironmentProgramsUpdated` function
|
||||||
|
- Fixed
|
||||||
|
- Sites
|
||||||
|
- PornHub: some videos won't download
|
||||||
|
- xHamster:
|
||||||
|
- some videos are missing when downloading creators
|
||||||
|
- user videos aren't downloading
|
||||||
|
- **JustForFans: fixed video downloading**
|
||||||
|
- TikTok (standalone downloader): files with long names aren't downloaded
|
||||||
|
- Users: incorrect decision to set last update date, which resulted in an incorrect last download date for some users
|
||||||
|
- Feed: a scrolling bug where the feed scrolls up after returning to it
|
||||||
|
- Minor bugs
|
||||||
|
|
||||||
|
# 2024.2.25.0
|
||||||
|
|
||||||
|
*2024-02-25*
|
||||||
|
|
||||||
|
- Added
|
||||||
|
- A `Feed` button has been added to notifications
|
||||||
|
- Feed:
|
||||||
|
- ability to merge multiple special feeds into one
|
||||||
|
- ability to select all/none media
|
||||||
|
- ability to add to a special feed(s) with removal from the current one
|
||||||
|
- the name of the loaded feed is now displayed in the form title
|
||||||
|
- `Refresh` button now refreshes the loaded feed
|
||||||
|
- ability to move/copy media
|
||||||
|
- Scheduler: the ability to move tasks (higher, lower) *(just a view attribute, doesn't affect the scheduler)*
|
||||||
|
- YouTube (standalone app): add `Open file` to the context menu
|
||||||
|
- YouTube (standalone app): **the ability to edit each playlist item**
|
||||||
|
- YouTube (standalone app): **embed thumbnail in the audio/video as cover art** (Settings: `Defaults Audio` - `Embed thumbnail`; `Defaults Video` - `Embed thumbnail (video)`)
|
||||||
|
- Instagram: the `csrftoken` can now be automatically extracted from cookies
|
||||||
|
- Instagram: remove `x-ig-www-claim` from settings
|
||||||
|
- Threads: the `csrftoken` can now be automatically extracted from cookies
|
||||||
|
- Threads: simplify 500 error when updating tokens
|
||||||
|
- Facebook: simplify token update errors
|
||||||
|
- OnlyFans: handle 500 error
|
||||||
|
- Plugins: added `ReplaceInternalPluginAttribute` attribute
|
||||||
|
- Other improvements
|
||||||
|
- Fixed
|
||||||
|
- Main window: incorrect sorting of profiles and collections
|
||||||
|
- Standalone downloader: url array form doesn't show scrollbars
|
||||||
|
- Feed: image rendering bug
|
||||||
|
- YouTube (standalone app): audio codec does not change when changing audio/video in the video options form
|
||||||
|
- Instagram: error downloading single post
|
||||||
|
- TikTok: files with long names aren't downloaded
|
||||||
|
- Minor bugs
|
||||||
|
|
||||||
|
# 2024.1.26.0
|
||||||
|
|
||||||
|
*2024-01-26*
|
||||||
|
|
||||||
|
- Added
|
||||||
|
- YouTube (standalone app): **the ability to reduce video FPS**
|
||||||
|
- TikTok: the ability to use a regex to clean the title
|
||||||
|
- YouTube (SCrawler): the ability to ignore community errors
|
||||||
|
- Fixed
|
||||||
|
- Instagram: stories (user) downloading with the wrong aspect ratio for some users
|
||||||
|
- Minor bugs
|
||||||
|
|
||||||
|
# 2024.1.20.0
|
||||||
|
|
||||||
|
*2024-01-20*
|
||||||
|
|
||||||
|
- Added
|
||||||
|
- Instagram: **the ability to download reels**
|
||||||
|
- LPSG: handle 404 error
|
||||||
|
|
||||||
|
# 2024.1.18.0
|
||||||
|
|
||||||
|
*2024-01-18*
|
||||||
|
|
||||||
|
- Fixed
|
||||||
|
- Main window: incorrect collection sorting
|
||||||
|
- xHamster: some user videos were not downloaded
|
||||||
|
- YouTube (standalone app): URL array form doesn't show scrollbars
|
||||||
|
- Minor bugs
|
||||||
|
|
||||||
|
# 2024.1.12.1
|
||||||
|
|
||||||
|
*2024-01-12*
|
||||||
|
|
||||||
|
- Added
|
||||||
|
- YouTube (SCrawler): data downloading by dates
|
||||||
|
- Feed: ability to merge multiple session feeds into one
|
||||||
|
- Feed: remove session number from special feeds
|
||||||
|
- Fixed
|
||||||
|
- **Instagram**: stories (user) downloading with the wrong aspect ratio for some users
|
||||||
|
- YouTube: incorrect opening of a post from the feed
|
||||||
|
- YouTube: wrong date to data parsing
|
||||||
|
|
||||||
|
# 2024.1.12.0
|
||||||
|
|
||||||
|
*2024-01-12*
|
||||||
|
|
||||||
|
- Added
|
||||||
|
- Feed: added a prompt before clearing the current session
|
||||||
|
- xHamster: creators
|
||||||
|
- YouTube communities: add error to log
|
||||||
|
- Added scheduler to tray menu
|
||||||
|
- Other improvements
|
||||||
|
- Fixed
|
||||||
|
- Feed: there is no option to create a new feed when adding checked items
|
||||||
|
- **Instagram**: downloading of tagged posts
|
||||||
|
- xHamster: profiles are not downloading
|
||||||
|
- Minor bugs
|
||||||
|
|
||||||
|
# 2023.12.27.0
|
||||||
|
|
||||||
|
*2023-12-27*
|
||||||
|
|
||||||
|
- Added
|
||||||
|
- Notification of new log data
|
||||||
|
- OnlyFans: **OF-Scrapper support to download DRM protected videos**
|
||||||
|
- Other improvements
|
||||||
|
- Fixed
|
||||||
|
- The default options are changed (`Favorite`, `Temporary`, etc.) when changing an account for a created user
|
||||||
|
- When changing the account for a created user, the new account does not apply to that user until SCrawler is restarted
|
||||||
|
- 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*
|
*2023-12-15*
|
||||||
|
|||||||
19
FAQ.md
@@ -14,8 +14,6 @@ Any other questions I will keep in this file.
|
|||||||
|
|
||||||
A: https://github.com/AAndyProgram/SCrawler/wiki/Settings#how-to-set-up-cookies
|
A: https://github.com/AAndyProgram/SCrawler/wiki/Settings#how-to-set-up-cookies
|
||||||
|
|
||||||
<!---**ATTENTION! If you need to use cookies but cannot import them, I highly recommend that you don't use SCrawler and use another program. Don't create issues, discussions, or write to me on Discord. Any issue or discussion about cookies will be deleted immediately without a response. Any user who asks me about cookies on Discord will be banned.**--->
|
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
#### Q: **Does this program have GUI or CLI.**
|
#### Q: **Does this program have GUI or CLI.**
|
||||||
@@ -41,13 +39,16 @@ A: NO.
|
|||||||
|
|
||||||
A: Check your credentials and **[SITES REQUIREMENTS](https://github.com/AAndyProgram/SCrawler/wiki/Settings#sites-requirements)**. If all settings are set, but nothing works, go to [create a new issue](https://github.com/AAndyProgram/SCrawler/issues). Don't forget to attach the LOG.
|
A: Check your credentials and **[SITES REQUIREMENTS](https://github.com/AAndyProgram/SCrawler/wiki/Settings#sites-requirements)**. If all settings are set, but nothing works, go to [create a new issue](https://github.com/AAndyProgram/SCrawler/issues). Don't forget to attach the LOG.
|
||||||
|
|
||||||
|
**You also can join our Discord server**: https://discord.gg/uFNUXvFFmg
|
||||||
|
<br/>*You can get help faster there!*
|
||||||
|
|
||||||
**ATTENTION! Issues without URLs will be closed without a response!**
|
**ATTENTION! Issues without URLs will be closed without a response!**
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
#### Q: **I have set credentials but still nothing is downloading**
|
#### Q: **I have set credentials but still nothing is downloading**
|
||||||
|
|
||||||
A: Click the ```Start downloading``` button
|
A: Click the `Start downloading` button or press `F5`
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
@@ -59,7 +60,7 @@ A: https://github.com/AAndyProgram/SCrawler/releases/latest
|
|||||||
|
|
||||||
#### Q: **How to run the program?**
|
#### Q: **How to run the program?**
|
||||||
|
|
||||||
A: Double-click ```SCrawler.exe```
|
A: Double-click `SCrawler.exe`
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
@@ -77,19 +78,19 @@ A: The program stored posts IDs in users' folders. For the first time, the progr
|
|||||||
|
|
||||||
#### Q: **How to redownload all data**
|
#### Q: **How to redownload all data**
|
||||||
|
|
||||||
A: Double-click on the user you want to redownload. In the opened window open folder setting. Delete the files ending with ```_Data.xml``` and ```_Posts.txt```. Restart SCrawler. Download this user again.
|
A: https://github.com/AAndyProgram/SCrawler/wiki#redownload-user
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
#### Q: **How to remove the label**
|
#### Q: **How to remove the label**
|
||||||
|
|
||||||
A: There is no functionality to remove an individual label. You can open the ```Labels.txt``` file in the program settings folder and delete any label you want. You also can delete this file (```Labels.txt```). In this case, when the program starts, the list of labels list will be updated with only existing labels (from the user data files).
|
A: There is no functionality to remove an individual label. You can open the `Labels.txt` file in the program settings folder and delete any label you want. You also can delete this file (`Labels.txt`). In this case, when the program starts, the list of labels list will be updated with only existing labels (from the user data files).
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
#### Q: **How to remove a user from the blacklist**
|
#### Q: **How to remove a user from the blacklist**
|
||||||
|
|
||||||
A: Just add that user back to the program. In the dialog box that opens, click on the ```Add and remove from blacklist``` button.
|
A: Just add that user back to the program. In the dialog box that opens, click on the `Add and remove from blacklist` button.
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
@@ -113,8 +114,8 @@ A: I can only [suggest](#q-you-lost-me-your-program-is-too-complicated) you find
|
|||||||
|
|
||||||
#### Q: **Can you add a step-by-step guide or video on how to use the program?**
|
#### Q: **Can you add a step-by-step guide or video on how to use the program?**
|
||||||
|
|
||||||
A: **NO! NEVER!** The guide fully covers all the functionality of SCrawler! If you don't respect my work, I don't waste my time. If you want, you can create a video tutorial and send it to me. Then I add it. All options and what each option does described on the wiki. The wiki also contains a description of all settings and how-to configure them. For complex settings, there is a steep-by-steep guide. Read the [main](README.md) information and [GUIDE](https://github.com/AAndyProgram/SCrawler/wiki/) and you won't have any problems. I have developed a program with an intuitive interface. There is a Settings button, download buttons, a context menu that drops down when a user is clicked, and other controls. Anyone can use it.
|
A: **NO!** The guide fully covers all the functionality of SCrawler! If you don't respect my work, I don't waste my time. If you want, you can create a video tutorial and send it to me. Then I add it. All options and what each option does described on the wiki. The wiki also contains a description of all settings and how-to configure them. For complex settings, there is a steep-by-steep guide. Read the [main](README.md) information and [GUIDE](https://github.com/AAndyProgram/SCrawler/wiki/) and you won't have any problems. I have developed a program with an intuitive interface. There is a Settings button, download buttons, a context menu that drops down when a user is clicked, and other controls. Anyone can use it.
|
||||||
|
|
||||||
**The following video shows how to add credentials:**
|
**The following video was recorded by a user who loves SCrawler and demonstrates how to add credentials using Instagram as an example:**
|
||||||
|
|
||||||
[](https://www.youtube.com/watch?v=XDn7zG4I700)
|
[](https://www.youtube.com/watch?v=XDn7zG4I700)
|
||||||
@@ -2,6 +2,8 @@ You can create a plugin for any site you want. **To create a plugin, read [this
|
|||||||
|
|
||||||
If you've created a plugin, you can create a [new issue](https://github.com/AAndyProgram/SCrawler/issues/new?assignees=&labels=New+Plugin&projects=&template=plugin_add.md&title=%5BNEW+PLUGIN%5D) and I'll add your plugin to the list below.
|
If you've created a plugin, you can create a [new issue](https://github.com/AAndyProgram/SCrawler/issues/new?assignees=&labels=New+Plugin&projects=&template=plugin_add.md&title=%5BNEW+PLUGIN%5D) and I'll add your plugin to the list below.
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
List of available plugins:
|
List of available plugins:
|
||||||
|
|
||||||
Tools:
|
Tools:
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 59 KiB |
BIN
ProgramScreenshots/FeedMoveCopyTo.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 491 KiB After Width: | Height: | Size: 472 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 101 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 20 KiB |
BIN
ProgramScreenshots/SettingsGlobalHeaders.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 35 KiB |
BIN
ProgramScreenshots/SettingsSiteInstagramData.png
Normal file
|
After Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 21 KiB |
@@ -6,11 +6,19 @@ https://github.com/yt-dlp/yt-dlp/
|
|||||||
|
|
||||||
SCrawler has advanced user management, collections, labels, groups, automatic downloads, a beautiful view, GUI, the ability to add plugins for other sites and much more. Just try it and compare.
|
SCrawler has advanced user management, collections, labels, groups, automatic downloads, a beautiful view, GUI, the ability to add plugins for other sites and much more. Just try it and compare.
|
||||||
|
|
||||||
|
# gallery-dl
|
||||||
|
|
||||||
|
https://github.com/mikf/gallery-dl
|
||||||
|
|
||||||
|
**Great powerful CLI tool that supports hundreds of sites.**
|
||||||
|
|
||||||
|
SCrawler has advanced user management, collections, labels, groups, automatic downloads, a beautiful view, GUI, the ability to add plugins for other sites and much more. Just try it and compare.
|
||||||
|
|
||||||
# 4K Video Downloader
|
# 4K Video Downloader
|
||||||
|
|
||||||
https://www.4kdownload.com/-plbrz/video-downloader
|
https://www.4kdownload.com/-plbrz/video-downloader
|
||||||
|
|
||||||
| Option | SCrawler | 4K Stogram |
|
| Option | SCrawler | 4K Video Downloader |
|
||||||
| ---- | ---- | ---- |
|
| ---- | ---- | ---- |
|
||||||
| User managament | **Advanced** | No |
|
| User managament | **Advanced** | No |
|
||||||
| Automatic downloads | **Yes** | No |
|
| Automatic downloads | **Yes** | No |
|
||||||
@@ -121,10 +129,3 @@ https://github.com/RipMeApp/ripme
|
|||||||
| Other sites support | **Yes** | No |
|
| Other sites support | **Yes** | No |
|
||||||
| Still supported | **Yes** | **No (last release date May 4, 2021)** |
|
| Still supported | **Yes** | **No (last release date May 4, 2021)** |
|
||||||
|
|
||||||
# gallery-dl
|
|
||||||
|
|
||||||
https://github.com/mikf/gallery-dl
|
|
||||||
|
|
||||||
**CLI tool**
|
|
||||||
|
|
||||||
SCrawler has advanced user management, collections, labels, groups, automatic downloads, a beautiful view, GUI, the ability to add plugins for other sites and much more. Just try it and compare.
|
|
||||||
18
README.md
@@ -15,6 +15,10 @@
|
|||||||
A program to download photo and video from [any site](#supported-sites) (e.g. YouTube, YouTube Music, OnlyFans, Reddit, Twitter, Mastodon, Instagram, Threads, Facebook, TikTok, RedGifs, JustForFans, PornHub, XHamster, XVIDEOS, ThisVid, LPSG, Pinterest).
|
A program to download photo and video from [any site](#supported-sites) (e.g. YouTube, YouTube Music, OnlyFans, Reddit, Twitter, Mastodon, Instagram, Threads, Facebook, TikTok, RedGifs, JustForFans, PornHub, XHamster, XVIDEOS, ThisVid, LPSG, Pinterest).
|
||||||
|
|
||||||
**If you like SCrawler, please like the program on [this site](https://alternativeto.net/software/scrawler/about/) and/or [this](https://www.softpedia.com/get/Internet/Download-Managers/Social-networks-crawler.shtml)**
|
**If you like SCrawler, please like the program on [this site](https://alternativeto.net/software/scrawler/about/) and/or [this](https://www.softpedia.com/get/Internet/Download-Managers/Social-networks-crawler.shtml)**
|
||||||
|
|
||||||
|
**Join our Discord server**: https://discord.gg/uFNUXvFFmg
|
||||||
|
<br/>*If you have problems using the program, you can get help faster on our Discord server!*
|
||||||
|
|
||||||
<!---Do you like this program? Consider adding to my coffee fund by making a donation to show your support. :blush:
|
<!---Do you like this program? Consider adding to my coffee fund by making a donation to show your support. :blush:
|
||||||
[](https://ko-fi.com/andyprogram)--->
|
[](https://ko-fi.com/andyprogram)--->
|
||||||
**Bitcoin**: BC1Q0NH839FT5TA44DD7L7RLR97XDQAG9V8D6N7XET
|
**Bitcoin**: BC1Q0NH839FT5TA44DD7L7RLR97XDQAG9V8D6N7XET
|
||||||
@@ -74,12 +78,12 @@ A program to download photo and video from [any site](#supported-sites) (e.g. Yo
|
|||||||
- **YouTube Music**
|
- **YouTube Music**
|
||||||
- **Reddit**
|
- **Reddit**
|
||||||
- **Twitter**
|
- **Twitter**
|
||||||
- **OnlyFans**
|
- **OnlyFans** *(partial support)*[^1]
|
||||||
- **Mastodon**
|
- **Mastodon**
|
||||||
- **Instagram**
|
- **Instagram**
|
||||||
- **Threads**
|
- **Threads**
|
||||||
- **Facebook**
|
- **Facebook**
|
||||||
- JustForFans
|
- JustForFans *(partial support)*[^1]
|
||||||
- TikTok
|
- TikTok
|
||||||
- RedGifs
|
- RedGifs
|
||||||
- Pinterest
|
- Pinterest
|
||||||
@@ -155,9 +159,15 @@ First, the program downloads the full profile. After the program downloads only
|
|||||||
|
|
||||||
**Don't put program in the ```Program Files``` system folder (this is portable program and program settings are stored in the program folder)**
|
**Don't put program in the ```Program Files``` system folder (this is portable program and program settings are stored in the program folder)**
|
||||||
|
|
||||||
|
**I highly doubt you can run SCrawler on Linux or Mac. SCrawler is a program that is heavily dependent on Windows.**
|
||||||
|
|
||||||
# Updating
|
# Updating
|
||||||
|
|
||||||
Just download [latest](https://github.com/AAndyProgram/SCrawler/releases/latest) version and unpack it into the program folder. **Before starting a new version, I recommend making a backup copy of the program settings folder.**
|
Just download [latest](https://github.com/AAndyProgram/SCrawler/releases/latest) version and unpack it into the program folder. **Before launching a new version, I recommend making a backup copy of the program settings folder and user settings/data files.**
|
||||||
|
|
||||||
|
**You can also use the updater included in the release package.**
|
||||||
|
|
||||||
|
# [How to report a problem](CONTRIBUTING.md#how-to-report-a-problem)
|
||||||
|
|
||||||
# [How to build from source](CONTRIBUTING.md#how-to-build-from-source)
|
# [How to build from source](CONTRIBUTING.md#how-to-build-from-source)
|
||||||
|
|
||||||
@@ -216,3 +226,5 @@ Discord server: https://discord.gg/uFNUXvFFmg
|
|||||||
|
|
||||||
[Wire](https://account.wire.com/user-profile/?id=93985052-cf2c-4b72-ac75-bbe3231cf544): @andyprogram
|
[Wire](https://account.wire.com/user-profile/?id=93985052-cf2c-4b72-ac75-bbe3231cf544): @andyprogram
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
[^1]: Partial support means that I don't have personal accounts on paid porn sites because I don't pay for porn. If this site has stopped downloading and you want me to fix it, please be ready to give me access to an account with at least one active subscription. Otherwise, the download from this site will not be fixed.
|
||||||
@@ -38,6 +38,8 @@ Namespace Plugin.Attributes
|
|||||||
Public Property LabelTextAlign As Drawing.ContentAlignment = Drawing.ContentAlignment.TopCenter
|
Public Property LabelTextAlign As Drawing.ContentAlignment = Drawing.ContentAlignment.TopCenter
|
||||||
''' <summary>This is an authorization property</summary>
|
''' <summary>This is an authorization property</summary>
|
||||||
Public Property IsAuth As Boolean = False
|
Public Property IsAuth As Boolean = False
|
||||||
|
Public Property Category As String = Nothing
|
||||||
|
Public Property InheritanceName As String = Nothing
|
||||||
''' <summary>Initialize a new property option attribute</summary>
|
''' <summary>Initialize a new property option attribute</summary>
|
||||||
''' <param name="PropertyName">Property name</param>
|
''' <param name="PropertyName">Property name</param>
|
||||||
Public Sub New(<CallerMemberName()> Optional ByVal PropertyName As String = Nothing)
|
Public Sub New(<CallerMemberName()> Optional ByVal PropertyName As String = Nothing)
|
||||||
@@ -57,6 +59,7 @@ Namespace Plugin.Attributes
|
|||||||
''' <summary>Store property value in settings XML file</summary>
|
''' <summary>Store property value in settings XML file</summary>
|
||||||
<AttributeUsage(AttributeTargets.Property, AllowMultiple:=False, Inherited:=False)> Public NotInheritable Class PXML : Inherits Attribute
|
<AttributeUsage(AttributeTargets.Property, AllowMultiple:=False, Inherited:=False)> Public NotInheritable Class PXML : Inherits Attribute
|
||||||
Public ReadOnly ElementName As String
|
Public ReadOnly ElementName As String
|
||||||
|
Public Property OnlyForChecked As Boolean = False
|
||||||
''' <summary>Initialize a new XML attribute</summary>
|
''' <summary>Initialize a new XML attribute</summary>
|
||||||
''' <param name="XMLElementName">XML element name</param>
|
''' <param name="XMLElementName">XML element name</param>
|
||||||
Public Sub New(<CallerMemberName()> Optional ByVal XMLElementName As String = Nothing)
|
Public Sub New(<CallerMemberName()> Optional ByVal XMLElementName As String = Nothing)
|
||||||
@@ -188,4 +191,13 @@ Namespace Plugin.Attributes
|
|||||||
Repository = RepoName
|
Repository = RepoName
|
||||||
End Sub
|
End Sub
|
||||||
End Class
|
End Class
|
||||||
|
''' <summary>Replace internal plugin with the current one</summary>
|
||||||
|
<AttributeUsage(AttributeTargets.Class, AllowMultiple:=False, Inherited:=False)> Public NotInheritable Class ReplaceInternalPluginAttribute : Inherits Attribute
|
||||||
|
Public ReadOnly SiteName As String
|
||||||
|
Public ReadOnly PluginKey As String
|
||||||
|
Public Sub New(ByVal PluginKey As String, Optional ByVal SiteName As String = Nothing)
|
||||||
|
Me.PluginKey = PluginKey
|
||||||
|
Me.SiteName = SiteName
|
||||||
|
End Sub
|
||||||
|
End Class
|
||||||
End Namespace
|
End Namespace
|
||||||
@@ -40,5 +40,6 @@ Namespace Plugin
|
|||||||
Sub GetMedia(ByVal Token As Threading.CancellationToken)
|
Sub GetMedia(ByVal Token As Threading.CancellationToken)
|
||||||
Sub Download(ByVal Token As Threading.CancellationToken)
|
Sub Download(ByVal Token As Threading.CancellationToken)
|
||||||
Sub DownloadSingleObject(ByVal Data As IDownloadableMedia, ByVal Token As Threading.CancellationToken)
|
Sub DownloadSingleObject(ByVal Data As IDownloadableMedia, ByVal Token As Threading.CancellationToken)
|
||||||
|
Sub ResetHistoryData()
|
||||||
End Interface
|
End Interface
|
||||||
End Namespace
|
End Namespace
|
||||||
@@ -17,6 +17,9 @@ Namespace Plugin
|
|||||||
ReadOnly Property Icon As Icon
|
ReadOnly Property Icon As Icon
|
||||||
ReadOnly Property Image As Image
|
ReadOnly Property Image As Image
|
||||||
ReadOnly Property Site As String
|
ReadOnly Property Site As String
|
||||||
|
Property CMDEncoding As String
|
||||||
|
Property EnvironmentPrograms As IEnumerable(Of String)
|
||||||
|
Sub EnvironmentProgramsUpdated()
|
||||||
Property AccountName As String
|
Property AccountName As String
|
||||||
Property Temporary As Boolean
|
Property Temporary As Boolean
|
||||||
Property DefaultInstance As ISiteSettings
|
Property DefaultInstance As ISiteSettings
|
||||||
|
|||||||
@@ -32,6 +32,6 @@ Imports System.Runtime.InteropServices
|
|||||||
' by using the '*' as shown below:
|
' by using the '*' as shown below:
|
||||||
' <Assembly: AssemblyVersion("1.0.*")>
|
' <Assembly: AssemblyVersion("1.0.*")>
|
||||||
|
|
||||||
<Assembly: AssemblyVersion("2023.11.24.0")>
|
<Assembly: AssemblyVersion("2024.4.13.0")>
|
||||||
<Assembly: AssemblyFileVersion("2023.11.24.0")>
|
<Assembly: AssemblyFileVersion("2024.4.13.0")>
|
||||||
<Assembly: NeutralResourcesLanguage("en")>
|
<Assembly: NeutralResourcesLanguage("en")>
|
||||||
|
|||||||
@@ -9,8 +9,23 @@
|
|||||||
Namespace Plugin
|
Namespace Plugin
|
||||||
Public NotInheritable Class PropertyValue : Implements IPropertyValue
|
Public NotInheritable Class PropertyValue : Implements IPropertyValue
|
||||||
Public Event ValueChanged As IPropertyValue.ValueChangedEventHandler Implements IPropertyValue.ValueChanged
|
Public Event ValueChanged As IPropertyValue.ValueChangedEventHandler Implements IPropertyValue.ValueChanged
|
||||||
|
Public Event CheckedChanged As IPropertyValue.ValueChangedEventHandler
|
||||||
Public Property [Type] As Type Implements IPropertyValue.Type
|
Public Property [Type] As Type Implements IPropertyValue.Type
|
||||||
Public Property OnChangeFunction As IPropertyValue.ValueChangedEventHandler
|
Public Property OnChangeFunction As IPropertyValue.ValueChangedEventHandler
|
||||||
|
Public Property OnCheckboxCheckedChange As EventHandler(Of PropertyValueEventArgs)
|
||||||
|
Private _Checked As Boolean = False
|
||||||
|
Public Property Checked As Boolean
|
||||||
|
Get
|
||||||
|
Return _Checked
|
||||||
|
End Get
|
||||||
|
Set(ByVal IsChecked As Boolean)
|
||||||
|
_Checked = IsChecked
|
||||||
|
If Not _Initialization Then
|
||||||
|
If Not OnCheckboxCheckedChange Is Nothing Then OnCheckboxCheckedChange.Invoke(Me, EventArgs.Empty)
|
||||||
|
RaiseEvent CheckedChanged(_Checked)
|
||||||
|
End If
|
||||||
|
End Set
|
||||||
|
End Property
|
||||||
Private _Initialization As Boolean = False
|
Private _Initialization As Boolean = False
|
||||||
''' <inheritdoc cref="PropertyValue.New(Object, Type, ByRef IPropertyValue.ValueChangedEventHandler)"/>
|
''' <inheritdoc cref="PropertyValue.New(Object, Type, ByRef IPropertyValue.ValueChangedEventHandler)"/>
|
||||||
''' <exception cref="ArgumentNullException"></exception>
|
''' <exception cref="ArgumentNullException"></exception>
|
||||||
@@ -59,6 +74,7 @@ Namespace Plugin
|
|||||||
Type = Source.Type
|
Type = Source.Type
|
||||||
OnChangeFunction = Source.OnChangeFunction
|
OnChangeFunction = Source.OnChangeFunction
|
||||||
_Value = Source._Value
|
_Value = Source._Value
|
||||||
|
_Checked = Source._Checked
|
||||||
_Initialization = False
|
_Initialization = False
|
||||||
End Sub
|
End Sub
|
||||||
End Class
|
End Class
|
||||||
@@ -71,4 +87,8 @@ Namespace Plugin
|
|||||||
''' <summary>Property value</summary>
|
''' <summary>Property value</summary>
|
||||||
Property Value As Object
|
Property Value As Object
|
||||||
End Interface
|
End Interface
|
||||||
|
Public Class PropertyValueEventArgs : Inherits EventArgs
|
||||||
|
Public Property Checked As Boolean = False
|
||||||
|
Public Property ControlEnabled As Boolean = True
|
||||||
|
End Class
|
||||||
End Namespace
|
End Namespace
|
||||||
@@ -81,6 +81,7 @@ Namespace API.YouTube.Base
|
|||||||
Public Structure MediaObject : Implements IIndexable, IComparable(Of MediaObject)
|
Public Structure MediaObject : Implements IIndexable, IComparable(Of MediaObject)
|
||||||
Public Type As Plugin.UserMediaTypes
|
Public Type As Plugin.UserMediaTypes
|
||||||
Public ID As String
|
Public ID As String
|
||||||
|
Public ID_DRC As Boolean
|
||||||
Public Extension As String
|
Public Extension As String
|
||||||
Public Width As Integer
|
Public Width As Integer
|
||||||
Public Height As Integer
|
Public Height As Integer
|
||||||
@@ -110,10 +111,14 @@ Namespace API.YouTube.Base
|
|||||||
End Function
|
End Function
|
||||||
Private Function CompareTo(ByVal Other As MediaObject) As Integer Implements IComparable(Of MediaObject).CompareTo
|
Private Function CompareTo(ByVal Other As MediaObject) As Integer Implements IComparable(Of MediaObject).CompareTo
|
||||||
If Type = Other.Type Then
|
If Type = Other.Type Then
|
||||||
If Width.CompareTo(Other.Width) = 0 Then
|
If ID_DRC.CompareTo(Other.ID_DRC) = 0 Then
|
||||||
Return Size.CompareTo(Other.Size) * -1
|
If Width.CompareTo(Other.Width) = 0 Then
|
||||||
|
Return Size.CompareTo(Other.Size) * -1
|
||||||
|
Else
|
||||||
|
Return Width.CompareTo(Other.Width) * -1
|
||||||
|
End If
|
||||||
Else
|
Else
|
||||||
Return Width.CompareTo(Other.Width) * -1
|
Return ID_DRC.CompareTo(Other.ID_DRC)
|
||||||
End If
|
End If
|
||||||
Else
|
Else
|
||||||
Return CInt(Type).CompareTo(CInt(Other.Type))
|
Return CInt(Type).CompareTo(CInt(Other.Type))
|
||||||
|
|||||||
@@ -174,7 +174,7 @@ Namespace API.YouTube.Base
|
|||||||
ByVal ObjType As YouTubeMediaType, ByVal ChannelTab As YouTubeChannelTab,
|
ByVal ObjType As YouTubeMediaType, ByVal ChannelTab As YouTubeChannelTab,
|
||||||
ByVal IsMusic As Boolean, ByVal UrlAsIs As Boolean) As Boolean
|
ByVal IsMusic As Boolean, ByVal UrlAsIs As Boolean) As Boolean
|
||||||
Try
|
Try
|
||||||
Dim command$ = "yt-dlp --write-info-json --skip-download"
|
Dim command$ = $"{YTDLP_NAME} --write-info-json --skip-download"
|
||||||
command.StringAppend(GetCookiesCommand(UseCookies, CookiesFile), " ")
|
command.StringAppend(GetCookiesCommand(UseCookies, CookiesFile), " ")
|
||||||
If DateAfter.HasValue Then command.StringAppend($"--dateafter {DateAfter.Value:yyyyMMdd}", " ")
|
If DateAfter.HasValue Then command.StringAppend($"--dateafter {DateAfter.Value:yyyyMMdd}", " ")
|
||||||
If DateBefore.HasValue Then command.StringAppend($"--datebefore {DateBefore.Value:yyyyMMdd}", " ")
|
If DateBefore.HasValue Then command.StringAppend($"--datebefore {DateBefore.Value:yyyyMMdd}", " ")
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ Imports PersonalUtilities.Forms
|
|||||||
Imports PersonalUtilities.Functions.XML
|
Imports PersonalUtilities.Functions.XML
|
||||||
Imports PersonalUtilities.Functions.XML.Base
|
Imports PersonalUtilities.Functions.XML.Base
|
||||||
Imports PersonalUtilities.Functions.XML.Objects
|
Imports PersonalUtilities.Functions.XML.Objects
|
||||||
|
Imports PersonalUtilities.Functions.XML.Attributes
|
||||||
Imports PersonalUtilities.Functions.XML.Attributes.Specialized
|
Imports PersonalUtilities.Functions.XML.Attributes.Specialized
|
||||||
Imports PersonalUtilities.Tools
|
Imports PersonalUtilities.Tools
|
||||||
Imports PersonalUtilities.Tools.Grid.Base
|
Imports PersonalUtilities.Tools.Grid.Base
|
||||||
@@ -35,7 +36,9 @@ Namespace API.YouTube.Base
|
|||||||
<Browsable(False)> Private Property Mode As GridUpdateModes = GridUpdateModes.OnConfirm Implements IGridValuesContainer.Mode
|
<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), XMLVV(-1)> Friend ReadOnly Property PlaylistFormSplitterDistance As XMLValue(Of Integer)
|
||||||
<Browsable(False)> Friend ReadOnly Property DownloadLocations As DownloadLocationsCollection
|
<Browsable(False)> Friend ReadOnly Property DownloadLocations As DownloadLocationsCollection
|
||||||
|
<Browsable(False)> Friend ReadOnly Property PlaylistsLocations As DownloadLocationsCollection
|
||||||
<Browsable(False)> Public Overridable Property AccountName As String
|
<Browsable(False)> Public Overridable Property AccountName As String
|
||||||
|
<Browsable(False), XMLVV(0)> Private ReadOnly Property SettingsVersion As XMLValue(Of Integer)
|
||||||
#Region "Environment"
|
#Region "Environment"
|
||||||
#Region "Programs"
|
#Region "Programs"
|
||||||
<Browsable(True), GridVisible(False), XMLVN({"Environment"}), Category("Environment programs"), DisplayName("Path to yt-dlp.exe"),
|
<Browsable(True), GridVisible(False), XMLVN({"Environment"}), Category("Environment programs"), DisplayName("Path to yt-dlp.exe"),
|
||||||
@@ -83,6 +86,18 @@ Namespace API.YouTube.Base
|
|||||||
Description("The default output path where files should be downloaded."),
|
Description("The default output path where files should be downloaded."),
|
||||||
Editor(GetType(GridSFileTypeEditorPath), GetType(UITypeEditor))>
|
Editor(GetType(GridSFileTypeEditorPath), GetType(UITypeEditor))>
|
||||||
Public ReadOnly Property OutputPath As XMLValue(Of SFile)
|
Public ReadOnly Property OutputPath As XMLValue(Of SFile)
|
||||||
|
<Browsable(True), GridVisible(False), XMLVN({"Environment"}), Category("Environment"), DisplayName("Playlist file"),
|
||||||
|
Description("Last selected playlist file"),
|
||||||
|
Editor(GetType(GridSFileTypeEditor_M3U8), GetType(UITypeEditor))>
|
||||||
|
Public ReadOnly Property LatestPlaylistFile As XMLValue(Of SFile)
|
||||||
|
Private Class GridSFileTypeEditor_M3U8 : Inherits GridSFileTypeEditor
|
||||||
|
Public Overrides Function EditValue(ByVal Context As ITypeDescriptorContext, ByVal Provider As IServiceProvider, ByVal Value As Object) As Object
|
||||||
|
Dim f As SFile = SFile.SelectFiles(New SFile(CStr(AConvert(Of String)(Value, AModes.Var, String.Empty))), False,
|
||||||
|
"Select playlist file", "Playlists|*.m3u;*.m3u8|All files|*.*", EDP.ReturnValue).FirstOrDefault()
|
||||||
|
If Not f.IsEmptyString() Then Value = f
|
||||||
|
Return Value
|
||||||
|
End Function
|
||||||
|
End Class
|
||||||
<Browsable(True), GridVisible(False), XMLVN({"Environment"}), Category("Environment"), DisplayName("Output path auto change"),
|
<Browsable(True), GridVisible(False), XMLVN({"Environment"}), Category("Environment"), DisplayName("Output path auto change"),
|
||||||
Description("Automatically change the output path when a new destination is selected in the opening forms.")>
|
Description("Automatically change the output path when a new destination is selected in the opening forms.")>
|
||||||
Public ReadOnly Property OutputPathAutoChange As XMLValue(Of Boolean)
|
Public ReadOnly Property OutputPathAutoChange As XMLValue(Of Boolean)
|
||||||
@@ -228,6 +243,9 @@ Namespace API.YouTube.Base
|
|||||||
<Browsable(True), GridVisible(False), XMLVN({"Defaults"}), Category("Defaults"), DisplayName("Program description"),
|
<Browsable(True), GridVisible(False), XMLVN({"Defaults"}), Category("Defaults"), DisplayName("Program description"),
|
||||||
Description("Add some additional info to the program info if you need")>
|
Description("Add some additional info to the program info if you need")>
|
||||||
Friend ReadOnly Property ProgramDescription As XMLValue(Of String)
|
Friend ReadOnly Property ProgramDescription As XMLValue(Of String)
|
||||||
|
<Browsable(True), GridVisible(False), XMLVN({"Defaults"}, "%"""), Category("Defaults"), DisplayName("Remove characters"),
|
||||||
|
Description("Remove specific characters from a file name")>
|
||||||
|
Friend ReadOnly Property FileRemoveCharacters As XMLValue(Of String)
|
||||||
#End Region
|
#End Region
|
||||||
#Region "Defaults ChannelsDownload"
|
#Region "Defaults ChannelsDownload"
|
||||||
<Browsable(True), GridVisible, XMLVN({"Defaults", "Channels"}), Category("Defaults"), DisplayName("Default download tabs for channels"),
|
<Browsable(True), GridVisible, XMLVN({"Defaults", "Channels"}), Category("Defaults"), DisplayName("Default download tabs for channels"),
|
||||||
@@ -272,9 +290,77 @@ Namespace API.YouTube.Base
|
|||||||
<Browsable(True), GridVisible, XMLVN({"DefaultsVideo"}, 1080), Category("Defaults Video"), DisplayName("Default definition"),
|
<Browsable(True), GridVisible, XMLVN({"DefaultsVideo"}, 1080), Category("Defaults Video"), DisplayName("Default definition"),
|
||||||
Description("The default maximum video resolution. -1 for max definition")>
|
Description("The default maximum video resolution. -1 for max definition")>
|
||||||
Public ReadOnly Property DefaultVideoDefinition As XMLValue(Of Integer)
|
Public ReadOnly Property DefaultVideoDefinition As XMLValue(Of Integer)
|
||||||
|
<Browsable(True), GridVisible, XMLVN({"DefaultsVideo"}, False), Category("Defaults Video"), DisplayName("Embed thumbnail (video)"),
|
||||||
|
Description("Embed thumbnail in the video as cover art. Default: true.")>
|
||||||
|
Public ReadOnly Property DefaultVideoEmbedThumbnail As XMLValue(Of Boolean)
|
||||||
<Browsable(True), GridVisible, XMLVN({"DefaultsVideo"}), Category("Defaults Video"), DisplayName("Include zero size formats"),
|
<Browsable(True), GridVisible, XMLVN({"DefaultsVideo"}), Category("Defaults Video"), DisplayName("Include zero size formats"),
|
||||||
Description("Include formats with zero size (or undefined size).")>
|
Description("Include formats with zero size (or undefined size).")>
|
||||||
Public ReadOnly Property DefaultVideoIncludeNullSize As XMLValue(Of Boolean)
|
Public ReadOnly Property DefaultVideoIncludeNullSize As XMLValue(Of Boolean)
|
||||||
|
<Browsable(False), XMLV("DefaultVideoFPS", {"DefaultsVideo"}, -1)>
|
||||||
|
Private ReadOnly Property DefaultVideoFPS_XML As XMLValue(Of Double)
|
||||||
|
<Browsable(True), GridVisible, Category("Defaults Video"), DisplayName("Default video FPS"),
|
||||||
|
Description("Set default video FPS (only to reduce video FPS). Default: -1 (disabled)."),
|
||||||
|
TypeConverter(GetType(FieldsTypeConverter)), GridFormatProvider(GetType(FpsFormatProvider))>
|
||||||
|
Public Property DefaultVideoFPS As Double
|
||||||
|
Get
|
||||||
|
Return DefaultVideoFPS_XML
|
||||||
|
End Get
|
||||||
|
Set(ByVal fps As Double)
|
||||||
|
DefaultVideoFPS_XML.Value = fps
|
||||||
|
End Set
|
||||||
|
End Property
|
||||||
|
Private Function ShouldSerializeDefaultVideoFPS() As Boolean
|
||||||
|
Return DefaultVideoFPS <> DefaultVideoFPS_XML.Value
|
||||||
|
End Function
|
||||||
|
Private Sub ResetDefaultVideoFPS()
|
||||||
|
DefaultVideoFPS = -1
|
||||||
|
End Sub
|
||||||
|
Friend Class FpsFormatProvider : Implements IGridConversionProvider
|
||||||
|
Private Property Converter As TypeConverter Implements IGridConversionProvider.Converter
|
||||||
|
Private Property Context As ITypeDescriptorContext Implements IGridConversionProvider.Context
|
||||||
|
Private Property DataType As Type Implements IGridConversionProvider.DataType
|
||||||
|
Private Property Instance As Object Implements IGridConversionProvider.Instance
|
||||||
|
Friend Shared ReadOnly Property MyProviderDefault As ANumbers
|
||||||
|
Get
|
||||||
|
Return New ANumbers(ANumbers.Cultures.Primitive) With {.DecimalDigits = 5, .TrimDecimalDigits = True}
|
||||||
|
End Get
|
||||||
|
End Property
|
||||||
|
Friend Const ErrorMessageDefault As String = "The fps value must be a number"
|
||||||
|
Private ReadOnly MyProvider As ANumbers = MyProviderDefault
|
||||||
|
Friend Function ToObject(ByVal Context As ITypeDescriptorContext, ByVal Culture As CultureInfo, ByVal Value As Object) As Object Implements IGridConversionProvider.ToObject
|
||||||
|
Return AConvert(Of Double)(Value, MyProvider, -1)
|
||||||
|
End Function
|
||||||
|
Friend Overloads Function ToString(ByVal Context As ITypeDescriptorContext, ByVal Culture As CultureInfo, ByVal Value As Object,
|
||||||
|
ByVal DestinationType As Type) As Object Implements IGridConversionProvider.ToString
|
||||||
|
If ACheck(Of Double)(Value, AModes.Var, MyProvider) Then
|
||||||
|
Return Value.ToString
|
||||||
|
Else
|
||||||
|
Return -1
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
Friend Function CreateInstance(ByVal Context As ITypeDescriptorContext, ByVal NewValue As Object, ByRef RefreshGrid As Boolean) As Object Implements IGridConversionProvider.CreateInstance
|
||||||
|
If ACheck(Of Double)(NewValue, AModes.Var, MyProvider) Then
|
||||||
|
Return NewValue
|
||||||
|
Else
|
||||||
|
RefreshGrid = True
|
||||||
|
Return -1
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
Friend 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
|
||||||
|
Return AConvert(Value, AModes.Var, DestinationType,, True, -1, MyProvider, EDP.ReturnValue)
|
||||||
|
End Function
|
||||||
|
Friend Function IsValid(ByVal Context As ITypeDescriptorContext, ByVal Value As Object, ByVal DestinationType As Type) As Boolean Implements IGridValidator.IsValid
|
||||||
|
If ACheck(Of Double)(Value, AModes.Var, MyProvider) Then
|
||||||
|
Return True
|
||||||
|
Else
|
||||||
|
Throw New FormatException(ErrorMessageDefault)
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
Private Function GetFormat(ByVal FormatType As Type) As Object Implements IFormatProvider.GetFormat
|
||||||
|
Throw New NotImplementedException("'GetFormat' is not available in 'FpsFormatProvider'")
|
||||||
|
End Function
|
||||||
|
End Class
|
||||||
#End Region
|
#End Region
|
||||||
#Region "Defaults Audio"
|
#Region "Defaults Audio"
|
||||||
<Browsable(True), GridVisible, XMLVN({"DefaultsAudio"}, "AAC"), Category("Defaults Audio"), DisplayName("Default codec"),
|
<Browsable(True), GridVisible, XMLVN({"DefaultsAudio"}, "AAC"), Category("Defaults Audio"), DisplayName("Default codec"),
|
||||||
@@ -294,6 +380,35 @@ Namespace API.YouTube.Base
|
|||||||
TypeConverter(GetType(ValueCollectionConverter)),
|
TypeConverter(GetType(ValueCollectionConverter)),
|
||||||
Description("Additional audio format for downloading videos. This means that the audio will be extracted and saved as a separate file in these formats.")>
|
Description("Additional audio format for downloading videos. This means that the audio will be extracted and saved as a separate file in these formats.")>
|
||||||
Public ReadOnly Property DefaultAudioCodecAddit As XMLValuesCollection(Of String)
|
Public ReadOnly Property DefaultAudioCodecAddit As XMLValuesCollection(Of String)
|
||||||
|
<Browsable(True), GridVisible, XMLVN({"DefaultsAudio"}, True), Category("Defaults Audio"), DisplayName("Embed thumbnail"),
|
||||||
|
Description("Embed thumbnail in the audio as cover art. Default: true.")>
|
||||||
|
Public ReadOnly Property DefaultAudioEmbedThumbnail As XMLValue(Of Boolean)
|
||||||
|
<Browsable(True), GridVisible, XMLVN({"DefaultsAudio"}, True), Category("Defaults Audio"), DisplayName("Embed thumbnail (extracted files)"),
|
||||||
|
Description("Embed thumbnail in the extracted (additional file ('mp3' only)) audio as cover art. Default: true.")>
|
||||||
|
Public ReadOnly Property DefaultAudioEmbedThumbnail_ExtractedFiles As XMLValue(Of Boolean)
|
||||||
|
<Browsable(True), GridVisible, XMLVN({"DefaultsAudio"}, -1), Category("Defaults Audio"), DisplayName("Bitrate"),
|
||||||
|
Description("Default audio bitrate if you want to change it during download. -1 to disable. Default: -1.")>
|
||||||
|
Public ReadOnly Property DefaultAudioBitrate As XMLValue(Of Integer)
|
||||||
|
<Browsable(True), GridVisible, XMLVN({"DefaultsAudio"}, 20), Category("Defaults Audio"), DisplayName("Bitrate: ffmpeg crf"),
|
||||||
|
Description("This is the ffmpeg argument. Change it only if you know what you're doing. Default: 20.")>
|
||||||
|
Public ReadOnly Property DefaultAudioBitrate_crf As XMLValue(Of Integer)
|
||||||
|
#Region "Music"
|
||||||
|
<Browsable(True), GridVisible, XMLVN({"Playlists"}, True), Category("Music"), DisplayName("Create M3U8"),
|
||||||
|
Description("Create M3U8 playlist for music. Default: true.")>
|
||||||
|
Public ReadOnly Property MusicPlaylistCreate_M3U8 As XMLValue(Of Boolean)
|
||||||
|
<Browsable(True), GridVisible, XMLVN({"Playlists"}), Category("Music"), DisplayName("Create M3U"),
|
||||||
|
Description("Create M3U playlist for music. Default: false.")>
|
||||||
|
Public ReadOnly Property MusicPlaylistCreate_M3U As XMLValue(Of Boolean)
|
||||||
|
<Browsable(True), GridVisible, XMLVN({"Playlists"}), Category("Music"), DisplayName("M3U8 Append artist"),
|
||||||
|
Description("Add artist to file name. Default: false.")>
|
||||||
|
Public ReadOnly Property MusicPlaylistCreate_M3U8_AppendArtist As XMLValue(Of Boolean)
|
||||||
|
<Browsable(True), GridVisible, XMLVN({"Playlists"}), Category("Music"), DisplayName("M3U8 Append file extension"),
|
||||||
|
Description("Add file extension to file name. Default: false.")>
|
||||||
|
Public ReadOnly Property MusicPlaylistCreate_M3U8_AppendExt As XMLValue(Of Boolean)
|
||||||
|
<Browsable(True), GridVisible, XMLVN({"Playlists"}), Category("Music"), DisplayName("M3U8 Append file number"),
|
||||||
|
Description("Add file number to file name. Default: false.")>
|
||||||
|
Public ReadOnly Property MusicPlaylistCreate_M3U8_AppendNumber As XMLValue(Of Boolean)
|
||||||
|
#End Region
|
||||||
#End Region
|
#End Region
|
||||||
#Region "Defaults Subtitles"
|
#Region "Defaults Subtitles"
|
||||||
<XMLVN({"DefaultsSubtitles"}, {"en"}, CollectionMode:=IXMLValuesCollection.Modes.String)>
|
<XMLVN({"DefaultsSubtitles"}, {"en"}, CollectionMode:=IXMLValuesCollection.Modes.String)>
|
||||||
@@ -346,7 +461,9 @@ Namespace API.YouTube.Base
|
|||||||
Public Sub New(ByVal AccountName As String)
|
Public Sub New(ByVal AccountName As String)
|
||||||
Me.AccountName = AccountName
|
Me.AccountName = AccountName
|
||||||
DownloadLocations = New DownloadLocationsCollection
|
DownloadLocations = New DownloadLocationsCollection
|
||||||
|
PlaylistsLocations = New DownloadLocationsCollection
|
||||||
DownloadLocations.Load(False, True)
|
DownloadLocations.Load(False, True)
|
||||||
|
PlaylistsLocations.Load(True, True, $"{XmlFile.SettingsFolder}\DownloadLocations_Playlists.xml")
|
||||||
Dim acc$ = String.Empty
|
Dim acc$ = String.Empty
|
||||||
If Not AccountName.IsEmptyString Then acc = $"_{AccountName}"
|
If Not AccountName.IsEmptyString Then acc = $"_{AccountName}"
|
||||||
Dim f As SFile = YouTubeSettingsFile
|
Dim f As SFile = YouTubeSettingsFile
|
||||||
|
|||||||
136
SCrawler.YouTube/Controls/MusicPlaylistsForm.Designer.vb
generated
@@ -43,8 +43,16 @@ Namespace API.YouTube.Controls
|
|||||||
Dim ActionButton8 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
Dim ActionButton8 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||||
Dim ActionButton9 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
Dim ActionButton9 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||||
Dim ActionButton10 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
Dim ActionButton10 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||||
|
Dim ActionButton11 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||||
Dim ListColumn1 As PersonalUtilities.Forms.Controls.Base.ListColumn = New PersonalUtilities.Forms.Controls.Base.ListColumn()
|
Dim ListColumn1 As PersonalUtilities.Forms.Controls.Base.ListColumn = New PersonalUtilities.Forms.Controls.Base.ListColumn()
|
||||||
Dim ListColumn2 As PersonalUtilities.Forms.Controls.Base.ListColumn = New PersonalUtilities.Forms.Controls.Base.ListColumn()
|
Dim ListColumn2 As PersonalUtilities.Forms.Controls.Base.ListColumn = New PersonalUtilities.Forms.Controls.Base.ListColumn()
|
||||||
|
Dim ActionButton12 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||||
|
Dim ActionButton13 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||||
|
Dim ActionButton14 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||||
|
Dim ActionButton15 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||||
|
Dim ActionButton16 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||||
|
Dim ActionButton17 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||||
|
Dim ActionButton18 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||||
Dim TT_MAIN As System.Windows.Forms.ToolTip
|
Dim TT_MAIN As System.Windows.Forms.ToolTip
|
||||||
Me.BTT_DOWN = New System.Windows.Forms.Button()
|
Me.BTT_DOWN = New System.Windows.Forms.Button()
|
||||||
Me.BTT_CANCEL = New System.Windows.Forms.Button()
|
Me.BTT_CANCEL = New System.Windows.Forms.Button()
|
||||||
@@ -58,6 +66,8 @@ Namespace API.YouTube.Controls
|
|||||||
Me.TXT_SUBS = New PersonalUtilities.Forms.Controls.TextBoxExtended()
|
Me.TXT_SUBS = New PersonalUtilities.Forms.Controls.TextBoxExtended()
|
||||||
Me.CH_DOWN_LYRICS = New System.Windows.Forms.CheckBox()
|
Me.CH_DOWN_LYRICS = New System.Windows.Forms.CheckBox()
|
||||||
Me.TXT_OUTPUT_PATH = New PersonalUtilities.Forms.Controls.ComboBoxExtended()
|
Me.TXT_OUTPUT_PATH = New PersonalUtilities.Forms.Controls.ComboBoxExtended()
|
||||||
|
Me.CMB_PLS = New PersonalUtilities.Forms.Controls.ComboBoxExtended()
|
||||||
|
Me.TXT_AUDIO_BITRATE = New PersonalUtilities.Forms.Controls.TextBoxExtended()
|
||||||
TP_MAIN = New System.Windows.Forms.TableLayoutPanel()
|
TP_MAIN = New System.Windows.Forms.TableLayoutPanel()
|
||||||
TP_BUTTONS = New System.Windows.Forms.TableLayoutPanel()
|
TP_BUTTONS = New System.Windows.Forms.TableLayoutPanel()
|
||||||
TP_PLS = New System.Windows.Forms.TableLayoutPanel()
|
TP_PLS = New System.Windows.Forms.TableLayoutPanel()
|
||||||
@@ -83,6 +93,8 @@ Namespace API.YouTube.Controls
|
|||||||
TP_LYRICS.SuspendLayout()
|
TP_LYRICS.SuspendLayout()
|
||||||
CType(Me.TXT_SUBS, System.ComponentModel.ISupportInitialize).BeginInit()
|
CType(Me.TXT_SUBS, System.ComponentModel.ISupportInitialize).BeginInit()
|
||||||
CType(Me.TXT_OUTPUT_PATH, System.ComponentModel.ISupportInitialize).BeginInit()
|
CType(Me.TXT_OUTPUT_PATH, System.ComponentModel.ISupportInitialize).BeginInit()
|
||||||
|
CType(Me.CMB_PLS, System.ComponentModel.ISupportInitialize).BeginInit()
|
||||||
|
CType(Me.TXT_AUDIO_BITRATE, System.ComponentModel.ISupportInitialize).BeginInit()
|
||||||
Me.SuspendLayout()
|
Me.SuspendLayout()
|
||||||
'
|
'
|
||||||
'TP_MAIN
|
'TP_MAIN
|
||||||
@@ -97,10 +109,10 @@ Namespace API.YouTube.Controls
|
|||||||
TP_MAIN.Margin = New System.Windows.Forms.Padding(0)
|
TP_MAIN.Margin = New System.Windows.Forms.Padding(0)
|
||||||
TP_MAIN.Name = "TP_MAIN"
|
TP_MAIN.Name = "TP_MAIN"
|
||||||
TP_MAIN.RowCount = 3
|
TP_MAIN.RowCount = 3
|
||||||
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 84.0!))
|
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 140.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.Percent, 100.0!))
|
||||||
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
|
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
|
||||||
TP_MAIN.Size = New System.Drawing.Size(434, 261)
|
TP_MAIN.Size = New System.Drawing.Size(434, 317)
|
||||||
TP_MAIN.TabIndex = 0
|
TP_MAIN.TabIndex = 0
|
||||||
'
|
'
|
||||||
'TP_BUTTONS
|
'TP_BUTTONS
|
||||||
@@ -112,14 +124,14 @@ Namespace API.YouTube.Controls
|
|||||||
TP_BUTTONS.Controls.Add(Me.BTT_DOWN, 1, 0)
|
TP_BUTTONS.Controls.Add(Me.BTT_DOWN, 1, 0)
|
||||||
TP_BUTTONS.Controls.Add(Me.BTT_CANCEL, 2, 0)
|
TP_BUTTONS.Controls.Add(Me.BTT_CANCEL, 2, 0)
|
||||||
TP_BUTTONS.Dock = System.Windows.Forms.DockStyle.Fill
|
TP_BUTTONS.Dock = System.Windows.Forms.DockStyle.Fill
|
||||||
TP_BUTTONS.Location = New System.Drawing.Point(0, 236)
|
TP_BUTTONS.Location = New System.Drawing.Point(0, 292)
|
||||||
TP_BUTTONS.Margin = New System.Windows.Forms.Padding(0)
|
TP_BUTTONS.Margin = New System.Windows.Forms.Padding(0)
|
||||||
TP_BUTTONS.Name = "TP_BUTTONS"
|
TP_BUTTONS.Name = "TP_BUTTONS"
|
||||||
TP_BUTTONS.RowCount = 1
|
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.Percent, 100.0!))
|
||||||
TP_BUTTONS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
|
TP_BUTTONS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
|
||||||
TP_BUTTONS.Size = New System.Drawing.Size(434, 25)
|
TP_BUTTONS.Size = New System.Drawing.Size(434, 25)
|
||||||
TP_BUTTONS.TabIndex = 2
|
TP_BUTTONS.TabIndex = 1
|
||||||
'
|
'
|
||||||
'BTT_DOWN
|
'BTT_DOWN
|
||||||
'
|
'
|
||||||
@@ -147,7 +159,7 @@ Namespace API.YouTube.Controls
|
|||||||
'SPLITTER_MAIN
|
'SPLITTER_MAIN
|
||||||
'
|
'
|
||||||
Me.SPLITTER_MAIN.Dock = System.Windows.Forms.DockStyle.Fill
|
Me.SPLITTER_MAIN.Dock = System.Windows.Forms.DockStyle.Fill
|
||||||
Me.SPLITTER_MAIN.Location = New System.Drawing.Point(3, 87)
|
Me.SPLITTER_MAIN.Location = New System.Drawing.Point(3, 143)
|
||||||
Me.SPLITTER_MAIN.Name = "SPLITTER_MAIN"
|
Me.SPLITTER_MAIN.Name = "SPLITTER_MAIN"
|
||||||
'
|
'
|
||||||
'SPLITTER_MAIN.Panel1
|
'SPLITTER_MAIN.Panel1
|
||||||
@@ -263,16 +275,20 @@ Namespace API.YouTube.Controls
|
|||||||
TP_SETTINGS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
TP_SETTINGS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
||||||
TP_SETTINGS.Controls.Add(TP_FORMATS, 0, 1)
|
TP_SETTINGS.Controls.Add(TP_FORMATS, 0, 1)
|
||||||
TP_SETTINGS.Controls.Add(TP_LYRICS, 0, 0)
|
TP_SETTINGS.Controls.Add(TP_LYRICS, 0, 0)
|
||||||
TP_SETTINGS.Controls.Add(Me.TXT_OUTPUT_PATH, 0, 2)
|
TP_SETTINGS.Controls.Add(Me.TXT_OUTPUT_PATH, 0, 3)
|
||||||
|
TP_SETTINGS.Controls.Add(Me.CMB_PLS, 0, 4)
|
||||||
|
TP_SETTINGS.Controls.Add(Me.TXT_AUDIO_BITRATE, 0, 2)
|
||||||
TP_SETTINGS.Dock = System.Windows.Forms.DockStyle.Fill
|
TP_SETTINGS.Dock = System.Windows.Forms.DockStyle.Fill
|
||||||
TP_SETTINGS.Location = New System.Drawing.Point(0, 0)
|
TP_SETTINGS.Location = New System.Drawing.Point(0, 0)
|
||||||
TP_SETTINGS.Margin = New System.Windows.Forms.Padding(0)
|
TP_SETTINGS.Margin = New System.Windows.Forms.Padding(0)
|
||||||
TP_SETTINGS.Name = "TP_SETTINGS"
|
TP_SETTINGS.Name = "TP_SETTINGS"
|
||||||
TP_SETTINGS.RowCount = 3
|
TP_SETTINGS.RowCount = 5
|
||||||
TP_SETTINGS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33333!))
|
TP_SETTINGS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 20.0!))
|
||||||
TP_SETTINGS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33333!))
|
TP_SETTINGS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 20.0!))
|
||||||
TP_SETTINGS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33333!))
|
TP_SETTINGS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 20.0!))
|
||||||
TP_SETTINGS.Size = New System.Drawing.Size(434, 84)
|
TP_SETTINGS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 20.0!))
|
||||||
|
TP_SETTINGS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 20.0!))
|
||||||
|
TP_SETTINGS.Size = New System.Drawing.Size(434, 140)
|
||||||
TP_SETTINGS.TabIndex = 1
|
TP_SETTINGS.TabIndex = 1
|
||||||
'
|
'
|
||||||
'TP_FORMATS
|
'TP_FORMATS
|
||||||
@@ -291,7 +307,7 @@ Namespace API.YouTube.Controls
|
|||||||
TP_FORMATS.RowCount = 1
|
TP_FORMATS.RowCount = 1
|
||||||
TP_FORMATS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
TP_FORMATS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
||||||
TP_FORMATS.Size = New System.Drawing.Size(434, 28)
|
TP_FORMATS.Size = New System.Drawing.Size(434, 28)
|
||||||
TP_FORMATS.TabIndex = 1
|
TP_FORMATS.TabIndex = 5
|
||||||
'
|
'
|
||||||
'TXT_FORMATS_ADDIT
|
'TXT_FORMATS_ADDIT
|
||||||
'
|
'
|
||||||
@@ -360,7 +376,7 @@ Namespace API.YouTube.Controls
|
|||||||
TP_LYRICS.RowCount = 1
|
TP_LYRICS.RowCount = 1
|
||||||
TP_LYRICS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
TP_LYRICS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
||||||
TP_LYRICS.Size = New System.Drawing.Size(434, 28)
|
TP_LYRICS.Size = New System.Drawing.Size(434, 28)
|
||||||
TP_LYRICS.TabIndex = 0
|
TP_LYRICS.TabIndex = 6
|
||||||
'
|
'
|
||||||
'TXT_SUBS
|
'TXT_SUBS
|
||||||
'
|
'
|
||||||
@@ -410,23 +426,28 @@ Namespace API.YouTube.Controls
|
|||||||
'TXT_OUTPUT_PATH
|
'TXT_OUTPUT_PATH
|
||||||
'
|
'
|
||||||
ActionButton7.BackgroundImage = CType(resources.GetObject("ActionButton7.BackgroundImage"), System.Drawing.Image)
|
ActionButton7.BackgroundImage = CType(resources.GetObject("ActionButton7.BackgroundImage"), System.Drawing.Image)
|
||||||
ActionButton7.Name = "Open"
|
ActionButton7.Name = "Save"
|
||||||
ActionButton7.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open
|
ActionButton7.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Save
|
||||||
ActionButton7.ToolTipText = "Choose a new location (Ctrl+O)"
|
ActionButton7.ToolTipText = "Save destination"
|
||||||
ActionButton8.BackgroundImage = CType(resources.GetObject("ActionButton8.BackgroundImage"), System.Drawing.Image)
|
ActionButton8.BackgroundImage = CType(resources.GetObject("ActionButton8.BackgroundImage"), System.Drawing.Image)
|
||||||
ActionButton8.Name = "Add"
|
ActionButton8.Name = "Open"
|
||||||
ActionButton8.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Add
|
ActionButton8.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open
|
||||||
ActionButton8.ToolTipText = "Choose a new location and add it to the list (Alt+O)"
|
ActionButton8.ToolTipText = "Choose a new location (Ctrl+O)"
|
||||||
ActionButton9.BackgroundImage = CType(resources.GetObject("ActionButton9.BackgroundImage"), System.Drawing.Image)
|
ActionButton9.BackgroundImage = CType(resources.GetObject("ActionButton9.BackgroundImage"), System.Drawing.Image)
|
||||||
ActionButton9.Name = "Clear"
|
ActionButton9.Name = "Add"
|
||||||
ActionButton9.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
|
ActionButton9.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Add
|
||||||
|
ActionButton9.ToolTipText = "Choose a new location and add it to the list (Alt+O)"
|
||||||
ActionButton10.BackgroundImage = CType(resources.GetObject("ActionButton10.BackgroundImage"), System.Drawing.Image)
|
ActionButton10.BackgroundImage = CType(resources.GetObject("ActionButton10.BackgroundImage"), System.Drawing.Image)
|
||||||
ActionButton10.Name = "ArrowDown"
|
ActionButton10.Name = "Clear"
|
||||||
ActionButton10.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.ArrowDown
|
ActionButton10.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
|
||||||
|
ActionButton11.BackgroundImage = CType(resources.GetObject("ActionButton11.BackgroundImage"), System.Drawing.Image)
|
||||||
|
ActionButton11.Name = "ArrowDown"
|
||||||
|
ActionButton11.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.ArrowDown
|
||||||
Me.TXT_OUTPUT_PATH.Buttons.Add(ActionButton7)
|
Me.TXT_OUTPUT_PATH.Buttons.Add(ActionButton7)
|
||||||
Me.TXT_OUTPUT_PATH.Buttons.Add(ActionButton8)
|
Me.TXT_OUTPUT_PATH.Buttons.Add(ActionButton8)
|
||||||
Me.TXT_OUTPUT_PATH.Buttons.Add(ActionButton9)
|
Me.TXT_OUTPUT_PATH.Buttons.Add(ActionButton9)
|
||||||
Me.TXT_OUTPUT_PATH.Buttons.Add(ActionButton10)
|
Me.TXT_OUTPUT_PATH.Buttons.Add(ActionButton10)
|
||||||
|
Me.TXT_OUTPUT_PATH.Buttons.Add(ActionButton11)
|
||||||
Me.TXT_OUTPUT_PATH.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.CheckBox
|
Me.TXT_OUTPUT_PATH.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.CheckBox
|
||||||
Me.TXT_OUTPUT_PATH.CaptionText = "Output path"
|
Me.TXT_OUTPUT_PATH.CaptionText = "Output path"
|
||||||
Me.TXT_OUTPUT_PATH.CaptionToolTipEnabled = True
|
Me.TXT_OUTPUT_PATH.CaptionToolTipEnabled = True
|
||||||
@@ -446,23 +467,82 @@ Namespace API.YouTube.Controls
|
|||||||
Me.TXT_OUTPUT_PATH.Columns.Add(ListColumn1)
|
Me.TXT_OUTPUT_PATH.Columns.Add(ListColumn1)
|
||||||
Me.TXT_OUTPUT_PATH.Columns.Add(ListColumn2)
|
Me.TXT_OUTPUT_PATH.Columns.Add(ListColumn2)
|
||||||
Me.TXT_OUTPUT_PATH.Dock = System.Windows.Forms.DockStyle.Fill
|
Me.TXT_OUTPUT_PATH.Dock = System.Windows.Forms.DockStyle.Fill
|
||||||
Me.TXT_OUTPUT_PATH.Location = New System.Drawing.Point(3, 59)
|
Me.TXT_OUTPUT_PATH.Location = New System.Drawing.Point(3, 87)
|
||||||
Me.TXT_OUTPUT_PATH.Name = "TXT_OUTPUT_PATH"
|
Me.TXT_OUTPUT_PATH.Name = "TXT_OUTPUT_PATH"
|
||||||
Me.TXT_OUTPUT_PATH.Size = New System.Drawing.Size(428, 22)
|
Me.TXT_OUTPUT_PATH.Size = New System.Drawing.Size(428, 22)
|
||||||
Me.TXT_OUTPUT_PATH.TabIndex = 2
|
Me.TXT_OUTPUT_PATH.TabIndex = 2
|
||||||
Me.TXT_OUTPUT_PATH.TextBoxBorderStyle = System.Windows.Forms.BorderStyle.FixedSingle
|
Me.TXT_OUTPUT_PATH.TextBoxBorderStyle = System.Windows.Forms.BorderStyle.FixedSingle
|
||||||
'
|
'
|
||||||
|
'CMB_PLS
|
||||||
|
'
|
||||||
|
ActionButton12.BackgroundImage = CType(resources.GetObject("ActionButton12.BackgroundImage"), System.Drawing.Image)
|
||||||
|
ActionButton12.Name = "Save"
|
||||||
|
ActionButton12.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Save
|
||||||
|
ActionButton12.ToolTipText = "Save playlist"
|
||||||
|
ActionButton13.BackgroundImage = CType(resources.GetObject("ActionButton13.BackgroundImage"), System.Drawing.Image)
|
||||||
|
ActionButton13.Name = "List"
|
||||||
|
ActionButton13.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.List
|
||||||
|
ActionButton13.ToolTipText = "Select multiple playlists"
|
||||||
|
ActionButton14.BackgroundImage = CType(resources.GetObject("ActionButton14.BackgroundImage"), System.Drawing.Image)
|
||||||
|
ActionButton14.Name = "Open"
|
||||||
|
ActionButton14.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open
|
||||||
|
ActionButton14.ToolTipText = "Choose an output file"
|
||||||
|
ActionButton15.BackgroundImage = CType(resources.GetObject("ActionButton15.BackgroundImage"), System.Drawing.Image)
|
||||||
|
ActionButton15.Name = "Add"
|
||||||
|
ActionButton15.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Add
|
||||||
|
ActionButton15.ToolTipText = "Choose an output file (add a new location to the list)"
|
||||||
|
ActionButton16.BackgroundImage = CType(resources.GetObject("ActionButton16.BackgroundImage"), System.Drawing.Image)
|
||||||
|
ActionButton16.Name = "Clear"
|
||||||
|
ActionButton16.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
|
||||||
|
ActionButton17.BackgroundImage = CType(resources.GetObject("ActionButton17.BackgroundImage"), System.Drawing.Image)
|
||||||
|
ActionButton17.Name = "ArrowDown"
|
||||||
|
ActionButton17.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.ArrowDown
|
||||||
|
Me.CMB_PLS.Buttons.Add(ActionButton12)
|
||||||
|
Me.CMB_PLS.Buttons.Add(ActionButton13)
|
||||||
|
Me.CMB_PLS.Buttons.Add(ActionButton14)
|
||||||
|
Me.CMB_PLS.Buttons.Add(ActionButton15)
|
||||||
|
Me.CMB_PLS.Buttons.Add(ActionButton16)
|
||||||
|
Me.CMB_PLS.Buttons.Add(ActionButton17)
|
||||||
|
Me.CMB_PLS.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.Label
|
||||||
|
Me.CMB_PLS.CaptionText = "Playlist"
|
||||||
|
Me.CMB_PLS.CaptionToolTipEnabled = True
|
||||||
|
Me.CMB_PLS.CaptionToolTipText = "Add downloaded item(s) to playlist"
|
||||||
|
Me.CMB_PLS.CaptionVisible = True
|
||||||
|
Me.CMB_PLS.CaptionWidth = 50.0R
|
||||||
|
Me.CMB_PLS.Dock = System.Windows.Forms.DockStyle.Fill
|
||||||
|
Me.CMB_PLS.Location = New System.Drawing.Point(3, 115)
|
||||||
|
Me.CMB_PLS.Name = "CMB_PLS"
|
||||||
|
Me.CMB_PLS.Size = New System.Drawing.Size(428, 22)
|
||||||
|
Me.CMB_PLS.TabIndex = 3
|
||||||
|
Me.CMB_PLS.TextBoxBorderStyle = System.Windows.Forms.BorderStyle.FixedSingle
|
||||||
|
'
|
||||||
|
'TXT_AUDIO_BITRATE
|
||||||
|
'
|
||||||
|
ActionButton18.BackgroundImage = CType(resources.GetObject("ActionButton18.BackgroundImage"), System.Drawing.Image)
|
||||||
|
ActionButton18.Name = "Clear"
|
||||||
|
ActionButton18.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
|
||||||
|
Me.TXT_AUDIO_BITRATE.Buttons.Add(ActionButton18)
|
||||||
|
Me.TXT_AUDIO_BITRATE.CaptionText = "Audio bitrate"
|
||||||
|
Me.TXT_AUDIO_BITRATE.CaptionToolTipEnabled = True
|
||||||
|
Me.TXT_AUDIO_BITRATE.CaptionToolTipText = "Default audio bitrate if you want to change it during download"
|
||||||
|
Me.TXT_AUDIO_BITRATE.CaptionWidth = 112.0R
|
||||||
|
Me.TXT_AUDIO_BITRATE.Dock = System.Windows.Forms.DockStyle.Fill
|
||||||
|
Me.TXT_AUDIO_BITRATE.Location = New System.Drawing.Point(3, 59)
|
||||||
|
Me.TXT_AUDIO_BITRATE.Name = "TXT_AUDIO_BITRATE"
|
||||||
|
Me.TXT_AUDIO_BITRATE.Size = New System.Drawing.Size(428, 22)
|
||||||
|
Me.TXT_AUDIO_BITRATE.TabIndex = 4
|
||||||
|
'
|
||||||
'MusicPlaylistsForm
|
'MusicPlaylistsForm
|
||||||
'
|
'
|
||||||
Me.AcceptButton = Me.BTT_DOWN
|
Me.AcceptButton = Me.BTT_DOWN
|
||||||
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
|
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
|
||||||
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
|
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
|
||||||
Me.CancelButton = Me.BTT_CANCEL
|
Me.CancelButton = Me.BTT_CANCEL
|
||||||
Me.ClientSize = New System.Drawing.Size(434, 261)
|
Me.ClientSize = New System.Drawing.Size(434, 317)
|
||||||
Me.Controls.Add(TP_MAIN)
|
Me.Controls.Add(TP_MAIN)
|
||||||
Me.Icon = Global.SCrawler.My.Resources.SiteYouTube.YouTubeMusicIcon_32
|
Me.Icon = Global.SCrawler.My.Resources.SiteYouTube.YouTubeMusicIcon_32
|
||||||
Me.KeyPreview = True
|
Me.KeyPreview = True
|
||||||
Me.MinimumSize = New System.Drawing.Size(450, 300)
|
Me.MinimumSize = New System.Drawing.Size(450, 356)
|
||||||
Me.Name = "MusicPlaylistsForm"
|
Me.Name = "MusicPlaylistsForm"
|
||||||
Me.Text = "Albums"
|
Me.Text = "Albums"
|
||||||
TP_MAIN.ResumeLayout(False)
|
TP_MAIN.ResumeLayout(False)
|
||||||
@@ -481,6 +561,8 @@ Namespace API.YouTube.Controls
|
|||||||
TP_LYRICS.PerformLayout()
|
TP_LYRICS.PerformLayout()
|
||||||
CType(Me.TXT_SUBS, System.ComponentModel.ISupportInitialize).EndInit()
|
CType(Me.TXT_SUBS, System.ComponentModel.ISupportInitialize).EndInit()
|
||||||
CType(Me.TXT_OUTPUT_PATH, System.ComponentModel.ISupportInitialize).EndInit()
|
CType(Me.TXT_OUTPUT_PATH, System.ComponentModel.ISupportInitialize).EndInit()
|
||||||
|
CType(Me.CMB_PLS, System.ComponentModel.ISupportInitialize).EndInit()
|
||||||
|
CType(Me.TXT_AUDIO_BITRATE, System.ComponentModel.ISupportInitialize).EndInit()
|
||||||
Me.ResumeLayout(False)
|
Me.ResumeLayout(False)
|
||||||
|
|
||||||
End Sub
|
End Sub
|
||||||
@@ -496,5 +578,7 @@ Namespace API.YouTube.Controls
|
|||||||
Private WithEvents SPLITTER_MAIN As SplitContainer
|
Private WithEvents SPLITTER_MAIN As SplitContainer
|
||||||
Private WithEvents CH_DOWN_LYRICS As CheckBox
|
Private WithEvents CH_DOWN_LYRICS As CheckBox
|
||||||
Private WithEvents TXT_OUTPUT_PATH As PersonalUtilities.Forms.Controls.ComboBoxExtended
|
Private WithEvents TXT_OUTPUT_PATH As PersonalUtilities.Forms.Controls.ComboBoxExtended
|
||||||
|
Private WithEvents CMB_PLS As PersonalUtilities.Forms.Controls.ComboBoxExtended
|
||||||
|
Private WithEvents TXT_AUDIO_BITRATE As PersonalUtilities.Forms.Controls.TextBoxExtended
|
||||||
End Class
|
End Class
|
||||||
End Namespace
|
End Namespace
|
||||||
@@ -222,6 +222,13 @@
|
|||||||
</value>
|
</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ActionButton7.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="ActionButton7.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>
|
||||||
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
||||||
|
wwAADsMBx2+oZAAAAFFJREFUOE9joAr49u3bf1Lw169f50O1QgBI0MnJCY4/vP8Ix8hiILqtrQ3TEFIM
|
||||||
|
AGGYIVDtpBsAwkQbgIyR1dDWAGLwqAGD0gByMFQ7JYCBAQChNviRiQ8ETwAAAABJRU5ErkJggg==
|
||||||
|
</value>
|
||||||
|
</data>
|
||||||
|
<data name="ActionButton8.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
<value>
|
<value>
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
||||||
wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP
|
wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP
|
||||||
@@ -232,7 +239,7 @@
|
|||||||
cMaRN0UdBBkAAAAASUVORK5CYII=
|
cMaRN0UdBBkAAAAASUVORK5CYII=
|
||||||
</value>
|
</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ActionButton8.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="ActionButton9.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
<value>
|
<value>
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6
|
iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6
|
||||||
JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAADmUlE
|
JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAADmUlE
|
||||||
@@ -254,7 +261,7 @@
|
|||||||
0AUyNxOP1DOwcaG/8I+/LRB+At7psBnyDBG0AAAAAElFTkSuQmCC
|
0AUyNxOP1DOwcaG/8I+/LRB+At7psBnyDBG0AAAAAElFTkSuQmCC
|
||||||
</value>
|
</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ActionButton9.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="ActionButton10.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
<value>
|
<value>
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
||||||
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
|
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
|
||||||
@@ -262,7 +269,7 @@
|
|||||||
AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC
|
AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC
|
||||||
</value>
|
</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ActionButton10.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="ActionButton11.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
<value>
|
<value>
|
||||||
iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAABGdBTUEAALGPC/xhBQAAE65JREFUeF7t
|
iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAABGdBTUEAALGPC/xhBQAAE65JREFUeF7t
|
||||||
3X2sJWddB/DdLi2lQG2hdOHuvfM887J7Cxca4ELTQMDWKigIFpBAEAgi9g+CJpJo9Q8NJhgBiYZIYspL
|
3X2sJWddB/DdLi2lQG2hdOHuvfM887J7Cxca4ELTQMDWKigIFpBAEAgi9g+CJpJo9Q8NJhgBiYZIYspL
|
||||||
@@ -350,6 +357,161 @@
|
|||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6LEtW/4flgYiLD1qeX0A
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6LEtW/4flgYiLD1qeX0A
|
||||||
AAAASUVORK5CYII=
|
AAAASUVORK5CYII=
|
||||||
|
</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+oZAAAAFFJREFUOE9joAr49u3bf1Lw169f50O1QgBI0MnJCY4/vP8Ix8hiILqtrQ3TEFIM
|
||||||
|
AGGYIVDtpBsAwkQbgIyR1dDWAGLwqAGD0gByMFQ7JYCBAQChNviRiQ8ETwAAAABJRU5ErkJggg==
|
||||||
|
</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+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAeElE
|
||||||
|
QVQ4T2P4//8/RRhMFHQfKgDi/yAaXQEhDCZAmkNbnvyXta4CciESLEws//FhmDqYAQUgzUBMngsowVgF
|
||||||
|
ScFgYjQQsUsQi8FEYsXyAiD+D6LRFRDCYAKk2bPo6H9J40wgFyKBLeCQMUwdzIACkGYgHnKB+J8BAD5Q
|
||||||
|
tqhi4tzWAAAAAElFTkSuQmCC
|
||||||
|
</value>
|
||||||
|
</data>
|
||||||
|
<data name="ActionButton14.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=
|
||||||
|
</value>
|
||||||
|
</data>
|
||||||
|
<data name="ActionButton15.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>
|
||||||
|
iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6
|
||||||
|
JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAADmUlE
|
||||||
|
QVRIS62WWWxMURjHL220JW1HausmlFrDFKUhnUGH6bRFzJ2idImlC0Vp2mlji1A8iNhCPIjIRES8EU+W
|
||||||
|
h2oEtbSDTk3HNNM7S01VKsXjkb/vXBo3k1Ee7sMvmZzzzf//ne/+z50RAAxL1MUIG4G/YAv3HSVhF5Vw
|
||||||
|
IYNdz3LadVj9RgdTB+HQYYPHIJuE1ocSdlEJFzG+1bPRLQLinglIeCkg+XUkKvz56hnkOfQs/rmA8S9H
|
||||||
|
YEp7FDI64tAQtKhnsMapZ7zzNHsUFnbGY4VzIk70l6hnIH4wsDR7NBZ3apDrSqL5T8eFgUr1DLZ78lim
|
||||||
|
Q4N8VzK29MxEpZSBa4M16hnU+c3M9CEFpdJsVHsXos63DDcHrf9nQEXD5VymwW/5USLNwl5vJhp7dTgW
|
||||||
|
NML2pR7jbsUMS+KdMTa5Q8NQxinfBU4dRFcOyjy52OtbhwOBDTgZLKPPmTgY0ON4MBdNfSbYBupxY8Aq
|
||||||
|
G10dqMG5/nIc7ytGQ6CQRliAamkTN/g1Ai4e95Qy3iogpX0UtBRDnhRzdxq2SXOxz5eFQ70rScCEU335
|
||||||
|
ssGxj0YS06HSm4GN3ekwdE2C1hGH1LZR0JDOJof5jwHvnIvzTa0jlooTYfktvt+fhcOBHDQFTWRgxJGP
|
||||||
|
ObAGsulZLMLWnjlY756K5c4JmNcRi6T2SGheCIihS2l5ozAo6NRhMolnUAcGV6IcwwqvFrX+JTjYuwKH
|
||||||
|
SfRAYDms/mzs9y1GFe2VSnOw1j0FejqpLN4WCX4ZufiIBwLMLxQGm12rsLQzgWKYgmLPLNTQw6ynpDSS
|
||||||
|
IBet8y+TqaVRVdFIeJrWuCcj+/0EzH43BomvIhBLI45uFiDcJ+6QwROFwa6+Amb9bGFNg6Xs9Ncd7Oy3
|
||||||
|
Knb2eyU7/20nu9y/m136tIvEl6BC0qKoZwby3alo9JVhj7T5R7m/kJVIIityi8zyXmTiW+I10SqyIQNb
|
||||||
|
uIgNwYuuf25kFd75KPKkI49OmUWnrfYWyXv/wBb2cijhhVf6a9lGei65XclYRDd6mj0GWz2iLBJaH0rY
|
||||||
|
RSVc5Eywmhm7kuQXHX+bJlBStrh+zTi0PpSwi0q4yNFAOVvgiEcKJWUsxZn/NhT+znlofShhF5VwkRpv
|
||||||
|
MUtti4KGYjj6sYCIh5QSu4oG27stjItHU+cjeQzvkcFzFQ2KnSKLoc4FukDCXeI2GbSoaFD4ziyPxNxK
|
||||||
|
0AUyNxOP1DOwcaG/8I+/LRB+At7psBnyDBG0AAAAAElFTkSuQmCC
|
||||||
|
</value>
|
||||||
|
</data>
|
||||||
|
<data name="ActionButton16.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>
|
||||||
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
||||||
|
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
|
||||||
|
tbbB43rK5xSAQq1VYFtmeQBoqZTSreVZvgTknM8yyyjA/qodsDF9gspD2Bj6B+DH+NqzhQQAG+POMnSX
|
||||||
|
AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC
|
||||||
|
</value>
|
||||||
|
</data>
|
||||||
|
<data name="ActionButton17.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>
|
||||||
|
iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAABGdBTUEAALGPC/xhBQAAE65JREFUeF7t
|
||||||
|
3X2sJWddB/DdLi2lQG2hdOHuvfM887J7Cxca4ELTQMDWKigIFpBAEAgi9g+CJpJo9Q8NJhgBiYZIYspL
|
||||||
|
GlAKCkhEC4KgQlsLQkqhKi/lrYWWlxaw3dLddrerz/Q89+7dc2fbfTn3npf5fJJv2rS758z85nnOzJz5
|
||||||
|
nZktAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMK3O3r79wVUIz65jfGNVxI/VIX69CvGO9M//a9P+e8o3B/8v
|
||||||
|
vKn9s+3fyX8dAJgmaWd+fl3E96Wd/E9XdvZHkfbvXNa+Rn45AGCS3bvjj/E/h3box5OrmxjPyy8PAEyS
|
||||||
|
XXO7zqhCeH/HDnwUOdCE+J6zdux4eH47YIrEGE8uy/Ls9Bnx/LooL0oH9b9Th/I1TVG+rCqKC+q6Xsh/
|
||||||
|
FJgmO8vy6WknfdPQTnsjckMdwlPy2wITLO3wF6si/lGas1ekuXvX0Fzuyg9S3psOCl6qDwimQB3ji9Ok
|
||||||
|
3btmEm907kpnEa/Mbw9Mlq1pB/6cdHZ/ZcfcPZrcXoXyrVVVFfl1gUmSdsS/libqPUMTd5NSvjktwrbB
|
||||||
|
kgDjVi1UT26K+Nnu+XrMuaud60uPWHpIfhtg3JqyfEaanHcPTdZNTRPCPy4uLj40LxIwBudt2fKAtOP/
|
||||||
|
0zQnN+5koIg3tpca81sC49J+LZcm5a3rJulYEq6LSV40YBOFEB6V5uFV6+flRiTsSwf9r81vDYzBCSO4
|
||||||
|
vjfq/KAuiqfm5QM2QRPjuWnubUbz71DCn6W33zpYCmDT1EX5m92Tcuy5q47xFXkxgQ3UduqnOXfn0Bzc
|
||||||
|
xJSvz4sCbIb2pzlp8v1w/WScnKSzkjekRT1hsMTAKC0vL5/Ydud3zb1NT1FelBcL2GiDm3d0TMTJy0ea
|
||||||
|
pjk1LzYwAu3NvtLc+uTQXBtn7tYYCJtja/vQno5JOJFpQrzWb4hhNJoQnpjm1Q3D82wCcnNRFKfnxQQ2
|
||||||
|
Qttk1zH5JjzhFmcIcHzyzb6O5aFem5J0sP/OvKjARmg7b7sm3xRkT3vDorwawJHb1t6Ep2NOTVoOtDch
|
||||||
|
yssMjFr6IPh8x8SbnsT4lrQamgPhCMzPzz+sifHjnXNpMnN5XnRglJaWlk5KE2z/0ISbxnzQQ0bgvlXz
|
||||||
|
1ePSXPnG0NyZ+DRF8Zi8CsCo7Azh0V0TbkrzRc2B0G3wIJ9429CcmZLce4MgYJTyff87JtzU5uayLM/J
|
||||||
|
qwcM7vD5+jQ3DgzNlWnKDXldgFFJZwW/2jHZpj1727uZ5VWE3mofqJXmw4eG5sdUpqqqXXm1gFGoQnhJ
|
||||||
|
12SbgRxoYvzjtIruK04vxRjPSvPgK0PzYmqTPqtemVcNGIU6xgu7JtusJH1ovH9ubu6UvLrQC2ncPyuN
|
||||||
|
/58Mz4fpTvnmvHrAKJQL5dO6J9ssJXxucWFhLq8yzLKtaUf5h2ncb9zz+8eUKsYP53UERmHX/PyOrsk2
|
||||||
|
g7nJDUWYZUuPWHpIE8oPdIz92UiMn86rCoxIOmOYta8KD5uftk2Peb1hZtTzdVOHcF3HmJ+ZVCF+Ia8u
|
||||||
|
MCppcl0+PNlmOG1zYPtYYc2BzIQ0np+ZxvWPh8b5LObqvMrAqEzRo4BHmctijCfnEsBUqkP5u2ksz8Kd
|
||||||
|
PI8g5SfyagOj0jbIpQk2c01DR5Brmh3NfC4DTI324LWO8V0dY3pm48mAsEGm7OEgo0sRb9wZ4+NzGWDi
|
||||||
|
lWUZ0ti9Zt1YnvUU8fdyCYBRmsFbAh9xqhDvqEN4Xi4FTKz8s93vD4/hPiSdpJyXywCMWPtrgKuGJ12P
|
||||||
|
ck/6gPmDXAuYOHVR/lY6UN3XMXb7kDv17MAGqhaqJ6WJ1sdegDUJ726a5oG5JDB2917vL+Kl3eO1N/lQ
|
||||||
|
LgewUdIO8E0dk69vubosy+25JDA2bYNuFeJnOsZovxLjhbkkwEZZXl4+0QfOvfl2Ogg4O5cFNl1dFE9N
|
||||||
|
4/B7Q+Oyj7mh/VzKZQE2UtM0j6iL+LWOidizhN3OPBiHuigvSmPwrvVjsn9pQnh1LguwGQa3Fo3fHp6M
|
||||||
|
Pcw97c1WcllgQy0tLZ2UDr7/qmMc9jJNiF/WkwNjMHhQ0GzfX/yIU8RLfRCxkdq+kzTfrugcf/3MgZ1l
|
||||||
|
+fRcHmCztU8Yq2P8h47J2cdcpTmQjdCE8IQ0vnzjdkjKP8nlAcZoWxXin3dP0n4l1eGb9UL92FwXOG51
|
||||||
|
Ub48ja09w2Otz2nvTJpKs21QIWDs0lnKb6TJqTEphN3NQvncXBY4VtvSju4N3WOs17l6cXHxoblGwKRo
|
||||||
|
r8mlHeAtHZO2b9mfDohem8sCR2XX3K4z0hj65NCYklSTGONpuUzApNlVFFWaqP81NHF7mvD2tnM7lwbu
|
||||||
|
V/vwqTR2vrV+LPU7VSjf4ff+MAU0B65NeWVd12fm0sBhpTnzosHDp7rGUV8T9lVFvDiXCJgSrmEezDea
|
||||||
|
onhMrgsM25rmyuvSODkwNG56nvZyYvi5XCNg2mgOXM3tVVH9ci4L3KtpmlN9W7Y+VYhfiEkuEzCt8n3L
|
||||||
|
fzA8yXuY/b7OZEVZlovt3ew6xknf8965ublTcpmAaac5cG3C2zQ09Vv7bVAaC/+7fmz0Og6QYVZpDlyT
|
||||||
|
GD/dPlgpl4b+2Nru5NIYuGfdmOhxmhB/VBblL+QaATNKc+DBfH1nCI/OdWHGtTewSdv874fGgIT4xfYb
|
||||||
|
wlwmYNZpDlzNbVUIz85lYUblJ2i6BDacGP/u7O3bH5zLBPSF5sDV7K+L+Nu5LMyYtJP7xbSNfzy0zfue
|
||||||
|
A+03gak8WwdVAnpHc+CaxHiJ5sCZsnK9f/+6bd3v3JZ2/r+SawT0mebAg0kfjB93v/Pp136t3X693bWN
|
||||||
|
e56v6nsBhmkOXE24Ph0EnJXrwpSp63qhDuXnu7dtn1P+U1VVP5PLBHAozYGDtD+LchvU6TN4Iqa+lqGs
|
||||||
|
XO8/YVAlgMPQHLiSsC+dNb0ml4UJVxflRWm73b1+O/Y5YXcVwvNziQDun+bANYnxkvO2bHlALg0TJsZ4
|
||||||
|
cl3ESzu3Xa8Trm+KYimXCeDIaQ48mKqIH9McOHl2zc/vaIr42a5t1vN8tCiK03OZAI6J5sCVFPFr7QNk
|
||||||
|
cl0Ys3yp6nvrtlO/s3K9f9ugSgDHSXPgILk58PxcFsYkX+93J8s1qUK8oynKF+YSAYyO5sCVhH3pgOjV
|
||||||
|
uSxsoqZpHpjq//bu7dLjFPHGND+Xc5kARk9z4JrE+JZUEl+1bpLFhYW5VPf/WLcd5N/ruj4zlwlg42gO
|
||||||
|
PCQfdXOVjdeE8MRU6xuGai9uXw2MgebA1YTrFkMoc10YsaYoX5rqfOf6uvc6e9LO/xW5RACbT3Pgam5N
|
||||||
|
B0Q/m8vCCLT3XnCQ2ZXwnWqhenIuE8D4aA5czV3OykZj19yuM1I9PzlUXwnhirIst+cyAYyf5sA1GTQH
|
||||||
|
uu/6MdoZ4+NTHb+1rq59j+v9wKTSHHhIPtI0zam5NByhNH5enGr306Fa9j1720ttuUQAE0tz4Epi/FJM
|
||||||
|
cl24b8ZNd25KdTk31whg8mkOXEm4pX1EbS4LHebn5x+WdnIf765fr3NVCOFRuUwA00Nz4Gr21kX58lwW
|
||||||
|
1qjmq8el+nxjqF4S4yVLS0sn5TIBTB/NgWuiOfAQTVE+J9XltnV16nXCvqqIF+cSAUw3zYGH5INnb9/+
|
||||||
|
4Fyavtra7uRSLe4Zqk3f88MmxvNyjQBmhiavg/liVVVFrkuvLC4uPjSt/4eG6iEhXlOWZchlApg9mgNX
|
||||||
|
c3P6wD8nl6UXqvlqZ1rv/xmqQ+/ThPJv5ufnH5TLBDC7NAeuZm97n/tclplWhfCstL4/GVr/nsf1fqCH
|
||||||
|
NAeu5kB7aSSVZFabA13v786tVVFckGsE0C+aAw8mnSG/f25u7pRcmpnQbt8mlB/oWt8+pwnxWk+PBNAc
|
||||||
|
uJoqxC/MSnNgs7BQ1yFc17WePc97Z+1AD+C4aA5czU3T/qjXdED3zLQePx5ar75nf77ev3VQJQBWaQ5c
|
||||||
|
zZ4qhJfkskyVuigvapvbOtapt2lC/FFTls/IJQKgi+bA1aw0B07FGWOM8eQ6xnd1rEe/E+OX2jGdywTA
|
||||||
|
fdEcuCYx/u2kXzNudjTz6az/c53L3+NUMX7YI6EBjp7mwIO5pt3J5rpMlHKhfFpavu8PLW/fM+s/7QTY
|
||||||
|
eJoDV/PduiiWc1kmwuB6f7x7aDn7ntvrGC/MJQLgeGgOHKQK8Y46hOflsoxN0zQPbEJ8Z9cy9jxf3RnC
|
||||||
|
o3OZABgFzYGrab9efl0qyViaAxcXFubSgchnOpar77k8xnhaLhMAo6Q5cE2K+L7NfoBMHcJT0nvfvG5Z
|
||||||
|
+h3X+wE2iebAg7m6LMvtuS4bKl/v14txSMLuKoQX5BIBsBk0B64kfCfV4om5LCN33pYtD3DA1ZVwfb1Q
|
||||||
|
PzaXCYDNpDlwJWH3RnSe75rbdUZ6/X9b/369z0eLojg9lwmAcdAcuJqV5sCRaEJ4QnrNbw+9R9+zcr1/
|
||||||
|
26BKAIyV5sBDcll7W95cmmPSPocgvc6dQ6/b9+xpivJluUQATBDNgQdz1TE2B6phV4p446TdhAmAIens
|
||||||
|
9ZXpQ1tz4KA58Am5LPfrrB07Hp7+zr90v1Z/UxXxU3Vdn5nLBMAk0xy4krC7WSifm8tyWGVZnl2F+M3u
|
||||||
|
1+hxYrxkeXn5xFwmAKaB5sDV7E9nsRfnsqyTdnIvGtxiuPPv9jV7Ul1ekUsEwLTRHHgwVSjfsbS0dFIu
|
||||||
|
TWtr+6uB9P8ODP/Znue7ZVmek2sEwBTT2Laa8sr2enb7jHoHRl0JV8QYH5nHDQCzwJ0DV/P1tKO7vuO/
|
||||||
|
9zpVKN/qej/AjNIcKB3Z24TyVXmIADCrNAfKmtzUxHhuHhoAzDrNgZJyVQjhUXlIANAjmgN7m/Du471d
|
||||||
|
MgBTzp0D+5Sw777uiQBAz2gO7EPCLSnn500OAAOaA2c615RlGfKmBoBDaQ6cvTQhvmd+fv5BeRMDwGFp
|
||||||
|
DpyJuN4PwDHQHDjVubUqigvypgSAo6M5cPrShHjtYghl3oQAcGw0B05Rivi+ubm5U/KmA4Djozlw4rM/
|
||||||
|
X+/fOthiADA6mgMnME2IP2rK8hl5GwHAxtAcOFH5SozxrLxpAGBjaQ4cf6oYP9w0zal5kwDA5tAcOLYc
|
||||||
|
aC/FpE1wwmBLAMAm0xy46bk91fvCXH4AGCvNgZuRIn6tKYrH5JoDwGTQHLihuTzGeFouNQBMFs2BI4/r
|
||||||
|
/QBMB82Bo0rYXYXwglxWAJh8mgOPN+H6eqF+bC4nAEwVzYHHkiL+c1EUp+caAsB00hx4FInxLalk2waV
|
||||||
|
A4AppznwfrOnLsqX53IBwOzQHHiYFPHGaqF6Ui4TAMwezYGHpirip+q6PjOXBwBmmubANjFesry8fGKu
|
||||||
|
CQD0Q4+bA/dWMf56LgMA9E8PmwO/W5blOXn1AaC/+tMcWF4ZY3xkXm0AYOabA2O8ZGlp6aS8ugDAGrPY
|
||||||
|
HLi3CeWr8voBAIczQ82BN6UDmnPzagEA92f6mwPLz1dVVeTVAQCO1LQ2B1Yh/PX8/PyD8moAAEdrupoD
|
||||||
|
w76qiBfnRQcAjtMUNAeGW1LOz8sLAIzKBDcHXlOWZciLCQCM2gQ2B142Nzd3Sl48AGCjTEhz4H7X+wFg
|
||||||
|
k425OfDWqqh+Pi8KALDJtqWDgDemHfKBoR30hqUJ8dqY5PcHAMalKcrnpJ3z94Z31qNO+/t+1/sBYIKk
|
||||||
|
k/LT6hD+Mu2oR/4rgXTW/+X02r+U3woAmDTtz/GaIv5F2nH/ZHhHfpS5J+Vf01n/S9LLbhu8OgAw0dpb
|
||||||
|
8TYL5XPTmfvb0o78v/MOvWtHvybtzXzKT1Qx/n5d1wv5pQCAaXXvAUFRLLXd+3WMFzZF+cKUl7X/rIri
|
||||||
|
gsWFhbn8RwEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6LEtW/4flgYiLD1qeX0A
|
||||||
|
AAAASUVORK5CYII=
|
||||||
|
</value>
|
||||||
|
</data>
|
||||||
|
<data name="ActionButton18.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>
|
||||||
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
||||||
|
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
|
||||||
|
tbbB43rK5xSAQq1VYFtmeQBoqZTSreVZvgTknM8yyyjA/qodsDF9gspD2Bj6B+DH+NqzhQQAG+POMnSX
|
||||||
|
AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC
|
||||||
</value>
|
</value>
|
||||||
</data>
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -19,10 +19,17 @@ Namespace API.YouTube.Controls
|
|||||||
Friend Class MusicPlaylistsForm : Implements IDesignXMLContainer
|
Friend Class MusicPlaylistsForm : Implements IDesignXMLContainer
|
||||||
#Region "Declarations"
|
#Region "Declarations"
|
||||||
Private MyView As FormView
|
Private MyView As FormView
|
||||||
|
Private ReadOnly MyFieldsChecker As FieldsChecker
|
||||||
Friend Property DesignXML As EContainer Implements IDesignXMLContainer.DesignXML
|
Friend Property DesignXML As EContainer Implements IDesignXMLContainer.DesignXML
|
||||||
Private Property DesignXMLNodes As String() Implements IDesignXMLContainer.DesignXMLNodes
|
Private Property DesignXMLNodes As String() Implements IDesignXMLContainer.DesignXMLNodes
|
||||||
Private Property DesignXMLNodeName As String Implements IDesignXMLContainer.DesignXMLNodeName
|
Private Property DesignXMLNodeName As String Implements IDesignXMLContainer.DesignXMLNodeName
|
||||||
Private ReadOnly MyContainer As IYouTubeMediaContainer
|
Private ReadOnly MyContainer As IYouTubeMediaContainer
|
||||||
|
Private ReadOnly M3U8Files As List(Of SFile)
|
||||||
|
Private ReadOnly Property M3U8FilesFull As List(Of SFile)
|
||||||
|
Get
|
||||||
|
Return ListAddList(Nothing, M3U8Files, LAP.NotContainsOnly).ListAddValue(CMB_PLS.Text, LAP.NotContainsOnly)
|
||||||
|
End Get
|
||||||
|
End Property
|
||||||
Private Initializing As Boolean = True
|
Private Initializing As Boolean = True
|
||||||
Private ReadOnly Property Current As IYouTubeMediaContainer
|
Private ReadOnly Property Current As IYouTubeMediaContainer
|
||||||
Get
|
Get
|
||||||
@@ -40,7 +47,9 @@ Namespace API.YouTube.Controls
|
|||||||
#Region "Initializer"
|
#Region "Initializer"
|
||||||
Friend Sub New(ByVal Container As IYouTubeMediaContainer)
|
Friend Sub New(ByVal Container As IYouTubeMediaContainer)
|
||||||
InitializeComponent()
|
InitializeComponent()
|
||||||
|
M3U8Files = New List(Of SFile)
|
||||||
MyContainer = Container
|
MyContainer = Container
|
||||||
|
MyFieldsChecker = New FieldsChecker
|
||||||
End Sub
|
End Sub
|
||||||
#End Region
|
#End Region
|
||||||
#Region "Form handlers"
|
#Region "Form handlers"
|
||||||
@@ -52,6 +61,9 @@ Namespace API.YouTube.Controls
|
|||||||
End If
|
End If
|
||||||
|
|
||||||
MyYouTubeSettings.DownloadLocations.PopulateComboBox(TXT_OUTPUT_PATH)
|
MyYouTubeSettings.DownloadLocations.PopulateComboBox(TXT_OUTPUT_PATH)
|
||||||
|
MyYouTubeSettings.PlaylistsLocations.PopulateComboBox(CMB_PLS,, True)
|
||||||
|
CMB_PLS.Text = MyYouTubeSettings.LatestPlaylistFile.Value
|
||||||
|
If MyYouTubeSettings.DefaultAudioBitrate > 0 Then TXT_AUDIO_BITRATE.Text = MyYouTubeSettings.DefaultAudioBitrate.Value
|
||||||
|
|
||||||
CMB_FORMATS.Items.AddRange(AvailableAudioFormats)
|
CMB_FORMATS.Items.AddRange(AvailableAudioFormats)
|
||||||
If MyYouTubeSettings.PlaylistFormSplitterDistance > 0 Then SPLITTER_MAIN.SplitterDistancePercentageSet(MyYouTubeSettings.PlaylistFormSplitterDistance)
|
If MyYouTubeSettings.PlaylistFormSplitterDistance > 0 Then SPLITTER_MAIN.SplitterDistancePercentageSet(MyYouTubeSettings.PlaylistFormSplitterDistance)
|
||||||
@@ -104,6 +116,9 @@ Namespace API.YouTube.Controls
|
|||||||
Text = .PlaylistTitle
|
Text = .PlaylistTitle
|
||||||
End If
|
End If
|
||||||
|
|
||||||
|
MyFieldsChecker.AddControl(Of Integer)(TXT_AUDIO_BITRATE, TXT_AUDIO_BITRATE.CaptionText, True)
|
||||||
|
MyFieldsChecker.EndLoaderOperations()
|
||||||
|
|
||||||
UpdateSizeText()
|
UpdateSizeText()
|
||||||
End With
|
End With
|
||||||
RefillAddit()
|
RefillAddit()
|
||||||
@@ -111,7 +126,9 @@ Namespace API.YouTube.Controls
|
|||||||
End Sub
|
End Sub
|
||||||
Private Sub MusicPlaylistsForm_Closing(sender As Object, e As CancelEventArgs) Handles Me.Closing
|
Private Sub MusicPlaylistsForm_Closing(sender As Object, e As CancelEventArgs) Handles Me.Closing
|
||||||
MyYouTubeSettings.PlaylistFormSplitterDistance.Value = SPLITTER_MAIN.SplitterDistancePercentageGet
|
MyYouTubeSettings.PlaylistFormSplitterDistance.Value = SPLITTER_MAIN.SplitterDistancePercentageGet
|
||||||
MyView.DisposeIfReady()
|
MyView.DisposeIfReady
|
||||||
|
MyFieldsChecker.DisposeIfReady
|
||||||
|
M3U8Files.Clear()
|
||||||
End Sub
|
End Sub
|
||||||
Private Sub MusicPlaylistsForm_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown
|
Private Sub MusicPlaylistsForm_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown
|
||||||
Dim b As Boolean = True
|
Dim b As Boolean = True
|
||||||
@@ -181,8 +198,52 @@ Namespace API.YouTube.Controls
|
|||||||
End With
|
End With
|
||||||
End Sub
|
End Sub
|
||||||
Private Sub TXT_OUTPUT_PATH_ActionOnButtonClick(ByVal Sender As ActionButton, ByVal e As ActionButtonEventArgs) Handles TXT_OUTPUT_PATH.ActionOnButtonClick
|
Private Sub TXT_OUTPUT_PATH_ActionOnButtonClick(ByVal Sender As ActionButton, ByVal e As ActionButtonEventArgs) Handles TXT_OUTPUT_PATH.ActionOnButtonClick
|
||||||
If Sender.DefaultButton = ADB.Open Or Sender.DefaultButton = ADB.Add Then _
|
Select Case e.DefaultButton
|
||||||
MyYouTubeSettings.DownloadLocations.ChooseNewLocation(TXT_OUTPUT_PATH, Sender.DefaultButton = ADB.Add, MyDownloaderSettings.OutputPathAskForName)
|
Case ADB.Open, ADB.Add
|
||||||
|
MyYouTubeSettings.DownloadLocations.ChooseNewLocation(TXT_OUTPUT_PATH, e.DefaultButton = ADB.Add, MyDownloaderSettings.OutputPathAskForName)
|
||||||
|
Case ADB.Save
|
||||||
|
If Not TXT_OUTPUT_PATH.Text.IsEmptyString Then
|
||||||
|
With MyYouTubeSettings.PlaylistsLocations
|
||||||
|
.Add(TXT_OUTPUT_PATH.Text, True)
|
||||||
|
.PopulateComboBox(TXT_OUTPUT_PATH, TXT_OUTPUT_PATH.Text)
|
||||||
|
End With
|
||||||
|
End If
|
||||||
|
End Select
|
||||||
|
End Sub
|
||||||
|
Private Sub CMB_PLS_ActionOnButtonClick(ByVal Sender As ActionButton, ByVal e As ActionButtonEventArgs) Handles CMB_PLS.ActionOnButtonClick
|
||||||
|
Try
|
||||||
|
Select Case e.DefaultButton
|
||||||
|
Case ADB.Add, ADB.Open
|
||||||
|
Dim f As SFile = Nothing
|
||||||
|
If Not CMB_PLS.Text.IsEmptyString Then
|
||||||
|
f = CMB_PLS.Text
|
||||||
|
ElseIf Not TXT_OUTPUT_PATH.Text.IsEmptyString Then
|
||||||
|
f = TXT_OUTPUT_PATH.Text
|
||||||
|
End If
|
||||||
|
f = SFile.SelectFiles(f, False, "Select a playlist...", "Playlists|*.m3u;*.m3u8|All files|*.*", EDP.ReturnValue).FirstOrDefault
|
||||||
|
If Not f.IsEmptyString Then
|
||||||
|
If Sender.DefaultButton = ADB.Add Then
|
||||||
|
MyYouTubeSettings.PlaylistsLocations.Add(f.ToString, True, True)
|
||||||
|
MyYouTubeSettings.PlaylistsLocations.PopulateComboBox(CMB_PLS, f, True)
|
||||||
|
End If
|
||||||
|
CMB_PLS.Text = f
|
||||||
|
End If
|
||||||
|
Case ADB.List
|
||||||
|
Dim result As Boolean = False
|
||||||
|
Dim selectedFiles As IEnumerable(Of SFile) = MyYouTubeSettings.PlaylistsLocations.ChooseNewPlaylistArray(CMB_PLS, result)
|
||||||
|
If result Then M3U8Files.ListAddList(selectedFiles, LAP.NotContainsOnly, LAP.ClearBeforeAdd)
|
||||||
|
Case ADB.Save
|
||||||
|
With MyYouTubeSettings.PlaylistsLocations
|
||||||
|
If Not CMB_PLS.Text.IsEmptyString AndAlso .IndexOf(CMB_PLS.Text,, True) = -1 Then
|
||||||
|
.Add(CMB_PLS.Text, True, True)
|
||||||
|
.PopulateComboBox(CMB_PLS, CMB_PLS.Text, True)
|
||||||
|
End If
|
||||||
|
End With
|
||||||
|
Case ADB.Clear : M3U8Files.Clear()
|
||||||
|
End Select
|
||||||
|
Catch ex As Exception
|
||||||
|
ErrorsDescriber.Execute(EDP.SendToLog, ex, "[API.YouTube.Controls.MusicPlaylistsForm.SelectPlaylist]")
|
||||||
|
End Try
|
||||||
End Sub
|
End Sub
|
||||||
#End Region
|
#End Region
|
||||||
#Region "Lists' handlers"
|
#Region "Lists' handlers"
|
||||||
@@ -268,7 +329,7 @@ Namespace API.YouTube.Controls
|
|||||||
Private Sub BTT_DOWN_Click(sender As Object, e As EventArgs) Handles BTT_DOWN.Click
|
Private Sub BTT_DOWN_Click(sender As Object, e As EventArgs) Handles BTT_DOWN.Click
|
||||||
If TXT_OUTPUT_PATH.IsEmptyString Then
|
If TXT_OUTPUT_PATH.IsEmptyString Then
|
||||||
MsgBoxE({"The output path cannot be null.", "Download music"}, vbCritical)
|
MsgBoxE({"The output path cannot be null.", "Download music"}, vbCritical)
|
||||||
Else
|
ElseIf MyFieldsChecker.AllParamsOK Then
|
||||||
With DirectCast(MyContainer, YouTubeMediaContainerBase)
|
With DirectCast(MyContainer, YouTubeMediaContainerBase)
|
||||||
.OutputSubtitlesFormat = IIf(CH_DOWN_LYRICS.Checked, "LRC", String.Empty)
|
.OutputSubtitlesFormat = IIf(CH_DOWN_LYRICS.Checked, "LRC", String.Empty)
|
||||||
If Not TXT_SUBS.Checked Then .PostProcessing_OutputSubtitlesFormats.Clear()
|
If Not TXT_SUBS.Checked Then .PostProcessing_OutputSubtitlesFormats.Clear()
|
||||||
@@ -276,8 +337,12 @@ Namespace API.YouTube.Controls
|
|||||||
If Not TXT_FORMATS_ADDIT.Checked Then .PostProcessing_OutputAudioFormats.Clear()
|
If Not TXT_FORMATS_ADDIT.Checked Then .PostProcessing_OutputAudioFormats.Clear()
|
||||||
.AbsolutePath = TXT_OUTPUT_PATH.Checked
|
.AbsolutePath = TXT_OUTPUT_PATH.Checked
|
||||||
.File = TXT_OUTPUT_PATH.Text.CSFileP
|
.File = TXT_OUTPUT_PATH.Text.CSFileP
|
||||||
|
.M3U8_PlaylistFiles = M3U8FilesFull
|
||||||
|
.OutputAudioBitrate = AConvert(Of Integer)(TXT_AUDIO_BITRATE.Text, -1)
|
||||||
If MyYouTubeSettings.OutputPathAutoChange Then MyYouTubeSettings.OutputPath.Value = .File
|
If MyYouTubeSettings.OutputPathAutoChange Then MyYouTubeSettings.OutputPath.Value = .File
|
||||||
If MyDownloaderSettings.OutputPathAutoAddPaths Then MyYouTubeSettings.DownloadLocations.Add(.File, False)
|
If MyDownloaderSettings.OutputPathAutoAddPaths Then MyYouTubeSettings.DownloadLocations.Add(.File, False)
|
||||||
|
If Not CMB_PLS.Text.IsEmptyString Then MyYouTubeSettings.PlaylistsLocations.Add(CMB_PLS.Text, False, True)
|
||||||
|
MyYouTubeSettings.LatestPlaylistFile.Value = CMB_PLS.Text
|
||||||
End With
|
End With
|
||||||
DialogResult = DialogResult.OK
|
DialogResult = DialogResult.OK
|
||||||
Close()
|
Close()
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ Namespace API.YouTube.Controls
|
|||||||
Me.TXT_URLS.MaxLength = 2147483647
|
Me.TXT_URLS.MaxLength = 2147483647
|
||||||
Me.TXT_URLS.Multiline = True
|
Me.TXT_URLS.Multiline = True
|
||||||
Me.TXT_URLS.Name = "TXT_URLS"
|
Me.TXT_URLS.Name = "TXT_URLS"
|
||||||
|
Me.TXT_URLS.ScrollBars = System.Windows.Forms.ScrollBars.Both
|
||||||
Me.TXT_URLS.Size = New System.Drawing.Size(372, 261)
|
Me.TXT_URLS.Size = New System.Drawing.Size(372, 261)
|
||||||
Me.TXT_URLS.TabIndex = 0
|
Me.TXT_URLS.TabIndex = 0
|
||||||
'
|
'
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ Namespace API.YouTube.Controls
|
|||||||
Friend Sub New(ByVal m As MediaObject, Optional ByVal SelectedAudio As MediaObject = Nothing)
|
Friend Sub New(ByVal m As MediaObject, Optional ByVal SelectedAudio As MediaObject = Nothing)
|
||||||
Me.New
|
Me.New
|
||||||
Const d$ = " " & ChrW(183) & " "
|
Const d$ = " " & ChrW(183) & " "
|
||||||
|
Const DRC$ = Objects.YouTubeMediaContainerBase.DRC
|
||||||
MyMedia = m
|
MyMedia = m
|
||||||
If m.Type = Plugin.UserMediaTypes.Audio Then
|
If m.Type = Plugin.UserMediaTypes.Audio Then
|
||||||
If m.Bitrate >= 320 Then
|
If m.Bitrate >= 320 Then
|
||||||
@@ -38,6 +39,7 @@ Namespace API.YouTube.Controls
|
|||||||
End If
|
End If
|
||||||
LBL_DEFINITION.Text = $"{m.Bitrate}k"
|
LBL_DEFINITION.Text = $"{m.Bitrate}k"
|
||||||
LBL_CODECS.Text = $"{m.Extension} {d} {m.Codec} {d} {m.Bitrate}k"
|
LBL_CODECS.Text = $"{m.Extension} {d} {m.Codec} {d} {m.Bitrate}k"
|
||||||
|
If Not m.ID.IsEmptyString AndAlso m.ID.StringToLower.Contains(DRC) Then LBL_CODECS.Text &= $" {d} DRC"
|
||||||
Else
|
Else
|
||||||
If m.Height >= 1440 Then
|
If m.Height >= 1440 Then
|
||||||
LBL_DEFINITION_INFO.Text = "Ultra High Definition"
|
LBL_DEFINITION_INFO.Text = "Ultra High Definition"
|
||||||
@@ -53,7 +55,9 @@ Namespace API.YouTube.Controls
|
|||||||
LBL_DEFINITION.Text = $"{m.Height}p"
|
LBL_DEFINITION.Text = $"{m.Height}p"
|
||||||
LBL_CODECS.Text = $"{m.Extension.StringToUpper}{d}{m.Codec.StringToUpper}{d}{m.FPS}fps{d}{m.Bitrate}k"
|
LBL_CODECS.Text = $"{m.Extension.StringToUpper}{d}{m.Codec.StringToUpper}{d}{m.FPS}fps{d}{m.Bitrate}k"
|
||||||
If Not m.Protocol.IsEmptyString Then LBL_CODECS.Text &= $" ({m.Protocol})"
|
If Not m.Protocol.IsEmptyString Then LBL_CODECS.Text &= $" ({m.Protocol})"
|
||||||
|
If Not m.ID.IsEmptyString AndAlso m.ID.StringToLower.Contains(DRC) Then LBL_CODECS.Text &= $"{d}DRC"
|
||||||
If Not SelectedAudio.ID.IsEmptyString Then LBL_CODECS.Text &= $" / {SelectedAudio.Extension}{d}{SelectedAudio.Codec}{d}{SelectedAudio.Bitrate}k"
|
If Not SelectedAudio.ID.IsEmptyString Then LBL_CODECS.Text &= $" / {SelectedAudio.Extension}{d}{SelectedAudio.Codec}{d}{SelectedAudio.Bitrate}k"
|
||||||
|
If Not SelectedAudio.ID.IsEmptyString AndAlso SelectedAudio.ID.StringToLower.Contains(DRC) Then LBL_CODECS.Text &= $"{d}DRC"
|
||||||
End If
|
End If
|
||||||
|
|
||||||
Dim sv% = m.Size / 1024
|
Dim sv% = m.Size / 1024
|
||||||
|
|||||||
339
SCrawler.YouTube/Controls/VideoOptionsForm.Designer.vb
generated
@@ -31,9 +31,15 @@ Namespace API.YouTube.Controls
|
|||||||
Dim TP_DESTINATION As System.Windows.Forms.TableLayoutPanel
|
Dim TP_DESTINATION As System.Windows.Forms.TableLayoutPanel
|
||||||
Dim ActionButton1 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
Dim ActionButton1 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||||
Dim resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(VideoOptionsForm))
|
Dim resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(VideoOptionsForm))
|
||||||
|
Dim ActionButton2 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||||
Dim ListColumn1 As PersonalUtilities.Forms.Controls.Base.ListColumn = New PersonalUtilities.Forms.Controls.Base.ListColumn()
|
Dim ListColumn1 As PersonalUtilities.Forms.Controls.Base.ListColumn = New PersonalUtilities.Forms.Controls.Base.ListColumn()
|
||||||
Dim ListColumn2 As PersonalUtilities.Forms.Controls.Base.ListColumn = New PersonalUtilities.Forms.Controls.Base.ListColumn()
|
Dim ListColumn2 As PersonalUtilities.Forms.Controls.Base.ListColumn = New PersonalUtilities.Forms.Controls.Base.ListColumn()
|
||||||
Dim TP_OK_CANCEL As System.Windows.Forms.TableLayoutPanel
|
Dim TP_OK_CANCEL As System.Windows.Forms.TableLayoutPanel
|
||||||
|
Dim TP_PLS As System.Windows.Forms.TableLayoutPanel
|
||||||
|
Dim ActionButton3 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||||
|
Dim ActionButton4 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||||
|
Dim ActionButton5 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||||
|
Dim ActionButton6 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||||
Dim LB_SEP_1 As System.Windows.Forms.Label
|
Dim LB_SEP_1 As System.Windows.Forms.Label
|
||||||
Dim LB_SEP_2 As System.Windows.Forms.Label
|
Dim LB_SEP_2 As System.Windows.Forms.Label
|
||||||
Dim TP_WHAT As System.Windows.Forms.TableLayoutPanel
|
Dim TP_WHAT As System.Windows.Forms.TableLayoutPanel
|
||||||
@@ -41,15 +47,18 @@ Namespace API.YouTube.Controls
|
|||||||
Dim LBL_FORMAT As System.Windows.Forms.Label
|
Dim LBL_FORMAT As System.Windows.Forms.Label
|
||||||
Dim LBL_SUBS_FORMAT As System.Windows.Forms.Label
|
Dim LBL_SUBS_FORMAT As System.Windows.Forms.Label
|
||||||
Dim TT_MAIN As System.Windows.Forms.ToolTip
|
Dim TT_MAIN As System.Windows.Forms.ToolTip
|
||||||
Dim ActionButton2 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
Dim TP_FPS_BITRATE As System.Windows.Forms.TableLayoutPanel
|
||||||
Dim ActionButton3 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
|
||||||
Dim ActionButton4 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
|
||||||
Dim ActionButton5 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
|
||||||
Dim ActionButton6 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
|
||||||
Dim ActionButton7 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
Dim ActionButton7 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||||
Dim ActionButton8 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
Dim ActionButton8 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||||
Dim ActionButton9 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
Dim ActionButton9 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||||
Dim ActionButton10 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
Dim ActionButton10 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||||
|
Dim ActionButton11 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||||
|
Dim ActionButton12 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||||
|
Dim ActionButton13 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||||
|
Dim ActionButton14 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||||
|
Dim ActionButton15 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||||
|
Dim ActionButton16 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||||
|
Dim ActionButton17 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||||
Me.ICON_VIDEO = New System.Windows.Forms.PictureBox()
|
Me.ICON_VIDEO = New System.Windows.Forms.PictureBox()
|
||||||
Me.LBL_TITLE = New System.Windows.Forms.Label()
|
Me.LBL_TITLE = New System.Windows.Forms.Label()
|
||||||
Me.TP_HEADER_INFO_2 = New System.Windows.Forms.TableLayoutPanel()
|
Me.TP_HEADER_INFO_2 = New System.Windows.Forms.TableLayoutPanel()
|
||||||
@@ -59,9 +68,13 @@ Namespace API.YouTube.Controls
|
|||||||
Me.BTT_BROWSE = New System.Windows.Forms.Button()
|
Me.BTT_BROWSE = New System.Windows.Forms.Button()
|
||||||
Me.BTT_DOWN = New System.Windows.Forms.Button()
|
Me.BTT_DOWN = New System.Windows.Forms.Button()
|
||||||
Me.BTT_CANCEL = New System.Windows.Forms.Button()
|
Me.BTT_CANCEL = New System.Windows.Forms.Button()
|
||||||
|
Me.CMB_PLS = New PersonalUtilities.Forms.Controls.ComboBoxExtended()
|
||||||
|
Me.BTT_PLS_BROWSE = New System.Windows.Forms.Button()
|
||||||
Me.OPT_VIDEO = New System.Windows.Forms.RadioButton()
|
Me.OPT_VIDEO = New System.Windows.Forms.RadioButton()
|
||||||
Me.OPT_AUDIO = New System.Windows.Forms.RadioButton()
|
Me.OPT_AUDIO = New System.Windows.Forms.RadioButton()
|
||||||
Me.LBL_AUDIO_CODEC = New System.Windows.Forms.Label()
|
Me.LBL_AUDIO_CODEC = New System.Windows.Forms.Label()
|
||||||
|
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()
|
Me.TP_HEADER_BASE = New System.Windows.Forms.TableLayoutPanel()
|
||||||
Me.TP_SUBS = New System.Windows.Forms.TableLayoutPanel()
|
Me.TP_SUBS = New System.Windows.Forms.TableLayoutPanel()
|
||||||
Me.TXT_SUBS = New PersonalUtilities.Forms.Controls.TextBoxExtended()
|
Me.TXT_SUBS = New PersonalUtilities.Forms.Controls.TextBoxExtended()
|
||||||
@@ -81,6 +94,7 @@ Namespace API.YouTube.Controls
|
|||||||
TP_FOOTER = New System.Windows.Forms.TableLayoutPanel()
|
TP_FOOTER = New System.Windows.Forms.TableLayoutPanel()
|
||||||
TP_DESTINATION = New System.Windows.Forms.TableLayoutPanel()
|
TP_DESTINATION = New System.Windows.Forms.TableLayoutPanel()
|
||||||
TP_OK_CANCEL = New System.Windows.Forms.TableLayoutPanel()
|
TP_OK_CANCEL = New System.Windows.Forms.TableLayoutPanel()
|
||||||
|
TP_PLS = New System.Windows.Forms.TableLayoutPanel()
|
||||||
LB_SEP_1 = New System.Windows.Forms.Label()
|
LB_SEP_1 = New System.Windows.Forms.Label()
|
||||||
LB_SEP_2 = New System.Windows.Forms.Label()
|
LB_SEP_2 = New System.Windows.Forms.Label()
|
||||||
TP_WHAT = New System.Windows.Forms.TableLayoutPanel()
|
TP_WHAT = New System.Windows.Forms.TableLayoutPanel()
|
||||||
@@ -88,6 +102,7 @@ Namespace API.YouTube.Controls
|
|||||||
LBL_FORMAT = New System.Windows.Forms.Label()
|
LBL_FORMAT = New System.Windows.Forms.Label()
|
||||||
LBL_SUBS_FORMAT = New System.Windows.Forms.Label()
|
LBL_SUBS_FORMAT = New System.Windows.Forms.Label()
|
||||||
TT_MAIN = New System.Windows.Forms.ToolTip(Me.components)
|
TT_MAIN = New System.Windows.Forms.ToolTip(Me.components)
|
||||||
|
TP_FPS_BITRATE = New System.Windows.Forms.TableLayoutPanel()
|
||||||
TP_HEADER.SuspendLayout()
|
TP_HEADER.SuspendLayout()
|
||||||
CType(Me.ICON_VIDEO, System.ComponentModel.ISupportInitialize).BeginInit()
|
CType(Me.ICON_VIDEO, System.ComponentModel.ISupportInitialize).BeginInit()
|
||||||
TP_HEADER_INFO.SuspendLayout()
|
TP_HEADER_INFO.SuspendLayout()
|
||||||
@@ -98,7 +113,12 @@ Namespace API.YouTube.Controls
|
|||||||
TP_DESTINATION.SuspendLayout()
|
TP_DESTINATION.SuspendLayout()
|
||||||
CType(Me.TXT_FILE, System.ComponentModel.ISupportInitialize).BeginInit()
|
CType(Me.TXT_FILE, System.ComponentModel.ISupportInitialize).BeginInit()
|
||||||
TP_OK_CANCEL.SuspendLayout()
|
TP_OK_CANCEL.SuspendLayout()
|
||||||
|
TP_PLS.SuspendLayout()
|
||||||
|
CType(Me.CMB_PLS, System.ComponentModel.ISupportInitialize).BeginInit()
|
||||||
TP_WHAT.SuspendLayout()
|
TP_WHAT.SuspendLayout()
|
||||||
|
TP_FPS_BITRATE.SuspendLayout()
|
||||||
|
CType(Me.TXT_FPS, System.ComponentModel.ISupportInitialize).BeginInit()
|
||||||
|
CType(Me.TXT_AUDIO_BITRATE, System.ComponentModel.ISupportInitialize).BeginInit()
|
||||||
Me.TP_HEADER_BASE.SuspendLayout()
|
Me.TP_HEADER_BASE.SuspendLayout()
|
||||||
Me.TP_SUBS.SuspendLayout()
|
Me.TP_SUBS.SuspendLayout()
|
||||||
CType(Me.TXT_SUBS, System.ComponentModel.ISupportInitialize).BeginInit()
|
CType(Me.TXT_SUBS, System.ComponentModel.ISupportInitialize).BeginInit()
|
||||||
@@ -240,17 +260,18 @@ Namespace API.YouTube.Controls
|
|||||||
'
|
'
|
||||||
TP_FOOTER.ColumnCount = 1
|
TP_FOOTER.ColumnCount = 1
|
||||||
TP_FOOTER.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
TP_FOOTER.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
||||||
TP_FOOTER.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20.0!))
|
TP_FOOTER.Controls.Add(TP_DESTINATION, 0, 1)
|
||||||
TP_FOOTER.Controls.Add(TP_DESTINATION, 0, 0)
|
TP_FOOTER.Controls.Add(TP_OK_CANCEL, 0, 2)
|
||||||
TP_FOOTER.Controls.Add(TP_OK_CANCEL, 0, 1)
|
TP_FOOTER.Controls.Add(TP_PLS, 0, 0)
|
||||||
TP_FOOTER.Dock = System.Windows.Forms.DockStyle.Fill
|
TP_FOOTER.Dock = System.Windows.Forms.DockStyle.Fill
|
||||||
TP_FOOTER.Location = New System.Drawing.Point(6, 215)
|
TP_FOOTER.Location = New System.Drawing.Point(6, 243)
|
||||||
TP_FOOTER.Margin = New System.Windows.Forms.Padding(6, 3, 6, 3)
|
TP_FOOTER.Margin = New System.Windows.Forms.Padding(6, 3, 6, 3)
|
||||||
TP_FOOTER.Name = "TP_FOOTER"
|
TP_FOOTER.Name = "TP_FOOTER"
|
||||||
TP_FOOTER.RowCount = 2
|
TP_FOOTER.RowCount = 3
|
||||||
TP_FOOTER.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50.0!))
|
TP_FOOTER.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33333!))
|
||||||
TP_FOOTER.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50.0!))
|
TP_FOOTER.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33333!))
|
||||||
TP_FOOTER.Size = New System.Drawing.Size(589, 52)
|
TP_FOOTER.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33333!))
|
||||||
|
TP_FOOTER.Size = New System.Drawing.Size(589, 81)
|
||||||
TP_FOOTER.TabIndex = 5
|
TP_FOOTER.TabIndex = 5
|
||||||
'
|
'
|
||||||
'TP_DESTINATION
|
'TP_DESTINATION
|
||||||
@@ -261,20 +282,25 @@ Namespace API.YouTube.Controls
|
|||||||
TP_DESTINATION.Controls.Add(Me.TXT_FILE, 0, 0)
|
TP_DESTINATION.Controls.Add(Me.TXT_FILE, 0, 0)
|
||||||
TP_DESTINATION.Controls.Add(Me.BTT_BROWSE, 1, 0)
|
TP_DESTINATION.Controls.Add(Me.BTT_BROWSE, 1, 0)
|
||||||
TP_DESTINATION.Dock = System.Windows.Forms.DockStyle.Fill
|
TP_DESTINATION.Dock = System.Windows.Forms.DockStyle.Fill
|
||||||
TP_DESTINATION.Location = New System.Drawing.Point(0, 0)
|
TP_DESTINATION.Location = New System.Drawing.Point(0, 27)
|
||||||
TP_DESTINATION.Margin = New System.Windows.Forms.Padding(0)
|
TP_DESTINATION.Margin = New System.Windows.Forms.Padding(0)
|
||||||
TP_DESTINATION.Name = "TP_DESTINATION"
|
TP_DESTINATION.Name = "TP_DESTINATION"
|
||||||
TP_DESTINATION.RowCount = 1
|
TP_DESTINATION.RowCount = 1
|
||||||
TP_DESTINATION.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
TP_DESTINATION.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
||||||
TP_DESTINATION.Size = New System.Drawing.Size(589, 26)
|
TP_DESTINATION.Size = New System.Drawing.Size(589, 27)
|
||||||
TP_DESTINATION.TabIndex = 0
|
TP_DESTINATION.TabIndex = 0
|
||||||
'
|
'
|
||||||
'TXT_FILE
|
'TXT_FILE
|
||||||
'
|
'
|
||||||
ActionButton1.BackgroundImage = CType(resources.GetObject("ActionButton1.BackgroundImage"), System.Drawing.Image)
|
ActionButton1.BackgroundImage = CType(resources.GetObject("ActionButton1.BackgroundImage"), System.Drawing.Image)
|
||||||
ActionButton1.Name = "ArrowDown"
|
ActionButton1.Name = "Save"
|
||||||
ActionButton1.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.ArrowDown
|
ActionButton1.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Save
|
||||||
|
ActionButton1.ToolTipText = "Save destination"
|
||||||
|
ActionButton2.BackgroundImage = CType(resources.GetObject("ActionButton2.BackgroundImage"), System.Drawing.Image)
|
||||||
|
ActionButton2.Name = "ArrowDown"
|
||||||
|
ActionButton2.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.ArrowDown
|
||||||
Me.TXT_FILE.Buttons.Add(ActionButton1)
|
Me.TXT_FILE.Buttons.Add(ActionButton1)
|
||||||
|
Me.TXT_FILE.Buttons.Add(ActionButton2)
|
||||||
Me.TXT_FILE.ChangeControlsEnableOnCheckedChange = False
|
Me.TXT_FILE.ChangeControlsEnableOnCheckedChange = False
|
||||||
ListColumn1.Name = "COL_NAME"
|
ListColumn1.Name = "COL_NAME"
|
||||||
ListColumn1.Text = "Name"
|
ListColumn1.Text = "Name"
|
||||||
@@ -300,7 +326,7 @@ Namespace API.YouTube.Controls
|
|||||||
Me.BTT_BROWSE.Location = New System.Drawing.Point(512, 2)
|
Me.BTT_BROWSE.Location = New System.Drawing.Point(512, 2)
|
||||||
Me.BTT_BROWSE.Margin = New System.Windows.Forms.Padding(3, 2, 3, 2)
|
Me.BTT_BROWSE.Margin = New System.Windows.Forms.Padding(3, 2, 3, 2)
|
||||||
Me.BTT_BROWSE.Name = "BTT_BROWSE"
|
Me.BTT_BROWSE.Name = "BTT_BROWSE"
|
||||||
Me.BTT_BROWSE.Size = New System.Drawing.Size(74, 22)
|
Me.BTT_BROWSE.Size = New System.Drawing.Size(74, 23)
|
||||||
Me.BTT_BROWSE.TabIndex = 1
|
Me.BTT_BROWSE.TabIndex = 1
|
||||||
Me.BTT_BROWSE.Text = "Browse"
|
Me.BTT_BROWSE.Text = "Browse"
|
||||||
TT_MAIN.SetToolTip(Me.BTT_BROWSE, "Choose an output file (Right click for add a new location to the list)")
|
TT_MAIN.SetToolTip(Me.BTT_BROWSE, "Choose an output file (Right click for add a new location to the list)")
|
||||||
@@ -315,13 +341,13 @@ Namespace API.YouTube.Controls
|
|||||||
TP_OK_CANCEL.Controls.Add(Me.BTT_DOWN, 1, 0)
|
TP_OK_CANCEL.Controls.Add(Me.BTT_DOWN, 1, 0)
|
||||||
TP_OK_CANCEL.Controls.Add(Me.BTT_CANCEL, 2, 0)
|
TP_OK_CANCEL.Controls.Add(Me.BTT_CANCEL, 2, 0)
|
||||||
TP_OK_CANCEL.Dock = System.Windows.Forms.DockStyle.Fill
|
TP_OK_CANCEL.Dock = System.Windows.Forms.DockStyle.Fill
|
||||||
TP_OK_CANCEL.Location = New System.Drawing.Point(0, 26)
|
TP_OK_CANCEL.Location = New System.Drawing.Point(0, 54)
|
||||||
TP_OK_CANCEL.Margin = New System.Windows.Forms.Padding(0)
|
TP_OK_CANCEL.Margin = New System.Windows.Forms.Padding(0)
|
||||||
TP_OK_CANCEL.Name = "TP_OK_CANCEL"
|
TP_OK_CANCEL.Name = "TP_OK_CANCEL"
|
||||||
TP_OK_CANCEL.RowCount = 1
|
TP_OK_CANCEL.RowCount = 1
|
||||||
TP_OK_CANCEL.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
TP_OK_CANCEL.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
||||||
TP_OK_CANCEL.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26.0!))
|
TP_OK_CANCEL.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 27.0!))
|
||||||
TP_OK_CANCEL.Size = New System.Drawing.Size(589, 26)
|
TP_OK_CANCEL.Size = New System.Drawing.Size(589, 27)
|
||||||
TP_OK_CANCEL.TabIndex = 1
|
TP_OK_CANCEL.TabIndex = 1
|
||||||
'
|
'
|
||||||
'BTT_DOWN
|
'BTT_DOWN
|
||||||
@@ -330,7 +356,7 @@ Namespace API.YouTube.Controls
|
|||||||
Me.BTT_DOWN.Location = New System.Drawing.Point(432, 2)
|
Me.BTT_DOWN.Location = New System.Drawing.Point(432, 2)
|
||||||
Me.BTT_DOWN.Margin = New System.Windows.Forms.Padding(3, 2, 3, 2)
|
Me.BTT_DOWN.Margin = New System.Windows.Forms.Padding(3, 2, 3, 2)
|
||||||
Me.BTT_DOWN.Name = "BTT_DOWN"
|
Me.BTT_DOWN.Name = "BTT_DOWN"
|
||||||
Me.BTT_DOWN.Size = New System.Drawing.Size(74, 22)
|
Me.BTT_DOWN.Size = New System.Drawing.Size(74, 23)
|
||||||
Me.BTT_DOWN.TabIndex = 0
|
Me.BTT_DOWN.TabIndex = 0
|
||||||
Me.BTT_DOWN.Text = "Download"
|
Me.BTT_DOWN.Text = "Download"
|
||||||
Me.BTT_DOWN.UseVisualStyleBackColor = True
|
Me.BTT_DOWN.UseVisualStyleBackColor = True
|
||||||
@@ -342,16 +368,79 @@ Namespace API.YouTube.Controls
|
|||||||
Me.BTT_CANCEL.Location = New System.Drawing.Point(512, 2)
|
Me.BTT_CANCEL.Location = New System.Drawing.Point(512, 2)
|
||||||
Me.BTT_CANCEL.Margin = New System.Windows.Forms.Padding(3, 2, 3, 2)
|
Me.BTT_CANCEL.Margin = New System.Windows.Forms.Padding(3, 2, 3, 2)
|
||||||
Me.BTT_CANCEL.Name = "BTT_CANCEL"
|
Me.BTT_CANCEL.Name = "BTT_CANCEL"
|
||||||
Me.BTT_CANCEL.Size = New System.Drawing.Size(74, 22)
|
Me.BTT_CANCEL.Size = New System.Drawing.Size(74, 23)
|
||||||
Me.BTT_CANCEL.TabIndex = 1
|
Me.BTT_CANCEL.TabIndex = 1
|
||||||
Me.BTT_CANCEL.Text = "Cancel"
|
Me.BTT_CANCEL.Text = "Cancel"
|
||||||
Me.BTT_CANCEL.UseVisualStyleBackColor = True
|
Me.BTT_CANCEL.UseVisualStyleBackColor = True
|
||||||
'
|
'
|
||||||
|
'TP_PLS
|
||||||
|
'
|
||||||
|
TP_PLS.ColumnCount = 2
|
||||||
|
TP_PLS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
||||||
|
TP_PLS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 80.0!))
|
||||||
|
TP_PLS.Controls.Add(Me.CMB_PLS, 0, 0)
|
||||||
|
TP_PLS.Controls.Add(Me.BTT_PLS_BROWSE, 1, 0)
|
||||||
|
TP_PLS.Dock = System.Windows.Forms.DockStyle.Fill
|
||||||
|
TP_PLS.Location = New System.Drawing.Point(0, 0)
|
||||||
|
TP_PLS.Margin = New System.Windows.Forms.Padding(0)
|
||||||
|
TP_PLS.Name = "TP_PLS"
|
||||||
|
TP_PLS.RowCount = 1
|
||||||
|
TP_PLS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
||||||
|
TP_PLS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 27.0!))
|
||||||
|
TP_PLS.Size = New System.Drawing.Size(589, 27)
|
||||||
|
TP_PLS.TabIndex = 2
|
||||||
|
'
|
||||||
|
'CMB_PLS
|
||||||
|
'
|
||||||
|
ActionButton3.BackgroundImage = CType(resources.GetObject("ActionButton3.BackgroundImage"), System.Drawing.Image)
|
||||||
|
ActionButton3.Name = "Save"
|
||||||
|
ActionButton3.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Save
|
||||||
|
ActionButton3.ToolTipText = "Save playlist"
|
||||||
|
ActionButton4.BackgroundImage = CType(resources.GetObject("ActionButton4.BackgroundImage"), System.Drawing.Image)
|
||||||
|
ActionButton4.Name = "List"
|
||||||
|
ActionButton4.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.List
|
||||||
|
ActionButton4.ToolTipText = "Select multiple playlists"
|
||||||
|
ActionButton5.BackgroundImage = CType(resources.GetObject("ActionButton5.BackgroundImage"), System.Drawing.Image)
|
||||||
|
ActionButton5.Name = "Clear"
|
||||||
|
ActionButton5.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
|
||||||
|
ActionButton6.BackgroundImage = CType(resources.GetObject("ActionButton6.BackgroundImage"), System.Drawing.Image)
|
||||||
|
ActionButton6.Name = "ArrowDown"
|
||||||
|
ActionButton6.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.ArrowDown
|
||||||
|
Me.CMB_PLS.Buttons.Add(ActionButton3)
|
||||||
|
Me.CMB_PLS.Buttons.Add(ActionButton4)
|
||||||
|
Me.CMB_PLS.Buttons.Add(ActionButton5)
|
||||||
|
Me.CMB_PLS.Buttons.Add(ActionButton6)
|
||||||
|
Me.CMB_PLS.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.Label
|
||||||
|
Me.CMB_PLS.CaptionText = "Playlist"
|
||||||
|
Me.CMB_PLS.CaptionToolTipEnabled = True
|
||||||
|
Me.CMB_PLS.CaptionToolTipText = "Add downloaded item(s) to playlist"
|
||||||
|
Me.CMB_PLS.CaptionVisible = True
|
||||||
|
Me.CMB_PLS.CaptionWidth = 50.0R
|
||||||
|
Me.CMB_PLS.Dock = System.Windows.Forms.DockStyle.Fill
|
||||||
|
Me.CMB_PLS.Location = New System.Drawing.Point(1, 1)
|
||||||
|
Me.CMB_PLS.Margin = New System.Windows.Forms.Padding(1)
|
||||||
|
Me.CMB_PLS.Name = "CMB_PLS"
|
||||||
|
Me.CMB_PLS.Size = New System.Drawing.Size(507, 22)
|
||||||
|
Me.CMB_PLS.TabIndex = 0
|
||||||
|
Me.CMB_PLS.TextBoxBorderStyle = System.Windows.Forms.BorderStyle.FixedSingle
|
||||||
|
'
|
||||||
|
'BTT_PLS_BROWSE
|
||||||
|
'
|
||||||
|
Me.BTT_PLS_BROWSE.Dock = System.Windows.Forms.DockStyle.Fill
|
||||||
|
Me.BTT_PLS_BROWSE.Location = New System.Drawing.Point(512, 2)
|
||||||
|
Me.BTT_PLS_BROWSE.Margin = New System.Windows.Forms.Padding(3, 2, 3, 2)
|
||||||
|
Me.BTT_PLS_BROWSE.Name = "BTT_PLS_BROWSE"
|
||||||
|
Me.BTT_PLS_BROWSE.Size = New System.Drawing.Size(74, 23)
|
||||||
|
Me.BTT_PLS_BROWSE.TabIndex = 1
|
||||||
|
Me.BTT_PLS_BROWSE.Text = "Browse"
|
||||||
|
TT_MAIN.SetToolTip(Me.BTT_PLS_BROWSE, "Choose an output file (Right click for add a new location to the list)")
|
||||||
|
Me.BTT_PLS_BROWSE.UseVisualStyleBackColor = True
|
||||||
|
'
|
||||||
'LB_SEP_1
|
'LB_SEP_1
|
||||||
'
|
'
|
||||||
LB_SEP_1.Anchor = CType((System.Windows.Forms.AnchorStyles.Left Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
|
LB_SEP_1.Anchor = CType((System.Windows.Forms.AnchorStyles.Left Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
|
||||||
LB_SEP_1.BackColor = System.Drawing.SystemColors.ControlDark
|
LB_SEP_1.BackColor = System.Drawing.SystemColors.ControlDark
|
||||||
LB_SEP_1.Location = New System.Drawing.Point(6, 179)
|
LB_SEP_1.Location = New System.Drawing.Point(6, 207)
|
||||||
LB_SEP_1.Margin = New System.Windows.Forms.Padding(6, 0, 6, 0)
|
LB_SEP_1.Margin = New System.Windows.Forms.Padding(6, 0, 6, 0)
|
||||||
LB_SEP_1.Name = "LB_SEP_1"
|
LB_SEP_1.Name = "LB_SEP_1"
|
||||||
LB_SEP_1.Size = New System.Drawing.Size(589, 1)
|
LB_SEP_1.Size = New System.Drawing.Size(589, 1)
|
||||||
@@ -361,7 +450,7 @@ Namespace API.YouTube.Controls
|
|||||||
'
|
'
|
||||||
LB_SEP_2.Anchor = CType((System.Windows.Forms.AnchorStyles.Left Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
|
LB_SEP_2.Anchor = CType((System.Windows.Forms.AnchorStyles.Left Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
|
||||||
LB_SEP_2.BackColor = System.Drawing.SystemColors.ControlDark
|
LB_SEP_2.BackColor = System.Drawing.SystemColors.ControlDark
|
||||||
LB_SEP_2.Location = New System.Drawing.Point(6, 209)
|
LB_SEP_2.Location = New System.Drawing.Point(6, 237)
|
||||||
LB_SEP_2.Margin = New System.Windows.Forms.Padding(6, 0, 6, 0)
|
LB_SEP_2.Margin = New System.Windows.Forms.Padding(6, 0, 6, 0)
|
||||||
LB_SEP_2.Name = "LB_SEP_2"
|
LB_SEP_2.Name = "LB_SEP_2"
|
||||||
LB_SEP_2.Size = New System.Drawing.Size(589, 1)
|
LB_SEP_2.Size = New System.Drawing.Size(589, 1)
|
||||||
@@ -456,6 +545,59 @@ Namespace API.YouTube.Controls
|
|||||||
Me.LBL_AUDIO_CODEC.TextAlign = System.Drawing.ContentAlignment.MiddleRight
|
Me.LBL_AUDIO_CODEC.TextAlign = System.Drawing.ContentAlignment.MiddleRight
|
||||||
TT_MAIN.SetToolTip(Me.LBL_AUDIO_CODEC, "Output Audio Codec")
|
TT_MAIN.SetToolTip(Me.LBL_AUDIO_CODEC, "Output Audio Codec")
|
||||||
'
|
'
|
||||||
|
'TP_FPS_BITRATE
|
||||||
|
'
|
||||||
|
TP_FPS_BITRATE.ColumnCount = 2
|
||||||
|
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.Controls.Add(Me.TXT_FPS, 0, 0)
|
||||||
|
TP_FPS_BITRATE.Controls.Add(Me.TXT_AUDIO_BITRATE, 1, 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
|
||||||
|
'
|
||||||
|
'TXT_FPS
|
||||||
|
'
|
||||||
|
ActionButton7.BackgroundImage = CType(resources.GetObject("ActionButton7.BackgroundImage"), System.Drawing.Image)
|
||||||
|
ActionButton7.Name = "Clear"
|
||||||
|
ActionButton7.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
|
||||||
|
Me.TXT_FPS.Buttons.Add(ActionButton7)
|
||||||
|
Me.TXT_FPS.CaptionText = "Video FPS"
|
||||||
|
Me.TXT_FPS.CaptionToolTipEnabled = True
|
||||||
|
Me.TXT_FPS.CaptionToolTipText = "Set the video FPS by setting the FPS value in this field. Leave blank so as not t" &
|
||||||
|
"o change"
|
||||||
|
Me.TXT_FPS.CaptionWidth = 60.0R
|
||||||
|
Me.TXT_FPS.Dock = System.Windows.Forms.DockStyle.Fill
|
||||||
|
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.TabIndex = 0
|
||||||
|
Me.TXT_FPS.TextBoxWidthMinimal = 30
|
||||||
|
'
|
||||||
|
'TXT_AUDIO_BITRATE
|
||||||
|
'
|
||||||
|
ActionButton8.BackgroundImage = CType(resources.GetObject("ActionButton8.BackgroundImage"), System.Drawing.Image)
|
||||||
|
ActionButton8.Name = "Clear"
|
||||||
|
ActionButton8.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
|
||||||
|
Me.TXT_AUDIO_BITRATE.Buttons.Add(ActionButton8)
|
||||||
|
Me.TXT_AUDIO_BITRATE.CaptionText = "Audio bitrate"
|
||||||
|
Me.TXT_AUDIO_BITRATE.CaptionToolTipEnabled = True
|
||||||
|
Me.TXT_AUDIO_BITRATE.CaptionToolTipText = "Set the video FPS if you want to change it during download. Leave blank so as not" &
|
||||||
|
" 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.Name = "TXT_AUDIO_BITRATE"
|
||||||
|
Me.TXT_AUDIO_BITRATE.Size = New System.Drawing.Size(289, 22)
|
||||||
|
Me.TXT_AUDIO_BITRATE.TabIndex = 1
|
||||||
|
'
|
||||||
'TP_HEADER_BASE
|
'TP_HEADER_BASE
|
||||||
'
|
'
|
||||||
Me.TP_HEADER_BASE.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.[Single]
|
Me.TP_HEADER_BASE.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.[Single]
|
||||||
@@ -471,7 +613,7 @@ Namespace API.YouTube.Controls
|
|||||||
Me.TP_HEADER_BASE.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
Me.TP_HEADER_BASE.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
||||||
Me.TP_HEADER_BASE.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 64.0!))
|
Me.TP_HEADER_BASE.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 64.0!))
|
||||||
Me.TP_HEADER_BASE.Size = New System.Drawing.Size(601, 65)
|
Me.TP_HEADER_BASE.Size = New System.Drawing.Size(601, 65)
|
||||||
Me.TP_HEADER_BASE.TabIndex = 6
|
Me.TP_HEADER_BASE.TabIndex = 7
|
||||||
'
|
'
|
||||||
'TP_SUBS
|
'TP_SUBS
|
||||||
'
|
'
|
||||||
@@ -483,7 +625,7 @@ Namespace API.YouTube.Controls
|
|||||||
Me.TP_SUBS.Controls.Add(LBL_SUBS_FORMAT, 1, 0)
|
Me.TP_SUBS.Controls.Add(LBL_SUBS_FORMAT, 1, 0)
|
||||||
Me.TP_SUBS.Controls.Add(Me.CMB_SUBS_FORMAT, 2, 0)
|
Me.TP_SUBS.Controls.Add(Me.CMB_SUBS_FORMAT, 2, 0)
|
||||||
Me.TP_SUBS.Dock = System.Windows.Forms.DockStyle.Fill
|
Me.TP_SUBS.Dock = System.Windows.Forms.DockStyle.Fill
|
||||||
Me.TP_SUBS.Location = New System.Drawing.Point(6, 93)
|
Me.TP_SUBS.Location = New System.Drawing.Point(6, 121)
|
||||||
Me.TP_SUBS.Margin = New System.Windows.Forms.Padding(6, 0, 6, 0)
|
Me.TP_SUBS.Margin = New System.Windows.Forms.Padding(6, 0, 6, 0)
|
||||||
Me.TP_SUBS.Name = "TP_SUBS"
|
Me.TP_SUBS.Name = "TP_SUBS"
|
||||||
Me.TP_SUBS.RowCount = 1
|
Me.TP_SUBS.RowCount = 1
|
||||||
@@ -493,21 +635,21 @@ Namespace API.YouTube.Controls
|
|||||||
'
|
'
|
||||||
'TXT_SUBS
|
'TXT_SUBS
|
||||||
'
|
'
|
||||||
ActionButton2.BackgroundImage = CType(resources.GetObject("ActionButton2.BackgroundImage"), System.Drawing.Image)
|
ActionButton9.BackgroundImage = CType(resources.GetObject("ActionButton9.BackgroundImage"), System.Drawing.Image)
|
||||||
ActionButton2.Name = "Open"
|
ActionButton9.Name = "Open"
|
||||||
ActionButton2.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open
|
ActionButton9.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open
|
||||||
ActionButton2.ToolTipText = "Choose subtitles"
|
ActionButton9.ToolTipText = "Choose subtitles"
|
||||||
ActionButton3.BackgroundImage = CType(resources.GetObject("ActionButton3.BackgroundImage"), System.Drawing.Image)
|
ActionButton10.BackgroundImage = CType(resources.GetObject("ActionButton10.BackgroundImage"), System.Drawing.Image)
|
||||||
ActionButton3.Name = "Refresh"
|
ActionButton10.Name = "Refresh"
|
||||||
ActionButton3.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Refresh
|
ActionButton10.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Refresh
|
||||||
ActionButton3.ToolTipText = "Reset subtitles to initial selected"
|
ActionButton10.ToolTipText = "Reset subtitles to initial selected"
|
||||||
ActionButton4.BackgroundImage = CType(resources.GetObject("ActionButton4.BackgroundImage"), System.Drawing.Image)
|
ActionButton11.BackgroundImage = CType(resources.GetObject("ActionButton11.BackgroundImage"), System.Drawing.Image)
|
||||||
ActionButton4.Name = "Clear"
|
ActionButton11.Name = "Clear"
|
||||||
ActionButton4.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
|
ActionButton11.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
|
||||||
ActionButton4.ToolTipText = "Clear subtitles selection (don't download subtitles)"
|
ActionButton11.ToolTipText = "Clear subtitles selection (don't download subtitles)"
|
||||||
Me.TXT_SUBS.Buttons.Add(ActionButton2)
|
Me.TXT_SUBS.Buttons.Add(ActionButton9)
|
||||||
Me.TXT_SUBS.Buttons.Add(ActionButton3)
|
Me.TXT_SUBS.Buttons.Add(ActionButton10)
|
||||||
Me.TXT_SUBS.Buttons.Add(ActionButton4)
|
Me.TXT_SUBS.Buttons.Add(ActionButton11)
|
||||||
Me.TXT_SUBS.CaptionText = "Subtitles"
|
Me.TXT_SUBS.CaptionText = "Subtitles"
|
||||||
Me.TXT_SUBS.CaptionToolTipEnabled = True
|
Me.TXT_SUBS.CaptionToolTipEnabled = True
|
||||||
Me.TXT_SUBS.CaptionToolTipText = "The selected subtitles will also be downloaded"
|
Me.TXT_SUBS.CaptionToolTipText = "The selected subtitles will also be downloaded"
|
||||||
@@ -535,29 +677,31 @@ Namespace API.YouTube.Controls
|
|||||||
Me.TP_MAIN.ColumnCount = 1
|
Me.TP_MAIN.ColumnCount = 1
|
||||||
Me.TP_MAIN.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
Me.TP_MAIN.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
||||||
Me.TP_MAIN.Controls.Add(Me.TP_HEADER_BASE, 0, 0)
|
Me.TP_MAIN.Controls.Add(Me.TP_HEADER_BASE, 0, 0)
|
||||||
Me.TP_MAIN.Controls.Add(TP_FOOTER, 0, 8)
|
Me.TP_MAIN.Controls.Add(TP_FOOTER, 0, 9)
|
||||||
Me.TP_MAIN.Controls.Add(Me.TP_OPTIONS, 0, 1)
|
Me.TP_MAIN.Controls.Add(Me.TP_OPTIONS, 0, 1)
|
||||||
Me.TP_MAIN.Controls.Add(Me.TP_CONTROLS, 0, 6)
|
Me.TP_MAIN.Controls.Add(Me.TP_CONTROLS, 0, 7)
|
||||||
Me.TP_MAIN.Controls.Add(LB_SEP_1, 0, 5)
|
Me.TP_MAIN.Controls.Add(LB_SEP_1, 0, 6)
|
||||||
Me.TP_MAIN.Controls.Add(LB_SEP_2, 0, 7)
|
Me.TP_MAIN.Controls.Add(LB_SEP_2, 0, 8)
|
||||||
Me.TP_MAIN.Controls.Add(Me.TP_SUBS, 0, 2)
|
Me.TP_MAIN.Controls.Add(Me.TP_SUBS, 0, 3)
|
||||||
Me.TP_MAIN.Controls.Add(Me.TXT_SUBS_ADDIT, 0, 3)
|
Me.TP_MAIN.Controls.Add(Me.TXT_SUBS_ADDIT, 0, 4)
|
||||||
Me.TP_MAIN.Controls.Add(Me.TXT_EXTRA_AUDIO_FORMATS, 0, 4)
|
Me.TP_MAIN.Controls.Add(Me.TXT_EXTRA_AUDIO_FORMATS, 0, 5)
|
||||||
|
Me.TP_MAIN.Controls.Add(TP_FPS_BITRATE, 0, 2)
|
||||||
Me.TP_MAIN.Dock = System.Windows.Forms.DockStyle.Fill
|
Me.TP_MAIN.Dock = System.Windows.Forms.DockStyle.Fill
|
||||||
Me.TP_MAIN.Location = New System.Drawing.Point(0, 0)
|
Me.TP_MAIN.Location = New System.Drawing.Point(0, 0)
|
||||||
Me.TP_MAIN.Name = "TP_MAIN"
|
Me.TP_MAIN.Name = "TP_MAIN"
|
||||||
Me.TP_MAIN.RowCount = 10
|
Me.TP_MAIN.RowCount = 11
|
||||||
Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 65.0!))
|
Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 65.0!))
|
||||||
Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
|
Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
|
||||||
Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
|
Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
|
||||||
Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
|
Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
|
||||||
Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
|
Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
|
||||||
|
Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
|
||||||
Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 5.0!))
|
Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 5.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, 25.0!))
|
||||||
Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 5.0!))
|
Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 5.0!))
|
||||||
Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 58.0!))
|
Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 87.0!))
|
||||||
Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle())
|
Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle())
|
||||||
Me.TP_MAIN.Size = New System.Drawing.Size(601, 271)
|
Me.TP_MAIN.Size = New System.Drawing.Size(601, 328)
|
||||||
Me.TP_MAIN.TabIndex = 0
|
Me.TP_MAIN.TabIndex = 0
|
||||||
'
|
'
|
||||||
'TP_OPTIONS
|
'TP_OPTIONS
|
||||||
@@ -569,6 +713,7 @@ Namespace API.YouTube.Controls
|
|||||||
Me.TP_OPTIONS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 80.0!))
|
Me.TP_OPTIONS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 80.0!))
|
||||||
Me.TP_OPTIONS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 80.0!))
|
Me.TP_OPTIONS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 80.0!))
|
||||||
Me.TP_OPTIONS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 80.0!))
|
Me.TP_OPTIONS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 80.0!))
|
||||||
|
Me.TP_OPTIONS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20.0!))
|
||||||
Me.TP_OPTIONS.Controls.Add(LBL_FORMAT, 1, 0)
|
Me.TP_OPTIONS.Controls.Add(LBL_FORMAT, 1, 0)
|
||||||
Me.TP_OPTIONS.Controls.Add(TP_WHAT, 0, 0)
|
Me.TP_OPTIONS.Controls.Add(TP_WHAT, 0, 0)
|
||||||
Me.TP_OPTIONS.Controls.Add(Me.CMB_FORMAT, 2, 0)
|
Me.TP_OPTIONS.Controls.Add(Me.CMB_FORMAT, 2, 0)
|
||||||
@@ -621,7 +766,7 @@ Namespace API.YouTube.Controls
|
|||||||
Me.TP_CONTROLS.ColumnCount = 1
|
Me.TP_CONTROLS.ColumnCount = 1
|
||||||
Me.TP_CONTROLS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
Me.TP_CONTROLS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
||||||
Me.TP_CONTROLS.Dock = System.Windows.Forms.DockStyle.Fill
|
Me.TP_CONTROLS.Dock = System.Windows.Forms.DockStyle.Fill
|
||||||
Me.TP_CONTROLS.Location = New System.Drawing.Point(3, 182)
|
Me.TP_CONTROLS.Location = New System.Drawing.Point(3, 210)
|
||||||
Me.TP_CONTROLS.Margin = New System.Windows.Forms.Padding(3, 0, 3, 0)
|
Me.TP_CONTROLS.Margin = New System.Windows.Forms.Padding(3, 0, 3, 0)
|
||||||
Me.TP_CONTROLS.Name = "TP_CONTROLS"
|
Me.TP_CONTROLS.Name = "TP_CONTROLS"
|
||||||
Me.TP_CONTROLS.RowCount = 1
|
Me.TP_CONTROLS.RowCount = 1
|
||||||
@@ -631,24 +776,24 @@ Namespace API.YouTube.Controls
|
|||||||
'
|
'
|
||||||
'TXT_SUBS_ADDIT
|
'TXT_SUBS_ADDIT
|
||||||
'
|
'
|
||||||
ActionButton5.BackgroundImage = CType(resources.GetObject("ActionButton5.BackgroundImage"), System.Drawing.Image)
|
ActionButton12.BackgroundImage = CType(resources.GetObject("ActionButton12.BackgroundImage"), System.Drawing.Image)
|
||||||
ActionButton5.Enabled = False
|
ActionButton12.Enabled = False
|
||||||
ActionButton5.Name = "Open"
|
ActionButton12.Name = "Open"
|
||||||
ActionButton5.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open
|
ActionButton12.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open
|
||||||
ActionButton5.ToolTipText = "Choose additional formats"
|
ActionButton12.ToolTipText = "Choose additional formats"
|
||||||
ActionButton6.BackgroundImage = CType(resources.GetObject("ActionButton6.BackgroundImage"), System.Drawing.Image)
|
ActionButton13.BackgroundImage = CType(resources.GetObject("ActionButton13.BackgroundImage"), System.Drawing.Image)
|
||||||
ActionButton6.Enabled = False
|
ActionButton13.Enabled = False
|
||||||
ActionButton6.Name = "Refresh"
|
ActionButton13.Name = "Refresh"
|
||||||
ActionButton6.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Refresh
|
ActionButton13.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Refresh
|
||||||
ActionButton6.ToolTipText = "Fill in additional formats from the defaults"
|
ActionButton13.ToolTipText = "Fill in additional formats from the defaults"
|
||||||
ActionButton7.BackgroundImage = CType(resources.GetObject("ActionButton7.BackgroundImage"), System.Drawing.Image)
|
ActionButton14.BackgroundImage = CType(resources.GetObject("ActionButton14.BackgroundImage"), System.Drawing.Image)
|
||||||
ActionButton7.Enabled = False
|
ActionButton14.Enabled = False
|
||||||
ActionButton7.Name = "Clear"
|
ActionButton14.Name = "Clear"
|
||||||
ActionButton7.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
|
ActionButton14.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
|
||||||
ActionButton7.ToolTipText = "Remove all additional formats"
|
ActionButton14.ToolTipText = "Remove all additional formats"
|
||||||
Me.TXT_SUBS_ADDIT.Buttons.Add(ActionButton5)
|
Me.TXT_SUBS_ADDIT.Buttons.Add(ActionButton12)
|
||||||
Me.TXT_SUBS_ADDIT.Buttons.Add(ActionButton6)
|
Me.TXT_SUBS_ADDIT.Buttons.Add(ActionButton13)
|
||||||
Me.TXT_SUBS_ADDIT.Buttons.Add(ActionButton7)
|
Me.TXT_SUBS_ADDIT.Buttons.Add(ActionButton14)
|
||||||
Me.TXT_SUBS_ADDIT.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.CheckBox
|
Me.TXT_SUBS_ADDIT.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.CheckBox
|
||||||
Me.TXT_SUBS_ADDIT.CaptionText = "Additional subtitle formats"
|
Me.TXT_SUBS_ADDIT.CaptionText = "Additional subtitle formats"
|
||||||
Me.TXT_SUBS_ADDIT.CaptionToolTipEnabled = True
|
Me.TXT_SUBS_ADDIT.CaptionToolTipEnabled = True
|
||||||
@@ -656,7 +801,7 @@ Namespace API.YouTube.Controls
|
|||||||
Me.TXT_SUBS_ADDIT.CaptionWidth = 150.0R
|
Me.TXT_SUBS_ADDIT.CaptionWidth = 150.0R
|
||||||
Me.TXT_SUBS_ADDIT.ClearTextByButtonClear = False
|
Me.TXT_SUBS_ADDIT.ClearTextByButtonClear = False
|
||||||
Me.TXT_SUBS_ADDIT.Dock = System.Windows.Forms.DockStyle.Fill
|
Me.TXT_SUBS_ADDIT.Dock = System.Windows.Forms.DockStyle.Fill
|
||||||
Me.TXT_SUBS_ADDIT.Location = New System.Drawing.Point(6, 124)
|
Me.TXT_SUBS_ADDIT.Location = New System.Drawing.Point(6, 152)
|
||||||
Me.TXT_SUBS_ADDIT.Margin = New System.Windows.Forms.Padding(6, 3, 6, 3)
|
Me.TXT_SUBS_ADDIT.Margin = New System.Windows.Forms.Padding(6, 3, 6, 3)
|
||||||
Me.TXT_SUBS_ADDIT.Name = "TXT_SUBS_ADDIT"
|
Me.TXT_SUBS_ADDIT.Name = "TXT_SUBS_ADDIT"
|
||||||
Me.TXT_SUBS_ADDIT.Size = New System.Drawing.Size(589, 22)
|
Me.TXT_SUBS_ADDIT.Size = New System.Drawing.Size(589, 22)
|
||||||
@@ -666,31 +811,31 @@ Namespace API.YouTube.Controls
|
|||||||
'
|
'
|
||||||
'TXT_EXTRA_AUDIO_FORMATS
|
'TXT_EXTRA_AUDIO_FORMATS
|
||||||
'
|
'
|
||||||
ActionButton8.BackgroundImage = CType(resources.GetObject("ActionButton8.BackgroundImage"), System.Drawing.Image)
|
ActionButton15.BackgroundImage = CType(resources.GetObject("ActionButton15.BackgroundImage"), System.Drawing.Image)
|
||||||
ActionButton8.Enabled = False
|
ActionButton15.Enabled = False
|
||||||
ActionButton8.Name = "Open"
|
ActionButton15.Name = "Open"
|
||||||
ActionButton8.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open
|
ActionButton15.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open
|
||||||
ActionButton8.ToolTipText = "Choose additional formats"
|
ActionButton15.ToolTipText = "Choose additional formats"
|
||||||
ActionButton9.BackgroundImage = CType(resources.GetObject("ActionButton9.BackgroundImage"), System.Drawing.Image)
|
ActionButton16.BackgroundImage = CType(resources.GetObject("ActionButton16.BackgroundImage"), System.Drawing.Image)
|
||||||
ActionButton9.Enabled = False
|
ActionButton16.Enabled = False
|
||||||
ActionButton9.Name = "Refresh"
|
ActionButton16.Name = "Refresh"
|
||||||
ActionButton9.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Refresh
|
ActionButton16.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Refresh
|
||||||
ActionButton9.ToolTipText = "Fill in additional formats from the defaults"
|
ActionButton16.ToolTipText = "Fill in additional formats from the defaults"
|
||||||
ActionButton10.BackgroundImage = CType(resources.GetObject("ActionButton10.BackgroundImage"), System.Drawing.Image)
|
ActionButton17.BackgroundImage = CType(resources.GetObject("ActionButton17.BackgroundImage"), System.Drawing.Image)
|
||||||
ActionButton10.Enabled = False
|
ActionButton17.Enabled = False
|
||||||
ActionButton10.Name = "Clear"
|
ActionButton17.Name = "Clear"
|
||||||
ActionButton10.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
|
ActionButton17.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
|
||||||
ActionButton10.ToolTipText = "Choose additional formats"
|
ActionButton17.ToolTipText = "Choose additional formats"
|
||||||
Me.TXT_EXTRA_AUDIO_FORMATS.Buttons.Add(ActionButton8)
|
Me.TXT_EXTRA_AUDIO_FORMATS.Buttons.Add(ActionButton15)
|
||||||
Me.TXT_EXTRA_AUDIO_FORMATS.Buttons.Add(ActionButton9)
|
Me.TXT_EXTRA_AUDIO_FORMATS.Buttons.Add(ActionButton16)
|
||||||
Me.TXT_EXTRA_AUDIO_FORMATS.Buttons.Add(ActionButton10)
|
Me.TXT_EXTRA_AUDIO_FORMATS.Buttons.Add(ActionButton17)
|
||||||
Me.TXT_EXTRA_AUDIO_FORMATS.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.CheckBox
|
Me.TXT_EXTRA_AUDIO_FORMATS.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.CheckBox
|
||||||
Me.TXT_EXTRA_AUDIO_FORMATS.CaptionText = "Additional audio formats"
|
Me.TXT_EXTRA_AUDIO_FORMATS.CaptionText = "Additional audio formats"
|
||||||
Me.TXT_EXTRA_AUDIO_FORMATS.CaptionToolTipEnabled = True
|
Me.TXT_EXTRA_AUDIO_FORMATS.CaptionToolTipEnabled = True
|
||||||
Me.TXT_EXTRA_AUDIO_FORMATS.CaptionWidth = 150.0R
|
Me.TXT_EXTRA_AUDIO_FORMATS.CaptionWidth = 150.0R
|
||||||
Me.TXT_EXTRA_AUDIO_FORMATS.ClearTextByButtonClear = False
|
Me.TXT_EXTRA_AUDIO_FORMATS.ClearTextByButtonClear = False
|
||||||
Me.TXT_EXTRA_AUDIO_FORMATS.Dock = System.Windows.Forms.DockStyle.Fill
|
Me.TXT_EXTRA_AUDIO_FORMATS.Dock = System.Windows.Forms.DockStyle.Fill
|
||||||
Me.TXT_EXTRA_AUDIO_FORMATS.Location = New System.Drawing.Point(6, 152)
|
Me.TXT_EXTRA_AUDIO_FORMATS.Location = New System.Drawing.Point(6, 180)
|
||||||
Me.TXT_EXTRA_AUDIO_FORMATS.Margin = New System.Windows.Forms.Padding(6, 3, 6, 3)
|
Me.TXT_EXTRA_AUDIO_FORMATS.Margin = New System.Windows.Forms.Padding(6, 3, 6, 3)
|
||||||
Me.TXT_EXTRA_AUDIO_FORMATS.Name = "TXT_EXTRA_AUDIO_FORMATS"
|
Me.TXT_EXTRA_AUDIO_FORMATS.Name = "TXT_EXTRA_AUDIO_FORMATS"
|
||||||
Me.TXT_EXTRA_AUDIO_FORMATS.Size = New System.Drawing.Size(589, 22)
|
Me.TXT_EXTRA_AUDIO_FORMATS.Size = New System.Drawing.Size(589, 22)
|
||||||
@@ -704,13 +849,14 @@ Namespace API.YouTube.Controls
|
|||||||
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
|
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
|
||||||
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
|
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
|
||||||
Me.CancelButton = Me.BTT_CANCEL
|
Me.CancelButton = Me.BTT_CANCEL
|
||||||
Me.ClientSize = New System.Drawing.Size(601, 271)
|
Me.ClientSize = New System.Drawing.Size(601, 328)
|
||||||
Me.Controls.Add(Me.TP_MAIN)
|
Me.Controls.Add(Me.TP_MAIN)
|
||||||
Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle
|
Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle
|
||||||
Me.Icon = Global.SCrawler.My.Resources.SiteYouTube.YouTubeIcon_32
|
Me.Icon = Global.SCrawler.My.Resources.SiteYouTube.YouTubeIcon_32
|
||||||
Me.KeyPreview = True
|
Me.KeyPreview = True
|
||||||
Me.MaximizeBox = False
|
Me.MaximizeBox = False
|
||||||
Me.MinimizeBox = False
|
Me.MinimizeBox = False
|
||||||
|
Me.MinimumSize = New System.Drawing.Size(617, 367)
|
||||||
Me.Name = "VideoOptionsForm"
|
Me.Name = "VideoOptionsForm"
|
||||||
Me.ShowInTaskbar = False
|
Me.ShowInTaskbar = False
|
||||||
Me.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide
|
Me.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide
|
||||||
@@ -726,8 +872,13 @@ Namespace API.YouTube.Controls
|
|||||||
TP_DESTINATION.ResumeLayout(False)
|
TP_DESTINATION.ResumeLayout(False)
|
||||||
CType(Me.TXT_FILE, System.ComponentModel.ISupportInitialize).EndInit()
|
CType(Me.TXT_FILE, System.ComponentModel.ISupportInitialize).EndInit()
|
||||||
TP_OK_CANCEL.ResumeLayout(False)
|
TP_OK_CANCEL.ResumeLayout(False)
|
||||||
|
TP_PLS.ResumeLayout(False)
|
||||||
|
CType(Me.CMB_PLS, System.ComponentModel.ISupportInitialize).EndInit()
|
||||||
TP_WHAT.ResumeLayout(False)
|
TP_WHAT.ResumeLayout(False)
|
||||||
TP_WHAT.PerformLayout()
|
TP_WHAT.PerformLayout()
|
||||||
|
TP_FPS_BITRATE.ResumeLayout(False)
|
||||||
|
CType(Me.TXT_FPS, System.ComponentModel.ISupportInitialize).EndInit()
|
||||||
|
CType(Me.TXT_AUDIO_BITRATE, System.ComponentModel.ISupportInitialize).EndInit()
|
||||||
Me.TP_HEADER_BASE.ResumeLayout(False)
|
Me.TP_HEADER_BASE.ResumeLayout(False)
|
||||||
Me.TP_SUBS.ResumeLayout(False)
|
Me.TP_SUBS.ResumeLayout(False)
|
||||||
Me.TP_SUBS.PerformLayout()
|
Me.TP_SUBS.PerformLayout()
|
||||||
@@ -765,5 +916,9 @@ Namespace API.YouTube.Controls
|
|||||||
Private WithEvents BTT_DOWN As Button
|
Private WithEvents BTT_DOWN As Button
|
||||||
Private WithEvents BTT_CANCEL As Button
|
Private WithEvents BTT_CANCEL As Button
|
||||||
Private WithEvents TP_HEADER_INFO_2 As TableLayoutPanel
|
Private WithEvents TP_HEADER_INFO_2 As TableLayoutPanel
|
||||||
|
Private WithEvents TXT_FPS As PersonalUtilities.Forms.Controls.TextBoxExtended
|
||||||
|
Private WithEvents CMB_PLS As PersonalUtilities.Forms.Controls.ComboBoxExtended
|
||||||
|
Private WithEvents BTT_PLS_BROWSE As Button
|
||||||
|
Private WithEvents TXT_AUDIO_BITRATE As PersonalUtilities.Forms.Controls.TextBoxExtended
|
||||||
End Class
|
End Class
|
||||||
End Namespace
|
End Namespace
|
||||||
@@ -137,6 +137,13 @@
|
|||||||
</metadata>
|
</metadata>
|
||||||
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||||
<data name="ActionButton1.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="ActionButton1.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>
|
||||||
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
||||||
|
wwAADsMBx2+oZAAAAFFJREFUOE9joAr49u3bf1Lw169f50O1QgBI0MnJCY4/vP8Ix8hiILqtrQ3TEFIM
|
||||||
|
AGGYIVDtpBsAwkQbgIyR1dDWAGLwqAGD0gByMFQ7JYCBAQChNviRiQ8ETwAAAABJRU5ErkJggg==
|
||||||
|
</value>
|
||||||
|
</data>
|
||||||
|
<data name="ActionButton2.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
<value>
|
<value>
|
||||||
iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAABGdBTUEAALGPC/xhBQAAE65JREFUeF7t
|
iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAABGdBTUEAALGPC/xhBQAAE65JREFUeF7t
|
||||||
3X2sJWddB/DdLi2lQG2hdOHuvfM887J7Cxca4ELTQMDWKigIFpBAEAgi9g+CJpJo9Q8NJhgBiYZIYspL
|
3X2sJWddB/DdLi2lQG2hdOHuvfM887J7Cxca4ELTQMDWKigIFpBAEAgi9g+CJpJo9Q8NJhgBiYZIYspL
|
||||||
@@ -235,6 +242,123 @@
|
|||||||
<metadata name="TP_OK_CANCEL.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
<metadata name="TP_OK_CANCEL.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||||
<value>False</value>
|
<value>False</value>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
<metadata name="TP_PLS.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||||
|
<value>False</value>
|
||||||
|
</metadata>
|
||||||
|
<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==
|
||||||
|
</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
|
||||||
|
</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
|
||||||
|
</value>
|
||||||
|
</data>
|
||||||
|
<data name="ActionButton6.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>
|
||||||
|
iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAABGdBTUEAALGPC/xhBQAAE65JREFUeF7t
|
||||||
|
3X2sJWddB/DdLi2lQG2hdOHuvfM887J7Cxca4ELTQMDWKigIFpBAEAgi9g+CJpJo9Q8NJhgBiYZIYspL
|
||||||
|
GlAKCkhEC4KgQlsLQkqhKi/lrYWWlxaw3dLddrerz/Q89+7dc2fbfTn3npf5fJJv2rS758z85nnOzJz5
|
||||||
|
nZktAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMK3O3r79wVUIz65jfGNVxI/VIX69CvGO9M//a9P+e8o3B/8v
|
||||||
|
vKn9s+3fyX8dAJgmaWd+fl3E96Wd/E9XdvZHkfbvXNa+Rn45AGCS3bvjj/E/h3box5OrmxjPyy8PAEyS
|
||||||
|
XXO7zqhCeH/HDnwUOdCE+J6zdux4eH47YIrEGE8uy/Ls9Bnx/LooL0oH9b9Th/I1TVG+rCqKC+q6Xsh/
|
||||||
|
FJgmO8vy6WknfdPQTnsjckMdwlPy2wITLO3wF6si/lGas1ekuXvX0Fzuyg9S3psOCl6qDwimQB3ji9Ok
|
||||||
|
3btmEm907kpnEa/Mbw9Mlq1pB/6cdHZ/ZcfcPZrcXoXyrVVVFfl1gUmSdsS/libqPUMTd5NSvjktwrbB
|
||||||
|
kgDjVi1UT26K+Nnu+XrMuaud60uPWHpIfhtg3JqyfEaanHcPTdZNTRPCPy4uLj40LxIwBudt2fKAtOP/
|
||||||
|
0zQnN+5koIg3tpca81sC49J+LZcm5a3rJulYEq6LSV40YBOFEB6V5uFV6+flRiTsSwf9r81vDYzBCSO4
|
||||||
|
vjfq/KAuiqfm5QM2QRPjuWnubUbz71DCn6W33zpYCmDT1EX5m92Tcuy5q47xFXkxgQ3UduqnOXfn0Bzc
|
||||||
|
xJSvz4sCbIb2pzlp8v1w/WScnKSzkjekRT1hsMTAKC0vL5/Ydud3zb1NT1FelBcL2GiDm3d0TMTJy0ea
|
||||||
|
pjk1LzYwAu3NvtLc+uTQXBtn7tYYCJtja/vQno5JOJFpQrzWb4hhNJoQnpjm1Q3D82wCcnNRFKfnxQQ2
|
||||||
|
Qttk1zH5JjzhFmcIcHzyzb6O5aFem5J0sP/OvKjARmg7b7sm3xRkT3vDorwawJHb1t6Ep2NOTVoOtDch
|
||||||
|
yssMjFr6IPh8x8SbnsT4lrQamgPhCMzPzz+sifHjnXNpMnN5XnRglJaWlk5KE2z/0ISbxnzQQ0bgvlXz
|
||||||
|
1ePSXPnG0NyZ+DRF8Zi8CsCo7Azh0V0TbkrzRc2B0G3wIJ9429CcmZLce4MgYJTyff87JtzU5uayLM/J
|
||||||
|
qwcM7vD5+jQ3DgzNlWnKDXldgFFJZwW/2jHZpj1727uZ5VWE3mofqJXmw4eG5sdUpqqqXXm1gFGoQnhJ
|
||||||
|
12SbgRxoYvzjtIruK04vxRjPSvPgK0PzYmqTPqtemVcNGIU6xgu7JtusJH1ovH9ubu6UvLrQC2ncPyuN
|
||||||
|
/58Mz4fpTvnmvHrAKJQL5dO6J9ssJXxucWFhLq8yzLKtaUf5h2ncb9zz+8eUKsYP53UERmHX/PyOrsk2
|
||||||
|
g7nJDUWYZUuPWHpIE8oPdIz92UiMn86rCoxIOmOYta8KD5uftk2Peb1hZtTzdVOHcF3HmJ+ZVCF+Ia8u
|
||||||
|
MCppcl0+PNlmOG1zYPtYYc2BzIQ0np+ZxvWPh8b5LObqvMrAqEzRo4BHmctijCfnEsBUqkP5u2ksz8Kd
|
||||||
|
PI8g5SfyagOj0jbIpQk2c01DR5Brmh3NfC4DTI324LWO8V0dY3pm48mAsEGm7OEgo0sRb9wZ4+NzGWDi
|
||||||
|
lWUZ0ti9Zt1YnvUU8fdyCYBRmsFbAh9xqhDvqEN4Xi4FTKz8s93vD4/hPiSdpJyXywCMWPtrgKuGJ12P
|
||||||
|
ck/6gPmDXAuYOHVR/lY6UN3XMXb7kDv17MAGqhaqJ6WJ1sdegDUJ726a5oG5JDB2917vL+Kl3eO1N/lQ
|
||||||
|
LgewUdIO8E0dk69vubosy+25JDA2bYNuFeJnOsZovxLjhbkkwEZZXl4+0QfOvfl2Ogg4O5cFNl1dFE9N
|
||||||
|
4/B7Q+Oyj7mh/VzKZQE2UtM0j6iL+LWOidizhN3OPBiHuigvSmPwrvVjsn9pQnh1LguwGQa3Fo3fHp6M
|
||||||
|
Pcw97c1WcllgQy0tLZ2UDr7/qmMc9jJNiF/WkwNjMHhQ0GzfX/yIU8RLfRCxkdq+kzTfrugcf/3MgZ1l
|
||||||
|
+fRcHmCztU8Yq2P8h47J2cdcpTmQjdCE8IQ0vnzjdkjKP8nlAcZoWxXin3dP0n4l1eGb9UL92FwXOG51
|
||||||
|
Ub48ja09w2Otz2nvTJpKs21QIWDs0lnKb6TJqTEphN3NQvncXBY4VtvSju4N3WOs17l6cXHxoblGwKRo
|
||||||
|
r8mlHeAtHZO2b9mfDohem8sCR2XX3K4z0hj65NCYklSTGONpuUzApNlVFFWaqP81NHF7mvD2tnM7lwbu
|
||||||
|
V/vwqTR2vrV+LPU7VSjf4ff+MAU0B65NeWVd12fm0sBhpTnzosHDp7rGUV8T9lVFvDiXCJgSrmEezDea
|
||||||
|
onhMrgsM25rmyuvSODkwNG56nvZyYvi5XCNg2mgOXM3tVVH9ci4L3KtpmlN9W7Y+VYhfiEkuEzCt8n3L
|
||||||
|
fzA8yXuY/b7OZEVZlovt3ew6xknf8965ublTcpmAaac5cG3C2zQ09Vv7bVAaC/+7fmz0Og6QYVZpDlyT
|
||||||
|
GD/dPlgpl4b+2Nru5NIYuGfdmOhxmhB/VBblL+QaATNKc+DBfH1nCI/OdWHGtTewSdv874fGgIT4xfYb
|
||||||
|
wlwmYNZpDlzNbVUIz85lYUblJ2i6BDacGP/u7O3bH5zLBPSF5sDV7K+L+Nu5LMyYtJP7xbSNfzy0zfue
|
||||||
|
A+03gak8WwdVAnpHc+CaxHiJ5sCZsnK9f/+6bd3v3JZ2/r+SawT0mebAg0kfjB93v/Pp136t3X693bWN
|
||||||
|
e56v6nsBhmkOXE24Ph0EnJXrwpSp63qhDuXnu7dtn1P+U1VVP5PLBHAozYGDtD+LchvU6TN4Iqa+lqGs
|
||||||
|
XO8/YVAlgMPQHLiSsC+dNb0ml4UJVxflRWm73b1+O/Y5YXcVwvNziQDun+bANYnxkvO2bHlALg0TJsZ4
|
||||||
|
cl3ESzu3Xa8Trm+KYimXCeDIaQ48mKqIH9McOHl2zc/vaIr42a5t1vN8tCiK03OZAI6J5sCVFPFr7QNk
|
||||||
|
cl0Ys3yp6nvrtlO/s3K9f9ugSgDHSXPgILk58PxcFsYkX+93J8s1qUK8oynKF+YSAYyO5sCVhH3pgOjV
|
||||||
|
uSxsoqZpHpjq//bu7dLjFPHGND+Xc5kARk9z4JrE+JZUEl+1bpLFhYW5VPf/WLcd5N/ruj4zlwlg42gO
|
||||||
|
PCQfdXOVjdeE8MRU6xuGai9uXw2MgebA1YTrFkMoc10YsaYoX5rqfOf6uvc6e9LO/xW5RACbT3Pgam5N
|
||||||
|
B0Q/m8vCCLT3XnCQ2ZXwnWqhenIuE8D4aA5czV3OykZj19yuM1I9PzlUXwnhirIst+cyAYyf5sA1GTQH
|
||||||
|
uu/6MdoZ4+NTHb+1rq59j+v9wKTSHHhIPtI0zam5NByhNH5enGr306Fa9j1720ttuUQAE0tz4Epi/FJM
|
||||||
|
cl24b8ZNd25KdTk31whg8mkOXEm4pX1EbS4LHebn5x+WdnIf765fr3NVCOFRuUwA00Nz4Gr21kX58lwW
|
||||||
|
1qjmq8el+nxjqF4S4yVLS0sn5TIBTB/NgWuiOfAQTVE+J9XltnV16nXCvqqIF+cSAUw3zYGH5INnb9/+
|
||||||
|
4Fyavtra7uRSLe4Zqk3f88MmxvNyjQBmhiavg/liVVVFrkuvLC4uPjSt/4eG6iEhXlOWZchlApg9mgNX
|
||||||
|
c3P6wD8nl6UXqvlqZ1rv/xmqQ+/ThPJv5ufnH5TLBDC7NAeuZm97n/tclplWhfCstL4/GVr/nsf1fqCH
|
||||||
|
NAeu5kB7aSSVZFabA13v786tVVFckGsE0C+aAw8mnSG/f25u7pRcmpnQbt8mlB/oWt8+pwnxWk+PBNAc
|
||||||
|
uJoqxC/MSnNgs7BQ1yFc17WePc97Z+1AD+C4aA5czU3T/qjXdED3zLQePx5ar75nf77ev3VQJQBWaQ5c
|
||||||
|
zZ4qhJfkskyVuigvapvbOtapt2lC/FFTls/IJQKgi+bA1aw0B07FGWOM8eQ6xnd1rEe/E+OX2jGdywTA
|
||||||
|
fdEcuCYx/u2kXzNudjTz6az/c53L3+NUMX7YI6EBjp7mwIO5pt3J5rpMlHKhfFpavu8PLW/fM+s/7QTY
|
||||||
|
eJoDV/PduiiWc1kmwuB6f7x7aDn7ntvrGC/MJQLgeGgOHKQK8Y46hOflsoxN0zQPbEJ8Z9cy9jxf3RnC
|
||||||
|
o3OZABgFzYGrab9efl0qyViaAxcXFubSgchnOpar77k8xnhaLhMAo6Q5cE2K+L7NfoBMHcJT0nvfvG5Z
|
||||||
|
+h3X+wE2iebAg7m6LMvtuS4bKl/v14txSMLuKoQX5BIBsBk0B64kfCfV4om5LCN33pYtD3DA1ZVwfb1Q
|
||||||
|
PzaXCYDNpDlwJWH3RnSe75rbdUZ6/X9b/369z0eLojg9lwmAcdAcuJqV5sCRaEJ4QnrNbw+9R9+zcr1/
|
||||||
|
26BKAIyV5sBDcll7W95cmmPSPocgvc6dQ6/b9+xpivJluUQATBDNgQdz1TE2B6phV4p446TdhAmAIens
|
||||||
|
9ZXpQ1tz4KA58Am5LPfrrB07Hp7+zr90v1Z/UxXxU3Vdn5nLBMAk0xy4krC7WSifm8tyWGVZnl2F+M3u
|
||||||
|
1+hxYrxkeXn5xFwmAKaB5sDV7E9nsRfnsqyTdnIvGtxiuPPv9jV7Ul1ekUsEwLTRHHgwVSjfsbS0dFIu
|
||||||
|
TWtr+6uB9P8ODP/Znue7ZVmek2sEwBTT2Laa8sr2enb7jHoHRl0JV8QYH5nHDQCzwJ0DV/P1tKO7vuO/
|
||||||
|
9zpVKN/qej/AjNIcKB3Z24TyVXmIADCrNAfKmtzUxHhuHhoAzDrNgZJyVQjhUXlIANAjmgN7m/Du471d
|
||||||
|
MgBTzp0D+5Sw777uiQBAz2gO7EPCLSnn500OAAOaA2c615RlGfKmBoBDaQ6cvTQhvmd+fv5BeRMDwGFp
|
||||||
|
DpyJuN4PwDHQHDjVubUqigvypgSAo6M5cPrShHjtYghl3oQAcGw0B05Rivi+ubm5U/KmA4Djozlw4rM/
|
||||||
|
X+/fOthiADA6mgMnME2IP2rK8hl5GwHAxtAcOFH5SozxrLxpAGBjaQ4cf6oYP9w0zal5kwDA5tAcOLYc
|
||||||
|
aC/FpE1wwmBLAMAm0xy46bk91fvCXH4AGCvNgZuRIn6tKYrH5JoDwGTQHLihuTzGeFouNQBMFs2BI4/r
|
||||||
|
/QBMB82Bo0rYXYXwglxWAJh8mgOPN+H6eqF+bC4nAEwVzYHHkiL+c1EUp+caAsB00hx4FInxLalk2waV
|
||||||
|
A4AppznwfrOnLsqX53IBwOzQHHiYFPHGaqF6Ui4TAMwezYGHpirip+q6PjOXBwBmmubANjFesry8fGKu
|
||||||
|
CQD0Q4+bA/dWMf56LgMA9E8PmwO/W5blOXn1AaC/+tMcWF4ZY3xkXm0AYOabA2O8ZGlp6aS8ugDAGrPY
|
||||||
|
HLi3CeWr8voBAIczQ82BN6UDmnPzagEA92f6mwPLz1dVVeTVAQCO1LQ2B1Yh/PX8/PyD8moAAEdrupoD
|
||||||
|
w76qiBfnRQcAjtMUNAeGW1LOz8sLAIzKBDcHXlOWZciLCQCM2gQ2B142Nzd3Sl48AGCjTEhz4H7X+wFg
|
||||||
|
k425OfDWqqh+Pi8KALDJtqWDgDemHfKBoR30hqUJ8dqY5PcHAMalKcrnpJ3z94Z31qNO+/t+1/sBYIKk
|
||||||
|
k/LT6hD+Mu2oR/4rgXTW/+X02r+U3woAmDTtz/GaIv5F2nH/ZHhHfpS5J+Vf01n/S9LLbhu8OgAw0dpb
|
||||||
|
8TYL5XPTmfvb0o78v/MOvWtHvybtzXzKT1Qx/n5d1wv5pQCAaXXvAUFRLLXd+3WMFzZF+cKUl7X/rIri
|
||||||
|
gsWFhbn8RwEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6LEtW/4flgYiLD1qeX0A
|
||||||
|
AAAASUVORK5CYII=
|
||||||
|
</value>
|
||||||
|
</data>
|
||||||
<metadata name="LB_SEP_1.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
<metadata name="LB_SEP_1.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||||
<value>False</value>
|
<value>False</value>
|
||||||
</metadata>
|
</metadata>
|
||||||
@@ -253,68 +377,9 @@
|
|||||||
<metadata name="LBL_SUBS_FORMAT.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
<metadata name="LBL_SUBS_FORMAT.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||||
<value>False</value>
|
<value>False</value>
|
||||||
</metadata>
|
</metadata>
|
||||||
<data name="ActionButton2.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<metadata name="TP_FPS_BITRATE.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||||
<value>
|
<value>False</value>
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
</metadata>
|
||||||
wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP
|
|
||||||
WQwhyWIyJIUW5NqyPb7oCVtIlhVTwYf8nv7/t2zJagel9KmqKsIACYL9RjI8UHz5zshougZr/AEvbxEP
|
|
||||||
aZCDBY3VslixaJvX3wzkkDiOwbZtDRGA5vdNAg+TL27qgmt5XkBG/gTdAG7Gt+3PP9oOaEGFCVEC6rp+
|
|
||||||
5g9MfM/c5e4OsEZMZkQEtGL5H2DdZ5JRArDwPA+iKII0TfkC9vroC9j5vq8JTWw3WzWgLMtZGIaa0MR8
|
|
||||||
vlAD8PYlSaIJTTiOowY0p0Bc19XEJo6HE59FAPuMzyAINKGJ1XLFZxHALtMrnkBXOIQIIIQ8YvF/KrgB
|
|
||||||
cMaRN0UdBBkAAAAASUVORK5CYII=
|
|
||||||
</value>
|
|
||||||
</data>
|
|
||||||
<data name="ActionButton3.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
|
||||||
<value>
|
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6
|
|
||||||
JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAACOElE
|
|
||||||
QVQ4T2P4//8/QczOJyyqHpzfiE0OQwAZC8iqszAzs7CJ69o4BR768V/W2jcGXQ0KB4aFNS3dDQtnrbCb
|
|
||||||
ePCK48wTN1wXXXzge/jXf/clV55zC4hIIatF0cjIyMikElzc57z0wX+XHd/+2+//99/ywP//xlu//tdb
|
|
||||||
+eK/4Zp3/1WTOhYzARViNUAluKjTdf37/0ZTTn9TbdhwXblhwwW1/qOP1Ja9+K8w+95/6cm3/6v2Xvkv
|
|
||||||
qKjniGGAoIqRpW3/4e8S9uGdzFz82gwMDFxAzCxm4ZegtuLDf+VJ1/8rZM25IqLvnM/CximCYYCic1QN
|
|
||||||
v7x2JIwPwyrJ3XNUylddE9G2TWNmZOBDl4czmJiZMSRBmFdSyYyJgUEQmxwIYxWEYXZBCUls4sgYq6CA
|
|
||||||
prWNbtG8nXKeaVPR5XiVjSxEzf0yYXy4BBMLO6eQjoOXZvrkbbazrv53Xf/2v4CSbjBMXkhBl1/CMyNZ
|
|
||||||
qWnvGy5pNQ+YONwAfjXzAOupl/47LLr333L50/96q9/8l23YdES6cO5KuYqVW+R7Tj6SnfP0v4hryjyY
|
|
||||||
HhQDmFjYeHVKFp7WX/Xuv9Kq9/+Vd/z7r7rv/3+l7f//y676DEwDN/9L+BVvYkKLCTgDhNkkVUyVlr74
|
|
||||||
qbbz73/VOTc/qsy89kWx+9h7qbQpJwS1bbOAscGGrB6EUTggLOqf16C55ft/HlnNAFZOXgVWdi4FRgYG
|
|
||||||
VnR1MIwhwMTCyqEQ37qEmZVDFF0OE/9nAACtFF4Ey6OP+wAAAABJRU5ErkJggg==
|
|
||||||
</value>
|
|
||||||
</data>
|
|
||||||
<data name="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
|
|
||||||
</value>
|
|
||||||
</data>
|
|
||||||
<data name="ActionButton5.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=
|
|
||||||
</value>
|
|
||||||
</data>
|
|
||||||
<data name="ActionButton6.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
|
||||||
<value>
|
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6
|
|
||||||
JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAACOElE
|
|
||||||
QVQ4T2P4//8/QczOJyyqHpzfiE0OQwAZC8iqszAzs7CJ69o4BR768V/W2jcGXQ0KB4aFNS3dDQtnrbCb
|
|
||||||
ePCK48wTN1wXXXzge/jXf/clV55zC4hIIatF0cjIyMikElzc57z0wX+XHd/+2+//99/ywP//xlu//tdb
|
|
||||||
+eK/4Zp3/1WTOhYzARViNUAluKjTdf37/0ZTTn9TbdhwXblhwwW1/qOP1Ja9+K8w+95/6cm3/6v2Xvkv
|
|
||||||
qKjniGGAoIqRpW3/4e8S9uGdzFz82gwMDFxAzCxm4ZegtuLDf+VJ1/8rZM25IqLvnM/CximCYYCic1QN
|
|
||||||
v7x2JIwPwyrJ3XNUylddE9G2TWNmZOBDl4czmJiZMSRBmFdSyYyJgUEQmxwIYxWEYXZBCUls4sgYq6CA
|
|
||||||
prWNbtG8nXKeaVPR5XiVjSxEzf0yYXy4BBMLO6eQjoOXZvrkbbazrv53Xf/2v4CSbjBMXkhBl1/CMyNZ
|
|
||||||
qWnvGy5pNQ+YONwAfjXzAOupl/47LLr333L50/96q9/8l23YdES6cO5KuYqVW+R7Tj6SnfP0v4hryjyY
|
|
||||||
HhQDmFjYeHVKFp7WX/Xuv9Kq9/+Vd/z7r7rv/3+l7f//y676DEwDN/9L+BVvYkKLCTgDhNkkVUyVlr74
|
|
||||||
qbbz73/VOTc/qsy89kWx+9h7qbQpJwS1bbOAscGGrB6EUTggLOqf16C55ft/HlnNAFZOXgVWdi4FRgYG
|
|
||||||
VnR1MIwhwMTCyqEQ37qEmZVDFF0OE/9nAACtFF4Ey6OP+wAAAABJRU5ErkJggg==
|
|
||||||
</value>
|
|
||||||
</data>
|
|
||||||
<data name="ActionButton7.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="ActionButton7.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
<value>
|
<value>
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
||||||
@@ -324,6 +389,14 @@
|
|||||||
</value>
|
</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ActionButton8.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="ActionButton8.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>
|
||||||
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
||||||
|
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
|
||||||
|
tbbB43rK5xSAQq1VYFtmeQBoqZTSreVZvgTknM8yyyjA/qodsDF9gspD2Bj6B+DH+NqzhQQAG+POMnSX
|
||||||
|
AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC
|
||||||
|
</value>
|
||||||
|
</data>
|
||||||
|
<data name="ActionButton9.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
<value>
|
<value>
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
||||||
wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP
|
wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP
|
||||||
@@ -334,7 +407,7 @@
|
|||||||
cMaRN0UdBBkAAAAASUVORK5CYII=
|
cMaRN0UdBBkAAAAASUVORK5CYII=
|
||||||
</value>
|
</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ActionButton9.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="ActionButton10.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
<value>
|
<value>
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6
|
||||||
JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAACOElE
|
JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAACOElE
|
||||||
@@ -350,7 +423,77 @@
|
|||||||
VnR1MIwhwMTCyqEQ37qEmZVDFF0OE/9nAACtFF4Ey6OP+wAAAABJRU5ErkJggg==
|
VnR1MIwhwMTCyqEQ37qEmZVDFF0OE/9nAACtFF4Ey6OP+wAAAABJRU5ErkJggg==
|
||||||
</value>
|
</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ActionButton10.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<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
|
||||||
|
</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=
|
||||||
|
</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==
|
||||||
|
</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
|
||||||
|
</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=
|
||||||
|
</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==
|
||||||
|
</value>
|
||||||
|
</data>
|
||||||
|
<data name="ActionButton17.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
<value>
|
<value>
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
||||||
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
|
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
|
||||||
|
|||||||
@@ -22,21 +22,49 @@ Namespace API.YouTube.Controls
|
|||||||
Friend Class VideoOptionsForm : Implements IDesignXMLContainer
|
Friend Class VideoOptionsForm : Implements IDesignXMLContainer
|
||||||
#Region "Declarations"
|
#Region "Declarations"
|
||||||
Private MyView As FormView
|
Private MyView As FormView
|
||||||
|
Private ReadOnly MyFieldsChecker As FieldsChecker
|
||||||
Friend Property DesignXML As EContainer Implements IDesignXMLContainer.DesignXML
|
Friend Property DesignXML As EContainer Implements IDesignXMLContainer.DesignXML
|
||||||
Private Property DesignXMLNodes As String() Implements IDesignXMLContainer.DesignXMLNodes
|
Private Property DesignXMLNodes As String() Implements IDesignXMLContainer.DesignXMLNodes
|
||||||
Private Property DesignXMLNodeName As String Implements IDesignXMLContainer.DesignXMLNodeName
|
Private Property DesignXMLNodeName As String Implements IDesignXMLContainer.DesignXMLNodeName
|
||||||
Private Const ControlsRow As Integer = 6
|
Private Const ControlsRow As Integer = 7
|
||||||
Private ReadOnly Property CNT_PROCESSOR As TableControlsProcessor
|
Private ReadOnly Property CNT_PROCESSOR As TableControlsProcessor
|
||||||
Friend Property MyContainer As YouTubeMediaContainerBase
|
Friend Property MyContainer As YouTubeMediaContainerBase
|
||||||
Private Initialization As Boolean = True
|
Private Initialization As Boolean = True
|
||||||
Private ReadOnly IsSavedObject As Boolean
|
Private ReadOnly InheritsFromContainer As Boolean
|
||||||
|
Private ReadOnly M3U8Files As List(Of SFile)
|
||||||
|
Private ReadOnly Property M3U8FilesFull As List(Of SFile)
|
||||||
|
Get
|
||||||
|
Return ListAddList(Nothing, M3U8Files, LAP.NotContainsOnly).ListAddValue(CMB_PLS.Text, LAP.NotContainsOnly)
|
||||||
|
End Get
|
||||||
|
End Property
|
||||||
|
Private Class FpsFieldChecker : Inherits FieldsCheckerProviderBase
|
||||||
|
Private ReadOnly MyProvider As ANumbers = YouTubeSettings.FpsFormatProvider.MyProviderDefault
|
||||||
|
Public Overrides Property ErrorMessage As String
|
||||||
|
Get
|
||||||
|
Return IIf(HasError, YouTubeSettings.FpsFormatProvider.ErrorMessageDefault, String.Empty)
|
||||||
|
End Get
|
||||||
|
Set : End Set
|
||||||
|
End Property
|
||||||
|
Public Overrides Function Convert(ByVal Value As Object, ByVal DestinationType As Type, ByVal Provider As IFormatProvider,
|
||||||
|
Optional ByVal NothingArg As Object = Nothing, Optional ByVal e As ErrorsDescriber = Nothing) As Object
|
||||||
|
If ACheck(Of Double)(Value, AModes.Var, MyProvider) Then
|
||||||
|
Return Value
|
||||||
|
Else
|
||||||
|
HasError = True
|
||||||
|
TypeError = True
|
||||||
|
Return Nothing
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
End Class
|
||||||
#End Region
|
#End Region
|
||||||
#Region "Initializers"
|
#Region "Initializers"
|
||||||
Friend Sub New(ByVal Container As YouTubeMediaContainerBase, Optional ByVal IsSavedObject As Boolean = False)
|
Friend Sub New(ByVal Container As YouTubeMediaContainerBase, Optional ByVal InheritsFromContainer As Boolean = False)
|
||||||
InitializeComponent()
|
InitializeComponent()
|
||||||
|
M3U8Files = New List(Of SFile)
|
||||||
MyContainer = Container
|
MyContainer = Container
|
||||||
CNT_PROCESSOR = New TableControlsProcessor(TP_CONTROLS)
|
CNT_PROCESSOR = New TableControlsProcessor(TP_CONTROLS)
|
||||||
Me.IsSavedObject = IsSavedObject
|
Me.InheritsFromContainer = InheritsFromContainer
|
||||||
|
MyFieldsChecker = New FieldsChecker
|
||||||
End Sub
|
End Sub
|
||||||
#End Region
|
#End Region
|
||||||
#Region "Form handlers"
|
#Region "Form handlers"
|
||||||
@@ -48,6 +76,8 @@ Namespace API.YouTube.Controls
|
|||||||
End If
|
End If
|
||||||
|
|
||||||
MyYouTubeSettings.DownloadLocations.PopulateComboBox(TXT_FILE)
|
MyYouTubeSettings.DownloadLocations.PopulateComboBox(TXT_FILE)
|
||||||
|
MyYouTubeSettings.PlaylistsLocations.PopulateComboBox(CMB_PLS,, True)
|
||||||
|
CMB_PLS.Text = MyYouTubeSettings.LatestPlaylistFile.Value
|
||||||
|
|
||||||
If Not MyContainer Is Nothing Then
|
If Not MyContainer Is Nothing Then
|
||||||
With MyContainer
|
With MyContainer
|
||||||
@@ -68,8 +98,8 @@ Namespace API.YouTube.Controls
|
|||||||
If Not .PlaylistTitle.IsEmptyString Or Not .Title.IsEmptyString Then Text &= $" - { .PlaylistTitle.IfNullOrEmpty(.Title)}"
|
If Not .PlaylistTitle.IsEmptyString Or Not .Title.IsEmptyString Then Text &= $" - { .PlaylistTitle.IfNullOrEmpty(.Title)}"
|
||||||
TP_MAIN.Controls.Remove(TP_HEADER_BASE)
|
TP_MAIN.Controls.Remove(TP_HEADER_BASE)
|
||||||
TP_MAIN.RowStyles(0).Height = 0
|
TP_MAIN.RowStyles(0).Height = 0
|
||||||
Dim def% = If(IsSavedObject, .ArrayMaxResolution, MyYouTubeSettings.DefaultVideoDefinition.Value)
|
Dim def% = If(InheritsFromContainer, .ArrayMaxResolution, MyYouTubeSettings.DefaultVideoDefinition.Value)
|
||||||
If IsSavedObject Then
|
If InheritsFromContainer Then
|
||||||
__audioOnly = def = -2
|
__audioOnly = def = -2
|
||||||
If def <= 0 Then def = MyYouTubeSettings.DefaultVideoDefinition
|
If def <= 0 Then def = MyYouTubeSettings.DefaultVideoDefinition
|
||||||
Else
|
Else
|
||||||
@@ -98,7 +128,7 @@ Namespace API.YouTube.Controls
|
|||||||
LBL_URL.Text = .URL
|
LBL_URL.Text = .URL
|
||||||
End If
|
End If
|
||||||
|
|
||||||
If .IsMusic Or __audioOnly Then
|
If .IsMusic Or __audioOnly Or (InheritsFromContainer And .IsAudioSelected) Then
|
||||||
OPT_AUDIO.Checked = True
|
OPT_AUDIO.Checked = True
|
||||||
Else
|
Else
|
||||||
OPT_VIDEO.Checked = True
|
OPT_VIDEO.Checked = True
|
||||||
@@ -107,7 +137,7 @@ Namespace API.YouTube.Controls
|
|||||||
|
|
||||||
arr = AvailableVideoFormats
|
arr = AvailableVideoFormats
|
||||||
CMB_FORMAT.Items.AddRange(arr)
|
CMB_FORMAT.Items.AddRange(arr)
|
||||||
If IsSavedObject Then
|
If InheritsFromContainer Then
|
||||||
__optionValue = .OutputVideoExtension.IfNullOrEmpty(MyYouTubeSettings.DefaultVideoFormat.Value)
|
__optionValue = .OutputVideoExtension.IfNullOrEmpty(MyYouTubeSettings.DefaultVideoFormat.Value)
|
||||||
Else
|
Else
|
||||||
__optionValue = MyYouTubeSettings.DefaultVideoFormat.Value
|
__optionValue = MyYouTubeSettings.DefaultVideoFormat.Value
|
||||||
@@ -116,7 +146,7 @@ Namespace API.YouTube.Controls
|
|||||||
|
|
||||||
arr = AvailableAudioFormats
|
arr = AvailableAudioFormats
|
||||||
CMB_AUDIO_CODEC.Items.AddRange(arr)
|
CMB_AUDIO_CODEC.Items.AddRange(arr)
|
||||||
If IsSavedObject Then
|
If InheritsFromContainer Then
|
||||||
__optionValue = .OutputAudioCodec.IfNullOrEmpty(IIf(.IsMusic, MyYouTubeSettings.DefaultAudioCodecMusic.Value, MyYouTubeSettings.DefaultAudioCodec.Value))
|
__optionValue = .OutputAudioCodec.IfNullOrEmpty(IIf(.IsMusic, MyYouTubeSettings.DefaultAudioCodecMusic.Value, MyYouTubeSettings.DefaultAudioCodec.Value))
|
||||||
Else
|
Else
|
||||||
__optionValue = IIf(.IsMusic, MyYouTubeSettings.DefaultAudioCodecMusic.Value, MyYouTubeSettings.DefaultAudioCodec.Value)
|
__optionValue = IIf(.IsMusic, MyYouTubeSettings.DefaultAudioCodecMusic.Value, MyYouTubeSettings.DefaultAudioCodec.Value)
|
||||||
@@ -125,13 +155,25 @@ Namespace API.YouTube.Controls
|
|||||||
|
|
||||||
arr = AvailableSubtitlesFormats
|
arr = AvailableSubtitlesFormats
|
||||||
CMB_SUBS_FORMAT.Items.AddRange(arr)
|
CMB_SUBS_FORMAT.Items.AddRange(arr)
|
||||||
If IsSavedObject Then
|
If InheritsFromContainer Then
|
||||||
__optionValue = .OutputSubtitlesFormat.IfNullOrEmpty(IIf(.IsMusic, "LRC", MyYouTubeSettings.DefaultSubtitlesFormat.Value))
|
__optionValue = .OutputSubtitlesFormat.IfNullOrEmpty(IIf(.IsMusic, "LRC", MyYouTubeSettings.DefaultSubtitlesFormat.Value))
|
||||||
Else
|
Else
|
||||||
__optionValue = IIf(.IsMusic, "LRC", MyYouTubeSettings.DefaultSubtitlesFormat.Value)
|
__optionValue = IIf(.IsMusic, "LRC", MyYouTubeSettings.DefaultSubtitlesFormat.Value)
|
||||||
End If
|
End If
|
||||||
setDef(CMB_SUBS_FORMAT, __optionValue)
|
setDef(CMB_SUBS_FORMAT, __optionValue)
|
||||||
|
|
||||||
|
If InheritsFromContainer Then
|
||||||
|
If .OutputVideoFPS > 0 Then TXT_FPS.Text = .OutputVideoFPS
|
||||||
|
If .OutputAudioBitrate > 0 Then TXT_AUDIO_BITRATE.Text = .OutputAudioBitrate
|
||||||
|
Else
|
||||||
|
If MyYouTubeSettings.DefaultVideoFPS > 0 Then TXT_FPS.Text = MyYouTubeSettings.DefaultVideoFPS
|
||||||
|
If MyYouTubeSettings.DefaultAudioBitrate > 0 Then TXT_AUDIO_BITRATE.Text = MyYouTubeSettings.DefaultAudioBitrate.Value
|
||||||
|
End If
|
||||||
|
With MyFieldsChecker
|
||||||
|
.AddControl(Of Double)(TXT_FPS, TXT_FPS.CaptionText, True, New FpsFieldChecker)
|
||||||
|
.AddControl(Of Integer)(TXT_AUDIO_BITRATE, TXT_AUDIO_BITRATE.CaptionText, True)
|
||||||
|
.EndLoaderOperations()
|
||||||
|
End With
|
||||||
TP_SUBS.Enabled = .Subtitles.Count > 0
|
TP_SUBS.Enabled = .Subtitles.Count > 0
|
||||||
TXT_SUBS_ADDIT.Enabled = .Subtitles.Count > 0
|
TXT_SUBS_ADDIT.Enabled = .Subtitles.Count > 0
|
||||||
RefillTextBoxes()
|
RefillTextBoxes()
|
||||||
@@ -151,6 +193,8 @@ Namespace API.YouTube.Controls
|
|||||||
End Sub
|
End Sub
|
||||||
Private Sub VideoOptionsForm_Closing(sender As Object, e As CancelEventArgs) Handles Me.Closing
|
Private Sub VideoOptionsForm_Closing(sender As Object, e As CancelEventArgs) Handles Me.Closing
|
||||||
MyView.DisposeIfReady()
|
MyView.DisposeIfReady()
|
||||||
|
MyFieldsChecker.DisposeIfReady()
|
||||||
|
M3U8Files.Clear()
|
||||||
End Sub
|
End Sub
|
||||||
#End Region
|
#End Region
|
||||||
#Region "Refill"
|
#Region "Refill"
|
||||||
@@ -183,7 +227,7 @@ Namespace API.YouTube.Controls
|
|||||||
Dim data As IEnumerable(Of Control)
|
Dim data As IEnumerable(Of Control)
|
||||||
|
|
||||||
If .HasElements Then
|
If .HasElements Then
|
||||||
data = .Elements.Select(Function(ee) New MediaItem(ee) With {.Dock = DockStyle.Fill, .Checked = ee.Checked, .IgnoreDownloadState = True})
|
data = .Elements.Select(Function(ee) New MediaItem(ee, True) With {.Dock = DockStyle.Fill, .Checked = ee.Checked})
|
||||||
Else
|
Else
|
||||||
data = (From m As MediaObject In .Self.MediaObjects
|
data = (From m As MediaObject In .Self.MediaObjects
|
||||||
Where m.Type = __contentType
|
Where m.Type = __contentType
|
||||||
@@ -204,6 +248,8 @@ Namespace API.YouTube.Controls
|
|||||||
If MyContainer.HasElements Then
|
If MyContainer.HasElements Then
|
||||||
With DirectCast(d, MediaItem)
|
With DirectCast(d, MediaItem)
|
||||||
AddHandler .CheckedChanged, AddressOf MediaItem_CheckedChanged
|
AddHandler .CheckedChanged, AddressOf MediaItem_CheckedChanged
|
||||||
|
AddHandler .BeforeOpenEditor, AddressOf MediaItem_BeforeOpenEditor
|
||||||
|
AddHandler .BeforeOpenEditorFull, AddressOf MediaItem_BeforeOpenEditorFull
|
||||||
AddHandler .Click, AddressOf CNT_PROCESSOR.MediaItem_Click
|
AddHandler .Click, AddressOf CNT_PROCESSOR.MediaItem_Click
|
||||||
AddHandler .KeyDown, AddressOf CNT_PROCESSOR.MediaItem_KeyDown
|
AddHandler .KeyDown, AddressOf CNT_PROCESSOR.MediaItem_KeyDown
|
||||||
End With
|
End With
|
||||||
@@ -271,21 +317,50 @@ Namespace API.YouTube.Controls
|
|||||||
Private Sub MediaItem_CheckedChanged(ByVal Sender As MediaItem, ByVal Container As IYouTubeMediaContainer)
|
Private Sub MediaItem_CheckedChanged(ByVal Sender As MediaItem, ByVal Container As IYouTubeMediaContainer)
|
||||||
ControlInvokeFast(TP_CONTROLS, Sub() Container.Checked = Sender.Checked, EDP.None)
|
ControlInvokeFast(TP_CONTROLS, Sub() Container.Checked = Sender.Checked, EDP.None)
|
||||||
End Sub
|
End Sub
|
||||||
|
Private Sub MediaItem_BeforeOpenEditor(ByVal Sender As MediaItem, ByVal Container As IYouTubeMediaContainer)
|
||||||
|
MediaItem_BeforeOpenEditorImpl(Sender, Container, False)
|
||||||
|
End Sub
|
||||||
|
Private Sub MediaItem_BeforeOpenEditorFull(ByVal Sender As MediaItem, ByVal Container As IYouTubeMediaContainer)
|
||||||
|
MediaItem_BeforeOpenEditorImpl(Sender, Container, True)
|
||||||
|
End Sub
|
||||||
|
Private Sub MediaItem_BeforeOpenEditorImpl(ByVal Sender As MediaItem, ByVal Container As IYouTubeMediaContainer, ByVal Full As Boolean)
|
||||||
|
If MyContainer.HasElements Then
|
||||||
|
ControlInvokeFast(TP_CONTROLS, Sub()
|
||||||
|
With DirectCast(Container, YouTubeMediaContainerBase)
|
||||||
|
.File = $"{TXT_FILE.Text.CSFilePS}{ .File.File}"
|
||||||
|
.M3U8_PlaylistFiles = M3U8FilesFull
|
||||||
|
If Full Then
|
||||||
|
.OutputVideoExtension = CMB_FORMAT.Text.StringToLower
|
||||||
|
.OutputVideoFPS = AConvert(Of Double)(TXT_FPS.Text, YouTubeSettings.FpsFormatProvider.MyProviderDefault, -1)
|
||||||
|
.OutputAudioBitrate = AConvert(Of Integer)(TXT_AUDIO_BITRATE.Text, -1)
|
||||||
|
.OutputAudioCodec = CMB_AUDIO_CODEC.Text.StringToLower
|
||||||
|
.OutputSubtitlesFormat = CMB_SUBS_FORMAT.Text.StringToLower
|
||||||
|
.IsAudioSelected = OPT_AUDIO.Checked
|
||||||
|
End If
|
||||||
|
End With
|
||||||
|
End Sub, EDP.None)
|
||||||
|
End If
|
||||||
|
End Sub
|
||||||
#End Region
|
#End Region
|
||||||
#Region "OK, Cancel"
|
#Region "OK, Cancel"
|
||||||
Private Sub BTT_DOWN_Click(sender As Object, e As EventArgs) Handles BTT_DOWN.Click
|
Private Sub BTT_DOWN_Click(sender As Object, e As EventArgs) Handles BTT_DOWN.Click
|
||||||
Try
|
Try
|
||||||
|
If Not MyFieldsChecker.AllParamsOK Then Exit Sub
|
||||||
Dim f As SFile
|
Dim f As SFile
|
||||||
If MyContainer.HasElements Then
|
If MyContainer.HasElements Then
|
||||||
f = TXT_FILE.Text.CSFileP
|
f = TXT_FILE.Text.CSFileP
|
||||||
Else
|
Else
|
||||||
f = TXT_FILE.Text
|
f = TXT_FILE.Text
|
||||||
End If
|
End If
|
||||||
|
f = CleanFileName(f)
|
||||||
If f.IsEmptyString Then Throw New ArgumentNullException("File", "The output file cannot be null")
|
If f.IsEmptyString Then Throw New ArgumentNullException("File", "The output file cannot be null")
|
||||||
With MyContainer
|
With MyContainer
|
||||||
.OutputVideoExtension = CMB_FORMAT.Text.StringToLower
|
.OutputVideoExtension = CMB_FORMAT.Text.StringToLower
|
||||||
|
.OutputVideoFPS = AConvert(Of Double)(TXT_FPS.Text, YouTubeSettings.FpsFormatProvider.MyProviderDefault, -1)
|
||||||
|
.OutputAudioBitrate = AConvert(Of Integer)(TXT_AUDIO_BITRATE.Text, -1)
|
||||||
.OutputAudioCodec = CMB_AUDIO_CODEC.Text.StringToLower
|
.OutputAudioCodec = CMB_AUDIO_CODEC.Text.StringToLower
|
||||||
.OutputSubtitlesFormat = CMB_SUBS_FORMAT.Text.StringToLower
|
.OutputSubtitlesFormat = CMB_SUBS_FORMAT.Text.StringToLower
|
||||||
|
.M3U8_PlaylistFiles = M3U8FilesFull
|
||||||
|
|
||||||
If Not .HasElements Then
|
If Not .HasElements Then
|
||||||
Dim cntIndex% = -1
|
Dim cntIndex% = -1
|
||||||
@@ -302,6 +377,7 @@ Namespace API.YouTube.Controls
|
|||||||
Else
|
Else
|
||||||
.SelectedVideoIndex = -1
|
.SelectedVideoIndex = -1
|
||||||
.SelectedAudioIndex = cntIndex
|
.SelectedAudioIndex = cntIndex
|
||||||
|
.MediaType = UMTypes.Audio
|
||||||
End If
|
End If
|
||||||
.FileSetManually = True
|
.FileSetManually = True
|
||||||
.File = f
|
.File = f
|
||||||
@@ -312,6 +388,7 @@ Namespace API.YouTube.Controls
|
|||||||
Else
|
Else
|
||||||
If OPT_AUDIO.Checked Then
|
If OPT_AUDIO.Checked Then
|
||||||
.SetMaxResolution(-2)
|
.SetMaxResolution(-2)
|
||||||
|
.MediaType = UMTypes.Audio
|
||||||
Else
|
Else
|
||||||
.SetMaxResolution(NUM_RES.Value)
|
.SetMaxResolution(NUM_RES.Value)
|
||||||
End If
|
End If
|
||||||
@@ -322,6 +399,8 @@ Namespace API.YouTube.Controls
|
|||||||
|
|
||||||
If MyYouTubeSettings.OutputPathAutoChange Then MyYouTubeSettings.OutputPath.Value = f
|
If MyYouTubeSettings.OutputPathAutoChange Then MyYouTubeSettings.OutputPath.Value = f
|
||||||
If MyDownloaderSettings.OutputPathAutoAddPaths Then MyYouTubeSettings.DownloadLocations.Add(f, False)
|
If MyDownloaderSettings.OutputPathAutoAddPaths Then MyYouTubeSettings.DownloadLocations.Add(f, False)
|
||||||
|
If Not CMB_PLS.Text.IsEmptyString Then MyYouTubeSettings.PlaylistsLocations.Add(CMB_PLS.Text, False, True)
|
||||||
|
MyYouTubeSettings.LatestPlaylistFile.Value = CMB_PLS.Text
|
||||||
|
|
||||||
DialogResult = DialogResult.OK
|
DialogResult = DialogResult.OK
|
||||||
Close()
|
Close()
|
||||||
@@ -346,6 +425,19 @@ Namespace API.YouTube.Controls
|
|||||||
Private Sub OPT_VIDEO_AUDIO_CheckedChanged(sender As Object, e As EventArgs) Handles OPT_VIDEO.CheckedChanged, OPT_AUDIO.CheckedChanged
|
Private Sub OPT_VIDEO_AUDIO_CheckedChanged(sender As Object, e As EventArgs) Handles OPT_VIDEO.CheckedChanged, OPT_AUDIO.CheckedChanged
|
||||||
If Not Initialization Then
|
If Not Initialization Then
|
||||||
CMB_FORMAT.Enabled = OPT_VIDEO.Checked
|
CMB_FORMAT.Enabled = OPT_VIDEO.Checked
|
||||||
|
Dim upFormat As Action(Of String) = Sub(ByVal format As String)
|
||||||
|
If Not format.IsEmptyString Then
|
||||||
|
format = format.ToLower
|
||||||
|
Dim fIndex% = CMB_AUDIO_CODEC.Items.Cast(Of String).ListIndexOf(Function(f) f.ToLower = format)
|
||||||
|
If fIndex >= 0 Then CMB_AUDIO_CODEC.SelectedIndex = fIndex
|
||||||
|
End If
|
||||||
|
End Sub
|
||||||
|
If OPT_VIDEO.Checked Then
|
||||||
|
upFormat(MyYouTubeSettings.DefaultAudioCodec)
|
||||||
|
Else
|
||||||
|
upFormat(MyYouTubeSettings.DefaultAudioCodecMusic)
|
||||||
|
End If
|
||||||
|
|
||||||
If MyContainer.HasElements Then
|
If MyContainer.HasElements Then
|
||||||
NUM_RES.Enabled = OPT_VIDEO.Checked
|
NUM_RES.Enabled = OPT_VIDEO.Checked
|
||||||
Else
|
Else
|
||||||
@@ -440,6 +532,42 @@ Namespace API.YouTube.Controls
|
|||||||
End Sub
|
End Sub
|
||||||
#End Region
|
#End Region
|
||||||
#Region "Footer"
|
#Region "Footer"
|
||||||
|
Private Sub CMB_PLS_ActionOnButtonClick(ByVal Sender As Object, ByVal e As ActionButtonEventArgs) Handles CMB_PLS.ActionOnButtonClick
|
||||||
|
Select Case e.DefaultButton
|
||||||
|
Case ADB.List
|
||||||
|
Dim result As Boolean = False
|
||||||
|
Dim selectedFiles As IEnumerable(Of SFile) = MyYouTubeSettings.PlaylistsLocations.ChooseNewPlaylistArray(CMB_PLS, result)
|
||||||
|
If result And selectedFiles.ListExists Then M3U8Files.ListAddList(selectedFiles, LAP.NotContainsOnly, LAP.ClearBeforeAdd)
|
||||||
|
Case ADB.Save
|
||||||
|
With MyYouTubeSettings.PlaylistsLocations
|
||||||
|
If Not CMB_PLS.Text.IsEmptyString AndAlso .IndexOf(CMB_PLS.Text,, True) = -1 Then
|
||||||
|
.Add(CMB_PLS.Text, True, True)
|
||||||
|
.PopulateComboBox(CMB_PLS, CMB_PLS.Text, True)
|
||||||
|
End If
|
||||||
|
End With
|
||||||
|
Case ADB.Clear : M3U8Files.Clear()
|
||||||
|
End Select
|
||||||
|
End Sub
|
||||||
|
Private Sub BTT_PLS_BROWSE_MouseDown(sender As Object, e As MouseEventArgs) Handles BTT_PLS_BROWSE.MouseDown
|
||||||
|
Try
|
||||||
|
Dim f As SFile = Nothing
|
||||||
|
If Not CMB_PLS.Text.IsEmptyString Then
|
||||||
|
f = CMB_PLS.Text
|
||||||
|
ElseIf Not TXT_FILE.Text.IsEmptyString Then
|
||||||
|
f = TXT_FILE.Text
|
||||||
|
End If
|
||||||
|
f = SFile.SelectFiles(f, False, "Select a playlist...", "Playlists|*.m3u;*.m3u8|All files|*.*", EDP.ReturnValue).FirstOrDefault
|
||||||
|
If Not f.IsEmptyString Then
|
||||||
|
If e.Button = MouseButtons.Right Then
|
||||||
|
MyYouTubeSettings.PlaylistsLocations.Add(f.ToString, True, True)
|
||||||
|
MyYouTubeSettings.PlaylistsLocations.PopulateComboBox(CMB_PLS, f, True)
|
||||||
|
End If
|
||||||
|
CMB_PLS.Text = f
|
||||||
|
End If
|
||||||
|
Catch ex As Exception
|
||||||
|
ErrorsDescriber.Execute(EDP.SendToLog, ex, "[API.YouTube.Controls.VideoOptionsForm.SelectPlaylist]")
|
||||||
|
End Try
|
||||||
|
End Sub
|
||||||
Private _FilePathBeforeItemChange As SFile = Nothing
|
Private _FilePathBeforeItemChange As SFile = Nothing
|
||||||
Private Sub TXT_FILE_ActionSelectedItemBeforeChanged(ByVal Sender As Object, ByVal e As EventArgs, ByVal Item As ListViewItem) Handles TXT_FILE.ActionSelectedItemBeforeChanged
|
Private Sub TXT_FILE_ActionSelectedItemBeforeChanged(ByVal Sender As Object, ByVal e As EventArgs, ByVal Item As ListViewItem) Handles TXT_FILE.ActionSelectedItemBeforeChanged
|
||||||
If Not TXT_FILE.Text.IsEmptyString Then _FilePathBeforeItemChange = TXT_FILE.Text Else _FilePathBeforeItemChange = Nothing
|
If Not TXT_FILE.Text.IsEmptyString Then _FilePathBeforeItemChange = TXT_FILE.Text Else _FilePathBeforeItemChange = Nothing
|
||||||
@@ -461,6 +589,14 @@ Namespace API.YouTube.Controls
|
|||||||
_FilePathBeforeItemChange = Nothing
|
_FilePathBeforeItemChange = Nothing
|
||||||
End Try
|
End Try
|
||||||
End Sub
|
End Sub
|
||||||
|
Private Sub TXT_FILE_ActionOnButtonClick(ByVal Sender As Object, ByVal e As ActionButtonEventArgs) Handles TXT_FILE.ActionOnButtonClick
|
||||||
|
If e.DefaultButton = ADB.Save And Not TXT_FILE.Text.IsEmptyString Then
|
||||||
|
With MyYouTubeSettings.PlaylistsLocations
|
||||||
|
.Add(TXT_FILE.Text, True)
|
||||||
|
.PopulateComboBox(TXT_FILE, TXT_FILE.Text)
|
||||||
|
End With
|
||||||
|
End If
|
||||||
|
End Sub
|
||||||
Private Sub BTT_BROWSE_MouseDown(sender As Object, e As MouseEventArgs) Handles BTT_BROWSE.MouseDown
|
Private Sub BTT_BROWSE_MouseDown(sender As Object, e As MouseEventArgs) Handles BTT_BROWSE.MouseDown
|
||||||
Dim f As SFile
|
Dim f As SFile
|
||||||
#Disable Warning BC40000
|
#Disable Warning BC40000
|
||||||
@@ -469,12 +605,15 @@ Namespace API.YouTube.Controls
|
|||||||
f = SFile.SelectPath(f, "Select the destination of the video files", EDP.ReturnValue)
|
f = SFile.SelectPath(f, "Select the destination of the video files", EDP.ReturnValue)
|
||||||
Else
|
Else
|
||||||
f = TXT_FILE.Text
|
f = TXT_FILE.Text
|
||||||
Dim sPattern$ = $"Video|{AvailableVideoFormats.Select(Function(vf) $"*.{vf.ToLower}").ListToString(";")}" &
|
Dim ext$ = f.Extension
|
||||||
$"|Audio|{AvailableAudioFormats.Select(Function(af) $"*.{af.ToLower}").ListToString(";")}" &
|
Dim sPattern$ = "All Files|*.*|" &
|
||||||
"|All Files|*.*"
|
$"Video|{AvailableVideoFormats.Select(Function(vf) $"*.{vf.ToLower}").ListToString(";")}" &
|
||||||
f = SFile.SaveAs(f, "Select the destination of the video file",,, sPattern, EDP.ReturnValue)
|
$"|Audio|{AvailableAudioFormats.Select(Function(af) $"*.{af.ToLower}").ListToString(";")}"
|
||||||
|
f = SFile.SaveAs(f, "Select the destination of the video file",, ext, sPattern, EDP.ReturnValue)
|
||||||
|
f.Extension = ext
|
||||||
End If
|
End If
|
||||||
#Enable Warning
|
#Enable Warning
|
||||||
|
f = CleanFileName(f)
|
||||||
If Not f.IsEmptyString Then
|
If Not f.IsEmptyString Then
|
||||||
If e.Button = MouseButtons.Right Then
|
If e.Button = MouseButtons.Right Then
|
||||||
MyYouTubeSettings.DownloadLocations.Add(f, MyDownloaderSettings.OutputPathAskForName)
|
MyYouTubeSettings.DownloadLocations.Add(f, MyDownloaderSettings.OutputPathAskForName)
|
||||||
|
|||||||
@@ -17,10 +17,21 @@ Namespace API.YouTube
|
|||||||
Public Const DownloaderDataFolderYouTube As String = DownloadObjects.STDownloader.DownloaderDataFolder & "YouTube\"
|
Public Const DownloaderDataFolderYouTube As String = DownloadObjects.STDownloader.DownloaderDataFolder & "YouTube\"
|
||||||
Friend Const YouTubeDownloadPathDefault As String = "YouTubeDownloads\"
|
Friend Const YouTubeDownloadPathDefault As String = "YouTubeDownloads\"
|
||||||
Friend Const SimpleArraysFormNode As String = "SimpleFormatsChooserForm"
|
Friend Const SimpleArraysFormNode As String = "SimpleFormatsChooserForm"
|
||||||
|
Private Const YTDLP_DefaultName As String = "yt-dlp"
|
||||||
Public Property MyYouTubeSettings As Base.YouTubeSettings
|
Public Property MyYouTubeSettings As Base.YouTubeSettings
|
||||||
Public Property MyCache As CacheKeeper
|
Public Property MyCache As CacheKeeper
|
||||||
Friend ReadOnly Property MyCacheSettings As New CacheKeeper(DownloaderDataFolderYouTube) With {.DeleteCacheOnDispose = False, .DeleteRootOnDispose = False}
|
Friend ReadOnly Property MyCacheSettings As New CacheKeeper(DownloaderDataFolderYouTube) With {.DeleteCacheOnDispose = False, .DeleteRootOnDispose = False}
|
||||||
Public ReadOnly Property YouTubeCookieNetscapeFile As New SFile($"Settings\Responser_{YouTubeSite}_Cookies_Netscape.txt")
|
Public ReadOnly Property YouTubeCookieNetscapeFile As New SFile($"Settings\Responser_{YouTubeSite}_Cookies_Netscape.txt")
|
||||||
|
Friend ReadOnly Property YTDLP_NAME As String
|
||||||
|
Get
|
||||||
|
Dim n$ = MyYouTubeSettings.YTDLP.Value.Name
|
||||||
|
If Not n.IsEmptyString Then
|
||||||
|
Return If(n.ToLower = YTDLP_DefaultName, n, $"""{n}""")
|
||||||
|
Else
|
||||||
|
Return YTDLP_DefaultName
|
||||||
|
End If
|
||||||
|
End Get
|
||||||
|
End Property
|
||||||
Friend ReadOnly Property AvailableSubtitlesFormats As String()
|
Friend ReadOnly Property AvailableSubtitlesFormats As String()
|
||||||
Get
|
Get
|
||||||
Return {"ASS", "LRC", "SRT", "VTT"}
|
Return {"ASS", "LRC", "SRT", "VTT"}
|
||||||
@@ -45,6 +56,19 @@ Namespace API.YouTube
|
|||||||
Friend ReadOnly TitleHtmlConverter As Func(Of String, String) = Function(Input) Input.StringRemoveWinForbiddenSymbols().StringTrim()
|
Friend ReadOnly TitleHtmlConverter As Func(Of String, String) = Function(Input) Input.StringRemoveWinForbiddenSymbols().StringTrim()
|
||||||
Friend ReadOnly ProgressProvider As IMyProgressNumberProvider = MyProgressNumberProvider.Percentage
|
Friend ReadOnly ProgressProvider As IMyProgressNumberProvider = MyProgressNumberProvider.Percentage
|
||||||
Public ReadOnly TrueUrlRegEx As RParams = RParams.DM(Base.YouTubeFunctions.TrueUrlPattern, 0, EDP.ReturnValue)
|
Public ReadOnly TrueUrlRegEx As RParams = RParams.DM(Base.YouTubeFunctions.TrueUrlPattern, 0, EDP.ReturnValue)
|
||||||
|
Friend Function CleanFileName(ByVal f As SFile) As SFile
|
||||||
|
If Not f.IsEmptyString And Not f.Name.IsEmptyString Then
|
||||||
|
Dim ff As SFile = f
|
||||||
|
ff.Name = ff.Name.StringRemoveWinForbiddenSymbols.StringTrim
|
||||||
|
ff.Name = ff.Name.StringTrimEnd(".")
|
||||||
|
If Not ff.Name.IsEmptyString And Not MyYouTubeSettings.FileRemoveCharacters.IsEmptyString Then _
|
||||||
|
ff.Name = ff.Name.StringReplaceSymbols(MyYouTubeSettings.FileRemoveCharacters.Value.AsList.ListCast(Of String).ToArray, String.Empty, EDP.ReturnValue)
|
||||||
|
If ff.Name.IsEmptyString Then ff.Name = "file"
|
||||||
|
Return ff
|
||||||
|
Else
|
||||||
|
Return f
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
Private Class TimeToStringConverter : Implements ICustomProvider
|
Private Class TimeToStringConverter : Implements ICustomProvider
|
||||||
Private ReadOnly _Provider As New ADateTime("mm\:ss") With {.TimeParseMode = ADateTime.TimeModes.TimeSpan}
|
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 _ProviderWithHours As New ADateTime("h\:mm\:ss") With {.TimeParseMode = ADateTime.TimeModes.TimeSpan}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
Imports PersonalUtilities.Functions.XML
|
Imports PersonalUtilities.Functions.XML
|
||||||
Imports PersonalUtilities.Functions.XML.Base
|
Imports PersonalUtilities.Functions.XML.Base
|
||||||
Imports PersonalUtilities.Functions.XML.Attributes
|
Imports PersonalUtilities.Functions.XML.Attributes
|
||||||
|
Imports PersonalUtilities.Forms
|
||||||
Imports PersonalUtilities.Forms.Controls
|
Imports PersonalUtilities.Forms.Controls
|
||||||
Imports PersonalUtilities.Forms.Controls.Base
|
Imports PersonalUtilities.Forms.Controls.Base
|
||||||
Imports PersonalUtilities.Tools
|
Imports PersonalUtilities.Tools
|
||||||
@@ -103,7 +104,7 @@ Namespace DownloadObjects.STDownloader
|
|||||||
If UseUpdate Then .EndUpdate(True)
|
If UseUpdate Then .EndUpdate(True)
|
||||||
End With
|
End With
|
||||||
End Sub
|
End Sub
|
||||||
Public Sub PopulateComboBox(ByRef CMB As ComboBoxExtended, Optional ByVal Current As SFile = Nothing)
|
Public Sub PopulateComboBox(ByRef CMB As ComboBoxExtended, Optional ByVal Current As SFile = Nothing, Optional ByVal IsFile As Boolean = False)
|
||||||
Locations.Sort()
|
Locations.Sort()
|
||||||
With CMB
|
With CMB
|
||||||
.BeginUpdate()
|
.BeginUpdate()
|
||||||
@@ -124,7 +125,7 @@ Namespace DownloadObjects.STDownloader
|
|||||||
.EndUpdate()
|
.EndUpdate()
|
||||||
|
|
||||||
If Not Current.IsEmptyString And Locations.Count > 0 Then
|
If Not Current.IsEmptyString And Locations.Count > 0 Then
|
||||||
Dim i% = IndexOf(Current.PathWithSeparator)
|
Dim i% = IndexOf(If(IsFile, Current.ToString, Current.PathWithSeparator),, IsFile)
|
||||||
If i.ValueBetween(0, .Items.Count - 1) Then .SelectedIndex = i
|
If i.ValueBetween(0, .Items.Count - 1) Then .SelectedIndex = i
|
||||||
If Current.File.IsEmptyString Then CMB.Text = Current.PathWithSeparator Else CMB.Text = Current
|
If Current.File.IsEmptyString Then CMB.Text = Current.PathWithSeparator Else CMB.Text = Current
|
||||||
End If
|
End If
|
||||||
@@ -141,6 +142,44 @@ Namespace DownloadObjects.STDownloader
|
|||||||
End If
|
End If
|
||||||
Return f
|
Return f
|
||||||
End Function
|
End Function
|
||||||
|
Friend Function ChooseNewPlaylistArray(ByRef CMB As ComboBoxExtended, ByRef Result As Boolean) As IEnumerable(Of SFile)
|
||||||
|
Try
|
||||||
|
Dim initFiles As IEnumerable(Of SFile) = Nothing
|
||||||
|
Dim selectedFiles As IEnumerable(Of SFile) = Nothing
|
||||||
|
If Count > 0 Then initFiles = Me.Select(Function(l) l.Path.CSFile)
|
||||||
|
Dim addh As New EventHandler(Of SimpleListFormEventArgs)(Sub(ByVal s As Object, ByVal ee As SimpleListFormEventArgs)
|
||||||
|
Dim ff As List(Of SFile) = SFile.SelectFiles(,, "Select playlist files", "Playlist|*.m3u;*.m3u8|AllFiles|*.*", EDP.ReturnValue)
|
||||||
|
If ff.ListExists Then
|
||||||
|
ee.AddItem(ff.Cast(Of Object))
|
||||||
|
ee.Result = True
|
||||||
|
Else
|
||||||
|
ee.Result = False
|
||||||
|
End If
|
||||||
|
End Sub)
|
||||||
|
Using f As New SimpleListForm(Of SFile)(initFiles, API.YouTube.MyYouTubeSettings.DesignXml) With {
|
||||||
|
.DesignXMLNodeName = "M3U8SelectorForm",
|
||||||
|
.FormText = "Playlists",
|
||||||
|
.Buttons = {ActionButton.DefaultButtons.Add},
|
||||||
|
.Icon = ImageRenderer.GetIcon(My.Resources.StartPic_Green_16, EDP.ReturnValue),
|
||||||
|
.AddFunction = addh
|
||||||
|
}
|
||||||
|
If f.ShowDialog = DialogResult.OK Then Result = True : selectedFiles = ListAddList(Nothing, f.DataResult, LAP.NotContainsOnly)
|
||||||
|
End Using
|
||||||
|
If selectedFiles.ListExists Then
|
||||||
|
Dim added As Boolean = False
|
||||||
|
selectedFiles.ListForEach(Sub(ByVal plsFile As SFile, ByVal ii As Integer)
|
||||||
|
If IndexOf(plsFile.ToString,, True) = -1 Then Add(plsFile.ToString, True, True) : added = True
|
||||||
|
End Sub)
|
||||||
|
If added Then PopulateComboBox(CMB, selectedFiles(0).ToString, True)
|
||||||
|
CMB.Text = selectedFiles(0)
|
||||||
|
Return selectedFiles
|
||||||
|
Else
|
||||||
|
Return Nothing
|
||||||
|
End If
|
||||||
|
Catch ex As Exception
|
||||||
|
Return ErrorsDescriber.Execute(EDP.LogMessageValue, ex, "Select playlist array")
|
||||||
|
End Try
|
||||||
|
End Function
|
||||||
Private Sub Update()
|
Private Sub Update()
|
||||||
If Locations.Count > 0 Then
|
If Locations.Count > 0 Then
|
||||||
Using x As New XmlFile With {.AllowSameNames = True}
|
Using x As New XmlFile With {.AllowSameNames = True}
|
||||||
@@ -158,9 +197,9 @@ Namespace DownloadObjects.STDownloader
|
|||||||
Public Overloads Sub Add(ByVal Item As DownloadLocation) Implements ICollection(Of DownloadLocation).Add
|
Public Overloads Sub Add(ByVal Item As DownloadLocation) Implements ICollection(Of DownloadLocation).Add
|
||||||
Add(Item, True)
|
Add(Item, True)
|
||||||
End Sub
|
End Sub
|
||||||
Public Overloads Sub Add(ByVal Item As DownloadLocation, ByVal AskForName As Boolean)
|
Public Overloads Sub Add(ByVal Item As DownloadLocation, ByVal AskForName As Boolean, Optional ByVal IsFile As Boolean = False)
|
||||||
If Not Item.Path.IsEmptyString Then
|
If Not Item.Path.IsEmptyString Then
|
||||||
Dim i% = IndexOf(Item)
|
Dim i% = IndexOf(Item,, IsFile)
|
||||||
Dim processUpdate As Boolean = True
|
Dim processUpdate As Boolean = True
|
||||||
If i >= 0 Then
|
If i >= 0 Then
|
||||||
If Locations(i).Model = Item.Model Then
|
If Locations(i).Model = Item.Model Then
|
||||||
@@ -183,8 +222,12 @@ Namespace DownloadObjects.STDownloader
|
|||||||
Public Function Contains(ByVal Item As DownloadLocation) As Boolean Implements ICollection(Of DownloadLocation).Contains
|
Public Function Contains(ByVal Item As DownloadLocation) As Boolean Implements ICollection(Of DownloadLocation).Contains
|
||||||
Return Not Item.Path.IsEmptyString AndAlso Locations.Contains(Item)
|
Return Not Item.Path.IsEmptyString AndAlso Locations.Contains(Item)
|
||||||
End Function
|
End Function
|
||||||
Public Function IndexOf(ByVal Item As DownloadLocation, Optional ByVal IgnoreModel As Boolean = False) As Integer
|
Public Function IndexOf(ByVal Item As DownloadLocation, Optional ByVal IgnoreModel As Boolean = False, Optional ByVal IsFile As Boolean = False) As Integer
|
||||||
Return Locations.FindIndex(Function(d) d.Path = Item.Path And (d.Model = Item.Model Or IgnoreModel))
|
If Not IsFile Then
|
||||||
|
Return Locations.FindIndex(Function(d) d.Path = Item.Path And (d.Model = Item.Model Or IgnoreModel))
|
||||||
|
Else
|
||||||
|
Return Locations.FindIndex(Function(d) d.Path = Item.Path)
|
||||||
|
End If
|
||||||
End Function
|
End Function
|
||||||
Public Function Remove(ByVal Item As DownloadLocation) As Boolean Implements ICollection(Of DownloadLocation).Remove
|
Public Function Remove(ByVal Item As DownloadLocation) As Boolean Implements ICollection(Of DownloadLocation).Remove
|
||||||
If Locations.Remove(Item) Then
|
If Locations.Remove(Item) Then
|
||||||
|
|||||||
59
SCrawler.YouTube/Downloader/MediaItem.Designer.vb
generated
@@ -30,11 +30,16 @@ Namespace DownloadObjects.STDownloader
|
|||||||
Me.BTT_DOWN = New System.Windows.Forms.ToolStripMenuItem()
|
Me.BTT_DOWN = New System.Windows.Forms.ToolStripMenuItem()
|
||||||
Me.SEP_DOWN = New System.Windows.Forms.ToolStripSeparator()
|
Me.SEP_DOWN = New System.Windows.Forms.ToolStripSeparator()
|
||||||
Me.BTT_OPEN_FOLDER = New System.Windows.Forms.ToolStripMenuItem()
|
Me.BTT_OPEN_FOLDER = New System.Windows.Forms.ToolStripMenuItem()
|
||||||
|
Me.BTT_OPEN_FILE = New System.Windows.Forms.ToolStripMenuItem()
|
||||||
Me.SEP_FOLDER = New System.Windows.Forms.ToolStripSeparator()
|
Me.SEP_FOLDER = New System.Windows.Forms.ToolStripSeparator()
|
||||||
|
Me.BTT_PLS_ITEM_EDIT = New System.Windows.Forms.ToolStripMenuItem()
|
||||||
|
Me.BTT_PLS_ITEM_EDIT_FULL = New System.Windows.Forms.ToolStripMenuItem()
|
||||||
|
Me.SEP_PLS_ITEM_EDIT = New System.Windows.Forms.ToolStripSeparator()
|
||||||
Me.BTT_COPY_LINK = New System.Windows.Forms.ToolStripMenuItem()
|
Me.BTT_COPY_LINK = New System.Windows.Forms.ToolStripMenuItem()
|
||||||
Me.BTT_OPEN_IN_BROWSER = New System.Windows.Forms.ToolStripMenuItem()
|
Me.BTT_OPEN_IN_BROWSER = New System.Windows.Forms.ToolStripMenuItem()
|
||||||
Me.SEP_DOWN_AGAIN = New System.Windows.Forms.ToolStripSeparator()
|
Me.SEP_DOWN_AGAIN = New System.Windows.Forms.ToolStripSeparator()
|
||||||
Me.BTT_DOWN_AGAIN = New System.Windows.Forms.ToolStripMenuItem()
|
Me.BTT_DOWN_AGAIN = New System.Windows.Forms.ToolStripMenuItem()
|
||||||
|
Me.BTT_VIEW_SETTINGS = New System.Windows.Forms.ToolStripMenuItem()
|
||||||
Me.SEP_DEL = New System.Windows.Forms.ToolStripSeparator()
|
Me.SEP_DEL = New System.Windows.Forms.ToolStripSeparator()
|
||||||
Me.BTT_REMOVE_FROM_LIST = New System.Windows.Forms.ToolStripMenuItem()
|
Me.BTT_REMOVE_FROM_LIST = New System.Windows.Forms.ToolStripMenuItem()
|
||||||
Me.BTT_DELETE_FILE = New System.Windows.Forms.ToolStripMenuItem()
|
Me.BTT_DELETE_FILE = New System.Windows.Forms.ToolStripMenuItem()
|
||||||
@@ -42,7 +47,6 @@ Namespace DownloadObjects.STDownloader
|
|||||||
Me.TP_CHECKED_TITLE = New System.Windows.Forms.TableLayoutPanel()
|
Me.TP_CHECKED_TITLE = New System.Windows.Forms.TableLayoutPanel()
|
||||||
Me.LBL_TITLE = New System.Windows.Forms.Label()
|
Me.LBL_TITLE = New System.Windows.Forms.Label()
|
||||||
Me.CH_CHECKED = New System.Windows.Forms.CheckBox()
|
Me.CH_CHECKED = New System.Windows.Forms.CheckBox()
|
||||||
Me.BTT_VIEW_SETTINGS = New System.Windows.Forms.ToolStripMenuItem()
|
|
||||||
TP_MAIN = New System.Windows.Forms.TableLayoutPanel()
|
TP_MAIN = New System.Windows.Forms.TableLayoutPanel()
|
||||||
TP_MAIN.SuspendLayout()
|
TP_MAIN.SuspendLayout()
|
||||||
CType(Me.ICON_VIDEO, System.ComponentModel.ISupportInitialize).BeginInit()
|
CType(Me.ICON_VIDEO, System.ComponentModel.ISupportInitialize).BeginInit()
|
||||||
@@ -83,10 +87,10 @@ Namespace DownloadObjects.STDownloader
|
|||||||
'
|
'
|
||||||
'CONTEXT_MAIN
|
'CONTEXT_MAIN
|
||||||
'
|
'
|
||||||
Me.CONTEXT_MAIN.Items.AddRange(New System.Windows.Forms.ToolStripItem() {Me.BTT_DOWN, Me.SEP_DOWN, Me.BTT_OPEN_FOLDER, Me.SEP_FOLDER, Me.BTT_COPY_LINK, Me.BTT_OPEN_IN_BROWSER, Me.SEP_DOWN_AGAIN, Me.BTT_DOWN_AGAIN, Me.BTT_VIEW_SETTINGS, Me.SEP_DEL, Me.BTT_REMOVE_FROM_LIST, Me.BTT_DELETE_FILE})
|
Me.CONTEXT_MAIN.Items.AddRange(New System.Windows.Forms.ToolStripItem() {Me.BTT_DOWN, Me.SEP_DOWN, Me.BTT_OPEN_FOLDER, Me.BTT_OPEN_FILE, Me.SEP_FOLDER, Me.BTT_PLS_ITEM_EDIT, Me.BTT_PLS_ITEM_EDIT_FULL, Me.SEP_PLS_ITEM_EDIT, Me.BTT_COPY_LINK, Me.BTT_OPEN_IN_BROWSER, Me.SEP_DOWN_AGAIN, Me.BTT_DOWN_AGAIN, Me.BTT_VIEW_SETTINGS, Me.SEP_DEL, Me.BTT_REMOVE_FROM_LIST, Me.BTT_DELETE_FILE})
|
||||||
Me.CONTEXT_MAIN.Name = "CONTEXT_MAIN"
|
Me.CONTEXT_MAIN.Name = "CONTEXT_MAIN"
|
||||||
Me.CONTEXT_MAIN.ShowItemToolTips = False
|
Me.CONTEXT_MAIN.ShowItemToolTips = False
|
||||||
Me.CONTEXT_MAIN.Size = New System.Drawing.Size(185, 226)
|
Me.CONTEXT_MAIN.Size = New System.Drawing.Size(185, 298)
|
||||||
'
|
'
|
||||||
'BTT_DOWN
|
'BTT_DOWN
|
||||||
'
|
'
|
||||||
@@ -107,11 +111,42 @@ Namespace DownloadObjects.STDownloader
|
|||||||
Me.BTT_OPEN_FOLDER.Size = New System.Drawing.Size(184, 22)
|
Me.BTT_OPEN_FOLDER.Size = New System.Drawing.Size(184, 22)
|
||||||
Me.BTT_OPEN_FOLDER.Text = "Open folder"
|
Me.BTT_OPEN_FOLDER.Text = "Open folder"
|
||||||
'
|
'
|
||||||
|
'BTT_OPEN_FILE
|
||||||
|
'
|
||||||
|
Me.BTT_OPEN_FILE.Image = CType(resources.GetObject("BTT_OPEN_FILE.Image"), System.Drawing.Image)
|
||||||
|
Me.BTT_OPEN_FILE.Name = "BTT_OPEN_FILE"
|
||||||
|
Me.BTT_OPEN_FILE.Size = New System.Drawing.Size(184, 22)
|
||||||
|
Me.BTT_OPEN_FILE.Text = "Open file"
|
||||||
|
'
|
||||||
'SEP_FOLDER
|
'SEP_FOLDER
|
||||||
'
|
'
|
||||||
Me.SEP_FOLDER.Name = "SEP_FOLDER"
|
Me.SEP_FOLDER.Name = "SEP_FOLDER"
|
||||||
Me.SEP_FOLDER.Size = New System.Drawing.Size(181, 6)
|
Me.SEP_FOLDER.Size = New System.Drawing.Size(181, 6)
|
||||||
'
|
'
|
||||||
|
'BTT_PLS_ITEM_EDIT
|
||||||
|
'
|
||||||
|
Me.BTT_PLS_ITEM_EDIT.Image = CType(resources.GetObject("BTT_PLS_ITEM_EDIT.Image"), System.Drawing.Image)
|
||||||
|
Me.BTT_PLS_ITEM_EDIT.Name = "BTT_PLS_ITEM_EDIT"
|
||||||
|
Me.BTT_PLS_ITEM_EDIT.Size = New System.Drawing.Size(184, 22)
|
||||||
|
Me.BTT_PLS_ITEM_EDIT.Text = "Edit"
|
||||||
|
Me.BTT_PLS_ITEM_EDIT.Visible = False
|
||||||
|
'
|
||||||
|
'BTT_PLS_ITEM_EDIT_FULL
|
||||||
|
'
|
||||||
|
Me.BTT_PLS_ITEM_EDIT_FULL.AutoToolTip = True
|
||||||
|
Me.BTT_PLS_ITEM_EDIT_FULL.Image = CType(resources.GetObject("BTT_PLS_ITEM_EDIT_FULL.Image"), System.Drawing.Image)
|
||||||
|
Me.BTT_PLS_ITEM_EDIT_FULL.Name = "BTT_PLS_ITEM_EDIT_FULL"
|
||||||
|
Me.BTT_PLS_ITEM_EDIT_FULL.Size = New System.Drawing.Size(184, 22)
|
||||||
|
Me.BTT_PLS_ITEM_EDIT_FULL.Text = "Edit (inherit settings)"
|
||||||
|
Me.BTT_PLS_ITEM_EDIT_FULL.ToolTipText = "Inherit settings selected in the form"
|
||||||
|
Me.BTT_PLS_ITEM_EDIT_FULL.Visible = False
|
||||||
|
'
|
||||||
|
'SEP_PLS_ITEM_EDIT
|
||||||
|
'
|
||||||
|
Me.SEP_PLS_ITEM_EDIT.Name = "SEP_PLS_ITEM_EDIT"
|
||||||
|
Me.SEP_PLS_ITEM_EDIT.Size = New System.Drawing.Size(181, 6)
|
||||||
|
Me.SEP_PLS_ITEM_EDIT.Visible = False
|
||||||
|
'
|
||||||
'BTT_COPY_LINK
|
'BTT_COPY_LINK
|
||||||
'
|
'
|
||||||
Me.BTT_COPY_LINK.Image = Global.SCrawler.My.Resources.Resources.LinkPic_32
|
Me.BTT_COPY_LINK.Image = Global.SCrawler.My.Resources.Resources.LinkPic_32
|
||||||
@@ -138,6 +173,13 @@ Namespace DownloadObjects.STDownloader
|
|||||||
Me.BTT_DOWN_AGAIN.Size = New System.Drawing.Size(184, 22)
|
Me.BTT_DOWN_AGAIN.Size = New System.Drawing.Size(184, 22)
|
||||||
Me.BTT_DOWN_AGAIN.Text = "Download again"
|
Me.BTT_DOWN_AGAIN.Text = "Download again"
|
||||||
'
|
'
|
||||||
|
'BTT_VIEW_SETTINGS
|
||||||
|
'
|
||||||
|
Me.BTT_VIEW_SETTINGS.Image = Global.SCrawler.My.Resources.Resources.SettingsPic_16
|
||||||
|
Me.BTT_VIEW_SETTINGS.Name = "BTT_VIEW_SETTINGS"
|
||||||
|
Me.BTT_VIEW_SETTINGS.Size = New System.Drawing.Size(184, 22)
|
||||||
|
Me.BTT_VIEW_SETTINGS.Text = "View settings"
|
||||||
|
'
|
||||||
'SEP_DEL
|
'SEP_DEL
|
||||||
'
|
'
|
||||||
Me.SEP_DEL.Name = "SEP_DEL"
|
Me.SEP_DEL.Name = "SEP_DEL"
|
||||||
@@ -212,13 +254,6 @@ Namespace DownloadObjects.STDownloader
|
|||||||
Me.CH_CHECKED.TabIndex = 0
|
Me.CH_CHECKED.TabIndex = 0
|
||||||
Me.CH_CHECKED.UseVisualStyleBackColor = True
|
Me.CH_CHECKED.UseVisualStyleBackColor = True
|
||||||
'
|
'
|
||||||
'BTT_VIEW_SETTINGS
|
|
||||||
'
|
|
||||||
Me.BTT_VIEW_SETTINGS.Image = Global.SCrawler.My.Resources.Resources.SettingsPic_16
|
|
||||||
Me.BTT_VIEW_SETTINGS.Name = "BTT_VIEW_SETTINGS"
|
|
||||||
Me.BTT_VIEW_SETTINGS.Size = New System.Drawing.Size(184, 22)
|
|
||||||
Me.BTT_VIEW_SETTINGS.Text = "View settings"
|
|
||||||
'
|
|
||||||
'MediaItem
|
'MediaItem
|
||||||
'
|
'
|
||||||
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
|
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
|
||||||
@@ -253,5 +288,9 @@ Namespace DownloadObjects.STDownloader
|
|||||||
Private WithEvents SEP_DOWN_AGAIN As ToolStripSeparator
|
Private WithEvents SEP_DOWN_AGAIN As ToolStripSeparator
|
||||||
Private WithEvents SEP_DEL As ToolStripSeparator
|
Private WithEvents SEP_DEL As ToolStripSeparator
|
||||||
Private WithEvents BTT_VIEW_SETTINGS As ToolStripMenuItem
|
Private WithEvents BTT_VIEW_SETTINGS As ToolStripMenuItem
|
||||||
|
Private WithEvents BTT_OPEN_FILE As ToolStripMenuItem
|
||||||
|
Private WithEvents BTT_PLS_ITEM_EDIT As ToolStripMenuItem
|
||||||
|
Private WithEvents SEP_PLS_ITEM_EDIT As ToolStripSeparator
|
||||||
|
Private WithEvents BTT_PLS_ITEM_EDIT_FULL As ToolStripMenuItem
|
||||||
End Class
|
End Class
|
||||||
End Namespace
|
End Namespace
|
||||||
@@ -138,6 +138,138 @@
|
|||||||
PNWfb/GWbyQQ2Z/pgjaJ9XsI91sIjr1H+fgUVeYh/KVrFs8Itp6O1B2RR+fAdhzupEHDXnw3U3GVpuAq
|
PNWfb/GWbyQQ2Z/pgjaJ9XsI91sIjr1H+fgUVeYh/KVrFs8Itp6O1B2RR+fAdhzupEHDXnw3U3GVpuAq
|
||||||
+w/y3l2wnHCNxMrhGIGMcp2WpkyYWeqcC30Co5OYu0gvwZIRtc4b606cpoUYtF9y3BgvNkFSmhcSO25a
|
+w/y3l2wnHCNxMrhGIGMcp2WpkyYWeqcC30Co5OYu0gvwZIRtc4b606cpoUYtF9y3BgvNkFSmhcSO25a
|
||||||
TGCkk99shOQwl9bXawAAAABJRU5ErkJggg==
|
TGCkk99shOQwl9bXawAAAABJRU5ErkJggg==
|
||||||
|
</value>
|
||||||
|
</data>
|
||||||
|
<data name="BTT_OPEN_FILE.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>
|
||||||
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
||||||
|
wwAADsMBx2+oZAAAAk9JREFUOE+Nk0tIVGEUgKcWZlZWEK1aZEmZUlmpEQUtrF1Q0WNfUUgtCqKZLMXQ
|
||||||
|
UPJRmuY4TYYKmbPJcVSCAkuFQislzCx7rSo3Oc5T7zzu17njdZosxMXH/bn/+b97zn/ONfBjAJoPQctR
|
||||||
|
sB2bH1rsw8OEvnVj0BbUbdNJ19HW2+fmbgZO4wIR2MRm3irIQcsO2RA0Qe3mf9EOajGRuEycuXEiaDky
|
||||||
|
vdlXTfjrc0Kfu1BfVkJjtqR68G80cfRDIrgSL4Km/fCinFC/GU9JEs7CTUy25hAafYIy8hhl2IEy2Iwy
|
||||||
|
0IzacRaq1kNNClizdEHjPhiw4nFcRClKFLukWZvG1PWVuI0GPMVr8LYb8XZcRvX/gqEH0HZSskmPEbyu
|
||||||
|
w9N+CaV41bS9/Qx86oQPdhh1EOwuwesw4bGdY7LvPoHhNgIlq5nIS5gl0DLoyIF3LfgbDjBhFap348uL
|
||||||
|
Q5EDyrVEgvZT+Htv4ytYiit/WYzAfh6lIgl1bBB/xQYmi1YQqkomVJmMapbLswhN2ajfX+ErW8dU+Vpc
|
||||||
|
eUtiBRcIPDpBoKcU99V4uJf1p2Va+2q3QE8hSvcN3PkJUL9r5g6kXW8sUoKRqSE7vlupBCUTrJl6u3SB
|
||||||
|
PNWfb/GWbyQQ2Z/pgjaJ9XsI91sIjr1H+fgUVeYh/KVrFs8Itp6O1B2RR+fAdhzupEHDXnw3U3GVpuAq
|
||||||
|
+w/y3l2wnHCNxMrhGIGMcp2WpkyYWeqcC30Co5OYu0gvwZIRtc4b606cpoUYtF9y3BgvNkFSmhcSO25a
|
||||||
|
TGCkk99shOQwl9bXawAAAABJRU5ErkJggg==
|
||||||
|
</value>
|
||||||
|
</data>
|
||||||
|
<data name="BTT_PLS_ITEM_EDIT.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>
|
||||||
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAACH
|
||||||
|
DwAAjA8AAP1SAACBQAAAfXkAAOmLAAA85QAAGcxzPIV3AAAKOWlDQ1BQaG90b3Nob3AgSUNDIHByb2Zp
|
||||||
|
bGUAAEjHnZZ3VFTXFofPvXd6oc0w0hl6ky4wgPQuIB0EURhmBhjKAMMMTWyIqEBEEREBRZCggAGjoUis
|
||||||
|
iGIhKKhgD0gQUGIwiqioZEbWSnx5ee/l5ffHvd/aZ+9z99l7n7UuACRPHy4vBZYCIJkn4Ad6ONNXhUfQ
|
||||||
|
sf0ABniAAaYAMFnpqb5B7sFAJC83F3q6yAn8i94MAUj8vmXo6U+ng/9P0qxUvgAAyF/E5mxOOkvE+SJO
|
||||||
|
yhSkiu0zIqbGJIoZRomZL0pQxHJijlvkpZ99FtlRzOxkHlvE4pxT2clsMfeIeHuGkCNixEfEBRlcTqaI
|
||||||
|
b4tYM0mYzBXxW3FsMoeZDgCKJLYLOKx4EZuImMQPDnQR8XIAcKS4LzjmCxZwsgTiQ7mkpGbzuXHxArou
|
||||||
|
S49uam3NoHtyMpM4AoGhP5OVyOSz6S4pyalMXjYAi2f+LBlxbemiIluaWltaGpoZmX5RqP+6+Dcl7u0i
|
||||||
|
vQr43DOI1veH7a/8UuoAYMyKarPrD1vMfgA6tgIgd/8Pm+YhACRFfWu/8cV5aOJ5iRcIUm2MjTMzM424
|
||||||
|
HJaRuKC/6386/A198T0j8Xa/l4fuyollCpMEdHHdWClJKUI+PT2VyeLQDf88xP848K/zWBrIieXwOTxR
|
||||||
|
RKhoyri8OFG7eWyugJvCo3N5/6mJ/zDsT1qca5Eo9Z8ANcoISN2gAuTnPoCiEAESeVDc9d/75oMPBeKb
|
||||||
|
F6Y6sTj3nwX9+65wifiRzo37HOcSGExnCfkZi2viawnQgAAkARXIAxWgAXSBITADVsAWOAI3sAL4gWAQ
|
||||||
|
DtYCFogHyYAPMkEu2AwKQBHYBfaCSlAD6kEjaAEnQAc4DS6Ay+A6uAnugAdgBIyD52AGvAHzEARhITJE
|
||||||
|
geQhVUgLMoDMIAZkD7lBPlAgFA5FQ3EQDxJCudAWqAgqhSqhWqgR+hY6BV2ArkID0D1oFJqCfoXewwhM
|
||||||
|
gqmwMqwNG8MM2An2hoPhNXAcnAbnwPnwTrgCroOPwe3wBfg6fAcegZ/DswhAiAgNUUMMEQbigvghEUgs
|
||||||
|
wkc2IIVIOVKHtCBdSC9yCxlBppF3KAyKgqKjDFG2KE9UCIqFSkNtQBWjKlFHUe2oHtQt1ChqBvUJTUYr
|
||||||
|
oQ3QNmgv9Cp0HDoTXYAuRzeg29CX0HfQ4+g3GAyGhtHBWGE8MeGYBMw6TDHmAKYVcx4zgBnDzGKxWHms
|
||||||
|
AdYO64dlYgXYAux+7DHsOewgdhz7FkfEqeLMcO64CBwPl4crxzXhzuIGcRO4ebwUXgtvg/fDs/HZ+BJ8
|
||||||
|
Pb4LfwM/jp8nSBN0CHaEYEICYTOhgtBCuER4SHhFJBLVidbEACKXuIlYQTxOvEIcJb4jyZD0SS6kSJKQ
|
||||||
|
tJN0hHSedI/0ikwma5MdyRFkAXknuZF8kfyY/FaCImEk4SXBltgoUSXRLjEo8UISL6kl6SS5VjJHslzy
|
||||||
|
pOQNyWkpvJS2lIsUU2qDVJXUKalhqVlpirSptJ90snSxdJP0VelJGayMtoybDFsmX+awzEWZMQpC0aC4
|
||||||
|
UFiULZR6yiXKOBVD1aF6UROoRdRvqP3UGVkZ2WWyobJZslWyZ2RHaAhNm+ZFS6KV0E7QhmjvlygvcVrC
|
||||||
|
WbJjScuSwSVzcopyjnIcuUK5Vrk7cu/l6fJu8onyu+U75B8poBT0FQIUMhUOKlxSmFakKtoqshQLFU8o
|
||||||
|
3leClfSVApXWKR1W6lOaVVZR9lBOVd6vfFF5WoWm4qiSoFKmclZlSpWiaq/KVS1TPaf6jC5Ld6In0Svo
|
||||||
|
PfQZNSU1TzWhWq1av9q8uo56iHqeeqv6Iw2CBkMjVqNMo1tjRlNV01czV7NZ874WXouhFa+1T6tXa05b
|
||||||
|
RztMe5t2h/akjpyOl06OTrPOQ12yroNumm6d7m09jB5DL1HvgN5NfVjfQj9ev0r/hgFsYGnANThgMLAU
|
||||||
|
vdR6KW9p3dJhQ5Khk2GGYbPhqBHNyMcoz6jD6IWxpnGE8W7jXuNPJhYmSSb1Jg9MZUxXmOaZdpn+aqZv
|
||||||
|
xjKrMrttTjZ3N99o3mn+cpnBMs6yg8vuWlAsfC22WXRbfLS0suRbtlhOWWlaRVtVWw0zqAx/RjHjijXa
|
||||||
|
2tl6o/Vp63c2ljYCmxM2v9ga2ibaNtlOLtdZzllev3zMTt2OaVdrN2JPt4+2P2Q/4qDmwHSoc3jiqOHI
|
||||||
|
dmxwnHDSc0pwOub0wtnEme/c5jznYuOy3uW8K+Lq4Vro2u8m4xbiVun22F3dPc692X3Gw8Jjncd5T7Sn
|
||||||
|
t+duz2EvZS+WV6PXzAqrFetX9HiTvIO8K72f+Oj78H26fGHfFb57fB+u1FrJW9nhB/y8/Pb4PfLX8U/z
|
||||||
|
/z4AE+AfUBXwNNA0MDewN4gSFBXUFPQm2Dm4JPhBiG6IMKQ7VDI0MrQxdC7MNaw0bGSV8ar1q66HK4Rz
|
||||||
|
wzsjsBGhEQ0Rs6vdVu9dPR5pEVkQObRGZ03WmqtrFdYmrT0TJRnFjDoZjY4Oi26K/sD0Y9YxZ2O8Yqpj
|
||||||
|
ZlgurH2s52xHdhl7imPHKeVMxNrFlsZOxtnF7YmbineIL4+f5rpwK7kvEzwTahLmEv0SjyQuJIUltSbj
|
||||||
|
kqOTT/FkeIm8nhSVlKyUgVSD1ILUkTSbtL1pM3xvfkM6lL4mvVNAFf1M9Ql1hVuFoxn2GVUZbzNDM09m
|
||||||
|
SWfxsvqy9bN3ZE/kuOd8vQ61jrWuO1ctd3Pu6Hqn9bUboA0xG7o3amzM3zi+yWPT0c2EzYmbf8gzySvN
|
||||||
|
e70lbEtXvnL+pvyxrR5bmwskCvgFw9tst9VsR23nbu/fYb5j/45PhezCa0UmReVFH4pZxde+Mv2q4quF
|
||||||
|
nbE7+0ssSw7uwuzi7Rra7bD7aKl0aU7p2B7fPe1l9LLCstd7o/ZeLV9WXrOPsE+4b6TCp6Jzv+b+Xfs/
|
||||||
|
VMZX3qlyrmqtVqreUT13gH1g8KDjwZYa5ZqimveHuIfu1nrUttdp15UfxhzOOPy0PrS+92vG140NCg1F
|
||||||
|
DR+P8I6MHA082tNo1djYpNRU0gw3C5unjkUeu/mN6zedLYYtta201qLj4Ljw+LNvo78dOuF9ovsk42TL
|
||||||
|
d1rfVbdR2grbofbs9pmO+I6RzvDOgVMrTnV32Xa1fW/0/ZHTaqerzsieKTlLOJt/duFczrnZ86nnpy/E
|
||||||
|
XRjrjup+cHHVxds9AT39l7wvXbnsfvlir1PvuSt2V05ftbl66hrjWsd1y+vtfRZ9bT9Y/NDWb9nffsPq
|
||||||
|
RudN65tdA8sHzg46DF645Xrr8m2v29fvrLwzMBQydHc4cnjkLvvu5L2key/vZ9yff7DpIfph4SOpR+WP
|
||||||
|
lR7X/aj3Y+uI5ciZUdfRvidBTx6Mscae/5T+04fx/Kfkp+UTqhONk2aTp6fcp24+W/1s/Hnq8/npgp+l
|
||||||
|
f65+ofviu18cf+mbWTUz/pL/cuHX4lfyr468Xva6e9Z/9vGb5Dfzc4Vv5d8efcd41/s+7P3EfOYH7IeK
|
||||||
|
j3ofuz55f3q4kLyw8Bv3hPP74uYdwgAAAAlwSFlzAAALEwAACxMBAJqcGAAAAahJREFUOE9j+P//P8l4
|
||||||
|
vaOjPYyNIYkPO1lZsa1wdNy42sHh3Hxb22KQGFaF2LC4qjjroUP7n97s6vx/Ny/3/ypn54+LbGwisSpG
|
||||||
|
x+aaouwZren/u5f2/3/18tX/qzNn/l/i4XGSgYFBFasGZKwjzcJ6YVnU152blvw3LHH53zCl/ufatWu+
|
||||||
|
T+1vDALJY9UEwxrijExHZgd+/Xy1Hcg98BNkCMglMM0gjKEJhuX5GVh2TvD+/O5c0///P9b///qo819P
|
||||||
|
lgmKZhBG0QTDMjwMzJs7XT+9OVHz///XFf+/PWj7j00zCKNwQFiah4FtXbPjp8d78////7bo/4/79Tg1
|
||||||
|
gzAKR1mUg3lOocXbe9uz/v9/M/H/1zuVeDWDMJwhJcDBvK4p4tb1DQn//r/u+f/zRh5BzSAMZyyrdVh9
|
||||||
|
c33B9//32159vZr2hxjNIAwm1GUE3e+ur/n9/+Ls/592Nf9fUun3khjNIMzAysTAv6g6+OT/E33/j09N
|
||||||
|
+zWpMuImsZpBmMHIQK9x19T8/03x1ufE+TkqsCnChxmUlFWuyEpJtAHTtT42BfjxfwYAtlm0ShMkSB4A
|
||||||
|
AAAASUVORK5CYII=
|
||||||
|
</value>
|
||||||
|
</data>
|
||||||
|
<data name="BTT_PLS_ITEM_EDIT_FULL.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>
|
||||||
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAACH
|
||||||
|
DwAAjA8AAP1SAACBQAAAfXkAAOmLAAA85QAAGcxzPIV3AAAKOWlDQ1BQaG90b3Nob3AgSUNDIHByb2Zp
|
||||||
|
bGUAAEjHnZZ3VFTXFofPvXd6oc0w0hl6ky4wgPQuIB0EURhmBhjKAMMMTWyIqEBEEREBRZCggAGjoUis
|
||||||
|
iGIhKKhgD0gQUGIwiqioZEbWSnx5ee/l5ffHvd/aZ+9z99l7n7UuACRPHy4vBZYCIJkn4Ad6ONNXhUfQ
|
||||||
|
sf0ABniAAaYAMFnpqb5B7sFAJC83F3q6yAn8i94MAUj8vmXo6U+ng/9P0qxUvgAAyF/E5mxOOkvE+SJO
|
||||||
|
yhSkiu0zIqbGJIoZRomZL0pQxHJijlvkpZ99FtlRzOxkHlvE4pxT2clsMfeIeHuGkCNixEfEBRlcTqaI
|
||||||
|
b4tYM0mYzBXxW3FsMoeZDgCKJLYLOKx4EZuImMQPDnQR8XIAcKS4LzjmCxZwsgTiQ7mkpGbzuXHxArou
|
||||||
|
S49uam3NoHtyMpM4AoGhP5OVyOSz6S4pyalMXjYAi2f+LBlxbemiIluaWltaGpoZmX5RqP+6+Dcl7u0i
|
||||||
|
vQr43DOI1veH7a/8UuoAYMyKarPrD1vMfgA6tgIgd/8Pm+YhACRFfWu/8cV5aOJ5iRcIUm2MjTMzM424
|
||||||
|
HJaRuKC/6386/A198T0j8Xa/l4fuyollCpMEdHHdWClJKUI+PT2VyeLQDf88xP848K/zWBrIieXwOTxR
|
||||||
|
RKhoyri8OFG7eWyugJvCo3N5/6mJ/zDsT1qca5Eo9Z8ANcoISN2gAuTnPoCiEAESeVDc9d/75oMPBeKb
|
||||||
|
F6Y6sTj3nwX9+65wifiRzo37HOcSGExnCfkZi2viawnQgAAkARXIAxWgAXSBITADVsAWOAI3sAL4gWAQ
|
||||||
|
DtYCFogHyYAPMkEu2AwKQBHYBfaCSlAD6kEjaAEnQAc4DS6Ay+A6uAnugAdgBIyD52AGvAHzEARhITJE
|
||||||
|
geQhVUgLMoDMIAZkD7lBPlAgFA5FQ3EQDxJCudAWqAgqhSqhWqgR+hY6BV2ArkID0D1oFJqCfoXewwhM
|
||||||
|
gqmwMqwNG8MM2An2hoPhNXAcnAbnwPnwTrgCroOPwe3wBfg6fAcegZ/DswhAiAgNUUMMEQbigvghEUgs
|
||||||
|
wkc2IIVIOVKHtCBdSC9yCxlBppF3KAyKgqKjDFG2KE9UCIqFSkNtQBWjKlFHUe2oHtQt1ChqBvUJTUYr
|
||||||
|
oQ3QNmgv9Cp0HDoTXYAuRzeg29CX0HfQ4+g3GAyGhtHBWGE8MeGYBMw6TDHmAKYVcx4zgBnDzGKxWHms
|
||||||
|
AdYO64dlYgXYAux+7DHsOewgdhz7FkfEqeLMcO64CBwPl4crxzXhzuIGcRO4ebwUXgtvg/fDs/HZ+BJ8
|
||||||
|
Pb4LfwM/jp8nSBN0CHaEYEICYTOhgtBCuER4SHhFJBLVidbEACKXuIlYQTxOvEIcJb4jyZD0SS6kSJKQ
|
||||||
|
tJN0hHSedI/0ikwma5MdyRFkAXknuZF8kfyY/FaCImEk4SXBltgoUSXRLjEo8UISL6kl6SS5VjJHslzy
|
||||||
|
pOQNyWkpvJS2lIsUU2qDVJXUKalhqVlpirSptJ90snSxdJP0VelJGayMtoybDFsmX+awzEWZMQpC0aC4
|
||||||
|
UFiULZR6yiXKOBVD1aF6UROoRdRvqP3UGVkZ2WWyobJZslWyZ2RHaAhNm+ZFS6KV0E7QhmjvlygvcVrC
|
||||||
|
WbJjScuSwSVzcopyjnIcuUK5Vrk7cu/l6fJu8onyu+U75B8poBT0FQIUMhUOKlxSmFakKtoqshQLFU8o
|
||||||
|
3leClfSVApXWKR1W6lOaVVZR9lBOVd6vfFF5WoWm4qiSoFKmclZlSpWiaq/KVS1TPaf6jC5Ld6In0Svo
|
||||||
|
PfQZNSU1TzWhWq1av9q8uo56iHqeeqv6Iw2CBkMjVqNMo1tjRlNV01czV7NZ874WXouhFa+1T6tXa05b
|
||||||
|
RztMe5t2h/akjpyOl06OTrPOQ12yroNumm6d7m09jB5DL1HvgN5NfVjfQj9ev0r/hgFsYGnANThgMLAU
|
||||||
|
vdR6KW9p3dJhQ5Khk2GGYbPhqBHNyMcoz6jD6IWxpnGE8W7jXuNPJhYmSSb1Jg9MZUxXmOaZdpn+aqZv
|
||||||
|
xjKrMrttTjZ3N99o3mn+cpnBMs6yg8vuWlAsfC22WXRbfLS0suRbtlhOWWlaRVtVWw0zqAx/RjHjijXa
|
||||||
|
2tl6o/Vp63c2ljYCmxM2v9ga2ibaNtlOLtdZzllev3zMTt2OaVdrN2JPt4+2P2Q/4qDmwHSoc3jiqOHI
|
||||||
|
dmxwnHDSc0pwOub0wtnEme/c5jznYuOy3uW8K+Lq4Vro2u8m4xbiVun22F3dPc692X3Gw8Jjncd5T7Sn
|
||||||
|
t+duz2EvZS+WV6PXzAqrFetX9HiTvIO8K72f+Oj78H26fGHfFb57fB+u1FrJW9nhB/y8/Pb4PfLX8U/z
|
||||||
|
/z4AE+AfUBXwNNA0MDewN4gSFBXUFPQm2Dm4JPhBiG6IMKQ7VDI0MrQxdC7MNaw0bGSV8ar1q66HK4Rz
|
||||||
|
wzsjsBGhEQ0Rs6vdVu9dPR5pEVkQObRGZ03WmqtrFdYmrT0TJRnFjDoZjY4Oi26K/sD0Y9YxZ2O8Yqpj
|
||||||
|
ZlgurH2s52xHdhl7imPHKeVMxNrFlsZOxtnF7YmbineIL4+f5rpwK7kvEzwTahLmEv0SjyQuJIUltSbj
|
||||||
|
kqOTT/FkeIm8nhSVlKyUgVSD1ILUkTSbtL1pM3xvfkM6lL4mvVNAFf1M9Ql1hVuFoxn2GVUZbzNDM09m
|
||||||
|
SWfxsvqy9bN3ZE/kuOd8vQ61jrWuO1ctd3Pu6Hqn9bUboA0xG7o3amzM3zi+yWPT0c2EzYmbf8gzySvN
|
||||||
|
e70lbEtXvnL+pvyxrR5bmwskCvgFw9tst9VsR23nbu/fYb5j/45PhezCa0UmReVFH4pZxde+Mv2q4quF
|
||||||
|
nbE7+0ssSw7uwuzi7Rra7bD7aKl0aU7p2B7fPe1l9LLCstd7o/ZeLV9WXrOPsE+4b6TCp6Jzv+b+Xfs/
|
||||||
|
VMZX3qlyrmqtVqreUT13gH1g8KDjwZYa5ZqimveHuIfu1nrUttdp15UfxhzOOPy0PrS+92vG140NCg1F
|
||||||
|
DR+P8I6MHA082tNo1djYpNRU0gw3C5unjkUeu/mN6zedLYYtta201qLj4Ljw+LNvo78dOuF9ovsk42TL
|
||||||
|
d1rfVbdR2grbofbs9pmO+I6RzvDOgVMrTnV32Xa1fW/0/ZHTaqerzsieKTlLOJt/duFczrnZ86nnpy/E
|
||||||
|
XRjrjup+cHHVxds9AT39l7wvXbnsfvlir1PvuSt2V05ftbl66hrjWsd1y+vtfRZ9bT9Y/NDWb9nffsPq
|
||||||
|
RudN65tdA8sHzg46DF645Xrr8m2v29fvrLwzMBQydHc4cnjkLvvu5L2key/vZ9yff7DpIfph4SOpR+WP
|
||||||
|
lR7X/aj3Y+uI5ciZUdfRvidBTx6Mscae/5T+04fx/Kfkp+UTqhONk2aTp6fcp24+W/1s/Hnq8/npgp+l
|
||||||
|
f65+ofviu18cf+mbWTUz/pL/cuHX4lfyr468Xva6e9Z/9vGb5Dfzc4Vv5d8efcd41/s+7P3EfOYH7IeK
|
||||||
|
j3ofuz55f3q4kLyw8Bv3hPP74uYdwgAAAAlwSFlzAAALEwAACxMBAJqcGAAAAahJREFUOE9j+P//P8l4
|
||||||
|
vaOjPYyNIYkPO1lZsa1wdNy42sHh3Hxb22KQGFaF2LC4qjjroUP7n97s6vx/Ny/3/ypn54+LbGwisSpG
|
||||||
|
x+aaouwZren/u5f2/3/18tX/qzNn/l/i4XGSgYFBFasGZKwjzcJ6YVnU152blvw3LHH53zCl/ufatWu+
|
||||||
|
T+1vDALJY9UEwxrijExHZgd+/Xy1Hcg98BNkCMglMM0gjKEJhuX5GVh2TvD+/O5c0///P9b///qo819P
|
||||||
|
lgmKZhBG0QTDMjwMzJs7XT+9OVHz///XFf+/PWj7j00zCKNwQFiah4FtXbPjp8d78////7bo/4/79Tg1
|
||||||
|
gzAKR1mUg3lOocXbe9uz/v9/M/H/1zuVeDWDMJwhJcDBvK4p4tb1DQn//r/u+f/zRh5BzSAMZyyrdVh9
|
||||||
|
c33B9//32159vZr2hxjNIAwm1GUE3e+ur/n9/+Ls/592Nf9fUun3khjNIMzAysTAv6g6+OT/E33/j09N
|
||||||
|
+zWpMuImsZpBmMHIQK9x19T8/03x1ufE+TkqsCnChxmUlFWuyEpJtAHTtT42BfjxfwYAtlm0ShMkSB4A
|
||||||
|
AAAASUVORK5CYII=
|
||||||
</value>
|
</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="BTT_OPEN_IN_BROWSER.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="BTT_OPEN_IN_BROWSER.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ Imports SCrawler.API.YouTube.Objects
|
|||||||
Imports SCrawler.API.YouTube.Controls
|
Imports SCrawler.API.YouTube.Controls
|
||||||
Imports PersonalUtilities.Tools
|
Imports PersonalUtilities.Tools
|
||||||
Imports PersonalUtilities.Forms.Toolbars
|
Imports PersonalUtilities.Forms.Toolbars
|
||||||
|
Imports PersonalUtilities.Functions.Messaging
|
||||||
Namespace DownloadObjects.STDownloader
|
Namespace DownloadObjects.STDownloader
|
||||||
Public Delegate Sub MediaItemEventHandler(ByVal Sender As MediaItem, ByVal Container As IYouTubeMediaContainer)
|
Public Delegate Sub MediaItemEventHandler(ByVal Sender As MediaItem, ByVal Container As IYouTubeMediaContainer)
|
||||||
<DefaultEvent("DoubleClick"), DesignTimeVisible(False), ToolboxItem(False)>
|
<DefaultEvent("DoubleClick"), DesignTimeVisible(False), ToolboxItem(False)>
|
||||||
@@ -23,6 +24,8 @@ Namespace DownloadObjects.STDownloader
|
|||||||
Public Event DownloadAgain As MediaItemEventHandler
|
Public Event DownloadAgain As MediaItemEventHandler
|
||||||
Public Event DownloadRequested As MediaItemEventHandler
|
Public Event DownloadRequested As MediaItemEventHandler
|
||||||
Public Event CheckedChanged As MediaItemEventHandler
|
Public Event CheckedChanged As MediaItemEventHandler
|
||||||
|
Public Event BeforeOpenEditor As MediaItemEventHandler
|
||||||
|
Public Event BeforeOpenEditorFull As MediaItemEventHandler
|
||||||
#End Region
|
#End Region
|
||||||
#Region "Declarations"
|
#Region "Declarations"
|
||||||
#Region "Controls"
|
#Region "Controls"
|
||||||
@@ -51,8 +54,8 @@ Namespace DownloadObjects.STDownloader
|
|||||||
ControlInvokeFast(CH_CHECKED, Sub() CH_CHECKED.Checked = _Checked, EDP.None)
|
ControlInvokeFast(CH_CHECKED, Sub() CH_CHECKED.Checked = _Checked, EDP.None)
|
||||||
End Set
|
End Set
|
||||||
End Property
|
End Property
|
||||||
<Browsable(False)> Public Property IgnoreDownloadState As Boolean = False
|
|
||||||
Private ReadOnly FileOption As SFO = SFO.File
|
Private ReadOnly FileOption As SFO = SFO.File
|
||||||
|
Private ReadOnly ContainerHasElements As Boolean = False
|
||||||
#End Region
|
#End Region
|
||||||
#Region "Initializers"
|
#Region "Initializers"
|
||||||
Public Sub New()
|
Public Sub New()
|
||||||
@@ -111,16 +114,18 @@ Namespace DownloadObjects.STDownloader
|
|||||||
.ContextMenuStrip = CONTEXT_MAIN
|
.ContextMenuStrip = CONTEXT_MAIN
|
||||||
}
|
}
|
||||||
End Sub
|
End Sub
|
||||||
Public Sub New(ByVal Container As IYouTubeMediaContainer)
|
Public Sub New(ByVal Container As IYouTubeMediaContainer, Optional ByVal HasElements As Boolean = False)
|
||||||
Me.New
|
Me.New
|
||||||
Const d$ = " " & ChrW(183) & " "
|
Const d$ = " " & ChrW(183) & " "
|
||||||
MyContainer = Container
|
MyContainer = Container
|
||||||
|
ContainerHasElements = HasElements
|
||||||
With MyContainer
|
With MyContainer
|
||||||
.Progress = MyProgress
|
.Progress = MyProgress
|
||||||
|
If HasElements Then BTT_PLS_ITEM_EDIT.Visible = True : BTT_PLS_ITEM_EDIT_FULL.Visible = True : SEP_PLS_ITEM_EDIT.Visible = True
|
||||||
If .HasElements Then FileOption = SFO.Path Else FileOption = SFO.File
|
If .HasElements Then FileOption = SFO.Path Else FileOption = SFO.File
|
||||||
If .DownloadState = Plugin.UserMediaStates.Downloaded AndAlso
|
If .DownloadState = Plugin.UserMediaStates.Downloaded AndAlso
|
||||||
(.ObjectType = Base.YouTubeMediaType.Channel Or .ObjectType = Base.YouTubeMediaType.PlayList) AndAlso FileOption = SFO.File AndAlso
|
(.ObjectType = Base.YouTubeMediaType.Channel Or .ObjectType = Base.YouTubeMediaType.PlayList) AndAlso FileOption = SFO.File AndAlso
|
||||||
Not .File.Exists AndAlso .File.Exists(SFO.Path, False) Then FileOption = SFO.Path
|
Not .File.Exists AndAlso .File.Exists(SFO.Path, False) Then FileOption = SFO.Path : BTT_OPEN_FILE.Visible = False
|
||||||
If Not .SiteKey = YouTubeSiteKey Then
|
If Not .SiteKey = YouTubeSiteKey Then
|
||||||
BTT_DOWN_AGAIN.Visible = False
|
BTT_DOWN_AGAIN.Visible = False
|
||||||
SEP_DOWN_AGAIN.Visible = False
|
SEP_DOWN_AGAIN.Visible = False
|
||||||
@@ -131,7 +136,7 @@ Namespace DownloadObjects.STDownloader
|
|||||||
LBL_TITLE.Text = .ToString(True)
|
LBL_TITLE.Text = .ToString(True)
|
||||||
If Not .SiteKey = YouTubeSiteKey And .ContentType = Plugin.UserMediaTypes.Picture Then
|
If Not .SiteKey = YouTubeSiteKey And .ContentType = Plugin.UserMediaTypes.Picture Then
|
||||||
LBL_INFO.Text = .File.Extension.StringToUpper
|
LBL_INFO.Text = .File.Extension.StringToUpper
|
||||||
ElseIf Not .IsMusic Then
|
ElseIf Not .IsMusic And Not (.MediaType = Plugin.UserMediaTypes.Audio Or .MediaType = Plugin.UserMediaTypes.AudioPre) Then
|
||||||
If .Height > 0 Then
|
If .Height > 0 Then
|
||||||
LBL_INFO.Text = $"{ .File.Extension.StringToUpper}{d}{ .Height}p"
|
LBL_INFO.Text = $"{ .File.Extension.StringToUpper}{d}{ .Height}p"
|
||||||
Else
|
Else
|
||||||
@@ -176,10 +181,10 @@ Namespace DownloadObjects.STDownloader
|
|||||||
With MyContainer
|
With MyContainer
|
||||||
If Not .SiteKey = YouTubeSiteKey And .ContentType = Plugin.UserMediaTypes.Picture Then
|
If Not .SiteKey = YouTubeSiteKey And .ContentType = Plugin.UserMediaTypes.Picture Then
|
||||||
ICON_WHAT.Image = My.Resources.ImagePic_32
|
ICON_WHAT.Image = My.Resources.ImagePic_32
|
||||||
ElseIf Not .IsMusic Then
|
ElseIf .IsMusic Or .MediaType = Plugin.UserMediaTypes.Audio Or .MediaType = Plugin.UserMediaTypes.AudioPre Then
|
||||||
ICON_WHAT.Image = My.Resources.VideoCamera_32
|
|
||||||
Else
|
|
||||||
ICON_WHAT.Image = My.Resources.AudioMusic_32
|
ICON_WHAT.Image = My.Resources.AudioMusic_32
|
||||||
|
Else
|
||||||
|
ICON_WHAT.Image = My.Resources.VideoCamera_32
|
||||||
End If
|
End If
|
||||||
End With
|
End With
|
||||||
End Sub, EDP.None)
|
End Sub, EDP.None)
|
||||||
@@ -224,11 +229,17 @@ Namespace DownloadObjects.STDownloader
|
|||||||
.Controls.Clear()
|
.Controls.Clear()
|
||||||
.ColumnStyles.Clear()
|
.ColumnStyles.Clear()
|
||||||
.ColumnCount = 0
|
.ColumnCount = 0
|
||||||
If IgnoreDownloadState Or MyContainer.MediaState = Plugin.UserMediaStates.Downloaded Then
|
If ContainerHasElements Or MyContainer.MediaState = Plugin.UserMediaStates.Downloaded Then
|
||||||
If Not MyContainer.SiteKey = YouTubeSiteKey Then UpdateMediaIcon()
|
UpdateMediaIcon()
|
||||||
If IgnoreDownloadState Then
|
If ContainerHasElements Then
|
||||||
BTT_OPEN_FOLDER.Visible = False
|
BTT_OPEN_FOLDER.Visible = False
|
||||||
|
BTT_OPEN_FILE.Visible = False
|
||||||
SEP_FOLDER.Visible = False
|
SEP_FOLDER.Visible = False
|
||||||
|
If Not ContainerHasElements Then
|
||||||
|
BTT_PLS_ITEM_EDIT.Visible = False
|
||||||
|
BTT_PLS_ITEM_EDIT_FULL.Visible = False
|
||||||
|
SEP_PLS_ITEM_EDIT.Visible = False
|
||||||
|
End If
|
||||||
BTT_DOWN_AGAIN.Visible = False
|
BTT_DOWN_AGAIN.Visible = False
|
||||||
SEP_DOWN_AGAIN.Visible = False
|
SEP_DOWN_AGAIN.Visible = False
|
||||||
BTT_REMOVE_FROM_LIST.Visible = False
|
BTT_REMOVE_FROM_LIST.Visible = False
|
||||||
@@ -369,7 +380,7 @@ Namespace DownloadObjects.STDownloader
|
|||||||
ICON_WHAT.DoubleClick, LBL_TIME.DoubleClick, ICON_SIZE.DoubleClick, LBL_INFO.DoubleClick,
|
ICON_WHAT.DoubleClick, LBL_TIME.DoubleClick, ICON_SIZE.DoubleClick, LBL_INFO.DoubleClick,
|
||||||
LBL_PROGRESS.DoubleClick, PR_MAIN.DoubleClick
|
LBL_PROGRESS.DoubleClick, PR_MAIN.DoubleClick
|
||||||
Controls_Click(sender, e)
|
Controls_Click(sender, e)
|
||||||
If Not IgnoreDownloadState AndAlso Not MyDownloaderSettings.OnItemDoubleClick = DoubleClickBehavior.None Then
|
If Not ContainerHasElements AndAlso Not MyDownloaderSettings.OnItemDoubleClick = DoubleClickBehavior.None Then
|
||||||
Dim m As New MMessage("The specified path was not found.", "Open file/folder",, vbExclamation)
|
Dim m As New MMessage("The specified path was not found.", "Open file/folder",, vbExclamation)
|
||||||
If MyDownloaderSettings.OnItemDoubleClick = DoubleClickBehavior.File Then
|
If MyDownloaderSettings.OnItemDoubleClick = DoubleClickBehavior.File Then
|
||||||
If FileOption = SFO.File And MyContainer.File.Exists(SFO.File, False) Then
|
If FileOption = SFO.File And MyContainer.File.Exists(SFO.File, False) Then
|
||||||
@@ -405,6 +416,27 @@ Namespace DownloadObjects.STDownloader
|
|||||||
Private Sub BTT_OPEN_FOLDER_Click(sender As Object, e As EventArgs) Handles BTT_OPEN_FOLDER.Click
|
Private Sub BTT_OPEN_FOLDER_Click(sender As Object, e As EventArgs) Handles BTT_OPEN_FOLDER.Click
|
||||||
If MyContainer.File.Exists(FileOption, False) Then GlobalOpenPath(MyContainer.File)
|
If MyContainer.File.Exists(FileOption, False) Then GlobalOpenPath(MyContainer.File)
|
||||||
End Sub
|
End Sub
|
||||||
|
Private Sub BTT_OPEN_FILE_Click(sender As Object, e As EventArgs) Handles BTT_OPEN_FILE.Click
|
||||||
|
If MyContainer.File.Exists(SFO.File) Then MyContainer.File.Open(,, EDP.ShowAllMsg)
|
||||||
|
End Sub
|
||||||
|
Private Sub BTT_PLS_ITEM_EDIT_Click(sender As Object, e As EventArgs) Handles BTT_PLS_ITEM_EDIT.Click, BTT_PLS_ITEM_EDIT_FULL.Click
|
||||||
|
If ContainerHasElements Then
|
||||||
|
With DirectCast(MyContainer, YouTubeMediaContainerBase)
|
||||||
|
Dim initProtected As Boolean = .Protected
|
||||||
|
Dim isFull As Boolean = sender Is BTT_PLS_ITEM_EDIT_FULL
|
||||||
|
.Protected = False
|
||||||
|
If isFull Then
|
||||||
|
RaiseEvent BeforeOpenEditorFull(Me, MyContainer)
|
||||||
|
Else
|
||||||
|
RaiseEvent BeforeOpenEditor(Me, MyContainer)
|
||||||
|
End If
|
||||||
|
Using f As New VideoOptionsForm(MyContainer, initProtected Or isFull)
|
||||||
|
f.ShowDialog()
|
||||||
|
.Protected = IIf(f.DialogResult = DialogResult.OK, True, initProtected)
|
||||||
|
End Using
|
||||||
|
End With
|
||||||
|
End If
|
||||||
|
End Sub
|
||||||
Private Sub BTT_COPY_LINK_Click(sender As Object, e As EventArgs) Handles BTT_COPY_LINK.Click
|
Private Sub BTT_COPY_LINK_Click(sender As Object, e As EventArgs) Handles BTT_COPY_LINK.Click
|
||||||
If Not MyContainer.URL.IsEmptyString Then
|
If Not MyContainer.URL.IsEmptyString Then
|
||||||
BufferText = MyContainer.URL
|
BufferText = MyContainer.URL
|
||||||
@@ -445,12 +477,28 @@ Namespace DownloadObjects.STDownloader
|
|||||||
RaiseEvent Removal(Me, MyContainer)
|
RaiseEvent Removal(Me, MyContainer)
|
||||||
End Sub
|
End Sub
|
||||||
Private Sub BTT_DELETE_FILE_Click(sender As Object, e As EventArgs) Handles BTT_DELETE_FILE.Click
|
Private Sub BTT_DELETE_FILE_Click(sender As Object, e As EventArgs) Handles BTT_DELETE_FILE.Click
|
||||||
If MsgBoxE({$"Are you sure you want to delete the following {FileOption.ToString.ToLower}:{vbCr}" &
|
Dim opt$
|
||||||
If(FileOption = SFO.File, MyContainer.File.ToString, MyContainer.File.PathWithSeparator),
|
Dim opt2$ = String.Empty
|
||||||
$"Deleting a {FileOption.ToString.ToLower}"}, vbExclamation,,, {"Process", "Cancel"}) = 0 Then
|
If FileOption = SFO.File Then
|
||||||
|
opt = "file"
|
||||||
|
Else
|
||||||
|
opt = "item"
|
||||||
|
opt2 = "THE ITEM MAY CONTAIN MULTIPLE FILES" & vbCr
|
||||||
|
End If
|
||||||
|
Dim b As New List(Of MsgBoxButton) From {New MsgBoxButton("Process")}
|
||||||
|
If Not opt2.IsEmptyString Then _
|
||||||
|
b.Add(New MsgBoxButton("Show files", "Show files to delete") With {
|
||||||
|
.IsDialogResultButton = False,
|
||||||
|
.CallBack = Function(r, m, bb) MsgBoxE(New MMessage($"The following files will be deleted:{vbCr}{vbCr}{MyContainer.Files.ListToString(vbCr)}",
|
||||||
|
"Files to delete",, vbExclamation) With {.Editable = True})})
|
||||||
|
b.Add(New MsgBoxButton("Cancel"))
|
||||||
|
If MsgBoxE({$"Are you sure you want to delete the following {opt}:{vbCr}{opt2}" &
|
||||||
|
If(FileOption = SFO.File, MyContainer.File.ToString, MyContainer.ToString(True)),
|
||||||
|
$"Deleting {opt}"}, vbExclamation,,, b) = 0 Then
|
||||||
MyContainer.Delete(True)
|
MyContainer.Delete(True)
|
||||||
RaiseEvent Removal(Me, MyContainer)
|
RaiseEvent Removal(Me, MyContainer)
|
||||||
End If
|
End If
|
||||||
|
b.Clear()
|
||||||
End Sub
|
End Sub
|
||||||
#End Region
|
#End Region
|
||||||
#Region "ISupportInitialize Support"
|
#Region "ISupportInitialize Support"
|
||||||
|
|||||||
@@ -32,6 +32,6 @@ Imports System.Runtime.InteropServices
|
|||||||
' by using the '*' as shown below:
|
' by using the '*' as shown below:
|
||||||
' <Assembly: AssemblyVersion("1.0.*")>
|
' <Assembly: AssemblyVersion("1.0.*")>
|
||||||
|
|
||||||
<Assembly: AssemblyVersion("2023.12.14.0")>
|
<Assembly: AssemblyVersion("2024.5.4.0")>
|
||||||
<Assembly: AssemblyFileVersion("2023.12.14.0")>
|
<Assembly: AssemblyFileVersion("2024.5.4.0")>
|
||||||
<Assembly: NeutralResourcesLanguage("en")>
|
<Assembly: NeutralResourcesLanguage("en")>
|
||||||
|
|||||||
@@ -19,17 +19,18 @@ Namespace API.YouTube.Objects
|
|||||||
Dim __title$ = $" - {Title}"
|
Dim __title$ = $" - {Title}"
|
||||||
If Not s.IsEmptyString Then s = $" [{s}]"
|
If Not s.IsEmptyString Then s = $" [{s}]"
|
||||||
If Not PlaylistTitle.IsEmptyString And Not ForMediaItem Then t = $"{PlaylistTitle} - "
|
If Not PlaylistTitle.IsEmptyString And Not ForMediaItem Then t = $"{PlaylistTitle} - "
|
||||||
|
Dim c% = {Count, ElementsNumber}.Max
|
||||||
If IsMusic Then
|
If IsMusic Then
|
||||||
If Count <= 1 Then t &= "Single" Else t &= "Album"
|
If c <= 1 Then t &= "Single" Else t &= "Album"
|
||||||
Else
|
Else
|
||||||
t &= "Playlist"
|
t &= "Playlist"
|
||||||
End If
|
End If
|
||||||
If Not PlaylistTitle.IsEmptyString And Not ForMediaItem Then t &= $" - {PlaylistTitle}"
|
If Not PlaylistTitle.IsEmptyString And Not ForMediaItem Then t &= $" - {PlaylistTitle}"
|
||||||
If PlaylistTitle = Title Then __title = String.Empty
|
If PlaylistTitle = Title Then __title = String.Empty
|
||||||
If ForMediaItem Then
|
If ForMediaItem Then
|
||||||
Return $"{t} ({Count}){__title}"
|
Return $"{t} ({c}){__title}"
|
||||||
Else
|
Else
|
||||||
Return $"{t} ({Count}){__title} ({AConvert(Of String)(Duration, TimeToStringProvider)}){s}"
|
Return $"{t} ({c}){__title} ({AConvert(Of String)(Duration, TimeToStringProvider)}){s}"
|
||||||
End If
|
End If
|
||||||
End Function
|
End Function
|
||||||
Public Overrides Function Parse(ByVal Container As EContainer, ByVal Path As SFile, ByVal IsMusic As Boolean,
|
Public Overrides Function Parse(ByVal Container As EContainer, ByVal Path As SFile, ByVal IsMusic As Boolean,
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ Namespace API.YouTube.Objects
|
|||||||
Else
|
Else
|
||||||
_File.Extension = mp3
|
_File.Extension = mp3
|
||||||
End If
|
End If
|
||||||
|
_File = CleanFileName(_File)
|
||||||
End If
|
End If
|
||||||
End Sub
|
End Sub
|
||||||
Public Overrides Function ToString(ByVal ForMediaItem As Boolean) As String
|
Public Overrides Function ToString(ByVal ForMediaItem As Boolean) As String
|
||||||
@@ -46,12 +47,17 @@ Namespace API.YouTube.Objects
|
|||||||
_ObjectType = Base.YouTubeMediaType.Single
|
_ObjectType = Base.YouTubeMediaType.Single
|
||||||
Me.IsMusic = IsMusic
|
Me.IsMusic = IsMusic
|
||||||
If MyBase.Parse(Container, Path, IsMusic, Token, Progress) Then
|
If MyBase.Parse(Container, Path, IsMusic, Token, Progress) Then
|
||||||
Dim f As SFile = MyYouTubeSettings.OutputPath
|
With MyYouTubeSettings
|
||||||
If f.IsEmptyString Then f = "YouTubeDownloads\OutputFile.mp3"
|
Dim f As SFile = .OutputPath
|
||||||
Dim ext$ = MyYouTubeSettings.DefaultAudioCodec.Value.StringToLower
|
If f.IsEmptyString Then f = "YouTubeDownloads\OutputFile.mp3"
|
||||||
If ext.IsEmptyString Then ext = "mp3"
|
Dim ext$ = .DefaultAudioCodecMusic.Value.StringToLower.IfNullOrEmpty(.DefaultAudioCodec.Value.StringToLower)
|
||||||
f.Extension = ext
|
If ext.IsEmptyString Then ext = "mp3"
|
||||||
File = f
|
f.Extension = ext
|
||||||
|
'If f.Name.IsEmptyString Then f.Name = File.Name
|
||||||
|
File = f
|
||||||
|
If _File.Extension.IsEmptyString Then _File.Extension = ext
|
||||||
|
_File = CleanFileName(_File)
|
||||||
|
End With
|
||||||
Return True
|
Return True
|
||||||
Else
|
Else
|
||||||
Return False
|
Return False
|
||||||
|
|||||||
@@ -123,6 +123,15 @@ Namespace API.YouTube.Objects
|
|||||||
<XMLEC> Public Property UserTitle As String Implements IYouTubeMediaContainer.UserTitle
|
<XMLEC> Public Property UserTitle As String Implements IYouTubeMediaContainer.UserTitle
|
||||||
#End Region
|
#End Region
|
||||||
#Region "Playlist support"
|
#Region "Playlist support"
|
||||||
|
Private _ElementsNumber As Integer = 0
|
||||||
|
<XMLEC> Protected Property ElementsNumber As Integer
|
||||||
|
Get
|
||||||
|
Return If(HasElements, Count, _ElementsNumber)
|
||||||
|
End Get
|
||||||
|
Set(ByVal _ElementsNumber As Integer)
|
||||||
|
Me._ElementsNumber = _ElementsNumber
|
||||||
|
End Set
|
||||||
|
End Property
|
||||||
Friend ReadOnly Property Elements As List(Of IYouTubeMediaContainer) Implements IYouTubeMediaContainer.Elements
|
Friend ReadOnly Property Elements As List(Of IYouTubeMediaContainer) Implements IYouTubeMediaContainer.Elements
|
||||||
Friend ReadOnly Property HasElements As Boolean Implements IYouTubeMediaContainer.HasElements
|
Friend ReadOnly Property HasElements As Boolean Implements IYouTubeMediaContainer.HasElements
|
||||||
Get
|
Get
|
||||||
@@ -139,22 +148,26 @@ Namespace API.YouTube.Objects
|
|||||||
#End Region
|
#End Region
|
||||||
#Region "Data info"
|
#Region "Data info"
|
||||||
Friend ReadOnly Property MediaObjects As List(Of MediaObject) Implements IYouTubeMediaContainer.MediaObjects
|
Friend ReadOnly Property MediaObjects As List(Of MediaObject) Implements IYouTubeMediaContainer.MediaObjects
|
||||||
|
Friend Property [Protected] As Boolean = False
|
||||||
|
Friend Property IsAudioSelected As Boolean = False
|
||||||
#Region "Array"
|
#Region "Array"
|
||||||
''' <summary>[-10] = disabled; [-1] = max; [-2] = audio only</summary>
|
''' <summary>[-10] = disabled; [-1] = max; [-2] = audio only</summary>
|
||||||
<XMLEC> Friend Property ArrayMaxResolution As Integer = -10
|
<XMLEC> Friend Property ArrayMaxResolution As Integer = -10
|
||||||
''' <param name="Value">[-1] = max; [-2] = audio only</param>
|
''' <param name="Value">[-1] = max; [-2] = audio only</param>
|
||||||
Friend Sub SetMaxResolution(ByVal Value As Integer)
|
Friend Sub SetMaxResolution(ByVal Value As Integer)
|
||||||
ArrayMaxResolution = Value
|
If Not [Protected] Then
|
||||||
SelectedVideoIndex = -1
|
ArrayMaxResolution = Value
|
||||||
If MediaObjects.Count > 0 And Value <> -2 Then
|
SelectedVideoIndex = -1
|
||||||
If Value = -1 Then
|
If MediaObjects.Count > 0 And Value <> -2 Then
|
||||||
SelectedVideoIndex = MediaObjects.FindIndex(Function(mo) mo.Type = UMTypes.Video)
|
If Value = -1 Then
|
||||||
Else
|
SelectedVideoIndex = MediaObjects.FindIndex(Function(mo) mo.Type = UMTypes.Video)
|
||||||
SelectedVideoIndex = MediaObjects.FindIndex(Function(mo) mo.Type = UMTypes.Video And mo.Height <= Value)
|
Else
|
||||||
If SelectedVideoIndex = -1 Then SelectedVideoIndex = MediaObjects.FindIndex(Function(mo) mo.Type = UMTypes.Video)
|
SelectedVideoIndex = MediaObjects.FindIndex(Function(mo) mo.Type = UMTypes.Video And mo.Height <= Value)
|
||||||
|
If SelectedVideoIndex = -1 Then SelectedVideoIndex = MediaObjects.FindIndex(Function(mo) mo.Type = UMTypes.Video)
|
||||||
|
End If
|
||||||
End If
|
End If
|
||||||
|
If HasElements Then Elements.ForEach(Sub(e As YouTubeMediaContainerBase) e.SetMaxResolution(Value))
|
||||||
End If
|
End If
|
||||||
If HasElements Then Elements.ForEach(Sub(e As YouTubeMediaContainerBase) e.SetMaxResolution(Value))
|
|
||||||
End Sub
|
End Sub
|
||||||
#End Region
|
#End Region
|
||||||
#Region "Thumbnails"
|
#Region "Thumbnails"
|
||||||
@@ -213,8 +226,22 @@ Namespace API.YouTube.Objects
|
|||||||
Return _OutputVideoExtension
|
Return _OutputVideoExtension
|
||||||
End Get
|
End Get
|
||||||
Set(ByVal _OutputVideoExtension As String)
|
Set(ByVal _OutputVideoExtension As String)
|
||||||
Me._OutputVideoExtension = _OutputVideoExtension
|
If Not [Protected] Then
|
||||||
If HasElements Then Elements.ForEach(Sub(e) e.OutputVideoExtension = _OutputVideoExtension)
|
Me._OutputVideoExtension = _OutputVideoExtension
|
||||||
|
If HasElements Then Elements.ForEach(Sub(e) e.OutputVideoExtension = _OutputVideoExtension)
|
||||||
|
End If
|
||||||
|
End Set
|
||||||
|
End Property
|
||||||
|
<XMLEC("OutputVideoFPS")> Protected _OutputVideoFPS As Double = -1
|
||||||
|
Friend Property OutputVideoFPS As Double
|
||||||
|
Get
|
||||||
|
Return _OutputVideoFPS
|
||||||
|
End Get
|
||||||
|
Set(ByVal fps As Double)
|
||||||
|
If Not [Protected] Then
|
||||||
|
_OutputVideoFPS = fps
|
||||||
|
If HasElements Then Elements.ForEach(Sub(elem) DirectCast(elem, YouTubeMediaContainerBase).OutputVideoFPS = fps)
|
||||||
|
End If
|
||||||
End Set
|
End Set
|
||||||
End Property
|
End Property
|
||||||
#End Region
|
#End Region
|
||||||
@@ -231,8 +258,10 @@ Namespace API.YouTube.Objects
|
|||||||
Return _OutputAudioCodec
|
Return _OutputAudioCodec
|
||||||
End Get
|
End Get
|
||||||
Set(ByVal _OutputAudioCodec As String)
|
Set(ByVal _OutputAudioCodec As String)
|
||||||
Me._OutputAudioCodec = _OutputAudioCodec
|
If Not [Protected] Then
|
||||||
If HasElements Then Elements.ForEach(Sub(e) e.OutputAudioCodec = _OutputAudioCodec)
|
Me._OutputAudioCodec = _OutputAudioCodec
|
||||||
|
If HasElements Then Elements.ForEach(Sub(e) e.OutputAudioCodec = _OutputAudioCodec)
|
||||||
|
End If
|
||||||
End Set
|
End Set
|
||||||
End Property
|
End Property
|
||||||
<XMLEC(CollectionMode:=CollectionModes.String)>
|
<XMLEC(CollectionMode:=CollectionModes.String)>
|
||||||
@@ -245,6 +274,18 @@ Namespace API.YouTube.Objects
|
|||||||
PostProcessing_OutputAudioFormats.RemoveAll(Function(s) s = -1)
|
PostProcessing_OutputAudioFormats.RemoveAll(Function(s) s = -1)
|
||||||
End If
|
End If
|
||||||
End Sub
|
End Sub
|
||||||
|
<XMLEC("OutputAudioBitrate")> Protected _OutputAudioBitrate As Integer = -1
|
||||||
|
Friend Property OutputAudioBitrate As Integer
|
||||||
|
Get
|
||||||
|
Return _OutputAudioBitrate
|
||||||
|
End Get
|
||||||
|
Set(ByVal NewBitrate As Integer)
|
||||||
|
If Not [Protected] Then
|
||||||
|
_OutputAudioBitrate = NewBitrate
|
||||||
|
If HasElements Then Elements.ForEach(Sub(elem) DirectCast(elem, YouTubeMediaContainerBase).OutputAudioBitrate = NewBitrate)
|
||||||
|
End If
|
||||||
|
End Set
|
||||||
|
End Property
|
||||||
#End Region
|
#End Region
|
||||||
#Region "Subtitles"
|
#Region "Subtitles"
|
||||||
Protected ReadOnly _Subtitles As List(Of Subtitles)
|
Protected ReadOnly _Subtitles As List(Of Subtitles)
|
||||||
@@ -272,8 +313,10 @@ Namespace API.YouTube.Objects
|
|||||||
Return _OutputSubtitlesFormat
|
Return _OutputSubtitlesFormat
|
||||||
End Get
|
End Get
|
||||||
Set(ByVal _OutputSubtitlesFormat As String)
|
Set(ByVal _OutputSubtitlesFormat As String)
|
||||||
Me._OutputSubtitlesFormat = _OutputSubtitlesFormat
|
If Not [Protected] Then
|
||||||
If HasElements Then Elements.ForEach(Sub(e) e.OutputSubtitlesFormat = _OutputSubtitlesFormat)
|
Me._OutputSubtitlesFormat = _OutputSubtitlesFormat
|
||||||
|
If HasElements Then Elements.ForEach(Sub(e) e.OutputSubtitlesFormat = _OutputSubtitlesFormat)
|
||||||
|
End If
|
||||||
End Set
|
End Set
|
||||||
End Property
|
End Property
|
||||||
<XMLEC(CollectionMode:=CollectionModes.String)>
|
<XMLEC(CollectionMode:=CollectionModes.String)>
|
||||||
@@ -354,10 +397,13 @@ Namespace API.YouTube.Objects
|
|||||||
End Set
|
End Set
|
||||||
End Property
|
End Property
|
||||||
Protected _Size As Integer = 0
|
Protected _Size As Integer = 0
|
||||||
|
<XMLEC("SizeRecalculated")> Protected _SizeRecalculated As Boolean = False
|
||||||
<XMLEC> Public Overridable Property Size As Integer Implements IDownloadableMedia.Size
|
<XMLEC> Public Overridable Property Size As Integer Implements IDownloadableMedia.Size
|
||||||
Get
|
Get
|
||||||
If HasElements Then
|
If HasElements Then
|
||||||
Return Elements.Sum(Function(e) If(e.Checked, e.Size, 0))
|
Return Elements.Sum(Function(e) If(e.Checked, e.Size, 0))
|
||||||
|
ElseIf _SizeRecalculated Then
|
||||||
|
Return _Size
|
||||||
Else
|
Else
|
||||||
If Checked Then
|
If Checked Then
|
||||||
If IsMusic And SelectedAudioIndex.ValueBetween(0, MediaObjects.Count - 1) Then
|
If IsMusic And SelectedAudioIndex.ValueBetween(0, MediaObjects.Count - 1) Then
|
||||||
@@ -537,7 +583,25 @@ Namespace API.YouTube.Objects
|
|||||||
If ObjectType = YouTubeMediaType.Single AndAlso Not GetPlayListTitle.IsEmptyString Then _SpecialPath.StringAppend(GetPlayListTitle(), "\")
|
If ObjectType = YouTubeMediaType.Single AndAlso Not GetPlayListTitle.IsEmptyString Then _SpecialPath.StringAppend(GetPlayListTitle(), "\")
|
||||||
If Elements.Count > 0 Then Elements.ForEach(Sub(e) e.SpecialFolder = Path)
|
If Elements.Count > 0 Then Elements.ForEach(Sub(e) e.SpecialFolder = Path)
|
||||||
End Sub
|
End Sub
|
||||||
<XMLEC> Protected Friend ReadOnly Property Files As List(Of SFile) Implements IYouTubeMediaContainer.Files
|
Private ReadOnly _Files As List(Of SFile)
|
||||||
|
<XMLEC> Protected Friend Property Files As List(Of SFile) Implements IYouTubeMediaContainer.Files
|
||||||
|
Get
|
||||||
|
If HasElements Then
|
||||||
|
Return GetFilesFiles()
|
||||||
|
Else
|
||||||
|
Return _Files
|
||||||
|
End If
|
||||||
|
End Get
|
||||||
|
Set(ByVal f As List(Of SFile))
|
||||||
|
_Files.ListAddList(f, LAP.NotContainsOnly)
|
||||||
|
End Set
|
||||||
|
End Property
|
||||||
|
Protected Overloads Sub AddFile(ByVal f As SFile)
|
||||||
|
_Files.ListAddValue(f, LAP.NotContainsOnly)
|
||||||
|
End Sub
|
||||||
|
Protected Overloads Sub AddFile(ByVal f As IEnumerable(Of SFile))
|
||||||
|
_Files.ListAddList(f, LAP.NotContainsOnly)
|
||||||
|
End Sub
|
||||||
<XMLEC> Protected _File As SFile
|
<XMLEC> Protected _File As SFile
|
||||||
<XMLEC> Protected Friend Property FileSetManually As Boolean = False
|
<XMLEC> Protected Friend Property FileSetManually As Boolean = False
|
||||||
Public Property FileIgnorePlaylist As Boolean = False
|
Public Property FileIgnorePlaylist As Boolean = False
|
||||||
@@ -555,8 +619,10 @@ Namespace API.YouTube.Objects
|
|||||||
Return _AbsolutePath
|
Return _AbsolutePath
|
||||||
End Get
|
End Get
|
||||||
Set(ByVal ap As Boolean)
|
Set(ByVal ap As Boolean)
|
||||||
_AbsolutePath = ap
|
If Not [Protected] Then
|
||||||
If Elements.Count > 0 Then Elements.ForEach(Sub(e As YouTubeMediaContainerBase) e.AbsolutePath = ap)
|
_AbsolutePath = ap
|
||||||
|
If Elements.Count > 0 Then Elements.ForEach(Sub(e As YouTubeMediaContainerBase) e.AbsolutePath = ap)
|
||||||
|
End If
|
||||||
End Set
|
End Set
|
||||||
End Property
|
End Property
|
||||||
Public Overridable Property File As SFile Implements IYouTubeMediaContainer.File
|
Public Overridable Property File As SFile Implements IYouTubeMediaContainer.File
|
||||||
@@ -564,28 +630,30 @@ Namespace API.YouTube.Objects
|
|||||||
Return _File
|
Return _File
|
||||||
End Get
|
End Get
|
||||||
Set(ByVal f As SFile)
|
Set(ByVal f As SFile)
|
||||||
Select Case ObjectType
|
If Not [Protected] Then
|
||||||
Case YouTubeMediaType.Channel : _File = f.Path
|
Select Case ObjectType
|
||||||
Case YouTubeMediaType.PlayList
|
Case YouTubeMediaType.Channel : _File = f.Path
|
||||||
If AbsolutePath Then
|
Case YouTubeMediaType.PlayList
|
||||||
_File.Path = f.Path
|
If AbsolutePath Then
|
||||||
Else
|
_File.Path = f.Path
|
||||||
_File.Path = $"{f.PathWithSeparator}{GetPlayListTitle()}"
|
Else
|
||||||
End If
|
_File.Path = $"{f.PathWithSeparator}{GetPlayListTitle()}"
|
||||||
Case YouTubeMediaType.Single
|
End If
|
||||||
If PlaylistCount > 0 And Not FileIgnorePlaylist Then
|
Case YouTubeMediaType.Single
|
||||||
_File.Path = f.Path
|
If PlaylistCount > 0 And Not FileIgnorePlaylist Then
|
||||||
Dim pls$ = If(AbsolutePath, String.Empty, GetPlayListTitle())
|
_File.Path = f.Path
|
||||||
If Not _File.Path.Contains(pls) Then _File.Path = $"{_File.PathWithSeparator(Not pls.IsEmptyString)}{pls}"
|
Dim pls$ = If(AbsolutePath, String.Empty, GetPlayListTitle())
|
||||||
ElseIf Not f.Name.IsEmptyString Then
|
If Not _File.Path.Contains(pls) Then _File.Path = $"{_File.PathWithSeparator(Not pls.IsEmptyString)}{pls}"
|
||||||
_File = f
|
ElseIf Not f.Name.IsEmptyString Then
|
||||||
Else
|
_File = f
|
||||||
_File.Path = f.Path
|
Else
|
||||||
End If
|
_File.Path = f.Path
|
||||||
Case Else : _File = f
|
End If
|
||||||
End Select
|
Case Else : _File = f
|
||||||
GenerateFileName()
|
End Select
|
||||||
If HasElements Then Elements.ForEach(Sub(e) e.File = _File)
|
GenerateFileName()
|
||||||
|
If HasElements Then Elements.ForEach(Sub(e) e.File = _File)
|
||||||
|
End If
|
||||||
End Set
|
End Set
|
||||||
End Property
|
End Property
|
||||||
Public Property FileSettings As SFile
|
Public Property FileSettings As SFile
|
||||||
@@ -602,6 +670,26 @@ Namespace API.YouTube.Objects
|
|||||||
If HasElements And Not IsMusic Then urls.ListAddList(Elements.SelectMany(Function(elem As YouTubeMediaContainerBase) elem.GetFiles()), LAP.NotContainsOnly)
|
If HasElements And Not IsMusic Then urls.ListAddList(Elements.SelectMany(Function(elem As YouTubeMediaContainerBase) elem.GetFiles()), LAP.NotContainsOnly)
|
||||||
Return urls
|
Return urls
|
||||||
End Function
|
End Function
|
||||||
|
Private Function GetFilesFiles() As IEnumerable(Of SFile)
|
||||||
|
Dim f As New List(Of SFile)
|
||||||
|
If File.Exists Then f.Add(File)
|
||||||
|
If _Files.Count > 0 Then f.AddRange(_Files)
|
||||||
|
If ThumbnailFile.Exists Then f.Add(ThumbnailFile)
|
||||||
|
If HasElements Then f.ListAddList(Elements.SelectMany(Function(elem As YouTubeMediaContainerBase) elem.GetFilesFiles()), LAP.NotContainsOnly)
|
||||||
|
Return f
|
||||||
|
End Function
|
||||||
|
Private _M3U8_PlaylistFiles As IEnumerable(Of SFile) = Nothing
|
||||||
|
Friend Property M3U8_PlaylistFiles As IEnumerable(Of SFile)
|
||||||
|
Get
|
||||||
|
Return _M3U8_PlaylistFiles
|
||||||
|
End Get
|
||||||
|
Set(ByVal f As IEnumerable(Of SFile))
|
||||||
|
If Not [Protected] Then
|
||||||
|
_M3U8_PlaylistFiles = f
|
||||||
|
If HasElements Then Elements.ForEach(Sub(e As YouTubeMediaContainerBase) e.M3U8_PlaylistFiles = f)
|
||||||
|
End If
|
||||||
|
End Set
|
||||||
|
End Property
|
||||||
#End Region
|
#End Region
|
||||||
#Region "Command"
|
#Region "Command"
|
||||||
<XMLEC> Public Property UseCookies As Boolean = MyYouTubeSettings.DefaultUseCookies Implements IYouTubeMediaContainer.UseCookies
|
<XMLEC> Public Property UseCookies As Boolean = MyYouTubeSettings.DefaultUseCookies Implements IYouTubeMediaContainer.UseCookies
|
||||||
@@ -609,41 +697,57 @@ Namespace API.YouTube.Objects
|
|||||||
Private Const aac As String = "aac"
|
Private Const aac As String = "aac"
|
||||||
Private Const ac3 As String = "ac3"
|
Private Const ac3 As String = "ac3"
|
||||||
Protected PostProcessing_AudioAC3 As Boolean = False
|
Protected PostProcessing_AudioAC3 As Boolean = False
|
||||||
|
Protected PostProcessing_AudioMP3 As Boolean = False
|
||||||
Public Overridable ReadOnly Property Command(ByVal WithCookies As Boolean) As String Implements IYouTubeMediaContainer.Command
|
Public Overridable ReadOnly Property Command(ByVal WithCookies As Boolean) As String Implements IYouTubeMediaContainer.Command
|
||||||
Get
|
Get
|
||||||
If Not File.IsEmptyString Then
|
If Not File.IsEmptyString Then
|
||||||
If File.Exists Then File = SFile.IndexReindex(File)
|
If File.Exists Then File = SFile.IndexReindex(File)
|
||||||
Dim cmd$ = String.Empty, formats$ = String.Empty, subs$ = String.Empty, remux$ = String.Empty
|
Dim cmd$ = String.Empty, formats$ = String.Empty, subs$ = String.Empty, remux$ = String.Empty
|
||||||
|
Dim embedThumbArgAdded As Boolean = False
|
||||||
_Size = 0
|
_Size = 0
|
||||||
Height = 0
|
Height = 0
|
||||||
Bitrate = 0
|
Bitrate = 0
|
||||||
_MediaType = UMTypes.Undefined
|
_MediaType = UMTypes.Undefined
|
||||||
If SelectedVideoIndex >= 0 Then
|
If SelectedVideoIndex >= 0 Then
|
||||||
'URGENT: 2023.3.4 -> 2023.7.6
|
'2023.3.4 -> 2023.7.6
|
||||||
'cmd.StringAppend($"bv*[format_id={SelectedVideo.ID}]")
|
'cmd.StringAppend($"bv*[format_id={SelectedVideo.ID}]")
|
||||||
cmd.StringAppend(SelectedVideo.ID)
|
cmd.StringAppend(SelectedVideo.ID)
|
||||||
_Size = SelectedVideo.Size
|
_Size = SelectedVideo.Size
|
||||||
_MediaType = UMTypes.Video
|
_MediaType = UMTypes.Video
|
||||||
Height = SelectedVideo.Height
|
Height = SelectedVideo.Height
|
||||||
_File.Extension = OutputVideoExtension
|
_File.Extension = OutputVideoExtension
|
||||||
|
If Not embedThumbArgAdded And MyYouTubeSettings.DefaultVideoEmbedThumbnail Then
|
||||||
|
formats.StringAppend("--embed-thumbnail", " ")
|
||||||
|
embedThumbArgAdded = True
|
||||||
|
End If
|
||||||
Else
|
Else
|
||||||
formats.StringAppend("--extract-audio", " ")
|
formats.StringAppend("--extract-audio", " ")
|
||||||
_MediaType = UMTypes.Audio
|
_MediaType = UMTypes.Audio
|
||||||
End If
|
End If
|
||||||
If SelectedAudioIndex >= 0 Then
|
If SelectedAudioIndex >= 0 Then
|
||||||
Dim atCodec$
|
Dim atCodec$
|
||||||
'URGENT: 2023.3.4 -> 2023.7.6
|
'2023.3.4 -> 2023.7.6
|
||||||
'cmd.StringAppend($"ba*[format_id={SelectedAudio.ID}]", "+")
|
'cmd.StringAppend($"ba*[format_id={SelectedAudio.ID}]", "+")
|
||||||
cmd.StringAppend(SelectedAudio.ID, "+")
|
cmd.StringAppend(SelectedAudio.ID, "+")
|
||||||
If OutputAudioCodec.StringToLower = ac3 Then
|
If OutputAudioCodec.StringToLower = ac3 Then
|
||||||
PostProcessing_AudioAC3 = True
|
PostProcessing_AudioAC3 = True
|
||||||
formats.StringAppend($"--audio-format {aac}", " ")
|
formats.StringAppend($"--audio-format {aac}", " ")
|
||||||
atCodec = aac
|
atCodec = aac
|
||||||
|
ElseIf SelectedVideoIndex >= 0 And OutputAudioCodec.StringToLower = mp3 Then
|
||||||
|
PostProcessing_AudioMP3 = True
|
||||||
|
formats.StringAppend($"--audio-format {aac}", " ")
|
||||||
|
atCodec = aac
|
||||||
Else
|
Else
|
||||||
formats.StringAppend($"--audio-format {OutputAudioCodec.StringToLower}", " ")
|
formats.StringAppend($"--audio-format {OutputAudioCodec.StringToLower}", " ")
|
||||||
atCodec = OutputAudioCodec.StringToLower
|
atCodec = OutputAudioCodec.StringToLower
|
||||||
End If
|
End If
|
||||||
If SelectedVideoIndex = -1 Then formats.StringAppend("--add-metadata", " ")
|
If SelectedVideoIndex = -1 Then
|
||||||
|
formats.StringAppend("--add-metadata", " ")
|
||||||
|
If Not embedThumbArgAdded And MyYouTubeSettings.DefaultAudioEmbedThumbnail Then
|
||||||
|
formats.StringAppend("--embed-thumbnail", " ")
|
||||||
|
embedThumbArgAdded = True
|
||||||
|
End If
|
||||||
|
End If
|
||||||
_Size += SelectedAudio.Size
|
_Size += SelectedAudio.Size
|
||||||
If _MediaType = UMTypes.Undefined Then _MediaType = UMTypes.Audio
|
If _MediaType = UMTypes.Undefined Then _MediaType = UMTypes.Audio
|
||||||
Bitrate = SelectedAudio.Bitrate
|
Bitrate = SelectedAudio.Bitrate
|
||||||
@@ -665,9 +769,10 @@ Namespace API.YouTube.Objects
|
|||||||
subs = $"--write-subs --write-auto-subs --sub-format {OutputSubtitlesFormat.StringToLower} --sub-langs ""{subs}"" --convert-subs {OutputSubtitlesFormat.StringToLower}"
|
subs = $"--write-subs --write-auto-subs --sub-format {OutputSubtitlesFormat.StringToLower} --sub-langs ""{subs}"" --convert-subs {OutputSubtitlesFormat.StringToLower}"
|
||||||
End If
|
End If
|
||||||
If Not cmd.IsEmptyString Then
|
If Not cmd.IsEmptyString Then
|
||||||
'URGENT: 2023.3.4 -> 2023.7.6
|
'2023.3.4 -> 2023.7.6
|
||||||
'cmd = $"yt-dlp -f ""{cmd}"""
|
'cmd = $"yt-dlp -f ""{cmd}"""
|
||||||
cmd = $"yt-dlp -f {cmd}"
|
'cmd = $"yt-dlp -f {cmd}"
|
||||||
|
cmd = $"{YTDLP_NAME} -f {cmd}"
|
||||||
If Not MyYouTubeSettings.ReplaceModificationDate Then cmd &= " --no-mtime"
|
If Not MyYouTubeSettings.ReplaceModificationDate Then cmd &= " --no-mtime"
|
||||||
cmd.StringAppend(formats, " ")
|
cmd.StringAppend(formats, " ")
|
||||||
cmd.StringAppend(subs, " ")
|
cmd.StringAppend(subs, " ")
|
||||||
@@ -689,7 +794,7 @@ Namespace API.YouTube.Objects
|
|||||||
_SubtitlesDelegated = New List(Of Subtitles)
|
_SubtitlesDelegated = New List(Of Subtitles)
|
||||||
SubtitlesSelectedIndexes = New List(Of Integer)
|
SubtitlesSelectedIndexes = New List(Of Integer)
|
||||||
MediaObjects = New List(Of MediaObject)
|
MediaObjects = New List(Of MediaObject)
|
||||||
Files = New List(Of SFile)
|
_Files = New List(Of SFile)
|
||||||
|
|
||||||
PostProcessing_OutputSubtitlesFormats = New List(Of String)
|
PostProcessing_OutputSubtitlesFormats = New List(Of String)
|
||||||
PostProcessing_OutputSubtitlesFormats.ListAddList(MyYouTubeSettings.DefaultSubtitlesFormatAddit)
|
PostProcessing_OutputSubtitlesFormats.ListAddList(MyYouTubeSettings.DefaultSubtitlesFormatAddit)
|
||||||
@@ -718,9 +823,19 @@ Namespace API.YouTube.Objects
|
|||||||
If RemoveFiles Then
|
If RemoveFiles Then
|
||||||
Dim fErr As New ErrorsDescriber(EDP.None)
|
Dim fErr As New ErrorsDescriber(EDP.None)
|
||||||
Dim dMode As SFODelete = SFODelete.DeleteToRecycleBin
|
Dim dMode As SFODelete = SFODelete.DeleteToRecycleBin
|
||||||
|
Dim paths As New List(Of SFile)
|
||||||
|
Dim l As New ListAddParams(LAP.NotContainsOnly) With {.Comparer = New FComparer(Of SFile)(Function(x, y) x.PathNoSeparator = y.PathNoSeparator)}
|
||||||
|
Dim isArr As Boolean = ObjectType <> YouTubeMediaType.Single And ObjectType <> YouTubeMediaType.Undefined
|
||||||
|
If isArr And AbsolutePath Then paths.ListAddValue(File, l)
|
||||||
File.Delete(SFO.File, dMode, fErr)
|
File.Delete(SFO.File, dMode, fErr)
|
||||||
|
If isArr Then paths.ListAddValue(ThumbnailFile, l)
|
||||||
ThumbnailFile.Delete(SFO.File, dMode, fErr)
|
ThumbnailFile.Delete(SFO.File, dMode, fErr)
|
||||||
If Files.Count > 0 Then Files.ForEach(Sub(f) f.Delete(SFO.File, dMode, fErr))
|
If Files.Count > 0 Then
|
||||||
|
If isArr Then paths.ListAddList(Files, l)
|
||||||
|
Files.ForEach(Sub(f) f.Delete(SFO.File, dMode, fErr))
|
||||||
|
End If
|
||||||
|
If paths.Count > 0 Then paths.ForEach(Sub(p) If SFile.GetFiles(p,, IO.SearchOption.AllDirectories, EDP.ReturnValue).Count = 0 Then _
|
||||||
|
p.Delete(SFO.Path, dMode, EDP.SendToLog))
|
||||||
End If
|
End If
|
||||||
If HasElements Then Elements.ForEach(Sub(e) e.Delete(RemoveFiles))
|
If HasElements Then Elements.ForEach(Sub(e) e.Delete(RemoveFiles))
|
||||||
End Sub
|
End Sub
|
||||||
@@ -751,6 +866,23 @@ Namespace API.YouTube.Objects
|
|||||||
Return Nothing
|
Return Nothing
|
||||||
End Try
|
End Try
|
||||||
End Function
|
End Function
|
||||||
|
Private Function GetPlaylistRow(ByVal Element As YouTubeMediaContainerBase, Optional ByVal __file As SFile = Nothing) As String
|
||||||
|
Const m3u8DataRow$ = "#EXTINF:{0},{1}" & vbCrLf & "{2}"
|
||||||
|
With Element
|
||||||
|
Dim f As SFile = __file.IfNullOrEmpty(.File)
|
||||||
|
Dim fName$ = .Title.IfNullOrEmpty(f.Name)
|
||||||
|
If MyYouTubeSettings.MusicPlaylistCreate_M3U8_AppendNumber And .PlaylistIndex > 0 Then fName = $"{ .PlaylistIndex}. {fName}"
|
||||||
|
If Not .UserTitle.IsEmptyString Then
|
||||||
|
fName = $"{ .UserTitle} - {fName}"
|
||||||
|
If MyYouTubeSettings.MusicPlaylistCreate_M3U8_AppendArtist Then fName = $"{ .UserTitle} - {fName}"
|
||||||
|
End If
|
||||||
|
If MyYouTubeSettings.MusicPlaylistCreate_M3U8_AppendExt Then fName &= $".{f.Extension}"
|
||||||
|
Return String.Format(m3u8DataRow,
|
||||||
|
CInt(.Duration.TotalSeconds),
|
||||||
|
fName,
|
||||||
|
$"file:///{SymbolsConverter.ASCII.EncodeSymbolsOnly(f)}")
|
||||||
|
End With
|
||||||
|
End Function
|
||||||
Private ReadOnly DownloadProgressPattern As RParams = RParams.DMS("\[download\]\s*([\d\.,]+)", 1, EDP.ReturnValue)
|
Private ReadOnly DownloadProgressPattern As RParams = RParams.DMS("\[download\]\s*([\d\.,]+)", 1, EDP.ReturnValue)
|
||||||
Public Property Progress As MyProgress Implements IYouTubeMediaContainer.Progress
|
Public Property Progress As MyProgress Implements IYouTubeMediaContainer.Progress
|
||||||
Private Property IDownloadableMedia_Progress As Object Implements IDownloadableMedia.Progress
|
Private Property IDownloadableMedia_Progress As Object Implements IDownloadableMedia.Progress
|
||||||
@@ -785,6 +917,32 @@ Namespace API.YouTube.Objects
|
|||||||
DownloadCommand(UseCookies, Token)
|
DownloadCommand(UseCookies, Token)
|
||||||
Else
|
Else
|
||||||
DownloadCommandArray(UseCookies, Token)
|
DownloadCommandArray(UseCookies, Token)
|
||||||
|
If HasElements AndAlso Elements(0).ObjectType = YouTubeMediaType.Single AndAlso Elements(0).IsMusic Then
|
||||||
|
Dim t As TextSaver = Nothing
|
||||||
|
Try
|
||||||
|
Dim f As SFile
|
||||||
|
If MyYouTubeSettings.MusicPlaylistCreate_M3U8 Then
|
||||||
|
t = New TextSaver
|
||||||
|
t.AppendLine("#EXTM3U")
|
||||||
|
Elements.ForEach(Sub(e) t.AppendLine(GetPlaylistRow(e)))
|
||||||
|
f = $"{Elements(0).File.PathWithSeparator}Playlist.m3u8"
|
||||||
|
t.SaveAs(f, EDP.SendToLog)
|
||||||
|
If f.Exists Then AddFile(f)
|
||||||
|
t.Dispose()
|
||||||
|
End If
|
||||||
|
If MyYouTubeSettings.MusicPlaylistCreate_M3U Then
|
||||||
|
t = New TextSaver
|
||||||
|
Elements.ForEach(Sub(e) t.AppendLine(e.File))
|
||||||
|
f = $"{Elements(0).File.PathWithSeparator}Playlist.m3u"
|
||||||
|
t.SaveAs(f, EDP.SendToLog)
|
||||||
|
If f.Exists Then AddFile(f)
|
||||||
|
t.Dispose()
|
||||||
|
End If
|
||||||
|
Catch ex As Exception
|
||||||
|
ErrorsDescriber.Execute(EDP.SendToLog, ex, "[YouTubeMediaContainerBase.Download.CreatePlaylist]")
|
||||||
|
End Try
|
||||||
|
t.DisposeIfReady
|
||||||
|
End If
|
||||||
End If
|
End If
|
||||||
RaiseEvent DataDownloaded(Me, Nothing)
|
RaiseEvent DataDownloaded(Me, Nothing)
|
||||||
End Sub
|
End Sub
|
||||||
@@ -854,7 +1012,7 @@ Namespace API.YouTube.Objects
|
|||||||
ff.Name = "album"
|
ff.Name = "album"
|
||||||
ff.Extension = "url"
|
ff.Extension = "url"
|
||||||
CreateUrlFile(url, ff)
|
CreateUrlFile(url, ff)
|
||||||
If ff.Exists Then Files.Add(ff)
|
If ff.Exists Then AddFile(ff)
|
||||||
End If
|
End If
|
||||||
If MyYouTubeSettings.CreateThumbnails_Music Then
|
If MyYouTubeSettings.CreateThumbnails_Music Then
|
||||||
Using resp As New Responser
|
Using resp As New Responser
|
||||||
@@ -880,7 +1038,7 @@ Namespace API.YouTube.Objects
|
|||||||
url = LinkFormatterSecure(u)
|
url = LinkFormatterSecure(u)
|
||||||
f.Name = "cover"
|
f.Name = "cover"
|
||||||
f.Extension = "jpg"
|
f.Extension = "jpg"
|
||||||
If resp.DownloadFile(url, f, EDP.ReturnValue) And f.Exists Then CoverDownloaded = True
|
If resp.DownloadFile(url, f, EDP.ReturnValue) And f.Exists Then CoverDownloaded = True : AddFile(f)
|
||||||
End If
|
End If
|
||||||
End If
|
End If
|
||||||
End Using
|
End Using
|
||||||
@@ -889,19 +1047,61 @@ Namespace API.YouTube.Objects
|
|||||||
ErrorsDescriber.Execute(EDP.SendToLog, ex, $"DownloadPlaylistCover({PlsId}, {f})")
|
ErrorsDescriber.Execute(EDP.SendToLog, ex, $"DownloadPlaylistCover({PlsId}, {f})")
|
||||||
End Try
|
End Try
|
||||||
End Sub
|
End Sub
|
||||||
|
Private Structure TempFileConversion
|
||||||
|
Friend File As SFile
|
||||||
|
Friend Requested As Boolean
|
||||||
|
Friend ToReplace As Boolean
|
||||||
|
Friend ReadOnly Property Exists As Boolean
|
||||||
|
Get
|
||||||
|
Return File.Exists
|
||||||
|
End Get
|
||||||
|
End Property
|
||||||
|
Friend Sub Delete()
|
||||||
|
If Not Requested Then File.Delete()
|
||||||
|
End Sub
|
||||||
|
Private Sub New(ByVal f As SFile)
|
||||||
|
File = f
|
||||||
|
Requested = False
|
||||||
|
ToReplace = False
|
||||||
|
End Sub
|
||||||
|
Friend Sub New(ByVal f As SFile, ByVal Source As YouTubeMediaContainerBase)
|
||||||
|
Me.New(f)
|
||||||
|
Requested = Source.PostProcessing_OutputAudioFormats.Count > 0 AndAlso
|
||||||
|
Source.PostProcessing_OutputAudioFormats.Exists(Function(af) af.StringToLower = f.Extension)
|
||||||
|
End Sub
|
||||||
|
Public Shared Widening Operator CType(ByVal f As SFile) As TempFileConversion
|
||||||
|
Return New TempFileConversion(f)
|
||||||
|
End Operator
|
||||||
|
Public Shared Widening Operator CType(ByVal f As TempFileConversion) As SFile
|
||||||
|
Return f.File
|
||||||
|
End Operator
|
||||||
|
Public Overrides Function Equals(ByVal Obj As Object) As Boolean
|
||||||
|
If Not IsNothing(Obj) Then
|
||||||
|
If TypeOf Obj Is TempFileConversion Then
|
||||||
|
Return DirectCast(Obj, TempFileConversion).File = File
|
||||||
|
ElseIf TypeOf Obj Is SFile Then
|
||||||
|
Return DirectCast(Obj, SFile) = File
|
||||||
|
ElseIf TypeOf Obj Is String Then
|
||||||
|
Return New TempFileConversion(CStr(Obj)).File = File
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
Return False
|
||||||
|
End Function
|
||||||
|
End Structure
|
||||||
Protected Sub DownloadCommand(ByVal UseCookies As Boolean, ByVal Token As CancellationToken)
|
Protected Sub DownloadCommand(ByVal UseCookies As Boolean, ByVal Token As CancellationToken)
|
||||||
Dim dCommand$ = String.Empty
|
Dim dCommand$ = String.Empty
|
||||||
Try
|
Try
|
||||||
ThrowAny(Token)
|
ThrowAny(Token)
|
||||||
If MediaState = UMStates.Downloaded Or Not Checked Then Exit Sub
|
If MediaState = UMStates.Downloaded Or Not Checked Then Exit Sub
|
||||||
Dim h As DataReceivedEventHandler = Sub(ByVal Sender As Object, ByVal e As DataReceivedEventArgs)
|
|
||||||
If Not e.Data.IsEmptyString Then
|
|
||||||
Dim v# = AConvert(Of Double)(RegexReplace(e.Data, DownloadProgressPattern), NumberProvider, -1)
|
|
||||||
If v >= 0 Then Progress.Value = v : Progress.Perform(0)
|
|
||||||
End If
|
|
||||||
End Sub
|
|
||||||
RaiseEvent FileDownloadStarted(Me, Nothing)
|
RaiseEvent FileDownloadStarted(Me, Nothing)
|
||||||
Using batch As New BatchExecutor(True) With {.Encoding = 65001}
|
Using batch As New BatchExecutor(True) With {.Encoding = 65001}
|
||||||
|
Dim h As DataReceivedEventHandler = Sub(ByVal Sender As Object, ByVal e As DataReceivedEventArgs)
|
||||||
|
If Not e.Data.IsEmptyString Then
|
||||||
|
Dim v# = AConvert(Of Double)(RegexReplace(e.Data, DownloadProgressPattern), NumberProvider, -1)
|
||||||
|
If v >= 0 Then Progress.Value = v : Progress.Perform(0)
|
||||||
|
If Token.IsCancellationRequested Then batch.Kill()
|
||||||
|
End If
|
||||||
|
End Sub
|
||||||
With batch
|
With batch
|
||||||
Dim prExists As Boolean = Not Progress Is Nothing
|
Dim prExists As Boolean = Not Progress Is Nothing
|
||||||
If prExists Then
|
If prExists Then
|
||||||
@@ -914,7 +1114,7 @@ Namespace API.YouTube.Objects
|
|||||||
.Information = $"Download {MediaType}"
|
.Information = $"Download {MediaType}"
|
||||||
End With
|
End With
|
||||||
End If
|
End If
|
||||||
.MainProcessName = "yt-dlp"
|
.MainProcessName = MyYouTubeSettings.YTDLP.Name '"yt-dlp"
|
||||||
.FileExchanger = MyCache.NewInstance(Of BatchFileExchanger)(CachePath, EDP.ReturnValue)
|
.FileExchanger = MyCache.NewInstance(Of BatchFileExchanger)(CachePath, EDP.ReturnValue)
|
||||||
.FileExchanger.DeleteCacheOnDispose = True
|
.FileExchanger.DeleteCacheOnDispose = True
|
||||||
.AddCommand("chcp 65001")
|
.AddCommand("chcp 65001")
|
||||||
@@ -934,18 +1134,20 @@ Namespace API.YouTube.Objects
|
|||||||
If Not File.Exists Then _File.Name = File.File
|
If Not File.Exists Then _File.Name = File.File
|
||||||
If File.Exists Then
|
If File.Exists Then
|
||||||
|
|
||||||
|
M3U8_Append()
|
||||||
|
|
||||||
If DownloadObjects.STDownloader.MyDownloaderSettings.CreateUrlFiles Then
|
If DownloadObjects.STDownloader.MyDownloaderSettings.CreateUrlFiles Then
|
||||||
Dim fileUrl As SFile = File
|
Dim fileUrl As SFile = File
|
||||||
fileUrl.Extension = "url"
|
fileUrl.Extension = "url"
|
||||||
CreateUrlFile(URL, fileUrl)
|
CreateUrlFile(URL, fileUrl)
|
||||||
If fileUrl.Exists Then Files.Add(fileUrl)
|
If fileUrl.Exists Then AddFile(fileUrl)
|
||||||
End If
|
End If
|
||||||
|
|
||||||
If MyYouTubeSettings.CreateDescriptionFiles And Not Description.IsEmptyString Then
|
If MyYouTubeSettings.CreateDescriptionFiles And Not Description.IsEmptyString Then
|
||||||
Dim fileDesr As SFile = File
|
Dim fileDesr As SFile = File
|
||||||
fileDesr.Extension = "txt"
|
fileDesr.Extension = "txt"
|
||||||
TextSaver.SaveTextToFile(Description, fileDesr,,, EDP.None)
|
TextSaver.SaveTextToFile(Description, fileDesr,,, EDP.None)
|
||||||
If fileDesr.Exists Then Files.Add(fileDesr)
|
If fileDesr.Exists Then AddFile(fileDesr)
|
||||||
End If
|
End If
|
||||||
|
|
||||||
If PlaylistCount > 0 And Not CoverDownloaded And Not PlaylistID.IsEmptyString Then DownloadPlaylistCover(PlaylistID, File, UseCookies)
|
If PlaylistCount > 0 And Not CoverDownloaded And Not PlaylistID.IsEmptyString Then DownloadPlaylistCover(PlaylistID, File, UseCookies)
|
||||||
@@ -969,66 +1171,164 @@ Namespace API.YouTube.Objects
|
|||||||
Dim format$
|
Dim format$
|
||||||
Dim fPattern$ = $"{File.PathWithSeparator}{File.Name}." & "{0}"
|
Dim fPattern$ = $"{File.PathWithSeparator}{File.Name}." & "{0}"
|
||||||
Dim fPatternFiles$ = $"{File.Name}*." & "{0}"
|
Dim fPatternFiles$ = $"{File.Name}*." & "{0}"
|
||||||
Dim fAacAudio As New SFile(String.Format(fPattern, aac))
|
Dim fAacAudio As New TempFileConversion(New SFile(String.Format(fPattern, aac)), Me)
|
||||||
Dim fAc3Audio As New SFile(String.Format(fPattern, ac3))
|
Dim mp3ThumbEmbedded As Boolean = False
|
||||||
Dim aacRequested As Boolean = PostProcessing_OutputAudioFormats.Count > 0 AndAlso
|
|
||||||
PostProcessing_OutputAudioFormats.Exists(Function(af) af.StringToLower = aac)
|
|
||||||
Dim ac3Requested As Boolean = PostProcessing_OutputAudioFormats.Count > 0 AndAlso
|
|
||||||
PostProcessing_OutputAudioFormats.Exists(Function(af) af.StringToLower = ac3)
|
|
||||||
|
|
||||||
|
Dim tempFilesList As New List(Of TempFileConversion)
|
||||||
|
Dim ttFile As TempFileConversion
|
||||||
|
|
||||||
|
Dim __updateBitrate As Boolean = OutputAudioBitrate > 0 AndAlso (SelectedAudioIndex = -1 OrElse SelectedAudio.Bitrate <> OutputAudioBitrate)
|
||||||
|
If __updateBitrate Then Bitrate = OutputAudioBitrate
|
||||||
|
Dim updateBitrate As Action(Of SFile) =
|
||||||
|
Sub(ByVal sourceFile As SFile)
|
||||||
|
If __updateBitrate AndAlso sourceFile.Exists Then
|
||||||
|
Dim destFile As SFile = sourceFile
|
||||||
|
destFile.Name &= "_new00"
|
||||||
|
.Execute($"ffmpeg -i ""{sourceFile}"" -crf {MyYouTubeSettings.DefaultAudioBitrate_crf.Value} -b:a {OutputAudioBitrate}k ""{destFile}""")
|
||||||
|
If destFile.Exists AndAlso sourceFile.Delete Then SFile.Rename(destFile, sourceFile)
|
||||||
|
End If
|
||||||
|
End Sub
|
||||||
|
Dim __getAAC_tried As Boolean = False
|
||||||
|
Dim AACExists As Func(Of Boolean) = Function() As Boolean
|
||||||
|
If Not __getAAC_tried Then
|
||||||
|
__getAAC_tried = True
|
||||||
|
.Execute($"ffmpeg -i ""{File}"" -vn -acodec {aac} ""{fAacAudio.File}""")
|
||||||
|
tempFilesList.Add(fAacAudio)
|
||||||
|
updateBitrate.Invoke(fAacAudio.File)
|
||||||
|
End If
|
||||||
|
Return fAacAudio.Exists
|
||||||
|
End Function
|
||||||
|
Dim tryToConvert As Action(Of String, SFile) =
|
||||||
|
Sub(ByVal codec As String, ByVal dFile As SFile)
|
||||||
|
ThrowAny(Token)
|
||||||
|
.Execute($"ffmpeg -i ""{File}"" -vn -acodec {codec} ""{dFile}""")
|
||||||
|
If Not codec = aac AndAlso Not dFile.Exists AndAlso AACExists.Invoke Then
|
||||||
|
ThrowAny(Token)
|
||||||
|
.Execute($"ffmpeg -i ""{fAacAudio.File}"" -f {codec} ""{dFile}""")
|
||||||
|
End If
|
||||||
|
End Sub
|
||||||
|
Dim embedThumbTo As Action(Of SFile) =
|
||||||
|
Sub(ByVal dFile As SFile)
|
||||||
|
If dFile.Exists And ThumbnailFile.Exists Then
|
||||||
|
Dim dFileNew As SFile = dFile
|
||||||
|
dFileNew.Name &= "_NEW"
|
||||||
|
.Execute($"ffmpeg -i ""{dFile}"" -i ""{ThumbnailFile}"" -map 0:0 -map 1:0 -c copy -id3v2_version 3 -metadata:s:v title=""Cover"" -metadata:s:v comment=""Cover"" ""{dFileNew}""")
|
||||||
|
If dFileNew.Exists AndAlso dFile.Delete(,, EDP.ReturnValue) Then SFile.Rename(dFileNew, dFile)
|
||||||
|
End If
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
'Subtitles
|
||||||
ThrowAny(Token)
|
ThrowAny(Token)
|
||||||
If PostProcessing_OutputSubtitlesFormats.Count > 0 Then
|
If SubtitlesSelectedIndexes.Count > 0 And Not OutputSubtitlesFormat.IsEmptyString Then
|
||||||
files = SFile.GetFiles(File, String.Format(fPatternFiles, OutputSubtitlesFormat.StringToLower),, EDP.ReturnValue)
|
files = SFile.GetFiles(File, String.Format(fPatternFiles, OutputSubtitlesFormat.StringToLower),, EDP.ReturnValue)
|
||||||
If files.ListExists Then
|
If files.ListExists Then
|
||||||
For Each f In files
|
AddFile(files)
|
||||||
For Each format In PostProcessing_OutputSubtitlesFormats
|
If PostProcessing_OutputSubtitlesFormats.Count > 0 Then
|
||||||
format = format.StringToLower
|
For Each f In files
|
||||||
commandFile = $"{f.PathWithSeparator}{f.Name}.{format}"
|
For Each format In PostProcessing_OutputSubtitlesFormats
|
||||||
Me.Files.Add(commandFile)
|
format = format.StringToLower
|
||||||
ThrowAny(Token)
|
commandFile = $"{f.PathWithSeparator}{f.Name}.{format}"
|
||||||
.Execute($"ffmpeg -i ""{f}"" ""{commandFile}""")
|
AddFile(commandFile)
|
||||||
|
ThrowAny(Token)
|
||||||
|
.Execute($"ffmpeg -i ""{f}"" ""{commandFile}""")
|
||||||
|
Next
|
||||||
Next
|
Next
|
||||||
Next
|
End If
|
||||||
End If
|
End If
|
||||||
End If
|
End If
|
||||||
|
|
||||||
|
'Audio
|
||||||
ThrowAny(Token)
|
ThrowAny(Token)
|
||||||
If PostProcessing_OutputAudioFormats.Count > 0 Or PostProcessing_AudioAC3 Then
|
If PostProcessing_OutputAudioFormats.Count > 0 Or PostProcessing_AudioAC3 Or PostProcessing_AudioMP3 Or __updateBitrate Then
|
||||||
If Not fAacAudio.Exists Then .Execute($"ffmpeg -i ""{File}"" -vn -acodec {aac} ""{fAacAudio}""")
|
|
||||||
If PostProcessing_AudioAC3 And Not fAc3Audio.Exists Then
|
If PostProcessing_AudioAC3 Then
|
||||||
ThrowAny(Token)
|
ttFile = New TempFileConversion(New SFile(String.Format(fPattern, ac3)), Me) With {.ToReplace = True}
|
||||||
.Execute($"ffmpeg -i ""{File}"" -vn -acodec {ac3} ""{fAc3Audio}""")
|
tempFilesList.Add(ttFile)
|
||||||
If Not fAc3Audio.Exists And fAacAudio.Exists Then ThrowAny(Token) : .Execute($"ffmpeg -i ""{fAacAudio}"" -f {ac3} ""{fAc3Audio}""")
|
If Not ttFile.Exists Then tryToConvert.Invoke(ac3, ttFile.File)
|
||||||
|
updateBitrate.Invoke(ttFile.File)
|
||||||
End If
|
End If
|
||||||
|
|
||||||
|
If PostProcessing_AudioMP3 Then
|
||||||
|
ttFile = New TempFileConversion(New SFile(String.Format(fPattern, mp3)), Me) With {.ToReplace = True}
|
||||||
|
tempFilesList.Add(ttFile)
|
||||||
|
If Not ttFile.Requested Then ttFile.Requested = SelectedVideoIndex = -1 And OutputAudioCodec.StringToLower = mp3
|
||||||
|
If Not ttFile.Exists Then tryToConvert.Invoke(mp3, ttFile.File)
|
||||||
|
updateBitrate.Invoke(ttFile.File)
|
||||||
|
embedThumbTo.Invoke(ttFile.File)
|
||||||
|
mp3ThumbEmbedded = True
|
||||||
|
End If
|
||||||
|
|
||||||
|
If __updateBitrate Then
|
||||||
|
format = OutputAudioCodec.StringToLower
|
||||||
|
If Not format.IsEmptyString Then
|
||||||
|
f = String.Format(fPattern, format)
|
||||||
|
ttFile = New TempFileConversion(f, Me) With {.ToReplace = True}
|
||||||
|
If Not ttFile.Requested Then ttFile.Requested = SelectedVideoIndex = -1
|
||||||
|
If Not f.Exists Then
|
||||||
|
tempFilesList.ListAddValue(ttFile, LAP.NotContainsOnly)
|
||||||
|
tryToConvert.Invoke(format, f)
|
||||||
|
updateBitrate.Invoke(f)
|
||||||
|
ElseIf Not tempFilesList.Contains(ttFile) Then
|
||||||
|
tempFilesList.Add(ttFile)
|
||||||
|
updateBitrate.Invoke(f)
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
|
||||||
If PostProcessing_OutputAudioFormats.Count > 0 Then
|
If PostProcessing_OutputAudioFormats.Count > 0 Then
|
||||||
For Each format In PostProcessing_OutputAudioFormats
|
For Each format In PostProcessing_OutputAudioFormats
|
||||||
format = format.StringToLower
|
format = format.StringToLower
|
||||||
f = String.Format(fPattern, format)
|
f = String.Format(fPattern, format)
|
||||||
Me.Files.Add(f)
|
AddFile(f)
|
||||||
If Not format = ac3 Or Not f.Exists Then ThrowAny(Token) : .Execute($"ffmpeg -i ""{fAacAudio}"" -f {format} ""{f}""")
|
If Not f.Exists Then
|
||||||
|
tryToConvert.Invoke(format, f)
|
||||||
|
updateBitrate(f)
|
||||||
|
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)
|
||||||
|
End If
|
||||||
Next
|
Next
|
||||||
End If
|
End If
|
||||||
End If
|
End If
|
||||||
|
|
||||||
|
'Update video
|
||||||
ThrowAny(Token)
|
ThrowAny(Token)
|
||||||
If PostProcessing_AudioAC3 Then
|
If SelectedVideoIndex >= 0 AndAlso tempFilesList.Count > 0 AndAlso tempFilesList.Exists(Function(tf) tf.ToReplace) Then
|
||||||
f = File
|
f = File
|
||||||
If SelectedVideoIndex >= 0 Then
|
f.Name &= "tmp00"
|
||||||
f.Name &= "tmp00"
|
Dim tfr As SFile = tempFilesList.FirstOrDefault(Function(tf) tf.ToReplace).File
|
||||||
Else
|
If tfr.Exists And Not f.Exists Then
|
||||||
f.Extension = ac3
|
ThrowAny(Token)
|
||||||
|
.Execute($"ffmpeg -i ""{File}"" -i ""{tfr}"" -c:v copy -c copy -map 0:v:0 -map 1:a:0 ""{f}""")
|
||||||
End If
|
End If
|
||||||
If Not f.Exists Then ThrowAny(Token) : .Execute($"ffmpeg -i ""{File}"" -i ""{fAc3Audio}"" -c:v copy -c copy -map 0:v:0 -map 1:a:0 ""{f}""")
|
|
||||||
If f.Exists Then
|
If f.Exists Then
|
||||||
File.Delete()
|
File.Delete()
|
||||||
If SelectedVideoIndex >= 0 Then SFile.Rename(f, File,, EDP.LogMessageValue)
|
If SelectedVideoIndex >= 0 Then SFile.Rename(f, File,, EDP.LogMessageValue)
|
||||||
End If
|
End If
|
||||||
If fAacAudio.Exists And Not aacRequested Then fAacAudio.Delete()
|
End If
|
||||||
If fAc3Audio.Exists And Not ac3Requested And SelectedVideoIndex >= 0 Then fAc3Audio.Delete()
|
|
||||||
|
'Delete unrequsted files
|
||||||
|
If tempFilesList.Count > 0 Then tempFilesList.ForEach(Sub(tfr) If Not tfr.Requested Then tfr.File.Delete(,, EDP.None)) : tempFilesList.Clear()
|
||||||
|
|
||||||
|
'Update video FPS
|
||||||
|
If SelectedVideoIndex >= 0 AndAlso OutputVideoFPS > 0 AndAlso SelectedVideo.Bitrate <> OutputVideoFPS Then
|
||||||
|
f = File
|
||||||
|
f.Name &= "tmp00"
|
||||||
|
.Execute($"ffmpeg -i ""{File}"" -filter:v fps={OutputVideoFPS.ToString.Replace(",", ".")} -c:a copy ""{f}""")
|
||||||
|
If f.Exists Then
|
||||||
|
File.Delete()
|
||||||
|
SFile.Rename(f, File,, EDP.LogMessageValue)
|
||||||
|
End If
|
||||||
End If
|
End If
|
||||||
End If
|
End If
|
||||||
End If
|
End If
|
||||||
End With
|
End With
|
||||||
|
|
||||||
|
Dim newSize# = 0
|
||||||
|
If File.Exists Then newSize += File.Size
|
||||||
|
If Files.Count > 0 Then newSize += (From eFile As SFile In Files Where eFile.Exists Select eFile.Size).Sum
|
||||||
|
If ThumbnailFile.Exists Then newSize += ThumbnailFile.Size
|
||||||
|
If newSize > 0 Then newSize /= 1024 : Size = newSize : _SizeRecalculated = True
|
||||||
End Using
|
End Using
|
||||||
_MediaState = UMStates.Downloaded
|
_MediaState = UMStates.Downloaded
|
||||||
Catch oex As OperationCanceledException When Token.IsCancellationRequested
|
Catch oex As OperationCanceledException When Token.IsCancellationRequested
|
||||||
@@ -1045,6 +1345,33 @@ Namespace API.YouTube.Objects
|
|||||||
End If
|
End If
|
||||||
End Try
|
End Try
|
||||||
End Sub
|
End Sub
|
||||||
|
Private Sub M3U8_Append(Optional ByVal __file As SFile = Nothing)
|
||||||
|
If M3U8_PlaylistFiles.ListExists Then
|
||||||
|
For Each m3u8_file As SFile In M3U8_PlaylistFiles
|
||||||
|
If Not m3u8_file.IsEmptyString Then
|
||||||
|
Dim m3u8Row$ = String.Empty
|
||||||
|
If Not m3u8_file.Extension.IsEmptyString Then
|
||||||
|
If m3u8_file.Extension.ToLower = "m3u8" Then
|
||||||
|
m3u8Row = GetPlaylistRow(Me, __file)
|
||||||
|
ElseIf m3u8_file.Extension.ToLower = "m3u" Then
|
||||||
|
m3u8Row = __file.IfNullOrEmpty(File).ToString
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
If Not m3u8Row.IsEmptyString Then
|
||||||
|
Dim m3u8Text$
|
||||||
|
If m3u8_file.Exists Then
|
||||||
|
m3u8Text = m3u8_file.GetText
|
||||||
|
m3u8_file.Delete(SFO.File, SFODelete.DeleteToRecycleBin, EDP.SendToLog)
|
||||||
|
Else
|
||||||
|
m3u8Text = "#EXTM3U"
|
||||||
|
End If
|
||||||
|
m3u8Text.StringAppendLine(m3u8Row, vbCrLf)
|
||||||
|
TextSaver.SaveTextToFile(m3u8Text, m3u8_file,,, EDP.SendToLog)
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
Next
|
||||||
|
End If
|
||||||
|
End Sub
|
||||||
#End Region
|
#End Region
|
||||||
#Region "Load"
|
#Region "Load"
|
||||||
Private Sub ApplyElementCheckedValue(ByVal e As EContainer)
|
Private Sub ApplyElementCheckedValue(ByVal e As EContainer)
|
||||||
@@ -1174,6 +1501,7 @@ Namespace API.YouTube.Objects
|
|||||||
End Sub
|
End Sub
|
||||||
#End Region
|
#End Region
|
||||||
#Region "Parse"
|
#Region "Parse"
|
||||||
|
Friend Const DRC As String = "drc"
|
||||||
Public Overridable Function Parse(ByVal Container As EContainer, ByVal Path As SFile, ByVal IsMusic As Boolean,
|
Public Overridable Function Parse(ByVal Container As EContainer, ByVal Path As SFile, ByVal IsMusic As Boolean,
|
||||||
Optional ByVal Token As CancellationToken = Nothing, Optional ByVal Progress As IMyProgress = Nothing) As Boolean Implements IYouTubeMediaContainer.Parse
|
Optional ByVal Token As CancellationToken = Nothing, Optional ByVal Progress As IMyProgress = Nothing) As Boolean Implements IYouTubeMediaContainer.Parse
|
||||||
Try
|
Try
|
||||||
@@ -1236,6 +1564,7 @@ Namespace API.YouTube.Objects
|
|||||||
_File.Name = $"{ID}.{ext}"
|
_File.Name = $"{ID}.{ext}"
|
||||||
End If
|
End If
|
||||||
If Not MyYouTubeSettings.OutputPath.IsEmptyString Then _File.Path = MyYouTubeSettings.OutputPath.Value.Path
|
If Not MyYouTubeSettings.OutputPath.IsEmptyString Then _File.Path = MyYouTubeSettings.OutputPath.Value.Path
|
||||||
|
_File = CleanFileName(_File)
|
||||||
File = _File
|
File = _File
|
||||||
|
|
||||||
If .Contains("duration") Then
|
If .Contains("duration") Then
|
||||||
@@ -1325,7 +1654,8 @@ Namespace API.YouTube.Objects
|
|||||||
obj = New MediaObject With {
|
obj = New MediaObject With {
|
||||||
.ID = ee.Value("format_id"),
|
.ID = ee.Value("format_id"),
|
||||||
.URL = ee.Value("url"),
|
.URL = ee.Value("url"),
|
||||||
.Extension = ee.Value("ext")
|
.Extension = ee.Value("ext"),
|
||||||
|
.ID_DRC = Not .ID.IsEmptyString AndAlso .ID.StringToLower.Contains(DRC)
|
||||||
}
|
}
|
||||||
obj.Width = AConvert(Of Integer)(ee.Value("width"), NumberProvider, -1)
|
obj.Width = AConvert(Of Integer)(ee.Value("width"), NumberProvider, -1)
|
||||||
obj.Height = AConvert(Of Integer)(ee.Value("height"), NumberProvider, -1)
|
obj.Height = AConvert(Of Integer)(ee.Value("height"), NumberProvider, -1)
|
||||||
@@ -1380,6 +1710,14 @@ Namespace API.YouTube.Objects
|
|||||||
If MediaObjects.Count > 0 AndAlso MediaObjects.LongCount(CountAVC) > 0 Then MediaObjects.RemoveAll(RemoveAVC)
|
If MediaObjects.Count > 0 AndAlso MediaObjects.LongCount(CountAVC) > 0 Then MediaObjects.RemoveAll(RemoveAVC)
|
||||||
Next
|
Next
|
||||||
End If
|
End If
|
||||||
|
If t = UMTypes.Audio And MediaObjects.Count > 0 Then
|
||||||
|
Dim __audioComparerCount As Func(Of MediaObject, MediaObject, Boolean) =
|
||||||
|
Function(mo, mo2) (mo2.Type = t And mo2.Extension = mo.Extension And mo2.Bitrate = mo.Bitrate) AndAlso
|
||||||
|
mo2.Size.RoundDown = mo.Size.RoundDown AndAlso ACheck(Of Integer)(mo2.ID)
|
||||||
|
Dim RemoveDRC As Predicate(Of MediaObject) = Function(mo) mo.Type = t AndAlso Not ACheck(Of Integer)(mo.ID) AndAlso
|
||||||
|
MediaObjects.LongCount(Function(mo2) __audioComparerCount.Invoke(mo, mo2)) > 0
|
||||||
|
MediaObjects.RemoveAll(RemoveDRC)
|
||||||
|
End If
|
||||||
End Sub
|
End Sub
|
||||||
Dim protocolCleaner As Action =
|
Dim protocolCleaner As Action =
|
||||||
Sub()
|
Sub()
|
||||||
@@ -1561,7 +1899,7 @@ Namespace API.YouTube.Objects
|
|||||||
_SubtitlesDelegated.Clear()
|
_SubtitlesDelegated.Clear()
|
||||||
SubtitlesSelectedIndexes.Clear()
|
SubtitlesSelectedIndexes.Clear()
|
||||||
MediaObjects.Clear()
|
MediaObjects.Clear()
|
||||||
Files.Clear()
|
_Files.Clear()
|
||||||
PostProcessing_OutputAudioFormats.Clear()
|
PostProcessing_OutputAudioFormats.Clear()
|
||||||
PostProcessing_OutputSubtitlesFormats.Clear()
|
PostProcessing_OutputSubtitlesFormats.Clear()
|
||||||
End If
|
End If
|
||||||
|
|||||||
@@ -32,6 +32,6 @@ Imports System.Runtime.InteropServices
|
|||||||
' by using the '*' as shown below:
|
' by using the '*' as shown below:
|
||||||
' <Assembly: AssemblyVersion("1.0.*")>
|
' <Assembly: AssemblyVersion("1.0.*")>
|
||||||
|
|
||||||
<Assembly: AssemblyVersion("2023.12.14.0")>
|
<Assembly: AssemblyVersion("2024.5.4.0")>
|
||||||
<Assembly: AssemblyFileVersion("2023.12.14.0")>
|
<Assembly: AssemblyFileVersion("2024.5.4.0")>
|
||||||
<Assembly: NeutralResourcesLanguage("en")>
|
<Assembly: NeutralResourcesLanguage("en")>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
'
|
'
|
||||||
' This program is distributed in the hope that it will be useful,
|
' This program is distributed in the hope that it will be useful,
|
||||||
' but WITHOUT ANY WARRANTY
|
' but WITHOUT ANY WARRANTY
|
||||||
|
Imports System.Runtime.CompilerServices
|
||||||
Imports PersonalUtilities.Forms
|
Imports PersonalUtilities.Forms
|
||||||
Imports PersonalUtilities.Functions.RegularExpressions
|
Imports PersonalUtilities.Functions.RegularExpressions
|
||||||
Namespace API.Base
|
Namespace API.Base
|
||||||
@@ -49,7 +50,7 @@ Namespace API.Base
|
|||||||
End Sub
|
End Sub
|
||||||
Public Overrides Function Convert(ByVal Value As Object, ByVal DestinationType As Type, ByVal Provider As IFormatProvider,
|
Public Overrides Function Convert(ByVal Value As Object, ByVal DestinationType As Type, ByVal Provider As IFormatProvider,
|
||||||
Optional ByVal NothingArg As Object = Nothing, Optional ByVal e As ErrorsDescriber = Nothing) As Object
|
Optional ByVal NothingArg As Object = Nothing, Optional ByVal e As ErrorsDescriber = Nothing) As Object
|
||||||
Dim v% = AConvert(Of Integer)(Value, -1)
|
Dim v% = AConvert(Of Integer)(Value, -1, EDP.ReturnValue)
|
||||||
If v > 0 Then
|
If v > 0 Then
|
||||||
Return Value
|
Return Value
|
||||||
ElseIf Not ACheck(Of Integer)(Value) Then
|
ElseIf Not ACheck(Of Integer)(Value) Then
|
||||||
@@ -72,5 +73,12 @@ Namespace API.Base
|
|||||||
$"Current query: [{CurrentQuery}]{vbCr}New query: [{NewQuery}]",
|
$"Current query: [{CurrentQuery}]{vbCr}New query: [{NewQuery}]",
|
||||||
"Changing a query"}, vbExclamation,,, {"Process", "Cancel"}) = 0
|
"Changing a query"}, vbExclamation,,, {"Process", "Cancel"}) = 0
|
||||||
End Function
|
End Function
|
||||||
|
<Extension> Friend Function GetCookieValue(ByVal Cookies As IEnumerable(Of System.Net.Cookie), ByVal CookieName As String) As String
|
||||||
|
If Cookies.ListExists Then Return If(Cookies.FirstOrDefault(Function(c) c.Name.ToLower = CookieName.ToLower)?.Value, String.Empty) Else Return String.Empty
|
||||||
|
End Function
|
||||||
|
<Extension> Friend Function GetCookieValue(ByVal Cookies As IEnumerable(Of System.Net.Cookie), ByVal CookieName As String,
|
||||||
|
ByVal PropName As String, ByVal PropNameComp As String) As String
|
||||||
|
Return If(PropName = PropNameComp, Cookies.GetCookieValue(CookieName), String.Empty)
|
||||||
|
End Function
|
||||||
End Module
|
End Module
|
||||||
End Namespace
|
End Namespace
|
||||||
@@ -11,8 +11,6 @@ Namespace API.Base
|
|||||||
Friend Const Header_Authorization As String = "authorization"
|
Friend Const Header_Authorization As String = "authorization"
|
||||||
Friend Const Header_CSRFToken As String = "x-csrf-token"
|
Friend Const Header_CSRFToken As String = "x-csrf-token"
|
||||||
|
|
||||||
Friend Const Header_FB_FRIENDLY_NAME As String = "x-fb-friendly-name"
|
|
||||||
|
|
||||||
Friend Const ConcurrentDownloadsCaption As String = "Concurrent downloads"
|
Friend Const ConcurrentDownloadsCaption As String = "Concurrent downloads"
|
||||||
Friend Const ConcurrentDownloadsToolTip As String = "The number of concurrent downloads."
|
Friend Const ConcurrentDownloadsToolTip As String = "The number of concurrent downloads."
|
||||||
Friend Const SavedPostsUserNameCaption As String = "Saved posts user"
|
Friend Const SavedPostsUserNameCaption As String = "Saved posts user"
|
||||||
|
|||||||
@@ -59,7 +59,6 @@ Namespace API.Base
|
|||||||
ReadOnly Property DownloadedTotal(Optional ByVal Total As Boolean = True) As Integer
|
ReadOnly Property DownloadedTotal(Optional ByVal Total As Boolean = True) As Integer
|
||||||
ReadOnly Property DownloadedInformation As String
|
ReadOnly Property DownloadedInformation As String
|
||||||
Property HasError As Boolean
|
Property HasError As Boolean
|
||||||
ReadOnly Property FitToAddParams As Boolean
|
|
||||||
ReadOnly Property Key As String
|
ReadOnly Property Key As String
|
||||||
Property DownloadImages As Boolean
|
Property DownloadImages As Boolean
|
||||||
Property DownloadVideos As Boolean
|
Property DownloadVideos As Boolean
|
||||||
|
|||||||
@@ -42,6 +42,13 @@ Namespace API.Base
|
|||||||
Friend NotInheritable Class M3U8Base
|
Friend NotInheritable Class M3U8Base
|
||||||
Friend Const TempCacheFolderName As String = "tmpCache"
|
Friend Const TempCacheFolderName As String = "tmpCache"
|
||||||
Friend Const TempFilePrefix As String = "ConPart_"
|
Friend Const TempFilePrefix As String = "ConPart_"
|
||||||
|
Friend Const TempFileDefaultExtension As String = "ts"
|
||||||
|
''' <summary><c>SFileNumbers.NumberProviderDefault</c></summary>
|
||||||
|
Friend Shared ReadOnly Property NumberProviderDefault As ANumbers
|
||||||
|
Get
|
||||||
|
Return SFileNumbers.NumberProviderDefault
|
||||||
|
End Get
|
||||||
|
End Property
|
||||||
Private Sub New()
|
Private Sub New()
|
||||||
End Sub
|
End Sub
|
||||||
Friend Shared Function CreateUrl(ByVal Appender As String, ByVal File As String) As String
|
Friend Shared Function CreateUrl(ByVal Appender As String, ByVal File As String) As String
|
||||||
@@ -63,8 +70,7 @@ Namespace API.Base
|
|||||||
Friend Overloads Shared Function Download(ByVal URLs As List(Of M3U8URL), ByVal DestinationFile As SFile, Optional ByVal Responser As Responser = Nothing,
|
Friend Overloads Shared Function Download(ByVal URLs As List(Of M3U8URL), ByVal DestinationFile As SFile, Optional ByVal Responser As Responser = Nothing,
|
||||||
Optional ByVal Token As CancellationToken = Nothing, Optional ByVal Progress As MyProgress = Nothing,
|
Optional ByVal Token As CancellationToken = Nothing, Optional ByVal Progress As MyProgress = Nothing,
|
||||||
Optional ByVal UsePreProgress As Boolean = True, Optional ByVal ExistingCache As CacheKeeper = Nothing,
|
Optional ByVal UsePreProgress As Boolean = True, Optional ByVal ExistingCache As CacheKeeper = Nothing,
|
||||||
Optional ByVal OnlyDownload As Boolean = False) As SFile
|
Optional ByVal OnlyDownload As Boolean = False, Optional ByVal SkipBroken As Boolean = False) As SFile
|
||||||
Const defaultExtension$ = "ts"
|
|
||||||
Dim Cache As CacheKeeper = Nothing
|
Dim Cache As CacheKeeper = Nothing
|
||||||
Using tmpPr As New PreProgress(Progress)
|
Using tmpPr As New PreProgress(Progress)
|
||||||
Try
|
Try
|
||||||
@@ -89,13 +95,13 @@ Namespace API.Base
|
|||||||
End If
|
End If
|
||||||
End If
|
End If
|
||||||
Dim p As SFileNumbers = SFileNumbers.Default(ConcatFile.Name)
|
Dim p As SFileNumbers = SFileNumbers.Default(ConcatFile.Name)
|
||||||
Dim pNum As ANumbers = SFileNumbers.NumberProviderDefault
|
Dim pNum As ANumbers = NumberProviderDefault
|
||||||
p.NumberProvider = pNum
|
p.NumberProvider = pNum
|
||||||
DirectCast(p.NumberProvider, ANumbers).GroupSize = {URLs.Count.ToString.Length, 3}.Max
|
DirectCast(p.NumberProvider, ANumbers).GroupSize = {URLs.Count.ToString.Length, 3}.Max
|
||||||
ConcatFile = SFile.IndexReindex(ConcatFile,,, p, EDP.ReturnValue)
|
ConcatFile = SFile.IndexReindex(ConcatFile,,, p, EDP.ReturnValue)
|
||||||
Dim i%
|
Dim i%
|
||||||
Dim dFile As SFile = cache2.RootDirectory
|
Dim dFile As SFile = cache2.RootDirectory
|
||||||
dFile.Extension = defaultExtension
|
dFile.Extension = TempFileDefaultExtension
|
||||||
Using w As New DownloadObjects.WebClient2(Responser)
|
Using w As New DownloadObjects.WebClient2(Responser)
|
||||||
For i = 0 To URLs.Count - 1
|
For i = 0 To URLs.Count - 1
|
||||||
If progressExists Then
|
If progressExists Then
|
||||||
@@ -107,9 +113,13 @@ Namespace API.Base
|
|||||||
End If
|
End If
|
||||||
Token.ThrowIfCancellationRequested()
|
Token.ThrowIfCancellationRequested()
|
||||||
dFile.Name = $"{TempFilePrefix}{i.NumToString(pNum)}"
|
dFile.Name = $"{TempFilePrefix}{i.NumToString(pNum)}"
|
||||||
dFile.Extension = URLs(i).Extension.IfNullOrEmpty(defaultExtension)
|
dFile.Extension = URLs(i).Extension.IfNullOrEmpty(TempFileDefaultExtension)
|
||||||
w.DownloadFile(URLs(i).URL, dFile)
|
Try
|
||||||
cache2.AddFile(dFile, True)
|
w.DownloadFile(URLs(i).URL, dFile)
|
||||||
|
cache2.AddFile(dFile, True)
|
||||||
|
Catch ex As Exception
|
||||||
|
If Not SkipBroken Then Throw ex
|
||||||
|
End Try
|
||||||
Next
|
Next
|
||||||
End Using
|
End Using
|
||||||
If Not OnlyDownload Then _
|
If Not OnlyDownload Then _
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ Namespace API.Base
|
|||||||
Progress.InformationTemporary = $"{HOST.Name} ({c - s}/{c}) Images: {_TotalImages}; Videos: {_TotalVideos}"
|
Progress.InformationTemporary = $"{HOST.Name} ({c - s}/{c}) Images: {_TotalImages}; Videos: {_TotalVideos}"
|
||||||
End If
|
End If
|
||||||
End If
|
End If
|
||||||
If _FeedDataExists Then Downloader.Files.Sort()
|
If _FeedDataExists Then Downloader.Files.Sort() : Downloader.FilesSave()
|
||||||
End Sub
|
End Sub
|
||||||
Private Overloads Sub Download(ByVal Host As SettingsHost, ByVal Number As Integer, ByVal Count As Integer,
|
Private Overloads Sub Download(ByVal Host As SettingsHost, ByVal Number As Integer, ByVal Count As Integer,
|
||||||
ByVal Token As CancellationToken, ByVal Multiple As Boolean)
|
ByVal Token As CancellationToken, ByVal Multiple As Boolean)
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ Imports Download = SCrawler.Plugin.ISiteSettings.Download
|
|||||||
Namespace API.Base
|
Namespace API.Base
|
||||||
Friend MustInherit Class SiteSettingsBase : Implements ISiteSettings, IResponserContainer
|
Friend MustInherit Class SiteSettingsBase : Implements ISiteSettings, IResponserContainer
|
||||||
#Region "Declarations"
|
#Region "Declarations"
|
||||||
|
<PXML> Protected ReadOnly Property SettingsVersion As PropertyValue
|
||||||
Friend ReadOnly Property Site As String Implements ISiteSettings.Site
|
Friend ReadOnly Property Site As String Implements ISiteSettings.Site
|
||||||
Protected _Icon As Icon = Nothing
|
Protected _Icon As Icon = Nothing
|
||||||
Friend Overridable ReadOnly Property Icon As Icon Implements ISiteSettings.Icon
|
Friend Overridable ReadOnly Property Icon As Icon Implements ISiteSettings.Icon
|
||||||
@@ -54,7 +55,14 @@ Namespace API.Base
|
|||||||
End Set
|
End Set
|
||||||
End Property
|
End Property
|
||||||
#End Region
|
#End Region
|
||||||
|
#Region "EnvironmentPrograms"
|
||||||
|
Private Property CMDEncoding As String Implements ISiteSettings.CMDEncoding
|
||||||
|
Private Property EnvironmentPrograms As IEnumerable(Of String) Implements ISiteSettings.EnvironmentPrograms
|
||||||
|
Private Sub EnvironmentProgramsUpdated() Implements ISiteSettings.EnvironmentProgramsUpdated
|
||||||
|
End Sub
|
||||||
|
#End Region
|
||||||
#Region "Responser and cookies support"
|
#Region "Responser and cookies support"
|
||||||
|
Friend Const ResponserFilePrefix As String = "Responser_"
|
||||||
Private _CookiesNetscapeFile As SFile = Nothing
|
Private _CookiesNetscapeFile As SFile = Nothing
|
||||||
Friend ReadOnly Property CookiesNetscapeFile As SFile
|
Friend ReadOnly Property CookiesNetscapeFile As SFile
|
||||||
Get
|
Get
|
||||||
@@ -85,7 +93,7 @@ Namespace API.Base
|
|||||||
End Property
|
End Property
|
||||||
Protected Sub UpdateResponserFile()
|
Protected Sub UpdateResponserFile()
|
||||||
Dim acc$ = If(AccountName.IsEmptyString OrElse AccountName = Hosts.SettingsHost.NameAccountNameDefault, String.Empty, $"_{AccountName}")
|
Dim acc$ = If(AccountName.IsEmptyString OrElse AccountName = Hosts.SettingsHost.NameAccountNameDefault, String.Empty, $"_{AccountName}")
|
||||||
Responser.File = $"{SettingsFolderName}\Responser_{Site}{acc}.xml"
|
Responser.File = $"{SettingsFolderName}\{ResponserFilePrefix}{Site}{acc}.xml"
|
||||||
_CookiesNetscapeFile = Responser.File
|
_CookiesNetscapeFile = Responser.File
|
||||||
_CookiesNetscapeFile.Name &= "_Cookies_Netscape"
|
_CookiesNetscapeFile.Name &= "_Cookies_Netscape"
|
||||||
_CookiesNetscapeFile.Extension = "txt"
|
_CookiesNetscapeFile.Extension = "txt"
|
||||||
@@ -100,6 +108,7 @@ Namespace API.Base
|
|||||||
_Icon = __Icon
|
_Icon = __Icon
|
||||||
_Image = __Image
|
_Image = __Image
|
||||||
Responser = New Responser With {.DeclaredError = EDP.ThrowException}
|
Responser = New Responser With {.DeclaredError = EDP.ThrowException}
|
||||||
|
SettingsVersion = New PropertyValue(0)
|
||||||
UpdateResponserFile()
|
UpdateResponserFile()
|
||||||
End Sub
|
End Sub
|
||||||
Friend Sub New(ByVal SiteName As String, ByVal CookiesDomain As String, ByVal AccName As String, ByVal Temp As Boolean,
|
Friend Sub New(ByVal SiteName As String, ByVal CookiesDomain As String, ByVal AccName As String, ByVal Temp As Boolean,
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ Imports PersonalUtilities.Tools.Web.Clients
|
|||||||
Imports PersonalUtilities.Tools.ImageRenderer
|
Imports PersonalUtilities.Tools.ImageRenderer
|
||||||
Imports UStates = SCrawler.API.Base.UserMedia.States
|
Imports UStates = SCrawler.API.Base.UserMedia.States
|
||||||
Imports UTypes = SCrawler.API.Base.UserMedia.Types
|
Imports UTypes = SCrawler.API.Base.UserMedia.Types
|
||||||
|
Imports CookieUpdateModes = PersonalUtilities.Tools.Web.Cookies.CookieKeeper.UpdateModes
|
||||||
Namespace API.Base
|
Namespace API.Base
|
||||||
Friend MustInherit Class UserDataBase : Implements IUserData, IPluginContentProvider, IThrower
|
Friend MustInherit Class UserDataBase : Implements IUserData, IPluginContentProvider, IThrower
|
||||||
Friend Const UserFileAppender As String = "User"
|
Friend Const UserFileAppender As String = "User"
|
||||||
@@ -205,7 +206,7 @@ Namespace API.Base
|
|||||||
If Not h Is Nothing Then _HostKey = h.Key
|
If Not h Is Nothing Then _HostKey = h.Key
|
||||||
End Set
|
End Set
|
||||||
End Property
|
End Property
|
||||||
Private Sub ResetHost()
|
Friend Sub ResetHost()
|
||||||
_HostObtained = False
|
_HostObtained = False
|
||||||
End Sub
|
End Sub
|
||||||
Friend Property HostStatic As Boolean = False Implements IUserData.HostStatic
|
Friend Property HostStatic As Boolean = False Implements IUserData.HostStatic
|
||||||
@@ -311,7 +312,7 @@ Namespace API.Base
|
|||||||
End Set
|
End Set
|
||||||
End Property
|
End Property
|
||||||
Protected Sub UserSiteNameUpdate(ByVal NewName As String)
|
Protected Sub UserSiteNameUpdate(ByVal NewName As String)
|
||||||
If Not NewName.IsEmptyString And (UserSiteName.IsEmptyString Or Settings.UserSiteNameUpdateEveryTime) Then UserSiteName = NewName
|
If Not NewName.IsEmptyString And (UserSiteName.IsEmptyString Or Settings.UpdateUserSiteNameEveryTime) Then UserSiteName = NewName
|
||||||
End Sub
|
End Sub
|
||||||
Friend ReadOnly Property UserModel As UsageModel Implements IUserData.UserModel
|
Friend ReadOnly Property UserModel As UsageModel Implements IUserData.UserModel
|
||||||
Get
|
Get
|
||||||
@@ -828,48 +829,19 @@ BlockNullPicture:
|
|||||||
Return ListImagesLoader.ApplyLVIColor(Me, New ListViewItem(ToString(), GetLVIGroup(Destination)) With {.Name = LVIKey, .Tag = LVIKey}, True)
|
Return ListImagesLoader.ApplyLVIColor(Me, New ListViewItem(ToString(), GetLVIGroup(Destination)) With {.Name = LVIKey, .Tag = LVIKey}, True)
|
||||||
End If
|
End If
|
||||||
End Function
|
End Function
|
||||||
Friend Overridable ReadOnly Property FitToAddParams As Boolean Implements IUserData.FitToAddParams
|
|
||||||
Get
|
|
||||||
With Settings
|
|
||||||
If IsSubscription And Not .MainFrameUsersShowSubscriptions Then Return False
|
|
||||||
If Not IsSubscription And Not .MainFrameUsersShowDefaults Then Return False
|
|
||||||
If LastUpdated.HasValue And Not .ViewDateMode.Value = ShowingDates.Off Then
|
|
||||||
Dim f As Date = If(.ViewDateFrom.HasValue, .ViewDateFrom.Value.Date, Date.MinValue.Date)
|
|
||||||
Dim t As Date = If(.ViewDateTo.HasValue, .ViewDateTo.Value.Date, Date.MaxValue.Date)
|
|
||||||
Select Case DirectCast(.ViewDateMode.Value, ShowingDates)
|
|
||||||
Case ShowingDates.In : If Not LastUpdated.Value.ValueBetween(f, t) Then Return False
|
|
||||||
Case ShowingDates.Not : If LastUpdated.Value.ValueBetween(f, t) Then Return False
|
|
||||||
End Select
|
|
||||||
End If
|
|
||||||
If Not .Labels.ExcludedIgnore AndAlso .Labels.Excluded.ValuesList.ListContains(Labels) Then Return False
|
|
||||||
If .SelectedSites.Count = 0 OrElse .SelectedSites.Contains(Site) Then
|
|
||||||
Select Case .ShowingMode.Value
|
|
||||||
Case ShowingModes.Regular : Return Not Temporary And Not Favorite
|
|
||||||
Case ShowingModes.Temporary : Return Temporary
|
|
||||||
Case ShowingModes.Favorite : Return Favorite
|
|
||||||
Case ShowingModes.Deleted : Return Not UserExists
|
|
||||||
Case ShowingModes.Suspended : Return UserSuspended
|
|
||||||
Case ShowingModes.Labels : Return Settings.Labels.Current.ValuesList.ListContains(Labels)
|
|
||||||
Case ShowingModes.NoLabels : Return Labels.Count = 0
|
|
||||||
Case Else : Return True
|
|
||||||
End Select
|
|
||||||
Else
|
|
||||||
Return False
|
|
||||||
End If
|
|
||||||
End With
|
|
||||||
End Get
|
|
||||||
End Property
|
|
||||||
Friend Function GetLVIGroup(ByVal Destination As ListView) As ListViewGroup Implements IUserData.GetLVIGroup
|
Friend Function GetLVIGroup(ByVal Destination As ListView) As ListViewGroup Implements IUserData.GetLVIGroup
|
||||||
Try
|
Try
|
||||||
If Settings.ShowingMode.Value = ShowingModes.Labels And Not Settings.ShowGroupsInsteadLabels Then
|
With Settings
|
||||||
If Labels.Count > 0 And Settings.Labels.Current.Count > 0 Then
|
If Not .ShowAllUsers.Value AndAlso (.AdvancedFilter.Labels.Count > 0 Or .AdvancedFilter.LabelsNo) AndAlso Not .ShowGroupsInsteadLabels Then
|
||||||
For i% = 0 To Labels.Count - 1
|
If Labels.Count > 0 And .AdvancedFilter.Labels.Count > 0 Then
|
||||||
If Settings.Labels.Current.Contains(Labels(i)) Then Return Destination.Groups.Item(Labels(i))
|
For i% = 0 To Labels.Count - 1
|
||||||
Next
|
If .AdvancedFilter.Labels.Contains(Labels(i)) Then Return Destination.Groups.Item(Labels(i))
|
||||||
|
Next
|
||||||
|
End If
|
||||||
|
ElseIf Settings.GroupUsers Then
|
||||||
|
Return Destination.Groups.Item(GetLviGroupName(HOST, Temporary, Favorite, IsCollection))
|
||||||
End If
|
End If
|
||||||
ElseIf Settings.ShowGroups Then
|
End With
|
||||||
Return Destination.Groups.Item(GetLviGroupName(HOST, Temporary, Favorite, IsCollection))
|
|
||||||
End If
|
|
||||||
Return Destination.Groups.Item(LabelsKeeper.NoLabeledName)
|
Return Destination.Groups.Item(LabelsKeeper.NoLabeledName)
|
||||||
Catch ex As Exception
|
Catch ex As Exception
|
||||||
Return Destination.Groups.Item(LabelsKeeper.NoLabeledName)
|
Return Destination.Groups.Item(LabelsKeeper.NoLabeledName)
|
||||||
@@ -1029,7 +1001,7 @@ BlockNullPicture:
|
|||||||
|
|
||||||
x.Save(MyFileSettings)
|
x.Save(MyFileSettings)
|
||||||
End Using
|
End Using
|
||||||
If Not IsSavedPosts Then Settings.UpdateUsersList(User)
|
If Not IsSavedPosts Then Settings.UpdateUsersList(User, True)
|
||||||
Catch ex As Exception
|
Catch ex As Exception
|
||||||
LogError(ex, "user information saving error")
|
LogError(ex, "user information saving error")
|
||||||
End Try
|
End Try
|
||||||
@@ -1150,6 +1122,8 @@ BlockNullPicture:
|
|||||||
Private _EnvirChanged As Boolean = False
|
Private _EnvirChanged As Boolean = False
|
||||||
Private _PictureExists As Boolean
|
Private _PictureExists As Boolean
|
||||||
Private _EnvirInvokeUserUpdated As Boolean = False
|
Private _EnvirInvokeUserUpdated As Boolean = False
|
||||||
|
Protected _ResponserAutoUpdateCookies As Boolean = False
|
||||||
|
Protected _ResponserAddResponseReceivedHandler As Boolean = False
|
||||||
Protected Sub EnvirDownloadSet()
|
Protected Sub EnvirDownloadSet()
|
||||||
TokenPersonal = Nothing
|
TokenPersonal = Nothing
|
||||||
ProgressPre.Reset()
|
ProgressPre.Reset()
|
||||||
@@ -1191,7 +1165,14 @@ BlockNullPicture:
|
|||||||
If Not Responser Is Nothing Then Responser.Dispose()
|
If Not Responser Is Nothing Then Responser.Dispose()
|
||||||
Responser = New Responser
|
Responser = New Responser
|
||||||
If Not HOST.Responser Is Nothing Then Responser.Copy(HOST.Responser)
|
If Not HOST.Responser Is Nothing Then Responser.Copy(HOST.Responser)
|
||||||
|
If Not Responser Is Nothing And (_ResponserAutoUpdateCookies Or _ResponserAddResponseReceivedHandler) Then
|
||||||
|
If _ResponserAutoUpdateCookies Then
|
||||||
|
Responser.CookiesUpdateMode = CookieUpdateModes.ReplaceByNameAll
|
||||||
|
Responser.CookiesExtractMode = Responser.CookiesExtractModes.Any
|
||||||
|
Responser.CookiesExtractedAutoSave = False
|
||||||
|
End If
|
||||||
|
If _ResponserAddResponseReceivedHandler Then AddHandler Responser.ResponseReceived, AddressOf Responser_ResponseReceived
|
||||||
|
End If
|
||||||
Responser.DecodersError = New ErrorsDescriber(EDP.SendToLog + EDP.ReturnValue) With {
|
Responser.DecodersError = New ErrorsDescriber(EDP.SendToLog + EDP.ReturnValue) With {
|
||||||
.DeclaredMessage = New MMessage($"SymbolsConverter error: [{ToStringForLog()}]", ToStringForLog())}
|
.DeclaredMessage = New MMessage($"SymbolsConverter error: [{ToStringForLog()}]", ToStringForLog())}
|
||||||
|
|
||||||
@@ -1259,8 +1240,10 @@ BlockNullPicture:
|
|||||||
Dim mca& = If(ContentMissingExists, _ContentList.LongCount(Function(c) MissingFinder(c)), 0)
|
Dim mca& = If(ContentMissingExists, _ContentList.LongCount(Function(c) MissingFinder(c)), 0)
|
||||||
If DownloadedTotal(False) > 0 Or _EnvirChanged Or Not mcb = mca Or _ForceSaveUserData Then
|
If DownloadedTotal(False) > 0 Or _EnvirChanged Or Not mcb = mca Or _ForceSaveUserData Then
|
||||||
If Not __isChannelsSupport Then
|
If Not __isChannelsSupport Then
|
||||||
LastUpdated = Now
|
If DownloadedTotal(False) > 0 Then
|
||||||
RunScript()
|
LastUpdated = Now
|
||||||
|
RunScript()
|
||||||
|
End If
|
||||||
DownloadedPictures(True) = SFile.GetFiles(MyFile.CutPath, "*.jpg|*.jpeg|*.png|*.gif|*.webm",, EDP.ReturnValue).Count
|
DownloadedPictures(True) = SFile.GetFiles(MyFile.CutPath, "*.jpg|*.jpeg|*.png|*.gif|*.webm",, EDP.ReturnValue).Count
|
||||||
DownloadedVideos(True) = SFile.GetFiles(MyFile.CutPath, "*.mp4|*.mkv|*.mov", SearchOption.AllDirectories, EDP.ReturnValue).Count
|
DownloadedVideos(True) = SFile.GetFiles(MyFile.CutPath, "*.mp4|*.mkv|*.mov", SearchOption.AllDirectories, EDP.ReturnValue).Count
|
||||||
If Labels.Contains(LabelsKeeper.NoParsedUser) Then Labels.Remove(LabelsKeeper.NoParsedUser)
|
If Labels.Contains(LabelsKeeper.NoParsedUser) Then Labels.Remove(LabelsKeeper.NoParsedUser)
|
||||||
@@ -1284,9 +1267,9 @@ BlockNullPicture:
|
|||||||
Catch exit_ex As ExitException
|
Catch exit_ex As ExitException
|
||||||
If Not exit_ex.Silent Then
|
If Not exit_ex.Silent Then
|
||||||
If exit_ex.SimpleLogLine Then
|
If exit_ex.SimpleLogLine Then
|
||||||
MyMainLOG = $"{ToStringForLog()}: downloading canceled (exit) ({exit_ex.Message})"
|
MyMainLOG = $"{ToStringForLog()}: downloading interrupted (exit) ({exit_ex.Message})"
|
||||||
Else
|
Else
|
||||||
ErrorsDescriber.Execute(EDP.SendToLog, exit_ex, $"{ToStringForLog()}: downloading canceled (exit)")
|
ErrorsDescriber.Execute(EDP.SendToLog, exit_ex, $"{ToStringForLog()}: downloading interrupted (exit)")
|
||||||
End If
|
End If
|
||||||
End If
|
End If
|
||||||
Canceled = True
|
Canceled = True
|
||||||
@@ -1311,6 +1294,7 @@ BlockNullPicture:
|
|||||||
ProgressPre.Done()
|
ProgressPre.Done()
|
||||||
__DOWNLOAD_IN_PROGRESS = False
|
__DOWNLOAD_IN_PROGRESS = False
|
||||||
OnUserDownloadStateChanged(False)
|
OnUserDownloadStateChanged(False)
|
||||||
|
If _ResponserAddResponseReceivedHandler Then Responser_ResponseReceived_RemoveHandler()
|
||||||
End Try
|
End Try
|
||||||
End Sub
|
End Sub
|
||||||
Protected Sub UpdateDataFiles()
|
Protected Sub UpdateDataFiles()
|
||||||
@@ -1333,6 +1317,13 @@ BlockNullPicture:
|
|||||||
End If
|
End If
|
||||||
End Sub
|
End Sub
|
||||||
Protected MustOverride Sub DownloadDataF(ByVal Token As CancellationToken)
|
Protected MustOverride Sub DownloadDataF(ByVal Token As CancellationToken)
|
||||||
|
Protected Overridable Sub Responser_ResponseReceived(ByVal Sender As Object, ByVal e As EventArguments.WebDataResponse)
|
||||||
|
End Sub
|
||||||
|
Protected Sub Responser_ResponseReceived_RemoveHandler()
|
||||||
|
If Not Responser Is Nothing And _ResponserAddResponseReceivedHandler And Not Disposed Then
|
||||||
|
Try : RemoveHandler Responser.ResponseReceived, AddressOf Responser_ResponseReceived : Catch : End Try
|
||||||
|
End If
|
||||||
|
End Sub
|
||||||
Protected Function CreateCache() As CacheKeeper
|
Protected Function CreateCache() As CacheKeeper
|
||||||
Dim Cache As New CacheKeeper($"{DownloadContentDefault_GetRootDir()}\_tCache\")
|
Dim Cache As New CacheKeeper($"{DownloadContentDefault_GetRootDir()}\_tCache\")
|
||||||
Cache.CacheDeleteError = CacheDeletionError(Cache)
|
Cache.CacheDeleteError = CacheDeletionError(Cache)
|
||||||
@@ -1348,6 +1339,7 @@ BlockNullPicture:
|
|||||||
ResetHost()
|
ResetHost()
|
||||||
URL = Data.URL
|
URL = Data.URL
|
||||||
AccountName = Data.AccountName
|
AccountName = Data.AccountName
|
||||||
|
TokenQueue = Token
|
||||||
If HOST Is Nothing Then Throw New ExitException($"Host '{AccountName}' not found")
|
If HOST Is Nothing Then Throw New ExitException($"Host '{AccountName}' not found")
|
||||||
Data.DownloadState = UserMediaStates.Tried
|
Data.DownloadState = UserMediaStates.Tried
|
||||||
Progress = Data.Progress
|
Progress = Data.Progress
|
||||||
@@ -1391,21 +1383,26 @@ BlockNullPicture:
|
|||||||
If _ContentNew.Count > 0 Then
|
If _ContentNew.Count > 0 Then
|
||||||
If _ContentNew.Any(Function(mm) mm.State = UStates.Downloaded) Then
|
If _ContentNew.Any(Function(mm) mm.State = UStates.Downloaded) Then
|
||||||
Data.DownloadState = UserMediaStates.Downloaded
|
Data.DownloadState = UserMediaStates.Downloaded
|
||||||
|
Dim thumbAlong As Boolean = False
|
||||||
|
If TypeOf Data Is DownloadableMediaHost Then thumbAlong = DirectCast(Data, DownloadableMediaHost).ThumbAlong
|
||||||
If _ContentNew(0).Type = UTypes.Picture Or _ContentNew(0).Type = UTypes.GIF Then
|
If _ContentNew(0).Type = UTypes.Picture Or _ContentNew(0).Type = UTypes.GIF Then
|
||||||
DirectCast(Data, IDownloadableMedia).ThumbnailFile = _ContentNew(0).File
|
DirectCast(Data, IDownloadableMedia).ThumbnailFile = _ContentNew(0).File
|
||||||
ElseIf Settings.STDownloader_TakeSnapshot And Settings.FfmpegFile.Exists And Not Settings.STDownloader_RemoveDownloadedAutomatically Then
|
ElseIf Settings.STDownloader_TakeSnapshot And Settings.FfmpegFile.Exists And Not Settings.STDownloader_RemoveDownloadedAutomatically Then
|
||||||
Dim f As SFile = _ContentNew(0).File
|
Dim f As SFile = _ContentNew(0).File
|
||||||
Dim ff As SFile
|
Dim ff As SFile
|
||||||
If Settings.STDownloader_SnapshotsKeepWithFiles Then
|
If Settings.STDownloader_SnapshotsKeepWithFiles Or thumbAlong Then
|
||||||
ff = f
|
ff = f
|
||||||
Else
|
Else
|
||||||
ff = Settings.CacheSnapshots(Settings.STDownloader_SnapShotsCachePermamnent).NewFile
|
ff = Settings.CacheSnapshots(Settings.STDownloader_SnapShotsCachePermamnent).NewFile
|
||||||
End If
|
End If
|
||||||
ff.Name &= "_thumb"
|
ff.Name &= "_thumb"
|
||||||
ff.Extension = "jpg"
|
ff.Extension = "jpg"
|
||||||
f = Web.FFMPEG.TakeSnapshot(f, ff, Settings.FfmpegFile, TimeSpan.FromSeconds(1),,, EDP.LogMessageValue)
|
f = Web.FFMPEG.TakeSnapshot(f, ff, Settings.FfmpegFile, TimeSpan.FromSeconds(1),,, EDP.SendToLog + EDP.ReturnValue)
|
||||||
If f.Exists Then DirectCast(Data, IDownloadableMedia).ThumbnailFile = f
|
If f.Exists Then DirectCast(Data, IDownloadableMedia).ThumbnailFile = f
|
||||||
End If
|
End If
|
||||||
|
Dim filesSize# = (From mm As UserMedia In _ContentNew Where mm.State = UStates.Downloaded AndAlso mm.File.Exists Select mm.File.Size).Sum
|
||||||
|
If filesSize > 0 Then filesSize /= 1024
|
||||||
|
Data.Size = filesSize
|
||||||
Else
|
Else
|
||||||
Data.DownloadState = UserMediaStates.Missing
|
Data.DownloadState = UserMediaStates.Missing
|
||||||
End If
|
End If
|
||||||
@@ -1778,6 +1775,7 @@ BlockNullPicture:
|
|||||||
Protected Overridable Function ValidateDownloadFile(ByVal URL As String, ByVal Media As UserMedia, ByRef Interrupt As Boolean) As Boolean
|
Protected Overridable Function ValidateDownloadFile(ByVal URL As String, ByVal Media As UserMedia, ByRef Interrupt As Boolean) As Boolean
|
||||||
Return True
|
Return True
|
||||||
End Function
|
End Function
|
||||||
|
''' <returns><c>MyFile.CutPath(IIf(IsSingleObjectDownload, 0, 1)).PathNoSeparator</c></returns>
|
||||||
Protected Overridable Function DownloadContentDefault_GetRootDir() As String
|
Protected Overridable Function DownloadContentDefault_GetRootDir() As String
|
||||||
Return MyFile.CutPath(IIf(IsSingleObjectDownload, 0, 1)).PathNoSeparator
|
Return MyFile.CutPath(IIf(IsSingleObjectDownload, 0, 1)).PathNoSeparator
|
||||||
End Function
|
End Function
|
||||||
@@ -1896,7 +1894,9 @@ BlockNullPicture:
|
|||||||
If m.Contains(IUserData.EraseMode.History) Then
|
If m.Contains(IUserData.EraseMode.History) Then
|
||||||
If MyFilePosts.Delete(SFO.File, SFODelete.DeleteToRecycleBin, e) Then result = True
|
If MyFilePosts.Delete(SFO.File, SFODelete.DeleteToRecycleBin, e) Then result = True
|
||||||
If MyFileData.Delete(SFO.File, SFODelete.DeleteToRecycleBin, e) Then result = True
|
If MyFileData.Delete(SFO.File, SFODelete.DeleteToRecycleBin, e) Then result = True
|
||||||
|
LastUpdated = Nothing
|
||||||
EraseData_AdditionalDataFiles()
|
EraseData_AdditionalDataFiles()
|
||||||
|
UpdateUserInformation()
|
||||||
End If
|
End If
|
||||||
If m.Contains(IUserData.EraseMode.Data) Then
|
If m.Contains(IUserData.EraseMode.Data) Then
|
||||||
Dim files As List(Of SFile) = SFile.GetFiles(DownloadContentDefault_GetRootDir.CSFileP,, SearchOption.AllDirectories, e)
|
Dim files As List(Of SFile) = SFile.GetFiles(DownloadContentDefault_GetRootDir.CSFileP,, SearchOption.AllDirectories, e)
|
||||||
@@ -1918,7 +1918,7 @@ BlockNullPicture:
|
|||||||
Return ErrorsDescriber.Execute(EDP.SendToLog + EDP.ReturnValue, ex, $"EraseData({CInt(Mode)}): {ToStringForLog()}", False)
|
Return ErrorsDescriber.Execute(EDP.SendToLog + EDP.ReturnValue, ex, $"EraseData({CInt(Mode)}): {ToStringForLog()}", False)
|
||||||
End Try
|
End Try
|
||||||
End Function
|
End Function
|
||||||
Protected Overridable Sub EraseData_AdditionalDataFiles()
|
Protected Overridable Sub EraseData_AdditionalDataFiles() Implements IPluginContentProvider.ResetHistoryData
|
||||||
End Sub
|
End Sub
|
||||||
Friend Overridable Function Delete(Optional ByVal Multiple As Boolean = False, Optional ByVal CollectionValue As Integer = -1) As Integer Implements IUserData.Delete
|
Friend Overridable Function Delete(Optional ByVal Multiple As Boolean = False, Optional ByVal CollectionValue As Integer = -1) As Integer Implements IUserData.Delete
|
||||||
Dim f As SFile = SFile.GetPath(MyFile.CutPath.Path)
|
Dim f As SFile = SFile.GetPath(MyFile.CutPath.Path)
|
||||||
@@ -2057,7 +2057,7 @@ BlockNullPicture:
|
|||||||
End Function
|
End Function
|
||||||
Private Class FilesCopyingException : Inherits ErrorsDescriberException
|
Private Class FilesCopyingException : Inherits ErrorsDescriberException
|
||||||
Friend Sub New(ByVal User As IUserData, ByVal Msg As String, ByVal Path As SFile)
|
Friend Sub New(ByVal User As IUserData, ByVal Msg As String, ByVal Path As SFile)
|
||||||
SendInLogOnlyMessage = True
|
SendToLogOnlyMessage = True
|
||||||
If User.IncludedInCollection Then _MainMessage = $"[{User.CollectionName}] - "
|
If User.IncludedInCollection Then _MainMessage = $"[{User.CollectionName}] - "
|
||||||
_MainMessage &= $"[{User.Site}] - [{User.Name}]. {Msg}: {Path.Path}."
|
_MainMessage &= $"[{User.Site}] - [{User.Name}]. {Msg}: {Path.Path}."
|
||||||
End Sub
|
End Sub
|
||||||
@@ -2181,18 +2181,20 @@ BlockNullPicture:
|
|||||||
End Sub
|
End Sub
|
||||||
#End Region
|
#End Region
|
||||||
#Region "IComparable Support"
|
#Region "IComparable Support"
|
||||||
Friend Overridable Function CompareTo(ByVal Other As UserDataBase) As Integer Implements IComparable(Of UserDataBase).CompareTo
|
Friend Overridable Overloads Function CompareTo(ByVal Other As UserDataBase) As Integer Implements IComparable(Of UserDataBase).CompareTo
|
||||||
If IsCollection Then
|
If TypeOf Other Is UserDataBind Then
|
||||||
|
Return 1
|
||||||
|
ElseIf IsCollection Then
|
||||||
Return Name.CompareTo(Other.Name)
|
Return Name.CompareTo(Other.Name)
|
||||||
Else
|
Else
|
||||||
Return FriendlyName.IfNullOrEmpty(Name).StringTrim.CompareTo(Other.FriendlyName.IfNullOrEmpty(Other.Name).StringTrim)
|
Return FriendlyName.IfNullOrEmpty(Name).StringTrim.CompareTo(Other.FriendlyName.IfNullOrEmpty(Other.Name).StringTrim)
|
||||||
End If
|
End If
|
||||||
End Function
|
End Function
|
||||||
Friend Overridable Function CompareTo(ByVal Obj As Object) As Integer Implements IComparable.CompareTo
|
Friend Overridable Overloads Function CompareTo(ByVal Obj As Object) As Integer Implements IComparable.CompareTo
|
||||||
If Not Obj Is Nothing AndAlso TypeOf Obj Is UserDataBase Then
|
If Not Obj Is Nothing AndAlso TypeOf Obj Is UserDataBase Then
|
||||||
Return CompareTo(DirectCast(Obj, UserDataBase))
|
Return CompareTo(DirectCast(Obj, UserDataBase))
|
||||||
Else
|
Else
|
||||||
Return False
|
Return 0
|
||||||
End If
|
End If
|
||||||
End Function
|
End Function
|
||||||
#End Region
|
#End Region
|
||||||
@@ -2200,7 +2202,7 @@ BlockNullPicture:
|
|||||||
Friend Overridable Overloads Function Equals(ByVal Other As UserDataBase) As Boolean Implements IEquatable(Of UserDataBase).Equals
|
Friend Overridable Overloads Function Equals(ByVal Other As UserDataBase) As Boolean Implements IEquatable(Of UserDataBase).Equals
|
||||||
Return LVIKey = Other.LVIKey And IsSavedPosts = Other.IsSavedPosts
|
Return LVIKey = Other.LVIKey And IsSavedPosts = Other.IsSavedPosts
|
||||||
End Function
|
End Function
|
||||||
Public Overrides Function Equals(ByVal Obj As Object) As Boolean
|
Public Overloads Overrides Function Equals(ByVal Obj As Object) As Boolean
|
||||||
If Not Obj Is Nothing AndAlso TypeOf Obj Is UserDataBase Then
|
If Not Obj Is Nothing AndAlso TypeOf Obj Is UserDataBase Then
|
||||||
Return Equals(DirectCast(Obj, UserDataBase))
|
Return Equals(DirectCast(Obj, UserDataBase))
|
||||||
Else
|
Else
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ Namespace API.Base.YTDLP
|
|||||||
Friend Sub New(ByVal _Token As Threading.CancellationToken)
|
Friend Sub New(ByVal _Token As Threading.CancellationToken)
|
||||||
MyBase.New(_Token)
|
MyBase.New(_Token)
|
||||||
Commands.Clear()
|
Commands.Clear()
|
||||||
MainProcessName = "yt-dlp"
|
MainProcessName = Settings.YtdlpFile.File.Name '"yt-dlp"
|
||||||
ChangeDirectory(Settings.YtdlpFile.File)
|
ChangeDirectory(Settings.YtdlpFile.File)
|
||||||
End Sub
|
End Sub
|
||||||
End Class
|
End Class
|
||||||
|
|||||||
@@ -11,9 +11,8 @@ Imports PersonalUtilities.Functions.XML.Base
|
|||||||
Imports PersonalUtilities.Functions.RegularExpressions
|
Imports PersonalUtilities.Functions.RegularExpressions
|
||||||
Namespace API.Facebook
|
Namespace API.Facebook
|
||||||
Friend Module Declarations
|
Friend Module Declarations
|
||||||
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_UserID As RParams = RParams.DMS("userid.:.(\d+)", 1, RegexOptions.IgnoreCase, EDP.ReturnValue)
|
Friend ReadOnly Regex_UserID As RParams = RParams.DMS("userid.:.(\d+)", 1, RegexOptions.IgnoreCase, EDP.ReturnValue)
|
||||||
|
Friend ReadOnly Regex_AppID As RParams = RParams.DMS("APP_ID.:.(\d+)", 1, RegexOptions.IgnoreCase, EDP.ReturnValue)
|
||||||
|
|
||||||
Friend ReadOnly Regex_Photos_by As RParams = RParams.DMS("photos_by"",""id"":""([^""]+)", 1, EDP.ReturnValue)
|
Friend ReadOnly Regex_Photos_by As RParams = RParams.DMS("photos_by"",""id"":""([^""]+)", 1, EDP.ReturnValue)
|
||||||
Friend ReadOnly Regex_FileName As RParams = RParams.DM("([^/\?]+\..{3,4})(?=(\?|\Z))", 0, EDP.ReturnValue)
|
Friend ReadOnly Regex_FileName As RParams = RParams.DM("([^/\?]+\..{3,4})(?=(\?|\Z))", 0, EDP.ReturnValue)
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ Namespace API.Facebook
|
|||||||
#Region "Auth"
|
#Region "Auth"
|
||||||
<PropertyOption(AllowNull:=False, ControlText:="Accept", ControlToolTip:="Header 'Accept'", IsAuth:=True), ControlNumber(21), PXML, PClonable>
|
<PropertyOption(AllowNull:=False, ControlText:="Accept", ControlToolTip:="Header 'Accept'", IsAuth:=True), ControlNumber(21), PXML, PClonable>
|
||||||
Friend ReadOnly Property Header_Accept As PropertyValue
|
Friend ReadOnly Property Header_Accept As PropertyValue
|
||||||
<PropertyOption(ControlText:="x-ig-app-id", AllowNull:=True, IsAuth:=True)>
|
<PropertyOption(ControlText:="x-ig-app-id", AllowNull:=True, IsAuth:=True), HiddenControl>
|
||||||
Friend Overrides ReadOnly Property HH_IG_APP_ID As PropertyValue
|
Friend Overrides ReadOnly Property HH_IG_APP_ID As PropertyValue
|
||||||
Get
|
Get
|
||||||
Return __HH_IG_APP_ID
|
Return __HH_IG_APP_ID
|
||||||
@@ -29,8 +29,6 @@ Namespace API.Facebook
|
|||||||
Return __HH_CSRF_TOKEN
|
Return __HH_CSRF_TOKEN
|
||||||
End Get
|
End Get
|
||||||
End Property
|
End Property
|
||||||
<PropertyOption(ControlText:="sec-ch-ua-platform-ver", ControlToolTip:="sec-ch-ua-platform-version", IsAuth:=True, LeftOffset:=120), ControlNumber(51), PXML, PClonable>
|
|
||||||
Friend ReadOnly Property HH_PLATFORM_VER As PropertyValue
|
|
||||||
#End Region
|
#End Region
|
||||||
#Region "Defaults"
|
#Region "Defaults"
|
||||||
<PropertyOption(ControlText:="Download photos", IsAuth:=False), PXML, PClonable>
|
<PropertyOption(ControlText:="Download photos", IsAuth:=False), PXML, PClonable>
|
||||||
@@ -48,10 +46,9 @@ Namespace API.Facebook
|
|||||||
With Responser.Headers
|
With Responser.Headers
|
||||||
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.Authority, "www.facebook.com"))
|
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.Authority, "www.facebook.com"))
|
||||||
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.Origin, "https://www.facebook.com"))
|
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.Origin, "https://www.facebook.com"))
|
||||||
.Remove(DeclaredNames.Header_FB_FRIENDLY_NAME)
|
.Remove(Instagram.UserData.GQL_HEADER_FB_FRINDLY_NAME)
|
||||||
End With
|
End With
|
||||||
Header_Accept = New PropertyValue(String.Empty, GetType(String))
|
Header_Accept = New PropertyValue(String.Empty, GetType(String))
|
||||||
HH_PLATFORM_VER = New PropertyValue(String.Empty, GetType(String))
|
|
||||||
ParsePhotoBlock = New PropertyValue(True)
|
ParsePhotoBlock = New PropertyValue(True)
|
||||||
ParseVideoBlock = New PropertyValue(True)
|
ParseVideoBlock = New PropertyValue(True)
|
||||||
ParseStoriesBlock = New PropertyValue(True)
|
ParseStoriesBlock = New PropertyValue(True)
|
||||||
@@ -77,7 +74,7 @@ Namespace API.Facebook
|
|||||||
#End Region
|
#End Region
|
||||||
#Region "BaseAuthExists, GetUserUrl, GetUserPostUrl, IsMyUser, IsMyImageVideo"
|
#Region "BaseAuthExists, GetUserUrl, GetUserPostUrl, IsMyUser, IsMyImageVideo"
|
||||||
Friend Overrides Function BaseAuthExists() As Boolean
|
Friend Overrides Function BaseAuthExists() As Boolean
|
||||||
Return Responser.CookiesExists And ACheck(HH_IG_APP_ID.Value)
|
Return Responser.CookiesExists And CBool(DownloadData_Impl.Value) 'And ACheck(HH_IG_APP_ID.Value)
|
||||||
End Function
|
End Function
|
||||||
Friend Overrides Function GetUserUrl(ByVal User As IPluginContentProvider) As String
|
Friend Overrides Function GetUserUrl(ByVal User As IPluginContentProvider) As String
|
||||||
Return DirectCast(User, UserData).GetProfileUrl
|
Return DirectCast(User, UserData).GetProfileUrl
|
||||||
|
|||||||
@@ -107,27 +107,52 @@ Namespace API.Facebook
|
|||||||
End With
|
End With
|
||||||
End Sub
|
End Sub
|
||||||
#End Region
|
#End Region
|
||||||
|
#Region "Initializer"
|
||||||
|
Friend Sub New()
|
||||||
|
_ResponserAutoUpdateCookies = True
|
||||||
|
End Sub
|
||||||
|
#End Region
|
||||||
#Region "Download functions"
|
#Region "Download functions"
|
||||||
Private Token_dtsg As String = String.Empty
|
Private Class TokensException : Inherits Plugin.ExitException
|
||||||
Private Token_lsd As String = String.Empty
|
Friend ReadOnly Property BasicTokens As Boolean
|
||||||
|
Public Sub New(ByVal Message As String, ByVal _BasicTokens As Boolean)
|
||||||
|
MyBase.New(Message)
|
||||||
|
BasicTokens = _BasicTokens
|
||||||
|
End Sub
|
||||||
|
Friend Shared Sub SendToLog(ByVal Source As UserData, ByVal ex As TokensException, ByVal f As String)
|
||||||
|
ErrorsDescriber.Execute(EDP.SendToLog, New ErrorsDescriberException($"{Source.ToStringForLog()} ({f}): {ex.Message}",,, ex) With {
|
||||||
|
.SendToLogOnlyMessage = True, .ReplaceMainMessage = True})
|
||||||
|
End Sub
|
||||||
|
End Class
|
||||||
Private Token_Photosby As String = String.Empty
|
Private Token_Photosby As String = String.Empty
|
||||||
Private Limit As Integer = -1
|
Private Limit As Integer = -1
|
||||||
|
Private Sub WaitTimer()
|
||||||
|
If CInt(MySettings.RequestsWaitTimer_Any.Value) > 0 Then Thread.Sleep(CInt(MySettings.RequestsWaitTimer_Any.Value))
|
||||||
|
End Sub
|
||||||
|
Private Sub DisableDownload()
|
||||||
|
MySettings.DownloadData_Impl.Value = False
|
||||||
|
MyMainLOG = $"{Site} downloading is disabled until you update your credentials"
|
||||||
|
End Sub
|
||||||
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
|
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
|
||||||
Try
|
If CBool(MySettings.DownloadData_Impl.Value) Then
|
||||||
GetUserTokens(Token)
|
Try
|
||||||
LoadSavePostsKV(True)
|
If Responser.Headers.Value(IG.Header_IG_APP_ID).IsEmptyString Then Responser.Headers.Remove(IG.Header_IG_APP_ID)
|
||||||
Limit = If(DownloadTopCount, -1)
|
ResetBaseTokens()
|
||||||
If IsSavedPosts Then
|
GetUserTokens(Token)
|
||||||
DownloadData_SavedPosts(String.Empty, Token)
|
LoadSavePostsKV(True)
|
||||||
Else
|
Limit = If(DownloadTopCount, -1)
|
||||||
If DownloadImages And ParsePhotoBlock Then DownloadData_Photo(String.Empty, Token)
|
If IsSavedPosts Then
|
||||||
If DownloadVideos And ParseVideoBlock Then DownloadData_Video(String.Empty, Token)
|
DownloadData_SavedPosts(String.Empty, Token)
|
||||||
If (DownloadImages Or DownloadVideos) And ParseStoriesBlock Then DownloadData_Stories(Token)
|
Else
|
||||||
End If
|
If DownloadImages And ParsePhotoBlock Then DownloadData_Photo(String.Empty, Token)
|
||||||
LoadSavePostsKV(False)
|
If DownloadVideos And ParseVideoBlock Then DownloadData_Video(String.Empty, Token)
|
||||||
Finally
|
If (DownloadImages Or DownloadVideos) And ParseStoriesBlock Then DownloadData_Stories(Token)
|
||||||
MySettings.UpdateResponserData(Responser)
|
End If
|
||||||
End Try
|
LoadSavePostsKV(False)
|
||||||
|
Finally
|
||||||
|
MySettings.UpdateResponserData(Responser)
|
||||||
|
End Try
|
||||||
|
End If
|
||||||
End Sub
|
End Sub
|
||||||
Private Const Header_fb_fr_name_Photo As String = "ProfileCometAppCollectionPhotosRendererPaginationQuery"
|
Private Const Header_fb_fr_name_Photo As String = "ProfileCometAppCollectionPhotosRendererPaginationQuery"
|
||||||
Private Const Header_fb_fr_name_Video As String = "PagesCometChannelTabAllVideosCardImplPaginationQuery"
|
Private Const Header_fb_fr_name_Video As String = "PagesCometChannelTabAllVideosCardImplPaginationQuery"
|
||||||
@@ -149,15 +174,15 @@ Namespace API.Facebook
|
|||||||
Dim pid As PostKV
|
Dim pid As PostKV
|
||||||
|
|
||||||
ValidateBaseTokens()
|
ValidateBaseTokens()
|
||||||
If Token_Photosby.IsEmptyString Then Throw New ArgumentNullException("Token_Photosby", "Unable to obtain token")
|
If Token_Photosby.IsEmptyString Then Throw New TokensException("Unable to obtain token 'Token_Photosby'", False)
|
||||||
|
|
||||||
URL = String.Format(Graphql_UrlPattern, Token_lsd, DocID_Photo, Header_fb_fr_name_Photo,
|
URL = String.Format(Graphql_UrlPattern, Token_lsd, DocID_Photo, Header_fb_fr_name_Photo, Token_dtsg_Var,
|
||||||
SymbolsConverter.ASCII.EncodeSymbolsOnly(Token_dtsg),
|
|
||||||
SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & String.Format(VarPattern, Cursor, Token_Photosby) & "}"))
|
SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & String.Format(VarPattern, Cursor, Token_Photosby) & "}"))
|
||||||
|
|
||||||
ResponserApplyDefs(Header_fb_fr_name_Photo)
|
ResponserApplyDefs(Header_fb_fr_name_Photo)
|
||||||
ThrowAny(Token)
|
ThrowAny(Token)
|
||||||
|
|
||||||
|
WaitTimer()
|
||||||
Dim r$ = Responser.GetResponse(URL)
|
Dim r$ = Responser.GetResponse(URL)
|
||||||
If Not r.IsEmptyString Then
|
If Not r.IsEmptyString Then
|
||||||
Using j As EContainer = JsonDocument.Parse(r)
|
Using j As EContainer = JsonDocument.Parse(r)
|
||||||
@@ -199,6 +224,8 @@ Namespace API.Facebook
|
|||||||
End If
|
End If
|
||||||
|
|
||||||
If newPostsDetected And Not nextCursor.IsEmptyString Then DownloadData_Photo(nextCursor, Token)
|
If newPostsDetected And Not nextCursor.IsEmptyString Then DownloadData_Photo(nextCursor, Token)
|
||||||
|
Catch tex As TokensException When Not tex.BasicTokens
|
||||||
|
TokensException.SendToLog(Me, tex, "data (photo)")
|
||||||
Catch ex As Exception
|
Catch ex As Exception
|
||||||
ProcessException(ex, Token, $"data (photo) downloading error [{URL}]",, Responser)
|
ProcessException(ex, Token, $"data (photo) downloading error [{URL}]",, Responser)
|
||||||
End Try
|
End Try
|
||||||
@@ -212,16 +239,16 @@ Namespace API.Facebook
|
|||||||
Dim pid As PostKV
|
Dim pid As PostKV
|
||||||
|
|
||||||
If VideoPageID.IsEmptyString Then GetVideoPageID(Token)
|
If VideoPageID.IsEmptyString Then GetVideoPageID(Token)
|
||||||
If VideoPageID.IsEmptyString Then Throw New ArgumentNullException("VideoPageID", "Unable to obtain VideoPageID")
|
If VideoPageID.IsEmptyString Then Throw New TokensException("Unable to obtain 'VideoPageID'", False)
|
||||||
ValidateBaseTokens()
|
ValidateBaseTokens()
|
||||||
|
|
||||||
URL = String.Format(Graphql_UrlPattern, Token_lsd, DocID_Video, Header_fb_fr_name_Video,
|
URL = String.Format(Graphql_UrlPattern, Token_lsd, DocID_Video, Header_fb_fr_name_Video, Token_dtsg_Var,
|
||||||
SymbolsConverter.ASCII.EncodeSymbolsOnly(Token_dtsg),
|
|
||||||
SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & String.Format(VarPattern, If(Cursor.IsEmptyString, "null", $"""{Cursor}"""), VideoPageID) & "}"))
|
SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & String.Format(VarPattern, If(Cursor.IsEmptyString, "null", $"""{Cursor}"""), VideoPageID) & "}"))
|
||||||
|
|
||||||
ResponserApplyDefs(Header_fb_fr_name_Video)
|
ResponserApplyDefs(Header_fb_fr_name_Video)
|
||||||
ThrowAny(Token)
|
ThrowAny(Token)
|
||||||
|
|
||||||
|
WaitTimer()
|
||||||
Dim r$ = Responser.GetResponse(URL)
|
Dim r$ = Responser.GetResponse(URL)
|
||||||
If Not r.IsEmptyString Then
|
If Not r.IsEmptyString Then
|
||||||
Using j As EContainer = JsonDocument.Parse(r)
|
Using j As EContainer = JsonDocument.Parse(r)
|
||||||
@@ -252,6 +279,8 @@ Namespace API.Facebook
|
|||||||
End If
|
End If
|
||||||
|
|
||||||
If newPostsDetected And Not nextCursor.IsEmptyString Then DownloadData_Video(nextCursor, Token)
|
If newPostsDetected And Not nextCursor.IsEmptyString Then DownloadData_Video(nextCursor, Token)
|
||||||
|
Catch tex As TokensException When Not tex.BasicTokens
|
||||||
|
TokensException.SendToLog(Me, tex, "data (video)")
|
||||||
Catch ex As Exception
|
Catch ex As Exception
|
||||||
ProcessException(ex, Token, $"data (video) downloading error [{URL}]",, Responser)
|
ProcessException(ex, Token, $"data (video) downloading error [{URL}]",, Responser)
|
||||||
End Try
|
End Try
|
||||||
@@ -266,15 +295,15 @@ Namespace API.Facebook
|
|||||||
Dim postDate As Date?
|
Dim postDate As Date?
|
||||||
|
|
||||||
ValidateBaseTokens()
|
ValidateBaseTokens()
|
||||||
If StoryBucket.IsEmptyString Then Throw New ArgumentNullException("StoryBucket", "Unable to obtain StoryBucket")
|
If StoryBucket.IsEmptyString Then Throw New TokensException("Unable to obtain 'StoryBucket'", False)
|
||||||
|
|
||||||
URL = String.Format(Graphql_UrlPattern, Token_lsd, DocID_Stories, Header_fb_fr_name_Stories,
|
URL = String.Format(Graphql_UrlPattern, Token_lsd, DocID_Stories, Header_fb_fr_name_Stories, Token_dtsg_Var,
|
||||||
SymbolsConverter.ASCII.EncodeSymbolsOnly(Token_dtsg),
|
|
||||||
SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & String.Format(VarPattern, StoryBucket) & "}"))
|
SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & String.Format(VarPattern, StoryBucket) & "}"))
|
||||||
|
|
||||||
ResponserApplyDefs(Header_fb_fr_name_Stories)
|
ResponserApplyDefs(Header_fb_fr_name_Stories)
|
||||||
ThrowAny(Token)
|
ThrowAny(Token)
|
||||||
|
|
||||||
|
WaitTimer()
|
||||||
Dim r$ = Responser.GetResponse(URL)
|
Dim r$ = Responser.GetResponse(URL)
|
||||||
If Not r.IsEmptyString Then r = RegexReplace(r, RParams.DM("[^\r\n]+", 0, EDP.ReturnValue))
|
If Not r.IsEmptyString Then r = RegexReplace(r, RParams.DM("[^\r\n]+", 0, EDP.ReturnValue))
|
||||||
If Not r.IsEmptyString Then
|
If Not r.IsEmptyString Then
|
||||||
@@ -320,6 +349,8 @@ Namespace API.Facebook
|
|||||||
End If
|
End If
|
||||||
End Using
|
End Using
|
||||||
End If
|
End If
|
||||||
|
Catch tex As TokensException When Not tex.BasicTokens
|
||||||
|
TokensException.SendToLog(Me, tex, "data (stories)")
|
||||||
Catch ex As Exception
|
Catch ex As Exception
|
||||||
ProcessException(ex, Token, $"data (stories) downloading error [{URL}]",, Responser)
|
ProcessException(ex, Token, $"data (stories) downloading error [{URL}]",, Responser)
|
||||||
End Try
|
End Try
|
||||||
@@ -335,13 +366,13 @@ Namespace API.Facebook
|
|||||||
Dim pid As PostKV
|
Dim pid As PostKV
|
||||||
|
|
||||||
ValidateBaseTokens()
|
ValidateBaseTokens()
|
||||||
URL = String.Format(Graphql_UrlPattern, Token_lsd, DocID_SavedPosts, Header_fb_fr_name_SavedPosts,
|
URL = String.Format(Graphql_UrlPattern, Token_lsd, DocID_SavedPosts, Header_fb_fr_name_SavedPosts, Token_dtsg_Var,
|
||||||
SymbolsConverter.ASCII.EncodeSymbolsOnly(Token_dtsg),
|
|
||||||
SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & String.Format(VarPattern, If(Cursor.IsEmptyString, "null", $"""{Cursor}""")) & "}"))
|
SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & String.Format(VarPattern, If(Cursor.IsEmptyString, "null", $"""{Cursor}""")) & "}"))
|
||||||
|
|
||||||
ResponserApplyDefs(Header_fb_fr_name_SavedPosts)
|
ResponserApplyDefs(Header_fb_fr_name_SavedPosts)
|
||||||
ThrowAny(Token)
|
ThrowAny(Token)
|
||||||
|
|
||||||
|
WaitTimer()
|
||||||
Dim r$ = Responser.GetResponse(URL)
|
Dim r$ = Responser.GetResponse(URL)
|
||||||
If Not r.IsEmptyString Then
|
If Not r.IsEmptyString Then
|
||||||
Using j As EContainer = JsonDocument.Parse(r)
|
Using j As EContainer = JsonDocument.Parse(r)
|
||||||
@@ -399,6 +430,7 @@ Namespace API.Facebook
|
|||||||
If Round > 0 Then ThrowAny(Token)
|
If Round > 0 Then ThrowAny(Token)
|
||||||
Dim script$, newUrl$
|
Dim script$, newUrl$
|
||||||
Dim jNode As EContainer, jNode2 As EContainer
|
Dim jNode As EContainer, jNode2 As EContainer
|
||||||
|
WaitTimer()
|
||||||
Dim r$ = resp.GetResponse(PostUrl)
|
Dim r$ = resp.GetResponse(PostUrl)
|
||||||
|
|
||||||
If Not r.IsEmptyString Then
|
If Not r.IsEmptyString Then
|
||||||
@@ -466,14 +498,20 @@ Namespace API.Facebook
|
|||||||
#End Region
|
#End Region
|
||||||
#Region "ValidateBaseTokens, GetVideoPageID, GetUserTokens"
|
#Region "ValidateBaseTokens, GetVideoPageID, GetUserTokens"
|
||||||
''' <exception cref="ArgumentNullException"></exception>
|
''' <exception cref="ArgumentNullException"></exception>
|
||||||
Private Sub ValidateBaseTokens()
|
Protected Overrides Function ValidateBaseTokens() As Boolean
|
||||||
If Token_dtsg.IsEmptyString Then Throw New ArgumentNullException("Token_dtsg", "Unable to obtain token")
|
Dim tokens$ = String.Empty
|
||||||
If Token_lsd.IsEmptyString Then Throw New ArgumentNullException("Token_lsd", "Unable to obtain token")
|
If Not ValidateBaseTokens(tokens) Then
|
||||||
End Sub
|
DisableDownload()
|
||||||
|
Throw New TokensException($"Unable to obtain token(s) ({tokens}). Your credentials may have expired.", True)
|
||||||
|
Else
|
||||||
|
Return True
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
Private Sub GetVideoPageID(ByVal Token As CancellationToken)
|
Private Sub GetVideoPageID(ByVal Token As CancellationToken)
|
||||||
Dim URL$ = $"{GetProfileUrl()}\videos"
|
Dim URL$ = $"{GetProfileUrl()}\videos"
|
||||||
Dim resp As Responser = HtmlResponserCreate()
|
Dim resp As Responser = HtmlResponserCreate()
|
||||||
Try
|
Try
|
||||||
|
WaitTimer()
|
||||||
Dim r$ = resp.GetResponse(URL)
|
Dim r$ = resp.GetResponse(URL)
|
||||||
If Not r.IsEmptyString Then VideoPageID = RegexReplace(r, Regex_VideoPageID)
|
If Not r.IsEmptyString Then VideoPageID = RegexReplace(r, Regex_VideoPageID)
|
||||||
Catch ex As Exception
|
Catch ex As Exception
|
||||||
@@ -486,14 +524,20 @@ Namespace API.Facebook
|
|||||||
Dim URL$ = If(IsSavedPosts, "https://www.facebook.com/saved", GetProfileUrl())
|
Dim URL$ = If(IsSavedPosts, "https://www.facebook.com/saved", GetProfileUrl())
|
||||||
Dim resp As Responser = HtmlResponserCreate()
|
Dim resp As Responser = HtmlResponserCreate()
|
||||||
Try
|
Try
|
||||||
Token_dtsg = String.Empty
|
ResetBaseTokens()
|
||||||
Token_lsd = String.Empty
|
|
||||||
Token_Photosby = String.Empty
|
Token_Photosby = String.Empty
|
||||||
|
WaitTimer()
|
||||||
Dim r$ = resp.GetResponse(URL)
|
Dim r$ = resp.GetResponse(URL)
|
||||||
If Not r.IsEmptyString Then
|
If Not r.IsEmptyString Then
|
||||||
If Responser.CookiesExists Then Responser.Cookies.Update(resp.Cookies)
|
If Responser.CookiesExists Then Responser.Cookies.Update(resp.Cookies)
|
||||||
Token_dtsg = RegexReplace(r, Regex_UserToken_dtsg)
|
ParseTokens(r, 0)
|
||||||
Token_lsd = RegexReplace(r, Regex_UserToken_lsd)
|
Dim app_id$ = RegexReplace(r, Regex_AppID)
|
||||||
|
If Not app_id.IsEmptyString Then
|
||||||
|
If Not AEquals(Of String)(MySettings.HH_IG_APP_ID.Value, app_id) Then
|
||||||
|
MySettings.HH_IG_APP_ID.Value = app_id
|
||||||
|
Responser.Headers.Add(IG.Header_IG_APP_ID, app_id)
|
||||||
|
End If
|
||||||
|
End If
|
||||||
Token_Photosby = RegexReplace(r, Regex_Photos_by)
|
Token_Photosby = RegexReplace(r, Regex_Photos_by)
|
||||||
If StoryBucket.IsEmptyString Then StoryBucket = RegexReplace(r, Regex_StoryBucket)
|
If StoryBucket.IsEmptyString Then StoryBucket = RegexReplace(r, Regex_StoryBucket)
|
||||||
If ID.IsEmptyString Then
|
If ID.IsEmptyString Then
|
||||||
@@ -511,8 +555,7 @@ Namespace API.Facebook
|
|||||||
#Region "Responser options"
|
#Region "Responser options"
|
||||||
Private Sub ResponserApplyDefs(ByVal __fb_friendly_name As String)
|
Private Sub ResponserApplyDefs(ByVal __fb_friendly_name As String)
|
||||||
With Responser
|
With Responser
|
||||||
.Headers.Add(ThreadsNet.UserData.Header_FB_LSD, Token_lsd)
|
UpdateHeadersGQL(__fb_friendly_name)
|
||||||
.Headers.Add(DeclaredNames.Header_FB_FRIENDLY_NAME, __fb_friendly_name)
|
|
||||||
.Method = "POST"
|
.Method = "POST"
|
||||||
.Accept = "*/*"
|
.Accept = "*/*"
|
||||||
.Referer = GetProfileUrl()
|
.Referer = GetProfileUrl()
|
||||||
@@ -532,14 +575,14 @@ Namespace API.Facebook
|
|||||||
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecFetchSite, "none"))
|
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecFetchSite, "none"))
|
||||||
.Add("Sec-Fetch-User", "?1")
|
.Add("Sec-Fetch-User", "?1")
|
||||||
.Add("Upgrade-Insecure-Requests", 1)
|
.Add("Upgrade-Insecure-Requests", 1)
|
||||||
Dim h$ = Responser.Headers.Value(IG.Header_Browser)
|
Dim cloneHeader As Action(Of String) = Sub(ByVal hName As String)
|
||||||
If Not h.IsEmptyString Then .Add(IG.Header_Browser, h)
|
Dim hValue$ = Responser.Headers.Value(hName)
|
||||||
h = Responser.Headers.Value(IG.Header_BrowserExt)
|
If Not hValue.IsEmptyString Then .Add(hName, hValue)
|
||||||
If Not h.IsEmptyString Then .Add(IG.Header_BrowserExt, h)
|
End Sub
|
||||||
h = .Value(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecChUaPlatform))
|
cloneHeader.Invoke(IG.Header_Browser)
|
||||||
If Not h.IsEmptyString Then .Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecChUaPlatform, h))
|
cloneHeader.Invoke(IG.Header_BrowserExt)
|
||||||
If ACheck(MySettings.HH_PLATFORM_VER.Value) Then _
|
cloneHeader.Invoke(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecChUaPlatform).Name)
|
||||||
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecChUaPlatformVersion, MySettings.HH_PLATFORM_VER.Value))
|
cloneHeader.Invoke(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecChUaPlatformVersion).Name)
|
||||||
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecChUaMobile, "?0"))
|
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecChUaMobile, "?0"))
|
||||||
.Add("Sec-Ch-Ua-Model", "")
|
.Add("Sec-Ch-Ua-Model", "")
|
||||||
End With
|
End With
|
||||||
@@ -631,6 +674,7 @@ Namespace API.Facebook
|
|||||||
Else
|
Else
|
||||||
URL = String.Format(VideoHtmlUrlPattern, m.Post.ID)
|
URL = String.Format(VideoHtmlUrlPattern, m.Post.ID)
|
||||||
End If
|
End If
|
||||||
|
WaitTimer()
|
||||||
r = resp.GetResponse(URL)
|
r = resp.GetResponse(URL)
|
||||||
If Not r.IsEmptyString Then
|
If Not r.IsEmptyString Then
|
||||||
re.Pattern = String.Format(pattern, nameHD)
|
re.Pattern = String.Format(pattern, nameHD)
|
||||||
|
|||||||
@@ -15,7 +15,12 @@ Namespace API.Instagram
|
|||||||
Friend Const InstagramSite As String = "Instagram"
|
Friend Const InstagramSite As String = "Instagram"
|
||||||
Friend Const InstagramSiteKey As String = "AndyProgram_Instagram"
|
Friend Const InstagramSiteKey As String = "AndyProgram_Instagram"
|
||||||
Friend ReadOnly FilesPattern As RParams = RParams.DMS(".+?([^/\?]+?\.[\w\d]{3,4})(?=(\?|\Z))", 1, EDP.ReturnValue)
|
Friend ReadOnly FilesPattern As RParams = RParams.DMS(".+?([^/\?]+?\.[\w\d]{3,4})(?=(\?|\Z))", 1, EDP.ReturnValue)
|
||||||
Friend Sub UpdateResponser(ByVal Source As IResponse, ByRef Destination As Responser)
|
Friend ReadOnly ObtainMedia_SizeFuncPic_RegexP As RParams = RParams.DMS("_p(\d+)x(\d+)", 1, EDP.ReturnValue)
|
||||||
|
Friend ReadOnly ObtainMedia_SizeFuncPic_RegexS As RParams = RParams.DMS("_s(\d+)x(\d+)", 1, EDP.ReturnValue)
|
||||||
|
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 Sub UpdateResponser(ByVal Source As IResponse, ByRef Destination As Responser, ByVal UpdateWwwClaim As Boolean)
|
||||||
Const r_wwwClaimName$ = "x-ig-set-www-claim"
|
Const r_wwwClaimName$ = "x-ig-set-www-claim"
|
||||||
Const r_tokenName$ = SiteSettings.Header_CSRF_TOKEN_COOKIE
|
Const r_tokenName$ = SiteSettings.Header_CSRF_TOKEN_COOKIE
|
||||||
If Not Source Is Nothing Then
|
If Not Source Is Nothing Then
|
||||||
@@ -32,17 +37,17 @@ Namespace API.Instagram
|
|||||||
Dim token$ = String.Empty
|
Dim token$ = String.Empty
|
||||||
With Source
|
With Source
|
||||||
If isInternal Then
|
If isInternal Then
|
||||||
If .HeadersExists Then wwwClaim = .Headers.Value(wwwClaimName)
|
If UpdateWwwClaim And .HeadersExists Then wwwClaim = .Headers.Value(wwwClaimName)
|
||||||
If .CookiesExists Then token = If(.Cookies.FirstOrDefault(Function(c) c.Name = tokenName)?.Value, String.Empty)
|
If .CookiesExists Then token = If(.Cookies.FirstOrDefault(Function(c) c.Name = tokenName)?.Value, String.Empty)
|
||||||
Else
|
Else
|
||||||
If .HeadersExists Then
|
If .HeadersExists Then
|
||||||
wwwClaim = .Headers.Value(wwwClaimName)
|
If UpdateWwwClaim Then wwwClaim = .Headers.Value(wwwClaimName)
|
||||||
token = .Headers.Value(tokenName)
|
token = .Headers.Value(tokenName)
|
||||||
End If
|
End If
|
||||||
End If
|
End If
|
||||||
End With
|
End With
|
||||||
|
|
||||||
If Not wwwClaim.IsEmptyString Then Destination.Headers.Add(SiteSettings.Header_IG_WWW_CLAIM, wwwClaim)
|
If UpdateWwwClaim And Not wwwClaim.IsEmptyString Then Destination.Headers.Add(SiteSettings.Header_IG_WWW_CLAIM, wwwClaim)
|
||||||
If Not token.IsEmptyString Then Destination.Headers.Add(SiteSettings.Header_CSRF_TOKEN, token)
|
If Not token.IsEmptyString Then Destination.Headers.Add(SiteSettings.Header_CSRF_TOKEN, token)
|
||||||
If Not isInternal Then
|
If Not isInternal Then
|
||||||
Destination.Cookies.Update(Source.Cookies, CookieKeeper.UpdateModes.ReplaceByNameAll, False, EDP.SendToLog)
|
Destination.Cookies.Update(Source.Cookies, CookieKeeper.UpdateModes.ReplaceByNameAll, False, EDP.SendToLog)
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ Namespace API.Instagram
|
|||||||
Friend Class EditorExchangeOptions
|
Friend Class EditorExchangeOptions
|
||||||
<PSetting(Caption:="Get timeline", ToolTip:="Download user timeline")>
|
<PSetting(Caption:="Get timeline", ToolTip:="Download user timeline")>
|
||||||
Friend Property GetTimeline As Boolean
|
Friend Property GetTimeline As Boolean
|
||||||
|
<PSetting(Caption:="Get reels", ToolTip:="Download user reels")>
|
||||||
|
Friend Property GetReels As Boolean
|
||||||
<PSetting(Caption:="Get stories", ToolTip:="Download user stories (pinned)")>
|
<PSetting(Caption:="Get stories", ToolTip:="Download user stories (pinned)")>
|
||||||
Friend Property GetStories As Boolean
|
Friend Property GetStories As Boolean
|
||||||
<PSetting(Caption:="Get stories: user", ToolTip:="Download user stories")>
|
<PSetting(Caption:="Get stories: user", ToolTip:="Download user stories")>
|
||||||
@@ -20,6 +22,7 @@ Namespace API.Instagram
|
|||||||
Friend Sub New(ByVal u As UserData)
|
Friend Sub New(ByVal u As UserData)
|
||||||
With u
|
With u
|
||||||
GetTimeline = .GetTimeline
|
GetTimeline = .GetTimeline
|
||||||
|
GetReels = .GetReels
|
||||||
GetStories = .GetStories
|
GetStories = .GetStories
|
||||||
GetStoriesUser = .GetStoriesUser
|
GetStoriesUser = .GetStoriesUser
|
||||||
GetTagged = .GetTaggedData
|
GetTagged = .GetTaggedData
|
||||||
@@ -28,6 +31,7 @@ Namespace API.Instagram
|
|||||||
Friend Sub New(ByVal s As SiteSettings)
|
Friend Sub New(ByVal s As SiteSettings)
|
||||||
With s
|
With s
|
||||||
GetTimeline = CBool(.GetTimeline.Value)
|
GetTimeline = CBool(.GetTimeline.Value)
|
||||||
|
GetReels = CBool(.GetReels.Value)
|
||||||
GetStories = CBool(.GetStories.Value)
|
GetStories = CBool(.GetStories.Value)
|
||||||
GetStoriesUser = CBool(.GetStoriesUser.Value)
|
GetStoriesUser = CBool(.GetStoriesUser.Value)
|
||||||
GetTagged = CBool(.GetTagged.Value)
|
GetTagged = CBool(.GetTagged.Value)
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ Namespace API.Instagram
|
|||||||
Friend Class SiteSettings : Inherits SiteSettingsBase
|
Friend Class SiteSettings : Inherits SiteSettingsBase
|
||||||
#Region "Declarations"
|
#Region "Declarations"
|
||||||
#Region "Providers"
|
#Region "Providers"
|
||||||
Private Class TimersChecker : Inherits FieldsCheckerProviderBase
|
Friend Class TimersChecker : Inherits FieldsCheckerProviderBase
|
||||||
Private ReadOnly LVProvider As New ANumbers With {.FormatOptions = ANumbers.Options.GroupIntegral}
|
Private ReadOnly LVProvider As New ANumbers With {.FormatOptions = ANumbers.Options.GroupIntegral}
|
||||||
Private ReadOnly _LowestValue As Integer
|
Private ReadOnly _LowestValue As Integer
|
||||||
Friend Sub New(ByVal LowestValue As Integer)
|
Friend Sub New(ByVal LowestValue As Integer)
|
||||||
@@ -32,7 +32,7 @@ Namespace API.Instagram
|
|||||||
If Not ACheck(Of Integer)(Value) Then
|
If Not ACheck(Of Integer)(Value) Then
|
||||||
TypeError = True
|
TypeError = True
|
||||||
ElseIf CInt(Value) < _LowestValue Then
|
ElseIf CInt(Value) < _LowestValue Then
|
||||||
ErrorMessage = $"The value of [{Name}] field must be greater than or equal to {_LowestValue.NumToString(LVProvider)}"
|
ErrorMessage = $"The value of '{Name}' field must be greater than or equal to {_LowestValue.NumToString(LVProvider)}"
|
||||||
HasError = True
|
HasError = True
|
||||||
Else
|
Else
|
||||||
Return Value
|
Return Value
|
||||||
@@ -47,7 +47,7 @@ Namespace API.Instagram
|
|||||||
If v > 0 Or v = -1 Then
|
If v > 0 Or v = -1 Then
|
||||||
Return Value
|
Return Value
|
||||||
Else
|
Else
|
||||||
ErrorMessage = $"The value of [{Name}] field must be greater than 0 or equal to -1"
|
ErrorMessage = $"The value of '{Name}' field must be greater than 0 or equal to -1"
|
||||||
HasError = True
|
HasError = True
|
||||||
Return Nothing
|
Return Nothing
|
||||||
End If
|
End If
|
||||||
@@ -62,25 +62,38 @@ Namespace API.Instagram
|
|||||||
Friend Const Header_ASBD_ID As String = "X-Asbd-Id"
|
Friend Const Header_ASBD_ID As String = "X-Asbd-Id"
|
||||||
Friend Const Header_Browser As String = "Sec-Ch-Ua"
|
Friend Const Header_Browser As String = "Sec-Ch-Ua"
|
||||||
Friend Const Header_BrowserExt As String = "Sec-Ch-Ua-Full-Version-List"
|
Friend Const Header_BrowserExt As String = "Sec-Ch-Ua-Full-Version-List"
|
||||||
Friend Const Header_Platform As String = "Sec-Ch-Ua-Platform-Version"
|
Friend Const Header_Platform_Verion As String = "Sec-Ch-Ua-Platform-Version"
|
||||||
<PropertyOption(ControlText:="Hash", ControlToolTip:="Instagram session hash for tagged posts", IsAuth:=True), PXML("InstaHash"), ControlNumber(0), PClonable(Clone:=False)>
|
<PropertyOption(ControlText:="x-csrftoken", ControlToolTip:="Can be automatically extracted from cookies", IsAuth:=True, AllowNull:=True), ControlNumber(2), PClonable(Clone:=False)>
|
||||||
Friend ReadOnly Property HashTagged As PropertyValue
|
|
||||||
<PropertyOption(ControlText:="x-csrftoken", IsAuth:=True, AllowNull:=False), ControlNumber(2), PClonable(Clone:=False)>
|
|
||||||
Friend ReadOnly Property HH_CSRF_TOKEN As PropertyValue
|
Friend ReadOnly Property HH_CSRF_TOKEN As PropertyValue
|
||||||
|
<CookieValueExtractor(NameOf(HH_CSRF_TOKEN))>
|
||||||
|
Private Function GetValueFromCookies(ByVal PropName As String, ByVal c As CookieKeeper) As String
|
||||||
|
Return c.GetCookieValue(Header_CSRF_TOKEN_COOKIE, PropName, NameOf(HH_CSRF_TOKEN))
|
||||||
|
End Function
|
||||||
<PropertyOption(ControlText:="x-ig-app-id", IsAuth:=True, AllowNull:=False), ControlNumber(3), PClonable(Clone:=False)>
|
<PropertyOption(ControlText:="x-ig-app-id", IsAuth:=True, AllowNull:=False), ControlNumber(3), PClonable(Clone:=False)>
|
||||||
Friend Property HH_IG_APP_ID As PropertyValue
|
Friend ReadOnly Property HH_IG_APP_ID As PropertyValue
|
||||||
<PropertyOption(ControlText:="x-asbd-id", IsAuth:=True, AllowNull:=True), ControlNumber(4), PClonable(Clone:=False)>
|
<PropertyOption(ControlText:="x-asbd-id", IsAuth:=True, AllowNull:=True), ControlNumber(4), PClonable(Clone:=False)>
|
||||||
Friend Property HH_ASBD_ID As PropertyValue
|
Friend ReadOnly Property HH_ASBD_ID As PropertyValue
|
||||||
<PropertyOption(ControlText:="x-ig-www-claim", IsAuth:=True, AllowNull:=True), ControlNumber(5), PClonable(Clone:=False)>
|
'PropertyOption(ControlText:="x-ig-www-claim", IsAuth:=True, AllowNull:=True)
|
||||||
Friend Property HH_IG_WWW_CLAIM As PropertyValue
|
<ControlNumber(5), PClonable(Clone:=False)>
|
||||||
<PropertyOption(ControlText:="sec-ch-ua", IsAuth:=True, AllowNull:=True), ControlNumber(6), PClonable>
|
Friend ReadOnly Property HH_IG_WWW_CLAIM As PropertyValue
|
||||||
Private Property HH_BROWSER As PropertyValue
|
Private ReadOnly Property HH_IG_WWW_CLAIM_IS_ZERO As Boolean
|
||||||
<PropertyOption(ControlText:="sec-ch-ua-full", ControlToolTip:="sec-ch-ua-full-version-list", IsAuth:=True, AllowNull:=True), ControlNumber(7), PClonable>
|
Get
|
||||||
Private Property HH_BROWSER_EXT As PropertyValue
|
Dim v$ = AConvert(Of String)(HH_IG_WWW_CLAIM.Value, String.Empty)
|
||||||
<PropertyOption(ControlText:="sec-ch-ua-platform-ver", ControlToolTip:="sec-ch-ua-platform-version", IsAuth:=True, AllowNull:=True), ControlNumber(8), PClonable>
|
Return Not v.IsEmptyString AndAlso v = "0"
|
||||||
Private Property HH_PLATFORM As PropertyValue
|
End Get
|
||||||
<PropertyOption(ControlText:="UserAgent", IsAuth:=True, AllowNull:=True), ControlNumber(9), PClonable>
|
End Property
|
||||||
Private Property HH_USER_AGENT As PropertyValue
|
<PropertyOption(ControlText:="sec-ch-ua", IsAuth:=True, AllowNull:=True,
|
||||||
|
InheritanceName:=SettingsCLS.HEADER_DEF_sec_ch_ua), ControlNumber(6), PClonable, PXML(OnlyForChecked:=True)>
|
||||||
|
Private ReadOnly Property HH_BROWSER As PropertyValue
|
||||||
|
<PropertyOption(ControlText:="sec-ch-ua-full", ControlToolTip:="sec-ch-ua-full-version-list", IsAuth:=True, AllowNull:=True,
|
||||||
|
InheritanceName:=SettingsCLS.HEADER_DEF_sec_ch_ua_full_version_list), ControlNumber(7), PClonable, PXML(OnlyForChecked:=True)>
|
||||||
|
Private ReadOnly Property HH_BROWSER_EXT As PropertyValue
|
||||||
|
<PropertyOption(ControlText:="sec-ch-ua-platform-ver", ControlToolTip:="sec-ch-ua-platform-version", IsAuth:=True, AllowNull:=True, LeftOffset:=135,
|
||||||
|
InheritanceName:=SettingsCLS.HEADER_DEF_sec_ch_ua_platform_version), ControlNumber(8), PClonable, PXML(OnlyForChecked:=True)>
|
||||||
|
Private ReadOnly Property HH_PLATFORM As PropertyValue
|
||||||
|
<PropertyOption(ControlText:="UserAgent", IsAuth:=True, AllowNull:=True,
|
||||||
|
InheritanceName:=SettingsCLS.HEADER_DEF_UserAgent), ControlNumber(9), PClonable, PXML(OnlyForChecked:=True)>
|
||||||
|
Private ReadOnly Property HH_USER_AGENT As PropertyValue
|
||||||
Friend Overrides Function BaseAuthExists() As Boolean
|
Friend Overrides Function BaseAuthExists() As Boolean
|
||||||
Return Responser.CookiesExists And ACheck(HH_IG_APP_ID.Value) And ACheck(HH_CSRF_TOKEN.Value)
|
Return Responser.CookiesExists And ACheck(HH_IG_APP_ID.Value) And ACheck(HH_CSRF_TOKEN.Value)
|
||||||
End Function
|
End Function
|
||||||
@@ -96,7 +109,7 @@ Namespace API.Instagram
|
|||||||
Case NameOf(HH_CSRF_TOKEN) : f = Header_CSRF_TOKEN
|
Case NameOf(HH_CSRF_TOKEN) : f = Header_CSRF_TOKEN
|
||||||
Case NameOf(HH_BROWSER) : f = Header_Browser
|
Case NameOf(HH_BROWSER) : f = Header_Browser
|
||||||
Case NameOf(HH_BROWSER_EXT) : f = Header_BrowserExt
|
Case NameOf(HH_BROWSER_EXT) : f = Header_BrowserExt
|
||||||
Case NameOf(HH_PLATFORM) : f = Header_Platform
|
Case NameOf(HH_PLATFORM) : f = Header_Platform_Verion
|
||||||
Case NameOf(HH_USER_AGENT) : isUserAgent = True
|
Case NameOf(HH_USER_AGENT) : isUserAgent = True
|
||||||
End Select
|
End Select
|
||||||
If Not f.IsEmptyString Then
|
If Not f.IsEmptyString Then
|
||||||
@@ -107,27 +120,67 @@ Namespace API.Instagram
|
|||||||
End If
|
End If
|
||||||
End If
|
End If
|
||||||
End Sub
|
End Sub
|
||||||
|
#Region "HH_IG_WWW_CLAIM"
|
||||||
|
<PropertyOption(ControlText:="ig-www-claim update interval", IsAuth:=True, LeftOffset:=150), PXML, ControlNumber(10), PClonable, HiddenControl>
|
||||||
|
Private ReadOnly Property HH_IG_WWW_CLAIM_UPDATE_INTERVAL As PropertyValue
|
||||||
|
<PropertyOption(ControlText:="ig-www-claim: always 0", ControlToolTip:="Keep token value always = 0", IsAuth:=True),
|
||||||
|
PXML, ControlNumber(11), PClonable, HiddenControl>
|
||||||
|
Friend ReadOnly Property HH_IG_WWW_CLAIM_ALWAYS_ZERO As PropertyValue
|
||||||
|
<PropertyOption(ControlText:="ig-www-claim: reset each session", ControlToolTip:="Set 'x-ig-www-claim' to '0' before each session", IsAuth:=True),
|
||||||
|
PXML, ControlNumber(12), PClonable, HiddenControl>
|
||||||
|
Friend ReadOnly Property HH_IG_WWW_CLAIM_RESET_EACH_SESSION As PropertyValue
|
||||||
|
<PropertyOption(ControlText:="ig-www-claim: reset each target", ControlToolTip:="Set 'x-ig-www-claim' to '0' before each target", IsAuth:=True),
|
||||||
|
PXML, ControlNumber(13), PClonable, HiddenControl>
|
||||||
|
Friend ReadOnly Property HH_IG_WWW_CLAIM_RESET_EACH_TARGET As PropertyValue
|
||||||
|
<PropertyOption(ControlText:="ig-www-claim: use in requests", IsAuth:=True), PXML, ControlNumber(14), PClonable, HiddenControl>
|
||||||
|
Friend ReadOnly Property HH_IG_WWW_CLAIM_USE As PropertyValue
|
||||||
|
<PropertyOption(ControlText:="ig-www-claim: use default algorithm to update", IsAuth:=True), PXML, ControlNumber(15), PClonable, HiddenControl>
|
||||||
|
Friend ReadOnly Property HH_IG_WWW_CLAIM_USE_DEFAULT_ALGO As PropertyValue
|
||||||
|
<Provider(NameOf(HH_IG_WWW_CLAIM_UPDATE_INTERVAL), FieldsChecker:=True)>
|
||||||
|
Private ReadOnly Property TokenUpdateIntervalProvider As IFormatProvider
|
||||||
|
#End Region
|
||||||
|
<PropertyOption(ControlText:="Use GraphQL to download", IsAuth:=True), PXML, ControlNumber(16), PClonable>
|
||||||
|
Friend ReadOnly Property USE_GQL As PropertyValue
|
||||||
#End Region
|
#End Region
|
||||||
#Region "Download properties"
|
#Region "Download properties"
|
||||||
<PropertyOption(ControlText:="Request timer", AllowNull:=False), PXML("RequestsWaitTimer"), ControlNumber(20), PClonable>
|
Friend Const TimersUrgentTip As String = vbCr & "It is highly recommended not to change the default value."
|
||||||
|
<PropertyOption(ControlText:="Request timer (any)",
|
||||||
|
ControlToolTip:="The timer (in milliseconds) that SCrawler should wait before executing the next request." &
|
||||||
|
vbCr & "The default value is 1'000." & vbCr & "The minimum value is 0." & TimersUrgentTip, AllowNull:=False),
|
||||||
|
PXML, ControlNumber(19), PClonable>
|
||||||
|
Friend ReadOnly Property RequestsWaitTimer_Any As PropertyValue
|
||||||
|
<Provider(NameOf(RequestsWaitTimer_Any), FieldsChecker:=True)>
|
||||||
|
Private ReadOnly Property RequestsWaitTimer_AnyProvider As IFormatProvider
|
||||||
|
<PropertyOption(ControlText:="Request timer",
|
||||||
|
ControlToolTip:="The time value (in milliseconds) that the program will wait before processing the next 'Request time counter' request." &
|
||||||
|
vbCr & "The default value is 1'000." & vbCr & "The minimum value is 100." & TimersUrgentTip,
|
||||||
|
AllowNull:=False), PXML, ControlNumber(20), PClonable>
|
||||||
Friend ReadOnly Property RequestsWaitTimer As PropertyValue
|
Friend ReadOnly Property RequestsWaitTimer As PropertyValue
|
||||||
<Provider(NameOf(RequestsWaitTimer), FieldsChecker:=True)>
|
<Provider(NameOf(RequestsWaitTimer), FieldsChecker:=True)>
|
||||||
Private ReadOnly Property RequestsWaitTimerProvider As IFormatProvider
|
Private ReadOnly Property RequestsWaitTimerProvider As IFormatProvider
|
||||||
<PropertyOption(ControlText:="Request timer counter", AllowNull:=False, LeftOffset:=120), PXML("RequestsWaitTimerTaskCount"), ControlNumber(21), PClonable>
|
<PropertyOption(ControlText:="Request timer counter",
|
||||||
|
ControlToolTip:="How many requests will be sent to Instagram before the program waits 'Request timer'." &
|
||||||
|
vbCr & "The default value is 1." & vbCr & "The minimum value is 1." & TimersUrgentTip,
|
||||||
|
AllowNull:=False, LeftOffset:=120), PXML, ControlNumber(21), PClonable>
|
||||||
Friend ReadOnly Property RequestsWaitTimerTaskCount As PropertyValue
|
Friend ReadOnly Property RequestsWaitTimerTaskCount As PropertyValue
|
||||||
<Provider(NameOf(RequestsWaitTimerTaskCount), FieldsChecker:=True)>
|
<Provider(NameOf(RequestsWaitTimerTaskCount), FieldsChecker:=True)>
|
||||||
Private ReadOnly Property RequestsWaitTimerTaskCountProvider As IFormatProvider
|
Private ReadOnly Property RequestsWaitTimerTaskCountProvider As IFormatProvider
|
||||||
<PropertyOption(ControlText:="Posts limit timer", AllowNull:=False), PXML("SleepTimerOnPostsLimit"), ControlNumber(22), PClonable>
|
<PropertyOption(ControlText:="Posts limit timer",
|
||||||
|
ControlToolTip:="The time value (in milliseconds) the program will wait before processing the next request after 195 requests." &
|
||||||
|
vbCr & "The default value is 60'000." & vbCr & "The minimum value is 10'000." & TimersUrgentTip,
|
||||||
|
AllowNull:=False), PXML, ControlNumber(22), PClonable>
|
||||||
Friend ReadOnly Property SleepTimerOnPostsLimit As PropertyValue
|
Friend ReadOnly Property SleepTimerOnPostsLimit As PropertyValue
|
||||||
<Provider(NameOf(SleepTimerOnPostsLimit), FieldsChecker:=True)>
|
<Provider(NameOf(SleepTimerOnPostsLimit), FieldsChecker:=True)>
|
||||||
Private ReadOnly Property SleepTimerOnPostsLimitProvider As IFormatProvider
|
Private ReadOnly Property SleepTimerOnPostsLimitProvider As IFormatProvider
|
||||||
<PropertyOption(ControlText:="Get timeline", ControlToolTip:="Default value for new users"), PXML, ControlNumber(23), PClonable>
|
<PropertyOption(ControlText:="Get timeline", ControlToolTip:="Default value for new users"), PXML, ControlNumber(23), PClonable>
|
||||||
Friend ReadOnly Property GetTimeline As PropertyValue
|
Friend ReadOnly Property GetTimeline As PropertyValue
|
||||||
<PropertyOption(ControlText:="Get stories", ControlToolTip:="Default value for new users"), PXML, ControlNumber(24), PClonable>
|
<PropertyOption(ControlText:="Get reels", ControlToolTip:="Default value for new users"), PXML, ControlNumber(24), PClonable>
|
||||||
|
Friend ReadOnly Property GetReels As PropertyValue
|
||||||
|
<PropertyOption(ControlText:="Get stories", ControlToolTip:="Default value for new users"), PXML, ControlNumber(25), PClonable>
|
||||||
Friend ReadOnly Property GetStories As PropertyValue
|
Friend ReadOnly Property GetStories As PropertyValue
|
||||||
<PropertyOption(ControlText:="Get stories: user", ControlToolTip:="Default value for new users"), PXML, ControlNumber(25), PClonable>
|
<PropertyOption(ControlText:="Get stories: user", ControlToolTip:="Default value for new users"), PXML, ControlNumber(26), PClonable>
|
||||||
Friend ReadOnly Property GetStoriesUser As PropertyValue
|
Friend ReadOnly Property GetStoriesUser As PropertyValue
|
||||||
<PropertyOption(ControlText:="Get tagged photos", ControlToolTip:="Default value for new users"), PXML, ControlNumber(26), PClonable>
|
<PropertyOption(ControlText:="Get tagged photos", ControlToolTip:="Default value for new users"), PXML, ControlNumber(27), PClonable>
|
||||||
Friend ReadOnly Property GetTagged As PropertyValue
|
Friend ReadOnly Property GetTagged As PropertyValue
|
||||||
<PropertyOption(ControlText:="Tagged notify limit",
|
<PropertyOption(ControlText:="Tagged notify limit",
|
||||||
ControlToolTip:="If the number of tagged posts exceeds this number you will be notified." & vbCr &
|
ControlToolTip:="If the number of tagged posts exceeds this number you will be notified." & vbCr &
|
||||||
@@ -139,16 +192,38 @@ Namespace API.Instagram
|
|||||||
#Region "Download ready"
|
#Region "Download ready"
|
||||||
<PropertyOption(ControlText:="Download timeline", ControlToolTip:="Download timeline"), PXML, ControlNumber(10), PClonable>
|
<PropertyOption(ControlText:="Download timeline", ControlToolTip:="Download timeline"), PXML, ControlNumber(10), PClonable>
|
||||||
Friend ReadOnly Property DownloadTimeline As PropertyValue
|
Friend ReadOnly Property DownloadTimeline As PropertyValue
|
||||||
<PropertyOption(ControlText:="Download stories", ControlToolTip:="Download stories"), PXML, ControlNumber(11), PClonable>
|
<PXML> Private ReadOnly Property DownloadTimeline_Def As PropertyValue
|
||||||
|
<PropertyOption(ControlText:="Download reels", ControlToolTip:="Download reels"), PXML, ControlNumber(11), PClonable>
|
||||||
|
Friend ReadOnly Property DownloadReels As PropertyValue
|
||||||
|
<PXML> Private ReadOnly Property DownloadReels_Def As PropertyValue
|
||||||
|
<PropertyOption(ControlText:="Download stories", ControlToolTip:="Download stories"), PXML, ControlNumber(12), PClonable>
|
||||||
Friend ReadOnly Property DownloadStories As PropertyValue
|
Friend ReadOnly Property DownloadStories As PropertyValue
|
||||||
<PropertyOption(ControlText:="Download stories: user", ControlToolTip:="Download stories (user)"), PXML, ControlNumber(12), PClonable>
|
<PXML> Private ReadOnly Property DownloadStories_Def As PropertyValue
|
||||||
|
<PropertyOption(ControlText:="Download stories: user", ControlToolTip:="Download stories (user)"), PXML, ControlNumber(13), PClonable>
|
||||||
Friend ReadOnly Property DownloadStoriesUser As PropertyValue
|
Friend ReadOnly Property DownloadStoriesUser As PropertyValue
|
||||||
<PropertyOption(ControlText:="Download tagged", ControlToolTip:="Download tagged posts"), PXML, ControlNumber(13), PClonable>
|
<PXML> Private ReadOnly Property DownloadStoriesUser_Def As PropertyValue
|
||||||
|
<PropertyOption(ControlText:="Download tagged", ControlToolTip:="Download tagged posts"), PXML, ControlNumber(14), PClonable>
|
||||||
Friend ReadOnly Property DownloadTagged As PropertyValue
|
Friend ReadOnly Property DownloadTagged As PropertyValue
|
||||||
|
<PXML> Private ReadOnly Property DownloadTagged_Def As PropertyValue
|
||||||
#End Region
|
#End Region
|
||||||
#Region "429 bypass"
|
#Region "429 bypass"
|
||||||
<PXML("InstagramDownloadingErrorDate")>
|
<PXML("InstagramDownloadingErrorDate")>
|
||||||
Private ReadOnly Property DownloadingErrorDate As PropertyValue
|
Private ReadOnly Property DownloadingErrorDate As PropertyValue
|
||||||
|
<Provider(NameOf(DownloadingErrorDate))>
|
||||||
|
Private ReadOnly Property DownloadingErrorDateProvider As IFormatProvider =
|
||||||
|
New CustomProvider(Function(ByVal v As Object, ByVal d As Type) As Object
|
||||||
|
If d Is GetType(Date) Then
|
||||||
|
Return AConvert(Of Date)(v, AModes.Var, Nothing)
|
||||||
|
ElseIf d Is GetType(String) Then
|
||||||
|
If Not IsNothing(v) AndAlso TypeOf v Is Date AndAlso CDate(v) = Date.MinValue Then
|
||||||
|
Return String.Empty
|
||||||
|
Else
|
||||||
|
Return AConvert(Of String)(v, AModes.XML, String.Empty)
|
||||||
|
End If
|
||||||
|
Else
|
||||||
|
Return Nothing
|
||||||
|
End If
|
||||||
|
End Function)
|
||||||
Friend Property LastApplyingValue As Integer? = Nothing
|
Friend Property LastApplyingValue As Integer? = Nothing
|
||||||
Friend ReadOnly Property ReadyForDownload As Boolean
|
Friend ReadOnly Property ReadyForDownload As Boolean
|
||||||
Get
|
Get
|
||||||
@@ -162,11 +237,64 @@ Namespace API.Instagram
|
|||||||
End With
|
End With
|
||||||
End Get
|
End Get
|
||||||
End Property
|
End Property
|
||||||
|
Private Const LastDownloadDateResetInterval As Integer = 60
|
||||||
<PXML> Private ReadOnly Property LastDownloadDate As PropertyValue
|
<PXML> Private ReadOnly Property LastDownloadDate As PropertyValue
|
||||||
<PXML> Private ReadOnly Property LastRequestsCount As PropertyValue
|
<PXML> Private ReadOnly Property LastRequestsCount As PropertyValue
|
||||||
|
Private ReadOnly MyLastRequests As Dictionary(Of Date, Integer)
|
||||||
|
Private ReadOnly Property MyLastRequestsDate As Date
|
||||||
|
Get
|
||||||
|
Try
|
||||||
|
Return If(MyLastRequests.Count > 0, MyLastRequests.Keys.Max, Now.AddDays(-1))
|
||||||
|
Catch ex As Exception
|
||||||
|
Return ErrorsDescriber.Execute(EDP.SendToLog + EDP.ReturnValue, ex, "[SiteSettings.Instagram.MyLastRequestsDate]", Now.AddDays(-1))
|
||||||
|
End Try
|
||||||
|
End Get
|
||||||
|
End Property
|
||||||
|
Private Property MyLastRequestsCount As Integer
|
||||||
|
Get
|
||||||
|
Try
|
||||||
|
Return If(MyLastRequests.Count > 0, MyLastRequests.Values.Sum, 0)
|
||||||
|
Catch ex As Exception
|
||||||
|
Return ErrorsDescriber.Execute(EDP.SendToLog + EDP.ReturnValue, ex, "[SiteSettings.Instagram.MyLastRequestsCount]", 0)
|
||||||
|
End Try
|
||||||
|
End Get
|
||||||
|
Set(ByVal NewValue As Integer)
|
||||||
|
If Not MyLastRequests.ContainsKey(ActiveSessionDate) Then
|
||||||
|
MyLastRequests.Add(ActiveSessionDate, NewValue)
|
||||||
|
Else
|
||||||
|
MyLastRequests(ActiveSessionDate) += NewValue
|
||||||
|
End If
|
||||||
|
End Set
|
||||||
|
End Property
|
||||||
|
Private Sub RefreshMyLastRequests(Optional ByVal DateToReplace As Date? = Nothing)
|
||||||
|
Try
|
||||||
|
With MyLastRequests
|
||||||
|
If .Count > 0 Then
|
||||||
|
Dim d As Date
|
||||||
|
For i% = .Count - 1 To 0 Step -1
|
||||||
|
d = .Keys(i)
|
||||||
|
If (Not DateToReplace.HasValue OrElse ActiveJobs < 1 OrElse d <> ActiveSessionDate) And
|
||||||
|
d.AddMinutes(LastDownloadDateResetInterval) < Now Then .Remove(d)
|
||||||
|
Next
|
||||||
|
End If
|
||||||
|
If .Count > 0 Then
|
||||||
|
If DateToReplace.HasValue Then
|
||||||
|
If .Keys.Contains(ActiveSessionDate) Then
|
||||||
|
Dim v% = .Item(ActiveSessionDate)
|
||||||
|
.Remove(ActiveSessionDate)
|
||||||
|
.Add(DateToReplace.Value, v)
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
LastDownloadDate.Value = .Keys.Max
|
||||||
|
LastRequestsCount.Value = .Values.Sum
|
||||||
|
End If
|
||||||
|
End With
|
||||||
|
Catch ex As Exception
|
||||||
|
ErrorsDescriber.Execute(EDP.SendToLog, ex, "[SiteSettings.Instagram.RefreshMyLastRequests]")
|
||||||
|
End Try
|
||||||
|
End Sub
|
||||||
<PropertyOption(IsInformationLabel:=True), ControlNumber(100)>
|
<PropertyOption(IsInformationLabel:=True), ControlNumber(100)>
|
||||||
Private Property LastRequestsCountLabel As PropertyValue
|
Private ReadOnly Property LastRequestsCountLabel As PropertyValue
|
||||||
Private ReadOnly LastRequestsCountLabelStr As Func(Of Integer, String) = Function(r) $"Number of spent requests: {r.NumToGroupIntegral}"
|
|
||||||
Private TooManyRequestsReadyForCatch As Boolean = True
|
Private TooManyRequestsReadyForCatch As Boolean = True
|
||||||
Friend Function GetWaitDate() As Date
|
Friend Function GetWaitDate() As Date
|
||||||
With DownloadingErrorDate
|
With DownloadingErrorDate
|
||||||
@@ -221,13 +349,16 @@ Namespace API.Instagram
|
|||||||
asbd = .Value(Header_ASBD_ID)
|
asbd = .Value(Header_ASBD_ID)
|
||||||
browser = .Value(Header_Browser)
|
browser = .Value(Header_Browser)
|
||||||
browserExt = .Value(Header_BrowserExt)
|
browserExt = .Value(Header_BrowserExt)
|
||||||
platform = .Value(Header_Platform)
|
platform = .Value(Header_Platform_Verion)
|
||||||
End If
|
End If
|
||||||
|
'.Add(Header_IG_WWW_CLAIM, 0)
|
||||||
.Add("Dnt", 1)
|
.Add("Dnt", 1)
|
||||||
|
.Add("Dpr", 1)
|
||||||
.Add("Sec-Ch-Ua-Mobile", "?0")
|
.Add("Sec-Ch-Ua-Mobile", "?0")
|
||||||
|
.Add("Sec-Ch-Ua-Model", """""")
|
||||||
.Add("Sec-Ch-Ua-Platform", """Windows""")
|
.Add("Sec-Ch-Ua-Platform", """Windows""")
|
||||||
.Add("Sec-Fetch-Dest", "empty")
|
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecFetchDest, "empty"))
|
||||||
.Add("Sec-Fetch-Mode", "cors")
|
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecFetchMode, "cors"))
|
||||||
.Add("Sec-Fetch-Site", "same-origin")
|
.Add("Sec-Fetch-Site", "same-origin")
|
||||||
.Add("X-Requested-With", "XMLHttpRequest")
|
.Add("X-Requested-With", "XMLHttpRequest")
|
||||||
End With
|
End With
|
||||||
@@ -236,7 +367,6 @@ Namespace API.Instagram
|
|||||||
.CookiesExtractedAutoSave = False
|
.CookiesExtractedAutoSave = False
|
||||||
End With
|
End With
|
||||||
|
|
||||||
HashTagged = New PropertyValue(String.Empty, GetType(String))
|
|
||||||
HH_CSRF_TOKEN = New PropertyValue(token, GetType(String), Sub(v) ChangeResponserFields(NameOf(HH_CSRF_TOKEN), v))
|
HH_CSRF_TOKEN = New PropertyValue(token, GetType(String), Sub(v) ChangeResponserFields(NameOf(HH_CSRF_TOKEN), v))
|
||||||
HH_IG_APP_ID = New PropertyValue(app_id, GetType(String), Sub(v) ChangeResponserFields(NameOf(HH_IG_APP_ID), v))
|
HH_IG_APP_ID = New PropertyValue(app_id, GetType(String), Sub(v) ChangeResponserFields(NameOf(HH_IG_APP_ID), v))
|
||||||
HH_ASBD_ID = New PropertyValue(asbd, GetType(String), Sub(v) ChangeResponserFields(NameOf(HH_ASBD_ID), v))
|
HH_ASBD_ID = New PropertyValue(asbd, GetType(String), Sub(v) ChangeResponserFields(NameOf(HH_ASBD_ID), v))
|
||||||
@@ -246,11 +376,28 @@ Namespace API.Instagram
|
|||||||
HH_PLATFORM = New PropertyValue(platform, GetType(String), Sub(v) ChangeResponserFields(NameOf(HH_PLATFORM), v))
|
HH_PLATFORM = New PropertyValue(platform, GetType(String), Sub(v) ChangeResponserFields(NameOf(HH_PLATFORM), v))
|
||||||
HH_USER_AGENT = New PropertyValue(useragent, GetType(String), Sub(v) ChangeResponserFields(NameOf(HH_USER_AGENT), v))
|
HH_USER_AGENT = New PropertyValue(useragent, GetType(String), Sub(v) ChangeResponserFields(NameOf(HH_USER_AGENT), v))
|
||||||
|
|
||||||
DownloadTimeline = New PropertyValue(True)
|
HH_IG_WWW_CLAIM_UPDATE_INTERVAL = New PropertyValue(120)
|
||||||
DownloadStories = New PropertyValue(True)
|
HH_IG_WWW_CLAIM_ALWAYS_ZERO = New PropertyValue(False)
|
||||||
DownloadStoriesUser = New PropertyValue(True)
|
HH_IG_WWW_CLAIM_RESET_EACH_SESSION = New PropertyValue(True)
|
||||||
DownloadTagged = New PropertyValue(False)
|
HH_IG_WWW_CLAIM_RESET_EACH_TARGET = New PropertyValue(True)
|
||||||
|
HH_IG_WWW_CLAIM_USE = New PropertyValue(True)
|
||||||
|
HH_IG_WWW_CLAIM_USE_DEFAULT_ALGO = New PropertyValue(True)
|
||||||
|
TokenUpdateIntervalProvider = New TokenRefreshIntervalProvider
|
||||||
|
USE_GQL = New PropertyValue(False)
|
||||||
|
|
||||||
|
DownloadTimeline = New PropertyValue(True)
|
||||||
|
DownloadTimeline_Def = New PropertyValue(DownloadTimeline.Value, GetType(Boolean))
|
||||||
|
DownloadReels = New PropertyValue(False)
|
||||||
|
DownloadReels_Def = New PropertyValue(DownloadReels.Value, GetType(Boolean))
|
||||||
|
DownloadStories = New PropertyValue(True)
|
||||||
|
DownloadStories_Def = New PropertyValue(DownloadStories.Value, GetType(Boolean))
|
||||||
|
DownloadStoriesUser = New PropertyValue(True)
|
||||||
|
DownloadStoriesUser_Def = New PropertyValue(DownloadStoriesUser.Value, GetType(Boolean))
|
||||||
|
DownloadTagged = New PropertyValue(False)
|
||||||
|
DownloadTagged_Def = New PropertyValue(DownloadTagged.Value, GetType(Boolean))
|
||||||
|
|
||||||
|
RequestsWaitTimer_Any = New PropertyValue(1000)
|
||||||
|
RequestsWaitTimer_AnyProvider = New TimersChecker(0)
|
||||||
RequestsWaitTimer = New PropertyValue(1000)
|
RequestsWaitTimer = New PropertyValue(1000)
|
||||||
RequestsWaitTimerProvider = New TimersChecker(100)
|
RequestsWaitTimerProvider = New TimersChecker(100)
|
||||||
RequestsWaitTimerTaskCount = New PropertyValue(1)
|
RequestsWaitTimerTaskCount = New PropertyValue(1)
|
||||||
@@ -259,23 +406,29 @@ Namespace API.Instagram
|
|||||||
SleepTimerOnPostsLimitProvider = New TimersChecker(10000)
|
SleepTimerOnPostsLimitProvider = New TimersChecker(10000)
|
||||||
|
|
||||||
GetTimeline = New PropertyValue(True)
|
GetTimeline = New PropertyValue(True)
|
||||||
|
GetReels = New PropertyValue(False)
|
||||||
GetStories = New PropertyValue(False)
|
GetStories = New PropertyValue(False)
|
||||||
GetStoriesUser = New PropertyValue(False)
|
GetStoriesUser = New PropertyValue(False)
|
||||||
GetTagged = New PropertyValue(False)
|
GetTagged = New PropertyValue(False)
|
||||||
TaggedNotifyLimit = New PropertyValue(200)
|
TaggedNotifyLimit = New PropertyValue(200)
|
||||||
TaggedNotifyLimitProvider = New TaggedNotifyLimitChecker
|
TaggedNotifyLimitProvider = New TaggedNotifyLimitChecker
|
||||||
|
|
||||||
DownloadingErrorDate = New PropertyValue(Nothing, GetType(Date))
|
DownloadingErrorDate = New PropertyValue(Now.AddYears(10), GetType(Date))
|
||||||
LastDownloadDate = New PropertyValue(Now.AddDays(-1))
|
LastDownloadDate = New PropertyValue(Now.AddDays(-1))
|
||||||
LastRequestsCount = New PropertyValue(0)
|
LastRequestsCount = New PropertyValue(0)
|
||||||
LastRequestsCountLabel = New PropertyValue(LastRequestsCountLabelStr.Invoke(LastRequestsCount.Value))
|
LastRequestsCountLabel = New PropertyValue(String.Empty, GetType(String))
|
||||||
LastRequestsCount.OnChangeFunction = Sub(vv) LastRequestsCountLabel.Value = LastRequestsCountLabelStr.Invoke(vv)
|
MyLastRequests = New Dictionary(Of Date, Integer)
|
||||||
|
|
||||||
_AllowUserAgentUpdate = False
|
_AllowUserAgentUpdate = False
|
||||||
UrlPatternUser = "https://www.instagram.com/{0}/"
|
UrlPatternUser = "https://www.instagram.com/{0}/"
|
||||||
UserRegex = RParams.DMS("[htps:/]{7,8}.*?instagram.com/([^/]+)", 1)
|
UserRegex = RParams.DMS(String.Format(UserRegexDefaultPattern, "instagram.com/"), 1)
|
||||||
ImageVideoContains = "instagram.com"
|
ImageVideoContains = "instagram.com"
|
||||||
End Sub
|
End Sub
|
||||||
|
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)
|
||||||
|
MyBase.EndInit()
|
||||||
|
End Sub
|
||||||
#End Region
|
#End Region
|
||||||
#Region "PropertiesDataChecker"
|
#Region "PropertiesDataChecker"
|
||||||
<PropertiesDataChecker({NameOf(TaggedNotifyLimit)})>
|
<PropertiesDataChecker({NameOf(TaggedNotifyLimit)})>
|
||||||
@@ -308,11 +461,23 @@ Namespace API.Instagram
|
|||||||
Return ActiveJobs < 2 AndAlso Not SkipUntilNextSession AndAlso ReadyForDownload AndAlso BaseAuthExists() AndAlso DownloadTimeline.Value
|
Return ActiveJobs < 2 AndAlso Not SkipUntilNextSession AndAlso ReadyForDownload AndAlso BaseAuthExists() AndAlso DownloadTimeline.Value
|
||||||
End Function
|
End Function
|
||||||
Private ActiveJobs As Integer = 0
|
Private ActiveJobs As Integer = 0
|
||||||
|
Private ActiveSessionDate As Date
|
||||||
Private _NextWNM As UserData.WNM = UserData.WNM.Notify
|
Private _NextWNM As UserData.WNM = UserData.WNM.Notify
|
||||||
Private _NextTagged As Boolean = True
|
Private _NextTagged As Boolean = True
|
||||||
Friend Overrides Sub DownloadStarted(ByVal What As Download)
|
Friend Overrides Sub DownloadStarted(ByVal What As Download)
|
||||||
ActiveJobs += 1
|
ActiveJobs += 1
|
||||||
If CDate(LastDownloadDate.Value).AddMinutes(120) < Now Or Not ACheck(HH_IG_WWW_CLAIM.Value) Then HH_IG_WWW_CLAIM.Value = "0"
|
If ActiveJobs = 1 Then ActiveSessionDate = Now
|
||||||
|
If Not HH_IG_WWW_CLAIM_IS_ZERO AndAlso
|
||||||
|
(
|
||||||
|
(CBool(HH_IG_WWW_CLAIM_USE_DEFAULT_ALGO.Value) AndAlso MyLastRequestsDate.AddMinutes(HH_IG_WWW_CLAIM_UPDATE_INTERVAL.Value) < Now) Or
|
||||||
|
Not ACheck(HH_IG_WWW_CLAIM.Value) Or
|
||||||
|
(
|
||||||
|
Not (
|
||||||
|
CBool(HH_IG_WWW_CLAIM_USE_DEFAULT_ALGO.Value) And
|
||||||
|
(CBool(HH_IG_WWW_CLAIM_RESET_EACH_SESSION.Value) Or CBool(HH_IG_WWW_CLAIM_ALWAYS_ZERO.Value))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
) Then HH_IG_WWW_CLAIM.Value = "0"
|
||||||
End Sub
|
End Sub
|
||||||
Friend Overrides Sub BeforeStartDownload(ByVal User As Object, ByVal What As Download)
|
Friend Overrides Sub BeforeStartDownload(ByVal User As Object, ByVal What As Download)
|
||||||
With DirectCast(User, UserData)
|
With DirectCast(User, UserData)
|
||||||
@@ -320,10 +485,9 @@ Namespace API.Instagram
|
|||||||
.WaitNotificationMode = _NextWNM
|
.WaitNotificationMode = _NextWNM
|
||||||
.TaggedCheckSession = _NextTagged
|
.TaggedCheckSession = _NextTagged
|
||||||
End If
|
End If
|
||||||
If CDate(LastDownloadDate.Value).AddMinutes(60) > Now Then
|
If MyLastRequestsDate.AddMinutes(LastDownloadDateResetInterval) > Now Then
|
||||||
.RequestsCount = LastRequestsCount.Value
|
.RequestsCount = MyLastRequestsCount
|
||||||
Else
|
Else
|
||||||
LastRequestsCount.Value = 0
|
|
||||||
.RequestsCount = 0
|
.RequestsCount = 0
|
||||||
End If
|
End If
|
||||||
End With
|
End With
|
||||||
@@ -333,8 +497,7 @@ Namespace API.Instagram
|
|||||||
_NextWNM = .WaitNotificationMode
|
_NextWNM = .WaitNotificationMode
|
||||||
If _NextWNM = UserData.WNM.SkipTemp Or _NextWNM = UserData.WNM.SkipCurrent Then _NextWNM = UserData.WNM.Notify
|
If _NextWNM = UserData.WNM.SkipTemp Or _NextWNM = UserData.WNM.SkipCurrent Then _NextWNM = UserData.WNM.Notify
|
||||||
_NextTagged = .TaggedCheckSession
|
_NextTagged = .TaggedCheckSession
|
||||||
LastDownloadDate.Value = Now
|
MyLastRequestsCount = .RequestsCountSession
|
||||||
LastRequestsCount.Value = .RequestsCount
|
|
||||||
_FieldsChangerSuspended = True
|
_FieldsChangerSuspended = True
|
||||||
HH_IG_WWW_CLAIM.Value = Responser.Headers.Value(Header_IG_WWW_CLAIM)
|
HH_IG_WWW_CLAIM.Value = Responser.Headers.Value(Header_IG_WWW_CLAIM)
|
||||||
HH_CSRF_TOKEN.Value = Responser.Headers.Value(Header_CSRF_TOKEN)
|
HH_CSRF_TOKEN.Value = Responser.Headers.Value(Header_CSRF_TOKEN)
|
||||||
@@ -344,11 +507,91 @@ Namespace API.Instagram
|
|||||||
Friend Overrides Sub DownloadDone(ByVal What As Download)
|
Friend Overrides Sub DownloadDone(ByVal What As Download)
|
||||||
_NextWNM = UserData.WNM.Notify
|
_NextWNM = UserData.WNM.Notify
|
||||||
_NextTagged = True
|
_NextTagged = True
|
||||||
LastDownloadDate.Value = Now
|
RefreshMyLastRequests(Now)
|
||||||
ActiveJobs -= 1
|
ActiveJobs -= 1
|
||||||
SkipUntilNextSession = False
|
SkipUntilNextSession = False
|
||||||
End Sub
|
End Sub
|
||||||
#End Region
|
#End Region
|
||||||
|
#Region "Settings"
|
||||||
|
Private ____HH_CSRF_TOKEN As String = String.Empty
|
||||||
|
Private ____HH_IG_APP_ID As String = String.Empty
|
||||||
|
Private ____HH_ASBD_ID As String = String.Empty
|
||||||
|
Private ____HH_BROWSER As String = String.Empty
|
||||||
|
Private ____HH_BROWSER_EXT As String = String.Empty
|
||||||
|
Private ____HH_PLATFORM As String = String.Empty
|
||||||
|
Private ____HH_USER_AGENT As String = String.Empty
|
||||||
|
Private ____Cookies As CookieKeeper = Nothing
|
||||||
|
Private __DownloadTimeline As Boolean = False
|
||||||
|
Private __DownloadReels As Boolean = False
|
||||||
|
Private __DownloadStories As Boolean = False
|
||||||
|
Private __DownloadStoriesUser As Boolean = False
|
||||||
|
Private __DownloadTagged As Boolean = False
|
||||||
|
Friend Overrides Sub BeginEdit()
|
||||||
|
RefreshMyLastRequests()
|
||||||
|
Dim v% = MyLastRequestsCount
|
||||||
|
Dim d$ = String.Empty
|
||||||
|
If v > 0 Then d = $" ({MyLastRequestsDate.ToStringDate(DateTimeDefaultProvider)})"
|
||||||
|
LastRequestsCountLabel.Value = $"Number of spent requests: {v.NumToGroupIntegral}{d}"
|
||||||
|
____HH_CSRF_TOKEN = AConvert(Of String)(HH_CSRF_TOKEN.Value, String.Empty)
|
||||||
|
____HH_IG_APP_ID = AConvert(Of String)(HH_IG_APP_ID.Value, String.Empty)
|
||||||
|
____HH_ASBD_ID = AConvert(Of String)(HH_ASBD_ID.Value, String.Empty)
|
||||||
|
____HH_BROWSER = AConvert(Of String)(HH_BROWSER.Value, String.Empty)
|
||||||
|
____HH_BROWSER_EXT = AConvert(Of String)(HH_BROWSER_EXT.Value, String.Empty)
|
||||||
|
____HH_PLATFORM = AConvert(Of String)(HH_PLATFORM.Value, String.Empty)
|
||||||
|
____HH_USER_AGENT = AConvert(Of String)(HH_USER_AGENT.Value, String.Empty)
|
||||||
|
____Cookies = Responser.Cookies.Copy
|
||||||
|
__DownloadTimeline = DownloadTimeline.Value
|
||||||
|
__DownloadReels = DownloadReels.Value
|
||||||
|
__DownloadStories = DownloadStories.Value
|
||||||
|
__DownloadStoriesUser = DownloadStoriesUser.Value
|
||||||
|
__DownloadTagged = DownloadTagged.Value
|
||||||
|
MyBase.BeginEdit()
|
||||||
|
End Sub
|
||||||
|
Friend Overrides Sub Update()
|
||||||
|
If _SiteEditorFormOpened Then
|
||||||
|
Dim vals() = {New With {.ValueOld = ____HH_CSRF_TOKEN, .ValueNew = AConvert(Of String)(HH_CSRF_TOKEN.Value, String.Empty).ToString},
|
||||||
|
New With {.ValueOld = ____HH_IG_APP_ID, .ValueNew = AConvert(Of String)(HH_IG_APP_ID.Value, String.Empty).ToString},
|
||||||
|
New With {.ValueOld = ____HH_ASBD_ID, .ValueNew = AConvert(Of String)(HH_ASBD_ID.Value, String.Empty).ToString},
|
||||||
|
New With {.ValueOld = ____HH_BROWSER, .ValueNew = AConvert(Of String)(HH_BROWSER.Value, String.Empty).ToString},
|
||||||
|
New With {.ValueOld = ____HH_BROWSER_EXT, .ValueNew = AConvert(Of String)(HH_BROWSER_EXT.Value, String.Empty).ToString},
|
||||||
|
New With {.ValueOld = ____HH_PLATFORM, .ValueNew = AConvert(Of String)(HH_PLATFORM.Value, String.Empty).ToString},
|
||||||
|
New With {.ValueOld = ____HH_USER_AGENT, .ValueNew = AConvert(Of String)(HH_USER_AGENT.Value, String.Empty).ToString}
|
||||||
|
}
|
||||||
|
Dim credentialsUpdated As Boolean = False
|
||||||
|
If vals.Any(Function(v) Not v.ValueOld = v.ValueNew) OrElse
|
||||||
|
Not Responser.Cookies.ListEquals(____Cookies) Then HH_IG_WWW_CLAIM.Value = 0 : credentialsUpdated = True
|
||||||
|
If Responser.CookiesExists Then
|
||||||
|
Dim csrf$ = GetValueFromCookies(NameOf(HH_CSRF_TOKEN), Responser.Cookies)
|
||||||
|
If Not csrf.IsEmptyString Then
|
||||||
|
If Not AEquals(Of String)(CStr(HH_CSRF_TOKEN.Value), csrf) Then credentialsUpdated = True
|
||||||
|
HH_CSRF_TOKEN.Value = csrf
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
If credentialsUpdated AndAlso {New With {.ValueOld = __DownloadTimeline, .ValueNew = CBool(DownloadTimeline.Value)},
|
||||||
|
New With {.ValueOld = __DownloadReels, .ValueNew = CBool(DownloadReels.Value)},
|
||||||
|
New With {.ValueOld = __DownloadStories, .ValueNew = CBool(DownloadStories.Value)},
|
||||||
|
New With {.ValueOld = __DownloadStoriesUser, .ValueNew = CBool(DownloadStoriesUser.Value)},
|
||||||
|
New With {.ValueOld = __DownloadTagged, .ValueNew = CBool(DownloadTagged.Value)}}.
|
||||||
|
All(Function(v) v.ValueOld = v.ValueNew) Then
|
||||||
|
DownloadTimeline.Value = DownloadTimeline_Def.Value
|
||||||
|
DownloadReels.Value = DownloadReels_Def.Value
|
||||||
|
DownloadStories.Value = DownloadStories_Def.Value
|
||||||
|
DownloadStoriesUser.Value = DownloadStoriesUser_Def.Value
|
||||||
|
DownloadTagged.Value = DownloadTagged_Def.Value
|
||||||
|
End If
|
||||||
|
DownloadTimeline_Def.Value = DownloadTimeline.Value
|
||||||
|
DownloadReels_Def.Value = DownloadReels.Value
|
||||||
|
DownloadStories_Def.Value = DownloadStories.Value
|
||||||
|
DownloadStoriesUser_Def.Value = DownloadStoriesUser.Value
|
||||||
|
DownloadTagged_Def.Value = DownloadTagged.Value
|
||||||
|
End If
|
||||||
|
MyBase.Update()
|
||||||
|
End Sub
|
||||||
|
Friend Overrides Sub EndEdit()
|
||||||
|
If _SiteEditorFormOpened Then ____Cookies.DisposeIfReady(False) : ____Cookies = Nothing
|
||||||
|
MyBase.EndEdit()
|
||||||
|
End Sub
|
||||||
|
#End Region
|
||||||
#Region "UserOptions, GetUserUrl, GetUserPostUrl"
|
#Region "UserOptions, GetUserUrl, GetUserPostUrl"
|
||||||
Friend Overrides Sub UserOptions(ByRef Options As Object, ByVal OpenForm As Boolean)
|
Friend Overrides Sub UserOptions(ByRef Options As Object, ByVal OpenForm As Boolean)
|
||||||
If Options Is Nothing OrElse Not TypeOf Options Is EditorExchangeOptions Then Options = New EditorExchangeOptions(Me)
|
If Options Is Nothing OrElse Not TypeOf Options Is EditorExchangeOptions Then Options = New EditorExchangeOptions(Me)
|
||||||
@@ -367,6 +610,12 @@ Namespace API.Instagram
|
|||||||
Return ErrorsDescriber.Execute(EDP.SendToLog, ex, "Can't open user's post", String.Empty)
|
Return ErrorsDescriber.Execute(EDP.SendToLog, ex, "Can't open user's post", String.Empty)
|
||||||
End Try
|
End Try
|
||||||
End Function
|
End Function
|
||||||
|
#End Region
|
||||||
|
#Region "IDisposable Support"
|
||||||
|
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
|
||||||
|
If Not disposedValue And disposing And Not MyLastRequests Is Nothing Then MyLastRequests.Clear()
|
||||||
|
MyBase.Dispose(disposing)
|
||||||
|
End Sub
|
||||||
#End Region
|
#End Region
|
||||||
End Class
|
End Class
|
||||||
End Namespace
|
End Namespace
|
||||||
347
SCrawler/API/Instagram/UserData.GQL.vb
Normal file
@@ -0,0 +1,347 @@
|
|||||||
|
' Copyright (C) Andy https://github.com/AAndyProgram
|
||||||
|
' This program is free software: you can redistribute it and/or modify
|
||||||
|
' it under the terms of the GNU General Public License as published by
|
||||||
|
' the Free Software Foundation, either version 3 of the License, or
|
||||||
|
' (at your option) any later version.
|
||||||
|
'
|
||||||
|
' This program is distributed in the hope that it will be useful,
|
||||||
|
' but WITHOUT ANY WARRANTY
|
||||||
|
Imports System.Threading
|
||||||
|
Imports SCrawler.API.Base
|
||||||
|
Imports PersonalUtilities.Functions.XML
|
||||||
|
Imports PersonalUtilities.Functions.RegularExpressions
|
||||||
|
Imports PersonalUtilities.Tools.Web.Clients
|
||||||
|
Imports PersonalUtilities.Tools.Web.Documents.JSON
|
||||||
|
Namespace API.Instagram
|
||||||
|
Partial Friend Class UserData
|
||||||
|
#Region "Tokens"
|
||||||
|
Protected Property Token_dtsg As String = String.Empty
|
||||||
|
Protected ReadOnly Property Token_dtsg_Var As String
|
||||||
|
Get
|
||||||
|
Return If(Token_dtsg.IsEmptyString, String.Empty, SymbolsConverter.ASCII.EncodeSymbolsOnly(Token_dtsg))
|
||||||
|
End Get
|
||||||
|
End Property
|
||||||
|
Protected Property Token_lsd As String = String.Empty
|
||||||
|
Protected Sub ResetBaseTokens()
|
||||||
|
Token_dtsg = String.Empty
|
||||||
|
Token_lsd = String.Empty
|
||||||
|
End Sub
|
||||||
|
#End Region
|
||||||
|
#Region "Headers"
|
||||||
|
Friend Const GQL_HEADER_FB_FRINDLY_NAME As String = "x-fb-friendly-name"
|
||||||
|
Friend Const GQL_HEADER_FB_LSD As String = "x-fb-lsd"
|
||||||
|
#End Region
|
||||||
|
#Region "Data constants"
|
||||||
|
Private Const GQL_UserData_DocId As String = "7381344031985950"
|
||||||
|
Private Const GQL_UserData_FbFriendlyName As String = "PolarisProfilePageContentQuery"
|
||||||
|
|
||||||
|
Private Const GQL_Highlights_DocId As String = "8298007123561120"
|
||||||
|
Private Const GQL_Highlights_DocId_Second As String = "7559771384111300"
|
||||||
|
Private Const GQL_Highlights_FbFriendlyName As String = "PolarisProfileStoryHighlightsTrayContentQuery"
|
||||||
|
Private Const GQL_Highlights_FbFriendlyName_Second As String = "PolarisStoriesV3HighlightsPageQuery"
|
||||||
|
|
||||||
|
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_FbFriendlyName As String = "PolarisProfilePostsQuery"
|
||||||
|
Private Const GQL_Timeline_DocId_Second As String = "7286316061475375"
|
||||||
|
Private Const GQL_Timeline_FbFriendlyName_Second As String = "PolarisProfilePostsTabContentQuery_connection"
|
||||||
|
|
||||||
|
Private Const GQL_Reels_DocId As String = "7191572580905225"
|
||||||
|
Private Const GQL_Reels_FbFriendlyName As String = "PolarisProfileReelsTabContentQuery"
|
||||||
|
|
||||||
|
Private Const GQL_Tagged_DocId As String = "7289408964443685"
|
||||||
|
Private Const GQL_Tagged_FbFriendlyName As String = "PolarisProfileTaggedTabContentQuery"
|
||||||
|
#End Region
|
||||||
|
#Region "Url & var constants"
|
||||||
|
Private Const GQL_URL_PATTERN_VARS As String = "doc_id={0}&lsd={1}&fb_dtsg={2}&fb_api_req_friendly_name={3}&variables={4}"
|
||||||
|
Private Const GQL_URL As String = "https://www.instagram.com/api/graphql"
|
||||||
|
Private Const GQL_URL_Q As String = "https://www.instagram.com/graphql/query"
|
||||||
|
#End Region
|
||||||
|
#Region "Download functions"
|
||||||
|
Protected Sub UpdateHeadersGQL(ByVal HeaderValue As String)
|
||||||
|
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)
|
||||||
|
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
|
||||||
|
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 vars$
|
||||||
|
|
||||||
|
ThrowAny(Token)
|
||||||
|
UpdateRequestNumber()
|
||||||
|
ChangeResponserMode(True)
|
||||||
|
|
||||||
|
If Cursor.IsEmptyString Then
|
||||||
|
vars = "{""data"":{""count"":50,""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"":""" &
|
||||||
|
NameTrue & """,""__relay_internal__pv__PolarisShareMenurelayprovider"":false}"
|
||||||
|
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)
|
||||||
|
End If
|
||||||
|
|
||||||
|
DefaultParser_ElemNode = {"node"}
|
||||||
|
|
||||||
|
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", "xdt_api__v1__feed__user_timeline_graphql_connection"})
|
||||||
|
If .ListExists Then
|
||||||
|
With .Item("page_info")
|
||||||
|
If .ListExists Then
|
||||||
|
nextCursor = .Value("end_cursor")
|
||||||
|
hasNextPage = .Value("has_next_page").FromXML(Of Boolean)(False)
|
||||||
|
End If
|
||||||
|
End With
|
||||||
|
With .Item("edges")
|
||||||
|
If .ListExists Then
|
||||||
|
If Not DefaultParser(.Self, Sections.Timeline, Token) Then Throw New ExitException
|
||||||
|
End If
|
||||||
|
End With
|
||||||
|
End If
|
||||||
|
End With
|
||||||
|
End If
|
||||||
|
End Using
|
||||||
|
End If
|
||||||
|
|
||||||
|
Return If(hasNextPage And (Not nextCursor.IsEmptyString AndAlso Not nextCursor.StringToLower = none_cursor), nextCursor, String.Empty)
|
||||||
|
End Function
|
||||||
|
Private Function GetHighlightsGQL_List() As List(Of String)
|
||||||
|
|
||||||
|
Dim nextCursor$ = String.Empty, hasNextPage$ = String.Empty
|
||||||
|
Dim i% = -1
|
||||||
|
Dim hList As New List(Of String)
|
||||||
|
Dim tmpList As New List(Of String)
|
||||||
|
Dim vars$ = String.Format(GQL_URL_PATTERN_VARS, GQL_Highlights_DocId, Token_lsd, Token_dtsg_Var, GQL_Highlights_FbFriendlyName,
|
||||||
|
SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & $"""user_id"":""{ID}""" & "}"))
|
||||||
|
UpdateRequestNumber()
|
||||||
|
ChangeResponserMode(True)
|
||||||
|
UpdateHeadersGQL(GQL_Highlights_FbFriendlyName)
|
||||||
|
Dim r$ = Responser.GetResponse(GQL_URL_Q, vars)
|
||||||
|
|
||||||
|
If Not r.IsEmptyString Then
|
||||||
|
Using j As EContainer = JsonDocument.Parse(r)
|
||||||
|
If j.ListExists Then
|
||||||
|
'With j({"data"})
|
||||||
|
With j({"data", "highlights"})
|
||||||
|
If .ListExists Then
|
||||||
|
With .Item("page_info")
|
||||||
|
If .ListExists Then
|
||||||
|
nextCursor = .Value("end_cursor")
|
||||||
|
hasNextPage = .Value("has_next_page").FromXML(Of Boolean)(False)
|
||||||
|
End If
|
||||||
|
End With
|
||||||
|
With .Item({"edges"})
|
||||||
|
If .ListExists Then hList.ListAddList(.Select(Function(jj) jj.Value({"node"}, "id")), LNC)
|
||||||
|
End With
|
||||||
|
End If
|
||||||
|
End With
|
||||||
|
End If
|
||||||
|
End Using
|
||||||
|
End If
|
||||||
|
Return hList
|
||||||
|
End Function
|
||||||
|
Private Sub GetHighlightsGQL(ByRef StoriesList As List(Of String), ByVal Token As CancellationToken)
|
||||||
|
Const highlightData$ = """first"":50,""initial_reel_id"":""{0}"",""last"":2,""reel_ids"":[{1}]"
|
||||||
|
Dim tmpList As New List(Of String)
|
||||||
|
Dim i% = -1
|
||||||
|
If StoriesList.ListExists Then
|
||||||
|
tmpList.AddRange(StoriesList.Take(10))
|
||||||
|
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,
|
||||||
|
SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & String.Format(highlightData, tmpList(0), tmpList.Select(Function(hl) $"""{hl}""").ListToString(",")) & "}"))
|
||||||
|
ThrowAny(Token)
|
||||||
|
UpdateRequestNumber()
|
||||||
|
ChangeResponserMode(True)
|
||||||
|
UpdateHeadersGQL(GQL_Highlights_FbFriendlyName_Second)
|
||||||
|
Dim r$ = Responser.GetResponse(GQL_URL_Q, vars)
|
||||||
|
If Not r.IsEmptyString Then
|
||||||
|
Using j As EContainer = JsonDocument.Parse(r)
|
||||||
|
If j.ListExists Then
|
||||||
|
With j({"data", "xdt_api__v1__feed__reels_media__connection", "edges"})
|
||||||
|
If .ListExists Then
|
||||||
|
ProgressPre.ChangeMax(.Count)
|
||||||
|
For Each n As EContainer In .Self : GetStoriesData_ParseSingleHighlight(n("node"), i, False, Token) : Next
|
||||||
|
End If
|
||||||
|
End With
|
||||||
|
End If
|
||||||
|
End Using
|
||||||
|
End If
|
||||||
|
tmpList.Clear()
|
||||||
|
End If
|
||||||
|
|
||||||
|
tmpList.Clear()
|
||||||
|
End Sub
|
||||||
|
Private Sub GetUserStoriesGQL(ByVal Token As CancellationToken)
|
||||||
|
'"{" & $"""user_id"":""{ID}""" & "}"
|
||||||
|
Dim vars$ = String.Format(GQL_URL_PATTERN_VARS, GQL_UserStories_DocId, Token_lsd, Token_dtsg_Var, GQL_UserStories_FbFriendlyName,
|
||||||
|
SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & $"""reel_ids_arr"":[""{ID}""]" & "}"))
|
||||||
|
UpdateRequestNumber()
|
||||||
|
ChangeResponserMode(True)
|
||||||
|
UpdateHeadersGQL(GQL_UserStories_FbFriendlyName)
|
||||||
|
Dim r$ = Responser.GetResponse(GQL_URL, vars)
|
||||||
|
If Not r.IsEmptyString Then
|
||||||
|
Using j As EContainer = JsonDocument.Parse(r)
|
||||||
|
If j.ListExists Then
|
||||||
|
Dim i% = -1
|
||||||
|
GetStoriesData_ParseSingleHighlight(j.ItemF({"data", "xdt_api__v1__feed__reels_media", "reels_media", 0}), i, True, Token)
|
||||||
|
End If
|
||||||
|
End Using
|
||||||
|
End If
|
||||||
|
End Sub
|
||||||
|
Private WriteOnly Property GetReelsGQL_SetEnvir As Boolean
|
||||||
|
Set(ByVal init As Boolean)
|
||||||
|
If init Then
|
||||||
|
ObtainMedia_SetReelsFunc()
|
||||||
|
DefaultParser_PostUrlCreator = Function(post) $"{MySiteSettings.GetUserUrl(Me).TrimEnd("/")}/reel/{post.Code}"
|
||||||
|
Else
|
||||||
|
ObtainMedia_SizeFuncPic = Nothing
|
||||||
|
ObtainMedia_SizeFuncVid = Nothing
|
||||||
|
DefaultParser_PostUrlCreator = DefaultParser_PostUrlCreator_Default
|
||||||
|
End If
|
||||||
|
End Set
|
||||||
|
End Property
|
||||||
|
''' <returns>Response</returns>
|
||||||
|
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)
|
||||||
|
|
||||||
|
Dim vars$ = """data"":{""include_feed_video"":true,""page_size"":50,""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 & "}"))
|
||||||
|
UpdateRequestNumber()
|
||||||
|
ChangeResponserMode(True)
|
||||||
|
UpdateHeadersGQL(GQL_Reels_FbFriendlyName)
|
||||||
|
Return Responser.GetResponse(GQL_URL, vars)
|
||||||
|
End Function
|
||||||
|
''' <summary>Response</summary>
|
||||||
|
Private Function GetTaggedGQL(ByVal Cursor As String) As String
|
||||||
|
'default count = 12
|
||||||
|
'max count = 21
|
||||||
|
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}""" & "}"))
|
||||||
|
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}""" & "}"))
|
||||||
|
End If
|
||||||
|
UpdateRequestNumber()
|
||||||
|
ChangeResponserMode(True)
|
||||||
|
UpdateHeadersGQL(GQL_Tagged_FbFriendlyName)
|
||||||
|
Return Responser.GetResponse(GQL_URL, vars)
|
||||||
|
End Function
|
||||||
|
#End Region
|
||||||
|
#Region "ValidateBaseTokens"
|
||||||
|
Protected Overridable Overloads Function ValidateBaseTokens() As Boolean
|
||||||
|
Return ValidateBaseTokens(Nothing)
|
||||||
|
End Function
|
||||||
|
Protected Overridable Overloads Function ValidateBaseTokens(ByRef ErrData As String) As Boolean
|
||||||
|
ErrData = String.Empty
|
||||||
|
If Token_dtsg.IsEmptyString Then ErrData.StringAppend("dtsg")
|
||||||
|
If Token_lsd.IsEmptyString Then ErrData.StringAppend("lsd")
|
||||||
|
Return ErrData.IsEmptyString
|
||||||
|
End Function
|
||||||
|
Protected Overridable Sub ValidateBaseTokens_Error(Optional ByVal ErrData As String = "")
|
||||||
|
If _UseGQL Then DisableSection(Sections.Timeline)
|
||||||
|
ExitException.ThrowTokens(Me, ErrData)
|
||||||
|
End Sub
|
||||||
|
#End Region
|
||||||
|
#Region "GetPageTokens"
|
||||||
|
Private Sub GetPageTokens()
|
||||||
|
ResetBaseTokens()
|
||||||
|
Try
|
||||||
|
UpdateRequestNumber()
|
||||||
|
ChangeResponserMode(False, Not _UseGQL)
|
||||||
|
With Responser
|
||||||
|
With .Headers
|
||||||
|
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecFetchDest, "document"))
|
||||||
|
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecFetchMode, "navigate"))
|
||||||
|
End With
|
||||||
|
End With
|
||||||
|
Dim r$ = Responser.GetResponse(MySiteSettings.GetUserUrl(Me))
|
||||||
|
ParseTokens(r, 0)
|
||||||
|
Catch ex As Exception
|
||||||
|
Finally
|
||||||
|
ChangeResponserMode(_UseGQL, Not _UseGQL)
|
||||||
|
End Try
|
||||||
|
End Sub
|
||||||
|
Protected Sub ParseTokens(ByVal r As String, ByVal Attempt As Integer)
|
||||||
|
Try
|
||||||
|
If Not r.IsEmptyString Then
|
||||||
|
ResetBaseTokens()
|
||||||
|
Select Case Attempt
|
||||||
|
Case 0
|
||||||
|
Dim rr As RParams = RParams.DM(PageTokenRegexPatternDefault, 0, RegexReturn.List, EDP.ReturnValue)
|
||||||
|
Dim tokens As List(Of String) = RegexReplace(r, rr)
|
||||||
|
Dim tt$, ttVal$
|
||||||
|
If tokens.ListExists Then
|
||||||
|
With rr
|
||||||
|
.Match = Nothing
|
||||||
|
.MatchSub = 1
|
||||||
|
.WhatGet = RegexReturn.Value
|
||||||
|
End With
|
||||||
|
For Each tt In tokens
|
||||||
|
If Not Token_lsd.IsEmptyString And Not Token_dtsg.IsEmptyString Then
|
||||||
|
Exit For
|
||||||
|
Else
|
||||||
|
ttVal = RegexReplace(tt, rr)
|
||||||
|
If Not ttVal.IsEmptyString Then
|
||||||
|
If ttVal.Contains(":") Then
|
||||||
|
If Token_dtsg.IsEmptyString Then Token_dtsg = ttVal
|
||||||
|
Else
|
||||||
|
If Token_lsd.IsEmptyString Then Token_lsd = ttVal
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
Next
|
||||||
|
End If
|
||||||
|
Case 1
|
||||||
|
Token_dtsg = RegexReplace(r, Regex_UserToken_dtsg)
|
||||||
|
Token_lsd = RegexReplace(r, Regex_UserToken_lsd)
|
||||||
|
End Select
|
||||||
|
If Not ValidateBaseTokens() And Attempt = 0 Then ParseTokens(r, Attempt + 1)
|
||||||
|
End If
|
||||||
|
Catch
|
||||||
|
End Try
|
||||||
|
End Sub
|
||||||
|
#End Region
|
||||||
|
End Class
|
||||||
|
End Namespace
|
||||||
@@ -15,6 +15,8 @@ Imports PersonalUtilities.Functions.XML.Base
|
|||||||
Imports PersonalUtilities.Functions.Messaging
|
Imports PersonalUtilities.Functions.Messaging
|
||||||
Imports PersonalUtilities.Functions.RegularExpressions
|
Imports PersonalUtilities.Functions.RegularExpressions
|
||||||
Imports PersonalUtilities.Tools.Web.Clients
|
Imports PersonalUtilities.Tools.Web.Clients
|
||||||
|
Imports PersonalUtilities.Tools.Web.Clients.Base
|
||||||
|
Imports PersonalUtilities.Tools.Web.Documents
|
||||||
Imports PersonalUtilities.Tools.Web.Documents.JSON
|
Imports PersonalUtilities.Tools.Web.Documents.JSON
|
||||||
Imports UTypes = SCrawler.API.Base.UserMedia.Types
|
Imports UTypes = SCrawler.API.Base.UserMedia.Types
|
||||||
Imports UStates = SCrawler.API.Base.UserMedia.States
|
Imports UStates = SCrawler.API.Base.UserMedia.States
|
||||||
@@ -24,6 +26,7 @@ Namespace API.Instagram
|
|||||||
Private Const Name_LastCursor As String = "LastCursor"
|
Private Const Name_LastCursor As String = "LastCursor"
|
||||||
Private Const Name_FirstLoadingDone As String = "FirstLoadingDone"
|
Private Const Name_FirstLoadingDone As String = "FirstLoadingDone"
|
||||||
Private Const Name_GetTimeline As String = "GetTimeline"
|
Private Const Name_GetTimeline As String = "GetTimeline"
|
||||||
|
Private Const Name_GetReels As String = "GetReels"
|
||||||
Private Const Name_GetStories As String = "GetStories"
|
Private Const Name_GetStories As String = "GetStories"
|
||||||
Private Const Name_GetStoriesUser As String = "GetStoriesUser"
|
Private Const Name_GetStoriesUser As String = "GetStoriesUser"
|
||||||
Private Const Name_GetTagged As String = "GetTaggedData"
|
Private Const Name_GetTagged As String = "GetTaggedData"
|
||||||
@@ -76,6 +79,7 @@ Namespace API.Instagram
|
|||||||
Private LastCursor As String = String.Empty
|
Private LastCursor As String = String.Empty
|
||||||
Private FirstLoadingDone As Boolean = False
|
Private FirstLoadingDone As Boolean = False
|
||||||
Friend Property GetTimeline As Boolean = True
|
Friend Property GetTimeline As Boolean = True
|
||||||
|
Friend Property GetReels As Boolean = False
|
||||||
Friend Property GetStories As Boolean
|
Friend Property GetStories As Boolean
|
||||||
Friend Property GetStoriesUser As Boolean
|
Friend Property GetStoriesUser As Boolean
|
||||||
Friend Property GetTaggedData As Boolean
|
Friend Property GetTaggedData As Boolean
|
||||||
@@ -94,6 +98,7 @@ Namespace API.Instagram
|
|||||||
LastCursor = .Value(Name_LastCursor)
|
LastCursor = .Value(Name_LastCursor)
|
||||||
FirstLoadingDone = .Value(Name_FirstLoadingDone).FromXML(Of Boolean)(False)
|
FirstLoadingDone = .Value(Name_FirstLoadingDone).FromXML(Of Boolean)(False)
|
||||||
GetTimeline = .Value(Name_GetTimeline).FromXML(Of Boolean)(CBool(MySiteSettings.GetTimeline.Value))
|
GetTimeline = .Value(Name_GetTimeline).FromXML(Of Boolean)(CBool(MySiteSettings.GetTimeline.Value))
|
||||||
|
GetReels = .Value(Name_GetReels).FromXML(Of Boolean)(MySiteSettings.GetReels.Value)
|
||||||
GetStories = .Value(Name_GetStories).FromXML(Of Boolean)(CBool(MySiteSettings.GetStories.Value))
|
GetStories = .Value(Name_GetStories).FromXML(Of Boolean)(CBool(MySiteSettings.GetStories.Value))
|
||||||
GetStoriesUser = .Value(Name_GetStoriesUser).FromXML(Of Boolean)(MySiteSettings.GetStoriesUser.Value)
|
GetStoriesUser = .Value(Name_GetStoriesUser).FromXML(Of Boolean)(MySiteSettings.GetStoriesUser.Value)
|
||||||
GetTaggedData = .Value(Name_GetTagged).FromXML(Of Boolean)(CBool(MySiteSettings.GetTagged.Value))
|
GetTaggedData = .Value(Name_GetTagged).FromXML(Of Boolean)(CBool(MySiteSettings.GetTagged.Value))
|
||||||
@@ -103,6 +108,7 @@ Namespace API.Instagram
|
|||||||
.Add(Name_LastCursor, LastCursor)
|
.Add(Name_LastCursor, LastCursor)
|
||||||
.Add(Name_FirstLoadingDone, FirstLoadingDone.BoolToInteger)
|
.Add(Name_FirstLoadingDone, FirstLoadingDone.BoolToInteger)
|
||||||
.Add(Name_GetTimeline, GetTimeline.BoolToInteger)
|
.Add(Name_GetTimeline, GetTimeline.BoolToInteger)
|
||||||
|
.Add(Name_GetReels, GetReels.BoolToInteger)
|
||||||
.Add(Name_GetStories, GetStories.BoolToInteger)
|
.Add(Name_GetStories, GetStories.BoolToInteger)
|
||||||
.Add(Name_GetStoriesUser, GetStoriesUser.BoolToInteger)
|
.Add(Name_GetStoriesUser, GetStoriesUser.BoolToInteger)
|
||||||
.Add(Name_GetTagged, GetTaggedData.BoolToInteger)
|
.Add(Name_GetTagged, GetTaggedData.BoolToInteger)
|
||||||
@@ -120,6 +126,7 @@ Namespace API.Instagram
|
|||||||
If Not Obj Is Nothing AndAlso TypeOf Obj Is EditorExchangeOptions Then
|
If Not Obj Is Nothing AndAlso TypeOf Obj Is EditorExchangeOptions Then
|
||||||
With DirectCast(Obj, EditorExchangeOptions)
|
With DirectCast(Obj, EditorExchangeOptions)
|
||||||
GetTimeline = .GetTimeline
|
GetTimeline = .GetTimeline
|
||||||
|
GetReels = .GetReels
|
||||||
GetStories = .GetStories
|
GetStories = .GetStories
|
||||||
GetStoriesUser = .GetStoriesUser
|
GetStoriesUser = .GetStoriesUser
|
||||||
GetTaggedData = .GetTagged
|
GetTaggedData = .GetTagged
|
||||||
@@ -134,14 +141,26 @@ Namespace API.Instagram
|
|||||||
End Sub
|
End Sub
|
||||||
#End Region
|
#End Region
|
||||||
#Region "Download data"
|
#Region "Download data"
|
||||||
|
Private WwwClaimUpdate As Boolean = True
|
||||||
|
Private WwwClaimUpdate_R As Boolean = True
|
||||||
|
Private WwwClaimDefaultAlgo As Boolean = True
|
||||||
|
Private WwwClaimUse As Boolean = True
|
||||||
Private E560Thrown As Boolean = False
|
Private E560Thrown As Boolean = False
|
||||||
|
Friend Err5xx As Integer = -1
|
||||||
Private Class ExitException : Inherits Exception
|
Private Class ExitException : Inherits Exception
|
||||||
|
Friend Property Is560 As Boolean = False
|
||||||
|
Friend Property IsTokens As Boolean = False
|
||||||
|
Friend Property TokensData As String = String.Empty
|
||||||
Friend Shared Sub Throw560(ByRef Source As UserData)
|
Friend Shared Sub Throw560(ByRef Source As UserData)
|
||||||
If Not Source.E560Thrown Then
|
If Not Source.E560Thrown Then
|
||||||
MyMainLOG = $"{Source.ToStringForLog}: (560) Download skipped until next session"
|
MyMainLOG = $"{Source.ToStringForLog}: ({IIf(Source.Err5xx > 0, Source.Err5xx, 560)}) Download skipped until next session"
|
||||||
Source.E560Thrown = True
|
Source.E560Thrown = True
|
||||||
End If
|
End If
|
||||||
Throw New ExitException
|
Throw New ExitException With {.Is560 = True}
|
||||||
|
End Sub
|
||||||
|
Friend Shared Sub ThrowTokens(ByRef Source As UserData, ByVal Data As String)
|
||||||
|
MyMainLOG = $"{Source.ToStringForLog}: failed to update some{IIf(Data.IsEmptyString, String.Empty, $" ({Data})")} credentials"
|
||||||
|
Throw New ExitException With {.IsTokens = True, .TokensData = Data}
|
||||||
End Sub
|
End Sub
|
||||||
End Class
|
End Class
|
||||||
Private ReadOnly Property MyFilePostsKV As SFile
|
Private ReadOnly Property MyFilePostsKV As SFile
|
||||||
@@ -224,11 +243,79 @@ Namespace API.Instagram
|
|||||||
Private _DownloadingInProgress As Boolean = False
|
Private _DownloadingInProgress As Boolean = False
|
||||||
Private _Limit As Integer = -1
|
Private _Limit As Integer = -1
|
||||||
Private _TotalPostsParsed As Integer = 0
|
Private _TotalPostsParsed As Integer = 0
|
||||||
|
Private _LastWwwClaim As String = String.Empty
|
||||||
|
Private _ResponserGQLMode As Boolean = False
|
||||||
|
Private _UseGQL As Boolean = False
|
||||||
|
Private Sub ChangeResponserMode(ByVal GQL As Boolean, Optional ByVal Force As Boolean = False)
|
||||||
|
If Not _ResponserGQLMode = GQL Or Force Then
|
||||||
|
_ResponserGQLMode = GQL
|
||||||
|
ChangeResponserMode_StoreWwwClaim()
|
||||||
|
Responser.Headers.Clear()
|
||||||
|
Responser.Headers.AddRange(MySiteSettings.Responser.Headers)
|
||||||
|
If GQL Then
|
||||||
|
WwwClaimUpdate = False
|
||||||
|
With Responser
|
||||||
|
.Method = "POST"
|
||||||
|
.ContentType = "application/x-www-form-urlencoded"
|
||||||
|
.Referer = MySiteSettings.GetUserUrl(Me)
|
||||||
|
.CookiesExtractMode = Responser.CookiesExtractModes.Any
|
||||||
|
With .Headers
|
||||||
|
.Remove(SiteSettings.Header_IG_WWW_CLAIM)
|
||||||
|
.Add("origin", "https://www.instagram.com")
|
||||||
|
.Add("authority", "www.instagram.com")
|
||||||
|
End With
|
||||||
|
End With
|
||||||
|
Else
|
||||||
|
WwwClaimUpdate = WwwClaimUpdate_R
|
||||||
|
With Responser
|
||||||
|
.Method = "GET"
|
||||||
|
.ContentType = Nothing
|
||||||
|
.Referer = Nothing
|
||||||
|
.CookiesExtractMode = MySiteSettings.Responser.CookiesExtractMode
|
||||||
|
With .Headers
|
||||||
|
.Remove("origin")
|
||||||
|
.Remove("authority")
|
||||||
|
.Remove(GQL_HEADER_FB_FRINDLY_NAME)
|
||||||
|
.Remove(GQL_HEADER_FB_LSD)
|
||||||
|
Dim hv$ = MySiteSettings.Responser.Headers.Value(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecFetchDest)).IfNullOrEmpty("empty")
|
||||||
|
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecFetchDest, hv))
|
||||||
|
hv = MySiteSettings.Responser.Headers.Value(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecFetchMode)).IfNullOrEmpty("cors")
|
||||||
|
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecFetchMode, hv))
|
||||||
|
If Not _UseGQL And WwwClaimUse Then .Add(SiteSettings.Header_IG_WWW_CLAIM, _LastWwwClaim)
|
||||||
|
End With
|
||||||
|
End With
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
End Sub
|
||||||
|
Private Sub ChangeResponserMode_StoreWwwClaim()
|
||||||
|
If Not _UseGQL Then
|
||||||
|
With Responser.Headers
|
||||||
|
If .Contains(SiteSettings.Header_IG_WWW_CLAIM) AndAlso Not .Value(SiteSettings.Header_IG_WWW_CLAIM).IsEmptyString Then _LastWwwClaim = .Value(SiteSettings.Header_IG_WWW_CLAIM)
|
||||||
|
End With
|
||||||
|
End If
|
||||||
|
End Sub
|
||||||
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
|
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
|
||||||
|
ResetBaseTokens()
|
||||||
UserNameRequested = False
|
UserNameRequested = False
|
||||||
|
RequestsCountSession = 0
|
||||||
|
_LastWwwClaim = String.Empty
|
||||||
|
_ResponserGQLMode = False
|
||||||
|
_UseGQL = MySiteSettings.USE_GQL.Value
|
||||||
|
WwwClaimUse = MySiteSettings.HH_IG_WWW_CLAIM_USE.Value
|
||||||
|
WwwClaimDefaultAlgo = MySiteSettings.HH_IG_WWW_CLAIM_USE_DEFAULT_ALGO.Value
|
||||||
|
With MySiteSettings : WwwClaimUpdate = (Not CBool(.HH_IG_WWW_CLAIM_ALWAYS_ZERO.Value) And CBool(.HH_IG_WWW_CLAIM_USE.Value)) Or
|
||||||
|
WwwClaimDefaultAlgo : End With
|
||||||
|
WwwClaimUpdate_R = WwwClaimUpdate
|
||||||
|
Dim upClaimRequest As Action = Sub() If WwwClaimUpdate And Not WwwClaimDefaultAlgo And CBool(MySiteSettings.HH_IG_WWW_CLAIM_RESET_EACH_TARGET.Value) Then _
|
||||||
|
Responser.Headers.Add(SiteSettings.Header_IG_WWW_CLAIM, 0)
|
||||||
|
|
||||||
|
DefaultParser_ElemNode = Nothing
|
||||||
|
ChangeResponserMode(_UseGQL)
|
||||||
|
|
||||||
Dim s As Sections = Sections.Timeline
|
Dim s As Sections = Sections.Timeline
|
||||||
Dim errorFound As Boolean = False
|
Dim errorFound As Boolean = False
|
||||||
Try
|
Try
|
||||||
|
Err5xx = -1
|
||||||
_Limit = If(DownloadTopCount, -1)
|
_Limit = If(DownloadTopCount, -1)
|
||||||
_TotalPostsParsed = 0
|
_TotalPostsParsed = 0
|
||||||
LoadSavePostsKV(True)
|
LoadSavePostsKV(True)
|
||||||
@@ -239,6 +326,7 @@ Namespace API.Instagram
|
|||||||
Dim dt As Func(Of Boolean) = Function() (CBool(MySiteSettings.DownloadTimeline.Value) And GetTimeline) Or IsSavedPosts
|
Dim dt As Func(Of Boolean) = Function() (CBool(MySiteSettings.DownloadTimeline.Value) And GetTimeline) Or IsSavedPosts
|
||||||
If dt.Invoke And Not LastCursor.IsEmptyString Then
|
If dt.Invoke And Not LastCursor.IsEmptyString Then
|
||||||
s = IIf(IsSavedPosts, Sections.SavedPosts, Sections.Timeline)
|
s = IIf(IsSavedPosts, Sections.SavedPosts, Sections.Timeline)
|
||||||
|
upClaimRequest.Invoke
|
||||||
DownloadData(LastCursor, s, Token)
|
DownloadData(LastCursor, s, Token)
|
||||||
ProgressPre.Done()
|
ProgressPre.Done()
|
||||||
ThrowAny(Token)
|
ThrowAny(Token)
|
||||||
@@ -246,16 +334,53 @@ Namespace API.Instagram
|
|||||||
End If
|
End If
|
||||||
If dt.Invoke And Not HasError Then
|
If dt.Invoke And Not HasError Then
|
||||||
s = IIf(IsSavedPosts, Sections.SavedPosts, Sections.Timeline)
|
s = IIf(IsSavedPosts, Sections.SavedPosts, Sections.Timeline)
|
||||||
|
upClaimRequest.Invoke
|
||||||
|
ChangeResponserMode(_UseGQL)
|
||||||
DownloadData(String.Empty, s, Token)
|
DownloadData(String.Empty, s, Token)
|
||||||
ProgressPre.Done()
|
ProgressPre.Done()
|
||||||
ThrowAny(Token)
|
ThrowAny(Token)
|
||||||
If Not HasError Then FirstLoadingDone = True
|
If Not HasError Then FirstLoadingDone = True
|
||||||
End If
|
End If
|
||||||
|
DefaultParser_ElemNode = Nothing
|
||||||
If FirstLoadingDone Then LastCursor = String.Empty
|
If FirstLoadingDone Then LastCursor = String.Empty
|
||||||
If Not IsSavedPosts AndAlso MySiteSettings.BaseAuthExists() Then
|
If Not IsSavedPosts AndAlso MySiteSettings.BaseAuthExists() Then
|
||||||
If CBool(MySiteSettings.DownloadStories.Value) And GetStories Then s = Sections.Stories : DownloadData(String.Empty, s, Token) : ProgressPre.Done()
|
DefaultParser_ElemNode = Nothing
|
||||||
If CBool(MySiteSettings.DownloadStoriesUser.Value) And GetStoriesUser Then s = Sections.UserStories : DownloadData(String.Empty, s, Token) : ProgressPre.Done()
|
ChangeResponserMode(_UseGQL)
|
||||||
If CBool(MySiteSettings.DownloadTagged.Value) And ACheck(MySiteSettings.HashTagged.Value) And GetTaggedData Then s = Sections.Tagged : DownloadData(String.Empty, s, Token) : ProgressPre.Done()
|
If CBool(MySiteSettings.DownloadReels.Value) And GetReels Then
|
||||||
|
s = Sections.Reels
|
||||||
|
DefaultParser_ElemNode = {"node", "media"}
|
||||||
|
upClaimRequest.Invoke
|
||||||
|
ChangeResponserMode(True)
|
||||||
|
DownloadData(String.Empty, s, Token)
|
||||||
|
GetReelsGQL_SetEnvir = False
|
||||||
|
ProgressPre.Done()
|
||||||
|
End If
|
||||||
|
DefaultParser_ElemNode = Nothing
|
||||||
|
ChangeResponserMode(_UseGQL)
|
||||||
|
If CBool(MySiteSettings.DownloadStories.Value) And GetStories Then
|
||||||
|
s = Sections.Stories
|
||||||
|
upClaimRequest.Invoke
|
||||||
|
DownloadData(String.Empty, s, Token)
|
||||||
|
ProgressPre.Done()
|
||||||
|
End If
|
||||||
|
DefaultParser_ElemNode = Nothing
|
||||||
|
ChangeResponserMode(_UseGQL)
|
||||||
|
If CBool(MySiteSettings.DownloadStoriesUser.Value) And GetStoriesUser Then
|
||||||
|
s = Sections.UserStories
|
||||||
|
upClaimRequest.Invoke
|
||||||
|
DownloadData(String.Empty, s, Token)
|
||||||
|
ProgressPre.Done()
|
||||||
|
End If
|
||||||
|
DefaultParser_ElemNode = Nothing
|
||||||
|
ChangeResponserMode(_UseGQL)
|
||||||
|
If CBool(MySiteSettings.DownloadTagged.Value) And GetTaggedData Then
|
||||||
|
s = Sections.Tagged
|
||||||
|
upClaimRequest.Invoke
|
||||||
|
DownloadData(String.Empty, s, Token)
|
||||||
|
ProgressPre.Done()
|
||||||
|
DefaultParser_ElemNode = Nothing
|
||||||
|
If PostsToReparse.Count > 0 Then DownloadPosts(Token, True)
|
||||||
|
End If
|
||||||
End If
|
End If
|
||||||
If WaitNotificationMode = WNM.SkipTemp Or WaitNotificationMode = WNM.SkipCurrent Then WaitNotificationMode = WNM.Notify
|
If WaitNotificationMode = WNM.SkipTemp Or WaitNotificationMode = WNM.SkipCurrent Then WaitNotificationMode = WNM.Notify
|
||||||
Catch eex As ExitException
|
Catch eex As ExitException
|
||||||
@@ -263,6 +388,8 @@ Namespace API.Instagram
|
|||||||
errorFound = True
|
errorFound = True
|
||||||
Throw ex
|
Throw ex
|
||||||
Finally
|
Finally
|
||||||
|
DefaultParser_ElemNode = Nothing
|
||||||
|
GetReelsGQL_SetEnvir = False
|
||||||
E560Thrown = False
|
E560Thrown = False
|
||||||
UpdateResponser()
|
UpdateResponser()
|
||||||
ValidateExtension()
|
ValidateExtension()
|
||||||
@@ -287,21 +414,27 @@ Namespace API.Instagram
|
|||||||
Try
|
Try
|
||||||
If _DownloadingInProgress AndAlso Not Responser Is Nothing AndAlso Not Responser.Disposed Then
|
If _DownloadingInProgress AndAlso Not Responser Is Nothing AndAlso Not Responser.Disposed Then
|
||||||
_DownloadingInProgress = False
|
_DownloadingInProgress = False
|
||||||
RemoveHandler Responser.ResponseReceived, AddressOf Responser_ResponseReceived
|
Responser_ResponseReceived_RemoveHandler()
|
||||||
Declarations.UpdateResponser(Responser, MySiteSettings.Responser)
|
Declarations.UpdateResponser(Responser, MySiteSettings.Responser, WwwClaimUpdate)
|
||||||
End If
|
End If
|
||||||
Catch
|
Catch
|
||||||
End Try
|
End Try
|
||||||
End Sub
|
End Sub
|
||||||
Protected Overridable Sub Responser_ResponseReceived(ByVal Sender As Object, ByVal e As EventArguments.WebDataResponse)
|
Protected Overrides Sub Responser_ResponseReceived(ByVal Sender As Object, ByVal e As EventArguments.WebDataResponse)
|
||||||
Declarations.UpdateResponser(e, Responser)
|
Declarations.UpdateResponser(e, Responser, WwwClaimUpdate)
|
||||||
End Sub
|
End Sub
|
||||||
Protected Enum Sections : Timeline : Tagged : Stories : UserStories : SavedPosts : End Enum
|
Protected Enum Sections : Timeline : Reels : Tagged : Stories : UserStories : SavedPosts : End Enum
|
||||||
Protected Const StoriesFolder As String = "Stories"
|
Protected Const StoriesFolder As String = "Stories"
|
||||||
Private Const TaggedFolder As String = "Tagged"
|
Private Const TaggedFolder As String = "Tagged"
|
||||||
#Region "429 bypass"
|
#Region "429 bypass"
|
||||||
Private Const MaxPostsCount As Integer = 200
|
Private Const MaxPostsCount As Integer = 200
|
||||||
Friend Property RequestsCount As Integer = 0
|
Friend Property RequestsCount As Integer = 0
|
||||||
|
Friend Property RequestsCountSession As Integer = 0
|
||||||
|
Private Sub UpdateRequestNumber()
|
||||||
|
If CInt(MySiteSettings.RequestsWaitTimer_Any.Value) > 0 Then Thread.Sleep(CInt(MySiteSettings.RequestsWaitTimer_Any.Value))
|
||||||
|
RequestsCount += 1
|
||||||
|
RequestsCountSession += 1
|
||||||
|
End Sub
|
||||||
Friend Enum WNM As Integer
|
Friend Enum WNM As Integer
|
||||||
Notify = 0
|
Notify = 0
|
||||||
SkipCurrent = 1
|
SkipCurrent = 1
|
||||||
@@ -436,49 +569,79 @@ Namespace API.Instagram
|
|||||||
ReconfigureAwaiter()
|
ReconfigureAwaiter()
|
||||||
|
|
||||||
Try
|
Try
|
||||||
|
Dim r$ = String.Empty
|
||||||
Dim n As EContainer, nn As EContainer
|
Dim n As EContainer, nn As EContainer
|
||||||
Dim HasNextPage As Boolean = False
|
Dim HasNextPage As Boolean = False
|
||||||
Dim EndCursor$ = String.Empty
|
Dim EndCursor$ = String.Empty
|
||||||
Dim PostID$ = String.Empty, PostDate$ = String.Empty, SpecFolder$ = String.Empty
|
Dim PostID$ = String.Empty, PostDate$ = String.Empty, SpecFolder$ = String.Empty
|
||||||
|
Dim TokensErrData$ = String.Empty
|
||||||
Dim PostIDKV As PostKV
|
Dim PostIDKV As PostKV
|
||||||
Dim ENode() As Object = Nothing
|
Dim ENode() As Object = Nothing
|
||||||
|
Dim processGetResponse As Boolean = True
|
||||||
NextRequest(True)
|
NextRequest(True)
|
||||||
|
|
||||||
'Check environment
|
'Check environment
|
||||||
If Not IsSavedPosts Then
|
If Not IsSavedPosts Then
|
||||||
If ID.IsEmptyString Then GetUserId()
|
If ID.IsEmptyString Then GetUserData()
|
||||||
If ID.IsEmptyString Then Throw New Plugin.ExitException("can't get user ID")
|
If ID.IsEmptyString Then 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
|
||||||
End If
|
End If
|
||||||
|
|
||||||
'Create query
|
'Create query
|
||||||
Select Case Section
|
Select Case Section
|
||||||
Case Sections.Timeline
|
Case Sections.Timeline
|
||||||
URL = $"https://www.instagram.com/api/v1/feed/user/{NameTrue}/username/?count=50" &
|
If _UseGQL Then
|
||||||
If(Cursor.IsEmptyString, String.Empty, $"&max_id={Cursor}")
|
EndCursor = GetTimelineGQL(Cursor, Token)
|
||||||
ENode = Nothing
|
HasNextPage = Not EndCursor.IsEmptyString
|
||||||
|
MySiteSettings.TooManyRequests(False)
|
||||||
|
GoTo NextPageBlock
|
||||||
|
Else
|
||||||
|
URL = $"https://www.instagram.com/api/v1/feed/user/{NameTrue}/username/?count=50" &
|
||||||
|
If(Cursor.IsEmptyString, String.Empty, $"&max_id={Cursor}")
|
||||||
|
ENode = Nothing
|
||||||
|
End If
|
||||||
|
Case Sections.Reels
|
||||||
|
ChangeResponserMode(True)
|
||||||
|
r = GetReelsGQL(Cursor)
|
||||||
|
ENode = {"data", "xdt_api__v1__clips__user__connection_v2"}
|
||||||
|
processGetResponse = False
|
||||||
Case Sections.SavedPosts
|
Case Sections.SavedPosts
|
||||||
SavedPostsDownload(String.Empty, Token)
|
ChangeResponserMode(False)
|
||||||
Exit Sub
|
EndCursor = SavedPostsDownload(String.Empty, Token)
|
||||||
|
HasNextPage = Not EndCursor.IsEmptyString
|
||||||
|
MySiteSettings.TooManyRequests(False)
|
||||||
|
ThrowAny(Token)
|
||||||
|
GoTo NextPageBlock
|
||||||
Case Sections.Tagged
|
Case Sections.Tagged
|
||||||
Dim h$ = AConvert(Of String)(MySiteSettings.HashTagged.Value, String.Empty)
|
|
||||||
If h.IsEmptyString Then Throw New ExitException
|
|
||||||
Dim vars$ = "{""id"":" & ID & ",""first"":50,""after"":""" & Cursor & """}"
|
|
||||||
vars = SymbolsConverter.ASCII.EncodeSymbolsOnly(vars)
|
|
||||||
URL = $"https://www.instagram.com/graphql/query/?query_hash={h}&variables={vars}"
|
|
||||||
ENode = {"data", "user", 0}
|
|
||||||
SpecFolder = TaggedFolder
|
SpecFolder = TaggedFolder
|
||||||
|
If _UseGQL Then
|
||||||
|
r = GetTaggedGQL(Cursor)
|
||||||
|
ENode = {"data", "xdt_api__v1__usertags__user_id__feed_connection"}
|
||||||
|
processGetResponse = False
|
||||||
|
Else
|
||||||
|
Dim vars$ = "{""id"":" & ID & ",""first"":50,""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"}
|
||||||
|
End If
|
||||||
Case Sections.Stories
|
Case Sections.Stories
|
||||||
If Not StoriesRequested Then
|
If Not StoriesRequested Then
|
||||||
StoriesList = GetStoriesList()
|
StoriesList = If(_UseGQL, GetHighlightsGQL_List(), GetStoriesList())
|
||||||
StoriesRequested = True
|
StoriesRequested = True
|
||||||
MySiteSettings.TooManyRequests(False)
|
MySiteSettings.TooManyRequests(False)
|
||||||
RequestsCount += 1
|
|
||||||
ThrowAny(Token)
|
ThrowAny(Token)
|
||||||
|
Continue Do
|
||||||
End If
|
End If
|
||||||
If StoriesList.ListExists Then
|
If StoriesList.ListExists Then
|
||||||
GetStoriesData(StoriesList, False, Token)
|
If _UseGQL Then
|
||||||
|
GetHighlightsGQL(StoriesList, Token)
|
||||||
|
Else
|
||||||
|
GetStoriesData(StoriesList, False, Token)
|
||||||
|
End If
|
||||||
MySiteSettings.TooManyRequests(False)
|
MySiteSettings.TooManyRequests(False)
|
||||||
RequestsCount += 1
|
|
||||||
End If
|
End If
|
||||||
If StoriesList.ListExists Then
|
If StoriesList.ListExists Then
|
||||||
Continue Do
|
Continue Do
|
||||||
@@ -486,16 +649,17 @@ Namespace API.Instagram
|
|||||||
Throw New ExitException
|
Throw New ExitException
|
||||||
End If
|
End If
|
||||||
Case Sections.UserStories
|
Case Sections.UserStories
|
||||||
GetStoriesData(Nothing, True, Token)
|
If _UseGQL Then GetUserStoriesGQL(Token) Else GetStoriesData(Nothing, True, Token)
|
||||||
MySiteSettings.TooManyRequests(False)
|
MySiteSettings.TooManyRequests(False)
|
||||||
RequestsCount += 1
|
|
||||||
Throw New ExitException
|
Throw New ExitException
|
||||||
End Select
|
End Select
|
||||||
|
|
||||||
'Get response
|
'Get response
|
||||||
Dim r$ = Responser.GetResponse(URL,, EDP.ThrowException)
|
If processGetResponse Then
|
||||||
|
UpdateRequestNumber()
|
||||||
|
r = Responser.GetResponse(URL)
|
||||||
|
End If
|
||||||
MySiteSettings.TooManyRequests(False)
|
MySiteSettings.TooManyRequests(False)
|
||||||
RequestsCount += 1
|
|
||||||
ThrowAny(Token)
|
ThrowAny(Token)
|
||||||
|
|
||||||
'Parsing
|
'Parsing
|
||||||
@@ -515,6 +679,20 @@ Namespace API.Instagram
|
|||||||
HasNextPage = False
|
HasNextPage = False
|
||||||
End If
|
End If
|
||||||
End With
|
End With
|
||||||
|
Case Sections.Reels
|
||||||
|
With n
|
||||||
|
If .Contains("page_info") Then
|
||||||
|
With .Item("page_info")
|
||||||
|
HasNextPage = .Value("has_next_page").FromXML(Of Boolean)(False)
|
||||||
|
EndCursor = .Value("end_cursor")
|
||||||
|
End With
|
||||||
|
Else
|
||||||
|
HasNextPage = False
|
||||||
|
End If
|
||||||
|
If If(.Item("edges")?.Count, 0) > 0 Then
|
||||||
|
If Not DefaultParser(.Item("edges"), Section, Token, "Reels*") Then Throw New ExitException
|
||||||
|
End If
|
||||||
|
End With
|
||||||
Case Sections.Tagged
|
Case Sections.Tagged
|
||||||
With n
|
With n
|
||||||
If .Contains("page_info") Then
|
If .Contains("page_info") Then
|
||||||
@@ -565,28 +743,43 @@ Namespace API.Instagram
|
|||||||
Else
|
Else
|
||||||
Throw New ExitException
|
Throw New ExitException
|
||||||
End If
|
End If
|
||||||
|
NextPageBlock:
|
||||||
dValue = 0
|
dValue = 0
|
||||||
If HasNextPage And Not EndCursor.IsEmptyString Then DownloadData(EndCursor, Section, Token)
|
If HasNextPage And Not EndCursor.IsEmptyString Then DownloadData(EndCursor, Section, Token)
|
||||||
|
Catch jsonNull As JsonDocumentException When jsonNull.State = WebDocumentEventArgs.States.Error And
|
||||||
|
(Section = Sections.Reels Or Section = Sections.SavedPosts)
|
||||||
|
Throw jsonNull
|
||||||
Catch eex As ExitException
|
Catch eex As ExitException
|
||||||
Throw eex
|
Throw eex
|
||||||
Catch ex As Exception
|
Catch ex As Exception
|
||||||
dValue = ProcessException(ex, Token, $"data downloading error [{URL}]",, Section, False)
|
dValue = ProcessException(ex, Token, $"data downloading error [{URL}]",, Section, False)
|
||||||
End Try
|
End Try
|
||||||
Loop
|
Loop
|
||||||
|
Catch jsonNull2 As JsonDocumentException When jsonNull2.State = WebDocumentEventArgs.States.Error And
|
||||||
|
(Section = Sections.Reels Or Section = Sections.SavedPosts)
|
||||||
|
If Section = Sections.SavedPosts Then DisableSection(Section)
|
||||||
Catch eex2 As ExitException
|
Catch eex2 As ExitException
|
||||||
If (Section = Sections.Timeline Or Section = Sections.Tagged) And Not Cursor.IsEmptyString Then Throw eex2
|
If eex2.Is560 Then
|
||||||
|
Throw New Plugin.ExitException With {.Silent = True}
|
||||||
|
ElseIf eex2.IsTokens And _UseGQL Then
|
||||||
|
Throw New Plugin.ExitException With {.Silent = True}
|
||||||
|
Else
|
||||||
|
If Not Section = Sections.Reels And (Section = Sections.Timeline Or Section = Sections.Tagged) And Not Cursor.IsEmptyString Then Throw eex2
|
||||||
|
End If
|
||||||
Catch oex2 As OperationCanceledException When Token.IsCancellationRequested Or oex2.HelpLink = InstAborted
|
Catch oex2 As OperationCanceledException When Token.IsCancellationRequested Or oex2.HelpLink = InstAborted
|
||||||
If oex2.HelpLink = InstAborted Then HasError = True
|
If oex2.HelpLink = InstAborted Then HasError = True
|
||||||
Catch DoEx As Exception
|
Catch DoEx As Exception
|
||||||
ProcessException(DoEx, Token, $"data downloading error [{URL}]",, Section)
|
ProcessException(DoEx, Token, $"data downloading error [{URL}]",, Section)
|
||||||
End Try
|
End Try
|
||||||
End Sub
|
End Sub
|
||||||
Private Sub DownloadPosts(ByVal Token As CancellationToken)
|
Private Sub DownloadPosts(ByVal Token As CancellationToken, Optional ByVal IsTagged As Boolean = False)
|
||||||
Dim URL$ = String.Empty
|
Dim URL$ = String.Empty
|
||||||
Dim dValue% = 1
|
Dim dValue% = 1
|
||||||
Dim _Index% = 0
|
Dim _Index% = 0
|
||||||
Dim before%
|
Dim before%
|
||||||
|
Dim specFolder$ = IIf(IsTagged, "Tagged", String.Empty)
|
||||||
If PostsToReparse.Count > 0 Then ProgressPre.ChangeMax(PostsToReparse.Count)
|
If PostsToReparse.Count > 0 Then ProgressPre.ChangeMax(PostsToReparse.Count)
|
||||||
|
ChangeResponserMode(False)
|
||||||
Try
|
Try
|
||||||
Do While dValue = 1
|
Do While dValue = 1
|
||||||
ThrowAny(Token)
|
ThrowAny(Token)
|
||||||
@@ -606,9 +799,9 @@ Namespace API.Instagram
|
|||||||
ThrowAny(Token)
|
ThrowAny(Token)
|
||||||
NextRequest(((i + 1) Mod 5) = 0)
|
NextRequest(((i + 1) Mod 5) = 0)
|
||||||
ThrowAny(Token)
|
ThrowAny(Token)
|
||||||
|
UpdateRequestNumber()
|
||||||
r = Responser.GetResponse(URL,, e)
|
r = Responser.GetResponse(URL,, e)
|
||||||
MySiteSettings.TooManyRequests(False)
|
MySiteSettings.TooManyRequests(False)
|
||||||
RequestsCount += 1
|
|
||||||
If Not r.IsEmptyString Then
|
If Not r.IsEmptyString Then
|
||||||
j = JsonDocument.Parse(r)
|
j = JsonDocument.Parse(r)
|
||||||
If Not j Is Nothing Then
|
If Not j Is Nothing Then
|
||||||
@@ -616,7 +809,7 @@ Namespace API.Instagram
|
|||||||
With j("items")
|
With j("items")
|
||||||
For Each jj In .Self
|
For Each jj In .Self
|
||||||
before = _TempMediaList.Count
|
before = _TempMediaList.Count
|
||||||
ObtainMedia(jj, PostsToReparse(i).ID)
|
ObtainMedia(jj, PostsToReparse(i).ID, specFolder)
|
||||||
If Not before = _TempMediaList.Count Then _TotalPostsParsed += 1
|
If Not before = _TempMediaList.Count Then _TotalPostsParsed += 1
|
||||||
If _Limit > 0 And _TotalPostsParsed >= _Limit Then Throw New ExitException
|
If _Limit > 0 And _TotalPostsParsed >= _Limit Then Throw New ExitException
|
||||||
Next
|
Next
|
||||||
@@ -641,29 +834,33 @@ Namespace API.Instagram
|
|||||||
ProcessException(DoEx, Token, $"downloading posts error [{URL}]",, Sections.Tagged)
|
ProcessException(DoEx, Token, $"downloading posts error [{URL}]",, Sections.Tagged)
|
||||||
End Try
|
End Try
|
||||||
End Sub
|
End Sub
|
||||||
Private Sub SavedPostsDownload(ByVal Cursor As String, ByVal Token As CancellationToken)
|
''' <summary>Cursor</summary>
|
||||||
|
Private Function SavedPostsDownload(ByVal Cursor As String, ByVal Token As CancellationToken) As String
|
||||||
Dim URL$ = $"https://www.instagram.com/api/v1/feed/saved/posts/?max_id={Cursor}"
|
Dim URL$ = $"https://www.instagram.com/api/v1/feed/saved/posts/?max_id={Cursor}"
|
||||||
Dim HasNextPage As Boolean = False
|
Dim HasNextPage As Boolean = False
|
||||||
Dim NextCursor$ = String.Empty
|
Dim NextCursor$ = String.Empty
|
||||||
ThrowAny(Token)
|
Dim processNext As Boolean = False
|
||||||
|
UpdateRequestNumber()
|
||||||
Dim r$ = Responser.GetResponse(URL)
|
Dim r$ = Responser.GetResponse(URL)
|
||||||
Dim nodes As IEnumerable(Of EContainer) = Nothing
|
Dim nodes As IEnumerable(Of EContainer) = Nothing
|
||||||
If Not r.IsEmptyString Then
|
If Not r.IsEmptyString Then
|
||||||
Using e As EContainer = JsonDocument.Parse(r)
|
Using e As EContainer = JsonDocument.Parse(r)
|
||||||
If If(e?.Count, 0) > 0 Then
|
If e.ListExists Then
|
||||||
With e
|
With e
|
||||||
HasNextPage = .Value("more_available").FromXML(Of Boolean)(False)
|
HasNextPage = .Value("more_available").FromXML(Of Boolean)(False)
|
||||||
NextCursor = .Value("next_max_id")
|
NextCursor = .Value("next_max_id")
|
||||||
If .Contains("items") Then nodes = (From ee As EContainer In .Item("items") Where ee.Count > 0 Select ee(0))
|
If .Contains("items") Then nodes = (From ee As EContainer In .Item("items") Where ee.Count > 0 Select ee(0))
|
||||||
End With
|
End With
|
||||||
If nodes.ListExists AndAlso DefaultParser(nodes, Sections.SavedPosts, Token) AndAlso
|
If nodes.ListExists AndAlso DefaultParser(nodes, Sections.SavedPosts, Token) AndAlso
|
||||||
HasNextPage AndAlso Not NextCursor.IsEmptyString Then SavedPostsDownload(NextCursor, Token)
|
HasNextPage AndAlso Not NextCursor.IsEmptyString Then processNext = True
|
||||||
End If
|
End If
|
||||||
End Using
|
End Using
|
||||||
End If
|
End If
|
||||||
End Sub
|
Return If(processNext, NextCursor, String.Empty)
|
||||||
|
End Function
|
||||||
Protected DefaultParser_ElemNode() As Object = Nothing
|
Protected DefaultParser_ElemNode() As Object = Nothing
|
||||||
Protected DefaultParser_IgnorePass As Boolean = False
|
Protected DefaultParser_IgnorePass As Boolean = False
|
||||||
|
Private ReadOnly DefaultParser_PostUrlCreator_Default As Func(Of PostKV, String) = Function(post) $"https://www.instagram.com/p/{post.Code}/"
|
||||||
Protected DefaultParser_PostUrlCreator As Func(Of PostKV, String) = Function(post) $"https://www.instagram.com/p/{post.Code}/"
|
Protected DefaultParser_PostUrlCreator As Func(Of PostKV, String) = Function(post) $"https://www.instagram.com/p/{post.Code}/"
|
||||||
Protected Function DefaultParser(ByVal Items As IEnumerable(Of EContainer), ByVal Section As Sections, ByVal Token As CancellationToken,
|
Protected Function DefaultParser(ByVal Items As IEnumerable(Of EContainer), ByVal Section As Sections, ByVal Token As CancellationToken,
|
||||||
Optional ByVal SpecFolder As String = Nothing, Optional ByVal State As UStates = UStates.Unknown,
|
Optional ByVal SpecFolder As String = Nothing, Optional ByVal State As UStates = UStates.Unknown,
|
||||||
@@ -685,25 +882,29 @@ Namespace API.Instagram
|
|||||||
For Each nn In Items
|
For Each nn In Items
|
||||||
ProgressPre.Perform()
|
ProgressPre.Perform()
|
||||||
With If(Not DefaultParser_ElemNode Is Nothing, nn.ItemF(DefaultParser_ElemNode), nn)
|
With If(Not DefaultParser_ElemNode Is Nothing, nn.ItemF(DefaultParser_ElemNode), nn)
|
||||||
PostIDKV = New PostKV(.Value("code"), .Value("id"), Section)
|
If .ListExists Then
|
||||||
PostOriginUrl = DefaultParser_PostUrlCreator(PostIDKV)
|
PostIDKV = New PostKV(.Value("code"), .Value("id"), Section)
|
||||||
Pinned = .Contains("timeline_pinned_user_ids")
|
PostOriginUrl = DefaultParser_PostUrlCreator(PostIDKV)
|
||||||
If Not DefaultParser_IgnorePass AndAlso PostKvExists(PostIDKV) Then
|
Pinned = .Contains("timeline_pinned_user_ids")
|
||||||
If Not Pinned Then Return False
|
If Not DefaultParser_IgnorePass AndAlso PostKvExists(PostIDKV) Then
|
||||||
Else
|
If Not Section = Sections.Timeline OrElse Not Pinned Then Return False
|
||||||
_TempPostsList.Add(PostIDKV.ID)
|
Else
|
||||||
PostsKVIDs.ListAddValue(PostIDKV, LNC)
|
_TempPostsList.Add(PostIDKV.ID)
|
||||||
PostDate = .Value("taken_at")
|
PostsKVIDs.ListAddValue(PostIDKV, LNC)
|
||||||
If Not DefaultParser_IgnorePass And Not IsSavedPosts Then
|
PostDate = .Value("taken_at")
|
||||||
Select Case CheckDatesLimit(PostDate, UnixDate32Provider)
|
If Not DefaultParser_IgnorePass And Not IsSavedPosts Then
|
||||||
Case DateResult.Skip : Continue For
|
Select Case CheckDatesLimit(PostDate, UnixDate32Provider)
|
||||||
Case DateResult.Exit : If Not Pinned Then Return False
|
Case DateResult.Skip : Continue For
|
||||||
End Select
|
Case DateResult.Exit : If Not Pinned Then Return False
|
||||||
|
End Select
|
||||||
|
End If
|
||||||
|
before = _TempMediaList.Count
|
||||||
|
ObtainMedia(.Self, PostIDKV.ID, SpecFolder, PostDate,, PostOriginUrl, State, Attempts)
|
||||||
|
If Not before = _TempMediaList.Count Then _TotalPostsParsed += 1
|
||||||
|
If _Limit > 0 And _TotalPostsParsed >= _Limit Then Return False
|
||||||
End If
|
End If
|
||||||
before = _TempMediaList.Count
|
Else
|
||||||
ObtainMedia(.Self, PostIDKV.ID, SpecFolder, PostDate,, PostOriginUrl, State, Attempts)
|
Return False
|
||||||
If Not before = _TempMediaList.Count Then _TotalPostsParsed += 1
|
|
||||||
If _Limit > 0 And _TotalPostsParsed >= _Limit Then Return False
|
|
||||||
End If
|
End If
|
||||||
End With
|
End With
|
||||||
Next
|
Next
|
||||||
@@ -737,15 +938,39 @@ Namespace API.Instagram
|
|||||||
Protected ObtainMedia_SizeFuncVid As Func(Of EContainer, Sizes) = Nothing
|
Protected ObtainMedia_SizeFuncVid As Func(Of EContainer, Sizes) = Nothing
|
||||||
Protected ObtainMedia_SizeFuncPic As Func(Of EContainer, Sizes) = Nothing
|
Protected ObtainMedia_SizeFuncPic As Func(Of EContainer, Sizes) = Nothing
|
||||||
Protected ObtainMedia_AllowAbstract As Boolean = False
|
Protected ObtainMedia_AllowAbstract As Boolean = False
|
||||||
|
Protected Sub ObtainMedia_SetReelsFunc()
|
||||||
|
ObtainMedia_SizeFuncPic = Function(ByVal ss As EContainer) As Sizes
|
||||||
|
If ss.Value("url").IsEmptyString Then
|
||||||
|
Return New Sizes("----", "")
|
||||||
|
ElseIf Not ss.Value("width").IsEmptyString Or Not ss.Value("width").IsEmptyString Then
|
||||||
|
Return New Sizes(CInt(AConvert(Of Integer)(ss.Value("width"), 0)) +
|
||||||
|
CInt(AConvert(Of Integer)(ss.Value("height"), 0)), ss.Value("url"))
|
||||||
|
Else
|
||||||
|
Dim rval$ = RegexReplace(ss.Value("url"), ObtainMedia_SizeFuncPic_RegexP)
|
||||||
|
If Not rval.IsEmptyString Then Return New Sizes(rval, ss.Value("url"))
|
||||||
|
rval = RegexReplace(ss.Value("url"), ObtainMedia_SizeFuncPic_RegexS)
|
||||||
|
If Not rval.IsEmptyString Then Return New Sizes(AConvert(Of Integer)(rval, 1) * -1, ss.Value("url"))
|
||||||
|
Return New Sizes(10000, ss.Value("url"))
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
ObtainMedia_SizeFuncVid = Function(ss) If(ss.Value("url").IsEmptyString, New Sizes("----", ""), New Sizes(10000, ss.Value("url")))
|
||||||
|
End Sub
|
||||||
Protected Sub ObtainMedia(ByVal n As EContainer, ByVal PostID As String, Optional ByVal SpecialFolder As String = Nothing,
|
Protected Sub ObtainMedia(ByVal n As EContainer, ByVal PostID As String, Optional ByVal SpecialFolder As String = Nothing,
|
||||||
Optional ByVal DateObj As String = Nothing, Optional ByVal InitialType As Integer = -1,
|
Optional ByVal DateObj As String = Nothing, Optional ByVal InitialType As Integer = -1,
|
||||||
Optional ByVal PostOriginUrl As String = Nothing,
|
Optional ByVal PostOriginUrl As String = Nothing,
|
||||||
Optional ByVal State As UStates = UStates.Unknown, Optional ByVal Attempts As Integer = 0)
|
Optional ByVal State As UStates = UStates.Unknown, Optional ByVal Attempts As Integer = 0,
|
||||||
|
Optional ByVal TryExtractImage As Boolean = False)
|
||||||
Try
|
Try
|
||||||
|
Dim maxSize As Func(Of EContainer, Integer) = Function(ByVal _ss As EContainer) As Integer
|
||||||
|
Dim w% = AConvert(Of Integer)(_ss.Value("width"), 0)
|
||||||
|
Dim h% = AConvert(Of Integer)(_ss.Value("height"), 0)
|
||||||
|
'Return w + h
|
||||||
|
Return Math.Max(w, h)
|
||||||
|
End Function
|
||||||
Dim wrongData As Predicate(Of Sizes) = Function(_ss) _ss.HasError Or _ss.Data.IsEmptyString
|
Dim wrongData As Predicate(Of Sizes) = Function(_ss) _ss.HasError Or _ss.Data.IsEmptyString
|
||||||
Dim img As Predicate(Of EContainer) = Function(_img) Not _img.Name.IsEmptyString AndAlso _img.Name.StartsWith("image_versions") AndAlso _img.Count > 0
|
Dim img As Predicate(Of EContainer) = Function(_img) Not _img.Name.IsEmptyString AndAlso _img.Name.StartsWith("image_versions") AndAlso _img.Count > 0
|
||||||
Dim vid As Predicate(Of EContainer) = Function(_vid) Not _vid.Name.IsEmptyString AndAlso _vid.Name.StartsWith("video_versions") AndAlso _vid.Count > 0
|
Dim vid As Predicate(Of EContainer) = Function(_vid) Not _vid.Name.IsEmptyString AndAlso _vid.Name.StartsWith("video_versions") AndAlso _vid.Count > 0
|
||||||
Dim ss As Func(Of EContainer, Sizes) = Function(_ss) New Sizes(_ss.Value("width"), _ss.Value("url"))
|
Dim ss As Func(Of EContainer, Sizes) = Function(_ss) New Sizes(maxSize(_ss), _ss.Value("url"))
|
||||||
Dim ssVid As Func(Of EContainer, Sizes) = ss
|
Dim ssVid As Func(Of EContainer, Sizes) = ss
|
||||||
Dim ssPic As Func(Of EContainer, Sizes) = ss
|
Dim ssPic As Func(Of EContainer, Sizes) = ss
|
||||||
Dim mDate As Func(Of EContainer, String) = Function(ByVal elem As EContainer) As String
|
Dim mDate As Func(Of EContainer, String) = Function(ByVal elem As EContainer) As String
|
||||||
@@ -778,7 +1003,10 @@ Namespace API.Instagram
|
|||||||
'2 - one video
|
'2 - one video
|
||||||
'1 - one picture
|
'1 - one picture
|
||||||
t = n.Value("media_type").FromXML(Of Integer)(-1)
|
t = n.Value("media_type").FromXML(Of Integer)(-1)
|
||||||
If t = -1 And InitialType = 8 And ObtainMedia_AllowAbstract Then
|
If TryExtractImage Then
|
||||||
|
t = 1
|
||||||
|
abstractDecision = True
|
||||||
|
ElseIf t = -1 And InitialType = 8 And ObtainMedia_AllowAbstract Then
|
||||||
If n.Contains(vid) Then
|
If n.Contains(vid) Then
|
||||||
t = 2
|
t = 2
|
||||||
abstractDecision = True
|
abstractDecision = True
|
||||||
@@ -789,7 +1017,7 @@ Namespace API.Instagram
|
|||||||
End If
|
End If
|
||||||
If t >= 0 Then
|
If t >= 0 Then
|
||||||
Select Case t
|
Select Case t
|
||||||
Case 1
|
Case 1 'one picture
|
||||||
If n.Contains(img) Then
|
If n.Contains(img) Then
|
||||||
If Not abstractDecision Then t = n.Value("media_type").FromXML(Of Integer)(-1)
|
If Not abstractDecision Then t = n.Value("media_type").FromXML(Of Integer)(-1)
|
||||||
DateObj = mDate(n)
|
DateObj = mDate(n)
|
||||||
@@ -808,7 +1036,7 @@ Namespace API.Instagram
|
|||||||
End With
|
End With
|
||||||
End If
|
End If
|
||||||
End If
|
End If
|
||||||
Case 2
|
Case 2 'one video
|
||||||
If n.Contains(vid) Then
|
If n.Contains(vid) Then
|
||||||
DateObj = mDate(n)
|
DateObj = mDate(n)
|
||||||
With n.ItemF({vid}).XmlIfNothing
|
With n.ItemF({vid}).XmlIfNothing
|
||||||
@@ -824,7 +1052,8 @@ Namespace API.Instagram
|
|||||||
End If
|
End If
|
||||||
End With
|
End With
|
||||||
End If
|
End If
|
||||||
Case 8
|
If Not TryExtractImage Then ObtainMedia(n, PostID, SpecialFolder, DateObj, InitialType, PostOriginUrl, State, Attempts, True)
|
||||||
|
Case 8 'gallery
|
||||||
DateObj = mDate(n)
|
DateObj = mDate(n)
|
||||||
With n("carousel_media").XmlIfNothing
|
With n("carousel_media").XmlIfNothing
|
||||||
If .Count > 0 Then
|
If .Count > 0 Then
|
||||||
@@ -841,11 +1070,12 @@ Namespace API.Instagram
|
|||||||
End Sub
|
End Sub
|
||||||
#End Region
|
#End Region
|
||||||
#Region "GetUserId, GetUserName"
|
#Region "GetUserId, GetUserName"
|
||||||
Private Sub GetUserId()
|
Private Sub GetUserData()
|
||||||
Dim __idFound As Boolean = False
|
Dim __idFound As Boolean = False
|
||||||
Try
|
Try
|
||||||
RequestsCount += 1
|
ChangeResponserMode(False)
|
||||||
Dim r$ = Responser.GetResponse($"https://i.instagram.com/api/v1/users/web_profile_info/?username={NameTrue}",, EDP.ThrowException)
|
UpdateRequestNumber()
|
||||||
|
Dim r$ = Responser.GetResponse($"https://i.instagram.com/api/v1/users/web_profile_info/?username={NameTrue}")
|
||||||
If Not r.IsEmptyString Then
|
If Not r.IsEmptyString Then
|
||||||
Using j As EContainer = JsonDocument.Parse(r)
|
Using j As EContainer = JsonDocument.Parse(r)
|
||||||
If Not j Is Nothing AndAlso j.Contains({"data", "user"}) Then
|
If Not j Is Nothing AndAlso j.Contains({"data", "user"}) Then
|
||||||
@@ -859,7 +1089,7 @@ Namespace API.Instagram
|
|||||||
Dim eUrl$ = .Value("external_url")
|
Dim eUrl$ = .Value("external_url")
|
||||||
If Not eUrl.IsEmptyString AndAlso (descr.IsEmptyString OrElse Not descr.Contains(eUrl)) Then descr.StringAppendLine(eUrl)
|
If Not eUrl.IsEmptyString AndAlso (descr.IsEmptyString OrElse Not descr.Contains(eUrl)) Then descr.StringAppendLine(eUrl)
|
||||||
UserDescriptionUpdate(descr)
|
UserDescriptionUpdate(descr)
|
||||||
Dim f As New SFile With {.Path = MyFile.CutPath.Path, .Name = "ProfilePicture", .Extension = "jpg"}
|
Dim f As New SFile With {.Path = DownloadContentDefault_GetRootDir(), .Name = "ProfilePicture", .Extension = "jpg"}
|
||||||
If Not f.Exists Then
|
If Not f.Exists Then
|
||||||
Dim profilePicture$ = .Value("profile_pic_url_hd")
|
Dim profilePicture$ = .Value("profile_pic_url_hd")
|
||||||
If profilePicture.IsEmptyString OrElse Not GetWebFile(profilePicture, f, EDP.ReturnValue) Then
|
If profilePicture.IsEmptyString OrElse Not GetWebFile(profilePicture, f, EDP.ReturnValue) Then
|
||||||
@@ -879,13 +1109,15 @@ Namespace API.Instagram
|
|||||||
LogError(ex, "get Instagram user ID")
|
LogError(ex, "get Instagram user ID")
|
||||||
End If
|
End If
|
||||||
End If
|
End If
|
||||||
|
Finally
|
||||||
|
ChangeResponserMode(_UseGQL)
|
||||||
End Try
|
End Try
|
||||||
End Sub
|
End Sub
|
||||||
Private Function GetUserNameById() As Boolean
|
Private Function GetUserNameById() As Boolean
|
||||||
UserNameRequested = True
|
UserNameRequested = True
|
||||||
Try
|
Try
|
||||||
If Not ID.IsEmptyString Then
|
If Not ID.IsEmptyString Then
|
||||||
RequestsCount += 1
|
UpdateRequestNumber()
|
||||||
Dim r$ = Responser.GetResponse($"https://i.instagram.com/api/v1/users/{ID}/info/",, EDP.ReturnValue)
|
Dim r$ = Responser.GetResponse($"https://i.instagram.com/api/v1/users/{ID}/info/",, EDP.ReturnValue)
|
||||||
If Not r.IsEmptyString Then
|
If Not r.IsEmptyString Then
|
||||||
Using j As EContainer = JsonDocument.Parse(r, EDP.ReturnValue)
|
Using j As EContainer = JsonDocument.Parse(r, EDP.ReturnValue)
|
||||||
@@ -918,9 +1150,9 @@ Namespace API.Instagram
|
|||||||
Private Sub GetStoriesData(ByRef StoriesList As List(Of String), ByVal GetUserStory As Boolean, ByVal Token As CancellationToken)
|
Private Sub GetStoriesData(ByRef StoriesList As List(Of String), ByVal GetUserStory As Boolean, ByVal Token As CancellationToken)
|
||||||
Const ReqUrl$ = "https://i.instagram.com/api/v1/feed/reels_media/?{0}"
|
Const ReqUrl$ = "https://i.instagram.com/api/v1/feed/reels_media/?{0}"
|
||||||
Dim tmpList As IEnumerable(Of String) = Nothing
|
Dim tmpList As IEnumerable(Of String) = Nothing
|
||||||
Dim qStr$, r$, sFolder$, storyID$, pid$
|
Dim qStr$, r$
|
||||||
Dim i% = -1
|
Dim i% = -1
|
||||||
Dim jj As EContainer, s As EContainer
|
Dim jj As EContainer
|
||||||
ThrowAny(Token)
|
ThrowAny(Token)
|
||||||
If StoriesList.ListExists Or GetUserStory Then
|
If StoriesList.ListExists Or GetUserStory Then
|
||||||
If Not GetUserStory Then tmpList = StoriesList.Take(5)
|
If Not GetUserStory Then tmpList = StoriesList.Take(5)
|
||||||
@@ -930,38 +1162,14 @@ Namespace API.Instagram
|
|||||||
Else
|
Else
|
||||||
qStr = String.Format(ReqUrl, tmpList.Select(Function(q) $"reel_ids=highlight:{q}").ListToString("&"))
|
qStr = String.Format(ReqUrl, tmpList.Select(Function(q) $"reel_ids=highlight:{q}").ListToString("&"))
|
||||||
End If
|
End If
|
||||||
|
UpdateRequestNumber()
|
||||||
r = Responser.GetResponse(qStr,, EDP.ThrowException)
|
r = Responser.GetResponse(qStr,, EDP.ThrowException)
|
||||||
ThrowAny(Token)
|
ThrowAny(Token)
|
||||||
If Not r.IsEmptyString Then
|
If Not r.IsEmptyString Then
|
||||||
Using j As EContainer = JsonDocument.Parse(r).XmlIfNothing
|
Using j As EContainer = JsonDocument.Parse(r).XmlIfNothing
|
||||||
If j.Contains("reels") Then
|
If j.Contains("reels") Then
|
||||||
ProgressPre.ChangeMax(j("reels").Count)
|
ProgressPre.ChangeMax(j("reels").Count)
|
||||||
For Each jj In j("reels")
|
For Each jj In j("reels") : GetStoriesData_ParseSingleHighlight(jj, i, GetUserStory, Token) : Next
|
||||||
ProgressPre.Perform()
|
|
||||||
i += 1
|
|
||||||
sFolder = jj.Value("title").StringRemoveWinForbiddenSymbols
|
|
||||||
storyID = jj.Value("id").Replace("highlight:", String.Empty)
|
|
||||||
If GetUserStory Then
|
|
||||||
sFolder = $"{StoriesFolder} (user)"
|
|
||||||
Else
|
|
||||||
If sFolder.IsEmptyString Then sFolder = $"Story_{storyID}"
|
|
||||||
If sFolder.IsEmptyString Then sFolder = $"Story_{i}"
|
|
||||||
sFolder = $"{StoriesFolder}\{sFolder}"
|
|
||||||
End If
|
|
||||||
If Not storyID.IsEmptyString Then storyID &= ":"
|
|
||||||
With jj("items").XmlIfNothing
|
|
||||||
If .Count > 0 Then
|
|
||||||
For Each s In .Self
|
|
||||||
pid = storyID & s.Value("id")
|
|
||||||
If Not _TempPostsList.Contains(pid) Then
|
|
||||||
ThrowAny(Token)
|
|
||||||
ObtainMedia(s, pid, sFolder)
|
|
||||||
_TempPostsList.Add(pid)
|
|
||||||
End If
|
|
||||||
Next
|
|
||||||
End If
|
|
||||||
End With
|
|
||||||
Next
|
|
||||||
End If
|
End If
|
||||||
End Using
|
End Using
|
||||||
End If
|
End If
|
||||||
@@ -969,8 +1177,39 @@ Namespace API.Instagram
|
|||||||
End If
|
End If
|
||||||
End If
|
End If
|
||||||
End Sub
|
End Sub
|
||||||
|
Private Sub GetStoriesData_ParseSingleHighlight(ByVal Node As EContainer, ByRef Index As Integer, ByVal GetUserStory As Boolean, ByVal Token As CancellationToken)
|
||||||
|
If Not Node Is Nothing Then
|
||||||
|
With Node
|
||||||
|
ProgressPre.Perform()
|
||||||
|
Index += 1
|
||||||
|
Dim pid$
|
||||||
|
Dim sFolder$ = .Value("title").StringRemoveWinForbiddenSymbols
|
||||||
|
Dim storyID$ = .Value("id").Replace("highlight:", String.Empty)
|
||||||
|
If GetUserStory Then
|
||||||
|
sFolder = $"{StoriesFolder} (user)"
|
||||||
|
Else
|
||||||
|
If sFolder.IsEmptyString Then sFolder = $"Story_{storyID.IfNullOrEmpty(Index)}"
|
||||||
|
sFolder = $"{StoriesFolder}\{sFolder}"
|
||||||
|
End If
|
||||||
|
If Not storyID.IsEmptyString Then storyID &= ":"
|
||||||
|
With .Item("items")
|
||||||
|
If .ListExists Then
|
||||||
|
For Each s As EContainer In .Self
|
||||||
|
pid = storyID & s.Value("id")
|
||||||
|
If Not _TempPostsList.Contains(pid) Then
|
||||||
|
ThrowAny(Token)
|
||||||
|
ObtainMedia(s, pid, sFolder)
|
||||||
|
_TempPostsList.Add(pid)
|
||||||
|
End If
|
||||||
|
Next
|
||||||
|
End If
|
||||||
|
End With
|
||||||
|
End With
|
||||||
|
End If
|
||||||
|
End Sub
|
||||||
Private Function GetStoriesList() As List(Of String)
|
Private Function GetStoriesList() As List(Of String)
|
||||||
Try
|
Try
|
||||||
|
UpdateRequestNumber()
|
||||||
Dim r$ = Responser.GetResponse($"https://i.instagram.com/api/v1/highlights/{ID}/highlights_tray/",, EDP.ThrowException)
|
Dim r$ = Responser.GetResponse($"https://i.instagram.com/api/v1/highlights/{ID}/highlights_tray/",, EDP.ThrowException)
|
||||||
If Not r.IsEmptyString Then
|
If Not r.IsEmptyString Then
|
||||||
Dim ee As New ErrorsDescriber(EDP.ReturnValue) With {.DeclaredMessage = New MMessage($"{ToStringForLog()}:")}
|
Dim ee As New ErrorsDescriber(EDP.ReturnValue) With {.DeclaredMessage = New MMessage($"{ToStringForLog()}:")}
|
||||||
@@ -994,6 +1233,7 @@ Namespace API.Instagram
|
|||||||
Protected Overrides Sub EraseData_AdditionalDataFiles()
|
Protected Overrides Sub EraseData_AdditionalDataFiles()
|
||||||
Dim f As SFile = MyFilePostsKV
|
Dim f As SFile = MyFilePostsKV
|
||||||
If f.Exists Then f.Delete(SFO.File, SFODelete.DeleteToRecycleBin, EDP.ReturnValue)
|
If f.Exists Then f.Delete(SFO.File, SFODelete.DeleteToRecycleBin, EDP.ReturnValue)
|
||||||
|
FirstLoadingDone = False
|
||||||
End Sub
|
End Sub
|
||||||
#End Region
|
#End Region
|
||||||
#Region "Exceptions"
|
#Region "Exceptions"
|
||||||
@@ -1028,6 +1268,10 @@ Namespace API.Instagram
|
|||||||
Return 1
|
Return 1
|
||||||
ElseIf Responser.StatusCode = 560 Or Responser.StatusCode = HttpStatusCode.InternalServerError Then '560, 500
|
ElseIf Responser.StatusCode = 560 Or Responser.StatusCode = HttpStatusCode.InternalServerError Then '560, 500
|
||||||
MySiteSettings.SkipUntilNextSession = True
|
MySiteSettings.SkipUntilNextSession = True
|
||||||
|
Err5xx = Responser.StatusCode
|
||||||
|
ElseIf Responser.StatusCode = -1 And Responser.Status = -1 Then
|
||||||
|
MySiteSettings.SkipUntilNextSession = True
|
||||||
|
Err5xx = Responser.StatusCode
|
||||||
Else
|
Else
|
||||||
MyMainLOG = $"Something is wrong. Your credentials may have expired [{CInt(Responser.StatusCode)}/{CInt(Responser.Status)}]: {ToString()} [{s}]"
|
MyMainLOG = $"Something is wrong. Your credentials may have expired [{CInt(Responser.StatusCode)}/{CInt(Responser.Status)}]: {ToString()} [{s}]"
|
||||||
DisableSection(s)
|
DisableSection(s)
|
||||||
@@ -1039,15 +1283,26 @@ Namespace API.Instagram
|
|||||||
Private Sub DisableSection(ByVal Section As Object)
|
Private Sub DisableSection(ByVal Section As Object)
|
||||||
If Not IsNothing(Section) AndAlso TypeOf Section Is Sections Then
|
If Not IsNothing(Section) AndAlso TypeOf Section Is Sections Then
|
||||||
Dim s As Sections = DirectCast(Section, Sections)
|
Dim s As Sections = DirectCast(Section, Sections)
|
||||||
Select Case s
|
Dim ss As New List(Of Sections)([Enum].GetValues(GetType(Sections)).ToObjectsList(Of Sections))
|
||||||
Case Sections.Timeline : MySiteSettings.DownloadTimeline.Value = False
|
If s = Sections.Reels And Not _UseGQL Then
|
||||||
Case Sections.Stories, Sections.UserStories
|
ss.Clear()
|
||||||
MySiteSettings.DownloadTimeline.Value = False
|
ss.Add(s)
|
||||||
MySiteSettings.DownloadStories.Value = False
|
ElseIf s = Sections.Tagged Then
|
||||||
MySiteSettings.DownloadStoriesUser.Value = False
|
ss.Clear()
|
||||||
Case Else : MySiteSettings.DownloadTagged.Value = False
|
ss.Add(s)
|
||||||
End Select
|
End If
|
||||||
MyMainLOG = $"[{s}] downloading is disabled until you update your credentials".ToUpper
|
If ss.Count > 0 Then
|
||||||
|
For Each s In ss
|
||||||
|
Select Case s
|
||||||
|
Case Sections.Reels : MySiteSettings.DownloadReels.Value = False
|
||||||
|
Case Sections.Tagged : MySiteSettings.DownloadTagged.Value = False
|
||||||
|
Case Sections.Timeline, Sections.SavedPosts : MySiteSettings.DownloadTimeline.Value = False
|
||||||
|
Case Sections.Stories : MySiteSettings.DownloadStories.Value = False
|
||||||
|
Case Sections.UserStories : MySiteSettings.DownloadStoriesUser.Value = False
|
||||||
|
End Select
|
||||||
|
Next
|
||||||
|
MyMainLOG = $"[{ss.ListToStringE(, New ANumbers.EnumToStringProvider(GetType(Sections)))}] downloading is disabled until you update your credentials".ToUpper
|
||||||
|
End If
|
||||||
End If
|
End If
|
||||||
End Sub
|
End Sub
|
||||||
#End Region
|
#End Region
|
||||||
@@ -1066,7 +1321,7 @@ Namespace API.Instagram
|
|||||||
#End Region
|
#End Region
|
||||||
#Region "Standalone downloader"
|
#Region "Standalone downloader"
|
||||||
Protected Overrides Sub DownloadSingleObject_GetPosts(ByVal Data As IYouTubeMediaContainer, ByVal Token As CancellationToken)
|
Protected Overrides Sub DownloadSingleObject_GetPosts(ByVal Data As IYouTubeMediaContainer, ByVal Token As CancellationToken)
|
||||||
Dim PID$ = RegexReplace(Data.URL, RParams.DMS(".*?instagram.com/p/([_\w\d]+)", 1))
|
Dim PID$ = RegexReplace(Data.URL, RParams.DMS(String.Format(UserRegexDefaultPattern, "instagram.com/p/"), 1))
|
||||||
If Not PID.IsEmptyString AndAlso Not ACheck(Of Long)(PID) Then PID = CodeToID(PID)
|
If Not PID.IsEmptyString AndAlso Not ACheck(Of Long)(PID) Then PID = CodeToID(PID)
|
||||||
If Not PID.IsEmptyString Then
|
If Not PID.IsEmptyString Then
|
||||||
PostsToReparse.Add(New PostKV With {.ID = PID})
|
PostsToReparse.Add(New PostKV With {.ID = PID})
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
'
|
'
|
||||||
' This program is distributed in the hope that it will be useful,
|
' This program is distributed in the hope that it will be useful,
|
||||||
' but WITHOUT ANY WARRANTY
|
' but WITHOUT ANY WARRANTY
|
||||||
Imports System.Threading
|
Imports System.Net
|
||||||
Imports SCrawler.API.Base
|
Imports SCrawler.API.Base
|
||||||
Imports PersonalUtilities.Tools
|
Imports PersonalUtilities.Tools
|
||||||
Imports PersonalUtilities.Tools.Web
|
Imports PersonalUtilities.Tools.Web
|
||||||
@@ -18,8 +18,10 @@ Namespace API.JustForFans
|
|||||||
Friend NotInheritable Class M3U8 : Implements IDisposable
|
Friend NotInheritable Class M3U8 : Implements IDisposable
|
||||||
#Region "Declarations"
|
#Region "Declarations"
|
||||||
Friend Const AllVid As UTypes = UTypes.m3u8 + UTypes.VideoPre
|
Friend Const AllVid As UTypes = UTypes.m3u8 + UTypes.VideoPre
|
||||||
Private ReadOnly DataVideo As List(Of String)
|
Private Structure M3U8URL_Indexed
|
||||||
Private ReadOnly DataAudio As List(Of String)
|
Friend Index As Integer
|
||||||
|
Friend File As SFile
|
||||||
|
End Structure
|
||||||
Private Media As UserMedia
|
Private Media As UserMedia
|
||||||
Private DestinationFile As SFile
|
Private DestinationFile As SFile
|
||||||
Private ReadOnly Thrower As Plugin.IThrower
|
Private ReadOnly Thrower As Plugin.IThrower
|
||||||
@@ -32,31 +34,37 @@ Namespace API.JustForFans
|
|||||||
Private UrlAudio As String
|
Private UrlAudio As String
|
||||||
Private FileVideo As SFile
|
Private FileVideo As SFile
|
||||||
Private FileAudio As SFile
|
Private FileAudio As SFile
|
||||||
|
Private FileVideo_M3U8 As SFile
|
||||||
|
Private FileAudio_M3U8 As SFile
|
||||||
|
Private ReadOnly FileVideo_IndexedParts As List(Of M3U8URL_Indexed)
|
||||||
|
Private ReadOnly FileAudio_IndexedParts As List(Of M3U8URL_Indexed)
|
||||||
Private RootPlaylistUrl As String
|
Private RootPlaylistUrl As String
|
||||||
Private ReadOnly Cache As CacheKeeper
|
Private ReadOnly Cache As CacheKeeper
|
||||||
Private ReadOnly Progress As MyProgress
|
Private ReadOnly Progress As MyProgress
|
||||||
Private ReadOnly ProgressPre As PreProgress
|
Private ReadOnly ProgressPre As PreProgress
|
||||||
Private ReadOnly ProgressExists As Boolean
|
Private ReadOnly ProgressExists As Boolean
|
||||||
Private ReadOnly UsePreProgress As Boolean
|
Private ReadOnly UsePreProgress As Boolean
|
||||||
Private Property Token As CancellationToken
|
Private ReadOnly REGEX_FILE_EXT As RParams = RParams.DMS("[^\s""]+\.(\w+)([\?&]{1}.+|)", 1, EDP.ReturnValue)
|
||||||
|
Private ReadOnly REGEX_FILE_EXT_M4S As RParams = RParams.DM("[^\s""]+\.m4s([\?&]{1}.+|)", 0, EDP.ReturnValue)
|
||||||
|
Private ReadOnly MyFileNumberProvider As ANumbers
|
||||||
#End Region
|
#End Region
|
||||||
#Region "Initializer"
|
#Region "Initializer"
|
||||||
Private Sub New(ByVal m As UserMedia, ByVal Destination As SFile, ByVal Resp As Responser, ByVal _Thrower As Plugin.IThrower,
|
Private Sub New(ByVal m As UserMedia, ByVal Destination As SFile, ByVal Resp As Responser, ByVal _Thrower As Plugin.IThrower,
|
||||||
ByVal _Progress As MyProgress, ByVal _UsePreProgress As Boolean, ByVal _Token As CancellationToken)
|
ByVal _Progress As MyProgress, ByVal _UsePreProgress As Boolean)
|
||||||
Media = m
|
Media = m
|
||||||
DataVideo = New List(Of String)
|
|
||||||
DataAudio = New List(Of String)
|
|
||||||
DestinationFile = Destination
|
DestinationFile = Destination
|
||||||
Thrower = _Thrower
|
Thrower = _Thrower
|
||||||
'Responser = Resp
|
'Responser = Resp
|
||||||
Responser = New Responser
|
Responser = New Responser
|
||||||
ResponserInternal = True
|
ResponserInternal = True
|
||||||
|
FileVideo_IndexedParts = New List(Of M3U8URL_Indexed)
|
||||||
|
FileAudio_IndexedParts = New List(Of M3U8URL_Indexed)
|
||||||
Progress = _Progress
|
Progress = _Progress
|
||||||
ProgressExists = Not Progress Is Nothing
|
ProgressExists = Not Progress Is Nothing
|
||||||
If ProgressExists Then ProgressPre = New PreProgress(Progress)
|
If ProgressExists Then ProgressPre = New PreProgress(Progress)
|
||||||
UsePreProgress = _UsePreProgress
|
UsePreProgress = _UsePreProgress
|
||||||
Token = _Token
|
MyFileNumberProvider = M3U8Base.NumberProviderDefault
|
||||||
Cache = New CacheKeeper($"{DestinationFile.PathWithSeparator}_{M3U8Base.TempCacheFolderName}\")
|
Cache = New CacheKeeper($"{DestinationFile.PathWithSeparator}_{M3U8Base.TempCacheFolderName}\") With {.DisposeSuspended = True}
|
||||||
With Cache
|
With Cache
|
||||||
.CacheDeleteError = CacheDeletionError(Cache)
|
.CacheDeleteError = CacheDeletionError(Cache)
|
||||||
.DisposeSuspended = True
|
.DisposeSuspended = True
|
||||||
@@ -91,30 +99,138 @@ Namespace API.JustForFans
|
|||||||
UrlVideo = RegexReplace(r, RParams.DMS(R_VIDEO_REGEX_PATTERN, 6, EDP.ReturnValue))
|
UrlVideo = RegexReplace(r, RParams.DMS(R_VIDEO_REGEX_PATTERN, 6, EDP.ReturnValue))
|
||||||
UrlAudio = RegexReplace(r, REGEX_AUDIO_URL)
|
UrlAudio = RegexReplace(r, REGEX_AUDIO_URL)
|
||||||
If UrlVideo.IsEmptyString Then Throw New ArgumentException("Unable to identify m3u8 video track", "M3U8 video track")
|
If UrlVideo.IsEmptyString Then Throw New ArgumentException("Unable to identify m3u8 video track", "M3U8 video track")
|
||||||
|
|
||||||
Thrower.ThrowAny()
|
Thrower.ThrowAny()
|
||||||
GetFiles(UrlVideo, FileVideo, False)
|
GetFileParts(UrlVideo, FileVideo_M3U8, FileVideo_IndexedParts, False)
|
||||||
Thrower.ThrowAny()
|
Thrower.ThrowAny()
|
||||||
If Not UrlAudio.IsEmptyString Then GetFiles(UrlAudio, FileAudio, True)
|
If Not UrlAudio.IsEmptyString Then GetFileParts(UrlAudio, FileAudio_M3U8, FileAudio_IndexedParts, True)
|
||||||
|
|
||||||
|
If FileVideo_IndexedParts.Count > 0 Then _
|
||||||
|
FileVideo = GetTempFile(FileVideo_M3U8, FileVideo_IndexedParts, False, FileAudio_IndexedParts, FileAudio_IndexedParts.Count = 0)
|
||||||
|
If FileAudio_IndexedParts.Count > 0 Then _
|
||||||
|
FileAudio = GetTempFile(FileAudio_M3U8, FileAudio_IndexedParts, True, FileVideo_IndexedParts, False)
|
||||||
Thrower.ThrowAny()
|
Thrower.ThrowAny()
|
||||||
MergeFiles()
|
MergeFiles()
|
||||||
End If
|
End If
|
||||||
End If
|
End If
|
||||||
End Sub
|
End Sub
|
||||||
Private Sub GetFiles(ByVal URL As String, ByRef File As SFile, ByVal IsAudio As Boolean)
|
Private Function GetTempFile(ByVal M3U8File As SFile, ByVal IndexedList As List(Of M3U8URL_Indexed), ByVal IsAudio As Boolean,
|
||||||
|
ByVal IndexedListOther As List(Of M3U8URL_Indexed), ByVal IgnoreAudio As Boolean) As SFile
|
||||||
|
Const mapStr$ = "#EXT-X-MAP:URI"
|
||||||
|
Const extinfStr$ = "#EXTINF:"
|
||||||
|
Const m4s$ = "m4s"
|
||||||
|
Dim M3U8FileLines$() = M3U8File.GetLines
|
||||||
|
If M3U8FileLines.ListExists AndAlso IndexedList.Count > 0 AndAlso (IndexedListOther.Count > 0 Or (Not IsAudio And IgnoreAudio)) Then
|
||||||
|
Dim outputFile As SFile = $"{Cache.RootDirectory.PathWithSeparator}{IIf(IsAudio, "AUDIO.aac", "VIDEO.mp4")}"
|
||||||
|
Dim M3U8FileNew As SFile = M3U8File
|
||||||
|
M3U8FileNew.Path = IndexedList(0).File.Path
|
||||||
|
Dim v$
|
||||||
|
Dim i%, fIndx%, fIndx2%
|
||||||
|
Dim extIsm4s As Boolean
|
||||||
|
Dim LookingIndex% = -1
|
||||||
|
Dim ignoreOtherList As Boolean = IndexedListOther.Count = 0 And (Not IsAudio And IgnoreAudio)
|
||||||
|
Dim fileFinder As Predicate(Of M3U8URL_Indexed) = Function(input) input.Index = LookingIndex
|
||||||
|
|
||||||
|
Using m3u8Text As New TextSaver
|
||||||
|
For i = 0 To M3U8FileLines.Length - 1
|
||||||
|
v = M3U8FileLines(i)
|
||||||
|
|
||||||
|
If Not v.IsEmptyString Then
|
||||||
|
If v.StartsWith(mapStr) Then
|
||||||
|
LookingIndex += 1
|
||||||
|
fIndx = IndexedList.FindIndex(fileFinder)
|
||||||
|
If fIndx >= 0 Then
|
||||||
|
extIsm4s = Not IndexedList(fIndx).File.Extension.IsEmptyString AndAlso IndexedList(fIndx).File.Extension = m4s
|
||||||
|
v = v.Replace(RegexReplace(v, If(extIsm4s, REGEX_FILE_EXT_M4S, REGEX_FILE_EXT)), IndexedList(fIndx).File.File)
|
||||||
|
m3u8Text.AppendLine(v)
|
||||||
|
Else
|
||||||
|
Throw New Exception($"The map file is missing ({IIf(IsAudio, "audio", "video")})")
|
||||||
|
End If
|
||||||
|
ElseIf v.StartsWith(extinfStr) Then
|
||||||
|
LookingIndex += 1
|
||||||
|
If (i + 1) <= M3U8FileLines.Length - 1 Then
|
||||||
|
fIndx = IndexedList.FindIndex(fileFinder)
|
||||||
|
fIndx2 = If(ignoreOtherList, -1, IndexedListOther.FindIndex(fileFinder))
|
||||||
|
If fIndx >= 0 And (fIndx2 >= 0 Or ignoreOtherList) Then
|
||||||
|
If ignoreOtherList OrElse IndexedListOther(fIndx2).Index = IndexedList(fIndx).Index Then
|
||||||
|
m3u8Text.AppendLine(v)
|
||||||
|
m3u8Text.AppendLine(IndexedList(fIndx).File.File)
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
i += 1
|
||||||
|
Else
|
||||||
|
Throw New Exception($"Unexpected end of m3u8 file ({IIf(IsAudio, "audio", "video")})")
|
||||||
|
End If
|
||||||
|
Else
|
||||||
|
m3u8Text.AppendLine(v)
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
Next
|
||||||
|
|
||||||
|
m3u8Text.SaveAs(M3U8FileNew)
|
||||||
|
End Using
|
||||||
|
|
||||||
|
If M3U8FileNew.Exists Then
|
||||||
|
Using b As New BatchExecutor
|
||||||
|
AddHandler b.ErrorDataReceived, AddressOf Batch_OutputDataReceived
|
||||||
|
Thrower.ThrowAny()
|
||||||
|
ProgressChangeMax(IndexedList.Count)
|
||||||
|
b.ChangeDirectory(M3U8FileNew)
|
||||||
|
b.Execute($"""{Settings.FfmpegFile}"" -i {M3U8FileNew.File} -vcodec copy -strict -2 ""{outputFile}""")
|
||||||
|
End Using
|
||||||
|
If Not outputFile.Exists Then outputFile = Nothing
|
||||||
|
End If
|
||||||
|
|
||||||
|
Return outputFile
|
||||||
|
Else
|
||||||
|
Return Nothing
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
Private Sub GetFileParts(ByVal URL As String, ByRef M3U8File As SFile, ByRef IndexedList As List(Of M3U8URL_Indexed), ByVal IsAudio As Boolean)
|
||||||
Try
|
Try
|
||||||
Dim r$ = Responser.GetResponse(URL)
|
Dim r$ = Responser.GetResponse(URL)
|
||||||
If Not r.IsEmptyString Then
|
If Not r.IsEmptyString Then
|
||||||
Dim data As List(Of RegexMatchStruct) = RegexFields(Of RegexMatchStruct)(r, {REGEX_PLS_FILES}, {1, 2}, EDP.ReturnValue)
|
Dim data As List(Of RegexMatchStruct) = RegexFields(Of RegexMatchStruct)(r, {REGEX_PLS_FILES}, {1, 2}, EDP.ReturnValue)
|
||||||
If data.ListExists Then
|
If data.ListExists Then
|
||||||
File = $"{Cache.RootDirectory.PathWithSeparator}{IIf(IsAudio, "AUDIO.aac", "VIDEO.mp4")}"
|
Dim appender$ = URL.Replace(URL.Split("/").LastOrDefault, String.Empty)
|
||||||
Using b As New TokenBatch(Token) With {.Encoding = Settings.CMDEncoding, .MainProcessName = "ffmpeg"}
|
Dim createM3U8URL As Func(Of String, M3U8URL) =
|
||||||
AddHandler b.ErrorDataReceived, AddressOf Batch_OutputDataReceived
|
Function(input) New M3U8URL(M3U8Base.CreateUrl(appender, input), RegexReplace(input, REGEX_FILE_EXT))
|
||||||
ProgressChangeMax(data.Count)
|
With (From d As RegexMatchStruct In data
|
||||||
b.ChangeDirectory(Cache.RootDirectory)
|
Where Not d.Arr(0).IfNullOrEmpty(d.Arr(1)).IsEmptyString
|
||||||
b.Execute($"""{Settings.FfmpegFile}"" -i {URL} -vcodec copy -strict -2 ""{File}""")
|
Select createM3U8URL.Invoke(d.Arr(0).IfNullOrEmpty(d.Arr(1)).StringTrim))
|
||||||
Token.ThrowIfCancellationRequested()
|
If .ListExists Then
|
||||||
If Not File.Exists Then File = Nothing
|
ProgressChangeMax(.Count)
|
||||||
End Using
|
M3U8File = $"{Cache.RootDirectory.PathWithSeparator}{IIf(IsAudio, "AUDIO", "VIDEO")}.m3u8"
|
||||||
|
M3U8File = TextSaver.SaveTextToFile(r, M3U8File, True)
|
||||||
|
|
||||||
|
Dim tmpCache As CacheKeeper = Cache.NewInstance
|
||||||
|
Dim dFile As SFile = tmpCache.RootDirectory
|
||||||
|
dFile.Extension = .ElementAt(0).Extension.IfNullOrEmpty("m4s")
|
||||||
|
MyFileNumberProvider.GroupSize = { .Count.ToString.Length, 3}.Max
|
||||||
|
If tmpCache.Validate Then
|
||||||
|
Using w As New WebClient
|
||||||
|
For i% = 0 To .Count - 1
|
||||||
|
Thrower.ThrowAny()
|
||||||
|
dFile.Name = $"{M3U8Base.TempFilePrefix}{i.NumToString(MyFileNumberProvider)}"
|
||||||
|
dFile.Extension = .ElementAt(i).Extension.IfNullOrEmpty(M3U8Base.TempFileDefaultExtension)
|
||||||
|
Try
|
||||||
|
ProgressPerform()
|
||||||
|
w.DownloadFile(.ElementAt(i).URL, dFile)
|
||||||
|
tmpCache.AddFile(dFile, True)
|
||||||
|
IndexedList.Add(New M3U8URL_Indexed With {.File = dFile, .Index = i})
|
||||||
|
Catch down_oex As OperationCanceledException
|
||||||
|
Throw down_oex
|
||||||
|
Catch down_dex As ObjectDisposedException
|
||||||
|
Throw down_dex
|
||||||
|
Catch ex As Exception
|
||||||
|
End Try
|
||||||
|
Next
|
||||||
|
End Using
|
||||||
|
Else
|
||||||
|
Throw New Exception("Can't create cache directory")
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
End With
|
||||||
End If
|
End If
|
||||||
End If
|
End If
|
||||||
Catch oex As OperationCanceledException
|
Catch oex As OperationCanceledException
|
||||||
@@ -123,7 +239,7 @@ Namespace API.JustForFans
|
|||||||
Throw dex
|
Throw dex
|
||||||
Catch ex As Exception
|
Catch ex As Exception
|
||||||
ErrorsDescriber.Execute(EDP.SendToLog + EDP.ThrowException, ex,
|
ErrorsDescriber.Execute(EDP.SendToLog + EDP.ThrowException, ex,
|
||||||
$"API.JustForFans.M3U8.GetFiles({IIf(IsAudio, "audio", "video")}):{vbCr}URL: {URL}{vbCr}File: {File}")
|
$"API.JustForFans.M3U8.GetFileParts({IIf(IsAudio, "audio", "video")}):{vbCr}URL: {URL}{vbCr}Post: {Media.URL_BASE}")
|
||||||
End Try
|
End Try
|
||||||
End Sub
|
End Sub
|
||||||
Private Async Sub Batch_OutputDataReceived(ByVal Sender As Object, ByVal e As DataReceivedEventArgs)
|
Private Async Sub Batch_OutputDataReceived(ByVal Sender As Object, ByVal e As DataReceivedEventArgs)
|
||||||
@@ -135,8 +251,10 @@ Namespace API.JustForFans
|
|||||||
Dim f As SFile = SFile.IndexReindex(DestinationFile,,, p, EDP.ReturnValue).IfNullOrEmpty(DestinationFile)
|
Dim f As SFile = SFile.IndexReindex(DestinationFile,,, p, EDP.ReturnValue).IfNullOrEmpty(DestinationFile)
|
||||||
If Not FileVideo.IsEmptyString And Not FileAudio.IsEmptyString Then
|
If Not FileVideo.IsEmptyString And Not FileAudio.IsEmptyString Then
|
||||||
DestinationFile = FFMPEG.MergeFiles({FileVideo, FileAudio}, Settings.FfmpegFile, f, Settings.CMDEncoding, p, EDP.ThrowException)
|
DestinationFile = FFMPEG.MergeFiles({FileVideo, FileAudio}, Settings.FfmpegFile, f, Settings.CMDEncoding, p, EDP.ThrowException)
|
||||||
Else
|
ElseIf FileVideo.Exists Then
|
||||||
If Not SFile.Move(FileVideo, f) Then DestinationFile = FileVideo
|
If Not SFile.Move(FileVideo, f) Then DestinationFile = FileVideo
|
||||||
|
Else
|
||||||
|
Throw New Exception($"Unable to download file ({Media.URL_BASE})")
|
||||||
End If
|
End If
|
||||||
Catch ex As Exception
|
Catch ex As Exception
|
||||||
ErrorsDescriber.Execute(EDP.SendToLog + EDP.ThrowException, ex, $"[M3U8.MergeFiles]")
|
ErrorsDescriber.Execute(EDP.SendToLog + EDP.ThrowException, ex, $"[M3U8.MergeFiles]")
|
||||||
@@ -165,8 +283,8 @@ Namespace API.JustForFans
|
|||||||
#End Region
|
#End Region
|
||||||
#Region "Static Download"
|
#Region "Static Download"
|
||||||
Friend Shared Function Download(ByVal Media As UserMedia, ByVal DestinationFile As SFile, ByVal Resp As Responser, ByVal Thrower As Plugin.IThrower,
|
Friend Shared Function Download(ByVal Media As UserMedia, ByVal DestinationFile As SFile, ByVal Resp As Responser, ByVal Thrower As Plugin.IThrower,
|
||||||
ByVal Progress As MyProgress, ByVal UsePreProgress As Boolean, ByVal _Token As CancellationToken) As SFile
|
ByVal Progress As MyProgress, ByVal UsePreProgress As Boolean) As SFile
|
||||||
Using m As New M3U8(Media, DestinationFile, Resp, Thrower, Progress, UsePreProgress, _Token)
|
Using m As New M3U8(Media, DestinationFile, Resp, Thrower, Progress, UsePreProgress)
|
||||||
m.Download()
|
m.Download()
|
||||||
If m.DestinationFile.Exists Then Return m.DestinationFile Else Return Nothing
|
If m.DestinationFile.Exists Then Return m.DestinationFile Else Return Nothing
|
||||||
End Using
|
End Using
|
||||||
@@ -177,8 +295,8 @@ Namespace API.JustForFans
|
|||||||
Private Overloads Sub Dispose(ByVal disposing As Boolean)
|
Private Overloads Sub Dispose(ByVal disposing As Boolean)
|
||||||
If Not disposedValue Then
|
If Not disposedValue Then
|
||||||
If disposing Then
|
If disposing Then
|
||||||
DataVideo.Clear()
|
FileVideo_IndexedParts.Clear()
|
||||||
DataAudio.Clear()
|
FileAudio_IndexedParts.Clear()
|
||||||
ProgressPre.DisposeIfReady
|
ProgressPre.DisposeIfReady
|
||||||
Cache.Dispose()
|
Cache.Dispose()
|
||||||
If ResponserInternal Then Responser.DisposeIfReady
|
If ResponserInternal Then Responser.DisposeIfReady
|
||||||
|
|||||||
@@ -20,9 +20,14 @@ Namespace API.JustForFans
|
|||||||
Friend ReadOnly Property UserID As PropertyValue
|
Friend ReadOnly Property UserID As PropertyValue
|
||||||
<PropertyOption, PXML, PClonable(Clone:=False)>
|
<PropertyOption, PXML, PClonable(Clone:=False)>
|
||||||
Friend ReadOnly Property UserHash4 As PropertyValue
|
Friend ReadOnly Property UserHash4 As PropertyValue
|
||||||
|
<CookieValueExtractor(NameOf(UserHash4))>
|
||||||
|
Private Function GetValueFromCookies(ByVal PropName As String, ByVal c As CookieKeeper) As String
|
||||||
|
Return c.GetCookieValue(UserHash4_CookieName, PropName, NameOf(UserHash4))
|
||||||
|
End Function
|
||||||
<PropertyOption(ControlText:="Accept", ControlToolTip:="Header 'Accept'"), PClonable>
|
<PropertyOption(ControlText:="Accept", ControlToolTip:="Header 'Accept'"), PClonable>
|
||||||
Friend ReadOnly Property HeaderAccept As PropertyValue
|
Friend ReadOnly Property HeaderAccept As PropertyValue
|
||||||
<PropertyOption, PClonable> Friend ReadOnly Property UserAgent As PropertyValue
|
<PropertyOption(InheritanceName:=SettingsCLS.HEADER_DEF_UserAgent), PClonable, PXML(OnlyForChecked:=True)>
|
||||||
|
Friend ReadOnly Property UserAgent As PropertyValue
|
||||||
Private Sub UpdateHeader(ByVal HeaderName As String, ByVal HeaderValue As String)
|
Private Sub UpdateHeader(ByVal HeaderName As String, ByVal HeaderValue As String)
|
||||||
Select Case HeaderName
|
Select Case HeaderName
|
||||||
Case NameOf(HeaderAccept) : If HeaderValue.IsEmptyString Then Responser.Accept = Nothing Else Responser.Accept = HeaderValue
|
Case NameOf(HeaderAccept) : If HeaderValue.IsEmptyString Then Responser.Accept = Nothing Else Responser.Accept = HeaderValue
|
||||||
@@ -46,7 +51,7 @@ Namespace API.JustForFans
|
|||||||
UserAgent = New PropertyValue(If(Responser.UserAgentExists, Responser.UserAgent, String.Empty), GetType(String), Sub(v) UpdateHeader(NameOf(UserAgent), v))
|
UserAgent = New PropertyValue(If(Responser.UserAgentExists, Responser.UserAgent, String.Empty), GetType(String), Sub(v) UpdateHeader(NameOf(UserAgent), v))
|
||||||
|
|
||||||
_AllowUserAgentUpdate = False
|
_AllowUserAgentUpdate = False
|
||||||
UserRegex = RParams.DMS("https://justfor.fans/([^/\?]+)", 1, EDP.ReturnValue)
|
UserRegex = RParams.DMS(String.Format(UserRegexDefaultPattern, "justfor.fans/"), 1, EDP.ReturnValue)
|
||||||
UrlPatternUser = "https://justfor.fans/{0}"
|
UrlPatternUser = "https://justfor.fans/{0}"
|
||||||
ImageVideoContains = "justfor.fans"
|
ImageVideoContains = "justfor.fans"
|
||||||
End Sub
|
End Sub
|
||||||
@@ -60,7 +65,7 @@ Namespace API.JustForFans
|
|||||||
Private Sub UpdateUserHash4()
|
Private Sub UpdateUserHash4()
|
||||||
If Responser.CookiesExists Then
|
If Responser.CookiesExists Then
|
||||||
Dim hv_current$ = UserHash4.Value
|
Dim hv_current$ = UserHash4.Value
|
||||||
Dim hv_cookie$ = If(Responser.Cookies.FirstOrDefault(Function(cc) cc.Name.ToLower = UserHash4_CookieName)?.Value, String.Empty)
|
Dim hv_cookie$ = GetValueFromCookies(NameOf(UserHash4), Responser.Cookies)
|
||||||
If Not hv_cookie.IsEmptyString And Not hv_cookie = hv_current And Responser.Cookies.Changed Then UserHash4.Value = hv_cookie
|
If Not hv_cookie.IsEmptyString And Not hv_cookie = hv_current And Responser.Cookies.Changed Then UserHash4.Value = hv_cookie
|
||||||
End If
|
End If
|
||||||
End Sub
|
End Sub
|
||||||
|
|||||||
@@ -168,6 +168,7 @@ Namespace API.JustForFans
|
|||||||
#Region "Initializer"
|
#Region "Initializer"
|
||||||
Friend Sub New()
|
Friend Sub New()
|
||||||
UseInternalM3U8Function = True
|
UseInternalM3U8Function = True
|
||||||
|
_ResponserAutoUpdateCookies = True
|
||||||
End Sub
|
End Sub
|
||||||
#End Region
|
#End Region
|
||||||
#Region "Download functions"
|
#Region "Download functions"
|
||||||
@@ -191,11 +192,11 @@ Namespace API.JustForFans
|
|||||||
DownloadData(0, Token)
|
DownloadData(0, Token)
|
||||||
Finally
|
Finally
|
||||||
If DownloadTopCount.HasValue Then DownloadTopCount = Nothing
|
If DownloadTopCount.HasValue Then DownloadTopCount = Nothing
|
||||||
Try : RemoveHandler Responser.ResponseReceived, AddressOf Responser_ResponseReceived : Catch : End Try
|
Responser_ResponseReceived_RemoveHandler()
|
||||||
MySettings.UpdateResponser(Responser)
|
MySettings.UpdateResponser(Responser)
|
||||||
End Try
|
End Try
|
||||||
End Sub
|
End Sub
|
||||||
Private Sub Responser_ResponseReceived(ByVal Source As Object, ByVal e As EventArguments.WebDataResponse)
|
Protected Overrides Sub Responser_ResponseReceived(ByVal Source As Object, ByVal e As EventArguments.WebDataResponse)
|
||||||
If e.CookiesExists Then
|
If e.CookiesExists Then
|
||||||
Dim hv$ = If(e.Cookies.FirstOrDefault(Function(cc) cc.Name.StringToLower = SiteSettings.UserHash4_CookieName)?.Value, String.Empty)
|
Dim hv$ = If(e.Cookies.FirstOrDefault(Function(cc) cc.Name.StringToLower = SiteSettings.UserHash4_CookieName)?.Value, String.Empty)
|
||||||
If Not hv.IsEmptyString And Not _UserHash4 = hv Then _UserHash4 = hv
|
If Not hv.IsEmptyString And Not _UserHash4 = hv Then _UserHash4 = hv
|
||||||
@@ -335,7 +336,7 @@ Namespace API.JustForFans
|
|||||||
DownloadContentDefault(Token)
|
DownloadContentDefault(Token)
|
||||||
End Sub
|
End Sub
|
||||||
Protected Overrides Function DownloadM3U8(ByVal URL As String, ByVal Media As UserMedia, ByVal DestinationFile As SFile, ByVal Token As CancellationToken) As SFile
|
Protected Overrides Function DownloadM3U8(ByVal URL As String, ByVal Media As UserMedia, ByVal DestinationFile As SFile, ByVal Token As CancellationToken) As SFile
|
||||||
Return M3U8.Download(Media, DestinationFile, ResponserNoHandlers, Me, Progress, Not IsSingleObjectDownload, Token)
|
Return M3U8.Download(Media, DestinationFile, ResponserNoHandlers, Me, Progress, Not IsSingleObjectDownload)
|
||||||
End Function
|
End Function
|
||||||
#End Region
|
#End Region
|
||||||
#Region "DownloadSingleObject"
|
#Region "DownloadSingleObject"
|
||||||
|
|||||||
@@ -106,9 +106,12 @@ Namespace API.LPSG
|
|||||||
End Sub
|
End Sub
|
||||||
Protected Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False,
|
Protected Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False,
|
||||||
Optional ByVal EObj As Object = Nothing) As Integer
|
Optional ByVal EObj As Object = Nothing) As Integer
|
||||||
If Responser.StatusCode = Net.HttpStatusCode.ServiceUnavailable Then
|
If Responser.StatusCode = Net.HttpStatusCode.ServiceUnavailable Then '503
|
||||||
MyMainLOG = $"{ToStringForLog()}: LPSG not available"
|
MyMainLOG = $"{ToStringForLog()}: LPSG not available"
|
||||||
Return 1
|
Return 1
|
||||||
|
ElseIf Responser.StatusCode = Net.HttpStatusCode.NotFound Then '404
|
||||||
|
UserExists = False
|
||||||
|
Return 1
|
||||||
Else
|
Else
|
||||||
Return 0
|
Return 0
|
||||||
End If
|
End If
|
||||||
|
|||||||
77
SCrawler/API/OnlyFans/OFResources.Designer.vb
generated
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
'------------------------------------------------------------------------------
|
||||||
|
' <auto-generated>
|
||||||
|
' This code was generated by a tool.
|
||||||
|
' Runtime Version:4.0.30319.42000
|
||||||
|
'
|
||||||
|
' Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
' the code is regenerated.
|
||||||
|
' </auto-generated>
|
||||||
|
'------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Option Strict On
|
||||||
|
Option Explicit On
|
||||||
|
|
||||||
|
Imports System
|
||||||
|
|
||||||
|
Namespace My.Resources
|
||||||
|
|
||||||
|
'This class was auto-generated by the StronglyTypedResourceBuilder
|
||||||
|
'class via a tool like ResGen or Visual Studio.
|
||||||
|
'To add or remove a member, edit your .ResX file then rerun ResGen
|
||||||
|
'with the /str option, or rebuild your VS project.
|
||||||
|
'''<summary>
|
||||||
|
''' A strongly-typed resource class, for looking up localized strings, etc.
|
||||||
|
'''</summary>
|
||||||
|
<Global.System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0"), _
|
||||||
|
Global.System.Diagnostics.DebuggerNonUserCodeAttribute(), _
|
||||||
|
Global.System.Runtime.CompilerServices.CompilerGeneratedAttribute()> _
|
||||||
|
Friend Class OFResources
|
||||||
|
|
||||||
|
Private Shared resourceMan As Global.System.Resources.ResourceManager
|
||||||
|
|
||||||
|
Private Shared resourceCulture As Global.System.Globalization.CultureInfo
|
||||||
|
|
||||||
|
<Global.System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")> _
|
||||||
|
Friend Sub New()
|
||||||
|
MyBase.New
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
'''<summary>
|
||||||
|
''' Returns the cached ResourceManager instance used by this class.
|
||||||
|
'''</summary>
|
||||||
|
<Global.System.ComponentModel.EditorBrowsableAttribute(Global.System.ComponentModel.EditorBrowsableState.Advanced)> _
|
||||||
|
Friend Shared 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.OFResources", GetType(OFResources).Assembly)
|
||||||
|
resourceMan = temp
|
||||||
|
End If
|
||||||
|
Return resourceMan
|
||||||
|
End Get
|
||||||
|
End Property
|
||||||
|
|
||||||
|
'''<summary>
|
||||||
|
''' Overrides the current thread's CurrentUICulture property for all
|
||||||
|
''' resource lookups using this strongly typed resource class.
|
||||||
|
'''</summary>
|
||||||
|
<Global.System.ComponentModel.EditorBrowsableAttribute(Global.System.ComponentModel.EditorBrowsableState.Advanced)> _
|
||||||
|
Friend Shared Property Culture() As Global.System.Globalization.CultureInfo
|
||||||
|
Get
|
||||||
|
Return resourceCulture
|
||||||
|
End Get
|
||||||
|
Set
|
||||||
|
resourceCulture = value
|
||||||
|
End Set
|
||||||
|
End Property
|
||||||
|
|
||||||
|
'''<summary>
|
||||||
|
''' Looks up a localized resource of type System.Byte[].
|
||||||
|
'''</summary>
|
||||||
|
Friend Shared ReadOnly Property OFScraperConfigPattern() As Byte()
|
||||||
|
Get
|
||||||
|
Dim obj As Object = ResourceManager.GetObject("OFScraperConfigPattern", resourceCulture)
|
||||||
|
Return CType(obj,Byte())
|
||||||
|
End Get
|
||||||
|
End Property
|
||||||
|
End Class
|
||||||
|
End Namespace
|
||||||
124
SCrawler/API/OnlyFans/OFResources.resx
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
<?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>
|
||||||
|
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||||
|
<data name="OFScraperConfigPattern" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
|
<value>OFScraperConfigPattern.json;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</data>
|
||||||
|
</root>
|
||||||
61
SCrawler/API/OnlyFans/OFScraperConfigPattern.json
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"main_profile": "main_profile",
|
||||||
|
"metadata": "{configpath}/{profile}/.data/{model_username}_{model_id}",
|
||||||
|
"discord": "",
|
||||||
|
"file_options": {
|
||||||
|
"save_location": "",
|
||||||
|
"dir_format": "",
|
||||||
|
"file_format": "{filename}.{ext}",
|
||||||
|
"textlength": 0,
|
||||||
|
"space-replacer": " ",
|
||||||
|
"date": "YYYY-MM-DD"
|
||||||
|
},
|
||||||
|
"download_options": {
|
||||||
|
"file_size_limit": 0,
|
||||||
|
"file_size_min": 0,
|
||||||
|
"filter": [
|
||||||
|
"Images",
|
||||||
|
"Audios",
|
||||||
|
"Videos"
|
||||||
|
],
|
||||||
|
"auto_resume": false
|
||||||
|
},
|
||||||
|
"binary_options": {
|
||||||
|
"mp4decrypt": "",
|
||||||
|
"ffmpeg": ""
|
||||||
|
},
|
||||||
|
"cdm_options": {
|
||||||
|
"private-key": null,
|
||||||
|
"client-id": null,
|
||||||
|
"key-mode-default": "cdrm",
|
||||||
|
"keydb_api": ""
|
||||||
|
},
|
||||||
|
"performance_options": {
|
||||||
|
"download-sems": 6,
|
||||||
|
"maxfile-sem": 0,
|
||||||
|
"threads": 5
|
||||||
|
},
|
||||||
|
"advanced_options": {
|
||||||
|
"code-execution": false,
|
||||||
|
"dynamic-mode-default": "deviint",
|
||||||
|
"backend": "aio",
|
||||||
|
"downloadbars": false,
|
||||||
|
"cache-mode": "sqlite",
|
||||||
|
"appendlog": true,
|
||||||
|
"custom": null,
|
||||||
|
"sanitize_text": false,
|
||||||
|
"avatar": true
|
||||||
|
},
|
||||||
|
"responsetype": {
|
||||||
|
"timeline": "Posts",
|
||||||
|
"message": "Messages",
|
||||||
|
"archived": "Archived",
|
||||||
|
"paid": "Messages",
|
||||||
|
"stories": "Stories",
|
||||||
|
"highlights": "Stories",
|
||||||
|
"profile": "Profile",
|
||||||
|
"pinned": "Posts"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,26 +18,31 @@ Namespace API.OnlyFans
|
|||||||
Friend Class SiteSettings : Inherits SiteSettingsBase
|
Friend Class SiteSettings : Inherits SiteSettingsBase
|
||||||
#Region "Declarations"
|
#Region "Declarations"
|
||||||
#Region "Options"
|
#Region "Options"
|
||||||
|
<PropertyOption(ControlText:="Download timeline", ControlToolTip:="Download user timeline"), PXML, PClonable>
|
||||||
|
Friend ReadOnly Property DownloadTimeline As PropertyValue
|
||||||
|
<PropertyOption(ControlText:="Download stories", ControlToolTip:="Download profile stories if they exists"), PXML, PClonable>
|
||||||
|
Friend ReadOnly Property DownloadStories As PropertyValue
|
||||||
<PropertyOption(ControlText:="Download highlights", ControlToolTip:="Download profile highlights if they exists"), PXML, PClonable>
|
<PropertyOption(ControlText:="Download highlights", ControlToolTip:="Download profile highlights if they exists"), PXML, PClonable>
|
||||||
Friend Property DownloadHighlights As PropertyValue
|
Friend ReadOnly Property DownloadHighlights As PropertyValue
|
||||||
<PropertyOption(ControlText:="Download chat", ControlToolTip:="Download unlocked chat media"), PXML, PClonable>
|
<PropertyOption(ControlText:="Download chat", ControlToolTip:="Download unlocked chat media"), PXML, PClonable>
|
||||||
Friend Property DownloadChatMedia As PropertyValue
|
Friend ReadOnly Property DownloadChatMedia As PropertyValue
|
||||||
#End Region
|
#End Region
|
||||||
#Region "Headers"
|
#Region "Headers"
|
||||||
Private Const HeaderBrowser As String = "sec-ch-ua"
|
Private Const HeaderBrowser As String = "sec-ch-ua"
|
||||||
Private Const HeaderUserID As String = "User-Id"
|
Private Const HeaderUserID As String = "User-Id"
|
||||||
Private Const HeaderXBC As String = "X-Bc"
|
Friend Const HeaderXBC As String = "X-Bc"
|
||||||
Private Const HeaderAppToken As String = "App-Token"
|
Friend Const HeaderAppToken As String = "App-Token"
|
||||||
<PropertyOption(ControlText:=HeaderUserID, AllowNull:=False), PClonable(Clone:=False)>
|
<PropertyOption(ControlText:=HeaderUserID, AllowNull:=False), PClonable(Clone:=False)>
|
||||||
Friend ReadOnly Property HH_USER_ID As PropertyValue
|
Friend ReadOnly Property HH_USER_ID As PropertyValue
|
||||||
<PropertyOption(ControlText:=HeaderXBC, AllowNull:=False), PClonable(Clone:=False)>
|
<PropertyOption(ControlText:=HeaderXBC, AllowNull:=False), PClonable(Clone:=False)>
|
||||||
Private ReadOnly Property HH_X_BC As PropertyValue
|
Private ReadOnly Property HH_X_BC As PropertyValue
|
||||||
<PropertyOption(ControlText:=HeaderAppToken, AllowNull:=False), PClonable(Clone:=False)>
|
<PropertyOption(ControlText:=HeaderAppToken, AllowNull:=False), PClonable(Clone:=False)>
|
||||||
Private ReadOnly Property HH_APP_TOKEN As PropertyValue
|
Private ReadOnly Property HH_APP_TOKEN As PropertyValue
|
||||||
<PropertyOption(ControlText:=HeaderBrowser, ControlToolTip:="Can be null", AllowNull:=True), PClonable>
|
<PropertyOption(ControlText:=HeaderBrowser, ControlToolTip:="Can be null", AllowNull:=True,
|
||||||
|
InheritanceName:=SettingsCLS.HEADER_DEF_sec_ch_ua), PClonable, PXML(OnlyForChecked:=True)>
|
||||||
Private ReadOnly Property HH_BROWSER As PropertyValue
|
Private ReadOnly Property HH_BROWSER As PropertyValue
|
||||||
<PropertyOption(AllowNull:=False), PClonable>
|
<PropertyOption(AllowNull:=False, InheritanceName:=SettingsCLS.HEADER_DEF_UserAgent), PClonable, PXML(OnlyForChecked:=True)>
|
||||||
Private ReadOnly Property UserAgent As PropertyValue
|
Friend ReadOnly Property UserAgent As PropertyValue
|
||||||
Private Sub UpdateHeader(ByVal PropertyName As String, ByVal Value As String)
|
Private Sub UpdateHeader(ByVal PropertyName As String, ByVal Value As String)
|
||||||
Dim hName$ = String.Empty
|
Dim hName$ = String.Empty
|
||||||
Dim isUserAgent As Boolean = False
|
Dim isUserAgent As Boolean = False
|
||||||
@@ -54,6 +59,16 @@ Namespace API.OnlyFans
|
|||||||
Responser.UserAgent = Value
|
Responser.UserAgent = Value
|
||||||
End If
|
End If
|
||||||
End Sub
|
End Sub
|
||||||
|
<CookieValueExtractor(NameOf(HH_USER_ID)), CookieValueExtractor(NameOf(HH_X_BC))>
|
||||||
|
Private Function GetValueFromCookies(ByVal PropName As String, ByVal c As CookieKeeper) As String
|
||||||
|
If c.ListExists Then
|
||||||
|
Select Case PropName
|
||||||
|
Case NameOf(HH_USER_ID) : Return c.GetCookieValue("auth_id")
|
||||||
|
Case NameOf(HH_X_BC) : Return c.GetCookieValue("fp")
|
||||||
|
End Select
|
||||||
|
End If
|
||||||
|
Return String.Empty
|
||||||
|
End Function
|
||||||
#End Region
|
#End Region
|
||||||
#Region "Rules"
|
#Region "Rules"
|
||||||
<PXML("LastDateUpdated")> Private ReadOnly Property LastDateUpdated_XML As PropertyValue
|
<PXML("LastDateUpdated")> Private ReadOnly Property LastDateUpdated_XML As PropertyValue
|
||||||
@@ -78,6 +93,42 @@ Namespace API.OnlyFans
|
|||||||
"Change this value only if you know what you are doing."), PXML, PClonable>
|
"Change this value only if you know what you are doing."), PXML, PClonable>
|
||||||
Friend ReadOnly Property DynamicRules As PropertyValue
|
Friend ReadOnly Property DynamicRules As PropertyValue
|
||||||
#End Region
|
#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'")>
|
||||||
|
Friend ReadOnly Property OFScraperPath As PropertyValue
|
||||||
|
Get
|
||||||
|
If Not DefaultInstance Is Nothing Then
|
||||||
|
Return DirectCast(DefaultInstance, SiteSettings).OFScraperPath_XML
|
||||||
|
Else
|
||||||
|
Return OFScraperPath_XML
|
||||||
|
End If
|
||||||
|
End Get
|
||||||
|
End Property
|
||||||
|
<PClonable, PXML("OFScraperMP4decrypt")> Private ReadOnly Property OFScraperMP4decrypt_XML As PropertyValue
|
||||||
|
<PropertyOption(ControlText:="mp4decrypt path", ControlToolTip:="The path to the 'mp4decrypt.exe'")>
|
||||||
|
Friend ReadOnly Property OFScraperMP4decrypt As PropertyValue
|
||||||
|
Get
|
||||||
|
If Not DefaultInstance Is Nothing Then
|
||||||
|
Return DirectCast(DefaultInstance, SiteSettings).OFScraperMP4decrypt_XML
|
||||||
|
Else
|
||||||
|
Return OFScraperMP4decrypt_XML
|
||||||
|
End If
|
||||||
|
End Get
|
||||||
|
End Property
|
||||||
|
Friend Const KeyModeDefault_Default As String = "cdrm"
|
||||||
|
<PClonable, PXML("KeyModeDefault")> Private ReadOnly Property KeyModeDefault_XML As PropertyValue
|
||||||
|
<PropertyOption(ControlText:="key-mode-default")>
|
||||||
|
Friend ReadOnly Property KeyModeDefault As PropertyValue
|
||||||
|
Get
|
||||||
|
If Not DefaultInstance Is Nothing Then
|
||||||
|
Return DirectCast(DefaultInstance, SiteSettings).KeyModeDefault_XML
|
||||||
|
Else
|
||||||
|
Return KeyModeDefault_XML
|
||||||
|
End If
|
||||||
|
End Get
|
||||||
|
End Property
|
||||||
|
#End Region
|
||||||
#End Region
|
#End Region
|
||||||
#Region "Initializer"
|
#Region "Initializer"
|
||||||
Friend Sub New(ByVal AccName As String, ByVal Temp As Boolean)
|
Friend Sub New(ByVal AccName As String, ByVal Temp As Boolean)
|
||||||
@@ -108,16 +159,34 @@ Namespace API.OnlyFans
|
|||||||
UserAgent = New PropertyValue(IIf(.UserAgentExists, .UserAgent, String.Empty), GetType(String), Sub(v) UpdateHeader(NameOf(UserAgent), v))
|
UserAgent = New PropertyValue(IIf(.UserAgentExists, .UserAgent, String.Empty), GetType(String), Sub(v) UpdateHeader(NameOf(UserAgent), v))
|
||||||
End With
|
End With
|
||||||
|
|
||||||
|
DownloadTimeline = New PropertyValue(True)
|
||||||
|
DownloadStories = New PropertyValue(True)
|
||||||
DownloadHighlights = New PropertyValue(True)
|
DownloadHighlights = New PropertyValue(True)
|
||||||
DownloadChatMedia = New PropertyValue(True)
|
DownloadChatMedia = New PropertyValue(True)
|
||||||
|
|
||||||
LastDateUpdated_XML = New PropertyValue(Now.AddYears(-1), GetType(Date))
|
LastDateUpdated_XML = New PropertyValue(Now.AddYears(-1), GetType(Date))
|
||||||
UseOldAuthRules = New PropertyValue(False)
|
'URGENT: OF [UseOldAuthRules = True]
|
||||||
|
UseOldAuthRules = New PropertyValue(True)
|
||||||
DynamicRulesUpdateInterval = New PropertyValue(60 * 24)
|
DynamicRulesUpdateInterval = New PropertyValue(60 * 24)
|
||||||
DynamicRulesUpdateIntervalProvider = New FieldsCheckerProviderSimple(Function(v) IIf(AConvert(Of Integer)(v, 0) > 0, v, Nothing),
|
DynamicRulesUpdateIntervalProvider = New FieldsCheckerProviderSimple(Function(v) IIf(AConvert(Of Integer)(v, 0) > 0, v, Nothing),
|
||||||
"The value of [{0}] field must be greater than 0")
|
"The value of [{0}] field must be greater than 0")
|
||||||
DynamicRules = New PropertyValue(String.Empty, GetType(String))
|
DynamicRules = New PropertyValue(String.Empty, GetType(String))
|
||||||
UserRegex = RParams.DMS("onlyfans.com/([\w\._]+)", 1, EDP.ReturnValue)
|
OFScraperPath_XML = New PropertyValue(String.Empty, GetType(String))
|
||||||
|
If ACheck(OFScraperPath_XML.Value) Then
|
||||||
|
Dim f As SFile = OFScraperPath_XML.Value
|
||||||
|
If Not f.Exists AndAlso f.Exists(SFO.Path, False) Then
|
||||||
|
With SFile.GetFiles(f, "*.exe",, EDP.ReturnValue)
|
||||||
|
If .ListExists Then
|
||||||
|
f = .FirstOrDefault(Function(ff) ff.Name.StringToLower.StartsWith("ofscraper"))
|
||||||
|
If f.Exists Then OFScraperPath_XML.Value = f.ToString
|
||||||
|
End If
|
||||||
|
End With
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
OFScraperMP4decrypt_XML = New PropertyValue(String.Empty, GetType(String))
|
||||||
|
KeyModeDefault_XML = New PropertyValue(KeyModeDefault_Default)
|
||||||
|
|
||||||
|
UserRegex = RParams.DMS(String.Format(UserRegexDefaultPattern, "onlyfans.com/"), 1, EDP.ReturnValue)
|
||||||
UrlPatternUser = "https://onlyfans.com/{0}"
|
UrlPatternUser = "https://onlyfans.com/{0}"
|
||||||
ImageVideoContains = "onlyfans.com"
|
ImageVideoContains = "onlyfans.com"
|
||||||
End Sub
|
End Sub
|
||||||
@@ -151,6 +220,7 @@ Namespace API.OnlyFans
|
|||||||
End Sub
|
End Sub
|
||||||
#End Region
|
#End Region
|
||||||
#Region "GetUserUrl, GetUserPostUrl, UserOptions"
|
#Region "GetUserUrl, GetUserPostUrl, UserOptions"
|
||||||
|
Friend Const UserPostPattern As String = "https://onlyfans.com/{0}/{1}"
|
||||||
Friend Overrides Function GetUserUrl(ByVal User As IPluginContentProvider) As String
|
Friend Overrides Function GetUserUrl(ByVal User As IPluginContentProvider) As String
|
||||||
Return String.Format(UrlPatternUser, If(User.ID.IsEmptyString, User.Name, $"u{User.ID}"))
|
Return String.Format(UrlPatternUser, If(User.ID.IsEmptyString, User.Name, $"u{User.ID}"))
|
||||||
End Function
|
End Function
|
||||||
@@ -168,7 +238,7 @@ Namespace API.OnlyFans
|
|||||||
If p.IsEmptyString Then
|
If p.IsEmptyString Then
|
||||||
Return GetUserUrl(User)
|
Return GetUserUrl(User)
|
||||||
Else
|
Else
|
||||||
Return String.Format("https://onlyfans.com/{0}/{1}", p, If(User.ID.IsEmptyString, User.Name, $"u{User.ID}"))
|
Return String.Format(UserPostPattern, p, If(User.ID.IsEmptyString, User.Name, $"u{User.ID}"))
|
||||||
End If
|
End If
|
||||||
Else
|
Else
|
||||||
Return String.Empty
|
Return String.Empty
|
||||||
|
|||||||
@@ -7,10 +7,12 @@
|
|||||||
' This program is distributed in the hope that it will be useful,
|
' This program is distributed in the hope that it will be useful,
|
||||||
' but WITHOUT ANY WARRANTY
|
' but WITHOUT ANY WARRANTY
|
||||||
Imports System.Threading
|
Imports System.Threading
|
||||||
|
Imports System.Text.RegularExpressions
|
||||||
Imports SCrawler.API.Base
|
Imports SCrawler.API.Base
|
||||||
Imports SCrawler.API.YouTube.Objects
|
Imports SCrawler.API.YouTube.Objects
|
||||||
Imports PersonalUtilities.Functions.XML
|
Imports PersonalUtilities.Functions.XML
|
||||||
Imports PersonalUtilities.Functions.RegularExpressions
|
Imports PersonalUtilities.Functions.RegularExpressions
|
||||||
|
Imports PersonalUtilities.Tools
|
||||||
Imports PersonalUtilities.Tools.Web.Clients
|
Imports PersonalUtilities.Tools.Web.Clients
|
||||||
Imports PersonalUtilities.Tools.Web.Clients.EventArguments
|
Imports PersonalUtilities.Tools.Web.Clients.EventArguments
|
||||||
Imports PersonalUtilities.Tools.Web.Cookies
|
Imports PersonalUtilities.Tools.Web.Cookies
|
||||||
@@ -20,6 +22,8 @@ Imports UStates = SCrawler.API.Base.UserMedia.States
|
|||||||
Namespace API.OnlyFans
|
Namespace API.OnlyFans
|
||||||
Friend Class UserData : Inherits UserDataBase
|
Friend Class UserData : Inherits UserDataBase
|
||||||
#Region "XML names"
|
#Region "XML names"
|
||||||
|
Private Const Name_MediaDownloadTimeline As String = "MediaDownloadTimeline"
|
||||||
|
Private Const Name_MediaDownloadStories As String = "MediaDownloadStories"
|
||||||
Private Const Name_MediaDownloadHighlights As String = "DownloadHighlights"
|
Private Const Name_MediaDownloadHighlights As String = "DownloadHighlights"
|
||||||
Private Const Name_MediaDownloadChatMedia As String = "DownloadChatMedia"
|
Private Const Name_MediaDownloadChatMedia As String = "DownloadChatMedia"
|
||||||
#End Region
|
#End Region
|
||||||
@@ -28,6 +32,8 @@ Namespace API.OnlyFans
|
|||||||
Private Const HeaderSign As String = "Sign"
|
Private Const HeaderSign As String = "Sign"
|
||||||
Private Const HeaderTime As String = "Time"
|
Private Const HeaderTime As String = "Time"
|
||||||
Private ReadOnly HighlightsList As List(Of String)
|
Private ReadOnly HighlightsList As List(Of String)
|
||||||
|
Friend Property MediaDownloadTimeline As Boolean = True
|
||||||
|
Friend Property MediaDownloadStories As Boolean = True
|
||||||
Friend Property MediaDownloadHighlights As Boolean = True
|
Friend Property MediaDownloadHighlights As Boolean = True
|
||||||
Friend Property MediaDownloadChatMedia As Boolean = True
|
Friend Property MediaDownloadChatMedia As Boolean = True
|
||||||
Private ReadOnly Property MySettings As SiteSettings
|
Private ReadOnly Property MySettings As SiteSettings
|
||||||
@@ -40,9 +46,13 @@ Namespace API.OnlyFans
|
|||||||
Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean)
|
Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean)
|
||||||
With Container
|
With Container
|
||||||
If Loading Then
|
If Loading Then
|
||||||
|
MediaDownloadTimeline = .Value(Name_MediaDownloadTimeline).FromXML(Of Boolean)(True)
|
||||||
|
MediaDownloadStories = .Value(Name_MediaDownloadStories).FromXML(Of Boolean)(True)
|
||||||
MediaDownloadHighlights = .Value(Name_MediaDownloadHighlights).FromXML(Of Boolean)(True)
|
MediaDownloadHighlights = .Value(Name_MediaDownloadHighlights).FromXML(Of Boolean)(True)
|
||||||
MediaDownloadChatMedia = .Value(Name_MediaDownloadChatMedia).FromXML(Of Boolean)(True)
|
MediaDownloadChatMedia = .Value(Name_MediaDownloadChatMedia).FromXML(Of Boolean)(True)
|
||||||
Else
|
Else
|
||||||
|
.Add(Name_MediaDownloadTimeline, MediaDownloadTimeline.BoolToInteger)
|
||||||
|
.Add(Name_MediaDownloadStories, MediaDownloadStories.BoolToInteger)
|
||||||
.Add(Name_MediaDownloadHighlights, MediaDownloadHighlights.BoolToInteger)
|
.Add(Name_MediaDownloadHighlights, MediaDownloadHighlights.BoolToInteger)
|
||||||
.Add(Name_MediaDownloadChatMedia, MediaDownloadChatMedia.BoolToInteger)
|
.Add(Name_MediaDownloadChatMedia, MediaDownloadChatMedia.BoolToInteger)
|
||||||
End If
|
End If
|
||||||
@@ -56,6 +66,8 @@ Namespace API.OnlyFans
|
|||||||
Friend Overrides Sub ExchangeOptionsSet(ByVal Obj As Object)
|
Friend Overrides Sub ExchangeOptionsSet(ByVal Obj As Object)
|
||||||
If Not Obj Is Nothing AndAlso TypeOf Obj Is UserExchangeOptions Then
|
If Not Obj Is Nothing AndAlso TypeOf Obj Is UserExchangeOptions Then
|
||||||
With DirectCast(Obj, UserExchangeOptions)
|
With DirectCast(Obj, UserExchangeOptions)
|
||||||
|
MediaDownloadTimeline = .DownloadTimeline
|
||||||
|
MediaDownloadStories = .DownloadStories
|
||||||
MediaDownloadHighlights = .DownloadHighlights
|
MediaDownloadHighlights = .DownloadHighlights
|
||||||
MediaDownloadChatMedia = .DownloadChatMedia
|
MediaDownloadChatMedia = .DownloadChatMedia
|
||||||
End With
|
End With
|
||||||
@@ -65,24 +77,47 @@ Namespace API.OnlyFans
|
|||||||
#Region "Initializer"
|
#Region "Initializer"
|
||||||
Friend Sub New()
|
Friend Sub New()
|
||||||
HighlightsList = New List(Of String)
|
HighlightsList = New List(Of String)
|
||||||
|
UseInternalDownloadFileFunction = True
|
||||||
End Sub
|
End Sub
|
||||||
#End Region
|
#End Region
|
||||||
#Region "Download functions"
|
#Region "Download functions"
|
||||||
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
|
Private _OFScraperExists As Boolean = False
|
||||||
If Not MySettings.SessionAborted Then
|
Private OFSCache As CacheKeeper = Nothing
|
||||||
If Not CCookie Is Nothing Then CCookie.Dispose()
|
Private _AbsMediaIndex As Integer = 0
|
||||||
CCookie = Responser.Cookies.Copy
|
Private FunctionErr As Integer = FunctionErrDef
|
||||||
Responser.Cookies.Clear()
|
Private Const FunctionErrDef As Integer = -100
|
||||||
AddHandler Responser.ResponseReceived, AddressOf OnResponseReceived
|
Private Sub ValidateOFScraper()
|
||||||
UpdateCookieHeader()
|
_OFScraperExists = ACheck(MySettings.OFScraperPath.Value) AndAlso CStr(MySettings.OFScraperPath.Value).CSFile.Exists
|
||||||
DownloadTimeline(IIf(IsSavedPosts, 0, String.Empty), Token)
|
|
||||||
If Not IsSavedPosts Then
|
|
||||||
If MediaDownloadHighlights Then DownloadHighlights(Token)
|
|
||||||
If MediaDownloadChatMedia Then DownloadChatMedia(0, Token)
|
|
||||||
End If
|
|
||||||
End If
|
|
||||||
End Sub
|
End Sub
|
||||||
Private Sub OnResponseReceived(ByVal Sender As Object, ByVal e As WebDataResponse)
|
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
|
||||||
|
Try
|
||||||
|
If Not MySettings.SessionAborted Then
|
||||||
|
ValidateOFScraper()
|
||||||
|
_AbsMediaIndex = 0
|
||||||
|
FunctionErr = FunctionErrDef
|
||||||
|
If Not CCookie Is Nothing Then CCookie.Dispose()
|
||||||
|
CCookie = Responser.Cookies.Copy
|
||||||
|
Responser.Cookies.Clear()
|
||||||
|
AddHandler Responser.ResponseReceived, AddressOf Responser_ResponseReceived
|
||||||
|
UpdateCookieHeader()
|
||||||
|
|
||||||
|
If Not IsSavedPosts Then
|
||||||
|
If ID.IsEmptyString Then GetUserID()
|
||||||
|
If ID.IsEmptyString Then Throw New ArgumentNullException("ID", "Unable to get user ID")
|
||||||
|
End If
|
||||||
|
|
||||||
|
If MediaDownloadTimeline Then DownloadTimeline(IIf(IsSavedPosts, 0, String.Empty), Token)
|
||||||
|
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
|
||||||
|
End If
|
||||||
|
Finally
|
||||||
|
Responser_ResponseReceived_RemoveHandler()
|
||||||
|
End Try
|
||||||
|
End Sub
|
||||||
|
Protected Overrides Sub Responser_ResponseReceived(ByVal Sender As Object, ByVal e As WebDataResponse)
|
||||||
If e.CookiesExists Then
|
If e.CookiesExists Then
|
||||||
CCookie.Update(e.Cookies, CookieKeeper.UpdateModes.ReplaceByNameAll,, EDP.ReturnValue)
|
CCookie.Update(e.Cookies, CookieKeeper.UpdateModes.ReplaceByNameAll,, EDP.ReturnValue)
|
||||||
UpdateCookieHeader()
|
UpdateCookieHeader()
|
||||||
@@ -91,7 +126,12 @@ Namespace API.OnlyFans
|
|||||||
Private Sub UpdateCookieHeader()
|
Private Sub UpdateCookieHeader()
|
||||||
Responser.Headers.Add("Cookie", CCookie.ToString(False))
|
Responser.Headers.Add("Cookie", CCookie.ToString(False))
|
||||||
End Sub
|
End Sub
|
||||||
|
Private Function ProcessFunctionErrComplete(ByVal ErrValue As Integer) As Boolean
|
||||||
|
If ErrValue <= 0 Or (ErrValue > 0 And ErrValue <> 2) Then FunctionErr = ErrValue
|
||||||
|
Return ErrValue <> 2
|
||||||
|
End Function
|
||||||
Friend Const A_HIGHLIGHT As String = "HL"
|
Friend Const A_HIGHLIGHT As String = "HL"
|
||||||
|
Friend Const A_STORIES As String = "ST"
|
||||||
Friend Const A_MESSAGE As String = "MSG"
|
Friend Const A_MESSAGE As String = "MSG"
|
||||||
Private Const BaseUrlPattern As String = "https://onlyfans.com{0}"
|
Private Const BaseUrlPattern As String = "https://onlyfans.com{0}"
|
||||||
#Region "Download timeline"
|
#Region "Download timeline"
|
||||||
@@ -111,9 +151,6 @@ Namespace API.OnlyFans
|
|||||||
If IsSavedPosts Then
|
If IsSavedPosts Then
|
||||||
path = $"/api2/v2/posts/bookmarks/all/?format=infinite&limit=10&offset={Cursor}"
|
path = $"/api2/v2/posts/bookmarks/all/?format=infinite&limit=10&offset={Cursor}"
|
||||||
Else
|
Else
|
||||||
If ID.IsEmptyString Then GetUserID()
|
|
||||||
If ID.IsEmptyString Then Throw New ArgumentNullException("ID", "Unable to get user ID")
|
|
||||||
|
|
||||||
path = $"/api2/v2/users/{ID}/posts/medias?limit=50&order=publish_date_desc&skip_users=all&format=infinite&counters=1"
|
path = $"/api2/v2/users/{ID}/posts/medias?limit=50&order=publish_date_desc&skip_users=all&format=infinite&counters=1"
|
||||||
If Not Cursor.IsEmptyString Then path &= $"&counters=0&beforePublishTime={Cursor}" Else path &= "&counters=1"
|
If Not Cursor.IsEmptyString Then path &= $"&counters=0&beforePublishTime={Cursor}" Else path &= "&counters=1"
|
||||||
End If
|
End If
|
||||||
@@ -169,7 +206,7 @@ Namespace API.OnlyFans
|
|||||||
DownloadTimeline(tmpCursor, Token)
|
DownloadTimeline(tmpCursor, Token)
|
||||||
End If
|
End If
|
||||||
Catch ex As Exception
|
Catch ex As Exception
|
||||||
_complete = Not ProcessException(ex, Token, $"data downloading error [{url}]") = 2
|
_complete = ProcessFunctionErrComplete(ProcessException(ex, Token, $"data downloading error [{url}]"))
|
||||||
End Try
|
End Try
|
||||||
Loop While Not _complete
|
Loop While Not _complete
|
||||||
End Sub
|
End Sub
|
||||||
@@ -208,7 +245,7 @@ Namespace API.OnlyFans
|
|||||||
End If
|
End If
|
||||||
If hasMore Then DownloadHighlights(Cursor + 5, Token)
|
If hasMore Then DownloadHighlights(Cursor + 5, Token)
|
||||||
Catch ex As Exception
|
Catch ex As Exception
|
||||||
_complete = Not ProcessException(ex, Token, $"highlights downloading error [{url}]") = 2
|
_complete = ProcessFunctionErrComplete(ProcessException(ex, Token, $"highlights downloading error [{url}]"))
|
||||||
End Try
|
End Try
|
||||||
Loop While Not _complete
|
Loop While Not _complete
|
||||||
End Sub
|
End Sub
|
||||||
@@ -228,7 +265,7 @@ Namespace API.OnlyFans
|
|||||||
If Not r.IsEmptyString Then
|
If Not r.IsEmptyString Then
|
||||||
Using j As EContainer = JsonDocument.Parse(r)
|
Using j As EContainer = JsonDocument.Parse(r)
|
||||||
If j.ListExists Then
|
If j.ListExists Then
|
||||||
specFolder = j.Value("title").StringRemoveWinForbiddenSymbols.IfNullOrEmpty(HLID)
|
specFolder = "Highlights\" & j.Value("title").StringRemoveWinForbiddenSymbols.IfNullOrEmpty(HLID)
|
||||||
specFolder &= "*"
|
specFolder &= "*"
|
||||||
With j("stories")
|
With j("stories")
|
||||||
If .ListExists Then
|
If .ListExists Then
|
||||||
@@ -253,7 +290,58 @@ Namespace API.OnlyFans
|
|||||||
End If
|
End If
|
||||||
End If
|
End If
|
||||||
Catch ex As Exception
|
Catch ex As Exception
|
||||||
_complete = Not ProcessException(ex, Token, $"highlights downloading error [{url}]") = 2
|
_complete = ProcessFunctionErrComplete(ProcessException(ex, Token, $"highlights downloading error [{url}]"))
|
||||||
|
End Try
|
||||||
|
Loop While Not _complete
|
||||||
|
End Sub
|
||||||
|
#End Region
|
||||||
|
#Region "Download stories"
|
||||||
|
Private Sub DownloadStories(ByVal Token As CancellationToken)
|
||||||
|
Dim url$ = String.Empty
|
||||||
|
Dim _complete As Boolean = True
|
||||||
|
Do
|
||||||
|
Try
|
||||||
|
Dim specFolder$ = "Stories"
|
||||||
|
Dim postID$, postDate$
|
||||||
|
Dim media As List(Of UserMedia)
|
||||||
|
Dim result As Boolean
|
||||||
|
Dim path$ = $"/api2/v2/users/{ID}/stories"
|
||||||
|
If UpdateSignature(path) Then
|
||||||
|
url = String.Format(BaseUrlPattern, path)
|
||||||
|
ThrowAny(Token)
|
||||||
|
Dim r$ = Responser.GetResponse(url)
|
||||||
|
If Not r.IsEmptyString Then
|
||||||
|
Using j As EContainer = JsonDocument.Parse(r)
|
||||||
|
If j.ListExists Then
|
||||||
|
ProgressPre.ChangeMax(j.Count)
|
||||||
|
For Each n As EContainer In j
|
||||||
|
ProgressPre.Perform()
|
||||||
|
With n.ItemF({"media", 0})
|
||||||
|
If .ListExists Then
|
||||||
|
postID = $"{A_STORIES}_{ .Value("id")}"
|
||||||
|
postDate = .Value("createdAt")
|
||||||
|
Else
|
||||||
|
postID = String.Empty
|
||||||
|
postDate = String.Empty
|
||||||
|
End If
|
||||||
|
End With
|
||||||
|
If Not postID.IsEmptyString Then
|
||||||
|
If Not _TempPostsList.Contains(postID) Then
|
||||||
|
_TempPostsList.Add(postID)
|
||||||
|
Else
|
||||||
|
Exit Sub
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
result = False
|
||||||
|
media = TryCreateMedia(n, postID, postDate, result, True, specFolder,, False)
|
||||||
|
If result Then _TempMediaList.ListAddList(media, LNC)
|
||||||
|
Next
|
||||||
|
End If
|
||||||
|
End Using
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
Catch ex As Exception
|
||||||
|
_complete = ProcessFunctionErrComplete(ProcessException(ex, Token, $"stories downloading error [{url}]"))
|
||||||
End Try
|
End Try
|
||||||
Loop While Not _complete
|
Loop While Not _complete
|
||||||
End Sub
|
End Sub
|
||||||
@@ -300,15 +388,16 @@ Namespace API.OnlyFans
|
|||||||
End If
|
End If
|
||||||
If hasMore Then DownloadChatMedia(Cursor + 20, Token)
|
If hasMore Then DownloadChatMedia(Cursor + 20, Token)
|
||||||
Catch ex As Exception
|
Catch ex As Exception
|
||||||
_complete = Not ProcessException(ex, Token, $"chats downloading error [{url}]") = 2
|
_complete = ProcessFunctionErrComplete(ProcessException(ex, Token, $"chats downloading error [{url}]"))
|
||||||
End Try
|
End Try
|
||||||
Loop While Not _complete
|
Loop While Not _complete
|
||||||
End Sub
|
End Sub
|
||||||
#End Region
|
#End Region
|
||||||
Private Function TryCreateMedia(ByVal n As EContainer, ByVal PostID As String, Optional ByVal PostDate As String = Nothing,
|
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 ByRef Result As Boolean = False, Optional ByVal IsHL As Boolean = False,
|
||||||
Optional ByVal SpecFolder As String = Nothing) As List(Of UserMedia)
|
Optional ByVal SpecFolder As String = Nothing, Optional ByVal PostUserID As String = Nothing,
|
||||||
Dim postUrl$, ext$
|
Optional ByVal TryUseOFS As Boolean = True) As List(Of UserMedia)
|
||||||
|
Dim postUrl$, postUrlBase$, ext$
|
||||||
Dim t As UTypes
|
Dim t As UTypes
|
||||||
Dim mList As New List(Of UserMedia)
|
Dim mList As New List(Of UserMedia)
|
||||||
Result = False
|
Result = False
|
||||||
@@ -320,16 +409,27 @@ Namespace API.OnlyFans
|
|||||||
Else
|
Else
|
||||||
postUrl = m.Value({"source"}, "source").IfNullOrEmpty(m.Value("full"))
|
postUrl = m.Value({"source"}, "source").IfNullOrEmpty(m.Value("full"))
|
||||||
End If
|
End If
|
||||||
|
postUrlBase = String.Empty
|
||||||
Select Case m.Value("type")
|
Select Case m.Value("type")
|
||||||
Case "photo" : t = UTypes.Picture : ext = "jpg"
|
Case "photo" : t = UTypes.Picture : ext = "jpg"
|
||||||
Case "video" : t = UTypes.Video : ext = "mp4"
|
Case "video"
|
||||||
|
t = UTypes.Video
|
||||||
|
ext = "mp4"
|
||||||
|
If postUrl.IsEmptyString And Not IsHL And TryUseOFS Then
|
||||||
|
t = UTypes.VideoPre
|
||||||
|
_AbsMediaIndex += 1
|
||||||
|
If Not PostUserID.IsEmptyString And IsSingleObjectDownload Then _
|
||||||
|
postUrlBase = String.Format(SiteSettings.UserPostPattern, PostID, $"u{PostUserID}")
|
||||||
|
End If
|
||||||
Case Else : t = UTypes.Undefined : ext = String.Empty
|
Case Else : t = UTypes.Undefined : ext = String.Empty
|
||||||
End Select
|
End Select
|
||||||
If Not t = UTypes.Undefined And Not postUrl.IsEmptyString Then
|
If Not t = UTypes.Undefined And (Not postUrl.IsEmptyString Or t = UTypes.VideoPre) Then
|
||||||
Dim media As New UserMedia(postUrl, t) With {
|
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)),
|
.Post = New UserPost(PostID, AConvert(Of Date)(PostDate, DateProvider, Nothing)),
|
||||||
.SpecialFolder = SpecFolder
|
.SpecialFolder = SpecFolder
|
||||||
}
|
}
|
||||||
|
If postUrlBase.IsEmptyString And Not IsSingleObjectDownload Then postUrlBase = GetPostUrl(Me, media)
|
||||||
|
If Not postUrlBase.IsEmptyString Then media.URL_BASE = postUrlBase
|
||||||
media.File.Extension = ext
|
media.File.Extension = ext
|
||||||
Result = True
|
Result = True
|
||||||
mList.Add(media)
|
mList.Add(media)
|
||||||
@@ -387,7 +487,7 @@ Namespace API.OnlyFans
|
|||||||
End Function
|
End Function
|
||||||
Dim mList As List(Of UserMedia)
|
Dim mList As List(Of UserMedia)
|
||||||
Dim mediaResult As Boolean
|
Dim mediaResult As Boolean
|
||||||
Dim r$, path$, postDate$
|
Dim r$, path$, postDate$, postUserID$
|
||||||
Dim j As EContainer
|
Dim j As EContainer
|
||||||
ProgressPre.ChangeMax(_ContentList.Count)
|
ProgressPre.ChangeMax(_ContentList.Count)
|
||||||
For i% = 0 To _ContentList.Count - 1
|
For i% = 0 To _ContentList.Count - 1
|
||||||
@@ -404,8 +504,9 @@ Namespace API.OnlyFans
|
|||||||
j = JsonDocument.Parse(r)
|
j = JsonDocument.Parse(r)
|
||||||
If Not j Is Nothing Then
|
If Not j Is Nothing Then
|
||||||
postDate = j.Value("postedAt")
|
postDate = j.Value("postedAt")
|
||||||
|
postUserID = j.Value({"author"}, "id")
|
||||||
mediaResult = False
|
mediaResult = False
|
||||||
mList = TryCreateMedia(j, m.Post.ID, postDate, mediaResult)
|
mList = TryCreateMedia(j, m.Post.ID, postDate, mediaResult,,, postUserID)
|
||||||
If mediaResult Then
|
If mediaResult Then
|
||||||
_TempMediaList.ListAddList(mList.ListForEachCopy(stateRefill, True), LNC)
|
_TempMediaList.ListAddList(mList.ListForEachCopy(stateRefill, True), LNC)
|
||||||
rList.Add(i)
|
rList.Add(i)
|
||||||
@@ -531,10 +632,145 @@ Namespace API.OnlyFans
|
|||||||
Return result
|
Return result
|
||||||
End Function
|
End Function
|
||||||
#End Region
|
#End Region
|
||||||
|
#Region "OFScraper support"
|
||||||
|
Private Function OFS_DownloadFile(ByVal URL As String, ByVal Token As CancellationToken) As List(Of SFile)
|
||||||
|
Try
|
||||||
|
Const requestPattern$ = """{0}"" manual --config ""{1}"" --url {2}"
|
||||||
|
Dim conf As SFile = OFS_CreateConfig()
|
||||||
|
If conf.Exists Then
|
||||||
|
Dim command$ = String.Format(requestPattern, MySettings.OFScraperPath.Value, conf, URL)
|
||||||
|
'#If DEBUG Then
|
||||||
|
'Debug.WriteLine(command)
|
||||||
|
'#End If
|
||||||
|
Using b As New TokenBatch(Token) : b.Execute(command) : End Using
|
||||||
|
Return SFile.GetFiles(conf, "*.mp4", IO.SearchOption.AllDirectories, EDP.ReturnValue)
|
||||||
|
End If
|
||||||
|
Return Nothing
|
||||||
|
Catch ex As Exception
|
||||||
|
Return ErrorsDescriber.Execute(EDP.SendToLog, ex, "OnlyFans.UserData.OFS_DownloadFile", Nothing)
|
||||||
|
End Try
|
||||||
|
End Function
|
||||||
|
Private Function OFS_CreateConfig() As SFile
|
||||||
|
Try
|
||||||
|
Const confMainPattern$ = "{0}"": ""([^""]*)"""
|
||||||
|
If OFSCache Is Nothing Then OFSCache = If(IsSingleObjectDownload, Settings.Cache.NewInstance, CreateCache())
|
||||||
|
Dim currentCache As CacheKeeper = OFSCache.NewInstance
|
||||||
|
currentCache.Validate()
|
||||||
|
Dim cacheRoot As SFile = currentCache.NewPath
|
||||||
|
cacheRoot.Exists(SFO.Path, True, EDP.ThrowException)
|
||||||
|
Dim f As SFile = $"{SettingsFolderName}\OFScraperConfigPattern.json"
|
||||||
|
Dim configText$
|
||||||
|
If Not f.Exists Then
|
||||||
|
configText = Text.Encoding.UTF8.GetString(My.Resources.OFResources.OFScraperConfigPattern)
|
||||||
|
TextSaver.SaveTextToFile(configText, f, True)
|
||||||
|
End If
|
||||||
|
If f.Exists Then
|
||||||
|
Dim replaceValue$ = String.Empty
|
||||||
|
Dim rp As RParams = RParams.DMS(String.Empty, 1, RegexReturn.Replace, RegexOptions.IgnoreCase,
|
||||||
|
CType(Function(input) replaceValue, Func(Of String, String)), String.Empty, EDP.ReturnValue)
|
||||||
|
Dim ff As SFile
|
||||||
|
configText = f.GetText
|
||||||
|
Dim updateConf As Action(Of String, String) = Sub(ByVal patternValue As String, ByVal __replaceValue As String)
|
||||||
|
rp.Pattern = String.Format(confMainPattern, patternValue)
|
||||||
|
rp.Nothing = configText
|
||||||
|
replaceValue = __replaceValue
|
||||||
|
configText = RegexReplace(configText, rp)
|
||||||
|
End Sub
|
||||||
|
If Not configText.IsEmptyString Then
|
||||||
|
updateConf("save_location", cacheRoot.PathNoSeparator.Replace("\", "/"))
|
||||||
|
If ACheck(MySettings.OFScraperMP4decrypt.Value) Then
|
||||||
|
ff = CStr(MySettings.OFScraperMP4decrypt.Value)
|
||||||
|
If ff.Exists Then updateConf("mp4decrypt", ff.ToString.Replace("\", "/"))
|
||||||
|
End If
|
||||||
|
If Settings.FfmpegFile.Exists Then updateConf("ffmpeg", Settings.FfmpegFile.File.ToString.Replace("\", "/"))
|
||||||
|
updateConf("key-mode-default", CStr(MySettings.KeyModeDefault.Value).IfNullOrEmpty(SiteSettings.KeyModeDefault_Default))
|
||||||
|
f = currentCache
|
||||||
|
f.Name = "config"
|
||||||
|
f.Extension = "json"
|
||||||
|
If TextSaver.SaveTextToFile(configText, f, True).Exists AndAlso OFS_CreateAuth(currentCache) Then Return f
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
Return Nothing
|
||||||
|
Catch ex As Exception
|
||||||
|
Return ErrorsDescriber.Execute(EDP.SendToLog, ex, "OnlyFans.UserData.OFS_CreateConfig", Nothing)
|
||||||
|
End Try
|
||||||
|
End Function
|
||||||
|
Private Function OFS_CreateAuth(ByVal DestinationPath As SFile) As Boolean
|
||||||
|
Const authText$ = """user_agent"": ""{0}"",""app-token"": ""{1}"",""x-bc"": ""{2}"",""auth_id"": ""{3}"",""sess"": ""{4}"",""auth_uid_"": ""{3}"",""cookie"": ""{5}"""
|
||||||
|
Try
|
||||||
|
Dim sess$ = If(If(CCookie, Responser.Cookies).FirstOrDefault(Function(c) c.Name.StringToLower = "sess")?.Value, String.Empty)
|
||||||
|
Dim outText$ = "{""auth"":{" &
|
||||||
|
String.Format(authText,
|
||||||
|
MySettings.UserAgent.Value,
|
||||||
|
Responser.Headers.Value(SiteSettings.HeaderAppToken),
|
||||||
|
Responser.Headers.Value(SiteSettings.HeaderXBC),
|
||||||
|
MySettings.HH_USER_ID.Value,
|
||||||
|
sess,
|
||||||
|
If(CCookie, Responser.Cookies).ToString()) &
|
||||||
|
"}}"
|
||||||
|
If DestinationPath.Exists(SFO.Path, False) Then
|
||||||
|
Dim f As SFile = $"{DestinationPath.PathWithSeparator}main_profile\auth.json"
|
||||||
|
Return TextSaver.SaveTextToFile(outText, f, True).Exists
|
||||||
|
End If
|
||||||
|
Return False
|
||||||
|
Catch ex As Exception
|
||||||
|
Return ErrorsDescriber.Execute(EDP.SendToLog, ex, "OnlyFans.UserData.OFS_CreateAuth", False)
|
||||||
|
End Try
|
||||||
|
End Function
|
||||||
|
#End Region
|
||||||
#Region "DownloadContent"
|
#Region "DownloadContent"
|
||||||
|
Private OFSPostFiles As Dictionary(Of String, List(Of SFile)) = Nothing
|
||||||
Protected Overrides Sub DownloadContent(ByVal Token As CancellationToken)
|
Protected Overrides Sub DownloadContent(ByVal Token As CancellationToken)
|
||||||
DownloadContentDefault(Token)
|
DownloadContentDefault(Token)
|
||||||
|
OFSCache.DisposeIfReady
|
||||||
|
OFSPostFiles.ListClearDispose
|
||||||
End Sub
|
End Sub
|
||||||
|
Protected Overrides Function ValidateDownloadFile(ByVal URL As String, ByVal Media As UserMedia, ByRef Interrupt As Boolean) As Boolean
|
||||||
|
Return Media.Type = UTypes.VideoPre
|
||||||
|
End Function
|
||||||
|
Protected Overrides Function DownloadFile(ByVal URL As String, ByVal Media As UserMedia, ByVal DestinationFile As SFile,
|
||||||
|
ByVal Token As CancellationToken) As SFile
|
||||||
|
ValidateOFScraper()
|
||||||
|
If _OFScraperExists Then
|
||||||
|
If OFSPostFiles Is Nothing Then OFSPostFiles = New Dictionary(Of String, List(Of SFile))
|
||||||
|
If IsSingleObjectDownload Then
|
||||||
|
URL = Media.URL_BASE
|
||||||
|
Else
|
||||||
|
URL = GetPostUrl(Me, Media)
|
||||||
|
End If
|
||||||
|
If Not URL.IsEmptyString Then
|
||||||
|
Dim f As SFile = Nothing
|
||||||
|
If OFSPostFiles.Count > 0 AndAlso OFSPostFiles.ContainsKey(Media.Post.ID) AndAlso OFSPostFiles(Media.Post.ID).Count > 0 Then
|
||||||
|
f = OFSPostFiles(Media.Post.ID)(0)
|
||||||
|
OFSPostFiles(Media.Post.ID).RemoveAt(0)
|
||||||
|
Else
|
||||||
|
Dim files As List(Of SFile) = OFS_DownloadFile(URL, Token)
|
||||||
|
If files.ListExists Then
|
||||||
|
Dim ff As SFile
|
||||||
|
For i% = files.Count - 1 To 0 Step -1
|
||||||
|
ff = files(i)
|
||||||
|
DestinationFile.Name = ff.Name
|
||||||
|
DestinationFile.Extension = ff.Extension
|
||||||
|
If SFile.Move(ff, DestinationFile,,,, EDP.ThrowException) Then
|
||||||
|
files(i) = DestinationFile
|
||||||
|
Else
|
||||||
|
files.RemoveAt(i)
|
||||||
|
End If
|
||||||
|
Next
|
||||||
|
If files.Count > 0 Then
|
||||||
|
f = files(0)
|
||||||
|
files.RemoveAt(0)
|
||||||
|
If files.Count > 0 Then OFSPostFiles.Add(Media.Post.ID, files)
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
Return f
|
||||||
|
End If
|
||||||
|
Return Nothing
|
||||||
|
Else
|
||||||
|
Throw New InvalidProgramException("OF-Scraper not found")
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
#End Region
|
#End Region
|
||||||
#Region "DownloadingException"
|
#Region "DownloadingException"
|
||||||
Private _DownloadingException_AuthFileUpdate As Boolean = False
|
Private _DownloadingException_AuthFileUpdate As Boolean = False
|
||||||
@@ -546,20 +782,22 @@ Namespace API.OnlyFans
|
|||||||
Return 2
|
Return 2
|
||||||
Else
|
Else
|
||||||
MySettings.SessionAborted = True
|
MySettings.SessionAborted = True
|
||||||
MyMainLOG = $"{ToStringForLog()}: OnlyFans credentials expired"
|
MyMainLOG = $"{ToStringForLog()} [{CInt(Responser.StatusCode)}]: OnlyFans credentials expired"
|
||||||
Return 1
|
Return 1
|
||||||
End If
|
End If
|
||||||
ElseIf Responser.StatusCode = Net.HttpStatusCode.NotFound Then '404
|
ElseIf Responser.StatusCode = Net.HttpStatusCode.NotFound Then '404
|
||||||
UserExists = False
|
UserExists = False
|
||||||
Return 1
|
Return 3
|
||||||
ElseIf Responser.StatusCode = Net.HttpStatusCode.GatewayTimeout Or Responser.StatusCode = 429 Then '504, 429
|
ElseIf Responser.StatusCode = Net.HttpStatusCode.GatewayTimeout Or Responser.StatusCode = 429 Then '504, 429
|
||||||
If Responser.StatusCode = 429 Then MyMainLOG = $"[429] OnlyFans too many requests ({ToStringForLog()})"
|
If Responser.StatusCode = 429 Then MyMainLOG = $"[429] OnlyFans too many requests ({ToStringForLog()})"
|
||||||
MySettings.SessionAborted = True
|
MySettings.SessionAborted = True
|
||||||
Return 1
|
Return 3
|
||||||
ElseIf Responser.StatusCode = Net.HttpStatusCode.Unauthorized Then '401
|
ElseIf Responser.StatusCode = Net.HttpStatusCode.Unauthorized Then '401
|
||||||
MySettings.SessionAborted = True
|
MySettings.SessionAborted = True
|
||||||
MyMainLOG = $"{ToStringForLog()}: OnlyFans credentials expired"
|
MyMainLOG = $"{ToStringForLog()} [{CInt(Responser.StatusCode)}]: OnlyFans credentials expired"
|
||||||
Return 1
|
Return 3
|
||||||
|
ElseIf Responser.StatusCode = Net.HttpStatusCode.InternalServerError Then '500
|
||||||
|
Return 3
|
||||||
Else
|
Else
|
||||||
Return 0
|
Return 0
|
||||||
End If
|
End If
|
||||||
@@ -567,7 +805,13 @@ Namespace API.OnlyFans
|
|||||||
#End Region
|
#End Region
|
||||||
#Region "IDisposable Support"
|
#Region "IDisposable Support"
|
||||||
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
|
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
|
||||||
If Not disposedValue And disposing Then CCookie.DisposeIfReady(False) : CCookie = Nothing : HighlightsList.Clear()
|
If Not disposedValue And disposing Then
|
||||||
|
CCookie.DisposeIfReady(False)
|
||||||
|
CCookie = Nothing
|
||||||
|
HighlightsList.Clear()
|
||||||
|
OFSCache.DisposeIfReady
|
||||||
|
OFSPostFiles.ListClearDispose
|
||||||
|
End If
|
||||||
MyBase.Dispose(disposing)
|
MyBase.Dispose(disposing)
|
||||||
End Sub
|
End Sub
|
||||||
#End Region
|
#End Region
|
||||||
|
|||||||
@@ -9,17 +9,25 @@
|
|||||||
Imports SCrawler.Plugin.Attributes
|
Imports SCrawler.Plugin.Attributes
|
||||||
Namespace API.OnlyFans
|
Namespace API.OnlyFans
|
||||||
Friend Class UserExchangeOptions
|
Friend Class UserExchangeOptions
|
||||||
|
<PSetting(NameOf(SiteSettings.DownloadTimeline), NameOf(MySettings))>
|
||||||
|
Friend Property DownloadTimeline As Boolean
|
||||||
|
<PSetting(NameOf(SiteSettings.DownloadStories), NameOf(MySettings))>
|
||||||
|
Friend Property DownloadStories As Boolean
|
||||||
<PSetting(NameOf(SiteSettings.DownloadHighlights), NameOf(MySettings))>
|
<PSetting(NameOf(SiteSettings.DownloadHighlights), NameOf(MySettings))>
|
||||||
Friend Property DownloadHighlights As Boolean
|
Friend Property DownloadHighlights As Boolean
|
||||||
<PSetting(NameOf(SiteSettings.DownloadChatMedia), NameOf(MySettings))>
|
<PSetting(NameOf(SiteSettings.DownloadChatMedia), NameOf(MySettings))>
|
||||||
Friend Property DownloadChatMedia As Boolean
|
Friend Property DownloadChatMedia As Boolean
|
||||||
Private ReadOnly MySettings As SiteSettings
|
Private ReadOnly MySettings As SiteSettings
|
||||||
Friend Sub New(ByVal u As UserData)
|
Friend Sub New(ByVal u As UserData)
|
||||||
|
DownloadTimeline = u.MediaDownloadTimeline
|
||||||
|
DownloadStories = u.MediaDownloadStories
|
||||||
DownloadHighlights = u.MediaDownloadHighlights
|
DownloadHighlights = u.MediaDownloadHighlights
|
||||||
DownloadChatMedia = u.MediaDownloadChatMedia
|
DownloadChatMedia = u.MediaDownloadChatMedia
|
||||||
MySettings = u.HOST.Source
|
MySettings = u.HOST.Source
|
||||||
End Sub
|
End Sub
|
||||||
Friend Sub New(ByVal s As SiteSettings)
|
Friend Sub New(ByVal s As SiteSettings)
|
||||||
|
DownloadTimeline = s.DownloadTimeline.Value
|
||||||
|
DownloadStories = s.DownloadStories.Value
|
||||||
DownloadHighlights = s.DownloadHighlights.Value
|
DownloadHighlights = s.DownloadHighlights.Value
|
||||||
DownloadChatMedia = s.DownloadChatMedia.Value
|
DownloadChatMedia = s.DownloadChatMedia.Value
|
||||||
MySettings = s
|
MySettings = s
|
||||||
|
|||||||
@@ -287,7 +287,7 @@ Namespace API.Pinterest
|
|||||||
End Function
|
End Function
|
||||||
End Class
|
End Class
|
||||||
Private Function GetDataFromGalleryDL(ByVal URL As String, ByVal IsBoardsRequested As Boolean, ByVal Token As CancellationToken) As List(Of String)
|
Private Function GetDataFromGalleryDL(ByVal URL As String, ByVal IsBoardsRequested As Boolean, ByVal Token As CancellationToken) As List(Of String)
|
||||||
Dim command$ = $"gallery-dl --verbose --simulate "
|
Dim command$ = $"""{Settings.GalleryDLFile.File}"" --verbose --simulate "
|
||||||
Try
|
Try
|
||||||
If Not URL.IsEmptyString Then
|
If Not URL.IsEmptyString Then
|
||||||
If MySettings.CookiesNetscapeFile.Exists Then command &= $"--cookies ""{MySettings.CookiesNetscapeFile}"" "
|
If MySettings.CookiesNetscapeFile.Exists Then command &= $"--cookies ""{MySettings.CookiesNetscapeFile}"" "
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ Namespace API.PornHub
|
|||||||
Private Const Name_DownloadFavorite As String = "DownloadFavorite"
|
Private Const Name_DownloadFavorite As String = "DownloadFavorite"
|
||||||
Private Const Name_DownloadGifs As String = "DownloadGifs"
|
Private Const Name_DownloadGifs As String = "DownloadGifs"
|
||||||
Private Const Name_DownloadPhotoOnlyFromModelHub As String = "DownloadPhotoOnlyFromModelHub"
|
Private Const Name_DownloadPhotoOnlyFromModelHub As String = "DownloadPhotoOnlyFromModelHub"
|
||||||
<Obsolete> Private Const Name_IsUser As String = "IsUser"
|
|
||||||
#End Region
|
#End Region
|
||||||
#Region "Structures"
|
#Region "Structures"
|
||||||
Private Structure FlashVar : Implements IRegExCreator
|
Private Structure FlashVar : Implements IRegExCreator
|
||||||
@@ -254,14 +253,7 @@ Namespace API.PornHub
|
|||||||
DownloadFavorite = .Value(Name_DownloadFavorite).FromXML(Of Boolean)(False)
|
DownloadFavorite = .Value(Name_DownloadFavorite).FromXML(Of Boolean)(False)
|
||||||
DownloadGifs = .Value(Name_DownloadGifs).FromXML(Of Integer)(False)
|
DownloadGifs = .Value(Name_DownloadGifs).FromXML(Of Integer)(False)
|
||||||
DownloadPhotoOnlyFromModelHub = .Value(Name_DownloadPhotoOnlyFromModelHub).FromXML(Of Boolean)(True)
|
DownloadPhotoOnlyFromModelHub = .Value(Name_DownloadPhotoOnlyFromModelHub).FromXML(Of Boolean)(True)
|
||||||
If .Contains(Name_SiteMode) Then
|
SiteMode = .Value(Name_SiteMode).FromXML(Of Integer)(SiteModes.User)
|
||||||
SiteMode = .Value(Name_SiteMode).FromXML(Of Integer)(SiteModes.User)
|
|
||||||
Else
|
|
||||||
'TODELETE: PornHub 'IsUser' 20231113
|
|
||||||
#Disable Warning BC40008
|
|
||||||
SiteMode = IIf(.Value(Name_IsUser).FromXML(Of Boolean)(True), SiteModes.User, SiteModes.Search)
|
|
||||||
#Enable Warning
|
|
||||||
End If
|
|
||||||
UpdateUserOptions()
|
UpdateUserOptions()
|
||||||
Else
|
Else
|
||||||
If UpdateUserOptions() Then .Value(Name_LabelsName) = LabelsString
|
If UpdateUserOptions() Then .Value(Name_LabelsName) = LabelsString
|
||||||
@@ -404,7 +396,8 @@ Namespace API.PornHub
|
|||||||
Dim r$ = Responser.GetResponse(URL)
|
Dim r$ = Responser.GetResponse(URL)
|
||||||
If Not r.IsEmptyString Then
|
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, 10})
|
||||||
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(3, l.Count).ToList
|
||||||
|
If l.ListExists And Not SiteMode = SiteModes.Playlists Then l = l.ListTake(1, l.Count).ToList
|
||||||
If l.ListExists Then
|
If l.ListExists Then
|
||||||
If IsUser Then
|
If IsUser Then
|
||||||
If Type = VideoTypes.Favorite Then
|
If Type = VideoTypes.Favorite Then
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ Imports SCrawler.API.Base
|
|||||||
Imports SCrawler.Plugin
|
Imports SCrawler.Plugin
|
||||||
Imports SCrawler.Plugin.Attributes
|
Imports SCrawler.Plugin.Attributes
|
||||||
Imports PersonalUtilities.Tools.Web.Clients
|
Imports PersonalUtilities.Tools.Web.Clients
|
||||||
|
Imports PersonalUtilities.Tools.Web.Clients.Base
|
||||||
Imports PersonalUtilities.Tools.Web.Documents.JSON
|
Imports PersonalUtilities.Tools.Web.Documents.JSON
|
||||||
Imports PersonalUtilities.Functions.XML
|
Imports PersonalUtilities.Functions.XML
|
||||||
Imports PersonalUtilities.Functions.RegularExpressions
|
Imports PersonalUtilities.Functions.RegularExpressions
|
||||||
@@ -34,6 +35,8 @@ Namespace API.Reddit
|
|||||||
"You can find different tokens in the responses. Make sure that bearer token belongs to Reddit and not RedGifs." & vbCr &
|
"You can find different tokens in the responses. Make sure that bearer token belongs to Reddit and not RedGifs." & vbCr &
|
||||||
"There is not need to add a token if you are not using cookies to download the timeline.", IsAuth:=True)>
|
"There is not need to add a token if you are not using cookies to download the timeline.", IsAuth:=True)>
|
||||||
Friend ReadOnly Property BearerToken As PropertyValue
|
Friend ReadOnly Property BearerToken As PropertyValue
|
||||||
|
<PropertyOption(ControlText:="Use 'cUrl' to get a token", IsAuth:=True), PXML, PClonable, HiddenControl>
|
||||||
|
Private ReadOnly Property BearerTokenUseCurl As PropertyValue
|
||||||
#Region "TokenUpdateInterval"
|
#Region "TokenUpdateInterval"
|
||||||
<PropertyOption(ControlText:="Token refresh interval", ControlToolTip:="Interval (in minutes) to refresh the token",
|
<PropertyOption(ControlText:="Token refresh interval", ControlToolTip:="Interval (in minutes) to refresh the token",
|
||||||
AllowNull:=False, LeftOffset:=120, IsAuth:=True), PXML, PClonable>
|
AllowNull:=False, LeftOffset:=120, IsAuth:=True), PXML, PClonable>
|
||||||
@@ -60,6 +63,10 @@ Namespace API.Reddit
|
|||||||
#Region "Other"
|
#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", IsAuth:=False), PXML, PClonable>
|
||||||
Friend ReadOnly Property UseM3U8 As PropertyValue
|
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>
|
||||||
|
Friend ReadOnly Property CheckImage As PropertyValue
|
||||||
|
<PropertyOption(ControlText:="Check image: get original", ControlToolTip:="Get the original image if it exists", IsAuth:=False), PXML, PClonable>
|
||||||
|
Friend ReadOnly Property CheckImageReturnOrig As PropertyValue
|
||||||
#End Region
|
#End Region
|
||||||
#End Region
|
#End Region
|
||||||
#Region "Initializer"
|
#Region "Initializer"
|
||||||
@@ -78,6 +85,7 @@ Namespace API.Reddit
|
|||||||
ApiClientID = New PropertyValue(String.Empty, GetType(String))
|
ApiClientID = New PropertyValue(String.Empty, GetType(String))
|
||||||
ApiClientSecret = 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))
|
BearerToken = New PropertyValue(token, GetType(String), Sub(v) Responser.Headers.Add(DeclaredNames.Header_Authorization, v))
|
||||||
|
BearerTokenUseCurl = New PropertyValue(True)
|
||||||
TokenUpdateInterval = New PropertyValue(60 * 12)
|
TokenUpdateInterval = New PropertyValue(60 * 12)
|
||||||
TokenUpdateIntervalProvider = New TokenRefreshIntervalProvider
|
TokenUpdateIntervalProvider = New TokenRefreshIntervalProvider
|
||||||
BearerTokenDateUpdate = New PropertyValue(Now.AddYears(-1))
|
BearerTokenDateUpdate = New PropertyValue(Now.AddYears(-1))
|
||||||
@@ -87,10 +95,12 @@ Namespace API.Reddit
|
|||||||
SavedPostsUserName = New PropertyValue(String.Empty, GetType(String))
|
SavedPostsUserName = New PropertyValue(String.Empty, GetType(String))
|
||||||
|
|
||||||
UseM3U8 = New PropertyValue(True)
|
UseM3U8 = New PropertyValue(True)
|
||||||
|
CheckImage = New PropertyValue(False)
|
||||||
|
CheckImageReturnOrig = New PropertyValue(True)
|
||||||
|
|
||||||
UrlPatternUser = "https://www.reddit.com/{0}/{1}/"
|
UrlPatternUser = "https://www.reddit.com/{0}/{1}/"
|
||||||
ImageVideoContains = "reddit.com"
|
ImageVideoContains = "reddit.com"
|
||||||
UserRegex = RParams.DM("[htps:/]{7,8}.*?reddit.com/([user]{1,4})/([^/]+)", 0, RegexReturn.ListByMatch, EDP.ReturnValue)
|
UserRegex = RParams.DM("[htps:/]{7,8}.*?reddit.com/([user]{1,4})/([^/\?&]+)", 0, RegexReturn.ListByMatch, EDP.ReturnValue)
|
||||||
End Sub
|
End Sub
|
||||||
#End Region
|
#End Region
|
||||||
#Region "GetInstance"
|
#Region "GetInstance"
|
||||||
@@ -263,29 +273,51 @@ Namespace API.Reddit
|
|||||||
result = False
|
result = False
|
||||||
Dim r$ = String.Empty
|
Dim r$ = String.Empty
|
||||||
Dim c% = 0
|
Dim c% = 0
|
||||||
Dim _found As Boolean
|
Dim useCurl As Boolean = Settings.CurlFile.Exists And CBool(BearerTokenUseCurl.Value)
|
||||||
|
Dim curlUsed As Boolean = useCurl
|
||||||
Do
|
Do
|
||||||
c += 1
|
c += 1
|
||||||
Using resp As New Responser With {
|
Using resp As New Responser With {
|
||||||
.Method = "POST",
|
.Method = "POST",
|
||||||
.ProcessExceptionDecision = Function(status, obj, ee) If(status.StatusCode = 429, EDP.ReturnValue, ee)
|
.ProcessExceptionDecision = Function(ByVal status As IResponserStatus, ByVal nullArg As Object, ByVal currErr As ErrorsDescriber) As ErrorsDescriber
|
||||||
|
If status.StatusCode = 429 Then
|
||||||
|
useCurl = False
|
||||||
|
Return EDP.ReturnValue
|
||||||
|
ElseIf status.StatusCode = Net.HttpStatusCode.Forbidden And Not useCurl And Settings.CurlFile.Exists Then
|
||||||
|
useCurl = True
|
||||||
|
Return EDP.ReturnValue
|
||||||
|
Else
|
||||||
|
Return currErr
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
}
|
}
|
||||||
With resp
|
With resp
|
||||||
With .PayLoadValues
|
If useCurl Then
|
||||||
.Add("grant_type", "password")
|
If Settings.CurlFile.Exists Then
|
||||||
.Add("username", UserName)
|
curlUsed = True
|
||||||
.Add("password", Password)
|
.Mode = Responser.Modes.Curl
|
||||||
End With
|
.CurlPath = Settings.CurlFile
|
||||||
.CredentialsUserName = ClientID
|
.CurlArgumentsLeft = $"-d ""grant_type=password&username={UserName}&password={Password}"" --user ""{ClientID}:{ClientSecret}"""
|
||||||
.CredentialsPassword = ClientSecret
|
Else
|
||||||
.PreAuthenticate = True
|
Throw New ArgumentNullException("cUrl file", "The path to the cUrl file is not specified")
|
||||||
|
End If
|
||||||
|
Else
|
||||||
|
.Mode = Responser.Modes.Default
|
||||||
|
With .PayLoadValues
|
||||||
|
.Add("grant_type", "password")
|
||||||
|
.Add("username", UserName)
|
||||||
|
.Add("password", Password)
|
||||||
|
End With
|
||||||
|
.CredentialsUserName = ClientID
|
||||||
|
.CredentialsPassword = ClientSecret
|
||||||
|
.PreAuthenticate = True
|
||||||
|
End If
|
||||||
End With
|
End With
|
||||||
r = resp.GetResponse("https://www.reddit.com/api/v1/access_token",, EDP.ThrowException)
|
r = resp.GetResponse("https://www.reddit.com/api/v1/access_token",, EDP.ThrowException)
|
||||||
End Using
|
End Using
|
||||||
If Not r.IsEmptyString Then
|
If Not r.IsEmptyString Then
|
||||||
Using j As EContainer = JsonDocument.Parse(r)
|
Using j As EContainer = JsonDocument.Parse(r)
|
||||||
If j.ListExists Then
|
If j.ListExists Then
|
||||||
_found = True
|
|
||||||
Dim newToken$ = j.Value("access_token")
|
Dim newToken$ = j.Value("access_token")
|
||||||
If Not newToken.IsEmptyString Then
|
If Not newToken.IsEmptyString Then
|
||||||
BearerToken.Value = $"Bearer {newToken}"
|
BearerToken.Value = $"Bearer {newToken}"
|
||||||
@@ -296,7 +328,7 @@ Namespace API.Reddit
|
|||||||
End If
|
End If
|
||||||
End Using
|
End Using
|
||||||
End If
|
End If
|
||||||
Loop While c < 5 And Not _found
|
Loop While c < 5 And Not result
|
||||||
End If
|
End If
|
||||||
Return result
|
Return result
|
||||||
Catch ex As Exception
|
Catch ex As Exception
|
||||||
|
|||||||
@@ -681,12 +681,16 @@ Namespace API.Reddit
|
|||||||
End Function
|
End Function
|
||||||
Private Function TryImage(ByVal URL As String) As Boolean
|
Private Function TryImage(ByVal URL As String) As Boolean
|
||||||
Try
|
Try
|
||||||
Dim img As Image = GetImage(SFile.GetBytesFromNet(URL, EDP.ThrowException), EDP.ThrowException)
|
If Not CBool(MySiteSettings.CheckImage.Value) Then
|
||||||
If Not img Is Nothing Then
|
Return MySiteSettings.CheckImageReturnOrig.Value
|
||||||
img.Dispose()
|
|
||||||
Return True
|
|
||||||
Else
|
Else
|
||||||
Return False
|
Dim img As Image = GetImage(SFile.GetBytesFromNet(URL, EDP.ThrowException), EDP.ThrowException)
|
||||||
|
If Not img Is Nothing Then
|
||||||
|
img.Dispose()
|
||||||
|
Return True
|
||||||
|
Else
|
||||||
|
Return False
|
||||||
|
End If
|
||||||
End If
|
End If
|
||||||
Catch
|
Catch
|
||||||
Return False
|
Return False
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ Namespace API.RedGifs
|
|||||||
TokenUpdateIntervalProvider = New TokenRefreshIntervalProvider
|
TokenUpdateIntervalProvider = New TokenRefreshIntervalProvider
|
||||||
_AllowUserAgentUpdate = False
|
_AllowUserAgentUpdate = False
|
||||||
UrlPatternUser = "https://www.redgifs.com/users/{0}/"
|
UrlPatternUser = "https://www.redgifs.com/users/{0}/"
|
||||||
UserRegex = RParams.DMS("[htps:/]{7,8}.*?redgifs.com/users/([^/]+)", 1)
|
UserRegex = RParams.DMS(String.Format(UserRegexDefaultPattern, "redgifs.com/users/"), 1)
|
||||||
ImageVideoContains = "redgifs"
|
ImageVideoContains = "redgifs"
|
||||||
End Sub
|
End Sub
|
||||||
#End Region
|
#End Region
|
||||||
|
|||||||
@@ -55,8 +55,7 @@ Namespace API.ThisVid
|
|||||||
#End Region
|
#End Region
|
||||||
#Region "UpdateCookies"
|
#Region "UpdateCookies"
|
||||||
Friend Sub UpdateCookies(ByVal Source As Responser)
|
Friend Sub UpdateCookies(ByVal Source As Responser)
|
||||||
Responser.Cookies.Clear()
|
Responser.Cookies.Update(Source.Cookies)
|
||||||
Responser.Cookies.AddRange(Source.Cookies)
|
|
||||||
Update_SaveCookiesNetscape(True)
|
Update_SaveCookiesNetscape(True)
|
||||||
End Sub
|
End Sub
|
||||||
#End Region
|
#End Region
|
||||||
|
|||||||
@@ -178,6 +178,7 @@ Namespace API.ThisVid
|
|||||||
Friend Sub New()
|
Friend Sub New()
|
||||||
UseClientTokens = True
|
UseClientTokens = True
|
||||||
SessionPosts = New List(Of String)
|
SessionPosts = New List(Of String)
|
||||||
|
_ResponserAutoUpdateCookies = True
|
||||||
End Sub
|
End Sub
|
||||||
#End Region
|
#End Region
|
||||||
#Region "Validation"
|
#Region "Validation"
|
||||||
@@ -225,31 +226,34 @@ Namespace API.ThisVid
|
|||||||
Private AddedCount As Integer = 0
|
Private AddedCount As Integer = 0
|
||||||
Private _PageVideosRepeat As Integer = 0
|
Private _PageVideosRepeat As Integer = 0
|
||||||
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
|
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
|
||||||
SessionPosts.Clear()
|
Try
|
||||||
AddedCount = 0
|
SessionPosts.Clear()
|
||||||
_PageVideosRepeat = 0
|
AddedCount = 0
|
||||||
SessionPosts.Clear()
|
_PageVideosRepeat = 0
|
||||||
Responser.Cookies.ChangedAllowInternalDrop = False
|
SessionPosts.Clear()
|
||||||
Responser.Cookies.Changed = False
|
Responser.Cookies.ChangedAllowInternalDrop = False
|
||||||
If ID.IsEmptyString Then ID = Name
|
Responser.Cookies.Changed = False
|
||||||
If Not IsUser OrElse IsValid() Then
|
If ID.IsEmptyString Then ID = Name
|
||||||
If IsSavedPosts Then
|
If Not IsUser OrElse IsValid() Then
|
||||||
DownloadData(1, 0, Token)
|
If IsSavedPosts Then
|
||||||
DownloadData_Images(Token)
|
|
||||||
Else
|
|
||||||
If IsUser Then
|
|
||||||
If DownloadVideos Then
|
|
||||||
If DownloadPublic Then DownloadData(1, 0, Token)
|
|
||||||
If DownloadPrivate Then DownloadData(1, 1, Token)
|
|
||||||
If DownloadFavourite Then DownloadData(1, 2, Token)
|
|
||||||
End If
|
|
||||||
If DownloadImages And Not IsSubscription Then DownloadData_Images(Token)
|
|
||||||
Else
|
|
||||||
DownloadData(1, 0, Token)
|
DownloadData(1, 0, Token)
|
||||||
|
DownloadData_Images(Token)
|
||||||
|
Else
|
||||||
|
If IsUser Then
|
||||||
|
If DownloadVideos Then
|
||||||
|
If DownloadPublic Then DownloadData(1, 0, Token)
|
||||||
|
If DownloadPrivate Then DownloadData(1, 1, Token)
|
||||||
|
If DownloadFavourite Then DownloadData(1, 2, Token)
|
||||||
|
End If
|
||||||
|
If DownloadImages And Not IsSubscription Then DownloadData_Images(Token)
|
||||||
|
Else
|
||||||
|
DownloadData(1, 0, Token)
|
||||||
|
End If
|
||||||
End If
|
End If
|
||||||
End If
|
End If
|
||||||
End If
|
Finally
|
||||||
If Responser.Cookies.Changed Then MySettings.UpdateCookies(Responser) : Responser.Cookies.Changed = False
|
If Responser.Cookies.Changed Then MySettings.UpdateCookies(Responser) : Responser.Cookies.Changed = False
|
||||||
|
End Try
|
||||||
End Sub
|
End Sub
|
||||||
Friend Function GetNonUserUrl(ByVal Page As Integer) As String
|
Friend Function GetNonUserUrl(ByVal Page As Integer) As String
|
||||||
Dim url$ = String.Empty
|
Dim url$ = String.Empty
|
||||||
|
|||||||
@@ -19,12 +19,16 @@ Namespace API.ThreadsNet
|
|||||||
#Region "Declarations"
|
#Region "Declarations"
|
||||||
#Region "Authorization"
|
#Region "Authorization"
|
||||||
<PClonable(Clone:=False)> Protected ReadOnly __HH_CSRF_TOKEN As PropertyValue
|
<PClonable(Clone:=False)> Protected ReadOnly __HH_CSRF_TOKEN As PropertyValue
|
||||||
<PropertyOption(ControlText:="x-csrftoken", AllowNull:=False, IsAuth:=True), ControlNumber(0)>
|
<PropertyOption(ControlText:="x-csrftoken", AllowNull:=True, IsAuth:=True), ControlNumber(0)>
|
||||||
Friend Overridable ReadOnly Property HH_CSRF_TOKEN As PropertyValue
|
Friend Overridable ReadOnly Property HH_CSRF_TOKEN As PropertyValue
|
||||||
Get
|
Get
|
||||||
Return __HH_CSRF_TOKEN
|
Return __HH_CSRF_TOKEN
|
||||||
End Get
|
End Get
|
||||||
End Property
|
End Property
|
||||||
|
<CookieValueExtractor(NameOf(HH_CSRF_TOKEN))>
|
||||||
|
Private Function GetValueFromCookies(ByVal PropName As String, ByVal c As CookieKeeper) As String
|
||||||
|
Return c.GetCookieValue(IG.Header_CSRF_TOKEN_COOKIE, PropName, NameOf(HH_CSRF_TOKEN))
|
||||||
|
End Function
|
||||||
<PClonable> Protected ReadOnly __HH_IG_APP_ID As PropertyValue
|
<PClonable> Protected ReadOnly __HH_IG_APP_ID As PropertyValue
|
||||||
<PropertyOption(ControlText:="x-ig-app-id", AllowNull:=False, IsAuth:=True), ControlNumber(10)>
|
<PropertyOption(ControlText:="x-ig-app-id", AllowNull:=False, IsAuth:=True), ControlNumber(10)>
|
||||||
Friend Overridable ReadOnly Property HH_IG_APP_ID As PropertyValue
|
Friend Overridable ReadOnly Property HH_IG_APP_ID As PropertyValue
|
||||||
@@ -34,13 +38,17 @@ Namespace API.ThreadsNet
|
|||||||
End Property
|
End Property
|
||||||
<PropertyOption(ControlText:="x-asbd-id", AllowNull:=True, IsAuth:=True), ControlNumber(20), PClonable>
|
<PropertyOption(ControlText:="x-asbd-id", AllowNull:=True, IsAuth:=True), ControlNumber(20), PClonable>
|
||||||
Friend ReadOnly Property HH_ASBD_ID As PropertyValue
|
Friend ReadOnly Property HH_ASBD_ID As PropertyValue
|
||||||
<PropertyOption(ControlText:="sec-ch-ua", AllowNull:=True, IsAuth:=True), ControlNumber(30), PClonable>
|
<PropertyOption(ControlText:="sec-ch-ua", AllowNull:=True, IsAuth:=True,
|
||||||
Private ReadOnly Property HH_BROWSER As PropertyValue
|
InheritanceName:=SettingsCLS.HEADER_DEF_sec_ch_ua), ControlNumber(30), PClonable, PXML(OnlyForChecked:=True)>
|
||||||
<PropertyOption(ControlText:="sec-ch-ua-full", ControlToolTip:="sec-ch-ua-full-version-list", AllowNull:=True, IsAuth:=True), ControlNumber(40), PClonable>
|
Friend ReadOnly Property HH_BROWSER As PropertyValue
|
||||||
Private ReadOnly Property HH_BROWSER_EXT As PropertyValue
|
<PropertyOption(ControlText:="sec-ch-ua-full", ControlToolTip:=SettingsCLS.HEADER_DEF_sec_ch_ua_full_version_list, AllowNull:=True, IsAuth:=True,
|
||||||
<PropertyOption(ControlText:="sec-ch-ua-platform", ControlToolTip:="sec-ch-ua-platform", AllowNull:=True, IsAuth:=True, LeftOffset:=120), ControlNumber(50), PClonable>
|
InheritanceName:=SettingsCLS.HEADER_DEF_sec_ch_ua_full_version_list), ControlNumber(40), PClonable, PXML(OnlyForChecked:=True)>
|
||||||
Private ReadOnly Property HH_PLATFORM As PropertyValue
|
Friend ReadOnly Property HH_BROWSER_EXT As PropertyValue
|
||||||
<PropertyOption(ControlText:="UserAgent", IsAuth:=True), ControlNumber(60), PClonable>
|
<PropertyOption(ControlText:="sec-ch-ua-platform-ver", ControlToolTip:=SettingsCLS.HEADER_DEF_sec_ch_ua_platform_version, AllowNull:=True, IsAuth:=True, LeftOffset:=135,
|
||||||
|
InheritanceName:=SettingsCLS.HEADER_DEF_sec_ch_ua_platform_version), ControlNumber(50), PClonable, PXML(OnlyForChecked:=True)>
|
||||||
|
Friend ReadOnly Property HH_PLATFORM_VER As PropertyValue
|
||||||
|
<PropertyOption(ControlText:="UserAgent", IsAuth:=True,
|
||||||
|
InheritanceName:=SettingsCLS.HEADER_DEF_UserAgent), ControlNumber(60), PClonable, PXML(OnlyForChecked:=True)>
|
||||||
Private ReadOnly Property HH_USER_AGENT As PropertyValue
|
Private ReadOnly Property HH_USER_AGENT As PropertyValue
|
||||||
Private Sub ChangeResponserFields(ByVal PropName As String, ByVal Value As Object)
|
Private Sub ChangeResponserFields(ByVal PropName As String, ByVal Value As Object)
|
||||||
If Not PropName.IsEmptyString Then
|
If Not PropName.IsEmptyString Then
|
||||||
@@ -52,7 +60,7 @@ Namespace API.ThreadsNet
|
|||||||
Case NameOf(HH_CSRF_TOKEN) : f = IG.Header_CSRF_TOKEN
|
Case NameOf(HH_CSRF_TOKEN) : f = IG.Header_CSRF_TOKEN
|
||||||
Case NameOf(HH_BROWSER) : f = IG.Header_Browser
|
Case NameOf(HH_BROWSER) : f = IG.Header_Browser
|
||||||
Case NameOf(HH_BROWSER_EXT) : f = IG.Header_BrowserExt
|
Case NameOf(HH_BROWSER_EXT) : f = IG.Header_BrowserExt
|
||||||
Case NameOf(HH_PLATFORM) : f = IG.Header_Platform
|
Case NameOf(HH_PLATFORM_VER) : f = IG.Header_Platform_Verion
|
||||||
Case NameOf(HH_USER_AGENT) : isUserAgent = True
|
Case NameOf(HH_USER_AGENT) : isUserAgent = True
|
||||||
End Select
|
End Select
|
||||||
If Not f.IsEmptyString Then
|
If Not f.IsEmptyString Then
|
||||||
@@ -64,6 +72,19 @@ Namespace API.ThreadsNet
|
|||||||
End If
|
End If
|
||||||
End Sub
|
End Sub
|
||||||
#End Region
|
#End Region
|
||||||
|
#Region "Other properties"
|
||||||
|
<PropertyOption(ControlText:="Request timer (any)",
|
||||||
|
ControlToolTip:="The timer (in milliseconds) that SCrawler should wait before executing the next request." &
|
||||||
|
vbCr & "The default value is 1'000." & vbCr & "The minimum value is 0." & IG.TimersUrgentTip, AllowNull:=False),
|
||||||
|
PXML, PClonable>
|
||||||
|
Friend ReadOnly Property RequestsWaitTimer_Any As PropertyValue
|
||||||
|
<Provider(NameOf(RequestsWaitTimer_Any), FieldsChecker:=True)>
|
||||||
|
Private ReadOnly Property RequestsWaitTimer_AnyProvider As IFormatProvider
|
||||||
|
<PropertyOption(ControlText:="Download data",
|
||||||
|
ControlToolTip:="The internal value indicates that site data should be downloaded." & vbCr &
|
||||||
|
"It becomes unchecked when the site returns an error."), PXML>
|
||||||
|
Friend ReadOnly Property DownloadData_Impl As PropertyValue
|
||||||
|
#End Region
|
||||||
#End Region
|
#End Region
|
||||||
#Region "Initializer"
|
#Region "Initializer"
|
||||||
Friend Sub New(ByVal AccName As String, ByVal Temp As Boolean)
|
Friend Sub New(ByVal AccName As String, ByVal Temp As Boolean)
|
||||||
@@ -86,8 +107,6 @@ Namespace API.ThreadsNet
|
|||||||
|
|
||||||
With Responser
|
With Responser
|
||||||
.Accept = "*/*"
|
.Accept = "*/*"
|
||||||
'URGENT: remove after debug
|
|
||||||
.DeclaredError = EDP.SendToLog + EDP.ThrowException
|
|
||||||
If .UserAgentExists Then useragent = .UserAgent
|
If .UserAgentExists Then useragent = .UserAgent
|
||||||
With .Headers
|
With .Headers
|
||||||
If .Count > 0 Then
|
If .Count > 0 Then
|
||||||
@@ -96,7 +115,7 @@ Namespace API.ThreadsNet
|
|||||||
asbd = .Value(IG.Header_ASBD_ID)
|
asbd = .Value(IG.Header_ASBD_ID)
|
||||||
browser = .Value(IG.Header_Browser)
|
browser = .Value(IG.Header_Browser)
|
||||||
browserExt = .Value(IG.Header_BrowserExt)
|
browserExt = .Value(IG.Header_BrowserExt)
|
||||||
platform = .Value(IG.Header_Platform)
|
platform = .Value(IG.Header_Platform_Verion)
|
||||||
End If
|
End If
|
||||||
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.Authority, "www.threads.net"))
|
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.Authority, "www.threads.net"))
|
||||||
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.Origin, "https://www.threads.net"))
|
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.Origin, "https://www.threads.net"))
|
||||||
@@ -108,7 +127,10 @@ Namespace API.ThreadsNet
|
|||||||
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecFetchMode, "cors"))
|
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecFetchMode, "cors"))
|
||||||
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecFetchSite, "same-origin"))
|
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecFetchSite, "same-origin"))
|
||||||
.Add("Sec-Fetch-User", "?1")
|
.Add("Sec-Fetch-User", "?1")
|
||||||
.Add(DeclaredNames.Header_FB_FRIENDLY_NAME, "BarcelonaProfileThreadsTabRefetchableQuery")
|
.Add("dnt", 1)
|
||||||
|
.Add("drp", 1)
|
||||||
|
.Add(Instagram.UserData.GQL_HEADER_FB_FRINDLY_NAME, "BarcelonaProfileThreadsTabRefetchableQuery")
|
||||||
|
.Remove("dht")
|
||||||
End With
|
End With
|
||||||
.CookiesExtractMode = Responser.CookiesExtractModes.Any
|
.CookiesExtractMode = Responser.CookiesExtractModes.Any
|
||||||
.CookiesUpdateMode = CookieKeeper.UpdateModes.ReplaceByNameAll
|
.CookiesUpdateMode = CookieKeeper.UpdateModes.ReplaceByNameAll
|
||||||
@@ -122,11 +144,15 @@ Namespace API.ThreadsNet
|
|||||||
HH_ASBD_ID = New PropertyValue(asbd, GetType(String), Sub(v) ChangeResponserFields(NameOf(HH_ASBD_ID), v))
|
HH_ASBD_ID = New PropertyValue(asbd, GetType(String), Sub(v) ChangeResponserFields(NameOf(HH_ASBD_ID), v))
|
||||||
HH_BROWSER = New PropertyValue(browser, GetType(String), Sub(v) ChangeResponserFields(NameOf(HH_BROWSER), v))
|
HH_BROWSER = New PropertyValue(browser, GetType(String), Sub(v) ChangeResponserFields(NameOf(HH_BROWSER), v))
|
||||||
HH_BROWSER_EXT = New PropertyValue(browserExt, GetType(String), Sub(v) ChangeResponserFields(NameOf(HH_BROWSER_EXT), v))
|
HH_BROWSER_EXT = New PropertyValue(browserExt, GetType(String), Sub(v) ChangeResponserFields(NameOf(HH_BROWSER_EXT), v))
|
||||||
HH_PLATFORM = New PropertyValue(platform, GetType(String), Sub(v) ChangeResponserFields(NameOf(HH_PLATFORM), v))
|
HH_PLATFORM_VER = New PropertyValue(platform, GetType(String), Sub(v) ChangeResponserFields(NameOf(HH_PLATFORM_VER), v))
|
||||||
HH_USER_AGENT = New PropertyValue(useragent, GetType(String), Sub(v) ChangeResponserFields(NameOf(HH_USER_AGENT), v))
|
HH_USER_AGENT = New PropertyValue(useragent, GetType(String), Sub(v) ChangeResponserFields(NameOf(HH_USER_AGENT), v))
|
||||||
|
|
||||||
|
RequestsWaitTimer_Any = New PropertyValue(1000)
|
||||||
|
RequestsWaitTimer_AnyProvider = New IG.TimersChecker(0)
|
||||||
|
DownloadData_Impl = New PropertyValue(True)
|
||||||
|
|
||||||
UrlPatternUser = "https://www.threads.net/@{0}"
|
UrlPatternUser = "https://www.threads.net/@{0}"
|
||||||
UserRegex = RParams.DMS("threads.net/@([^/\?&]+)", 1)
|
UserRegex = RParams.DMS(String.Format(UserRegexDefaultPattern, "threads.net/@"), 1)
|
||||||
ImageVideoContains = "threads.net"
|
ImageVideoContains = "threads.net"
|
||||||
End Sub
|
End Sub
|
||||||
#End Region
|
#End Region
|
||||||
@@ -151,7 +177,7 @@ Namespace API.ThreadsNet
|
|||||||
#End Region
|
#End Region
|
||||||
#Region "BaseAuthExists, GetUserUrl, GetUserPostUrl"
|
#Region "BaseAuthExists, GetUserUrl, GetUserPostUrl"
|
||||||
Friend Overrides Function BaseAuthExists() As Boolean
|
Friend Overrides Function BaseAuthExists() As Boolean
|
||||||
Return Responser.CookiesExists And {HH_CSRF_TOKEN, HH_IG_APP_ID}.All(Function(v) ACheck(Of String)(v.Value))
|
Return Responser.CookiesExists And {HH_CSRF_TOKEN, HH_IG_APP_ID}.All(Function(v) ACheck(Of String)(v.Value)) And CBool(DownloadData_Impl.Value)
|
||||||
End Function
|
End Function
|
||||||
Friend Overrides Function GetUserUrl(ByVal User As IPluginContentProvider) As String
|
Friend Overrides Function GetUserUrl(ByVal User As IPluginContentProvider) As String
|
||||||
Return String.Format(UrlPatternUser, DirectCast(User, UserData).NameTrue)
|
Return String.Format(UrlPatternUser, DirectCast(User, UserData).NameTrue)
|
||||||
@@ -165,6 +191,25 @@ Namespace API.ThreadsNet
|
|||||||
Return ErrorsDescriber.Execute(EDP.SendToLog, ex, "Can't open user's post", String.Empty)
|
Return ErrorsDescriber.Execute(EDP.SendToLog, ex, "Can't open user's post", String.Empty)
|
||||||
End Try
|
End Try
|
||||||
End Function
|
End Function
|
||||||
|
#End Region
|
||||||
|
#Region "Update"
|
||||||
|
Private __Cookies As CookieKeeper = Nothing
|
||||||
|
Friend Overrides Sub BeginEdit()
|
||||||
|
__Cookies = Responser.Cookies.Copy
|
||||||
|
MyBase.BeginEdit()
|
||||||
|
End Sub
|
||||||
|
Friend Overrides Sub Update()
|
||||||
|
If _SiteEditorFormOpened And Responser.CookiesExists Then
|
||||||
|
Dim csrf$ = GetValueFromCookies(NameOf(HH_CSRF_TOKEN), Responser.Cookies)
|
||||||
|
If Not csrf.IsEmptyString Then HH_CSRF_TOKEN.Value = csrf
|
||||||
|
If Not __Cookies Is Nothing AndAlso Not __Cookies.ListEquals(Responser.Cookies) Then DownloadData_Impl.Value = True
|
||||||
|
End If
|
||||||
|
MyBase.Update()
|
||||||
|
End Sub
|
||||||
|
Friend Overrides Sub EndEdit()
|
||||||
|
__Cookies.DisposeIfReady
|
||||||
|
MyBase.EndEdit()
|
||||||
|
End Sub
|
||||||
#End Region
|
#End Region
|
||||||
End Class
|
End Class
|
||||||
End Namespace
|
End Namespace
|
||||||
@@ -18,20 +18,15 @@ Imports IGS = SCrawler.API.Instagram.SiteSettings
|
|||||||
Namespace API.ThreadsNet
|
Namespace API.ThreadsNet
|
||||||
Friend Class UserData : Inherits Instagram.UserData
|
Friend Class UserData : Inherits Instagram.UserData
|
||||||
#Region "Declarations"
|
#Region "Declarations"
|
||||||
Friend Const Header_FB_LSD As String = "x-fb-lsd"
|
|
||||||
Private ReadOnly Property MySettings As SiteSettings
|
Private ReadOnly Property MySettings As SiteSettings
|
||||||
Get
|
Get
|
||||||
Return HOST.Source
|
Return HOST.Source
|
||||||
End Get
|
End Get
|
||||||
End Property
|
End Property
|
||||||
Private ReadOnly ObtainMedia_SizeFuncPic_RegexP As RParams = RParams.DMS("_p(\d+)x(\d+)", 1, EDP.ReturnValue)
|
|
||||||
Private ReadOnly ObtainMedia_SizeFuncPic_RegexS As RParams = RParams.DMS("_s(\d+)x(\d+)", 1, EDP.ReturnValue)
|
|
||||||
Private ReadOnly DefaultParser_ElemNode_Default() As Object = {"node", "thread_items", 0, "post"}
|
Private ReadOnly DefaultParser_ElemNode_Default() As Object = {"node", "thread_items", 0, "post"}
|
||||||
Private OPT_LSD As String = String.Empty
|
|
||||||
Private OPT_FB_DTSG As String = String.Empty
|
|
||||||
Private ReadOnly Property Valid As Boolean
|
Private ReadOnly Property Valid As Boolean
|
||||||
Get
|
Get
|
||||||
Return Not OPT_LSD.IsEmptyString And Not OPT_FB_DTSG.IsEmptyString And Not ID.IsEmptyString
|
Return ValidateBaseTokens() And Not ID.IsEmptyString
|
||||||
End Get
|
End Get
|
||||||
End Property
|
End Property
|
||||||
#End Region
|
#End Region
|
||||||
@@ -48,44 +43,40 @@ Namespace API.ThreadsNet
|
|||||||
#End Region
|
#End Region
|
||||||
#Region "Initializer"
|
#Region "Initializer"
|
||||||
Friend Sub New()
|
Friend Sub New()
|
||||||
ObtainMedia_SizeFuncPic = Function(ByVal ss As EContainer) As Sizes
|
ObtainMedia_SetReelsFunc()
|
||||||
If ss.Value("url").IsEmptyString Then
|
|
||||||
Return New Sizes("----", "")
|
|
||||||
ElseIf Not ss.Value("width").IsEmptyString Then
|
|
||||||
Return New Sizes(ss.Value("height").IfNullOrEmpty(ss.Value("width")), ss.Value("url"))
|
|
||||||
Else
|
|
||||||
Dim rval$ = RegexReplace(ss.Value("url"), ObtainMedia_SizeFuncPic_RegexP)
|
|
||||||
If Not rval.IsEmptyString Then Return New Sizes(rval, ss.Value("url"))
|
|
||||||
rval = RegexReplace(ss.Value("url"), ObtainMedia_SizeFuncPic_RegexS)
|
|
||||||
If Not rval.IsEmptyString Then Return New Sizes(AConvert(Of Integer)(rval, 1) * -1, ss.Value("url"))
|
|
||||||
Return New Sizes(10000, ss.Value("url"))
|
|
||||||
End If
|
|
||||||
End Function
|
|
||||||
ObtainMedia_SizeFuncVid = Function(ss) If(ss.Value("url").IsEmptyString, New Sizes("----", ""), New Sizes(10000, ss.Value("url")))
|
|
||||||
ObtainMedia_AllowAbstract = True
|
ObtainMedia_AllowAbstract = True
|
||||||
DefaultParser_ElemNode = DefaultParser_ElemNode_Default
|
DefaultParser_ElemNode = DefaultParser_ElemNode_Default
|
||||||
DefaultParser_PostUrlCreator = Function(post) $"https://www.threads.net/@{NameTrue}/post/{post.Code}"
|
DefaultParser_PostUrlCreator = Function(post) $"https://www.threads.net/@{NameTrue}/post/{post.Code}"
|
||||||
|
_ResponserAutoUpdateCookies = True
|
||||||
|
_ResponserAddResponseReceivedHandler = True
|
||||||
End Sub
|
End Sub
|
||||||
#End Region
|
#End Region
|
||||||
#Region "Download functions"
|
#Region "Download functions"
|
||||||
|
Private Sub WaitTimer()
|
||||||
|
If CInt(MySettings.RequestsWaitTimer_Any.Value) > 0 Then Thread.Sleep(CInt(MySettings.RequestsWaitTimer_Any.Value))
|
||||||
|
End Sub
|
||||||
|
Private Sub DisableDownload()
|
||||||
|
MySettings.DownloadData_Impl.Value = False
|
||||||
|
MyMainLOG = $"{Site} downloading is disabled until you update your credentials"
|
||||||
|
End Sub
|
||||||
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
|
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
|
||||||
Dim errorFound As Boolean = False
|
If CBool(MySettings.DownloadData_Impl.Value) Then
|
||||||
Try
|
Dim errorFound As Boolean = False
|
||||||
Responser.Method = "POST"
|
Try
|
||||||
AddHandler Responser.ResponseReceived, AddressOf Responser_ResponseReceived
|
Responser.Method = "POST"
|
||||||
LoadSavePostsKV(True)
|
LoadSavePostsKV(True)
|
||||||
OPT_LSD = String.Empty
|
ResetBaseTokens()
|
||||||
OPT_FB_DTSG = String.Empty
|
DownloadData(String.Empty, Token)
|
||||||
DownloadData(String.Empty, Token)
|
Catch ex As Exception
|
||||||
Catch ex As Exception
|
errorFound = True
|
||||||
errorFound = True
|
Throw ex
|
||||||
Throw ex
|
Finally
|
||||||
Finally
|
Responser.Method = "POST"
|
||||||
Responser.Method = "POST"
|
UpdateResponser()
|
||||||
UpdateResponser()
|
MySettings.UpdateResponserData(Responser)
|
||||||
MySettings.UpdateResponserData(Responser)
|
If Not errorFound Then LoadSavePostsKV(False)
|
||||||
If Not errorFound Then LoadSavePostsKV(False)
|
End Try
|
||||||
End Try
|
End If
|
||||||
End Sub
|
End Sub
|
||||||
Protected Overrides Sub UpdateResponser()
|
Protected Overrides Sub UpdateResponser()
|
||||||
If Not Responser Is Nothing AndAlso Not Responser.Disposed Then
|
If Not Responser Is Nothing AndAlso Not Responser.Disposed Then
|
||||||
@@ -110,11 +101,11 @@ Namespace API.ThreadsNet
|
|||||||
UpdateCredentials()
|
UpdateCredentials()
|
||||||
If idIsNull And Not ID.IsEmptyString Then _ForceSaveUserInfo = True
|
If idIsNull And Not ID.IsEmptyString Then _ForceSaveUserInfo = True
|
||||||
End If
|
End If
|
||||||
If Not Valid Then Throw New Plugin.ExitException("Some credentials are missing")
|
If Not Valid Then DisableDownload() : Throw New Plugin.ExitException("Some credentials are missing")
|
||||||
|
|
||||||
Responser.Method = "POST"
|
Responser.Method = "POST"
|
||||||
Responser.Referer = $"https://www.threads.net/@{NameTrue}"
|
Responser.Referer = $"https://www.threads.net/@{NameTrue}"
|
||||||
Responser.Headers.Add(Header_FB_LSD, OPT_LSD)
|
Responser.Headers.Add(GQL_HEADER_FB_LSD, Token_lsd)
|
||||||
|
|
||||||
Dim nextCursor$ = String.Empty
|
Dim nextCursor$ = String.Empty
|
||||||
Dim dataFound As Boolean = False
|
Dim dataFound As Boolean = False
|
||||||
@@ -127,7 +118,7 @@ Namespace API.ThreadsNet
|
|||||||
End If
|
End If
|
||||||
vars = SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & vars & "}")
|
vars = SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & vars & "}")
|
||||||
|
|
||||||
URL = String.Format(urlPattern, OPT_LSD, vars, SymbolsConverter.ASCII.EncodeSymbolsOnly(OPT_FB_DTSG))
|
URL = String.Format(urlPattern, Token_lsd, vars, Token_dtsg_Var)
|
||||||
|
|
||||||
Using j As EContainer = GetDocument(URL, Token)
|
Using j As EContainer = GetDocument(URL, Token)
|
||||||
If j.ListExists Then
|
If j.ListExists Then
|
||||||
@@ -150,8 +141,9 @@ Namespace API.ThreadsNet
|
|||||||
Private Function GetDocument(ByVal URL As String, ByVal Token As CancellationToken, Optional ByVal Round As Integer = 0) As EContainer
|
Private Function GetDocument(ByVal URL As String, ByVal Token As CancellationToken, Optional ByVal Round As Integer = 0) As EContainer
|
||||||
Try
|
Try
|
||||||
ThrowAny(Token)
|
ThrowAny(Token)
|
||||||
If Round > 0 AndAlso Not UpdateCredentials() Then Throw New Exception("Failed to update credentials")
|
If Round > 0 AndAlso Not UpdateCredentials() Then DisableDownload() : Throw New Exception("Failed to update credentials")
|
||||||
ThrowAny(Token)
|
ThrowAny(Token)
|
||||||
|
WaitTimer()
|
||||||
Dim r$ = Responser.GetResponse(URL)
|
Dim r$ = Responser.GetResponse(URL)
|
||||||
If Not r.IsEmptyString Then Return JsonDocument.Parse(r) Else Throw New Exception("Failed to get a response")
|
If Not r.IsEmptyString Then Return JsonDocument.Parse(r) Else Throw New Exception("Failed to get a response")
|
||||||
Catch ex As Exception
|
Catch ex As Exception
|
||||||
@@ -164,49 +156,56 @@ Namespace API.ThreadsNet
|
|||||||
End Function
|
End Function
|
||||||
Private Function UpdateCredentials(Optional ByVal e As ErrorsDescriber = Nothing) As Boolean
|
Private Function UpdateCredentials(Optional ByVal e As ErrorsDescriber = Nothing) As Boolean
|
||||||
Dim URL$ = $"https://www.threads.net/@{NameTrue}"
|
Dim URL$ = $"https://www.threads.net/@{NameTrue}"
|
||||||
OPT_LSD = String.Empty
|
ResetBaseTokens()
|
||||||
OPT_FB_DTSG = String.Empty
|
Dim headers As New HttpHeaderCollection
|
||||||
|
headers.AddRange(Responser.Headers)
|
||||||
Try
|
Try
|
||||||
Responser.Method = "GET"
|
With Responser
|
||||||
Responser.Referer = URL
|
.Method = "GET"
|
||||||
Responser.Headers.Remove(Header_FB_LSD)
|
.Referer = URL
|
||||||
Dim r$ = Responser.GetResponse(URL,, EDP.SendToLog + EDP.ThrowException)
|
With .Headers
|
||||||
Dim rr As RParams
|
.Clear()
|
||||||
Dim tt$, ttVal$
|
.Add("dnt", 1)
|
||||||
|
.Add("drp", 1)
|
||||||
|
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.Authority, "www.threads.net"))
|
||||||
|
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.Origin, "https://www.threads.net"))
|
||||||
|
.Add("Sec-Ch-Ua-Model", "")
|
||||||
|
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecChUaMobile, "?0"))
|
||||||
|
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecChUaPlatform, """Windows"""))
|
||||||
|
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecFetchDest, "document"))
|
||||||
|
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecFetchMode, "navigate"))
|
||||||
|
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecFetchSite, "none"))
|
||||||
|
.Add("Upgrade-Insecure-Requests", 1)
|
||||||
|
.Add("Sec-Fetch-User", "?1")
|
||||||
|
.Add(IGS.Header_Browser, MySettings.HH_BROWSER.Value)
|
||||||
|
.Add(IGS.Header_BrowserExt, MySettings.HH_BROWSER_EXT.Value)
|
||||||
|
End With
|
||||||
|
End With
|
||||||
|
WaitTimer()
|
||||||
|
Dim r$ = Responser.GetResponse(URL,, EDP.ThrowException)
|
||||||
If Not r.IsEmptyString Then
|
If Not r.IsEmptyString Then
|
||||||
rr = RParams.DM("\[\],{""token"":""(.*?)""},\d+\]", 0, RegexReturn.List, EDP.ReturnValue)
|
ParseTokens(r, 0)
|
||||||
Dim tokens As List(Of String) = RegexReplace(r, rr)
|
If ID.IsEmptyString Then ID = RegexReplace(r, RParams.DMS("""props"":\{""user_id"":""(\d+)""", 1, EDP.ReturnValue))
|
||||||
If tokens.ListExists Then
|
|
||||||
With rr
|
|
||||||
.Match = Nothing
|
|
||||||
.MatchSub = 1
|
|
||||||
.WhatGet = RegexReturn.Value
|
|
||||||
End With
|
|
||||||
For Each tt In tokens
|
|
||||||
If Not OPT_FB_DTSG.IsEmptyString And Not OPT_LSD.IsEmptyString Then
|
|
||||||
Exit For
|
|
||||||
Else
|
|
||||||
ttVal = RegexReplace(tt, rr)
|
|
||||||
If Not ttVal.IsEmptyString Then
|
|
||||||
If ttVal.Contains(":") Then
|
|
||||||
If OPT_FB_DTSG.IsEmptyString Then OPT_FB_DTSG = ttVal
|
|
||||||
Else
|
|
||||||
If OPT_LSD.IsEmptyString Then OPT_LSD = ttVal
|
|
||||||
End If
|
|
||||||
End If
|
|
||||||
End If
|
|
||||||
Next
|
|
||||||
End If
|
|
||||||
If ID.IsEmptyString Then ID = RegexReplace(r, RParams.DMS("""props"":\{""user_id"":""(\d+)""\},", 1, EDP.ReturnValue))
|
|
||||||
End If
|
End If
|
||||||
Return Valid
|
Return Valid
|
||||||
Catch ex As Exception
|
Catch ex As Exception
|
||||||
Dim notFound$ = String.Empty
|
Dim notFound$ = String.Empty
|
||||||
If OPT_FB_DTSG.IsEmptyString Then notFound.StringAppend(Header_FB_LSD)
|
ValidateBaseTokens(notFound)
|
||||||
If OPT_LSD.IsEmptyString Then notFound.StringAppend("lsd")
|
|
||||||
If ID.IsEmptyString Then notFound.StringAppend("User ID")
|
If ID.IsEmptyString Then notFound.StringAppend("User ID")
|
||||||
LogError(ex, $"failed to update some{IIf(notFound.IsEmptyString, String.Empty, $" ({notFound})")} credentials", e)
|
DisableDownload()
|
||||||
|
Dim eex As New ErrorsDescriberException($"{ToStringForLog()}: failed to update some{IIf(notFound.IsEmptyString, String.Empty, $" ({notFound})")} credentials",,, ex) With {
|
||||||
|
.ReplaceMainMessage = True,
|
||||||
|
.SendToLogOnlyMessage = Responser.StatusCode = Net.HttpStatusCode.InternalServerError And Responser.Status = Net.WebExceptionStatus.ProtocolError
|
||||||
|
}
|
||||||
|
'LogError(ex, $"failed to update some{IIf(notFound.IsEmptyString, String.Empty, $" ({notFound})")} credentials", e)
|
||||||
|
LogError(eex, String.Empty, e)
|
||||||
Return False
|
Return False
|
||||||
|
Finally
|
||||||
|
If headers.ListExists Then
|
||||||
|
Responser.Headers.Clear()
|
||||||
|
Responser.Headers.AddRange(headers)
|
||||||
|
headers.Dispose()
|
||||||
|
End If
|
||||||
End Try
|
End Try
|
||||||
End Function
|
End Function
|
||||||
#End Region
|
#End Region
|
||||||
@@ -234,7 +233,7 @@ Namespace API.ThreadsNet
|
|||||||
If m.State = UserMedia.States.Missing And Not m.Post.ID.IsEmptyString Then
|
If m.State = UserMedia.States.Missing And Not m.Post.ID.IsEmptyString Then
|
||||||
ThrowAny(Token)
|
ThrowAny(Token)
|
||||||
vars = SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & String.Format(varsPattern, m.Post.ID.Split("_").FirstOrDefault, ID) & "}")
|
vars = SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & String.Format(varsPattern, m.Post.ID.Split("_").FirstOrDefault, ID) & "}")
|
||||||
URL = String.Format(urlPattern, OPT_LSD, vars, SymbolsConverter.ASCII.EncodeSymbolsOnly(OPT_FB_DTSG))
|
URL = String.Format(urlPattern, Token_lsd, vars, Token_dtsg_Var)
|
||||||
|
|
||||||
j = GetDocument(URL, Token)
|
j = GetDocument(URL, Token)
|
||||||
If j.ListExists Then
|
If j.ListExists Then
|
||||||
|
|||||||
@@ -17,24 +17,37 @@ Namespace API.TikTok
|
|||||||
Friend ReadOnly Property RemoveTagsFromTitle As PropertyValue
|
Friend ReadOnly Property RemoveTagsFromTitle As PropertyValue
|
||||||
<PropertyOption(ControlText:="Use native title", ControlToolTip:="Use a user-created video title for the filename instead of the video ID."), PXML, PClonable>
|
<PropertyOption(ControlText:="Use native title", ControlToolTip:="Use a user-created video title for the filename instead of the video ID."), PXML, PClonable>
|
||||||
Friend ReadOnly Property TitleUseNative As PropertyValue
|
Friend ReadOnly Property TitleUseNative As PropertyValue
|
||||||
<PropertyOption(ControlText:="Use native title in standalone downloader",
|
<PropertyOption(ControlText:="Use native title (standalone downloader)",
|
||||||
ControlToolTip:="Use a user-created video title for the filename instead of the video ID."), PXML, PClonable>
|
ControlToolTip:="Use a user-created video title for the filename instead of the video ID."), PXML, PClonable>
|
||||||
Friend ReadOnly Property TitleUseNativeSTD As PropertyValue
|
Friend ReadOnly Property TitleUseNativeSTD As PropertyValue
|
||||||
<PropertyOption(ControlText:="Add video ID to video title"), PXML, PClonable>
|
<PropertyOption(ControlText:="Add video ID to video title"), PXML, PClonable>
|
||||||
Friend ReadOnly Property TitleAddVideoID As PropertyValue
|
Friend ReadOnly Property TitleAddVideoID As PropertyValue
|
||||||
|
<PropertyOption(ControlText:="Add video ID to video title (standalone downloader)"), PXML, PClonable>
|
||||||
|
Friend ReadOnly Property TitleAddVideoIDSTD As PropertyValue
|
||||||
|
<PropertyOption(ControlText:="Use regex to clean video title"), PXML, PClonable>
|
||||||
|
Friend ReadOnly Property TitleUseRegexForTitle As PropertyValue
|
||||||
|
<PropertyOption(ControlText:="Title regex", ControlToolTip:="Regex to clean video title"), PXML, PClonable>
|
||||||
|
Friend ReadOnly Property TitleUseRegexForTitle_Value As PropertyValue
|
||||||
<PropertyOption(ControlText:="Use video date as file date",
|
<PropertyOption(ControlText:="Use video date as file date",
|
||||||
ControlToolTip:="Set the file date to the date the video was added (website) (if available)."), PXML, PClonable>
|
ControlToolTip:="Set the file date to the date the video was added (website) (if available)."), PXML, PClonable>
|
||||||
Friend ReadOnly Property UseParsedVideoDate As PropertyValue
|
Friend ReadOnly Property UseParsedVideoDate As PropertyValue
|
||||||
|
<PropertyOption(ControlText:="Use video date as file date (standalone downloader)",
|
||||||
|
ControlToolTip:="Set the file date to the date the video was added (website) (if available)."), PXML, PClonable>
|
||||||
|
Friend ReadOnly Property UseParsedVideoDateSTD As PropertyValue
|
||||||
Friend Sub New(ByVal AccName As String, ByVal Temp As Boolean)
|
Friend Sub New(ByVal AccName As String, ByVal Temp As Boolean)
|
||||||
MyBase.New("TikTok", "www.tiktok.com", AccName, Temp, My.Resources.SiteResources.TikTokIcon_32, My.Resources.SiteResources.TikTokPic_192)
|
MyBase.New("TikTok", "www.tiktok.com", AccName, Temp, My.Resources.SiteResources.TikTokIcon_32, My.Resources.SiteResources.TikTokPic_192)
|
||||||
RemoveTagsFromTitle = New PropertyValue(False)
|
RemoveTagsFromTitle = New PropertyValue(False)
|
||||||
TitleUseNative = New PropertyValue(True)
|
TitleUseNative = New PropertyValue(True)
|
||||||
TitleUseNativeSTD = New PropertyValue(False)
|
TitleUseNativeSTD = New PropertyValue(True)
|
||||||
TitleAddVideoID = New PropertyValue(True)
|
TitleAddVideoID = New PropertyValue(True)
|
||||||
|
TitleAddVideoIDSTD = New PropertyValue(True)
|
||||||
|
TitleUseRegexForTitle = New PropertyValue(False)
|
||||||
|
TitleUseRegexForTitle_Value = New PropertyValue(String.Empty, GetType(String))
|
||||||
UseParsedVideoDate = New PropertyValue(True)
|
UseParsedVideoDate = New PropertyValue(True)
|
||||||
|
UseParsedVideoDateSTD = New PropertyValue(False)
|
||||||
UseNetscapeCookies = True
|
UseNetscapeCookies = True
|
||||||
UrlPatternUser = "https://www.tiktok.com/@{0}/"
|
UrlPatternUser = "https://www.tiktok.com/@{0}/"
|
||||||
UserRegex = RParams.DMS("[htps:/]{7,8}.*?tiktok.com/@([^/]+)", 1)
|
UserRegex = RParams.DMS(String.Format(UserRegexDefaultPattern, "tiktok.com/@"), 1)
|
||||||
ImageVideoContains = "tiktok.com"
|
ImageVideoContains = "tiktok.com"
|
||||||
End Sub
|
End Sub
|
||||||
Friend Overrides Function GetInstance(ByVal What As ISiteSettings.Download) As IPluginContentProvider
|
Friend Overrides Function GetInstance(ByVal What As ISiteSettings.Download) As IPluginContentProvider
|
||||||
|
|||||||
@@ -20,6 +20,9 @@ Namespace API.TikTok
|
|||||||
Private Const Name_TitleUseNative As String = "TitleUseNative"
|
Private Const Name_TitleUseNative As String = "TitleUseNative"
|
||||||
Private Const Name_TitleAddVideoID As String = "TitleAddVideoID"
|
Private Const Name_TitleAddVideoID As String = "TitleAddVideoID"
|
||||||
Private Const Name_LastDownloadDate As String = "LastDownloadDate"
|
Private Const Name_LastDownloadDate As String = "LastDownloadDate"
|
||||||
|
Private Const Name_TitleUseRegexForTitle As String = "TitleUseRegexForTitle"
|
||||||
|
Private Const Name_TitleUseRegexForTitle_Value As String = "TitleUseRegexForTitle_Value"
|
||||||
|
Private Const Name_TitleUseGlobalRegexOptions As String = "TitleUseGlobalRegexOptions"
|
||||||
#End Region
|
#End Region
|
||||||
#Region "Declarations"
|
#Region "Declarations"
|
||||||
Private ReadOnly Property MySettings As SiteSettings
|
Private ReadOnly Property MySettings As SiteSettings
|
||||||
@@ -27,26 +30,37 @@ Namespace API.TikTok
|
|||||||
Return HOST.Source
|
Return HOST.Source
|
||||||
End Get
|
End Get
|
||||||
End Property
|
End Property
|
||||||
|
Private UserCache As CacheKeeper = Nothing
|
||||||
Private ReadOnly Property RootCacheTikTok As ICacheKeeper
|
Private ReadOnly Property RootCacheTikTok As ICacheKeeper
|
||||||
Get
|
Get
|
||||||
With Settings.Cache
|
If Not UserCache Is Nothing AndAlso Not UserCache.Disposed Then
|
||||||
Dim f As SFile = $"{Settings.Cache.RootDirectory.PathWithSeparator}TikTokCache\"
|
With DirectCast(UserCache.NewInstance(Of BatchFileExchanger), BatchFileExchanger)
|
||||||
If .ContainsFolder(f) Then
|
.Validate()
|
||||||
Return .GetInstance(f)
|
Return .Self
|
||||||
Else
|
End With
|
||||||
f.Exists(SFO.Path, True)
|
Else
|
||||||
With .NewInstance(Of BatchFileExchanger)(f)
|
With Settings.Cache
|
||||||
.DeleteCacheOnDispose = False
|
Dim f As SFile = $"{Settings.Cache.RootDirectory.PathWithSeparator}TikTokCache\"
|
||||||
.DeleteRootOnDispose = False
|
If .ContainsFolder(f) Then
|
||||||
Return .Self
|
Return .GetInstance(f)
|
||||||
End With
|
Else
|
||||||
End If
|
f.Exists(SFO.Path, True)
|
||||||
End With
|
With .NewInstance(Of BatchFileExchanger)(f)
|
||||||
|
.DeleteCacheOnDispose = False
|
||||||
|
.DeleteRootOnDispose = False
|
||||||
|
Return .Self
|
||||||
|
End With
|
||||||
|
End If
|
||||||
|
End With
|
||||||
|
End If
|
||||||
End Get
|
End Get
|
||||||
End Property
|
End Property
|
||||||
Friend Property RemoveTagsFromTitle As Boolean = False
|
Friend Property RemoveTagsFromTitle As Boolean = False
|
||||||
Friend Property TitleUseNative As Boolean = True
|
Friend Property TitleUseNative As Boolean = True
|
||||||
Friend Property TitleAddVideoID As Boolean = True
|
Friend Property TitleAddVideoID As Boolean = True
|
||||||
|
Friend Property TitleUseRegexForTitle As Boolean = False
|
||||||
|
Friend Property TitleUseRegexForTitle_Value As String = String.Empty
|
||||||
|
Friend Property TitleUseGlobalRegexOptions As Boolean = True
|
||||||
Private Property LastDownloadDate As Date? = Nothing
|
Private Property LastDownloadDate As Date? = Nothing
|
||||||
Private _TrueName As String = String.Empty
|
Private _TrueName As String = String.Empty
|
||||||
Friend Property TrueName As String
|
Friend Property TrueName As String
|
||||||
@@ -68,6 +82,9 @@ Namespace API.TikTok
|
|||||||
RemoveTagsFromTitle = .RemoveTagsFromTitle
|
RemoveTagsFromTitle = .RemoveTagsFromTitle
|
||||||
TitleUseNative = .TitleUseNative
|
TitleUseNative = .TitleUseNative
|
||||||
TitleAddVideoID = .TitleAddVideoID
|
TitleAddVideoID = .TitleAddVideoID
|
||||||
|
TitleUseRegexForTitle = .TitleUseRegexForTitle
|
||||||
|
TitleUseRegexForTitle_Value = .TitleUseRegexForTitle_Value
|
||||||
|
TitleUseGlobalRegexOptions = .TitleUseGlobalRegexOptions
|
||||||
End With
|
End With
|
||||||
End If
|
End If
|
||||||
End Sub
|
End Sub
|
||||||
@@ -82,12 +99,18 @@ Namespace API.TikTok
|
|||||||
LastDownloadDate = AConvert(Of Date)(.Value(Name_LastDownloadDate), ADateTime.Formats.BaseDateTime, Nothing)
|
LastDownloadDate = AConvert(Of Date)(.Value(Name_LastDownloadDate), ADateTime.Formats.BaseDateTime, Nothing)
|
||||||
If Not LastDownloadDate.HasValue Then LastDownloadDate = LastUpdated
|
If Not LastDownloadDate.HasValue Then LastDownloadDate = LastUpdated
|
||||||
_TrueName = .Value(Name_TrueName)
|
_TrueName = .Value(Name_TrueName)
|
||||||
|
TitleUseRegexForTitle = .Value(Name_TitleUseRegexForTitle).FromXML(Of Boolean)(False)
|
||||||
|
TitleUseRegexForTitle_Value = .Value(Name_TitleUseRegexForTitle_Value)
|
||||||
|
TitleUseGlobalRegexOptions = .Value(Name_TitleUseGlobalRegexOptions).FromXML(Of Boolean)(True)
|
||||||
Else
|
Else
|
||||||
.Add(Name_RemoveTagsFromTitle, RemoveTagsFromTitle.BoolToInteger)
|
.Add(Name_RemoveTagsFromTitle, RemoveTagsFromTitle.BoolToInteger)
|
||||||
.Add(Name_TitleUseNative, TitleUseNative.BoolToInteger)
|
.Add(Name_TitleUseNative, TitleUseNative.BoolToInteger)
|
||||||
.Add(Name_TitleAddVideoID, TitleAddVideoID.BoolToInteger)
|
.Add(Name_TitleAddVideoID, TitleAddVideoID.BoolToInteger)
|
||||||
.Add(Name_LastDownloadDate, AConvert(Of String)(LastDownloadDate, AModes.XML, ADateTime.Formats.BaseDateTime, String.Empty))
|
.Add(Name_LastDownloadDate, AConvert(Of String)(LastDownloadDate, AModes.XML, ADateTime.Formats.BaseDateTime, String.Empty))
|
||||||
.Add(Name_TrueName, _TrueName)
|
.Add(Name_TrueName, _TrueName)
|
||||||
|
.Add(Name_TitleUseRegexForTitle, TitleUseRegexForTitle.BoolToInteger)
|
||||||
|
.Add(Name_TitleUseRegexForTitle_Value, TitleUseRegexForTitle_Value)
|
||||||
|
.Add(Name_TitleUseGlobalRegexOptions, TitleUseGlobalRegexOptions.BoolToInteger)
|
||||||
End If
|
End If
|
||||||
End With
|
End With
|
||||||
End Sub
|
End Sub
|
||||||
@@ -99,110 +122,151 @@ Namespace API.TikTok
|
|||||||
End Sub
|
End Sub
|
||||||
#End Region
|
#End Region
|
||||||
#Region "Download functions"
|
#Region "Download functions"
|
||||||
|
Private Function GetTitleRegex() As RParams
|
||||||
|
Dim titleRegex As RParams = Nothing
|
||||||
|
If TitleUseGlobalRegexOptions Then
|
||||||
|
If CBool(MySettings.TitleUseRegexForTitle.Value) AndAlso Not CStr(MySettings.TitleUseRegexForTitle_Value.Value).IsEmptyString Then _
|
||||||
|
titleRegex = RParams.DM(MySettings.TitleUseRegexForTitle_Value.Value, 0, RegexReturn.List, EDP.ReturnValue)
|
||||||
|
ElseIf TitleUseRegexForTitle And Not TitleUseRegexForTitle_Value.IsEmptyString Then
|
||||||
|
titleRegex = RParams.DM(TitleUseRegexForTitle_Value, 0, RegexReturn.List, EDP.ReturnValue)
|
||||||
|
End If
|
||||||
|
If Not titleRegex Is Nothing Then
|
||||||
|
titleRegex.NothingExists = True
|
||||||
|
titleRegex.Nothing = New List(Of String)
|
||||||
|
titleRegex.Converter = Function(input) input.StringTrim
|
||||||
|
End If
|
||||||
|
Return titleRegex
|
||||||
|
End Function
|
||||||
|
Private Function ChangeTitleRegex(ByVal Title As String, ByVal Regex As RParams) As String
|
||||||
|
Try
|
||||||
|
If Not Regex Is Nothing Then
|
||||||
|
With DirectCast(RegexReplace(Title, Regex), List(Of String))
|
||||||
|
If .ListExists Then
|
||||||
|
Dim newTitle$ = .ListToString(String.Empty, EDP.ReturnValue).StringTrim
|
||||||
|
If Not newTitle.IsEmptyString Then Return newTitle
|
||||||
|
End If
|
||||||
|
End With
|
||||||
|
End If
|
||||||
|
Catch ex As Exception
|
||||||
|
End Try
|
||||||
|
Return Title
|
||||||
|
End Function
|
||||||
|
Private Function GetNewFileName(ByVal Title As String, ByVal Native As Boolean, ByVal RemoveTags As Boolean, ByVal AddVideoID As Boolean,
|
||||||
|
ByVal PostID As String, ByVal TitleRegex As RParams) As String
|
||||||
|
If Not Title.IsEmptyString Then Title = Left(Title, 150).StringTrim
|
||||||
|
If Title.IsEmptyString Or Not Native Then
|
||||||
|
Title = PostID
|
||||||
|
Else
|
||||||
|
If RemoveTags Then Title = RegexReplace(Title, RegexTagsReplacer)
|
||||||
|
Title = Title.StringTrim
|
||||||
|
If Title.IsEmptyString Then
|
||||||
|
Title = PostID
|
||||||
|
ElseIf AddVideoID Then
|
||||||
|
Title &= $" ({PostID})"
|
||||||
|
End If
|
||||||
|
Title = ChangeTitleRegex(Title, TitleRegex)
|
||||||
|
End If
|
||||||
|
Return Title
|
||||||
|
End Function
|
||||||
|
Friend Overrides Sub DownloadData(ByVal Token As CancellationToken)
|
||||||
|
MyBase.DownloadData(Token)
|
||||||
|
UserCache.DisposeIfReady(False)
|
||||||
|
UserCache = Nothing
|
||||||
|
End Sub
|
||||||
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
|
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
|
||||||
Dim URL$ = $"https://www.tiktok.com/@{TrueName}"
|
Dim URL$ = $"https://www.tiktok.com/@{TrueName}"
|
||||||
Using cache As CacheKeeper = CreateCache()
|
UserCache = CreateCache()
|
||||||
Try
|
Try
|
||||||
Dim postID$, title$, postUrl$, newName$
|
Dim postID$, title$, postUrl$, newName$
|
||||||
Dim postDate As Date?
|
Dim postDate As Date?
|
||||||
Dim dateAfterC As Date? = Nothing
|
Dim dateAfterC As Date? = Nothing
|
||||||
Dim dateBefore As Date? = DownloadDateTo
|
Dim dateBefore As Date? = DownloadDateTo
|
||||||
Dim dateAfter As Date? = DownloadDateFrom
|
Dim dateAfter As Date? = DownloadDateFrom
|
||||||
Dim baseDataObtained As Boolean = False
|
Dim baseDataObtained As Boolean = False
|
||||||
|
Dim titleRegex As RParams = GetTitleRegex()
|
||||||
|
|
||||||
If _ContentList.Count > 0 Then
|
If _ContentList.Count > 0 Then
|
||||||
With (From d In _ContentList Where d.Post.Date.HasValue Select d.Post.Date.Value)
|
With (From d In _ContentList Where d.Post.Date.HasValue Select d.Post.Date.Value)
|
||||||
If .ListExists Then dateAfterC = .Min
|
If .ListExists Then dateAfterC = .Min
|
||||||
End With
|
|
||||||
End If
|
|
||||||
|
|
||||||
With {CStr(AConvert(Of String)(dateAfter, SimpleDateConverter, String.Empty)).FromXML(Of Integer)(-1),
|
|
||||||
CStr(AConvert(Of String)(dateAfterC, SimpleDateConverter, String.Empty)).FromXML(Of Integer)(-1)}.ListWithRemove(Function(d) d = -1)
|
|
||||||
If .ListExists Then dateAfter = AConvert(Of Date)(CStr(.Min), SimpleDateConverter, Nothing)
|
|
||||||
End With
|
End With
|
||||||
|
End If
|
||||||
|
|
||||||
If LastDownloadDate.HasValue Then
|
With {CStr(AConvert(Of String)(dateAfter, SimpleDateConverter, String.Empty)).FromXML(Of Integer)(-1),
|
||||||
If Not DownloadDateTo.HasValue And Not DownloadDateFrom.HasValue Then
|
CStr(AConvert(Of String)(dateAfterC, SimpleDateConverter, String.Empty)).FromXML(Of Integer)(-1)}.ListWithRemove(Function(d) d = -1)
|
||||||
If LastDownloadDate.Value.AddDays(1) <= Now Then
|
If .ListExists Then dateAfter = AConvert(Of Date)(CStr(.Min), SimpleDateConverter, Nothing)
|
||||||
dateAfter = LastDownloadDate.Value
|
End With
|
||||||
Else
|
|
||||||
dateAfter = LastDownloadDate.Value.AddDays(-1)
|
If LastDownloadDate.HasValue Then
|
||||||
End If
|
If Not DownloadDateTo.HasValue And Not DownloadDateFrom.HasValue Then
|
||||||
dateBefore = Nothing
|
If LastDownloadDate.Value.AddDays(1) <= Now Then
|
||||||
ElseIf dateAfter.HasValue And Not DownloadDateFrom.HasValue Then
|
dateAfter = LastDownloadDate.Value
|
||||||
If (LastDownloadDate.Value - dateAfter.Value).TotalDays > 1 Then dateAfter = dateAfter.Value.AddDays(1)
|
Else
|
||||||
|
dateAfter = LastDownloadDate.Value.AddDays(-1)
|
||||||
End If
|
End If
|
||||||
|
dateBefore = Nothing
|
||||||
|
ElseIf dateAfter.HasValue And Not DownloadDateFrom.HasValue Then
|
||||||
|
If (LastDownloadDate.Value - dateAfter.Value).TotalDays > 1 Then dateAfter = dateAfter.Value.AddDays(1)
|
||||||
End If
|
End If
|
||||||
|
End If
|
||||||
|
|
||||||
Using b As New YTDLP.YTDLPBatch(Token) With {.TempPostsList = _TempPostsList}
|
Using b As New YTDLP.YTDLPBatch(Token) With {.TempPostsList = _TempPostsList}
|
||||||
b.Commands.Clear()
|
b.Commands.Clear()
|
||||||
b.ChangeDirectory(cache)
|
b.ChangeDirectory(UserCache)
|
||||||
b.Encoding = BatchExecutor.UnicodeEncoding
|
b.Encoding = BatchExecutor.UnicodeEncoding
|
||||||
b.Execute(CreateYTCommand(cache.RootDirectory, URL, False, dateBefore, dateAfter))
|
b.Execute(CreateYTCommand(UserCache.RootDirectory, URL, False, dateBefore, dateAfter))
|
||||||
End Using
|
End Using
|
||||||
|
|
||||||
ThrowAny(Token)
|
ThrowAny(Token)
|
||||||
|
|
||||||
Dim files As List(Of SFile) = SFile.GetFiles(cache, "*.json",, EDP.ReturnValue)
|
Dim files As List(Of SFile) = SFile.GetFiles(UserCache, "*.json",, EDP.ReturnValue)
|
||||||
If files.ListExists Then
|
If files.ListExists Then
|
||||||
Dim j As EContainer
|
Dim j As EContainer
|
||||||
For Each file As SFile In files
|
For Each file As SFile In files
|
||||||
j = JsonDocument.Parse(file.GetText, EDP.ReturnValue)
|
j = JsonDocument.Parse(file.GetText, EDP.ReturnValue)
|
||||||
If j.ListExists Then
|
If j.ListExists Then
|
||||||
If j.Value("_type").StringToLower = "video" Then
|
If j.Value("_type").StringToLower = "video" Then
|
||||||
If Not baseDataObtained Then
|
If Not baseDataObtained Then
|
||||||
baseDataObtained = True
|
baseDataObtained = True
|
||||||
If ID.IsEmptyString Then
|
If ID.IsEmptyString Then
|
||||||
ID = j.Value("uploader_id")
|
ID = j.Value("uploader_id")
|
||||||
If Not ID.IsEmptyString Then _ForceSaveUserInfo = True
|
If Not ID.IsEmptyString Then _ForceSaveUserInfo = True
|
||||||
End If
|
|
||||||
newName = j.Value("uploader")
|
|
||||||
If Not newName.IsEmptyString Then
|
|
||||||
If Not _TrueName = newName Then _ForceSaveUserInfo = True
|
|
||||||
_TrueName = newName
|
|
||||||
End If
|
|
||||||
newName = j.Value("creator")
|
|
||||||
If Not newName.IsEmptyString Then UserSiteName = newName
|
|
||||||
End If
|
End If
|
||||||
postID = j.Value("id")
|
newName = j.Value("uploader")
|
||||||
If Not _TempPostsList.Contains(postID) Then
|
If Not newName.IsEmptyString Then
|
||||||
_TempPostsList.Add(postID)
|
If Not _TrueName = newName Then _ForceSaveUserInfo = True
|
||||||
Else
|
_TrueName = newName
|
||||||
Exit Sub
|
|
||||||
End If
|
End If
|
||||||
title = j.Value("title").StringRemoveWinForbiddenSymbols
|
newName = j.Value("creator")
|
||||||
If title.IsEmptyString Or Not TitleUseNative Then
|
If Not newName.IsEmptyString Then UserSiteName = newName
|
||||||
title = postID
|
|
||||||
Else
|
|
||||||
If RemoveTagsFromTitle Then title = RegexReplace(title, RegexTagsReplacer)
|
|
||||||
title = title.StringTrim
|
|
||||||
If title.IsEmptyString Then
|
|
||||||
title = postID
|
|
||||||
ElseIf TitleAddVideoID Then
|
|
||||||
title &= $" ({postID})"
|
|
||||||
End If
|
|
||||||
End If
|
|
||||||
postDate = AConvert(Of Date)(j.Value("timestamp"), UnixDate32Provider, Nothing)
|
|
||||||
If Not postDate.HasValue Then postDate = AConvert(Of Date)(j.Value("upload_date"), SimpleDateConverter, Nothing)
|
|
||||||
Select Case CheckDatesLimit(postDate, SimpleDateConverter)
|
|
||||||
Case DateResult.Skip : Continue For
|
|
||||||
Case DateResult.Exit : Exit Sub
|
|
||||||
End Select
|
|
||||||
|
|
||||||
postUrl = j.Value("webpage_url")
|
|
||||||
If postUrl.IsEmptyString Then postUrl = $"https://www.tiktok.com/@{Name}/video/{postID}"
|
|
||||||
_TempMediaList.Add(New UserMedia(postUrl, UserMedia.Types.Video) With {
|
|
||||||
.File = $"{title}.mp4", .Post = New UserPost(postID, postDate)})
|
|
||||||
End If
|
End If
|
||||||
j.Dispose()
|
postID = j.Value("id")
|
||||||
|
If Not _TempPostsList.Contains(postID) Then
|
||||||
|
_TempPostsList.Add(postID)
|
||||||
|
Else
|
||||||
|
Exit Sub
|
||||||
|
End If
|
||||||
|
title = GetNewFileName(j.Value("title").StringRemoveWinForbiddenSymbols,
|
||||||
|
TitleUseNative, RemoveTagsFromTitle, TitleAddVideoID, postID, titleRegex)
|
||||||
|
postDate = AConvert(Of Date)(j.Value("timestamp"), UnixDate32Provider, Nothing)
|
||||||
|
If Not postDate.HasValue Then postDate = AConvert(Of Date)(j.Value("upload_date"), SimpleDateConverter, Nothing)
|
||||||
|
Select Case CheckDatesLimit(postDate, SimpleDateConverter)
|
||||||
|
Case DateResult.Skip : Continue For
|
||||||
|
Case DateResult.Exit : Exit Sub
|
||||||
|
End Select
|
||||||
|
|
||||||
|
postUrl = j.Value("webpage_url")
|
||||||
|
If postUrl.IsEmptyString Then postUrl = $"https://www.tiktok.com/@{Name}/video/{postID}"
|
||||||
|
_TempMediaList.Add(New UserMedia(postUrl, UserMedia.Types.Video) With {
|
||||||
|
.File = $"{title}.mp4", .Post = New UserPost(postID, postDate)})
|
||||||
End If
|
End If
|
||||||
Next
|
j.Dispose()
|
||||||
End If
|
End If
|
||||||
If _TempMediaList.Count > 0 Then LastDownloadDate = Now
|
Next
|
||||||
Catch ex As Exception
|
End If
|
||||||
ProcessException(ex, Token, $"data downloading error [{URL}]")
|
If _TempMediaList.Count > 0 Then LastDownloadDate = Now
|
||||||
End Try
|
Catch ex As Exception
|
||||||
End Using
|
ProcessException(ex, Token, $"data downloading error [{URL}]")
|
||||||
|
End Try
|
||||||
End Sub
|
End Sub
|
||||||
#End Region
|
#End Region
|
||||||
#Region "ReparseMissing"
|
#Region "ReparseMissing"
|
||||||
@@ -237,7 +301,7 @@ Namespace API.TikTok
|
|||||||
End If
|
End If
|
||||||
If DateBefore.HasValue Then command &= $"--datebefore {DateBefore.Value.AddDays(1).ToStringDate(SimpleDateConverter)} "
|
If DateBefore.HasValue Then command &= $"--datebefore {DateBefore.Value.AddDays(1).ToStringDate(SimpleDateConverter)} "
|
||||||
If DateAfter.HasValue Then command &= $"--dateafter {DateAfter.Value.AddDays(-1).ToStringDate(SimpleDateConverter)} "
|
If DateAfter.HasValue Then command &= $"--dateafter {DateAfter.Value.AddDays(-1).ToStringDate(SimpleDateConverter)} "
|
||||||
If Not CBool(MySettings.UseParsedVideoDate.Value) Then command &= "--no-mtime "
|
If Not CBool(If(IsSingleObjectDownload, MySettings.UseParsedVideoDateSTD, MySettings.UseParsedVideoDate).Value) Then command &= "--no-mtime "
|
||||||
If MySettings.CookiesNetscapeFile.Exists Then command &= $"--no-cookies-from-browser --cookies ""{MySettings.CookiesNetscapeFile}"" "
|
If MySettings.CookiesNetscapeFile.Exists Then command &= $"--no-cookies-from-browser --cookies ""{MySettings.CookiesNetscapeFile}"" "
|
||||||
command &= $"{URL} "
|
command &= $"{URL} "
|
||||||
If SupportOutput Then
|
If SupportOutput Then
|
||||||
@@ -262,7 +326,7 @@ Namespace API.TikTok
|
|||||||
b.Encoding = BatchExecutor.UnicodeEncoding
|
b.Encoding = BatchExecutor.UnicodeEncoding
|
||||||
b.Execute(CreateYTCommand(DestinationFile, URL, True))
|
b.Execute(CreateYTCommand(DestinationFile, URL, True))
|
||||||
End Using
|
End Using
|
||||||
Return DestinationFile
|
If DestinationFile.Exists Then Return DestinationFile Else Return Nothing
|
||||||
End Function
|
End Function
|
||||||
#End Region
|
#End Region
|
||||||
#Region "DownloadSingleObject"
|
#Region "DownloadSingleObject"
|
||||||
@@ -288,21 +352,32 @@ Namespace API.TikTok
|
|||||||
Dim m As New UserMedia(Data.URL, UserMedia.Types.Video)
|
Dim m As New UserMedia(Data.URL, UserMedia.Types.Video)
|
||||||
If Not f.IsEmptyString Then f = TitleHtmlConverter(f)
|
If Not f.IsEmptyString Then f = TitleHtmlConverter(f)
|
||||||
If Not f.IsEmptyString Then
|
If Not f.IsEmptyString Then
|
||||||
If CBool(MySettings.RemoveTagsFromTitle.Value) Then f = RegexReplace(f, RegexTagsReplacer)
|
f = GetNewFileName(f, MySettings.TitleUseNativeSTD.Value, MySettings.RemoveTagsFromTitle.Value, MySettings.TitleAddVideoIDSTD.Value,
|
||||||
f = f.StringTrim
|
m.File.Name, GetTitleRegex)
|
||||||
If Not f.IsEmptyString Then
|
If Not f.IsEmptyString Then m.File.Name = f.StringTrim
|
||||||
If CBool(MySettings.TitleAddVideoID.Value) Then f &= $" ({m.File.Name})"
|
|
||||||
m.File.Name = f
|
|
||||||
End If
|
|
||||||
End If
|
End If
|
||||||
_TempMediaList.Add(m)
|
_TempMediaList.Add(m)
|
||||||
End Sub
|
End Sub
|
||||||
#End Region
|
#End Region
|
||||||
|
#Region "EraseData"
|
||||||
|
Protected Overrides Sub EraseData_AdditionalDataFiles()
|
||||||
|
LastDownloadDate = Nothing
|
||||||
|
End Sub
|
||||||
|
#End Region
|
||||||
#Region "Exception"
|
#Region "Exception"
|
||||||
Protected Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False,
|
Protected Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False,
|
||||||
Optional ByVal EObj As Object = Nothing) As Integer
|
Optional ByVal EObj As Object = Nothing) As Integer
|
||||||
Return 0
|
Return 0
|
||||||
End Function
|
End Function
|
||||||
|
#End Region
|
||||||
|
#Region "IDisposable Support"
|
||||||
|
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
|
||||||
|
If Not disposedValue And disposing Then
|
||||||
|
UserCache.DisposeIfReady(False)
|
||||||
|
UserCache = Nothing
|
||||||
|
End If
|
||||||
|
MyBase.Dispose(disposing)
|
||||||
|
End Sub
|
||||||
#End Region
|
#End Region
|
||||||
End Class
|
End Class
|
||||||
End Namespace
|
End Namespace
|
||||||
@@ -15,18 +15,29 @@ Namespace API.TikTok
|
|||||||
Friend Property TitleUseNative As Boolean
|
Friend Property TitleUseNative As Boolean
|
||||||
<PSetting(NameOf(SiteSettings.TitleAddVideoID), NameOf(MySettings))>
|
<PSetting(NameOf(SiteSettings.TitleAddVideoID), NameOf(MySettings))>
|
||||||
Friend Property TitleAddVideoID As Boolean
|
Friend Property TitleAddVideoID As Boolean
|
||||||
|
<PSetting(NameOf(SiteSettings.TitleUseRegexForTitle), NameOf(MySettings))>
|
||||||
|
Friend Property TitleUseRegexForTitle As Boolean
|
||||||
|
<PSetting(NameOf(SiteSettings.TitleUseRegexForTitle_Value), NameOf(MySettings))>
|
||||||
|
Friend Property TitleUseRegexForTitle_Value As String
|
||||||
|
<PSetting(Caption:="Use global regex", ToolTip:="Use the global regex from the site settings to clean the video title")>
|
||||||
|
Friend Property TitleUseGlobalRegexOptions As Boolean = True
|
||||||
Private ReadOnly MySettings As SiteSettings
|
Private ReadOnly MySettings As SiteSettings
|
||||||
Friend Sub New(ByVal u As UserData)
|
Friend Sub New(ByVal u As UserData)
|
||||||
MySettings = u.HOST.Source
|
MySettings = u.HOST.Source
|
||||||
RemoveTagsFromTitle = u.RemoveTagsFromTitle
|
RemoveTagsFromTitle = u.RemoveTagsFromTitle
|
||||||
TitleUseNative = u.TitleUseNative
|
TitleUseNative = u.TitleUseNative
|
||||||
TitleAddVideoID = u.TitleAddVideoID
|
TitleAddVideoID = u.TitleAddVideoID
|
||||||
|
TitleUseRegexForTitle = u.TitleUseRegexForTitle
|
||||||
|
TitleUseRegexForTitle_Value = u.TitleUseRegexForTitle_Value
|
||||||
|
TitleUseGlobalRegexOptions = u.TitleUseGlobalRegexOptions
|
||||||
End Sub
|
End Sub
|
||||||
Friend Sub New(ByVal s As SiteSettings)
|
Friend Sub New(ByVal s As SiteSettings)
|
||||||
MySettings = s
|
MySettings = s
|
||||||
RemoveTagsFromTitle = s.RemoveTagsFromTitle.Value
|
RemoveTagsFromTitle = s.RemoveTagsFromTitle.Value
|
||||||
TitleUseNative = s.TitleUseNative.Value
|
TitleUseNative = s.TitleUseNative.Value
|
||||||
TitleAddVideoID = s.TitleAddVideoID.Value
|
TitleAddVideoID = s.TitleAddVideoID.Value
|
||||||
|
TitleUseRegexForTitle = s.TitleUseRegexForTitle.Value
|
||||||
|
TitleUseRegexForTitle_Value = s.TitleUseRegexForTitle_Value.Value
|
||||||
End Sub
|
End Sub
|
||||||
End Class
|
End Class
|
||||||
End Namespace
|
End Namespace
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ Namespace API.Twitter
|
|||||||
ConcurrentDownloads = New PropertyValue(1)
|
ConcurrentDownloads = New PropertyValue(1)
|
||||||
MyConcurrentDownloadsProvider = New ConcurrentDownloadsProvider
|
MyConcurrentDownloadsProvider = New ConcurrentDownloadsProvider
|
||||||
|
|
||||||
UserRegex = RParams.DMS("[htps:/]{7,8}.*?twitter.com/([^/]+)", 1)
|
UserRegex = RParams.DMS(String.Format(UserRegexDefaultPattern, "/(twitter|x).com/"), 2)
|
||||||
UrlPatternUser = "https://twitter.com/{0}"
|
UrlPatternUser = "https://twitter.com/{0}"
|
||||||
ImageVideoContains = "twitter"
|
ImageVideoContains = "twitter"
|
||||||
CheckNetscapeCookiesOnEndInit = True
|
CheckNetscapeCookiesOnEndInit = True
|
||||||
|
|||||||
@@ -157,7 +157,6 @@ Namespace API.Twitter
|
|||||||
Private Sub DownloadData_Timeline(ByVal Token As CancellationToken)
|
Private Sub DownloadData_Timeline(ByVal Token As CancellationToken)
|
||||||
Dim URL$ = String.Empty
|
Dim URL$ = String.Empty
|
||||||
Dim tCache As CacheKeeper = Nothing
|
Dim tCache As CacheKeeper = Nothing
|
||||||
Dim jsonArgs As New WebDocumentEventArgs With {.DeclaredError = EDP.ThrowException}
|
|
||||||
Try
|
Try
|
||||||
Const entry$ = "entry"
|
Const entry$ = "entry"
|
||||||
Dim PostID$ = String.Empty
|
Dim PostID$ = String.Empty
|
||||||
@@ -237,8 +236,7 @@ Namespace API.Twitter
|
|||||||
For i = 0 To timelineFiles.Count - 1 : timelineFiles(i) = RenameGdlFile(timelineFiles(i), i) : Next
|
For i = 0 To timelineFiles.Count - 1 : timelineFiles(i) = RenameGdlFile(timelineFiles(i), i) : Next
|
||||||
'parse files
|
'parse files
|
||||||
For i = 0 To timelineFiles.Count - 1
|
For i = 0 To timelineFiles.Count - 1
|
||||||
j = JsonDocument.Parse(timelineFiles(i).GetText, jsonArgs)
|
j = JsonDocument.Parse(timelineFiles(i).GetText)
|
||||||
jsonArgs.Reset()
|
|
||||||
If Not j Is Nothing Then
|
If Not j Is Nothing Then
|
||||||
If i = 0 Then
|
If i = 0 Then
|
||||||
If Not userInfoParsed Then
|
If Not userInfoParsed Then
|
||||||
@@ -363,15 +361,14 @@ Namespace API.Twitter
|
|||||||
End If
|
End If
|
||||||
DownloadModelForceApply = False
|
DownloadModelForceApply = False
|
||||||
FirstDownloadComplete = True
|
FirstDownloadComplete = True
|
||||||
Catch jsonNull_ex As ArgumentNullException When jsonArgs.State = WebDocumentEventArgs.States.Error
|
Catch jsonNull_ex As JsonDocumentException When jsonNull_ex.State = WebDocumentEventArgs.States.Error
|
||||||
Throw New Plugin.ExitException($"{ToStringForLog()}: No deserialized data found")
|
Throw New Plugin.ExitException("No deserialized data found")
|
||||||
Catch limit_ex As TwitterLimitException
|
Catch limit_ex As TwitterLimitException
|
||||||
Throw limit_ex
|
Throw limit_ex
|
||||||
Catch ex As Exception
|
Catch ex As Exception
|
||||||
ProcessException(ex, Token, $"data downloading error [{URL}]")
|
ProcessException(ex, Token, $"data downloading error [{URL}]")
|
||||||
Finally
|
Finally
|
||||||
If Not tCache Is Nothing Then tCache.Dispose()
|
If Not tCache Is Nothing Then tCache.Dispose()
|
||||||
jsonArgs.DisposeIfReady
|
|
||||||
If _TempPostsList.Count > 0 Then _TempPostsList.Sort()
|
If _TempPostsList.Count > 0 Then _TempPostsList.Sort()
|
||||||
End Try
|
End Try
|
||||||
End Sub
|
End Sub
|
||||||
|
|||||||
@@ -276,11 +276,6 @@ Namespace API
|
|||||||
Set(ByVal NewDate As Date?)
|
Set(ByVal NewDate As Date?)
|
||||||
End Set
|
End Set
|
||||||
End Property
|
End Property
|
||||||
Friend Overrides ReadOnly Property FitToAddParams As Boolean
|
|
||||||
Get
|
|
||||||
Return Count > 0 AndAlso Collections.Exists(Function(c) c.FitToAddParams)
|
|
||||||
End Get
|
|
||||||
End Property
|
|
||||||
Friend Overrides Property ScriptUse As Boolean
|
Friend Overrides Property ScriptUse As Boolean
|
||||||
Get
|
Get
|
||||||
Return Count > 0 AndAlso Collections.All(Function(c) c.ScriptUse)
|
Return Count > 0 AndAlso Collections.All(Function(c) c.ScriptUse)
|
||||||
@@ -741,6 +736,15 @@ Namespace API
|
|||||||
Return GetEnumerator()
|
Return GetEnumerator()
|
||||||
End Function
|
End Function
|
||||||
#End Region
|
#End Region
|
||||||
|
#Region "IComparable Support"
|
||||||
|
Friend Overrides Function CompareTo(ByVal Other As UserDataBase) As Integer
|
||||||
|
If TypeOf Other Is UserDataBind Then
|
||||||
|
Return CollectionName.CompareTo(Other.CollectionName)
|
||||||
|
Else
|
||||||
|
Return -1
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
#End Region
|
||||||
#Region "IEquatable support"
|
#Region "IEquatable support"
|
||||||
Friend Overrides Function Equals(ByVal Other As UserDataBase) As Boolean
|
Friend Overrides Function Equals(ByVal Other As UserDataBase) As Boolean
|
||||||
If Other.IsCollection Then
|
If Other.IsCollection Then
|
||||||
|
|||||||
@@ -84,10 +84,15 @@ Namespace API.Xhamster
|
|||||||
End Function
|
End Function
|
||||||
Private Shared Function ObtainUrls(ByVal URL As String, ByVal Responser As Responser, ByVal UHD As Boolean) As List(Of M3U8URL)
|
Private Shared Function ObtainUrls(ByVal URL As String, ByVal Responser As Responser, ByVal UHD As Boolean) As List(Of M3U8URL)
|
||||||
Try
|
Try
|
||||||
|
Const sk$ = "/key="
|
||||||
Dim file$ = ParseFirstM3U8(URL, Responser, UHD)
|
Dim file$ = ParseFirstM3U8(URL, Responser, UHD)
|
||||||
If Not file.IsEmptyString Then
|
If Not file.IsEmptyString Then
|
||||||
Responser.UseGZipStream = False
|
Responser.UseGZipStream = False
|
||||||
Dim appender$ = URL.Replace(URL.Split("/").LastOrDefault, String.Empty)
|
Dim appender$ = URL.Replace(URL.Split("/").LastOrDefault, String.Empty)
|
||||||
|
If file.StartsWith(sk) Then
|
||||||
|
Dim position% = InStr(URL, sk)
|
||||||
|
If position > 0 Then appender = URL.Remove(position - 1)
|
||||||
|
End If
|
||||||
URL = M3U8Base.CreateUrl(appender, file)
|
URL = M3U8Base.CreateUrl(appender, file)
|
||||||
Dim l As List(Of M3U8URL) = ParseSecondM3U8(URL, Responser, appender)
|
Dim l As List(Of M3U8URL) = ParseSecondM3U8(URL, Responser, appender)
|
||||||
If l.ListExists Then Return l
|
If l.ListExists Then Return l
|
||||||
|
|||||||