Compare commits
30 Commits
2024.2.25.
...
2024.8.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
26db0e3e24 | ||
|
|
0b0933b6f0 | ||
|
|
3ce9c55575 | ||
|
|
ef36a11566 | ||
|
|
dea14d35af | ||
|
|
744698c99e | ||
|
|
aef4ce1c8f | ||
|
|
93ea2a55ac | ||
|
|
2ae8c3acfc | ||
|
|
53dcb3e2c6 | ||
|
|
ca384e54d6 | ||
|
|
5a1b5c828a | ||
|
|
22c59b41f0 | ||
|
|
444b3521eb | ||
|
|
ec2266f1bf | ||
|
|
7d9255c916 | ||
|
|
5b5857e31d | ||
|
|
46372ec9fb | ||
|
|
7296fda977 | ||
|
|
5f90bf6a99 | ||
|
|
718eccc3c3 | ||
|
|
efa09fb457 | ||
|
|
b252d32a7e | ||
|
|
34cd510507 | ||
|
|
f5dd791941 | ||
|
|
2a2c12c651 | ||
|
|
2bacc17ac4 | ||
|
|
7a68067d77 | ||
|
|
d1eacc2db2 | ||
|
|
3f38803643 |
@@ -8,9 +8,10 @@ I welcome requests! Follow these steps to contribute:
|
||||
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 a **profile URL** that you cannot download.
|
||||
1. Attach the **profile URLs or links** that you cannot download.
|
||||
1. Attach the **LOG** if it exists.
|
||||
1. **Attach information to the issue with data copied from SCrawler (click the top right info button in the main window, then the `Environment` button, then the `Copy` button, and paste the copied text into the issue).**
|
||||
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
|
||||
1. Delete the `PersonalUtilities` project from the solution.
|
||||
|
||||
247
Changelog.md
@@ -1,3 +1,250 @@
|
||||
# 2024.8.1.0
|
||||
|
||||
*2024-08-01*
|
||||
|
||||
- Added
|
||||
- Minor improvements
|
||||
- Updated
|
||||
- yt-dlp up to version **2024.08.01**
|
||||
|
||||
# 2024.7.24.0
|
||||
|
||||
*2024-07-24*
|
||||
|
||||
- Added
|
||||
- YouTube (standalone app)
|
||||
- ability to convert non-`AVC` codecs (eg `VP9`) to `AVC` (`Settings` - `Defaults Video` - `Convert non-AVC codecs to AVC`)
|
||||
- add the ability to set the playlist creation mode: absolute links, relative links, or both (`Settings` - `Music` - `Create M3U8: creation mode`)
|
||||
- Threads: **saved posts downloading**
|
||||
- Feed
|
||||
- hotkeys `Esc` and `Ctrl+W` to close the form
|
||||
- the ability to search for missing files in *special feeds*
|
||||
- Scheduler: the ability to execute a script after the scheduler plan is executed *(`Settings` - `Behavior`)*
|
||||
- Main window:
|
||||
- added hotkey `Ctrl+F` to show the feed
|
||||
- changed the hotkey from `Ctrl+F` to `Alt+F` to show the search form
|
||||
- Updated
|
||||
- yt-dlp up to version **2024.07.16**
|
||||
- Fixed
|
||||
- YouTube (standalone app): video files with line breaks in the name do not download correctly
|
||||
- OnlyFans: rules parsing bug
|
||||
- Minor bugs
|
||||
|
||||
# 2024.6.25.0
|
||||
|
||||
*2024-06-25*
|
||||
|
||||
**ATTENTION! To support downloading of DRM protected videos (OnlyFans), please update OF-Scraper to version [3.10.7](https://github.com/datawhores/OF-Scraper/releases/tag/3.10.7) (download `zip`, not `exe`).**
|
||||
|
||||
- Added
|
||||
- OnlyFans: **new dynamic rules updating algorithm**
|
||||
- Feed: ability to set the last session as the current one
|
||||
- Updated
|
||||
- gallery-dl up to version **1.27.1**
|
||||
- Fixed
|
||||
- Minor bugs
|
||||
|
||||
# 2024.6.10.0
|
||||
|
||||
*2024-06-10*
|
||||
|
||||
- Added
|
||||
- YouTube (standalone app): add option to add extracted MP3 to playlist (`Settings` - `Defaults Video` - `Add extracted MP3 to playlist`)
|
||||
- Feed
|
||||
- settings to show/hide site name and file type from media title
|
||||
- ability to move/copy files of a loaded feed/session to another location
|
||||
- ability to reset current session
|
||||
- Fixed
|
||||
- Minor bugs
|
||||
|
||||
# 2024.6.6.0
|
||||
|
||||
*2024-06-06*
|
||||
|
||||
**ATTENTION!**
|
||||
1. **To support downloading of DRM protected videos (OnlyFans), please update OF-Scraper to version [3.10](https://github.com/datawhores/OF-Scraper/releases/tag/3.10) (download `zip`, not `exe`).**
|
||||
2. **If there is a `OFScraperConfigPattern.json` file in the SCrawler settings folder, replace the text of the file with [this text](https://github.com/AAndyProgram/SCrawler/blob/main/SCrawler/API/OnlyFans/OFScraperConfigPattern.json).**
|
||||
3. **Set the value to `Dynamic rules` (in the site settings) = `https://raw.githubusercontent.com/Growik/onlyfans-dynamic-rules/main/rules.json`.**
|
||||
|
||||
- Added
|
||||
- OnlyFans: new OF-Scraper option (`keydb_api`)
|
||||
- Minor improvements
|
||||
- Fixed
|
||||
- OnlyFans: **data is not downloading**
|
||||
- Minor bugs
|
||||
|
||||
# 2024.6.4.0
|
||||
|
||||
*2024-06-04*
|
||||
|
||||
**If you were using the [`yt-dlp-TTUser`](https://github.com/bashonly/yt-dlp-TTUser) plugin, you should remove it because this plugin was added to yt-dlp itself! Read more [here](https://github.com/AAndyProgram/SCrawler/wiki/Settings#tiktok-requirements).**
|
||||
|
||||
- Added
|
||||
- Added highlighting of scheduler plans (working, stopped, pending, etc.)
|
||||
- YouTube (standalone app): add option to add the video upload date before/after the file name (`Settings` - `Defaults` - `Add date to file name`)
|
||||
- Twitter: **`Communities` downloading**
|
||||
- Feed: ability to select one of the download sessions and set it as the current session
|
||||
- Minor improvements
|
||||
- Updated
|
||||
- yt-dlp up to version **2024.05.27**
|
||||
- gallery-dl up to version **1.27.0**
|
||||
- Fixed
|
||||
- Twitter: deleting user directory when redownloading missing posts
|
||||
- Minor bugs
|
||||
|
||||
# 2024.5.19.0
|
||||
|
||||
*2024-05-19*
|
||||
|
||||
- Added
|
||||
- YouTube (standalone app): add upload date to description (request #192) (`Settings` - `Info` - `Create description files: add upload date`, `Create description files: create without description`).
|
||||
- Fixed
|
||||
- YouTube (SCrawler): advanced settings are not saved when changed
|
||||
|
||||
# 2024.5.18.0
|
||||
|
||||
*2024-05-18*
|
||||
|
||||
- Added
|
||||
- YouTube (standalone app): highlight frame rates higher/lower than this value (`Settings` - `Defaults Video` - `Highlight FPS (higher/lower)`).
|
||||
- Sites
|
||||
- Instagram: 'DownDetector' support to determine if the site is accessible
|
||||
- Reddit: change the naming method of video files (hosted on Reddit) to the `YYYYMMDD_HHMMSS` pattern
|
||||
- Twitter
|
||||
- `Likes` downloading *(user settings)*
|
||||
- **changed domain from twitter.com to x.com**
|
||||
- Site settings: group options by category
|
||||
- Minor improvements
|
||||
- PluginProvider
|
||||
- `PropertyOption` attribute: set category name when `IsAuth = True`
|
||||
- `ISiteSettings`: added `UserAgentDefault` property
|
||||
- Updated
|
||||
- gallery-dl up to version **1.27.0-dev**
|
||||
- Fixed
|
||||
- Sites
|
||||
- Instagram: incorrect definition of pinned posts
|
||||
- Threads: new posts are no longer downloaded from profiles with pinned posts
|
||||
- Reddit: bypass error 429 for saved posts
|
||||
- Twitter: **data is not downloading due to domain change from twitter.com to x.com**
|
||||
- Minor bugs
|
||||
|
||||
# 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 `Friendly name` 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*
|
||||
|
||||
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 78 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: 18 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 24 KiB |
BIN
ProgramScreenshots/SettingsGlobalHeaders.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 29 KiB |
BIN
ProgramScreenshots/SettingsSiteOnlyFansAdditional.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 25 KiB |
26
README.md
@@ -1,5 +1,5 @@
|
||||
<!-- # :rainbow_flag: Happy LGBT Pride Month :tada:
|
||||
-->
|
||||
# 🏳️🌈 Happy LGBT Pride Month 🎉
|
||||
|
||||
# 🏳️🌈 Social networks crawler 🏳️🌈
|
||||
|
||||
[](https://github.com/AAndyProgram/SCrawler/releases/latest)
|
||||
@@ -33,16 +33,16 @@ A program to download photo and video from [any site](#supported-sites) (e.g. Yo
|
||||

|
||||
|
||||
# What can program do:
|
||||
- Download pictures and videos from users' profiles and subreddits:
|
||||
- Download pictures and videos from user profiles:
|
||||
- YouTube videos, shorts, community feeds, users, artists, playlists, music, tracks;
|
||||
- Reddit images, galleries of images, videos, saved posts;
|
||||
- Redgifs videos (https://www.redgifs.com/);
|
||||
- Twitter images and videos, saved (bookmarked) posts;
|
||||
- OnlyFans images and videos, saved (bookmarked) posts;
|
||||
- Redgifs images and videos (https://www.redgifs.com/);
|
||||
- Twitter images and videos, saved (bookmarked) posts, likes, communities;
|
||||
- OnlyFans images and videos, saved (bookmarked) posts, stories;
|
||||
- JustForFans images and videos, saved (bookmarked) posts;
|
||||
- Mastodon images and videos, saved (bookmarked) posts;
|
||||
- Instagram images and videos, tagged posts, stories, saved posts;
|
||||
- Threads images and videos;
|
||||
- Threads images and videos, saved posts;
|
||||
- Facebook images and videos, stories, saved posts;
|
||||
- TikTok videos;
|
||||
- Pinterest boards, users, saved posts;
|
||||
@@ -57,7 +57,7 @@ A program to download photo and video from [any site](#supported-sites) (e.g. Yo
|
||||
- Download [saved posts](https://github.com/AAndyProgram/SCrawler/wiki/Home#saved-posts)
|
||||
- Add users from parsed channel
|
||||
- **Advanced user management**
|
||||
- **Automation** ([downloading data automatically](https://github.com/AAndyProgram/SCrawler/wiki/Settings#automation) every ```X``` minutes)
|
||||
- **Automation** ([downloading data automatically](https://github.com/AAndyProgram/SCrawler/wiki/Settings#automation) every `X` minutes)
|
||||
- **Feed** ([feed](https://github.com/AAndyProgram/SCrawler/wiki#feed) of downloaded media files and subscriptions posts)
|
||||
- Multiple accounts support
|
||||
- Labeling users
|
||||
@@ -79,15 +79,15 @@ A program to download photo and video from [any site](#supported-sites) (e.g. Yo
|
||||
- **Reddit**
|
||||
- **Twitter**
|
||||
- **OnlyFans** *(partial support)*[^1]
|
||||
- **Mastodon**
|
||||
- **Instagram**
|
||||
- **Threads**
|
||||
- **Facebook**
|
||||
- JustForFans *(partial support)*[^1]
|
||||
- Mastodon *(out of support)*
|
||||
- TikTok
|
||||
- RedGifs
|
||||
- Pinterest
|
||||
- Imgur
|
||||
- Imgur *(out of support)*
|
||||
- Gfycat
|
||||
- LPSG
|
||||
- **PornHub**
|
||||
@@ -109,7 +109,7 @@ First, the program downloads the full profile. After the program downloads only
|
||||
|
||||
# Requirements
|
||||
|
||||
- Windows 10, 11 with NET Framework 4.6.1 or higher (v4.6.1 must be installed). You can check version compatibility with this [tool](Tools/NET.FrameworkVersion.ps1).
|
||||
- **Windows 10, 11** with NET Framework 4.6.1 or higher (v4.6.1 must be installed). You can check version compatibility with this [tool](Tools/NET.FrameworkVersion.ps1).
|
||||
- **[SITES REQUIREMENTS](https://github.com/AAndyProgram/SCrawler/wiki/Settings#sites-requirements)**
|
||||
|
||||
# Guide
|
||||
@@ -157,7 +157,7 @@ First, the program downloads the full profile. After the program downloads only
|
||||
|
||||
**Just download the [latest release](https://github.com/AAndyProgram/SCrawler/releases/latest), unzip the program archive to any folder and enjoy.** :blush:
|
||||
|
||||
**Don't put program in the ```Program Files``` system folder (this is portable program and program settings are stored in the program folder)**
|
||||
**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.**
|
||||
|
||||
@@ -183,7 +183,7 @@ The program has an intuitive interface.
|
||||
|
||||
[](https://www.youtube.com/watch?v=XDn7zG4I700)
|
||||
|
||||
Just add a user profile and **click the ```Download``` button**.
|
||||
Just add a user profile and **click the `Download` button**.
|
||||
|
||||
```mermaid
|
||||
stateDiagram
|
||||
|
||||
@@ -36,8 +36,24 @@ Namespace Plugin.Attributes
|
||||
Public Property IsInformationLabel As Boolean = False
|
||||
''' <summary>Label text alignment.<br/>Default: <see cref="Drawing.ContentAlignment.TopCenter"/></summary>
|
||||
Public Property LabelTextAlign As Drawing.ContentAlignment = Drawing.ContentAlignment.TopCenter
|
||||
Private _IsAuth As Boolean = False
|
||||
''' <summary>This is an authorization property</summary>
|
||||
Public Property IsAuth As Boolean = False
|
||||
Public Property IsAuth As Boolean
|
||||
Get
|
||||
Return _IsAuth
|
||||
End Get
|
||||
Set(ByVal _IsAuth As Boolean)
|
||||
Me._IsAuth = _IsAuth
|
||||
If _IsAuth And String.IsNullOrEmpty(Category) Then
|
||||
Category = CategoryAuth
|
||||
ElseIf Not _IsAuth AndAlso Not String.IsNullOrEmpty(Category) AndAlso Category = CategoryAuth Then
|
||||
Category = String.Empty
|
||||
End If
|
||||
End Set
|
||||
End Property
|
||||
Public Const CategoryAuth As String = "Authorization"
|
||||
Public Property Category As String = Nothing
|
||||
Public Property InheritanceName As String = Nothing
|
||||
''' <summary>Initialize a new property option attribute</summary>
|
||||
''' <param name="PropertyName">Property name</param>
|
||||
Public Sub New(<CallerMemberName()> Optional ByVal PropertyName As String = Nothing)
|
||||
@@ -57,6 +73,7 @@ Namespace Plugin.Attributes
|
||||
''' <summary>Store property value in settings XML file</summary>
|
||||
<AttributeUsage(AttributeTargets.Property, AllowMultiple:=False, Inherited:=False)> Public NotInheritable Class PXML : Inherits Attribute
|
||||
Public ReadOnly ElementName As String
|
||||
Public Property OnlyForChecked As Boolean = False
|
||||
''' <summary>Initialize a new XML attribute</summary>
|
||||
''' <param name="XMLElementName">XML element name</param>
|
||||
Public Sub New(<CallerMemberName()> Optional ByVal XMLElementName As String = Nothing)
|
||||
|
||||
@@ -40,5 +40,6 @@ Namespace Plugin
|
||||
Sub GetMedia(ByVal Token As Threading.CancellationToken)
|
||||
Sub Download(ByVal Token As Threading.CancellationToken)
|
||||
Sub DownloadSingleObject(ByVal Data As IDownloadableMedia, ByVal Token As Threading.CancellationToken)
|
||||
Sub ResetHistoryData()
|
||||
End Interface
|
||||
End Namespace
|
||||
@@ -17,6 +17,10 @@ Namespace Plugin
|
||||
ReadOnly Property Icon As Icon
|
||||
ReadOnly Property Image As Image
|
||||
ReadOnly Property Site As String
|
||||
Property CMDEncoding As String
|
||||
Property EnvironmentPrograms As IEnumerable(Of String)
|
||||
Property UserAgentDefault As String
|
||||
Sub EnvironmentProgramsUpdated()
|
||||
Property AccountName As String
|
||||
Property Temporary As Boolean
|
||||
Property DefaultInstance As ISiteSettings
|
||||
|
||||
@@ -32,6 +32,6 @@ Imports System.Runtime.InteropServices
|
||||
' by using the '*' as shown below:
|
||||
' <Assembly: AssemblyVersion("1.0.*")>
|
||||
|
||||
<Assembly: AssemblyVersion("2024.2.25.0")>
|
||||
<Assembly: AssemblyFileVersion("2024.2.25.0")>
|
||||
<Assembly: AssemblyVersion("2024.5.18.0")>
|
||||
<Assembly: AssemblyFileVersion("2024.5.18.0")>
|
||||
<Assembly: NeutralResourcesLanguage("en")>
|
||||
|
||||
@@ -9,8 +9,23 @@
|
||||
Namespace Plugin
|
||||
Public NotInheritable Class PropertyValue : Implements IPropertyValue
|
||||
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 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
|
||||
''' <inheritdoc cref="PropertyValue.New(Object, Type, ByRef IPropertyValue.ValueChangedEventHandler)"/>
|
||||
''' <exception cref="ArgumentNullException"></exception>
|
||||
@@ -59,6 +74,7 @@ Namespace Plugin
|
||||
Type = Source.Type
|
||||
OnChangeFunction = Source.OnChangeFunction
|
||||
_Value = Source._Value
|
||||
_Checked = Source._Checked
|
||||
_Initialization = False
|
||||
End Sub
|
||||
End Class
|
||||
@@ -71,4 +87,8 @@ Namespace Plugin
|
||||
''' <summary>Property value</summary>
|
||||
Property Value As Object
|
||||
End Interface
|
||||
Public Class PropertyValueEventArgs : Inherits EventArgs
|
||||
Public Property Checked As Boolean = False
|
||||
Public Property ControlEnabled As Boolean = True
|
||||
End Class
|
||||
End Namespace
|
||||
@@ -78,9 +78,21 @@ Namespace API.YouTube.Base
|
||||
https = 1
|
||||
m3u8 = 2
|
||||
End Enum
|
||||
<Editor(GetType(EnumDropDownEditor), GetType(UITypeEditor))>
|
||||
Public Enum FileDateMode As Integer
|
||||
None = 0
|
||||
Before = 1
|
||||
After = 2
|
||||
End Enum
|
||||
Public Enum M3U8CreationMode As Integer
|
||||
Relative = 0
|
||||
Absolute = 1
|
||||
Both = 2
|
||||
End Enum
|
||||
Public Structure MediaObject : Implements IIndexable, IComparable(Of MediaObject)
|
||||
Public Type As Plugin.UserMediaTypes
|
||||
Public ID As String
|
||||
Public ID_DRC As Boolean
|
||||
Public Extension As String
|
||||
Public Width As Integer
|
||||
Public Height As Integer
|
||||
@@ -110,10 +122,14 @@ Namespace API.YouTube.Base
|
||||
End Function
|
||||
Private Function CompareTo(ByVal Other As MediaObject) As Integer Implements IComparable(Of MediaObject).CompareTo
|
||||
If Type = Other.Type Then
|
||||
If Width.CompareTo(Other.Width) = 0 Then
|
||||
Return Size.CompareTo(Other.Size) * -1
|
||||
If ID_DRC.CompareTo(Other.ID_DRC) = 0 Then
|
||||
If Width.CompareTo(Other.Width) = 0 Then
|
||||
Return Size.CompareTo(Other.Size) * -1
|
||||
Else
|
||||
Return Width.CompareTo(Other.Width) * -1
|
||||
End If
|
||||
Else
|
||||
Return Width.CompareTo(Other.Width) * -1
|
||||
Return ID_DRC.CompareTo(Other.ID_DRC)
|
||||
End If
|
||||
Else
|
||||
Return CInt(Type).CompareTo(CInt(Other.Type))
|
||||
|
||||
@@ -22,6 +22,7 @@ Namespace API.YouTube.Base
|
||||
End Sub
|
||||
Public Shared Function StandardizeURL(ByVal URL As String) As String
|
||||
Try
|
||||
URL = URL.StringTrim
|
||||
Dim isMusic As Boolean = False, isShorts As Boolean = False
|
||||
If Info_GetUrlType(URL, isMusic, isShorts) = YouTubeMediaType.Single Then
|
||||
If Not isMusic And Not isShorts Then
|
||||
@@ -45,6 +46,7 @@ Namespace API.YouTube.Base
|
||||
End Function
|
||||
Public Shared Function StandardizeURL_Channel(ByVal URL As String, Optional ByVal Process As Boolean = True) As String
|
||||
Try
|
||||
URL = URL.StringTrim
|
||||
Dim ct As YouTubeChannelTab = YouTubeChannelTab.All
|
||||
Dim isMusic As Boolean = False
|
||||
If Process AndAlso Info_GetUrlType(URL, isMusic,,,, ct) = YouTubeMediaType.Channel AndAlso Not isMusic Then
|
||||
@@ -72,6 +74,7 @@ Namespace API.YouTube.Base
|
||||
Public Shared Function Info_GetUrlType(ByVal URL As String, Optional ByRef IsMusic As Boolean = False, Optional ByRef IsShorts As Boolean = False,
|
||||
Optional ByRef IsChannelUser As Boolean = False, Optional ByRef Id As String = Nothing,
|
||||
Optional ByRef ChannelOptions As YouTubeChannelTab = YouTubeChannelTab.All) As YouTubeMediaType
|
||||
URL = URL.StringTrim
|
||||
If Not URL.IsEmptyString Then
|
||||
IsMusic = URL.Contains("music.youtube.com")
|
||||
IsChannelUser = False
|
||||
@@ -118,6 +121,7 @@ Namespace API.YouTube.Base
|
||||
Optional ByVal Token As Threading.CancellationToken = Nothing, Optional ByVal Progress As IMyProgress = Nothing,
|
||||
Optional ByVal DateAfter As Date? = Nothing, Optional ByVal DateBefore As Date? = Nothing,
|
||||
Optional ByVal ChannelOption As YouTubeChannelTab? = Nothing, Optional ByVal UrlAsIs As Boolean = False) As IYouTubeMediaContainer
|
||||
URL = URL.StringTrim
|
||||
If URL.IsEmptyString Then Throw New ArgumentNullException("URL", "URL cannot be null")
|
||||
If Not MyYouTubeSettings.YTDLP.Value.Exists Then Throw New IO.FileNotFoundException("Path to 'yt-dlp.exe' not set or program not found at destination", MyYouTubeSettings.YTDLP.Value.ToString)
|
||||
Dim urlOrig$ = URL
|
||||
@@ -162,7 +166,7 @@ Namespace API.YouTube.Base
|
||||
|
||||
If result Then
|
||||
container.Parse(Nothing, _CachePathDefault, isMusic, Token, Progress)
|
||||
If Not container.HasError Then container.URL = URL : container.IsShorts = isShorts : Return container
|
||||
If Not container.HasError Then container.URL = URL.ToMusicUrl(isMusic) : container.IsShorts = isShorts : Return container
|
||||
End If
|
||||
container.Dispose()
|
||||
End If
|
||||
@@ -174,7 +178,7 @@ Namespace API.YouTube.Base
|
||||
ByVal ObjType As YouTubeMediaType, ByVal ChannelTab As YouTubeChannelTab,
|
||||
ByVal IsMusic As Boolean, ByVal UrlAsIs As Boolean) As Boolean
|
||||
Try
|
||||
Dim command$ = "yt-dlp --write-info-json --skip-download"
|
||||
Dim command$ = $"{YTDLP_NAME} --write-info-json --skip-download"
|
||||
command.StringAppend(GetCookiesCommand(UseCookies, CookiesFile), " ")
|
||||
If DateAfter.HasValue Then command.StringAppend($"--dateafter {DateAfter.Value:yyyyMMdd}", " ")
|
||||
If DateBefore.HasValue Then command.StringAppend($"--datebefore {DateBefore.Value:yyyyMMdd}", " ")
|
||||
|
||||
@@ -36,7 +36,9 @@ Namespace API.YouTube.Base
|
||||
<Browsable(False)> Private Property Mode As GridUpdateModes = GridUpdateModes.OnConfirm Implements IGridValuesContainer.Mode
|
||||
<Browsable(False), XMLVV(-1)> Friend ReadOnly Property PlaylistFormSplitterDistance As XMLValue(Of Integer)
|
||||
<Browsable(False)> Friend ReadOnly Property DownloadLocations As DownloadLocationsCollection
|
||||
<Browsable(False)> Friend ReadOnly Property PlaylistsLocations As DownloadLocationsCollection
|
||||
<Browsable(False)> Public Overridable Property AccountName As String
|
||||
<Browsable(False), XMLVV(0)> Private ReadOnly Property SettingsVersion As XMLValue(Of Integer)
|
||||
#Region "Environment"
|
||||
#Region "Programs"
|
||||
<Browsable(True), GridVisible(False), XMLVN({"Environment"}), Category("Environment programs"), DisplayName("Path to yt-dlp.exe"),
|
||||
@@ -84,6 +86,18 @@ Namespace API.YouTube.Base
|
||||
Description("The default output path where files should be downloaded."),
|
||||
Editor(GetType(GridSFileTypeEditorPath), GetType(UITypeEditor))>
|
||||
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"),
|
||||
Description("Automatically change the output path when a new destination is selected in the opening forms.")>
|
||||
Public ReadOnly Property OutputPathAutoChange As XMLValue(Of Boolean)
|
||||
@@ -117,22 +131,28 @@ Namespace API.YouTube.Base
|
||||
<Browsable(True), GridVisible(False), Category("EnvironmentFolder"), DisplayName("Open folders in another program"), DefaultValue(False)>
|
||||
Private Property IDownloaderSettings_OpenFolderInOtherProgram As Boolean Implements IDownloaderSettings.OpenFolderInOtherProgram
|
||||
Get
|
||||
Return OpenFolderInOtherProgram.Use
|
||||
Return OpenFolderInOtherProgram.Attribute.ValueTemp
|
||||
End Get
|
||||
Set(ByVal use As Boolean)
|
||||
OpenFolderInOtherProgram.Use = use
|
||||
OpenFolderInOtherProgram.Attribute.ValueTemp = use
|
||||
End Set
|
||||
End Property
|
||||
Private Function ShouldSerializeIDownloaderSettings_OpenFolderInOtherProgram() As Boolean
|
||||
Return DirectCast(OpenFolderInOtherProgram.Attribute, IGridValue).ShouldSerializeValue
|
||||
End Function
|
||||
<Browsable(True), GridVisible(False), Category("EnvironmentFolder"), DisplayName("Open folders in another program (command)"),
|
||||
Description("The command to open a folder."), DefaultValue("")>
|
||||
Private Property IDownloaderSettings_OpenFolderInOtherProgram_Command As String Implements IDownloaderSettings.OpenFolderInOtherProgram_Command
|
||||
Get
|
||||
Return OpenFolderInOtherProgram
|
||||
Return OpenFolderInOtherProgram.ValueTemp
|
||||
End Get
|
||||
Set(ByVal command As String)
|
||||
OpenFolderInOtherProgram.Value = command
|
||||
OpenFolderInOtherProgram.ValueTemp = command
|
||||
End Set
|
||||
End Property
|
||||
Private Function ShouldSerializeIDownloaderSettings_OpenFolderInOtherProgram_Command() As Boolean
|
||||
Return DirectCast(OpenFolderInOtherProgram, IGridValue).ShouldSerializeValue
|
||||
End Function
|
||||
<Browsable(True), GridVisible(False), XMLVN({"Environment"}, True), Category("Environment"), DisplayName("Check new version at start")>
|
||||
Friend ReadOnly Property CheckUpdatesAtStart As XMLValue(Of Boolean)
|
||||
#End Region
|
||||
@@ -148,6 +168,12 @@ Namespace API.YouTube.Base
|
||||
<Browsable(True), GridVisible, XMLVN({"Info"}), Category("Info"), DisplayName("Create description files"),
|
||||
Description("Create video description files. Default: false.")>
|
||||
Public ReadOnly Property CreateDescriptionFiles As XMLValue(Of Boolean)
|
||||
<Browsable(True), GridVisible, XMLVN({"Info"}, True), Category("Info"), DisplayName("Create description files: add upload date"),
|
||||
Description("Add the upload date to the top of the description file. Default: true.")>
|
||||
Public ReadOnly Property CreateDescriptionFiles_AddUploadDate As XMLValue(Of Boolean)
|
||||
<Browsable(True), GridVisible, XMLVN({"Info"}, True), Category("Info"), DisplayName("Create description files: create without description"),
|
||||
Description("Create a description file with the upload date, even if the description does not exist. Default: true.")>
|
||||
Public ReadOnly Property CreateDescriptionFiles_CreateWithNoDescription As XMLValue(Of Boolean)
|
||||
<Browsable(True), GridVisible, XMLVN({"Info"}, True), Category("Info"), DisplayName("Create thumbnail files (video)"),
|
||||
Description("Create video thumbnail files. Default: true.")>
|
||||
Public ReadOnly Property CreateThumbnails_Video As XMLValue(Of Boolean)
|
||||
@@ -229,6 +255,18 @@ Namespace API.YouTube.Base
|
||||
<Browsable(True), GridVisible(False), XMLVN({"Defaults"}), Category("Defaults"), DisplayName("Program description"),
|
||||
Description("Add some additional info to the program info if you need")>
|
||||
Friend ReadOnly Property ProgramDescription As XMLValue(Of String)
|
||||
<Browsable(True), GridVisible, XMLVN({"Defaults"}, "%"""), Category("Defaults"), DisplayName("Remove characters"),
|
||||
Description("Remove specific characters from a file name")>
|
||||
Public ReadOnly Property FileRemoveCharacters As XMLValue(Of String)
|
||||
<Browsable(True), GridVisible, XMLVN({"Defaults"}, FileDateMode.None), Category("Defaults"), DisplayName("Add date to file name"),
|
||||
Description("Add the video upload date before/after the file name")>
|
||||
Public ReadOnly Property FileAddDateToFileName As XMLValue(Of FileDateMode)
|
||||
<Browsable(True), GridVisible, XMLVN({"Defaults"}), Category("Defaults"), DisplayName("Add date to title: video form"),
|
||||
Description("Add video upload date before video title (visual only) in the video form")>
|
||||
Public ReadOnly Property FileAddDateToFileName_VideoForm As XMLValue(Of Boolean)
|
||||
<Browsable(True), GridVisible, XMLVN({"Defaults"}), Category("Defaults"), DisplayName("Add date to title: video list"),
|
||||
Description("Add video upload date before video title (visual only) in the video list")>
|
||||
Public ReadOnly Property FileAddDateToFileName_VideoList As XMLValue(Of Boolean)
|
||||
#End Region
|
||||
#Region "Defaults ChannelsDownload"
|
||||
<Browsable(True), GridVisible, XMLVN({"Defaults", "Channels"}), Category("Defaults"), DisplayName("Default download tabs for channels"),
|
||||
@@ -273,6 +311,9 @@ Namespace API.YouTube.Base
|
||||
<Browsable(True), GridVisible, XMLVN({"DefaultsVideo"}, 1080), Category("Defaults Video"), DisplayName("Default definition"),
|
||||
Description("The default maximum video resolution. -1 for max definition")>
|
||||
Public ReadOnly Property DefaultVideoDefinition As XMLValue(Of Integer)
|
||||
<Browsable(True), GridVisible, XMLVN({"DefaultsVideo"}), Category("Defaults Video"), DisplayName("Convert non-AVC codecs to AVC"),
|
||||
Description("Convert non-AVC codecs (eg 'VP9') to AVC. Not recommended due to high CPU usage!")>
|
||||
Public ReadOnly Property DefaultVideoConvertNonAVC As XMLValue(Of Boolean)
|
||||
<Browsable(True), GridVisible, XMLVN({"DefaultsVideo"}, False), Category("Defaults Video"), DisplayName("Embed thumbnail (video)"),
|
||||
Description("Embed thumbnail in the video as cover art. Default: true.")>
|
||||
Public ReadOnly Property DefaultVideoEmbedThumbnail As XMLValue(Of Boolean)
|
||||
@@ -344,6 +385,15 @@ Namespace API.YouTube.Base
|
||||
Throw New NotImplementedException("'GetFormat' is not available in 'FpsFormatProvider'")
|
||||
End Function
|
||||
End Class
|
||||
<Browsable(True), GridVisible, XMLVN({"DefaultsVideo"}, 30), Category("Defaults Video"), DisplayName("Highlight FPS (higher)"),
|
||||
Description("Highlight frame rates higher than this value." & vbCr & "Default: 30" & vbCr & "-1 to disable")>
|
||||
Public ReadOnly Property DefaultVideoHighlightFPS_H As XMLValue(Of Integer)
|
||||
<Browsable(True), GridVisible, XMLVN({"DefaultsVideo"}, -1), Category("Defaults Video"), DisplayName("Highlight FPS (lower)"),
|
||||
Description("Highlight frame rates lower than this value." & vbCr & "Default: -1" & vbCr & "-1 to disable")>
|
||||
Public ReadOnly Property DefaultVideoHighlightFPS_L As XMLValue(Of Integer)
|
||||
<Browsable(True), GridVisible, XMLVN({"DefaultsVideo"}), Category("Defaults Video"), DisplayName("Add extracted MP3 to playlist"),
|
||||
Description("If you also extract MP3 when download the video, add the extracted MP3 to the playlist. Default: false.")>
|
||||
Public ReadOnly Property VideoPlaylist_AddExtractedMP3 As XMLValue(Of Boolean)
|
||||
#End Region
|
||||
#Region "Defaults Audio"
|
||||
<Browsable(True), GridVisible, XMLVN({"DefaultsAudio"}, "AAC"), Category("Defaults Audio"), DisplayName("Default codec"),
|
||||
@@ -366,6 +416,35 @@ Namespace API.YouTube.Base
|
||||
<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)
|
||||
<Browsable(True), GridVisible, XMLVN({"Playlists"}, M3U8CreationMode.Relative), Category("Music"), DisplayName("Create M3U8: creation mode"),
|
||||
Description("Set the playlist creation mode: absolute links, relative links, or both. If 'Both' is selected, two playlists will be created. Default: 'Relative'.")>
|
||||
Public ReadOnly Property MusicPlaylistCreate_CreationMode As XMLValue(Of M3U8CreationMode)
|
||||
#End Region
|
||||
#End Region
|
||||
#Region "Defaults Subtitles"
|
||||
<XMLVN({"DefaultsSubtitles"}, {"en"}, CollectionMode:=IXMLValuesCollection.Modes.String)>
|
||||
@@ -418,7 +497,9 @@ Namespace API.YouTube.Base
|
||||
Public Sub New(ByVal AccountName As String)
|
||||
Me.AccountName = AccountName
|
||||
DownloadLocations = New DownloadLocationsCollection
|
||||
PlaylistsLocations = New DownloadLocationsCollection
|
||||
DownloadLocations.Load(False, True)
|
||||
PlaylistsLocations.Load(True, True, $"{XmlFile.SettingsFolder}\DownloadLocations_Playlists.xml")
|
||||
Dim acc$ = String.Empty
|
||||
If Not AccountName.IsEmptyString Then acc = $"_{AccountName}"
|
||||
Dim f As SFile = YouTubeSettingsFile
|
||||
|
||||
23
SCrawler.YouTube/Controls/ButtonRC.vb
Normal file
@@ -0,0 +1,23 @@
|
||||
' Copyright (C) Andy https://github.com/AAndyProgram
|
||||
' This program is free software: you can redistribute it and/or modify
|
||||
' it under the terms of the GNU General Public License as published by
|
||||
' the Free Software Foundation, either version 3 of the License, or
|
||||
' (at your option) any later version.
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Namespace API.YouTube.Controls
|
||||
Public Class ButtonRC : Inherits Button
|
||||
Private Const WM_CONTEXTMENU As Integer = 123 '&H7B
|
||||
Private Const WM_CANCELMODE As Integer = 31 '&H1F
|
||||
Private Const WM_INITMENUPOPUP As Integer = 279 '&H117
|
||||
Private Const SMTO_NOTIMEOUTIFNOTHUNG As Integer = 8
|
||||
Protected Overrides Sub WndProc(ByRef m As Message)
|
||||
If m.Msg = WM_CONTEXTMENU Or m.Msg = WM_CANCELMODE Or m.Msg = WM_INITMENUPOPUP Or m.Msg = SMTO_NOTIMEOUTIFNOTHUNG Then
|
||||
m.Result = IntPtr.Zero
|
||||
Else
|
||||
MyBase.WndProc(m)
|
||||
End If
|
||||
End Sub
|
||||
End Class
|
||||
End Namespace
|
||||
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 ActionButton9 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||
Dim ActionButton10 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||
Dim ActionButton11 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||
Dim 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 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
|
||||
Me.BTT_DOWN = 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.CH_DOWN_LYRICS = New System.Windows.Forms.CheckBox()
|
||||
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_BUTTONS = New System.Windows.Forms.TableLayoutPanel()
|
||||
TP_PLS = New System.Windows.Forms.TableLayoutPanel()
|
||||
@@ -83,6 +93,8 @@ Namespace API.YouTube.Controls
|
||||
TP_LYRICS.SuspendLayout()
|
||||
CType(Me.TXT_SUBS, 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()
|
||||
'
|
||||
'TP_MAIN
|
||||
@@ -97,10 +109,10 @@ Namespace API.YouTube.Controls
|
||||
TP_MAIN.Margin = New System.Windows.Forms.Padding(0)
|
||||
TP_MAIN.Name = "TP_MAIN"
|
||||
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.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_BUTTONS
|
||||
@@ -112,14 +124,14 @@ Namespace API.YouTube.Controls
|
||||
TP_BUTTONS.Controls.Add(Me.BTT_DOWN, 1, 0)
|
||||
TP_BUTTONS.Controls.Add(Me.BTT_CANCEL, 2, 0)
|
||||
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.Name = "TP_BUTTONS"
|
||||
TP_BUTTONS.RowCount = 1
|
||||
TP_BUTTONS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
||||
TP_BUTTONS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
|
||||
TP_BUTTONS.Size = New System.Drawing.Size(434, 25)
|
||||
TP_BUTTONS.TabIndex = 2
|
||||
TP_BUTTONS.TabIndex = 1
|
||||
'
|
||||
'BTT_DOWN
|
||||
'
|
||||
@@ -147,7 +159,7 @@ Namespace API.YouTube.Controls
|
||||
'SPLITTER_MAIN
|
||||
'
|
||||
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"
|
||||
'
|
||||
'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.Controls.Add(TP_FORMATS, 0, 1)
|
||||
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.Location = New System.Drawing.Point(0, 0)
|
||||
TP_SETTINGS.Margin = New System.Windows.Forms.Padding(0)
|
||||
TP_SETTINGS.Name = "TP_SETTINGS"
|
||||
TP_SETTINGS.RowCount = 3
|
||||
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, 33.33333!))
|
||||
TP_SETTINGS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33333!))
|
||||
TP_SETTINGS.Size = New System.Drawing.Size(434, 84)
|
||||
TP_SETTINGS.RowCount = 5
|
||||
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.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.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_FORMATS
|
||||
@@ -291,7 +307,7 @@ Namespace API.YouTube.Controls
|
||||
TP_FORMATS.RowCount = 1
|
||||
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.TabIndex = 1
|
||||
TP_FORMATS.TabIndex = 5
|
||||
'
|
||||
'TXT_FORMATS_ADDIT
|
||||
'
|
||||
@@ -360,7 +376,7 @@ Namespace API.YouTube.Controls
|
||||
TP_LYRICS.RowCount = 1
|
||||
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.TabIndex = 0
|
||||
TP_LYRICS.TabIndex = 6
|
||||
'
|
||||
'TXT_SUBS
|
||||
'
|
||||
@@ -410,23 +426,28 @@ Namespace API.YouTube.Controls
|
||||
'TXT_OUTPUT_PATH
|
||||
'
|
||||
ActionButton7.BackgroundImage = CType(resources.GetObject("ActionButton7.BackgroundImage"), System.Drawing.Image)
|
||||
ActionButton7.Name = "Open"
|
||||
ActionButton7.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open
|
||||
ActionButton7.ToolTipText = "Choose a new location (Ctrl+O)"
|
||||
ActionButton7.Name = "Save"
|
||||
ActionButton7.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Save
|
||||
ActionButton7.ToolTipText = "Save destination"
|
||||
ActionButton8.BackgroundImage = CType(resources.GetObject("ActionButton8.BackgroundImage"), System.Drawing.Image)
|
||||
ActionButton8.Name = "Add"
|
||||
ActionButton8.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Add
|
||||
ActionButton8.ToolTipText = "Choose a new location and add it to the list (Alt+O)"
|
||||
ActionButton8.Name = "Open"
|
||||
ActionButton8.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open
|
||||
ActionButton8.ToolTipText = "Choose a new location (Ctrl+O)"
|
||||
ActionButton9.BackgroundImage = CType(resources.GetObject("ActionButton9.BackgroundImage"), System.Drawing.Image)
|
||||
ActionButton9.Name = "Clear"
|
||||
ActionButton9.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
|
||||
ActionButton9.Name = "Add"
|
||||
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.Name = "ArrowDown"
|
||||
ActionButton10.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.ArrowDown
|
||||
ActionButton10.Name = "Clear"
|
||||
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(ActionButton8)
|
||||
Me.TXT_OUTPUT_PATH.Buttons.Add(ActionButton9)
|
||||
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.CaptionText = "Output path"
|
||||
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(ListColumn2)
|
||||
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.Size = New System.Drawing.Size(428, 22)
|
||||
Me.TXT_OUTPUT_PATH.TabIndex = 2
|
||||
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
|
||||
'
|
||||
Me.AcceptButton = Me.BTT_DOWN
|
||||
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
|
||||
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
|
||||
Me.CancelButton = Me.BTT_CANCEL
|
||||
Me.ClientSize = New System.Drawing.Size(434, 261)
|
||||
Me.ClientSize = New System.Drawing.Size(434, 317)
|
||||
Me.Controls.Add(TP_MAIN)
|
||||
Me.Icon = Global.SCrawler.My.Resources.SiteYouTube.YouTubeMusicIcon_32
|
||||
Me.KeyPreview = True
|
||||
Me.MinimumSize = New System.Drawing.Size(450, 300)
|
||||
Me.MinimumSize = New System.Drawing.Size(450, 356)
|
||||
Me.Name = "MusicPlaylistsForm"
|
||||
Me.Text = "Albums"
|
||||
TP_MAIN.ResumeLayout(False)
|
||||
@@ -481,6 +561,8 @@ Namespace API.YouTube.Controls
|
||||
TP_LYRICS.PerformLayout()
|
||||
CType(Me.TXT_SUBS, 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)
|
||||
|
||||
End Sub
|
||||
@@ -496,5 +578,7 @@ Namespace API.YouTube.Controls
|
||||
Private WithEvents SPLITTER_MAIN As SplitContainer
|
||||
Private WithEvents CH_DOWN_LYRICS As CheckBox
|
||||
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 Namespace
|
||||
@@ -222,6 +222,13 @@
|
||||
</value>
|
||||
</data>
|
||||
<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>
|
||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
||||
wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP
|
||||
@@ -232,7 +239,7 @@
|
||||
cMaRN0UdBBkAAAAASUVORK5CYII=
|
||||
</value>
|
||||
</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>
|
||||
iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6
|
||||
JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAADmUlE
|
||||
@@ -254,7 +261,7 @@
|
||||
0AUyNxOP1DOwcaG/8I+/LRB+At7psBnyDBG0AAAAAElFTkSuQmCC
|
||||
</value>
|
||||
</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>
|
||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
||||
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
|
||||
@@ -262,7 +269,7 @@
|
||||
AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC
|
||||
</value>
|
||||
</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>
|
||||
iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAABGdBTUEAALGPC/xhBQAAE65JREFUeF7t
|
||||
3X2sJWddB/DdLi2lQG2hdOHuvfM887J7Cxca4ELTQMDWKigIFpBAEAgi9g+CJpJo9Q8NJhgBiYZIYspL
|
||||
@@ -350,6 +357,161 @@
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6LEtW/4flgYiLD1qeX0A
|
||||
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>
|
||||
</data>
|
||||
</root>
|
||||
@@ -19,10 +19,17 @@ Namespace API.YouTube.Controls
|
||||
Friend Class MusicPlaylistsForm : Implements IDesignXMLContainer
|
||||
#Region "Declarations"
|
||||
Private MyView As FormView
|
||||
Private ReadOnly MyFieldsChecker As FieldsChecker
|
||||
Friend Property DesignXML As EContainer Implements IDesignXMLContainer.DesignXML
|
||||
Private Property DesignXMLNodes As String() Implements IDesignXMLContainer.DesignXMLNodes
|
||||
Private Property DesignXMLNodeName As String Implements IDesignXMLContainer.DesignXMLNodeName
|
||||
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 ReadOnly Property Current As IYouTubeMediaContainer
|
||||
Get
|
||||
@@ -40,7 +47,9 @@ Namespace API.YouTube.Controls
|
||||
#Region "Initializer"
|
||||
Friend Sub New(ByVal Container As IYouTubeMediaContainer)
|
||||
InitializeComponent()
|
||||
M3U8Files = New List(Of SFile)
|
||||
MyContainer = Container
|
||||
MyFieldsChecker = New FieldsChecker
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Form handlers"
|
||||
@@ -52,6 +61,9 @@ Namespace API.YouTube.Controls
|
||||
End If
|
||||
|
||||
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)
|
||||
If MyYouTubeSettings.PlaylistFormSplitterDistance > 0 Then SPLITTER_MAIN.SplitterDistancePercentageSet(MyYouTubeSettings.PlaylistFormSplitterDistance)
|
||||
@@ -104,6 +116,9 @@ Namespace API.YouTube.Controls
|
||||
Text = .PlaylistTitle
|
||||
End If
|
||||
|
||||
MyFieldsChecker.AddControl(Of Integer)(TXT_AUDIO_BITRATE, TXT_AUDIO_BITRATE.CaptionText, True)
|
||||
MyFieldsChecker.EndLoaderOperations()
|
||||
|
||||
UpdateSizeText()
|
||||
End With
|
||||
RefillAddit()
|
||||
@@ -111,7 +126,9 @@ Namespace API.YouTube.Controls
|
||||
End Sub
|
||||
Private Sub MusicPlaylistsForm_Closing(sender As Object, e As CancelEventArgs) Handles Me.Closing
|
||||
MyYouTubeSettings.PlaylistFormSplitterDistance.Value = SPLITTER_MAIN.SplitterDistancePercentageGet
|
||||
MyView.DisposeIfReady()
|
||||
MyView.DisposeIfReady
|
||||
MyFieldsChecker.DisposeIfReady
|
||||
M3U8Files.Clear()
|
||||
End Sub
|
||||
Private Sub MusicPlaylistsForm_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown
|
||||
Dim b As Boolean = True
|
||||
@@ -181,8 +198,52 @@ Namespace API.YouTube.Controls
|
||||
End With
|
||||
End Sub
|
||||
Private Sub TXT_OUTPUT_PATH_ActionOnButtonClick(ByVal Sender As ActionButton, ByVal e As ActionButtonEventArgs) Handles TXT_OUTPUT_PATH.ActionOnButtonClick
|
||||
If Sender.DefaultButton = ADB.Open Or Sender.DefaultButton = ADB.Add Then _
|
||||
MyYouTubeSettings.DownloadLocations.ChooseNewLocation(TXT_OUTPUT_PATH, Sender.DefaultButton = ADB.Add, MyDownloaderSettings.OutputPathAskForName)
|
||||
Select Case e.DefaultButton
|
||||
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 Region
|
||||
#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
|
||||
If TXT_OUTPUT_PATH.IsEmptyString Then
|
||||
MsgBoxE({"The output path cannot be null.", "Download music"}, vbCritical)
|
||||
Else
|
||||
ElseIf MyFieldsChecker.AllParamsOK Then
|
||||
With DirectCast(MyContainer, YouTubeMediaContainerBase)
|
||||
.OutputSubtitlesFormat = IIf(CH_DOWN_LYRICS.Checked, "LRC", String.Empty)
|
||||
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()
|
||||
.AbsolutePath = TXT_OUTPUT_PATH.Checked
|
||||
.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 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
|
||||
DialogResult = DialogResult.OK
|
||||
Close()
|
||||
|
||||
@@ -27,6 +27,7 @@ Namespace API.YouTube.Controls
|
||||
Friend Sub New(ByVal m As MediaObject, Optional ByVal SelectedAudio As MediaObject = Nothing)
|
||||
Me.New
|
||||
Const d$ = " " & ChrW(183) & " "
|
||||
Const DRC$ = Objects.YouTubeMediaContainerBase.DRC
|
||||
MyMedia = m
|
||||
If m.Type = Plugin.UserMediaTypes.Audio Then
|
||||
If m.Bitrate >= 320 Then
|
||||
@@ -38,6 +39,7 @@ Namespace API.YouTube.Controls
|
||||
End If
|
||||
LBL_DEFINITION.Text = $"{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
|
||||
If m.Height >= 1440 Then
|
||||
LBL_DEFINITION_INFO.Text = "Ultra High Definition"
|
||||
@@ -53,7 +55,14 @@ Namespace API.YouTube.Controls
|
||||
LBL_DEFINITION.Text = $"{m.Height}p"
|
||||
LBL_CODECS.Text = $"{m.Extension.StringToUpper}{d}{m.Codec.StringToUpper}{d}{m.FPS}fps{d}{m.Bitrate}k"
|
||||
If Not m.Protocol.IsEmptyString Then LBL_CODECS.Text &= $" ({m.Protocol})"
|
||||
If Not 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 AndAlso SelectedAudio.ID.StringToLower.Contains(DRC) Then LBL_CODECS.Text &= $"{d}DRC"
|
||||
|
||||
If MyYouTubeSettings.DefaultVideoHighlightFPS_H > 0 AndAlso m.FPS > MyYouTubeSettings.DefaultVideoHighlightFPS_H Then _
|
||||
BackColor = MyColor.DeleteBack : ForeColor = MyColor.DeleteFore
|
||||
If MyYouTubeSettings.DefaultVideoHighlightFPS_L > 0 AndAlso m.FPS < MyYouTubeSettings.DefaultVideoHighlightFPS_L Then _
|
||||
BackColor = MyColor.UpdateBack : ForeColor = MyColor.UpdateFore
|
||||
End If
|
||||
|
||||
Dim sv% = m.Size / 1024
|
||||
|
||||
415
SCrawler.YouTube/Controls/VideoOptionsForm.Designer.vb
generated
@@ -31,9 +31,15 @@ Namespace API.YouTube.Controls
|
||||
Dim TP_DESTINATION As System.Windows.Forms.TableLayoutPanel
|
||||
Dim ActionButton1 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||
Dim resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(VideoOptionsForm))
|
||||
Dim 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 ListColumn2 As PersonalUtilities.Forms.Controls.Base.ListColumn = New PersonalUtilities.Forms.Controls.Base.ListColumn()
|
||||
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_2 As System.Windows.Forms.Label
|
||||
Dim TP_WHAT As System.Windows.Forms.TableLayoutPanel
|
||||
@@ -41,28 +47,34 @@ Namespace API.YouTube.Controls
|
||||
Dim LBL_FORMAT As System.Windows.Forms.Label
|
||||
Dim LBL_SUBS_FORMAT As System.Windows.Forms.Label
|
||||
Dim TT_MAIN As System.Windows.Forms.ToolTip
|
||||
Dim ActionButton2 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||
Dim ActionButton3 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||
Dim ActionButton4 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||
Dim ActionButton5 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||
Dim ActionButton6 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||
Dim TP_FPS_BITRATE As System.Windows.Forms.TableLayoutPanel
|
||||
Dim ActionButton7 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||
Dim ActionButton8 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||
Dim ActionButton9 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||
Dim ActionButton10 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||
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.LBL_TITLE = New System.Windows.Forms.Label()
|
||||
Me.TP_HEADER_INFO_2 = New System.Windows.Forms.TableLayoutPanel()
|
||||
Me.LBL_TIME = New System.Windows.Forms.Label()
|
||||
Me.LBL_URL = New System.Windows.Forms.LinkLabel()
|
||||
Me.TXT_FILE = New PersonalUtilities.Forms.Controls.ComboBoxExtended()
|
||||
Me.BTT_BROWSE = New System.Windows.Forms.Button()
|
||||
Me.BTT_BROWSE = New SCrawler.API.YouTube.Controls.ButtonRC()
|
||||
Me.BTT_DOWN = 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 SCrawler.API.YouTube.Controls.ButtonRC()
|
||||
Me.OPT_VIDEO = New System.Windows.Forms.RadioButton()
|
||||
Me.OPT_AUDIO = New System.Windows.Forms.RadioButton()
|
||||
Me.LBL_AUDIO_CODEC = New System.Windows.Forms.Label()
|
||||
Me.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_SUBS = New System.Windows.Forms.TableLayoutPanel()
|
||||
Me.TXT_SUBS = New PersonalUtilities.Forms.Controls.TextBoxExtended()
|
||||
@@ -72,7 +84,6 @@ Namespace API.YouTube.Controls
|
||||
Me.CMB_FORMAT = New System.Windows.Forms.ComboBox()
|
||||
Me.CMB_AUDIO_CODEC = New System.Windows.Forms.ComboBox()
|
||||
Me.NUM_RES = New System.Windows.Forms.NumericUpDown()
|
||||
Me.TXT_FPS = New PersonalUtilities.Forms.Controls.TextBoxExtended()
|
||||
Me.TP_CONTROLS = New System.Windows.Forms.TableLayoutPanel()
|
||||
Me.TXT_SUBS_ADDIT = New PersonalUtilities.Forms.Controls.TextBoxExtended()
|
||||
Me.TXT_EXTRA_AUDIO_FORMATS = New PersonalUtilities.Forms.Controls.TextBoxExtended()
|
||||
@@ -83,6 +94,7 @@ Namespace API.YouTube.Controls
|
||||
TP_FOOTER = New System.Windows.Forms.TableLayoutPanel()
|
||||
TP_DESTINATION = 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_2 = New System.Windows.Forms.Label()
|
||||
TP_WHAT = New System.Windows.Forms.TableLayoutPanel()
|
||||
@@ -90,6 +102,7 @@ Namespace API.YouTube.Controls
|
||||
LBL_FORMAT = New System.Windows.Forms.Label()
|
||||
LBL_SUBS_FORMAT = New System.Windows.Forms.Label()
|
||||
TT_MAIN = New System.Windows.Forms.ToolTip(Me.components)
|
||||
TP_FPS_BITRATE = New System.Windows.Forms.TableLayoutPanel()
|
||||
TP_HEADER.SuspendLayout()
|
||||
CType(Me.ICON_VIDEO, System.ComponentModel.ISupportInitialize).BeginInit()
|
||||
TP_HEADER_INFO.SuspendLayout()
|
||||
@@ -100,14 +113,18 @@ Namespace API.YouTube.Controls
|
||||
TP_DESTINATION.SuspendLayout()
|
||||
CType(Me.TXT_FILE, System.ComponentModel.ISupportInitialize).BeginInit()
|
||||
TP_OK_CANCEL.SuspendLayout()
|
||||
TP_PLS.SuspendLayout()
|
||||
CType(Me.CMB_PLS, System.ComponentModel.ISupportInitialize).BeginInit()
|
||||
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_SUBS.SuspendLayout()
|
||||
CType(Me.TXT_SUBS, System.ComponentModel.ISupportInitialize).BeginInit()
|
||||
Me.TP_MAIN.SuspendLayout()
|
||||
Me.TP_OPTIONS.SuspendLayout()
|
||||
CType(Me.NUM_RES, System.ComponentModel.ISupportInitialize).BeginInit()
|
||||
CType(Me.TXT_FPS, System.ComponentModel.ISupportInitialize).BeginInit()
|
||||
CType(Me.TXT_SUBS_ADDIT, System.ComponentModel.ISupportInitialize).BeginInit()
|
||||
CType(Me.TXT_EXTRA_AUDIO_FORMATS, System.ComponentModel.ISupportInitialize).BeginInit()
|
||||
Me.SuspendLayout()
|
||||
@@ -126,7 +143,7 @@ Namespace API.YouTube.Controls
|
||||
TP_HEADER.Name = "TP_HEADER"
|
||||
TP_HEADER.RowCount = 1
|
||||
TP_HEADER.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
||||
TP_HEADER.Size = New System.Drawing.Size(719, 63)
|
||||
TP_HEADER.Size = New System.Drawing.Size(599, 63)
|
||||
TP_HEADER.TabIndex = 0
|
||||
'
|
||||
'ICON_VIDEO
|
||||
@@ -155,7 +172,7 @@ Namespace API.YouTube.Controls
|
||||
TP_HEADER_INFO.RowCount = 2
|
||||
TP_HEADER_INFO.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50.0!))
|
||||
TP_HEADER_INFO.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50.0!))
|
||||
TP_HEADER_INFO.Size = New System.Drawing.Size(589, 63)
|
||||
TP_HEADER_INFO.Size = New System.Drawing.Size(469, 63)
|
||||
TP_HEADER_INFO.TabIndex = 0
|
||||
'
|
||||
'LBL_TITLE
|
||||
@@ -164,7 +181,7 @@ Namespace API.YouTube.Controls
|
||||
Me.LBL_TITLE.Font = New System.Drawing.Font("Arial", 9.0!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(204, Byte))
|
||||
Me.LBL_TITLE.Location = New System.Drawing.Point(3, 0)
|
||||
Me.LBL_TITLE.Name = "LBL_TITLE"
|
||||
Me.LBL_TITLE.Size = New System.Drawing.Size(583, 31)
|
||||
Me.LBL_TITLE.Size = New System.Drawing.Size(463, 31)
|
||||
Me.LBL_TITLE.TabIndex = 0
|
||||
Me.LBL_TITLE.Text = "Video title"
|
||||
Me.LBL_TITLE.TextAlign = System.Drawing.ContentAlignment.MiddleLeft
|
||||
@@ -186,7 +203,7 @@ Namespace API.YouTube.Controls
|
||||
Me.TP_HEADER_INFO_2.Name = "TP_HEADER_INFO_2"
|
||||
Me.TP_HEADER_INFO_2.RowCount = 1
|
||||
Me.TP_HEADER_INFO_2.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
||||
Me.TP_HEADER_INFO_2.Size = New System.Drawing.Size(589, 32)
|
||||
Me.TP_HEADER_INFO_2.Size = New System.Drawing.Size(469, 32)
|
||||
Me.TP_HEADER_INFO_2.TabIndex = 1
|
||||
'
|
||||
'ICON_CLOCK
|
||||
@@ -233,7 +250,7 @@ Namespace API.YouTube.Controls
|
||||
Me.LBL_URL.LinkColor = System.Drawing.Color.FromArgb(CType(CType(0, Byte), Integer), CType(CType(0, Byte), Integer), CType(CType(192, Byte), Integer))
|
||||
Me.LBL_URL.Location = New System.Drawing.Point(115, 0)
|
||||
Me.LBL_URL.Name = "LBL_URL"
|
||||
Me.LBL_URL.Size = New System.Drawing.Size(471, 32)
|
||||
Me.LBL_URL.Size = New System.Drawing.Size(351, 32)
|
||||
Me.LBL_URL.TabIndex = 1
|
||||
Me.LBL_URL.TabStop = True
|
||||
Me.LBL_URL.Text = "https://www.youtube.com/watch?v=abcdefghijk"
|
||||
@@ -243,17 +260,18 @@ Namespace API.YouTube.Controls
|
||||
'
|
||||
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.Absolute, 20.0!))
|
||||
TP_FOOTER.Controls.Add(TP_DESTINATION, 0, 0)
|
||||
TP_FOOTER.Controls.Add(TP_OK_CANCEL, 0, 1)
|
||||
TP_FOOTER.Controls.Add(TP_DESTINATION, 0, 1)
|
||||
TP_FOOTER.Controls.Add(TP_OK_CANCEL, 0, 2)
|
||||
TP_FOOTER.Controls.Add(TP_PLS, 0, 0)
|
||||
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.Name = "TP_FOOTER"
|
||||
TP_FOOTER.RowCount = 2
|
||||
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, 50.0!))
|
||||
TP_FOOTER.Size = New System.Drawing.Size(709, 52)
|
||||
TP_FOOTER.RowCount = 3
|
||||
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, 33.33333!))
|
||||
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_DESTINATION
|
||||
@@ -264,20 +282,25 @@ Namespace API.YouTube.Controls
|
||||
TP_DESTINATION.Controls.Add(Me.TXT_FILE, 0, 0)
|
||||
TP_DESTINATION.Controls.Add(Me.BTT_BROWSE, 1, 0)
|
||||
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.Name = "TP_DESTINATION"
|
||||
TP_DESTINATION.RowCount = 1
|
||||
TP_DESTINATION.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
||||
TP_DESTINATION.Size = New System.Drawing.Size(709, 26)
|
||||
TP_DESTINATION.Size = New System.Drawing.Size(589, 27)
|
||||
TP_DESTINATION.TabIndex = 0
|
||||
'
|
||||
'TXT_FILE
|
||||
'
|
||||
ActionButton1.BackgroundImage = CType(resources.GetObject("ActionButton1.BackgroundImage"), System.Drawing.Image)
|
||||
ActionButton1.Name = "ArrowDown"
|
||||
ActionButton1.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.ArrowDown
|
||||
ActionButton1.Name = "Save"
|
||||
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(ActionButton2)
|
||||
Me.TXT_FILE.ChangeControlsEnableOnCheckedChange = False
|
||||
ListColumn1.Name = "COL_NAME"
|
||||
ListColumn1.Text = "Name"
|
||||
@@ -293,17 +316,17 @@ Namespace API.YouTube.Controls
|
||||
Me.TXT_FILE.Location = New System.Drawing.Point(1, 1)
|
||||
Me.TXT_FILE.Margin = New System.Windows.Forms.Padding(1)
|
||||
Me.TXT_FILE.Name = "TXT_FILE"
|
||||
Me.TXT_FILE.Size = New System.Drawing.Size(627, 22)
|
||||
Me.TXT_FILE.Size = New System.Drawing.Size(507, 22)
|
||||
Me.TXT_FILE.TabIndex = 0
|
||||
Me.TXT_FILE.TextBoxBorderStyle = System.Windows.Forms.BorderStyle.FixedSingle
|
||||
'
|
||||
'BTT_BROWSE
|
||||
'
|
||||
Me.BTT_BROWSE.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
Me.BTT_BROWSE.Location = New System.Drawing.Point(632, 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.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.Text = "Browse"
|
||||
TT_MAIN.SetToolTip(Me.BTT_BROWSE, "Choose an output file (Right click for add a new location to the list)")
|
||||
@@ -318,22 +341,22 @@ Namespace API.YouTube.Controls
|
||||
TP_OK_CANCEL.Controls.Add(Me.BTT_DOWN, 1, 0)
|
||||
TP_OK_CANCEL.Controls.Add(Me.BTT_CANCEL, 2, 0)
|
||||
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.Name = "TP_OK_CANCEL"
|
||||
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.Absolute, 26.0!))
|
||||
TP_OK_CANCEL.Size = New System.Drawing.Size(709, 26)
|
||||
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, 27)
|
||||
TP_OK_CANCEL.TabIndex = 1
|
||||
'
|
||||
'BTT_DOWN
|
||||
'
|
||||
Me.BTT_DOWN.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
Me.BTT_DOWN.Location = New System.Drawing.Point(552, 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.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.Text = "Download"
|
||||
Me.BTT_DOWN.UseVisualStyleBackColor = True
|
||||
@@ -342,32 +365,95 @@ Namespace API.YouTube.Controls
|
||||
'
|
||||
Me.BTT_CANCEL.DialogResult = System.Windows.Forms.DialogResult.Cancel
|
||||
Me.BTT_CANCEL.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
Me.BTT_CANCEL.Location = New System.Drawing.Point(632, 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.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.Text = "Cancel"
|
||||
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.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.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.Name = "LB_SEP_1"
|
||||
LB_SEP_1.Size = New System.Drawing.Size(709, 1)
|
||||
LB_SEP_1.Size = New System.Drawing.Size(589, 1)
|
||||
LB_SEP_1.TabIndex = 3
|
||||
'
|
||||
'LB_SEP_2
|
||||
'
|
||||
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.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.Name = "LB_SEP_2"
|
||||
LB_SEP_2.Size = New System.Drawing.Size(709, 1)
|
||||
LB_SEP_2.Size = New System.Drawing.Size(589, 1)
|
||||
LB_SEP_2.TabIndex = 5
|
||||
'
|
||||
'TP_WHAT
|
||||
@@ -439,7 +525,7 @@ Namespace API.YouTube.Controls
|
||||
'
|
||||
LBL_SUBS_FORMAT.AutoSize = True
|
||||
LBL_SUBS_FORMAT.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
LBL_SUBS_FORMAT.Location = New System.Drawing.Point(552, 0)
|
||||
LBL_SUBS_FORMAT.Location = New System.Drawing.Point(432, 0)
|
||||
LBL_SUBS_FORMAT.Name = "LBL_SUBS_FORMAT"
|
||||
LBL_SUBS_FORMAT.Size = New System.Drawing.Size(74, 28)
|
||||
LBL_SUBS_FORMAT.TabIndex = 2
|
||||
@@ -451,7 +537,7 @@ Namespace API.YouTube.Controls
|
||||
'
|
||||
Me.LBL_AUDIO_CODEC.AutoSize = True
|
||||
Me.LBL_AUDIO_CODEC.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
Me.LBL_AUDIO_CODEC.Location = New System.Drawing.Point(552, 0)
|
||||
Me.LBL_AUDIO_CODEC.Location = New System.Drawing.Point(432, 0)
|
||||
Me.LBL_AUDIO_CODEC.Name = "LBL_AUDIO_CODEC"
|
||||
Me.LBL_AUDIO_CODEC.Size = New System.Drawing.Size(74, 28)
|
||||
Me.LBL_AUDIO_CODEC.TabIndex = 5
|
||||
@@ -459,6 +545,59 @@ Namespace API.YouTube.Controls
|
||||
Me.LBL_AUDIO_CODEC.TextAlign = System.Drawing.ContentAlignment.MiddleRight
|
||||
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
|
||||
'
|
||||
Me.TP_HEADER_BASE.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.[Single]
|
||||
@@ -473,8 +612,8 @@ Namespace API.YouTube.Controls
|
||||
Me.TP_HEADER_BASE.RowCount = 1
|
||||
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.Size = New System.Drawing.Size(721, 65)
|
||||
Me.TP_HEADER_BASE.TabIndex = 6
|
||||
Me.TP_HEADER_BASE.Size = New System.Drawing.Size(601, 65)
|
||||
Me.TP_HEADER_BASE.TabIndex = 7
|
||||
'
|
||||
'TP_SUBS
|
||||
'
|
||||
@@ -486,31 +625,31 @@ Namespace API.YouTube.Controls
|
||||
Me.TP_SUBS.Controls.Add(LBL_SUBS_FORMAT, 1, 0)
|
||||
Me.TP_SUBS.Controls.Add(Me.CMB_SUBS_FORMAT, 2, 0)
|
||||
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.Name = "TP_SUBS"
|
||||
Me.TP_SUBS.RowCount = 1
|
||||
Me.TP_SUBS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
||||
Me.TP_SUBS.Size = New System.Drawing.Size(709, 28)
|
||||
Me.TP_SUBS.Size = New System.Drawing.Size(589, 28)
|
||||
Me.TP_SUBS.TabIndex = 2
|
||||
'
|
||||
'TXT_SUBS
|
||||
'
|
||||
ActionButton2.BackgroundImage = CType(resources.GetObject("ActionButton2.BackgroundImage"), System.Drawing.Image)
|
||||
ActionButton2.Name = "Open"
|
||||
ActionButton2.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open
|
||||
ActionButton2.ToolTipText = "Choose subtitles"
|
||||
ActionButton3.BackgroundImage = CType(resources.GetObject("ActionButton3.BackgroundImage"), System.Drawing.Image)
|
||||
ActionButton3.Name = "Refresh"
|
||||
ActionButton3.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Refresh
|
||||
ActionButton3.ToolTipText = "Reset subtitles to initial selected"
|
||||
ActionButton4.BackgroundImage = CType(resources.GetObject("ActionButton4.BackgroundImage"), System.Drawing.Image)
|
||||
ActionButton4.Name = "Clear"
|
||||
ActionButton4.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
|
||||
ActionButton4.ToolTipText = "Clear subtitles selection (don't download subtitles)"
|
||||
Me.TXT_SUBS.Buttons.Add(ActionButton2)
|
||||
Me.TXT_SUBS.Buttons.Add(ActionButton3)
|
||||
Me.TXT_SUBS.Buttons.Add(ActionButton4)
|
||||
ActionButton9.BackgroundImage = CType(resources.GetObject("ActionButton9.BackgroundImage"), System.Drawing.Image)
|
||||
ActionButton9.Name = "Open"
|
||||
ActionButton9.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open
|
||||
ActionButton9.ToolTipText = "Choose subtitles"
|
||||
ActionButton10.BackgroundImage = CType(resources.GetObject("ActionButton10.BackgroundImage"), System.Drawing.Image)
|
||||
ActionButton10.Name = "Refresh"
|
||||
ActionButton10.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Refresh
|
||||
ActionButton10.ToolTipText = "Reset subtitles to initial selected"
|
||||
ActionButton11.BackgroundImage = CType(resources.GetObject("ActionButton11.BackgroundImage"), System.Drawing.Image)
|
||||
ActionButton11.Name = "Clear"
|
||||
ActionButton11.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
|
||||
ActionButton11.ToolTipText = "Clear subtitles selection (don't download subtitles)"
|
||||
Me.TXT_SUBS.Buttons.Add(ActionButton9)
|
||||
Me.TXT_SUBS.Buttons.Add(ActionButton10)
|
||||
Me.TXT_SUBS.Buttons.Add(ActionButton11)
|
||||
Me.TXT_SUBS.CaptionText = "Subtitles"
|
||||
Me.TXT_SUBS.CaptionToolTipEnabled = True
|
||||
Me.TXT_SUBS.CaptionToolTipText = "The selected subtitles will also be downloaded"
|
||||
@@ -519,7 +658,7 @@ Namespace API.YouTube.Controls
|
||||
Me.TXT_SUBS.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
Me.TXT_SUBS.Location = New System.Drawing.Point(3, 3)
|
||||
Me.TXT_SUBS.Name = "TXT_SUBS"
|
||||
Me.TXT_SUBS.Size = New System.Drawing.Size(543, 22)
|
||||
Me.TXT_SUBS.Size = New System.Drawing.Size(423, 22)
|
||||
Me.TXT_SUBS.TabIndex = 0
|
||||
Me.TXT_SUBS.TextBoxReadOnly = True
|
||||
'
|
||||
@@ -528,7 +667,7 @@ Namespace API.YouTube.Controls
|
||||
Me.CMB_SUBS_FORMAT.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
Me.CMB_SUBS_FORMAT.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList
|
||||
Me.CMB_SUBS_FORMAT.FormattingEnabled = True
|
||||
Me.CMB_SUBS_FORMAT.Location = New System.Drawing.Point(632, 3)
|
||||
Me.CMB_SUBS_FORMAT.Location = New System.Drawing.Point(512, 3)
|
||||
Me.CMB_SUBS_FORMAT.Name = "CMB_SUBS_FORMAT"
|
||||
Me.CMB_SUBS_FORMAT.Size = New System.Drawing.Size(74, 21)
|
||||
Me.CMB_SUBS_FORMAT.TabIndex = 1
|
||||
@@ -538,55 +677,56 @@ Namespace API.YouTube.Controls
|
||||
Me.TP_MAIN.ColumnCount = 1
|
||||
Me.TP_MAIN.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
||||
Me.TP_MAIN.Controls.Add(Me.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_CONTROLS, 0, 6)
|
||||
Me.TP_MAIN.Controls.Add(LB_SEP_1, 0, 5)
|
||||
Me.TP_MAIN.Controls.Add(LB_SEP_2, 0, 7)
|
||||
Me.TP_MAIN.Controls.Add(Me.TP_SUBS, 0, 2)
|
||||
Me.TP_MAIN.Controls.Add(Me.TXT_SUBS_ADDIT, 0, 3)
|
||||
Me.TP_MAIN.Controls.Add(Me.TXT_EXTRA_AUDIO_FORMATS, 0, 4)
|
||||
Me.TP_MAIN.Controls.Add(Me.TP_CONTROLS, 0, 7)
|
||||
Me.TP_MAIN.Controls.Add(LB_SEP_1, 0, 6)
|
||||
Me.TP_MAIN.Controls.Add(LB_SEP_2, 0, 8)
|
||||
Me.TP_MAIN.Controls.Add(Me.TP_SUBS, 0, 3)
|
||||
Me.TP_MAIN.Controls.Add(Me.TXT_SUBS_ADDIT, 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.Location = New System.Drawing.Point(0, 0)
|
||||
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, 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, 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, 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.Size = New System.Drawing.Size(721, 271)
|
||||
Me.TP_MAIN.Size = New System.Drawing.Size(601, 328)
|
||||
Me.TP_MAIN.TabIndex = 0
|
||||
'
|
||||
'TP_OPTIONS
|
||||
'
|
||||
Me.TP_OPTIONS.ColumnCount = 7
|
||||
Me.TP_OPTIONS.ColumnCount = 6
|
||||
Me.TP_OPTIONS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.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, 120.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(TP_WHAT, 0, 0)
|
||||
Me.TP_OPTIONS.Controls.Add(Me.CMB_FORMAT, 2, 0)
|
||||
Me.TP_OPTIONS.Controls.Add(Me.LBL_AUDIO_CODEC, 5, 0)
|
||||
Me.TP_OPTIONS.Controls.Add(Me.CMB_AUDIO_CODEC, 6, 0)
|
||||
Me.TP_OPTIONS.Controls.Add(Me.LBL_AUDIO_CODEC, 4, 0)
|
||||
Me.TP_OPTIONS.Controls.Add(Me.CMB_AUDIO_CODEC, 5, 0)
|
||||
Me.TP_OPTIONS.Controls.Add(Me.NUM_RES, 3, 0)
|
||||
Me.TP_OPTIONS.Controls.Add(Me.TXT_FPS, 4, 0)
|
||||
Me.TP_OPTIONS.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
Me.TP_OPTIONS.Location = New System.Drawing.Point(6, 65)
|
||||
Me.TP_OPTIONS.Margin = New System.Windows.Forms.Padding(6, 0, 6, 0)
|
||||
Me.TP_OPTIONS.Name = "TP_OPTIONS"
|
||||
Me.TP_OPTIONS.RowCount = 1
|
||||
Me.TP_OPTIONS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
||||
Me.TP_OPTIONS.Size = New System.Drawing.Size(709, 28)
|
||||
Me.TP_OPTIONS.Size = New System.Drawing.Size(589, 28)
|
||||
Me.TP_OPTIONS.TabIndex = 1
|
||||
'
|
||||
'CMB_FORMAT
|
||||
@@ -604,7 +744,7 @@ Namespace API.YouTube.Controls
|
||||
Me.CMB_AUDIO_CODEC.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
Me.CMB_AUDIO_CODEC.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList
|
||||
Me.CMB_AUDIO_CODEC.FormattingEnabled = True
|
||||
Me.CMB_AUDIO_CODEC.Location = New System.Drawing.Point(632, 3)
|
||||
Me.CMB_AUDIO_CODEC.Location = New System.Drawing.Point(512, 3)
|
||||
Me.CMB_AUDIO_CODEC.Name = "CMB_AUDIO_CODEC"
|
||||
Me.CMB_AUDIO_CODEC.Size = New System.Drawing.Size(74, 21)
|
||||
Me.CMB_AUDIO_CODEC.TabIndex = 3
|
||||
@@ -621,57 +761,39 @@ Namespace API.YouTube.Controls
|
||||
Me.NUM_RES.TextAlign = System.Windows.Forms.HorizontalAlignment.Center
|
||||
Me.NUM_RES.Value = New Decimal(New Integer() {1080, 0, 0, 0})
|
||||
'
|
||||
'TXT_FPS
|
||||
'
|
||||
ActionButton5.BackgroundImage = CType(resources.GetObject("ActionButton5.BackgroundImage"), System.Drawing.Image)
|
||||
ActionButton5.Name = "Clear"
|
||||
ActionButton5.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
|
||||
Me.TXT_FPS.Buttons.Add(ActionButton5)
|
||||
Me.TXT_FPS.CaptionText = "FPS"
|
||||
Me.TXT_FPS.CaptionToolTipEnabled = True
|
||||
Me.TXT_FPS.CaptionToolTipText = "You can reduce the video FPS by setting the FPS value in this field."
|
||||
Me.TXT_FPS.CaptionWidth = 30.0R
|
||||
Me.TXT_FPS.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
Me.TXT_FPS.Location = New System.Drawing.Point(432, 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(114, 22)
|
||||
Me.TXT_FPS.TabIndex = 6
|
||||
Me.TXT_FPS.TextBoxWidthMinimal = 30
|
||||
'
|
||||
'TP_CONTROLS
|
||||
'
|
||||
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.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.Name = "TP_CONTROLS"
|
||||
Me.TP_CONTROLS.RowCount = 1
|
||||
Me.TP_CONTROLS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
||||
Me.TP_CONTROLS.Size = New System.Drawing.Size(715, 25)
|
||||
Me.TP_CONTROLS.Size = New System.Drawing.Size(595, 25)
|
||||
Me.TP_CONTROLS.TabIndex = 0
|
||||
'
|
||||
'TXT_SUBS_ADDIT
|
||||
'
|
||||
ActionButton6.BackgroundImage = CType(resources.GetObject("ActionButton6.BackgroundImage"), System.Drawing.Image)
|
||||
ActionButton6.Enabled = False
|
||||
ActionButton6.Name = "Open"
|
||||
ActionButton6.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open
|
||||
ActionButton6.ToolTipText = "Choose additional formats"
|
||||
ActionButton7.BackgroundImage = CType(resources.GetObject("ActionButton7.BackgroundImage"), System.Drawing.Image)
|
||||
ActionButton7.Enabled = False
|
||||
ActionButton7.Name = "Refresh"
|
||||
ActionButton7.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Refresh
|
||||
ActionButton7.ToolTipText = "Fill in additional formats from the defaults"
|
||||
ActionButton8.BackgroundImage = CType(resources.GetObject("ActionButton8.BackgroundImage"), System.Drawing.Image)
|
||||
ActionButton8.Enabled = False
|
||||
ActionButton8.Name = "Clear"
|
||||
ActionButton8.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
|
||||
ActionButton8.ToolTipText = "Remove all additional formats"
|
||||
Me.TXT_SUBS_ADDIT.Buttons.Add(ActionButton6)
|
||||
Me.TXT_SUBS_ADDIT.Buttons.Add(ActionButton7)
|
||||
Me.TXT_SUBS_ADDIT.Buttons.Add(ActionButton8)
|
||||
ActionButton12.BackgroundImage = CType(resources.GetObject("ActionButton12.BackgroundImage"), System.Drawing.Image)
|
||||
ActionButton12.Enabled = False
|
||||
ActionButton12.Name = "Open"
|
||||
ActionButton12.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open
|
||||
ActionButton12.ToolTipText = "Choose additional formats"
|
||||
ActionButton13.BackgroundImage = CType(resources.GetObject("ActionButton13.BackgroundImage"), System.Drawing.Image)
|
||||
ActionButton13.Enabled = False
|
||||
ActionButton13.Name = "Refresh"
|
||||
ActionButton13.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Refresh
|
||||
ActionButton13.ToolTipText = "Fill in additional formats from the defaults"
|
||||
ActionButton14.BackgroundImage = CType(resources.GetObject("ActionButton14.BackgroundImage"), System.Drawing.Image)
|
||||
ActionButton14.Enabled = False
|
||||
ActionButton14.Name = "Clear"
|
||||
ActionButton14.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
|
||||
ActionButton14.ToolTipText = "Remove all additional formats"
|
||||
Me.TXT_SUBS_ADDIT.Buttons.Add(ActionButton12)
|
||||
Me.TXT_SUBS_ADDIT.Buttons.Add(ActionButton13)
|
||||
Me.TXT_SUBS_ADDIT.Buttons.Add(ActionButton14)
|
||||
Me.TXT_SUBS_ADDIT.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.CheckBox
|
||||
Me.TXT_SUBS_ADDIT.CaptionText = "Additional subtitle formats"
|
||||
Me.TXT_SUBS_ADDIT.CaptionToolTipEnabled = True
|
||||
@@ -679,44 +801,44 @@ Namespace API.YouTube.Controls
|
||||
Me.TXT_SUBS_ADDIT.CaptionWidth = 150.0R
|
||||
Me.TXT_SUBS_ADDIT.ClearTextByButtonClear = False
|
||||
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.Name = "TXT_SUBS_ADDIT"
|
||||
Me.TXT_SUBS_ADDIT.Size = New System.Drawing.Size(709, 22)
|
||||
Me.TXT_SUBS_ADDIT.Size = New System.Drawing.Size(589, 22)
|
||||
Me.TXT_SUBS_ADDIT.TabIndex = 3
|
||||
Me.TXT_SUBS_ADDIT.Tag = "s"
|
||||
Me.TXT_SUBS_ADDIT.TextBoxReadOnly = True
|
||||
'
|
||||
'TXT_EXTRA_AUDIO_FORMATS
|
||||
'
|
||||
ActionButton9.BackgroundImage = CType(resources.GetObject("ActionButton9.BackgroundImage"), System.Drawing.Image)
|
||||
ActionButton9.Enabled = False
|
||||
ActionButton9.Name = "Open"
|
||||
ActionButton9.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open
|
||||
ActionButton9.ToolTipText = "Choose additional formats"
|
||||
ActionButton10.BackgroundImage = CType(resources.GetObject("ActionButton10.BackgroundImage"), System.Drawing.Image)
|
||||
ActionButton10.Enabled = False
|
||||
ActionButton10.Name = "Refresh"
|
||||
ActionButton10.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Refresh
|
||||
ActionButton10.ToolTipText = "Fill in additional formats from the defaults"
|
||||
ActionButton11.BackgroundImage = CType(resources.GetObject("ActionButton11.BackgroundImage"), System.Drawing.Image)
|
||||
ActionButton11.Enabled = False
|
||||
ActionButton11.Name = "Clear"
|
||||
ActionButton11.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
|
||||
ActionButton11.ToolTipText = "Choose additional formats"
|
||||
Me.TXT_EXTRA_AUDIO_FORMATS.Buttons.Add(ActionButton9)
|
||||
Me.TXT_EXTRA_AUDIO_FORMATS.Buttons.Add(ActionButton10)
|
||||
Me.TXT_EXTRA_AUDIO_FORMATS.Buttons.Add(ActionButton11)
|
||||
ActionButton15.BackgroundImage = CType(resources.GetObject("ActionButton15.BackgroundImage"), System.Drawing.Image)
|
||||
ActionButton15.Enabled = False
|
||||
ActionButton15.Name = "Open"
|
||||
ActionButton15.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open
|
||||
ActionButton15.ToolTipText = "Choose additional formats"
|
||||
ActionButton16.BackgroundImage = CType(resources.GetObject("ActionButton16.BackgroundImage"), System.Drawing.Image)
|
||||
ActionButton16.Enabled = False
|
||||
ActionButton16.Name = "Refresh"
|
||||
ActionButton16.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Refresh
|
||||
ActionButton16.ToolTipText = "Fill in additional formats from the defaults"
|
||||
ActionButton17.BackgroundImage = CType(resources.GetObject("ActionButton17.BackgroundImage"), System.Drawing.Image)
|
||||
ActionButton17.Enabled = False
|
||||
ActionButton17.Name = "Clear"
|
||||
ActionButton17.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
|
||||
ActionButton17.ToolTipText = "Choose additional formats"
|
||||
Me.TXT_EXTRA_AUDIO_FORMATS.Buttons.Add(ActionButton15)
|
||||
Me.TXT_EXTRA_AUDIO_FORMATS.Buttons.Add(ActionButton16)
|
||||
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.CaptionText = "Additional audio formats"
|
||||
Me.TXT_EXTRA_AUDIO_FORMATS.CaptionToolTipEnabled = True
|
||||
Me.TXT_EXTRA_AUDIO_FORMATS.CaptionWidth = 150.0R
|
||||
Me.TXT_EXTRA_AUDIO_FORMATS.ClearTextByButtonClear = False
|
||||
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.Name = "TXT_EXTRA_AUDIO_FORMATS"
|
||||
Me.TXT_EXTRA_AUDIO_FORMATS.Size = New System.Drawing.Size(709, 22)
|
||||
Me.TXT_EXTRA_AUDIO_FORMATS.Size = New System.Drawing.Size(589, 22)
|
||||
Me.TXT_EXTRA_AUDIO_FORMATS.TabIndex = 4
|
||||
Me.TXT_EXTRA_AUDIO_FORMATS.Tag = "a"
|
||||
Me.TXT_EXTRA_AUDIO_FORMATS.TextBoxReadOnly = True
|
||||
@@ -727,14 +849,14 @@ Namespace API.YouTube.Controls
|
||||
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
|
||||
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
|
||||
Me.CancelButton = Me.BTT_CANCEL
|
||||
Me.ClientSize = New System.Drawing.Size(721, 271)
|
||||
Me.ClientSize = New System.Drawing.Size(601, 328)
|
||||
Me.Controls.Add(Me.TP_MAIN)
|
||||
Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle
|
||||
Me.Icon = Global.SCrawler.My.Resources.SiteYouTube.YouTubeIcon_32
|
||||
Me.KeyPreview = True
|
||||
Me.MaximizeBox = False
|
||||
Me.MinimizeBox = False
|
||||
Me.MinimumSize = New System.Drawing.Size(737, 310)
|
||||
Me.MinimumSize = New System.Drawing.Size(617, 367)
|
||||
Me.Name = "VideoOptionsForm"
|
||||
Me.ShowInTaskbar = False
|
||||
Me.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide
|
||||
@@ -750,8 +872,13 @@ Namespace API.YouTube.Controls
|
||||
TP_DESTINATION.ResumeLayout(False)
|
||||
CType(Me.TXT_FILE, System.ComponentModel.ISupportInitialize).EndInit()
|
||||
TP_OK_CANCEL.ResumeLayout(False)
|
||||
TP_PLS.ResumeLayout(False)
|
||||
CType(Me.CMB_PLS, System.ComponentModel.ISupportInitialize).EndInit()
|
||||
TP_WHAT.ResumeLayout(False)
|
||||
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_SUBS.ResumeLayout(False)
|
||||
Me.TP_SUBS.PerformLayout()
|
||||
@@ -760,7 +887,6 @@ Namespace API.YouTube.Controls
|
||||
Me.TP_OPTIONS.ResumeLayout(False)
|
||||
Me.TP_OPTIONS.PerformLayout()
|
||||
CType(Me.NUM_RES, System.ComponentModel.ISupportInitialize).EndInit()
|
||||
CType(Me.TXT_FPS, System.ComponentModel.ISupportInitialize).EndInit()
|
||||
CType(Me.TXT_SUBS_ADDIT, System.ComponentModel.ISupportInitialize).EndInit()
|
||||
CType(Me.TXT_EXTRA_AUDIO_FORMATS, System.ComponentModel.ISupportInitialize).EndInit()
|
||||
Me.ResumeLayout(False)
|
||||
@@ -786,10 +912,13 @@ Namespace API.YouTube.Controls
|
||||
Private WithEvents TXT_SUBS_ADDIT As PersonalUtilities.Forms.Controls.TextBoxExtended
|
||||
Private WithEvents TXT_EXTRA_AUDIO_FORMATS As PersonalUtilities.Forms.Controls.TextBoxExtended
|
||||
Private WithEvents TXT_FILE As PersonalUtilities.Forms.Controls.ComboBoxExtended
|
||||
Private WithEvents BTT_BROWSE As Button
|
||||
Private WithEvents BTT_BROWSE As SCrawler.API.YouTube.Controls.ButtonRC
|
||||
Private WithEvents BTT_DOWN As Button
|
||||
Private WithEvents BTT_CANCEL As Button
|
||||
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 SCrawler.API.YouTube.Controls.ButtonRC
|
||||
Private WithEvents TXT_AUDIO_BITRATE As PersonalUtilities.Forms.Controls.TextBoxExtended
|
||||
End Class
|
||||
End Namespace
|
||||
@@ -137,6 +137,13 @@
|
||||
</metadata>
|
||||
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<data name="ActionButton1.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
||||
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>
|
||||
iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAABGdBTUEAALGPC/xhBQAAE65JREFUeF7t
|
||||
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">
|
||||
<value>False</value>
|
||||
</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">
|
||||
<value>False</value>
|
||||
</metadata>
|
||||
@@ -253,74 +377,15 @@
|
||||
<metadata name="LBL_SUBS_FORMAT.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>False</value>
|
||||
</metadata>
|
||||
<data name="ActionButton2.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="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
|
||||
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>
|
||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
||||
wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP
|
||||
WQwhyWIyJIUW5NqyPb7oCVtIlhVTwYf8nv7/t2zJagel9KmqKsIACYL9RjI8UHz5zshougZr/AEvbxEP
|
||||
aZCDBY3VslixaJvX3wzkkDiOwbZtDRGA5vdNAg+TL27qgmt5XkBG/gTdAG7Gt+3PP9oOaEGFCVEC6rp+
|
||||
5g9MfM/c5e4OsEZMZkQEtGL5H2DdZ5JRArDwPA+iKII0TfkC9vroC9j5vq8JTWw3WzWgLMtZGIaa0MR8
|
||||
vlAD8PYlSaIJTTiOowY0p0Bc19XEJo6HE59FAPuMzyAINKGJ1XLFZxHALtMrnkBXOIQIIIQ8YvF/KrgB
|
||||
cMaRN0UdBBkAAAAASUVORK5CYII=
|
||||
</value>
|
||||
</data>
|
||||
<metadata name="TP_FPS_BITRATE.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>False</value>
|
||||
</metadata>
|
||||
<data name="ActionButton7.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==
|
||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
||||
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
|
||||
tbbB43rK5xSAQq1VYFtmeQBoqZTSreVZvgTknM8yyyjA/qodsDF9gspD2Bj6B+DH+NqzhQQAG+POMnSX
|
||||
AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC
|
||||
</value>
|
||||
</data>
|
||||
<data name="ActionButton8.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
@@ -364,6 +429,76 @@
|
||||
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>
|
||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
||||
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
|
||||
tbbB43rK5xSAQq1VYFtmeQBoqZTSreVZvgTknM8yyyjA/qodsDF9gspD2Bj6B+DH+NqzhQQAG+POMnSX
|
||||
AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC
|
||||
</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -26,11 +26,17 @@ Namespace API.YouTube.Controls
|
||||
Friend Property DesignXML As EContainer Implements IDesignXMLContainer.DesignXML
|
||||
Private Property DesignXMLNodes As String() Implements IDesignXMLContainer.DesignXMLNodes
|
||||
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
|
||||
Friend Property MyContainer As YouTubeMediaContainerBase
|
||||
Private Initialization As Boolean = True
|
||||
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
|
||||
@@ -54,6 +60,7 @@ Namespace API.YouTube.Controls
|
||||
#Region "Initializers"
|
||||
Friend Sub New(ByVal Container As YouTubeMediaContainerBase, Optional ByVal InheritsFromContainer As Boolean = False)
|
||||
InitializeComponent()
|
||||
M3U8Files = New List(Of SFile)
|
||||
MyContainer = Container
|
||||
CNT_PROCESSOR = New TableControlsProcessor(TP_CONTROLS)
|
||||
Me.InheritsFromContainer = InheritsFromContainer
|
||||
@@ -69,6 +76,8 @@ Namespace API.YouTube.Controls
|
||||
End If
|
||||
|
||||
MyYouTubeSettings.DownloadLocations.PopulateComboBox(TXT_FILE)
|
||||
MyYouTubeSettings.PlaylistsLocations.PopulateComboBox(CMB_PLS,, True)
|
||||
CMB_PLS.Text = MyYouTubeSettings.LatestPlaylistFile.Value
|
||||
|
||||
If Not MyContainer Is Nothing Then
|
||||
With MyContainer
|
||||
@@ -112,7 +121,7 @@ Namespace API.YouTube.Controls
|
||||
img = ImageRenderer.GetImage(SFile.GetBytesFromNet(imgUrl, EDP.ReturnValue), EDP.ReturnValue)
|
||||
If Not img Is Nothing Then ICON_VIDEO.Image = img : ICON_VIDEO.InitialImage = img
|
||||
End If
|
||||
LBL_TITLE.Text = .Title
|
||||
LBL_TITLE.Text = $"{If(MyYouTubeSettings.FileAddDateToFileName_VideoForm.Value, $"[{ .DateAdded:yyyy-MM-dd}] ", String.Empty)}{ .Title}"
|
||||
LBL_TIME.Text = AConvert(Of String)(.Duration, TimeToStringProvider, String.Empty)
|
||||
TP_HEADER_INFO_2.ColumnStyles(1).Width = MeasureTextDefault(LBL_TIME.Text, LBL_TIME.Font).Width + PaddingE.GetOf({LBL_TIME}).Horizontal
|
||||
TP_HEADER_INFO_2.Refresh()
|
||||
@@ -155,11 +164,16 @@ Namespace API.YouTube.Controls
|
||||
|
||||
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
|
||||
MyFieldsChecker.AddControl(Of Double)(TXT_FPS, TXT_FPS.CaptionText, True, New FpsFieldChecker)
|
||||
MyFieldsChecker.EndLoaderOperations()
|
||||
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
|
||||
TXT_SUBS_ADDIT.Enabled = .Subtitles.Count > 0
|
||||
RefillTextBoxes()
|
||||
@@ -180,6 +194,7 @@ Namespace API.YouTube.Controls
|
||||
Private Sub VideoOptionsForm_Closing(sender As Object, e As CancelEventArgs) Handles Me.Closing
|
||||
MyView.DisposeIfReady()
|
||||
MyFieldsChecker.DisposeIfReady()
|
||||
M3U8Files.Clear()
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Refill"
|
||||
@@ -313,9 +328,11 @@ Namespace API.YouTube.Controls
|
||||
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
|
||||
@@ -335,12 +352,15 @@ Namespace API.YouTube.Controls
|
||||
Else
|
||||
f = TXT_FILE.Text
|
||||
End If
|
||||
f = CleanFileName(f)
|
||||
If f.IsEmptyString Then Throw New ArgumentNullException("File", "The output file cannot be null")
|
||||
With MyContainer
|
||||
.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
|
||||
.M3U8_PlaylistFiles = M3U8FilesFull
|
||||
|
||||
If Not .HasElements Then
|
||||
Dim cntIndex% = -1
|
||||
@@ -357,6 +377,7 @@ Namespace API.YouTube.Controls
|
||||
Else
|
||||
.SelectedVideoIndex = -1
|
||||
.SelectedAudioIndex = cntIndex
|
||||
.MediaType = UMTypes.Audio
|
||||
End If
|
||||
.FileSetManually = True
|
||||
.File = f
|
||||
@@ -367,6 +388,7 @@ Namespace API.YouTube.Controls
|
||||
Else
|
||||
If OPT_AUDIO.Checked Then
|
||||
.SetMaxResolution(-2)
|
||||
.MediaType = UMTypes.Audio
|
||||
Else
|
||||
.SetMaxResolution(NUM_RES.Value)
|
||||
End If
|
||||
@@ -377,6 +399,8 @@ Namespace API.YouTube.Controls
|
||||
|
||||
If MyYouTubeSettings.OutputPathAutoChange Then MyYouTubeSettings.OutputPath.Value = f
|
||||
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
|
||||
Close()
|
||||
@@ -508,6 +532,42 @@ Namespace API.YouTube.Controls
|
||||
End Sub
|
||||
#End Region
|
||||
#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 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
|
||||
@@ -529,6 +589,14 @@ Namespace API.YouTube.Controls
|
||||
_FilePathBeforeItemChange = Nothing
|
||||
End Try
|
||||
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
|
||||
Dim f As SFile
|
||||
#Disable Warning BC40000
|
||||
@@ -537,12 +605,15 @@ Namespace API.YouTube.Controls
|
||||
f = SFile.SelectPath(f, "Select the destination of the video files", EDP.ReturnValue)
|
||||
Else
|
||||
f = TXT_FILE.Text
|
||||
Dim sPattern$ = $"Video|{AvailableVideoFormats.Select(Function(vf) $"*.{vf.ToLower}").ListToString(";")}" &
|
||||
$"|Audio|{AvailableAudioFormats.Select(Function(af) $"*.{af.ToLower}").ListToString(";")}" &
|
||||
"|All Files|*.*"
|
||||
f = SFile.SaveAs(f, "Select the destination of the video file",,, sPattern, EDP.ReturnValue)
|
||||
Dim ext$ = f.Extension
|
||||
Dim sPattern$ = "All Files|*.*|" &
|
||||
$"Video|{AvailableVideoFormats.Select(Function(vf) $"*.{vf.ToLower}").ListToString(";")}" &
|
||||
$"|Audio|{AvailableAudioFormats.Select(Function(af) $"*.{af.ToLower}").ListToString(";")}"
|
||||
f = SFile.SaveAs(f, "Select the destination of the video file",, ext, sPattern, EDP.ReturnValue)
|
||||
If Not f.IsEmptyString Then f.Extension = ext
|
||||
End If
|
||||
#Enable Warning
|
||||
f = CleanFileName(f)
|
||||
If Not f.IsEmptyString Then
|
||||
If e.Button = MouseButtons.Right Then
|
||||
MyYouTubeSettings.DownloadLocations.Add(f, MyDownloaderSettings.OutputPathAskForName)
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports System.Runtime.CompilerServices
|
||||
Imports PersonalUtilities.Tools
|
||||
Imports PersonalUtilities.Forms.Toolbars
|
||||
Imports PersonalUtilities.Functions.RegularExpressions
|
||||
@@ -17,10 +18,21 @@ Namespace API.YouTube
|
||||
Public Const DownloaderDataFolderYouTube As String = DownloadObjects.STDownloader.DownloaderDataFolder & "YouTube\"
|
||||
Friend Const YouTubeDownloadPathDefault As String = "YouTubeDownloads\"
|
||||
Friend Const SimpleArraysFormNode As String = "SimpleFormatsChooserForm"
|
||||
Private Const YTDLP_DefaultName As String = "yt-dlp"
|
||||
Public Property MyYouTubeSettings As Base.YouTubeSettings
|
||||
Public Property MyCache As CacheKeeper
|
||||
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")
|
||||
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()
|
||||
Get
|
||||
Return {"ASS", "LRC", "SRT", "VTT"}
|
||||
@@ -45,6 +57,25 @@ Namespace API.YouTube
|
||||
Friend ReadOnly TitleHtmlConverter As Func(Of String, String) = Function(Input) Input.StringRemoveWinForbiddenSymbols().StringTrim()
|
||||
Friend ReadOnly ProgressProvider As IMyProgressNumberProvider = MyProgressNumberProvider.Percentage
|
||||
Public ReadOnly TrueUrlRegEx As RParams = RParams.DM(Base.YouTubeFunctions.TrueUrlPattern, 0, EDP.ReturnValue)
|
||||
Friend ReadOnly MusicUrlApply As RParams = RParams.DMS("https://([w\.]*)youtube.com.+", 1, RegexReturn.Replace, EDP.ReturnValue,
|
||||
CType(Function(input$) "music.", Func(Of String, String)), String.Empty)
|
||||
<Extension> Friend Function ToMusicUrl(ByVal URL As String, ByVal IsMusic As Boolean) As String
|
||||
Try : Return If(IsMusic And Not URL.IsEmptyString, CStr(RegexReplace(URL, MusicUrlApply)).IfNullOrEmpty(URL), URL) : Catch : Return URL : End Try
|
||||
End Function
|
||||
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.StringReplaceSymbols({vbLf, vbCr, vbCrLf}, String.Empty, EDP.ReturnValue)
|
||||
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 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}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
Imports PersonalUtilities.Functions.XML
|
||||
Imports PersonalUtilities.Functions.XML.Base
|
||||
Imports PersonalUtilities.Functions.XML.Attributes
|
||||
Imports PersonalUtilities.Forms
|
||||
Imports PersonalUtilities.Forms.Controls
|
||||
Imports PersonalUtilities.Forms.Controls.Base
|
||||
Imports PersonalUtilities.Tools
|
||||
@@ -103,7 +104,7 @@ Namespace DownloadObjects.STDownloader
|
||||
If UseUpdate Then .EndUpdate(True)
|
||||
End With
|
||||
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()
|
||||
With CMB
|
||||
.BeginUpdate()
|
||||
@@ -124,7 +125,7 @@ Namespace DownloadObjects.STDownloader
|
||||
.EndUpdate()
|
||||
|
||||
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 Current.File.IsEmptyString Then CMB.Text = Current.PathWithSeparator Else CMB.Text = Current
|
||||
End If
|
||||
@@ -141,6 +142,44 @@ Namespace DownloadObjects.STDownloader
|
||||
End If
|
||||
Return f
|
||||
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()
|
||||
If Locations.Count > 0 Then
|
||||
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
|
||||
Add(Item, True)
|
||||
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
|
||||
Dim i% = IndexOf(Item)
|
||||
Dim i% = IndexOf(Item,, IsFile)
|
||||
Dim processUpdate As Boolean = True
|
||||
If i >= 0 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
|
||||
Return Not Item.Path.IsEmptyString AndAlso Locations.Contains(Item)
|
||||
End Function
|
||||
Public Function IndexOf(ByVal Item As DownloadLocation, Optional ByVal IgnoreModel As Boolean = False) As Integer
|
||||
Return Locations.FindIndex(Function(d) d.Path = Item.Path And (d.Model = Item.Model Or IgnoreModel))
|
||||
Public Function IndexOf(ByVal Item As DownloadLocation, Optional ByVal IgnoreModel As Boolean = False, Optional ByVal IsFile As Boolean = False) As Integer
|
||||
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
|
||||
Public Function Remove(ByVal Item As DownloadLocation) As Boolean Implements ICollection(Of DownloadLocation).Remove
|
||||
If Locations.Remove(Item) Then
|
||||
|
||||
@@ -12,6 +12,7 @@ Imports SCrawler.API.YouTube.Objects
|
||||
Imports SCrawler.API.YouTube.Controls
|
||||
Imports PersonalUtilities.Tools
|
||||
Imports PersonalUtilities.Forms.Toolbars
|
||||
Imports PersonalUtilities.Functions.Messaging
|
||||
Namespace DownloadObjects.STDownloader
|
||||
Public Delegate Sub MediaItemEventHandler(ByVal Sender As MediaItem, ByVal Container As IYouTubeMediaContainer)
|
||||
<DefaultEvent("DoubleClick"), DesignTimeVisible(False), ToolboxItem(False)>
|
||||
@@ -132,18 +133,25 @@ Namespace DownloadObjects.STDownloader
|
||||
|
||||
ICON_SITE.Image = .SiteIcon
|
||||
LBL_TIME.Text = AConvert(Of String)(.Duration, TimeToStringProvider, String.Empty)
|
||||
LBL_TITLE.Text = .ToString(True)
|
||||
LBL_TITLE.Text = $"{If(MyYouTubeSettings.FileAddDateToFileName_VideoList.Value, $"[{ .DateAdded:yyyy-MM-dd}] ", String.Empty)}{ .ToString(True)}"
|
||||
Dim h%, b%
|
||||
If .Self.GetType Is GetType(YouTubeMediaContainerBase) OrElse (Not .Self.GetType.BaseType Is Nothing AndAlso .Self.GetType.BaseType Is GetType(YouTubeMediaContainerBase)) Then
|
||||
With DirectCast(.Self, YouTubeMediaContainerBase) : h = .HeightBase : b = .BitrateBase : End With
|
||||
Else
|
||||
h = .Height
|
||||
b = .Bitrate
|
||||
End If
|
||||
If Not .SiteKey = YouTubeSiteKey And .ContentType = Plugin.UserMediaTypes.Picture Then
|
||||
LBL_INFO.Text = .File.Extension.StringToUpper
|
||||
ElseIf Not .IsMusic Then
|
||||
If .Height > 0 Then
|
||||
LBL_INFO.Text = $"{ .File.Extension.StringToUpper}{d}{ .Height}p"
|
||||
ElseIf Not .IsMusic And Not (.MediaType = Plugin.UserMediaTypes.Audio Or .MediaType = Plugin.UserMediaTypes.AudioPre) Then
|
||||
If h > 0 Then
|
||||
LBL_INFO.Text = $"{ .File.Extension.StringToUpper}{d}{h}p"
|
||||
Else
|
||||
LBL_INFO.Text = .File.Extension.StringToUpper
|
||||
End If
|
||||
Else
|
||||
If .Bitrate > 0 Then
|
||||
LBL_INFO.Text = $"{ .File.Extension.StringToUpper}{d}{ .Bitrate}k"
|
||||
If b > 0 Then
|
||||
LBL_INFO.Text = $"{ .File.Extension.StringToUpper}{d}{b}k"
|
||||
Else
|
||||
LBL_INFO.Text = .File.Extension.StringToUpper
|
||||
End If
|
||||
@@ -180,10 +188,10 @@ Namespace DownloadObjects.STDownloader
|
||||
With MyContainer
|
||||
If Not .SiteKey = YouTubeSiteKey And .ContentType = Plugin.UserMediaTypes.Picture Then
|
||||
ICON_WHAT.Image = My.Resources.ImagePic_32
|
||||
ElseIf Not .IsMusic Then
|
||||
ICON_WHAT.Image = My.Resources.VideoCamera_32
|
||||
Else
|
||||
ElseIf .IsMusic Or .MediaType = Plugin.UserMediaTypes.Audio Or .MediaType = Plugin.UserMediaTypes.AudioPre Then
|
||||
ICON_WHAT.Image = My.Resources.AudioMusic_32
|
||||
Else
|
||||
ICON_WHAT.Image = My.Resources.VideoCamera_32
|
||||
End If
|
||||
End With
|
||||
End Sub, EDP.None)
|
||||
@@ -220,7 +228,7 @@ Namespace DownloadObjects.STDownloader
|
||||
t = 0
|
||||
End If
|
||||
|
||||
LBL_TITLE.Text = MyContainer.ToString(True)
|
||||
LBL_TITLE.Text = $"{If(MyYouTubeSettings.FileAddDateToFileName_VideoList.Value, $"[{ .DateAdded:yyyy-MM-dd}] ", String.Empty)}{ .ToString(True)}"
|
||||
|
||||
If Not .SiteKey = YouTubeSiteKey Then BTT_VIEW_SETTINGS.Visible = False
|
||||
|
||||
@@ -229,7 +237,7 @@ Namespace DownloadObjects.STDownloader
|
||||
.ColumnStyles.Clear()
|
||||
.ColumnCount = 0
|
||||
If ContainerHasElements Or MyContainer.MediaState = Plugin.UserMediaStates.Downloaded Then
|
||||
If Not MyContainer.SiteKey = YouTubeSiteKey Then UpdateMediaIcon()
|
||||
UpdateMediaIcon()
|
||||
If ContainerHasElements Then
|
||||
BTT_OPEN_FOLDER.Visible = False
|
||||
BTT_OPEN_FILE.Visible = False
|
||||
@@ -476,12 +484,28 @@ Namespace DownloadObjects.STDownloader
|
||||
RaiseEvent Removal(Me, MyContainer)
|
||||
End Sub
|
||||
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}" &
|
||||
If(FileOption = SFO.File, MyContainer.File.ToString, MyContainer.File.PathWithSeparator),
|
||||
$"Deleting a {FileOption.ToString.ToLower}"}, vbExclamation,,, {"Process", "Cancel"}) = 0 Then
|
||||
Dim opt$
|
||||
Dim opt2$ = String.Empty
|
||||
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)
|
||||
RaiseEvent Removal(Me, MyContainer)
|
||||
End If
|
||||
b.Clear()
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "ISupportInitialize Support"
|
||||
|
||||
@@ -57,6 +57,12 @@ Namespace DownloadObjects.STDownloader
|
||||
End If
|
||||
MyNotificator = New YTNotificator(Me)
|
||||
MyDownloaderSettings = MyYouTubeSettings
|
||||
ProgramLogInitialize()
|
||||
With ProgramLog
|
||||
AddHandler .TextAdded, AddressOf ProgramLog_TextAdded
|
||||
AddHandler .TextCleared, AddressOf ProgramLog_TextCleared
|
||||
End With
|
||||
UpdateLogButton()
|
||||
End If
|
||||
|
||||
With MyView : .Import() : .SetFormSize() : End With
|
||||
@@ -157,7 +163,7 @@ Namespace DownloadObjects.STDownloader
|
||||
If PerformClick Then cnt.PerformClick()
|
||||
If Not DisableDownload And MyDownloaderSettings.DownloadAutomatically Then AddToDownload(cnt, True)
|
||||
End With
|
||||
End Sub, EDP.None)
|
||||
End Sub, EDP.SendToLog)
|
||||
End Sub
|
||||
#Region "Controls rendering"
|
||||
Private Overloads Sub OffsetControls()
|
||||
@@ -247,7 +253,7 @@ Namespace DownloadObjects.STDownloader
|
||||
Dim useCookiesParse As Boolean? = Nothing
|
||||
If useCookies Then useCookiesParse = True
|
||||
Dim standardizeUrls As Boolean = MyYouTubeSettings.StandardizeURLs
|
||||
Dim standardize As Func(Of String, String) = Function(input) If(standardizeUrls, YouTubeFunctions.StandardizeURL(input), input)
|
||||
Dim standardize As Func(Of String, String) = Function(input) If(standardizeUrls, YouTubeFunctions.StandardizeURL(input), input.StringTrim)
|
||||
|
||||
Dim c As IYouTubeMediaContainer = Nothing
|
||||
Dim url$ = String.Empty
|
||||
@@ -449,12 +455,26 @@ Namespace DownloadObjects.STDownloader
|
||||
End Try
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "LOG"
|
||||
Private Sub BTT_LOG_Click(sender As Object, e As EventArgs) Handles BTT_LOG.Click
|
||||
MyMainLOG_ShowForm(DesignXML,,,, AddressOf UpdateLogButton)
|
||||
End Sub
|
||||
Friend Sub UpdateLogButton()
|
||||
If AppMode Then MyMainLOG_UpdateLogButton(BTT_LOG, TOOLBAR_TOP)
|
||||
Private Sub UpdateLogButton()
|
||||
If AppMode Then
|
||||
Try : MyMainLOG_UpdateLogButton(BTT_LOG, TOOLBAR_TOP) : Catch : End Try
|
||||
End If
|
||||
End Sub
|
||||
Private _LogUpdateButtonSuspended As Boolean = False
|
||||
Private Sub ProgramLog_TextAdded(ByVal Sender As Object, ByVal e As EventArgs)
|
||||
If Not _LogUpdateButtonSuspended Then
|
||||
_LogUpdateButtonSuspended = True
|
||||
Try : ControlInvokeFast(TOOLBAR_TOP, BTT_LOG, AddressOf UpdateLogButton, EDP.None) : Catch : End Try
|
||||
End If
|
||||
End Sub
|
||||
Private Sub ProgramLog_TextCleared(ByVal Sender As Object, ByVal e As EventArgs)
|
||||
_LogUpdateButtonSuspended = False
|
||||
End Sub
|
||||
#End Region
|
||||
Private Sub BTT_BUG_REPORT_Click(sender As Object, e As EventArgs) Handles BTT_BUG_REPORT.Click
|
||||
Try
|
||||
With MyYouTubeSettings
|
||||
|
||||
@@ -32,6 +32,6 @@ Imports System.Runtime.InteropServices
|
||||
' by using the '*' as shown below:
|
||||
' <Assembly: AssemblyVersion("1.0.*")>
|
||||
|
||||
<Assembly: AssemblyVersion("2024.2.25.0")>
|
||||
<Assembly: AssemblyFileVersion("2024.2.25.0")>
|
||||
<Assembly: AssemblyVersion("2024.7.24.0")>
|
||||
<Assembly: AssemblyFileVersion("2024.7.24.0")>
|
||||
<Assembly: NeutralResourcesLanguage("en")>
|
||||
|
||||
@@ -19,17 +19,18 @@ Namespace API.YouTube.Objects
|
||||
Dim __title$ = $" - {Title}"
|
||||
If Not s.IsEmptyString Then s = $" [{s}]"
|
||||
If Not PlaylistTitle.IsEmptyString And Not ForMediaItem Then t = $"{PlaylistTitle} - "
|
||||
Dim c% = {Count, ElementsNumber}.Max
|
||||
If IsMusic Then
|
||||
If Count <= 1 Then t &= "Single" Else t &= "Album"
|
||||
If c <= 1 Then t &= "Single" Else t &= "Album"
|
||||
Else
|
||||
t &= "Playlist"
|
||||
End If
|
||||
If Not PlaylistTitle.IsEmptyString And Not ForMediaItem Then t &= $" - {PlaylistTitle}"
|
||||
If PlaylistTitle = Title Then __title = String.Empty
|
||||
If ForMediaItem Then
|
||||
Return $"{t} ({Count}){__title}"
|
||||
Return $"{t} ({c}){__title}"
|
||||
Else
|
||||
Return $"{t} ({Count}){__title} ({AConvert(Of String)(Duration, TimeToStringProvider)}){s}"
|
||||
Return $"{t} ({c}){__title} ({AConvert(Of String)(Duration, TimeToStringProvider)}){s}"
|
||||
End If
|
||||
End Function
|
||||
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
|
||||
_File.Extension = mp3
|
||||
End If
|
||||
_File = CleanFileName(_File)
|
||||
End If
|
||||
End Sub
|
||||
Public Overrides Function ToString(ByVal ForMediaItem As Boolean) As String
|
||||
@@ -46,12 +47,17 @@ Namespace API.YouTube.Objects
|
||||
_ObjectType = Base.YouTubeMediaType.Single
|
||||
Me.IsMusic = IsMusic
|
||||
If MyBase.Parse(Container, Path, IsMusic, Token, Progress) Then
|
||||
Dim f As SFile = MyYouTubeSettings.OutputPath
|
||||
If f.IsEmptyString Then f = "YouTubeDownloads\OutputFile.mp3"
|
||||
Dim ext$ = MyYouTubeSettings.DefaultAudioCodec.Value.StringToLower
|
||||
If ext.IsEmptyString Then ext = "mp3"
|
||||
f.Extension = ext
|
||||
File = f
|
||||
With MyYouTubeSettings
|
||||
Dim f As SFile = .OutputPath
|
||||
If f.IsEmptyString Then f = "YouTubeDownloads\OutputFile.mp3"
|
||||
Dim ext$ = .DefaultAudioCodecMusic.Value.StringToLower.IfNullOrEmpty(.DefaultAudioCodec.Value.StringToLower)
|
||||
If ext.IsEmptyString Then ext = "mp3"
|
||||
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
|
||||
Else
|
||||
Return False
|
||||
|
||||
@@ -123,6 +123,15 @@ Namespace API.YouTube.Objects
|
||||
<XMLEC> Public Property UserTitle As String Implements IYouTubeMediaContainer.UserTitle
|
||||
#End Region
|
||||
#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 HasElements As Boolean Implements IYouTubeMediaContainer.HasElements
|
||||
Get
|
||||
@@ -258,13 +267,24 @@ Namespace API.YouTube.Objects
|
||||
<XMLEC(CollectionMode:=CollectionModes.String)>
|
||||
Friend ReadOnly Property PostProcessing_OutputAudioFormats As List(Of String)
|
||||
Friend Sub PostProcessing_OutputAudioFormats_Reset()
|
||||
PostProcessing_OutputAudioFormats.Clear()
|
||||
PostProcessing_OutputAudioFormats.ListAddList(MyYouTubeSettings.DefaultAudioCodecAddit)
|
||||
If PostProcessing_OutputAudioFormats.Count > 0 Then
|
||||
PostProcessing_OutputAudioFormats.Sort()
|
||||
PostProcessing_OutputAudioFormats.RemoveAll(Function(s) s = -1)
|
||||
End If
|
||||
With PostProcessing_OutputAudioFormats
|
||||
.Clear()
|
||||
.ListAddList(MyYouTubeSettings.DefaultAudioCodecAddit)
|
||||
If .Count > 0 Then .Sort()
|
||||
End With
|
||||
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
|
||||
#Region "Subtitles"
|
||||
Protected ReadOnly _Subtitles As List(Of Subtitles)
|
||||
@@ -301,21 +321,19 @@ Namespace API.YouTube.Objects
|
||||
<XMLEC(CollectionMode:=CollectionModes.String)>
|
||||
Friend ReadOnly Property PostProcessing_OutputSubtitlesFormats As List(Of String)
|
||||
Friend Sub PostProcessing_OutputSubtitlesFormats_Reset()
|
||||
PostProcessing_OutputSubtitlesFormats.Clear()
|
||||
PostProcessing_OutputSubtitlesFormats.ListAddList(MyYouTubeSettings.DefaultSubtitlesFormatAddit)
|
||||
If PostProcessing_OutputSubtitlesFormats.Count > 0 Then
|
||||
PostProcessing_OutputSubtitlesFormats.Sort()
|
||||
PostProcessing_OutputSubtitlesFormats.RemoveAll(Function(s) s = -1)
|
||||
End If
|
||||
With PostProcessing_OutputSubtitlesFormats
|
||||
.Clear()
|
||||
.ListAddList(MyYouTubeSettings.DefaultSubtitlesFormatAddit)
|
||||
If .Count > 0 Then .Sort()
|
||||
End With
|
||||
End Sub
|
||||
Friend Sub SubtitlesSelectedIndexesReset()
|
||||
SubtitlesSelectedIndexes.Clear()
|
||||
Dim subs As List(Of Subtitles) = Subtitles
|
||||
SubtitlesSelectedIndexes.ListAddList(MyYouTubeSettings.DefaultSubtitles.Select(Function(s) subs.FindIndex(Function(ss) ss.ID = s)))
|
||||
If SubtitlesSelectedIndexes.Count > 0 Then
|
||||
SubtitlesSelectedIndexes.Sort()
|
||||
SubtitlesSelectedIndexes.RemoveAll(Function(s) s = -1)
|
||||
End If
|
||||
With SubtitlesSelectedIndexes
|
||||
.Clear()
|
||||
Dim subs As List(Of Subtitles) = Subtitles
|
||||
.ListAddList(MyYouTubeSettings.DefaultSubtitles.Select(Function(s) subs.FindIndex(Function(ss) ss.ID = s)))
|
||||
If .Count > 0 Then .Sort() : .RemoveAll(Function(s) s = -1)
|
||||
End With
|
||||
End Sub
|
||||
Private Sub SetElementsSubtitles(ByVal Source As YouTubeMediaContainerBase)
|
||||
If Not Source Is Nothing And HasElements Then
|
||||
@@ -376,10 +394,13 @@ Namespace API.YouTube.Objects
|
||||
End Set
|
||||
End Property
|
||||
Protected _Size As Integer = 0
|
||||
<XMLEC("SizeRecalculated")> Protected _SizeRecalculated As Boolean = False
|
||||
<XMLEC> Public Overridable Property Size As Integer Implements IDownloadableMedia.Size
|
||||
Get
|
||||
If HasElements Then
|
||||
Return Elements.Sum(Function(e) If(e.Checked, e.Size, 0))
|
||||
ElseIf _SizeRecalculated Then
|
||||
Return _Size
|
||||
Else
|
||||
If Checked Then
|
||||
If IsMusic And SelectedAudioIndex.ValueBetween(0, MediaObjects.Count - 1) Then
|
||||
@@ -418,6 +439,19 @@ Namespace API.YouTube.Objects
|
||||
End Get
|
||||
End Property
|
||||
<XMLEC> Public Property Height As Integer Implements IYouTubeMediaContainer.Height
|
||||
Friend ReadOnly Property HeightBase As Integer
|
||||
Get
|
||||
If Height > 0 Then
|
||||
Return Height
|
||||
ElseIf SelectedVideoIndex.ValueBetween(0, MediaObjects.Count - 1) Then
|
||||
Return SelectedVideo.Height
|
||||
ElseIf SelectedAudioIndex.ValueBetween(0, MediaObjects.Count - 1) Then
|
||||
Return SelectedAudio.Height
|
||||
Else
|
||||
Return 0
|
||||
End If
|
||||
End Get
|
||||
End Property
|
||||
Protected _Bitrate As Integer = 0
|
||||
<XMLEC> Public Overridable Property Bitrate As Integer Implements IYouTubeMediaContainer.Bitrate
|
||||
Get
|
||||
@@ -435,6 +469,20 @@ Namespace API.YouTube.Objects
|
||||
Me._Bitrate = _Bitrate
|
||||
End Set
|
||||
End Property
|
||||
Friend ReadOnly Property BitrateBase As Integer
|
||||
Get
|
||||
If Bitrate > 0 Then
|
||||
Return Bitrate
|
||||
ElseIf OutputAudioBitrate > 0 Then
|
||||
Return OutputAudioBitrate
|
||||
ElseIf HasElements Then
|
||||
Try : Return Elements.Average(Function(e) DirectCast(e, YouTubeMediaContainerBase).BitrateBase) : Catch : End Try
|
||||
ElseIf SelectedAudioIndex.ValueBetween(0, MediaObjects.Count - 1) Then
|
||||
Return SelectedAudio.Bitrate
|
||||
End If
|
||||
Return 0
|
||||
End Get
|
||||
End Property
|
||||
<XMLEC> Public Property DateCreated As Date = Now Implements IYouTubeMediaContainer.DateCreated
|
||||
<XMLEC> Public Property DateAdded As Date Implements IYouTubeMediaContainer.DateAdded
|
||||
Private Property IUserMedia_PostDate As Date? Implements IUserMedia.PostDate
|
||||
@@ -559,7 +607,25 @@ Namespace API.YouTube.Objects
|
||||
If ObjectType = YouTubeMediaType.Single AndAlso Not GetPlayListTitle.IsEmptyString Then _SpecialPath.StringAppend(GetPlayListTitle(), "\")
|
||||
If Elements.Count > 0 Then Elements.ForEach(Sub(e) e.SpecialFolder = Path)
|
||||
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 Friend Property FileSetManually As Boolean = False
|
||||
Public Property FileIgnorePlaylist As Boolean = False
|
||||
@@ -614,6 +680,15 @@ Namespace API.YouTube.Objects
|
||||
End If
|
||||
End Set
|
||||
End Property
|
||||
Friend Sub FileDateUpdate()
|
||||
Dim n$ = _File.Name.StringTrim
|
||||
Dim s$ = IIf(n.IsEmptyString, String.Empty, " ")
|
||||
Select Case MyYouTubeSettings.FileAddDateToFileName.Value
|
||||
Case FileDateMode.Before : n = $"[{DateAdded:yyyy-MM-dd}]{s}{n}"
|
||||
Case FileDateMode.After : n = $"{n}{s}[{DateAdded:yyyy-MM-dd}]"
|
||||
End Select
|
||||
_File.Name = n
|
||||
End Sub
|
||||
Public Property FileSettings As SFile
|
||||
Private Property IUserMedia_File As String Implements IUserMedia.File
|
||||
Get
|
||||
@@ -628,6 +703,26 @@ Namespace API.YouTube.Objects
|
||||
If HasElements And Not IsMusic Then urls.ListAddList(Elements.SelectMany(Function(elem As YouTubeMediaContainerBase) elem.GetFiles()), LAP.NotContainsOnly)
|
||||
Return urls
|
||||
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
|
||||
#Region "Command"
|
||||
<XMLEC> Public Property UseCookies As Boolean = MyYouTubeSettings.DefaultUseCookies Implements IYouTubeMediaContainer.UseCookies
|
||||
@@ -635,6 +730,7 @@ Namespace API.YouTube.Objects
|
||||
Private Const aac As String = "aac"
|
||||
Private Const ac3 As String = "ac3"
|
||||
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
|
||||
Get
|
||||
If Not File.IsEmptyString Then
|
||||
@@ -646,7 +742,7 @@ Namespace API.YouTube.Objects
|
||||
Bitrate = 0
|
||||
_MediaType = UMTypes.Undefined
|
||||
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(SelectedVideo.ID)
|
||||
_Size = SelectedVideo.Size
|
||||
@@ -663,13 +759,17 @@ Namespace API.YouTube.Objects
|
||||
End If
|
||||
If SelectedAudioIndex >= 0 Then
|
||||
Dim atCodec$
|
||||
'URGENT: 2023.3.4 -> 2023.7.6
|
||||
'2023.3.4 -> 2023.7.6
|
||||
'cmd.StringAppend($"ba*[format_id={SelectedAudio.ID}]", "+")
|
||||
cmd.StringAppend(SelectedAudio.ID, "+")
|
||||
If OutputAudioCodec.StringToLower = ac3 Then
|
||||
PostProcessing_AudioAC3 = True
|
||||
formats.StringAppend($"--audio-format {aac}", " ")
|
||||
atCodec = aac
|
||||
ElseIf SelectedVideoIndex >= 0 And OutputAudioCodec.StringToLower = mp3 Then
|
||||
PostProcessing_AudioMP3 = True
|
||||
formats.StringAppend($"--audio-format {aac}", " ")
|
||||
atCodec = aac
|
||||
Else
|
||||
formats.StringAppend($"--audio-format {OutputAudioCodec.StringToLower}", " ")
|
||||
atCodec = OutputAudioCodec.StringToLower
|
||||
@@ -702,9 +802,10 @@ Namespace API.YouTube.Objects
|
||||
subs = $"--write-subs --write-auto-subs --sub-format {OutputSubtitlesFormat.StringToLower} --sub-langs ""{subs}"" --convert-subs {OutputSubtitlesFormat.StringToLower}"
|
||||
End If
|
||||
If Not cmd.IsEmptyString Then
|
||||
'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 = $"{YTDLP_NAME} -f {cmd}"
|
||||
If Not MyYouTubeSettings.ReplaceModificationDate Then cmd &= " --no-mtime"
|
||||
cmd.StringAppend(formats, " ")
|
||||
cmd.StringAppend(subs, " ")
|
||||
@@ -726,7 +827,7 @@ Namespace API.YouTube.Objects
|
||||
_SubtitlesDelegated = New List(Of Subtitles)
|
||||
SubtitlesSelectedIndexes = New List(Of Integer)
|
||||
MediaObjects = New List(Of MediaObject)
|
||||
Files = New List(Of SFile)
|
||||
_Files = New List(Of SFile)
|
||||
|
||||
PostProcessing_OutputSubtitlesFormats = New List(Of String)
|
||||
PostProcessing_OutputSubtitlesFormats.ListAddList(MyYouTubeSettings.DefaultSubtitlesFormatAddit)
|
||||
@@ -755,9 +856,19 @@ Namespace API.YouTube.Objects
|
||||
If RemoveFiles Then
|
||||
Dim fErr As New ErrorsDescriber(EDP.None)
|
||||
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)
|
||||
If isArr Then paths.ListAddValue(ThumbnailFile, l)
|
||||
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
|
||||
If HasElements Then Elements.ForEach(Sub(e) e.Delete(RemoveFiles))
|
||||
End Sub
|
||||
@@ -788,6 +899,23 @@ Namespace API.YouTube.Objects
|
||||
Return Nothing
|
||||
End Try
|
||||
End Function
|
||||
Private Function GetPlaylistRow(ByVal Element As YouTubeMediaContainerBase, Optional ByVal __file As SFile = Nothing,
|
||||
Optional ByVal Mode As M3U8CreationMode = M3U8CreationMode.Absolute) As String
|
||||
Const m3u8DataRow$ = "#EXTINF:{0},{1}" & vbCrLf & "{2}"
|
||||
With Element
|
||||
Dim f As SFile = __file.IfNullOrEmpty(.File)
|
||||
Dim __f$ = SymbolsConverter.ASCII.EncodeSymbolsOnly(If(Mode = M3U8CreationMode.Absolute, f.ToString, f.File))
|
||||
If Mode = M3U8CreationMode.Absolute Then __f = $"file:///{__f}"
|
||||
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, __f)
|
||||
End With
|
||||
End Function
|
||||
Private ReadOnly DownloadProgressPattern As RParams = RParams.DMS("\[download\]\s*([\d\.,]+)", 1, EDP.ReturnValue)
|
||||
Public Property Progress As MyProgress Implements IYouTubeMediaContainer.Progress
|
||||
Private Property IDownloadableMedia_Progress As Object Implements IDownloadableMedia.Progress
|
||||
@@ -822,6 +950,50 @@ Namespace API.YouTube.Objects
|
||||
DownloadCommand(UseCookies, Token)
|
||||
Else
|
||||
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
|
||||
Dim arr As M3U8CreationMode() = If(MyYouTubeSettings.MusicPlaylistCreate_CreationMode.Value = M3U8CreationMode.Both,
|
||||
{M3U8CreationMode.Relative, M3U8CreationMode.Absolute},
|
||||
{MyYouTubeSettings.MusicPlaylistCreate_CreationMode.Value})
|
||||
Dim postfix$
|
||||
Dim added As Boolean
|
||||
Dim checkFile As Func(Of IYouTubeMediaContainer, Boolean) = Function(ByVal e As IYouTubeMediaContainer) As Boolean
|
||||
If e.File.Exists Then
|
||||
added = True
|
||||
Return True
|
||||
Else
|
||||
Return False
|
||||
End If
|
||||
End Function
|
||||
For Each cm As M3U8CreationMode In arr
|
||||
If arr.Length > 1 AndAlso cm = M3U8CreationMode.Absolute Then postfix = "Abs" Else postfix = String.Empty
|
||||
added = False
|
||||
If MyYouTubeSettings.MusicPlaylistCreate_M3U8 Then
|
||||
t = New TextSaver
|
||||
t.AppendLine("#EXTM3U")
|
||||
Elements.ForEach(Sub(e) If checkFile(e) Then t.AppendLine(GetPlaylistRow(e,, cm)))
|
||||
f = $"{Elements(0).File.PathWithSeparator}Playlist{postfix}.m3u8"
|
||||
If added Then t.SaveAs(f, EDP.SendToLog)
|
||||
If f.Exists Then AddFile(f)
|
||||
t.Dispose()
|
||||
End If
|
||||
added = False
|
||||
If MyYouTubeSettings.MusicPlaylistCreate_M3U Then
|
||||
t = New TextSaver
|
||||
Elements.ForEach(Sub(e) If checkFile(e) Then t.AppendLine(If(cm = M3U8CreationMode.Relative, e.File.File, e.File.ToString)))
|
||||
f = $"{Elements(0).File.PathWithSeparator}Playlist{postfix}.m3u"
|
||||
If added Then t.SaveAs(f, EDP.SendToLog)
|
||||
If f.Exists Then AddFile(f)
|
||||
t.Dispose()
|
||||
End If
|
||||
Next
|
||||
Catch ex As Exception
|
||||
ErrorsDescriber.Execute(EDP.SendToLog, ex, "[YouTubeMediaContainerBase.Download.CreatePlaylist]")
|
||||
End Try
|
||||
t.DisposeIfReady
|
||||
End If
|
||||
End If
|
||||
RaiseEvent DataDownloaded(Me, Nothing)
|
||||
End Sub
|
||||
@@ -845,7 +1017,7 @@ Namespace API.YouTube.Objects
|
||||
.Visible = True
|
||||
.Value = 0
|
||||
.Maximum = DownloadGetElemCountSingle()
|
||||
.Information = $"Download {ObjectType}"
|
||||
.Information = "Downloading"
|
||||
End With
|
||||
End If
|
||||
|
||||
@@ -891,7 +1063,7 @@ Namespace API.YouTube.Objects
|
||||
ff.Name = "album"
|
||||
ff.Extension = "url"
|
||||
CreateUrlFile(url, ff)
|
||||
If ff.Exists Then Files.Add(ff)
|
||||
If ff.Exists Then AddFile(ff)
|
||||
End If
|
||||
If MyYouTubeSettings.CreateThumbnails_Music Then
|
||||
Using resp As New Responser
|
||||
@@ -917,7 +1089,7 @@ Namespace API.YouTube.Objects
|
||||
url = LinkFormatterSecure(u)
|
||||
f.Name = "cover"
|
||||
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 Using
|
||||
@@ -926,19 +1098,61 @@ Namespace API.YouTube.Objects
|
||||
ErrorsDescriber.Execute(EDP.SendToLog, ex, $"DownloadPlaylistCover({PlsId}, {f})")
|
||||
End Try
|
||||
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)
|
||||
Dim dCommand$ = String.Empty
|
||||
Try
|
||||
ThrowAny(Token)
|
||||
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)
|
||||
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
|
||||
Dim prExists As Boolean = Not Progress Is Nothing
|
||||
If prExists Then
|
||||
@@ -948,10 +1162,10 @@ Namespace API.YouTube.Objects
|
||||
.Value = 0
|
||||
.Maximum = 100
|
||||
.Provider = ProgressProvider
|
||||
.Information = $"Download {MediaType}"
|
||||
.Information = "Downloading"
|
||||
End With
|
||||
End If
|
||||
.MainProcessName = "yt-dlp"
|
||||
.MainProcessName = MyYouTubeSettings.YTDLP.Name '"yt-dlp"
|
||||
.FileExchanger = MyCache.NewInstance(Of BatchFileExchanger)(CachePath, EDP.ReturnValue)
|
||||
.FileExchanger.DeleteCacheOnDispose = True
|
||||
.AddCommand("chcp 65001")
|
||||
@@ -971,22 +1185,34 @@ Namespace API.YouTube.Objects
|
||||
If Not File.Exists Then _File.Name = File.File
|
||||
If File.Exists Then
|
||||
|
||||
M3U8_Append()
|
||||
|
||||
If DownloadObjects.STDownloader.MyDownloaderSettings.CreateUrlFiles Then
|
||||
Dim fileUrl As SFile = File
|
||||
fileUrl.Extension = "url"
|
||||
CreateUrlFile(URL, fileUrl)
|
||||
If fileUrl.Exists Then Files.Add(fileUrl)
|
||||
If fileUrl.Exists Then AddFile(fileUrl)
|
||||
End If
|
||||
|
||||
If MyYouTubeSettings.CreateDescriptionFiles And Not Description.IsEmptyString Then
|
||||
Dim fileDesr As SFile = File
|
||||
fileDesr.Extension = "txt"
|
||||
TextSaver.SaveTextToFile(Description, fileDesr,,, EDP.None)
|
||||
If fileDesr.Exists Then Files.Add(fileDesr)
|
||||
End If
|
||||
With MyYouTubeSettings
|
||||
If .CreateDescriptionFiles And (Not Description.IsEmptyString Or
|
||||
(.CreateDescriptionFiles_CreateWithNoDescription And .CreateDescriptionFiles_AddUploadDate)) Then
|
||||
Dim fileDesr As SFile = File
|
||||
fileDesr.Extension = "txt"
|
||||
Using fileDesrText As New TextSaver(fileDesr)
|
||||
If .CreateDescriptionFiles_AddUploadDate Then fileDesrText.Append($"Uploaded: {DateAdded:yyyy-MM-dd HH:mm:ss}")
|
||||
If Not Description.IsEmptyString Then
|
||||
If Not fileDesrText.IsEmptyString Then fileDesrText.AppendLine.AppendLine()
|
||||
fileDesrText.Append(Description)
|
||||
End If
|
||||
fileDesrText.Save(EDP.None)
|
||||
End Using
|
||||
If fileDesr.Exists Then AddFile(fileDesr)
|
||||
End If
|
||||
End With
|
||||
|
||||
If PlaylistCount > 0 And Not CoverDownloaded And Not PlaylistID.IsEmptyString Then DownloadPlaylistCover(PlaylistID, File, UseCookies)
|
||||
If prExists Then Progress.InformationTemporary = $"Download {MediaType}: post processing"
|
||||
If prExists Then Progress.InformationTemporary = "Downloading: post processing"
|
||||
_ThumbnailFile = File
|
||||
_ThumbnailFile.Name &= "_thumb"
|
||||
_ThumbnailFile.Extension = "jpg"
|
||||
@@ -1006,76 +1232,179 @@ Namespace API.YouTube.Objects
|
||||
Dim format$
|
||||
Dim fPattern$ = $"{File.PathWithSeparator}{File.Name}." & "{0}"
|
||||
Dim fPatternFiles$ = $"{File.Name}*." & "{0}"
|
||||
Dim fAacAudio As New SFile(String.Format(fPattern, aac))
|
||||
Dim fAc3Audio As New SFile(String.Format(fPattern, ac3))
|
||||
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 fAacAudio As New TempFileConversion(New SFile(String.Format(fPattern, aac)), Me)
|
||||
Dim mp3ThumbEmbedded As Boolean = False
|
||||
|
||||
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)
|
||||
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)
|
||||
If files.ListExists Then
|
||||
For Each f In files
|
||||
For Each format In PostProcessing_OutputSubtitlesFormats
|
||||
format = format.StringToLower
|
||||
commandFile = $"{f.PathWithSeparator}{f.Name}.{format}"
|
||||
Me.Files.Add(commandFile)
|
||||
ThrowAny(Token)
|
||||
.Execute($"ffmpeg -i ""{f}"" ""{commandFile}""")
|
||||
AddFile(files)
|
||||
If PostProcessing_OutputSubtitlesFormats.Count > 0 Then
|
||||
For Each f In files
|
||||
For Each format In PostProcessing_OutputSubtitlesFormats
|
||||
format = format.StringToLower
|
||||
commandFile = $"{f.PathWithSeparator}{f.Name}.{format}"
|
||||
AddFile(commandFile)
|
||||
ThrowAny(Token)
|
||||
.Execute($"ffmpeg -i ""{f}"" ""{commandFile}""")
|
||||
Next
|
||||
Next
|
||||
Next
|
||||
End If
|
||||
End If
|
||||
End If
|
||||
|
||||
'Audio
|
||||
ThrowAny(Token)
|
||||
If PostProcessing_OutputAudioFormats.Count > 0 Or PostProcessing_AudioAC3 Then
|
||||
If Not fAacAudio.Exists Then .Execute($"ffmpeg -i ""{File}"" -vn -acodec {aac} ""{fAacAudio}""")
|
||||
If PostProcessing_AudioAC3 And Not fAc3Audio.Exists Then
|
||||
ThrowAny(Token)
|
||||
.Execute($"ffmpeg -i ""{File}"" -vn -acodec {ac3} ""{fAc3Audio}""")
|
||||
If Not fAc3Audio.Exists And fAacAudio.Exists Then ThrowAny(Token) : .Execute($"ffmpeg -i ""{fAacAudio}"" -f {ac3} ""{fAc3Audio}""")
|
||||
If PostProcessing_OutputAudioFormats.Count > 0 Or PostProcessing_AudioAC3 Or PostProcessing_AudioMP3 Or __updateBitrate Then
|
||||
|
||||
If PostProcessing_AudioAC3 Then
|
||||
ttFile = New TempFileConversion(New SFile(String.Format(fPattern, ac3)), Me) With {.ToReplace = True}
|
||||
tempFilesList.Add(ttFile)
|
||||
If Not ttFile.Exists Then tryToConvert.Invoke(ac3, ttFile.File)
|
||||
updateBitrate.Invoke(ttFile.File)
|
||||
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
|
||||
For Each format In PostProcessing_OutputAudioFormats
|
||||
format = format.StringToLower
|
||||
f = String.Format(fPattern, format)
|
||||
Me.Files.Add(f)
|
||||
If Not format = ac3 Or Not f.Exists Then ThrowAny(Token) : .Execute($"ffmpeg -i ""{fAacAudio}"" -f {format} ""{f}""")
|
||||
AddFile(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)
|
||||
If format = mp3 AndAlso f.Exists AndAlso MyYouTubeSettings.VideoPlaylist_AddExtractedMP3.Value Then M3U8_Append(f)
|
||||
End If
|
||||
Next
|
||||
End If
|
||||
End If
|
||||
|
||||
'Update video
|
||||
ThrowAny(Token)
|
||||
If PostProcessing_AudioAC3 Then
|
||||
If SelectedVideoIndex >= 0 AndAlso tempFilesList.Count > 0 AndAlso tempFilesList.Exists(Function(tf) tf.ToReplace) Then
|
||||
f = File
|
||||
If SelectedVideoIndex >= 0 Then
|
||||
f.Name &= "tmp00"
|
||||
Else
|
||||
f.Extension = ac3
|
||||
f.Name &= "tmp00"
|
||||
Dim tfr As SFile = tempFilesList.FirstOrDefault(Function(tf) tf.ToReplace).File
|
||||
If tfr.Exists And Not f.Exists Then
|
||||
ThrowAny(Token)
|
||||
.Execute($"ffmpeg -i ""{File}"" -i ""{tfr}"" -c:v copy -c copy -map 0:v:0 -map 1:a:0 ""{f}""")
|
||||
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
|
||||
File.Delete()
|
||||
If SelectedVideoIndex >= 0 Then SFile.Rename(f, File,, EDP.LogMessageValue)
|
||||
End If
|
||||
If fAacAudio.Exists And Not aacRequested Then fAacAudio.Delete()
|
||||
If fAc3Audio.Exists And Not ac3Requested And SelectedVideoIndex >= 0 Then fAc3Audio.Delete()
|
||||
End If
|
||||
|
||||
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
|
||||
'Delete unrequsted files
|
||||
If tempFilesList.Count > 0 Then tempFilesList.ForEach(Sub(tfr) If Not tfr.Requested Then tfr.File.Delete(,, EDP.None)) : tempFilesList.Clear()
|
||||
|
||||
If SelectedVideoIndex >= 0 Then
|
||||
Dim reencodeFile As Action(Of String) =
|
||||
Sub(ByVal ffmpegCommand As String)
|
||||
f = File
|
||||
f.Name &= "tmp00"
|
||||
.Execute(String.Format(ffmpegCommand, File.ToString, f.ToString))
|
||||
If f.Exists Then
|
||||
If f.Size > 0 Then
|
||||
File.Delete()
|
||||
SFile.Rename(f, File,, EDP.LogMessageValue)
|
||||
Else
|
||||
f.Delete(, SFODelete.DeletePermanently, EDP.None)
|
||||
End If
|
||||
End If
|
||||
End Sub
|
||||
'Change video codec to AVC
|
||||
If MyYouTubeSettings.DefaultVideoConvertNonAVC.Value AndAlso
|
||||
Not SelectedVideo.Codec.IsEmptyString AndAlso Not SelectedVideo.Codec.Trim.ToLower.StartsWith("avc") Then _
|
||||
reencodeFile("ffmpeg -i ""{0}"" -c:a copy -c:v libx264 ""{1}""")
|
||||
|
||||
'Update video FPS
|
||||
If OutputVideoFPS > 0 AndAlso SelectedVideo.Bitrate <> OutputVideoFPS Then _
|
||||
reencodeFile("ffmpeg -i ""{0}"" -filter:v fps=" & OutputVideoFPS.ToString.Replace(", ", ".") & " -c:a copy ""{1}""")
|
||||
End If
|
||||
End If
|
||||
End If
|
||||
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
|
||||
_MediaState = UMStates.Downloaded
|
||||
Catch oex As OperationCanceledException When Token.IsCancellationRequested
|
||||
@@ -1092,6 +1421,33 @@ Namespace API.YouTube.Objects
|
||||
End If
|
||||
End Try
|
||||
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
|
||||
#Region "Load"
|
||||
Private Sub ApplyElementCheckedValue(ByVal e As EContainer)
|
||||
@@ -1221,6 +1577,7 @@ Namespace API.YouTube.Objects
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Parse"
|
||||
Friend Const DRC As String = "drc"
|
||||
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
|
||||
Try
|
||||
@@ -1253,7 +1610,7 @@ Namespace API.YouTube.Objects
|
||||
ID = .Value("id")
|
||||
Title = TitleHtmlConverter.Invoke(.Value("title"))
|
||||
Description = .Value("description")
|
||||
URL = .Value("webpage_url")
|
||||
URL = .Value("webpage_url").ToMusicUrl(IsMusic)
|
||||
|
||||
PlaylistID = .Value("playlist_id")
|
||||
PlaylistCount = .Value("n_entries").IfNullOrEmpty(.Value("playlist_count")).FromXML(Of Integer)(0)
|
||||
@@ -1283,6 +1640,7 @@ Namespace API.YouTube.Objects
|
||||
_File.Name = $"{ID}.{ext}"
|
||||
End If
|
||||
If Not MyYouTubeSettings.OutputPath.IsEmptyString Then _File.Path = MyYouTubeSettings.OutputPath.Value.Path
|
||||
_File = CleanFileName(_File)
|
||||
File = _File
|
||||
|
||||
If .Contains("duration") Then
|
||||
@@ -1290,6 +1648,7 @@ Namespace API.YouTube.Objects
|
||||
If tValue.HasValue Then Duration = TimeSpan.FromSeconds(tValue.Value)
|
||||
End If
|
||||
DateAdded = AConvert(Of Date)(.Value("release_date").IfNullOrEmpty(.Value("upload_date")), DateAddedProvider, New Date)
|
||||
If Not IsMusic Then FileDateUpdate()
|
||||
|
||||
ParseFormats(.Self)
|
||||
|
||||
@@ -1372,7 +1731,8 @@ Namespace API.YouTube.Objects
|
||||
obj = New MediaObject With {
|
||||
.ID = ee.Value("format_id"),
|
||||
.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.Height = AConvert(Of Integer)(ee.Value("height"), NumberProvider, -1)
|
||||
@@ -1427,6 +1787,14 @@ Namespace API.YouTube.Objects
|
||||
If MediaObjects.Count > 0 AndAlso MediaObjects.LongCount(CountAVC) > 0 Then MediaObjects.RemoveAll(RemoveAVC)
|
||||
Next
|
||||
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
|
||||
Dim protocolCleaner As Action =
|
||||
Sub()
|
||||
@@ -1608,7 +1976,7 @@ Namespace API.YouTube.Objects
|
||||
_SubtitlesDelegated.Clear()
|
||||
SubtitlesSelectedIndexes.Clear()
|
||||
MediaObjects.Clear()
|
||||
Files.Clear()
|
||||
_Files.Clear()
|
||||
PostProcessing_OutputAudioFormats.Clear()
|
||||
PostProcessing_OutputSubtitlesFormats.Clear()
|
||||
End If
|
||||
|
||||
@@ -115,6 +115,9 @@
|
||||
<ItemGroup>
|
||||
<Compile Include="Attributes\GridVisibleAttribute.vb" />
|
||||
<Compile Include="Base\TableControlsProcessor.vb" />
|
||||
<Compile Include="Controls\ButtonRC.vb">
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Controls\ChannelTabsChooserForm.Designer.vb">
|
||||
<DependentUpon>ChannelTabsChooserForm.vb</DependentUpon>
|
||||
</Compile>
|
||||
|
||||
@@ -32,6 +32,6 @@ Imports System.Runtime.InteropServices
|
||||
' by using the '*' as shown below:
|
||||
' <Assembly: AssemblyVersion("1.0.*")>
|
||||
|
||||
<Assembly: AssemblyVersion("2024.2.25.0")>
|
||||
<Assembly: AssemblyFileVersion("2024.2.25.0")>
|
||||
<Assembly: AssemblyVersion("2024.7.24.0")>
|
||||
<Assembly: AssemblyFileVersion("2024.7.24.0")>
|
||||
<Assembly: NeutralResourcesLanguage("en")>
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports System.Runtime.CompilerServices
|
||||
Imports PersonalUtilities.Forms
|
||||
Imports PersonalUtilities.Functions.RegularExpressions
|
||||
Namespace API.Base
|
||||
@@ -49,7 +50,7 @@ Namespace API.Base
|
||||
End Sub
|
||||
Public Overrides Function Convert(ByVal Value As Object, ByVal DestinationType As Type, ByVal Provider As IFormatProvider,
|
||||
Optional ByVal NothingArg As Object = Nothing, Optional ByVal e As ErrorsDescriber = Nothing) As Object
|
||||
Dim v% = AConvert(Of Integer)(Value, -1)
|
||||
Dim v% = AConvert(Of Integer)(Value, -1, EDP.ReturnValue)
|
||||
If v > 0 Then
|
||||
Return Value
|
||||
ElseIf Not ACheck(Of Integer)(Value) Then
|
||||
@@ -72,5 +73,12 @@ Namespace API.Base
|
||||
$"Current query: [{CurrentQuery}]{vbCr}New query: [{NewQuery}]",
|
||||
"Changing a query"}, vbExclamation,,, {"Process", "Cancel"}) = 0
|
||||
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 Namespace
|
||||
@@ -11,7 +11,8 @@ Namespace API.Base
|
||||
Friend Const Header_Authorization As String = "authorization"
|
||||
Friend Const Header_CSRFToken As String = "x-csrf-token"
|
||||
|
||||
Friend Const Header_FB_FRIENDLY_NAME As String = "x-fb-friendly-name"
|
||||
Friend Const CAT_UserDefs As String = "New user defaults"
|
||||
Friend Const CAT_Timers As String = "Timers"
|
||||
|
||||
Friend Const ConcurrentDownloadsCaption As String = "Concurrent downloads"
|
||||
Friend Const ConcurrentDownloadsToolTip As String = "The number of concurrent downloads."
|
||||
|
||||
@@ -59,7 +59,6 @@ Namespace API.Base
|
||||
ReadOnly Property DownloadedTotal(Optional ByVal Total As Boolean = True) As Integer
|
||||
ReadOnly Property DownloadedInformation As String
|
||||
Property HasError As Boolean
|
||||
ReadOnly Property FitToAddParams As Boolean
|
||||
ReadOnly Property Key As String
|
||||
Property DownloadImages As Boolean
|
||||
Property DownloadVideos As Boolean
|
||||
@@ -78,7 +77,7 @@ Namespace API.Base
|
||||
''' </summary>
|
||||
Function Delete(Optional ByVal Multiple As Boolean = False, Optional ByVal CollectionValue As Integer = -1) As Integer
|
||||
Function EraseData(ByVal Mode As EraseMode) As Boolean
|
||||
Function MoveFiles(ByVal CollectionName As String, ByVal SpecialCollectionPath As SFile) As Boolean
|
||||
Function MoveFiles(ByVal CollectionName As String, ByVal SpecialCollectionPath As SFile, Optional ByVal NewUser As SplitCollectionUserInfo? = Nothing) As Boolean
|
||||
Function CopyFiles(ByVal DestinationPath As SFile, Optional ByVal e As ErrorsDescriber = Nothing) As Boolean
|
||||
Sub OpenFolder()
|
||||
Property DownloadTopCount As Integer?
|
||||
|
||||
@@ -42,6 +42,13 @@ Namespace API.Base
|
||||
Friend NotInheritable Class M3U8Base
|
||||
Friend Const TempCacheFolderName As String = "tmpCache"
|
||||
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()
|
||||
End Sub
|
||||
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,
|
||||
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 OnlyDownload As Boolean = False) As SFile
|
||||
Const defaultExtension$ = "ts"
|
||||
Optional ByVal OnlyDownload As Boolean = False, Optional ByVal SkipBroken As Boolean = False) As SFile
|
||||
Dim Cache As CacheKeeper = Nothing
|
||||
Using tmpPr As New PreProgress(Progress)
|
||||
Try
|
||||
@@ -89,13 +95,13 @@ Namespace API.Base
|
||||
End If
|
||||
End If
|
||||
Dim p As SFileNumbers = SFileNumbers.Default(ConcatFile.Name)
|
||||
Dim pNum As ANumbers = SFileNumbers.NumberProviderDefault
|
||||
Dim pNum As ANumbers = NumberProviderDefault
|
||||
p.NumberProvider = pNum
|
||||
DirectCast(p.NumberProvider, ANumbers).GroupSize = {URLs.Count.ToString.Length, 3}.Max
|
||||
ConcatFile = SFile.IndexReindex(ConcatFile,,, p, EDP.ReturnValue)
|
||||
Dim i%
|
||||
Dim dFile As SFile = cache2.RootDirectory
|
||||
dFile.Extension = defaultExtension
|
||||
dFile.Extension = TempFileDefaultExtension
|
||||
Using w As New DownloadObjects.WebClient2(Responser)
|
||||
For i = 0 To URLs.Count - 1
|
||||
If progressExists Then
|
||||
@@ -107,9 +113,13 @@ Namespace API.Base
|
||||
End If
|
||||
Token.ThrowIfCancellationRequested()
|
||||
dFile.Name = $"{TempFilePrefix}{i.NumToString(pNum)}"
|
||||
dFile.Extension = URLs(i).Extension.IfNullOrEmpty(defaultExtension)
|
||||
w.DownloadFile(URLs(i).URL, dFile)
|
||||
cache2.AddFile(dFile, True)
|
||||
dFile.Extension = URLs(i).Extension.IfNullOrEmpty(TempFileDefaultExtension)
|
||||
Try
|
||||
w.DownloadFile(URLs(i).URL, dFile)
|
||||
cache2.AddFile(dFile, True)
|
||||
Catch ex As Exception
|
||||
If Not SkipBroken Then Throw ex
|
||||
End Try
|
||||
Next
|
||||
End Using
|
||||
If Not OnlyDownload Then _
|
||||
|
||||
@@ -54,9 +54,8 @@ Namespace API.Base
|
||||
Dim aStr$ = String.Empty
|
||||
If Count > 1 Then aStr = $" ({Number}/{Count})"
|
||||
Try
|
||||
If Host.Source.ReadyToDownload(PDownload.SavedPosts) Then
|
||||
If Host.Available(PDownload.SavedPosts, Multiple Or Count > 1) Then
|
||||
Host.DownloadStarted(PDownload.SavedPosts)
|
||||
If Host.Available(PDownload.SavedPosts, Multiple Or Count > 1) Then
|
||||
If Host.Source.ReadyToDownload(PDownload.SavedPosts) Then
|
||||
If Count > 1 Then Progress.Information = $"{Host.Name} - {Host.AccountName.IfNullOrEmpty(SettingsHost.NameAccountNameDefault)}"
|
||||
Using user As IUserData = Host.GetInstance(PDownload.SavedPosts, Nothing, False, False)
|
||||
If Not user Is Nothing Then
|
||||
@@ -83,11 +82,11 @@ Namespace API.Base
|
||||
End Using
|
||||
Else
|
||||
_Unavailable += 1
|
||||
Progress.InformationTemporary = $"Host [{Host.Name}{aStr}] is unavailable"
|
||||
Progress.InformationTemporary = $"Host [{Host.Name}{aStr}] is not ready"
|
||||
End If
|
||||
Else
|
||||
_NotReady += 1
|
||||
Progress.InformationTemporary = $"Host [{Host.Name}{aStr}] is not ready"
|
||||
Progress.InformationTemporary = $"Host [{Host.Name}{aStr}] is unavailable"
|
||||
End If
|
||||
Catch oex As OperationCanceledException When Token.IsCancellationRequested
|
||||
_ErrorCount += 1
|
||||
@@ -96,9 +95,6 @@ Namespace API.Base
|
||||
_ErrorCount += 1
|
||||
Progress.InformationTemporary = $"{Host.Name}{aStr} downloading error"
|
||||
ErrorsDescriber.Execute(EDP.SendToLog, ex, $"[API.Base.ProfileSaved.Download({Host.Key}{aStr})]")
|
||||
Finally
|
||||
Host.DownloadDone(PDownload.SavedPosts)
|
||||
MainFrameObj.UpdateLogButton()
|
||||
End Try
|
||||
End Sub
|
||||
End Class
|
||||
|
||||
@@ -17,6 +17,7 @@ Imports Download = SCrawler.Plugin.ISiteSettings.Download
|
||||
Namespace API.Base
|
||||
Friend MustInherit Class SiteSettingsBase : Implements ISiteSettings, IResponserContainer
|
||||
#Region "Declarations"
|
||||
<PXML> Protected ReadOnly Property SettingsVersion As PropertyValue
|
||||
Friend ReadOnly Property Site As String Implements ISiteSettings.Site
|
||||
Protected _Icon As Icon = Nothing
|
||||
Friend Overridable ReadOnly Property Icon As Icon Implements ISiteSettings.Icon
|
||||
@@ -33,6 +34,16 @@ Namespace API.Base
|
||||
Friend Property AccountName As String Implements ISiteSettings.AccountName
|
||||
Friend Property Temporary As Boolean = False Implements ISiteSettings.Temporary
|
||||
Friend Property DefaultInstance As ISiteSettings = Nothing Implements ISiteSettings.DefaultInstance
|
||||
Protected _UserAgentDefault As String = String.Empty
|
||||
Friend Overridable Property UserAgentDefault As String Implements ISiteSettings.UserAgentDefault
|
||||
Get
|
||||
Return _UserAgentDefault
|
||||
End Get
|
||||
Set(ByVal _UserAgentDefault As String)
|
||||
Me._UserAgentDefault = _UserAgentDefault
|
||||
If _AllowUserAgentUpdate And Not Responser Is Nothing And Not _UserAgentDefault.IsEmptyString Then Responser.UserAgent = _UserAgentDefault
|
||||
End Set
|
||||
End Property
|
||||
Protected _AllowUserAgentUpdate As Boolean = True
|
||||
Protected _SubscriptionsAllowed As Boolean = False
|
||||
Friend ReadOnly Property SubscriptionsAllowed As Boolean Implements ISiteSettings.SubscriptionsAllowed
|
||||
@@ -54,7 +65,14 @@ Namespace API.Base
|
||||
End Set
|
||||
End Property
|
||||
#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"
|
||||
Friend Const ResponserFilePrefix As String = "Responser_"
|
||||
Private _CookiesNetscapeFile As SFile = Nothing
|
||||
Friend ReadOnly Property CookiesNetscapeFile As SFile
|
||||
Get
|
||||
@@ -85,7 +103,7 @@ Namespace API.Base
|
||||
End Property
|
||||
Protected Sub UpdateResponserFile()
|
||||
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.Name &= "_Cookies_Netscape"
|
||||
_CookiesNetscapeFile.Extension = "txt"
|
||||
@@ -100,6 +118,7 @@ Namespace API.Base
|
||||
_Icon = __Icon
|
||||
_Image = __Image
|
||||
Responser = New Responser With {.DeclaredError = EDP.ThrowException}
|
||||
SettingsVersion = New PropertyValue(0)
|
||||
UpdateResponserFile()
|
||||
End Sub
|
||||
Friend Sub New(ByVal SiteName As String, ByVal CookiesDomain As String, ByVal AccName As String, ByVal Temp As Boolean,
|
||||
@@ -129,7 +148,6 @@ Namespace API.Base
|
||||
Friend Overridable Sub BeginInit() Implements ISiteSettings.BeginInit
|
||||
End Sub
|
||||
Friend Overridable Sub EndInit() Implements ISiteSettings.EndInit
|
||||
If _AllowUserAgentUpdate And Not DefaultUserAgent.IsEmptyString And Not Responser Is Nothing Then Responser.UserAgent = DefaultUserAgent
|
||||
If CheckNetscapeCookiesOnEndInit Then Update_SaveCookiesNetscape(, True)
|
||||
End Sub
|
||||
#End Region
|
||||
|
||||
@@ -143,7 +143,7 @@ Namespace API.Base
|
||||
Protected Const Name_UserID As String = "UserID"
|
||||
Protected Const Name_Options As String = "Options"
|
||||
Protected Const Name_Description As String = "Description"
|
||||
Private Const Name_ParseUserMediaOnly As String = "ParseUserMediaOnly"
|
||||
Protected Const Name_ParseUserMediaOnly As String = "ParseUserMediaOnly"
|
||||
Private Const Name_IsSubscription As String = UserInfo.Name_IsSubscription
|
||||
Private Const Name_Temporary As String = "Temporary"
|
||||
Private Const Name_Favorite As String = "Favorite"
|
||||
@@ -312,7 +312,7 @@ Namespace API.Base
|
||||
End Set
|
||||
End Property
|
||||
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
|
||||
Friend ReadOnly Property UserModel As UsageModel Implements IUserData.UserModel
|
||||
Get
|
||||
@@ -829,48 +829,19 @@ BlockNullPicture:
|
||||
Return ListImagesLoader.ApplyLVIColor(Me, New ListViewItem(ToString(), GetLVIGroup(Destination)) With {.Name = LVIKey, .Tag = LVIKey}, True)
|
||||
End If
|
||||
End Function
|
||||
Friend Overridable ReadOnly Property FitToAddParams As Boolean Implements IUserData.FitToAddParams
|
||||
Get
|
||||
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
|
||||
Try
|
||||
If Settings.ShowingMode.Value = ShowingModes.Labels And Not Settings.ShowGroupsInsteadLabels Then
|
||||
If Labels.Count > 0 And Settings.Labels.Current.Count > 0 Then
|
||||
For i% = 0 To Labels.Count - 1
|
||||
If Settings.Labels.Current.Contains(Labels(i)) Then Return Destination.Groups.Item(Labels(i))
|
||||
Next
|
||||
With Settings
|
||||
If Not .ShowAllUsers.Value AndAlso (.AdvancedFilter.Labels.Count > 0 Or .AdvancedFilter.LabelsNo) AndAlso Not .ShowGroupsInsteadLabels Then
|
||||
If Labels.Count > 0 And .AdvancedFilter.Labels.Count > 0 Then
|
||||
For i% = 0 To Labels.Count - 1
|
||||
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
|
||||
ElseIf Settings.ShowGroups Then
|
||||
Return Destination.Groups.Item(GetLviGroupName(HOST, Temporary, Favorite, IsCollection))
|
||||
End If
|
||||
End With
|
||||
Return Destination.Groups.Item(LabelsKeeper.NoLabeledName)
|
||||
Catch ex As Exception
|
||||
Return Destination.Groups.Item(LabelsKeeper.NoLabeledName)
|
||||
@@ -979,7 +950,10 @@ BlockNullPicture:
|
||||
LogError(ex, "user information loading error")
|
||||
End Try
|
||||
End Sub
|
||||
Friend Overridable Sub UpdateUserInformation() Implements IUserData.UpdateUserInformation
|
||||
Friend Overridable Overloads Sub UpdateUserInformation() Implements IUserData.UpdateUserInformation
|
||||
UpdateUserInformation(False)
|
||||
End Sub
|
||||
Friend Overridable Overloads Sub UpdateUserInformation(ByVal DisableUserInfoUpdate As Boolean)
|
||||
Try
|
||||
UpdateDataFiles()
|
||||
MyFileSettings.Exists(SFO.Path)
|
||||
@@ -1030,7 +1004,7 @@ BlockNullPicture:
|
||||
|
||||
x.Save(MyFileSettings)
|
||||
End Using
|
||||
If Not IsSavedPosts Then Settings.UpdateUsersList(User)
|
||||
If Not IsSavedPosts And Not DisableUserInfoUpdate Then Settings.UpdateUsersList(User, True)
|
||||
Catch ex As Exception
|
||||
LogError(ex, "user information saving error")
|
||||
End Try
|
||||
@@ -1194,7 +1168,7 @@ BlockNullPicture:
|
||||
If Not Responser Is Nothing Then Responser.Dispose()
|
||||
Responser = New Responser
|
||||
If Not HOST.Responser Is Nothing Then Responser.Copy(HOST.Responser)
|
||||
If Not Responser Is Nothing And _ResponserAutoUpdateCookies Then
|
||||
If Not Responser Is Nothing And (_ResponserAutoUpdateCookies Or _ResponserAddResponseReceivedHandler) Then
|
||||
If _ResponserAutoUpdateCookies Then
|
||||
Responser.CookiesUpdateMode = CookieUpdateModes.ReplaceByNameAll
|
||||
Responser.CookiesExtractMode = Responser.CookiesExtractModes.Any
|
||||
@@ -1269,8 +1243,10 @@ BlockNullPicture:
|
||||
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 Not __isChannelsSupport Then
|
||||
LastUpdated = Now
|
||||
RunScript()
|
||||
If DownloadedTotal(False) > 0 Then
|
||||
LastUpdated = Now
|
||||
RunScript()
|
||||
End If
|
||||
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
|
||||
If Labels.Contains(LabelsKeeper.NoParsedUser) Then Labels.Remove(LabelsKeeper.NoParsedUser)
|
||||
@@ -1366,6 +1342,7 @@ BlockNullPicture:
|
||||
ResetHost()
|
||||
URL = Data.URL
|
||||
AccountName = Data.AccountName
|
||||
TokenQueue = Token
|
||||
If HOST Is Nothing Then Throw New ExitException($"Host '{AccountName}' not found")
|
||||
Data.DownloadState = UserMediaStates.Tried
|
||||
Progress = Data.Progress
|
||||
@@ -1409,12 +1386,14 @@ BlockNullPicture:
|
||||
If _ContentNew.Count > 0 Then
|
||||
If _ContentNew.Any(Function(mm) mm.State = UStates.Downloaded) Then
|
||||
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
|
||||
DirectCast(Data, IDownloadableMedia).ThumbnailFile = _ContentNew(0).File
|
||||
ElseIf Settings.STDownloader_TakeSnapshot And Settings.FfmpegFile.Exists And Not Settings.STDownloader_RemoveDownloadedAutomatically Then
|
||||
Dim f As SFile = _ContentNew(0).File
|
||||
Dim ff As SFile
|
||||
If Settings.STDownloader_SnapshotsKeepWithFiles Then
|
||||
If Settings.STDownloader_SnapshotsKeepWithFiles Or thumbAlong Then
|
||||
ff = f
|
||||
Else
|
||||
ff = Settings.CacheSnapshots(Settings.STDownloader_SnapShotsCachePermamnent).NewFile
|
||||
@@ -1424,6 +1403,9 @@ BlockNullPicture:
|
||||
f = Web.FFMPEG.TakeSnapshot(f, ff, Settings.FfmpegFile, TimeSpan.FromSeconds(1),,, EDP.SendToLog + EDP.ReturnValue)
|
||||
If f.Exists Then DirectCast(Data, IDownloadableMedia).ThumbnailFile = f
|
||||
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
|
||||
Data.DownloadState = UserMediaStates.Missing
|
||||
End If
|
||||
@@ -1796,6 +1778,7 @@ BlockNullPicture:
|
||||
Protected Overridable Function ValidateDownloadFile(ByVal URL As String, ByVal Media As UserMedia, ByRef Interrupt As Boolean) As Boolean
|
||||
Return True
|
||||
End Function
|
||||
''' <returns><c>MyFile.CutPath(IIf(IsSingleObjectDownload, 0, 1)).PathNoSeparator</c></returns>
|
||||
Protected Overridable Function DownloadContentDefault_GetRootDir() As String
|
||||
Return MyFile.CutPath(IIf(IsSingleObjectDownload, 0, 1)).PathNoSeparator
|
||||
End Function
|
||||
@@ -1914,7 +1897,9 @@ BlockNullPicture:
|
||||
If m.Contains(IUserData.EraseMode.History) Then
|
||||
If MyFilePosts.Delete(SFO.File, SFODelete.DeleteToRecycleBin, e) Then result = True
|
||||
If MyFileData.Delete(SFO.File, SFODelete.DeleteToRecycleBin, e) Then result = True
|
||||
LastUpdated = Nothing
|
||||
EraseData_AdditionalDataFiles()
|
||||
UpdateUserInformation()
|
||||
End If
|
||||
If m.Contains(IUserData.EraseMode.Data) Then
|
||||
Dim files As List(Of SFile) = SFile.GetFiles(DownloadContentDefault_GetRootDir.CSFileP,, SearchOption.AllDirectories, e)
|
||||
@@ -1936,7 +1921,7 @@ BlockNullPicture:
|
||||
Return ErrorsDescriber.Execute(EDP.SendToLog + EDP.ReturnValue, ex, $"EraseData({CInt(Mode)}): {ToStringForLog()}", False)
|
||||
End Try
|
||||
End Function
|
||||
Protected Overridable Sub EraseData_AdditionalDataFiles()
|
||||
Protected Overridable Sub EraseData_AdditionalDataFiles() Implements IPluginContentProvider.ResetHistoryData
|
||||
End Sub
|
||||
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)
|
||||
@@ -1952,7 +1937,18 @@ BlockNullPicture:
|
||||
Return 0
|
||||
End If
|
||||
End Function
|
||||
Friend Overridable Function MoveFiles(ByVal __CollectionName As String, ByVal __SpecialCollectionPath As SFile) As Boolean Implements IUserData.MoveFiles
|
||||
Friend Function SplitCollectionGetNewUserInfo() As SplitCollectionUserInfo
|
||||
Dim u As New SplitCollectionUserInfo With {.UserOrig = User, .UserNew = User}
|
||||
With u.UserNew
|
||||
.CollectionName = String.Empty
|
||||
.SpecialCollectionPath = Nothing
|
||||
.UserModel = UsageModel.Default
|
||||
.CollectionModel = UsageModel.Default
|
||||
.UpdateUserFile()
|
||||
End With
|
||||
Return u
|
||||
End Function
|
||||
Friend Overridable Function MoveFiles(ByVal __CollectionName As String, ByVal __SpecialCollectionPath As SFile, Optional ByVal NewUser As SplitCollectionUserInfo? = Nothing) As Boolean Implements IUserData.MoveFiles
|
||||
Dim UserBefore As UserInfo = User
|
||||
Dim Removed As Boolean = True
|
||||
Dim _TurnBack As Boolean = False
|
||||
@@ -1968,6 +1964,7 @@ BlockNullPicture:
|
||||
User.SpecialCollectionPath = String.Empty
|
||||
User.UserModel = UsageModel.Default
|
||||
User.CollectionModel = UsageModel.Default
|
||||
If NewUser.HasValue Then User.SpecialPath = NewUser.Value.UserNew.SpecialPath
|
||||
Else
|
||||
Settings.Users.Remove(Me)
|
||||
Removed = True
|
||||
|
||||
@@ -11,7 +11,7 @@ Namespace API.Base.YTDLP
|
||||
Friend Sub New(ByVal _Token As Threading.CancellationToken)
|
||||
MyBase.New(_Token)
|
||||
Commands.Clear()
|
||||
MainProcessName = "yt-dlp"
|
||||
MainProcessName = Settings.YtdlpFile.File.Name '"yt-dlp"
|
||||
ChangeDirectory(Settings.YtdlpFile.File)
|
||||
End Sub
|
||||
End Class
|
||||
|
||||
28
SCrawler/API/BaseObjects/SplitCollectionUserInfo.vb
Normal file
@@ -0,0 +1,28 @@
|
||||
' Copyright (C) Andy https://github.com/AAndyProgram
|
||||
' This program is free software: you can redistribute it and/or modify
|
||||
' it under the terms of the GNU General Public License as published by
|
||||
' the Free Software Foundation, either version 3 of the License, or
|
||||
' (at your option) any later version.
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Namespace API.Base
|
||||
Friend Structure SplitCollectionUserInfo
|
||||
Friend UserOrig As UserInfo
|
||||
Friend UserNew As UserInfo
|
||||
Friend Changed As Boolean
|
||||
Friend ReadOnly Property SameDrive As Boolean
|
||||
Get
|
||||
Return GetUserDrive(UserOrig) = GetUserDrive(UserNew)
|
||||
End Get
|
||||
End Property
|
||||
Private Shared Function GetUserDrive(ByVal User As UserInfo) As String
|
||||
Dim u As UserInfo = User
|
||||
If u.File.IsEmptyString Then u.UpdateUserFile()
|
||||
Return u.File.Segments.FirstOrDefault.StringToLower
|
||||
End Function
|
||||
Public Overrides Function ToString() As String
|
||||
Return $"[{UserOrig.File.CutPath.PathWithSeparator}] -> [{UserNew.File.CutPath.PathWithSeparator}]"
|
||||
End Function
|
||||
End Structure
|
||||
End Namespace
|
||||
111
SCrawler/API/BaseObjects/SplitCollectionUserInfoChangePathsForm.Designer.vb
generated
Normal file
@@ -0,0 +1,111 @@
|
||||
' Copyright (C) Andy https://github.com/AAndyProgram
|
||||
' This program is free software: you can redistribute it and/or modify
|
||||
' it under the terms of the GNU General Public License as published by
|
||||
' the Free Software Foundation, either version 3 of the License, or
|
||||
' (at your option) any later version.
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Namespace API.Base
|
||||
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()>
|
||||
Partial Friend Class SplitCollectionUserInfoChangePathsForm : Inherits System.Windows.Forms.Form
|
||||
<System.Diagnostics.DebuggerNonUserCode()>
|
||||
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
|
||||
Try
|
||||
If disposing AndAlso components IsNot Nothing Then
|
||||
components.Dispose()
|
||||
End If
|
||||
Finally
|
||||
MyBase.Dispose(disposing)
|
||||
End Try
|
||||
End Sub
|
||||
Private components As System.ComponentModel.IContainer
|
||||
<System.Diagnostics.DebuggerStepThrough()>
|
||||
Private Sub InitializeComponent()
|
||||
Dim CONTAINER_MAIN As System.Windows.Forms.ToolStripContainer
|
||||
Dim TP_MAIN As System.Windows.Forms.TableLayoutPanel
|
||||
Dim LBL_INFO As System.Windows.Forms.Label
|
||||
Me.LIST_USERS = New System.Windows.Forms.ListBox()
|
||||
CONTAINER_MAIN = New System.Windows.Forms.ToolStripContainer()
|
||||
TP_MAIN = New System.Windows.Forms.TableLayoutPanel()
|
||||
LBL_INFO = New System.Windows.Forms.Label()
|
||||
CONTAINER_MAIN.ContentPanel.SuspendLayout()
|
||||
CONTAINER_MAIN.SuspendLayout()
|
||||
TP_MAIN.SuspendLayout()
|
||||
Me.SuspendLayout()
|
||||
'
|
||||
'CONTAINER_MAIN
|
||||
'
|
||||
'
|
||||
'CONTAINER_MAIN.ContentPanel
|
||||
'
|
||||
CONTAINER_MAIN.ContentPanel.Controls.Add(TP_MAIN)
|
||||
CONTAINER_MAIN.ContentPanel.Size = New System.Drawing.Size(384, 261)
|
||||
CONTAINER_MAIN.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
CONTAINER_MAIN.LeftToolStripPanelVisible = False
|
||||
CONTAINER_MAIN.Location = New System.Drawing.Point(0, 0)
|
||||
CONTAINER_MAIN.Name = "CONTAINER_MAIN"
|
||||
CONTAINER_MAIN.RightToolStripPanelVisible = False
|
||||
CONTAINER_MAIN.Size = New System.Drawing.Size(384, 261)
|
||||
CONTAINER_MAIN.TabIndex = 0
|
||||
CONTAINER_MAIN.TopToolStripPanelVisible = False
|
||||
'
|
||||
'TP_MAIN
|
||||
'
|
||||
TP_MAIN.ColumnCount = 1
|
||||
TP_MAIN.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
||||
TP_MAIN.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20.0!))
|
||||
TP_MAIN.Controls.Add(LBL_INFO, 0, 0)
|
||||
TP_MAIN.Controls.Add(Me.LIST_USERS, 0, 1)
|
||||
TP_MAIN.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
TP_MAIN.Location = New System.Drawing.Point(0, 0)
|
||||
TP_MAIN.Name = "TP_MAIN"
|
||||
TP_MAIN.RowCount = 2
|
||||
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 50.0!))
|
||||
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
||||
TP_MAIN.Size = New System.Drawing.Size(384, 261)
|
||||
TP_MAIN.TabIndex = 0
|
||||
'
|
||||
'LBL_INFO
|
||||
'
|
||||
LBL_INFO.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
LBL_INFO.Location = New System.Drawing.Point(3, 0)
|
||||
LBL_INFO.Name = "LBL_INFO"
|
||||
LBL_INFO.Size = New System.Drawing.Size(378, 50)
|
||||
LBL_INFO.TabIndex = 0
|
||||
LBL_INFO.Text = "Check the user destination paths and change them if necessary." & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "Double-click to c" &
|
||||
"hange."
|
||||
LBL_INFO.TextAlign = System.Drawing.ContentAlignment.MiddleCenter
|
||||
'
|
||||
'LIST_USERS
|
||||
'
|
||||
Me.LIST_USERS.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
Me.LIST_USERS.FormattingEnabled = True
|
||||
Me.LIST_USERS.Location = New System.Drawing.Point(3, 53)
|
||||
Me.LIST_USERS.Name = "LIST_USERS"
|
||||
Me.LIST_USERS.Size = New System.Drawing.Size(378, 205)
|
||||
Me.LIST_USERS.TabIndex = 1
|
||||
'
|
||||
'SplitCollectionUserInfoChangePathsForm
|
||||
'
|
||||
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
|
||||
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
|
||||
Me.ClientSize = New System.Drawing.Size(384, 261)
|
||||
Me.Controls.Add(CONTAINER_MAIN)
|
||||
Me.Icon = Global.SCrawler.My.Resources.Resources.UsersIcon_32
|
||||
Me.KeyPreview = True
|
||||
Me.MinimumSize = New System.Drawing.Size(400, 300)
|
||||
Me.Name = "SplitCollectionUserInfoChangePathsForm"
|
||||
Me.ShowInTaskbar = False
|
||||
Me.Text = "Collection users"
|
||||
CONTAINER_MAIN.ContentPanel.ResumeLayout(False)
|
||||
CONTAINER_MAIN.ResumeLayout(False)
|
||||
CONTAINER_MAIN.PerformLayout()
|
||||
TP_MAIN.ResumeLayout(False)
|
||||
Me.ResumeLayout(False)
|
||||
|
||||
End Sub
|
||||
|
||||
Private WithEvents LIST_USERS As ListBox
|
||||
End Class
|
||||
End Namespace
|
||||
@@ -0,0 +1,129 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<metadata name="CONTAINER_MAIN.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>False</value>
|
||||
</metadata>
|
||||
<metadata name="TP_MAIN.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>False</value>
|
||||
</metadata>
|
||||
<metadata name="LBL_INFO.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>False</value>
|
||||
</metadata>
|
||||
</root>
|
||||
@@ -0,0 +1,78 @@
|
||||
' Copyright (C) Andy https://github.com/AAndyProgram
|
||||
' This program is free software: you can redistribute it and/or modify
|
||||
' it under the terms of the GNU General Public License as published by
|
||||
' the Free Software Foundation, either version 3 of the License, or
|
||||
' (at your option) any later version.
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports PersonalUtilities.Forms
|
||||
Imports PersonalUtilities.Functions.Messaging
|
||||
Namespace API.Base
|
||||
Friend Class SplitCollectionUserInfoChangePathsForm
|
||||
Private WithEvents MyDefs As DefaultFormOptions
|
||||
Friend ReadOnly Property Users As List(Of SplitCollectionUserInfo)
|
||||
''' <summary>
|
||||
''' Cancel = use initial<br/>
|
||||
''' Abort = abort operation<br/>
|
||||
''' OK = use changes
|
||||
''' </summary>
|
||||
Friend Sub New(ByVal _Users As IEnumerable(Of SplitCollectionUserInfo))
|
||||
InitializeComponent()
|
||||
MyDefs = New DefaultFormOptions(Me, Settings.Design)
|
||||
Users = New List(Of SplitCollectionUserInfo)(_Users)
|
||||
End Sub
|
||||
Private Sub SplitCollectionUserInfoChangePathsForm_Load(sender As Object, e As EventArgs) Handles Me.Load
|
||||
With MyDefs
|
||||
.MyViewInitialize()
|
||||
.AddOkCancelToolbar()
|
||||
LIST_USERS.Items.AddRange(Users.Cast(Of Object).ToArray)
|
||||
.EndLoaderOperations()
|
||||
.MyOkCancel.EnableOK = True
|
||||
End With
|
||||
End Sub
|
||||
Private Sub MyDefs_ButtonOkClick(ByVal Sender As Object, ByVal e As KeyHandleEventArgs) Handles MyDefs.ButtonOkClick
|
||||
MyDefs.CloseForm()
|
||||
End Sub
|
||||
Private Sub MyDefs_ButtonCancelClick(ByVal Sender As Object, ByVal e As KeyHandleEventArgs) Handles MyDefs.ButtonCancelClick
|
||||
Dim m As New MMessage("You have canceled the change. Do you want to process user(s) as is or cancel the operation?", "Change user paths",
|
||||
{New MsgBoxButton("Initial", "Process users as is (IGNORE changes to this form)") With {.CallBackObject = DialogResult.Cancel},
|
||||
New MsgBoxButton("Process", "Process users as is (INCLUDE changes here)") With {.CallBackObject = DialogResult.OK},
|
||||
New MsgBoxButton("Abort", "Abort operation") With {.CallBackObject = DialogResult.Abort},
|
||||
New MsgBoxButton("Cancel", "Continue editing here") With {.CallBackObject = DialogResult.Retry}},
|
||||
vbExclamation) With {.ButtonsPerRow = 4}
|
||||
Dim result As DialogResult = CInt(MsgBoxE(m).Button.CallBackObject)
|
||||
If result = DialogResult.Retry Then
|
||||
e.Handled = True
|
||||
Exit Sub
|
||||
Else
|
||||
MyDefs.CloseForm(result)
|
||||
End If
|
||||
End Sub
|
||||
Private Sub SplitCollectionUserInfoChangePathsForm_Disposed(sender As Object, e As EventArgs) Handles Me.Disposed
|
||||
Users.Clear()
|
||||
End Sub
|
||||
Private Sub LIST_USERS_MouseDoubleClick(sender As Object, e As MouseEventArgs) Handles LIST_USERS.MouseDoubleClick
|
||||
Try
|
||||
With LIST_USERS
|
||||
If .SelectedIndex >= 0 Then
|
||||
Dim obj As SplitCollectionUserInfo = .Items(.SelectedIndex)
|
||||
Using f As New SplitCollectionUserInfoPathForm(obj)
|
||||
f.ShowDialog()
|
||||
If f.DialogResult = DialogResult.OK Then
|
||||
obj = f.User
|
||||
If obj.Changed Then
|
||||
Users(.SelectedIndex) = obj
|
||||
.Items(.SelectedIndex) = obj
|
||||
.Refresh()
|
||||
End If
|
||||
End If
|
||||
End Using
|
||||
End If
|
||||
End With
|
||||
Catch ex As Exception
|
||||
ErrorsDescriber.Execute(EDP.LogMessageValue, ex, "Change user paths")
|
||||
End Try
|
||||
End Sub
|
||||
End Class
|
||||
End Namespace
|
||||
134
SCrawler/API/BaseObjects/SplitCollectionUserInfoPathForm.Designer.vb
generated
Normal file
@@ -0,0 +1,134 @@
|
||||
' Copyright (C) Andy https://github.com/AAndyProgram
|
||||
' This program is free software: you can redistribute it and/or modify
|
||||
' it under the terms of the GNU General Public License as published by
|
||||
' the Free Software Foundation, either version 3 of the License, or
|
||||
' (at your option) any later version.
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Namespace API.Base
|
||||
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()>
|
||||
Partial Friend Class SplitCollectionUserInfoPathForm : Inherits System.Windows.Forms.Form
|
||||
<System.Diagnostics.DebuggerNonUserCode()>
|
||||
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
|
||||
Try
|
||||
If disposing AndAlso components IsNot Nothing Then
|
||||
components.Dispose()
|
||||
End If
|
||||
Finally
|
||||
MyBase.Dispose(disposing)
|
||||
End Try
|
||||
End Sub
|
||||
Private components As System.ComponentModel.IContainer
|
||||
<System.Diagnostics.DebuggerStepThrough()>
|
||||
Private Sub InitializeComponent()
|
||||
Dim CONTAINER_MAIN As System.Windows.Forms.ToolStripContainer
|
||||
Dim TP_MAIN As System.Windows.Forms.TableLayoutPanel
|
||||
Dim ActionButton1 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||
Dim resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(SplitCollectionUserInfoPathForm))
|
||||
Dim ActionButton2 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||
Me.TXT_PATH_CURR = New PersonalUtilities.Forms.Controls.TextBoxExtended()
|
||||
Me.TXT_PATH_NEW = New PersonalUtilities.Forms.Controls.TextBoxExtended()
|
||||
CONTAINER_MAIN = New System.Windows.Forms.ToolStripContainer()
|
||||
TP_MAIN = New System.Windows.Forms.TableLayoutPanel()
|
||||
CONTAINER_MAIN.ContentPanel.SuspendLayout()
|
||||
CONTAINER_MAIN.SuspendLayout()
|
||||
TP_MAIN.SuspendLayout()
|
||||
CType(Me.TXT_PATH_CURR, System.ComponentModel.ISupportInitialize).BeginInit()
|
||||
CType(Me.TXT_PATH_NEW, System.ComponentModel.ISupportInitialize).BeginInit()
|
||||
Me.SuspendLayout()
|
||||
'
|
||||
'CONTAINER_MAIN
|
||||
'
|
||||
'
|
||||
'CONTAINER_MAIN.ContentPanel
|
||||
'
|
||||
CONTAINER_MAIN.ContentPanel.Controls.Add(TP_MAIN)
|
||||
CONTAINER_MAIN.ContentPanel.Size = New System.Drawing.Size(484, 84)
|
||||
CONTAINER_MAIN.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
CONTAINER_MAIN.LeftToolStripPanelVisible = False
|
||||
CONTAINER_MAIN.Location = New System.Drawing.Point(0, 0)
|
||||
CONTAINER_MAIN.Name = "CONTAINER_MAIN"
|
||||
CONTAINER_MAIN.RightToolStripPanelVisible = False
|
||||
CONTAINER_MAIN.Size = New System.Drawing.Size(484, 84)
|
||||
CONTAINER_MAIN.TabIndex = 0
|
||||
CONTAINER_MAIN.TopToolStripPanelVisible = False
|
||||
'
|
||||
'TP_MAIN
|
||||
'
|
||||
TP_MAIN.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.[Single]
|
||||
TP_MAIN.ColumnCount = 1
|
||||
TP_MAIN.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
||||
TP_MAIN.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20.0!))
|
||||
TP_MAIN.Controls.Add(Me.TXT_PATH_CURR, 0, 0)
|
||||
TP_MAIN.Controls.Add(Me.TXT_PATH_NEW, 0, 1)
|
||||
TP_MAIN.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
TP_MAIN.Location = New System.Drawing.Point(0, 0)
|
||||
TP_MAIN.Name = "TP_MAIN"
|
||||
TP_MAIN.RowCount = 3
|
||||
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
|
||||
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
|
||||
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
||||
TP_MAIN.Size = New System.Drawing.Size(484, 84)
|
||||
TP_MAIN.TabIndex = 0
|
||||
'
|
||||
'TXT_PATH_CURR
|
||||
'
|
||||
Me.TXT_PATH_CURR.CaptionText = "Current"
|
||||
Me.TXT_PATH_CURR.CaptionWidth = 50.0R
|
||||
Me.TXT_PATH_CURR.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
Me.TXT_PATH_CURR.Location = New System.Drawing.Point(4, 4)
|
||||
Me.TXT_PATH_CURR.Name = "TXT_PATH_CURR"
|
||||
Me.TXT_PATH_CURR.Size = New System.Drawing.Size(476, 22)
|
||||
Me.TXT_PATH_CURR.TabIndex = 0
|
||||
Me.TXT_PATH_CURR.TextBoxReadOnly = True
|
||||
'
|
||||
'TXT_PATH_NEW
|
||||
'
|
||||
ActionButton1.BackgroundImage = CType(resources.GetObject("ActionButton1.BackgroundImage"), System.Drawing.Image)
|
||||
ActionButton1.Name = "Refresh"
|
||||
ActionButton1.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Refresh
|
||||
ActionButton2.BackgroundImage = CType(resources.GetObject("ActionButton2.BackgroundImage"), System.Drawing.Image)
|
||||
ActionButton2.Name = "Open"
|
||||
ActionButton2.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open
|
||||
Me.TXT_PATH_NEW.Buttons.Add(ActionButton1)
|
||||
Me.TXT_PATH_NEW.Buttons.Add(ActionButton2)
|
||||
Me.TXT_PATH_NEW.CaptionText = "New"
|
||||
Me.TXT_PATH_NEW.CaptionWidth = 50.0R
|
||||
Me.TXT_PATH_NEW.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
Me.TXT_PATH_NEW.Location = New System.Drawing.Point(4, 33)
|
||||
Me.TXT_PATH_NEW.Name = "TXT_PATH_NEW"
|
||||
Me.TXT_PATH_NEW.Size = New System.Drawing.Size(476, 22)
|
||||
Me.TXT_PATH_NEW.TabIndex = 1
|
||||
'
|
||||
'SplitCollectionUserInfoPathForm
|
||||
'
|
||||
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
|
||||
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
|
||||
Me.ClientSize = New System.Drawing.Size(484, 84)
|
||||
Me.Controls.Add(CONTAINER_MAIN)
|
||||
Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle
|
||||
Me.Icon = Global.SCrawler.My.Resources.Resources.UsersIcon_32
|
||||
Me.KeyPreview = True
|
||||
Me.MaximizeBox = False
|
||||
Me.MaximumSize = New System.Drawing.Size(500, 123)
|
||||
Me.MinimizeBox = False
|
||||
Me.MinimumSize = New System.Drawing.Size(500, 123)
|
||||
Me.Name = "SplitCollectionUserInfoPathForm"
|
||||
Me.ShowInTaskbar = False
|
||||
Me.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide
|
||||
Me.Text = "User paths"
|
||||
CONTAINER_MAIN.ContentPanel.ResumeLayout(False)
|
||||
CONTAINER_MAIN.ResumeLayout(False)
|
||||
CONTAINER_MAIN.PerformLayout()
|
||||
TP_MAIN.ResumeLayout(False)
|
||||
CType(Me.TXT_PATH_CURR, System.ComponentModel.ISupportInitialize).EndInit()
|
||||
CType(Me.TXT_PATH_NEW, System.ComponentModel.ISupportInitialize).EndInit()
|
||||
Me.ResumeLayout(False)
|
||||
|
||||
End Sub
|
||||
|
||||
Private WithEvents TXT_PATH_CURR As PersonalUtilities.Forms.Controls.TextBoxExtended
|
||||
Private WithEvents TXT_PATH_NEW As PersonalUtilities.Forms.Controls.TextBoxExtended
|
||||
End Class
|
||||
End Namespace
|
||||
154
SCrawler/API/BaseObjects/SplitCollectionUserInfoPathForm.resx
Normal file
@@ -0,0 +1,154 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<metadata name="CONTAINER_MAIN.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>False</value>
|
||||
</metadata>
|
||||
<metadata name="TP_MAIN.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>False</value>
|
||||
</metadata>
|
||||
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<data name="ActionButton1.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/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="ActionButton2.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>
|
||||
</root>
|
||||
68
SCrawler/API/BaseObjects/SplitCollectionUserInfoPathForm.vb
Normal file
@@ -0,0 +1,68 @@
|
||||
' Copyright (C) Andy https://github.com/AAndyProgram
|
||||
' This program is free software: you can redistribute it and/or modify
|
||||
' it under the terms of the GNU General Public License as published by
|
||||
' the Free Software Foundation, either version 3 of the License, or
|
||||
' (at your option) any later version.
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports PersonalUtilities.Forms
|
||||
Imports PersonalUtilities.Forms.Controls.Base
|
||||
Imports SCrawler.DownloadObjects.STDownloader
|
||||
Namespace API.Base
|
||||
Friend Class SplitCollectionUserInfoPathForm
|
||||
Private WithEvents MyDefs As DefaultFormOptions
|
||||
Friend User As SplitCollectionUserInfo
|
||||
Private ReadOnly UserNewPathDef As String
|
||||
Friend Sub New(ByVal _User As SplitCollectionUserInfo)
|
||||
InitializeComponent()
|
||||
MyDefs = New DefaultFormOptions(Me, Settings.Design)
|
||||
User = _User
|
||||
UserNewPathDef = User.UserNew.File.CutPath.PathWithSeparator
|
||||
End Sub
|
||||
Private Sub SplitCollectionUserInfoPathForm_Load(sender As Object, e As EventArgs) Handles Me.Load
|
||||
With MyDefs
|
||||
.MyViewInitialize()
|
||||
.AddOkCancelToolbar()
|
||||
|
||||
TXT_PATH_CURR.Text = User.UserOrig.File.CutPath.PathWithSeparator
|
||||
TXT_PATH_NEW.Text = UserNewPathDef
|
||||
|
||||
.MyFieldsCheckerE = New FieldsChecker
|
||||
.MyFieldsCheckerE.AddControl(Of String)(TXT_PATH_NEW, "New path")
|
||||
.MyFieldsCheckerE.EndLoaderOperations()
|
||||
|
||||
.EndLoaderOperations()
|
||||
End With
|
||||
End Sub
|
||||
Private Sub MyDefs_ButtonOkClick(ByVal Sender As Object, ByVal e As KeyHandleEventArgs) Handles MyDefs.ButtonOkClick
|
||||
If MyDefs.MyFieldsChecker.AllParamsOK Then MyDefs.CloseForm()
|
||||
End Sub
|
||||
Private Sub TXT_PATH_NEW_ActionOnButtonClick(ByVal Sender As ActionButton, ByVal e As ActionButtonEventArgs) Handles TXT_PATH_NEW.ActionOnButtonClick
|
||||
Select Case e.DefaultButton
|
||||
Case ActionButton.DefaultButtons.Refresh : TXT_PATH_NEW.Text = UserNewPathDef
|
||||
Case ActionButton.DefaultButtons.Open
|
||||
Using ff As New Editors.GlobalLocationsChooserForm With {.MyInitialLocation = TXT_PATH_NEW.Text}
|
||||
ff.ShowDialog()
|
||||
If ff.DialogResult = DialogResult.OK Then
|
||||
Dim dest As DownloadLocation = ff.MyDestination
|
||||
If Not dest.Path.IsEmptyString Then
|
||||
Dim ph As PathMoverHandler = Editors.GlobalLocationsChooserForm.ModelHandler(dest.Model)
|
||||
If Not ph Is Nothing Then TXT_PATH_NEW.Text = ph.Invoke(User.UserNew, dest.Path.CSFileP).ToString
|
||||
End If
|
||||
End If
|
||||
End Using
|
||||
End Select
|
||||
End Sub
|
||||
Private Sub TXT_PATH_NEW_ActionOnTextChanged(sender As Object, e As EventArgs) Handles TXT_PATH_NEW.ActionOnTextChanged
|
||||
If Not MyDefs.Initializing Then
|
||||
Dim f As SFile = TXT_PATH_NEW.Text.CSFileP
|
||||
If Not f.IsEmptyString Then
|
||||
User.UserNew.SpecialPath = f
|
||||
User.UserNew.UpdateUserFile()
|
||||
User.Changed = Not User.UserNew.File.CutPath.PathWithSeparator = UserNewPathDef
|
||||
End If
|
||||
End If
|
||||
End Sub
|
||||
End Class
|
||||
End Namespace
|
||||
@@ -11,9 +11,8 @@ Imports PersonalUtilities.Functions.XML.Base
|
||||
Imports PersonalUtilities.Functions.RegularExpressions
|
||||
Namespace API.Facebook
|
||||
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_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_FileName As RParams = RParams.DM("([^/\?]+\..{3,4})(?=(\?|\Z))", 0, EDP.ReturnValue)
|
||||
|
||||
@@ -11,6 +11,7 @@ Imports SCrawler.Plugin
|
||||
Imports SCrawler.Plugin.Attributes
|
||||
Imports PersonalUtilities.Tools.Web.Clients
|
||||
Imports PersonalUtilities.Functions.RegularExpressions
|
||||
Imports DN = SCrawler.API.Base.DeclaredNames
|
||||
Namespace API.Facebook
|
||||
<Manifest("AndyProgram_Facebook"), SavedPosts, SeparatedTasks(1), SpecialForm(False)>
|
||||
Friend Class SiteSettings : Inherits ThreadsNet.SiteSettings
|
||||
@@ -18,7 +19,7 @@ Namespace API.Facebook
|
||||
#Region "Auth"
|
||||
<PropertyOption(AllowNull:=False, ControlText:="Accept", ControlToolTip:="Header 'Accept'", IsAuth:=True), ControlNumber(21), PXML, PClonable>
|
||||
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
|
||||
Get
|
||||
Return __HH_IG_APP_ID
|
||||
@@ -29,15 +30,13 @@ Namespace API.Facebook
|
||||
Return __HH_CSRF_TOKEN
|
||||
End Get
|
||||
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
|
||||
#Region "Defaults"
|
||||
<PropertyOption(ControlText:="Download photos", IsAuth:=False), PXML, PClonable>
|
||||
<PropertyOption(ControlText:="Download photos", IsAuth:=False, Category:=DN.CAT_UserDefs), PXML, PClonable>
|
||||
Friend ReadOnly Property ParsePhotoBlock As PropertyValue
|
||||
<PropertyOption(ControlText:="Download videos", IsAuth:=False), PXML, PClonable>
|
||||
<PropertyOption(ControlText:="Download videos", IsAuth:=False, Category:=DN.CAT_UserDefs), PXML, PClonable>
|
||||
Friend ReadOnly Property ParseVideoBlock As PropertyValue
|
||||
<PropertyOption(ControlText:="Download stories", IsAuth:=False), PXML, PClonable>
|
||||
<PropertyOption(ControlText:="Download stories", IsAuth:=False, Category:=DN.CAT_UserDefs), PXML, PClonable>
|
||||
Friend ReadOnly Property ParseStoriesBlock As PropertyValue
|
||||
#End Region
|
||||
#End Region
|
||||
@@ -48,10 +47,9 @@ Namespace API.Facebook
|
||||
With Responser.Headers
|
||||
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.Authority, "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
|
||||
Header_Accept = New PropertyValue(String.Empty, GetType(String))
|
||||
HH_PLATFORM_VER = New PropertyValue(String.Empty, GetType(String))
|
||||
ParsePhotoBlock = New PropertyValue(True)
|
||||
ParseVideoBlock = New PropertyValue(True)
|
||||
ParseStoriesBlock = New PropertyValue(True)
|
||||
@@ -77,7 +75,7 @@ Namespace API.Facebook
|
||||
#End Region
|
||||
#Region "BaseAuthExists, GetUserUrl, GetUserPostUrl, IsMyUser, IsMyImageVideo"
|
||||
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
|
||||
Friend Overrides Function GetUserUrl(ByVal User As IPluginContentProvider) As String
|
||||
Return DirectCast(User, UserData).GetProfileUrl
|
||||
|
||||
@@ -124,26 +124,35 @@ Namespace API.Facebook
|
||||
.SendToLogOnlyMessage = True, .ReplaceMainMessage = True})
|
||||
End Sub
|
||||
End Class
|
||||
Private Token_dtsg As String = String.Empty
|
||||
Private Token_lsd As String = String.Empty
|
||||
Private Token_Photosby As String = String.Empty
|
||||
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)
|
||||
Try
|
||||
GetUserTokens(Token)
|
||||
LoadSavePostsKV(True)
|
||||
Limit = If(DownloadTopCount, -1)
|
||||
If IsSavedPosts Then
|
||||
DownloadData_SavedPosts(String.Empty, Token)
|
||||
Else
|
||||
If DownloadImages And ParsePhotoBlock Then DownloadData_Photo(String.Empty, Token)
|
||||
If DownloadVideos And ParseVideoBlock Then DownloadData_Video(String.Empty, Token)
|
||||
If (DownloadImages Or DownloadVideos) And ParseStoriesBlock Then DownloadData_Stories(Token)
|
||||
End If
|
||||
LoadSavePostsKV(False)
|
||||
Finally
|
||||
MySettings.UpdateResponserData(Responser)
|
||||
End Try
|
||||
If CBool(MySettings.DownloadData_Impl.Value) Then
|
||||
Try
|
||||
If Responser.Headers.Value(IG.Header_IG_APP_ID).IsEmptyString Then Responser.Headers.Remove(IG.Header_IG_APP_ID)
|
||||
ResetBaseTokens()
|
||||
GetUserTokens(Token)
|
||||
LoadSavePostsKV(True)
|
||||
Limit = If(DownloadTopCount, -1)
|
||||
If IsSavedPosts Then
|
||||
DownloadData_SavedPosts(String.Empty, Token)
|
||||
Else
|
||||
If DownloadImages And ParsePhotoBlock Then DownloadData_Photo(String.Empty, Token)
|
||||
If DownloadVideos And ParseVideoBlock Then DownloadData_Video(String.Empty, Token)
|
||||
If (DownloadImages Or DownloadVideos) And ParseStoriesBlock Then DownloadData_Stories(Token)
|
||||
End If
|
||||
LoadSavePostsKV(False)
|
||||
Finally
|
||||
MySettings.UpdateResponserData(Responser)
|
||||
End Try
|
||||
End If
|
||||
End Sub
|
||||
Private Const Header_fb_fr_name_Photo As String = "ProfileCometAppCollectionPhotosRendererPaginationQuery"
|
||||
Private Const Header_fb_fr_name_Video As String = "PagesCometChannelTabAllVideosCardImplPaginationQuery"
|
||||
@@ -167,13 +176,13 @@ Namespace API.Facebook
|
||||
ValidateBaseTokens()
|
||||
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,
|
||||
SymbolsConverter.ASCII.EncodeSymbolsOnly(Token_dtsg),
|
||||
URL = String.Format(Graphql_UrlPattern, Token_lsd, DocID_Photo, Header_fb_fr_name_Photo, Token_dtsg_Var,
|
||||
SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & String.Format(VarPattern, Cursor, Token_Photosby) & "}"))
|
||||
|
||||
ResponserApplyDefs(Header_fb_fr_name_Photo)
|
||||
ThrowAny(Token)
|
||||
|
||||
WaitTimer()
|
||||
Dim r$ = Responser.GetResponse(URL)
|
||||
If Not r.IsEmptyString Then
|
||||
Using j As EContainer = JsonDocument.Parse(r)
|
||||
@@ -233,13 +242,13 @@ Namespace API.Facebook
|
||||
If VideoPageID.IsEmptyString Then Throw New TokensException("Unable to obtain 'VideoPageID'", False)
|
||||
ValidateBaseTokens()
|
||||
|
||||
URL = String.Format(Graphql_UrlPattern, Token_lsd, DocID_Video, Header_fb_fr_name_Video,
|
||||
SymbolsConverter.ASCII.EncodeSymbolsOnly(Token_dtsg),
|
||||
URL = String.Format(Graphql_UrlPattern, Token_lsd, DocID_Video, Header_fb_fr_name_Video, Token_dtsg_Var,
|
||||
SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & String.Format(VarPattern, If(Cursor.IsEmptyString, "null", $"""{Cursor}"""), VideoPageID) & "}"))
|
||||
|
||||
ResponserApplyDefs(Header_fb_fr_name_Video)
|
||||
ThrowAny(Token)
|
||||
|
||||
WaitTimer()
|
||||
Dim r$ = Responser.GetResponse(URL)
|
||||
If Not r.IsEmptyString Then
|
||||
Using j As EContainer = JsonDocument.Parse(r)
|
||||
@@ -288,13 +297,13 @@ Namespace API.Facebook
|
||||
ValidateBaseTokens()
|
||||
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,
|
||||
SymbolsConverter.ASCII.EncodeSymbolsOnly(Token_dtsg),
|
||||
URL = String.Format(Graphql_UrlPattern, Token_lsd, DocID_Stories, Header_fb_fr_name_Stories, Token_dtsg_Var,
|
||||
SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & String.Format(VarPattern, StoryBucket) & "}"))
|
||||
|
||||
ResponserApplyDefs(Header_fb_fr_name_Stories)
|
||||
ThrowAny(Token)
|
||||
|
||||
WaitTimer()
|
||||
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
|
||||
@@ -357,13 +366,13 @@ Namespace API.Facebook
|
||||
Dim pid As PostKV
|
||||
|
||||
ValidateBaseTokens()
|
||||
URL = String.Format(Graphql_UrlPattern, Token_lsd, DocID_SavedPosts, Header_fb_fr_name_SavedPosts,
|
||||
SymbolsConverter.ASCII.EncodeSymbolsOnly(Token_dtsg),
|
||||
URL = String.Format(Graphql_UrlPattern, Token_lsd, DocID_SavedPosts, Header_fb_fr_name_SavedPosts, Token_dtsg_Var,
|
||||
SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & String.Format(VarPattern, If(Cursor.IsEmptyString, "null", $"""{Cursor}""")) & "}"))
|
||||
|
||||
ResponserApplyDefs(Header_fb_fr_name_SavedPosts)
|
||||
ThrowAny(Token)
|
||||
|
||||
WaitTimer()
|
||||
Dim r$ = Responser.GetResponse(URL)
|
||||
If Not r.IsEmptyString Then
|
||||
Using j As EContainer = JsonDocument.Parse(r)
|
||||
@@ -421,6 +430,7 @@ Namespace API.Facebook
|
||||
If Round > 0 Then ThrowAny(Token)
|
||||
Dim script$, newUrl$
|
||||
Dim jNode As EContainer, jNode2 As EContainer
|
||||
WaitTimer()
|
||||
Dim r$ = resp.GetResponse(PostUrl)
|
||||
|
||||
If Not r.IsEmptyString Then
|
||||
@@ -488,16 +498,20 @@ Namespace API.Facebook
|
||||
#End Region
|
||||
#Region "ValidateBaseTokens, GetVideoPageID, GetUserTokens"
|
||||
''' <exception cref="ArgumentNullException"></exception>
|
||||
Private Sub ValidateBaseTokens()
|
||||
Protected Overrides Function ValidateBaseTokens() As Boolean
|
||||
Dim tokens$ = String.Empty
|
||||
If Token_dtsg.IsEmptyString Then tokens.StringAppend("Token_dtsg")
|
||||
If Token_lsd.IsEmptyString Then tokens.StringAppend("Token_lsd")
|
||||
If Not tokens.IsEmptyString Then Throw New TokensException($"Unable to obtain token(s) ({tokens}){vbCr}Your credentials may have expired.", True)
|
||||
End Sub
|
||||
If Not ValidateBaseTokens(tokens) Then
|
||||
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)
|
||||
Dim URL$ = $"{GetProfileUrl()}\videos"
|
||||
Dim resp As Responser = HtmlResponserCreate()
|
||||
Try
|
||||
WaitTimer()
|
||||
Dim r$ = resp.GetResponse(URL)
|
||||
If Not r.IsEmptyString Then VideoPageID = RegexReplace(r, Regex_VideoPageID)
|
||||
Catch ex As Exception
|
||||
@@ -510,14 +524,20 @@ Namespace API.Facebook
|
||||
Dim URL$ = If(IsSavedPosts, "https://www.facebook.com/saved", GetProfileUrl())
|
||||
Dim resp As Responser = HtmlResponserCreate()
|
||||
Try
|
||||
Token_dtsg = String.Empty
|
||||
Token_lsd = String.Empty
|
||||
ResetBaseTokens()
|
||||
Token_Photosby = String.Empty
|
||||
WaitTimer()
|
||||
Dim r$ = resp.GetResponse(URL)
|
||||
If Not r.IsEmptyString Then
|
||||
If Responser.CookiesExists Then Responser.Cookies.Update(resp.Cookies)
|
||||
Token_dtsg = RegexReplace(r, Regex_UserToken_dtsg)
|
||||
Token_lsd = RegexReplace(r, Regex_UserToken_lsd)
|
||||
ParseTokens(r, 0)
|
||||
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)
|
||||
If StoryBucket.IsEmptyString Then StoryBucket = RegexReplace(r, Regex_StoryBucket)
|
||||
If ID.IsEmptyString Then
|
||||
@@ -535,8 +555,7 @@ Namespace API.Facebook
|
||||
#Region "Responser options"
|
||||
Private Sub ResponserApplyDefs(ByVal __fb_friendly_name As String)
|
||||
With Responser
|
||||
.Headers.Add(ThreadsNet.UserData.Header_FB_LSD, Token_lsd)
|
||||
.Headers.Add(DeclaredNames.Header_FB_FRIENDLY_NAME, __fb_friendly_name)
|
||||
UpdateHeadersGQL(__fb_friendly_name)
|
||||
.Method = "POST"
|
||||
.Accept = "*/*"
|
||||
.Referer = GetProfileUrl()
|
||||
@@ -556,14 +575,14 @@ Namespace API.Facebook
|
||||
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecFetchSite, "none"))
|
||||
.Add("Sec-Fetch-User", "?1")
|
||||
.Add("Upgrade-Insecure-Requests", 1)
|
||||
Dim h$ = Responser.Headers.Value(IG.Header_Browser)
|
||||
If Not h.IsEmptyString Then .Add(IG.Header_Browser, h)
|
||||
h = Responser.Headers.Value(IG.Header_BrowserExt)
|
||||
If Not h.IsEmptyString Then .Add(IG.Header_BrowserExt, h)
|
||||
h = .Value(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecChUaPlatform))
|
||||
If Not h.IsEmptyString Then .Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecChUaPlatform, h))
|
||||
If ACheck(MySettings.HH_PLATFORM_VER.Value) Then _
|
||||
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecChUaPlatformVersion, MySettings.HH_PLATFORM_VER.Value))
|
||||
Dim cloneHeader As Action(Of String) = Sub(ByVal hName As String)
|
||||
Dim hValue$ = Responser.Headers.Value(hName)
|
||||
If Not hValue.IsEmptyString Then .Add(hName, hValue)
|
||||
End Sub
|
||||
cloneHeader.Invoke(IG.Header_Browser)
|
||||
cloneHeader.Invoke(IG.Header_BrowserExt)
|
||||
cloneHeader.Invoke(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecChUaPlatform).Name)
|
||||
cloneHeader.Invoke(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecChUaPlatformVersion).Name)
|
||||
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecChUaMobile, "?0"))
|
||||
.Add("Sec-Ch-Ua-Model", "")
|
||||
End With
|
||||
@@ -655,6 +674,7 @@ Namespace API.Facebook
|
||||
Else
|
||||
URL = String.Format(VideoHtmlUrlPattern, m.Post.ID)
|
||||
End If
|
||||
WaitTimer()
|
||||
r = resp.GetResponse(URL)
|
||||
If Not r.IsEmptyString Then
|
||||
re.Pattern = String.Format(pattern, nameHD)
|
||||
|
||||
@@ -18,7 +18,9 @@ Namespace API.Instagram
|
||||
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 Sub UpdateResponser(ByVal Source As IResponse, ByRef Destination As Responser)
|
||||
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_tokenName$ = SiteSettings.Header_CSRF_TOKEN_COOKIE
|
||||
If Not Source Is Nothing Then
|
||||
@@ -35,17 +37,17 @@ Namespace API.Instagram
|
||||
Dim token$ = String.Empty
|
||||
With Source
|
||||
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)
|
||||
Else
|
||||
If .HeadersExists Then
|
||||
wwwClaim = .Headers.Value(wwwClaimName)
|
||||
If UpdateWwwClaim Then wwwClaim = .Headers.Value(wwwClaimName)
|
||||
token = .Headers.Value(tokenName)
|
||||
End If
|
||||
End If
|
||||
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 isInternal Then
|
||||
Destination.Cookies.Update(Source.Cookies, CookieKeeper.UpdateModes.ReplaceByNameAll, False, EDP.SendToLog)
|
||||
|
||||
@@ -14,12 +14,13 @@ Imports PersonalUtilities.Functions.RegularExpressions
|
||||
Imports PersonalUtilities.Tools.Web.Clients
|
||||
Imports PersonalUtilities.Tools.Web.Cookies
|
||||
Imports Download = SCrawler.Plugin.ISiteSettings.Download
|
||||
Imports DN = SCrawler.API.Base.DeclaredNames
|
||||
Namespace API.Instagram
|
||||
<Manifest(InstagramSiteKey), SeparatedTasks(1), SavedPosts, SpecialForm(False)>
|
||||
Friend Class SiteSettings : Inherits SiteSettingsBase
|
||||
#Region "Declarations"
|
||||
#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 _LowestValue As Integer
|
||||
Friend Sub New(ByVal LowestValue As Integer)
|
||||
@@ -32,7 +33,7 @@ Namespace API.Instagram
|
||||
If Not ACheck(Of Integer)(Value) Then
|
||||
TypeError = True
|
||||
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
|
||||
Else
|
||||
Return Value
|
||||
@@ -47,13 +48,16 @@ Namespace API.Instagram
|
||||
If v > 0 Or v = -1 Then
|
||||
Return Value
|
||||
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
|
||||
Return Nothing
|
||||
End If
|
||||
End Function
|
||||
End Class
|
||||
#End Region
|
||||
#Region "Categories"
|
||||
Private Const CAT_DOWN As String = "Download data"
|
||||
#End Region
|
||||
#Region "Authorization properties"
|
||||
Friend Const Header_IG_APP_ID As String = "x-ig-app-id"
|
||||
Friend Const Header_IG_WWW_CLAIM As String = "x-ig-www-claim"
|
||||
@@ -62,24 +66,38 @@ Namespace API.Instagram
|
||||
Friend Const Header_ASBD_ID As String = "X-Asbd-Id"
|
||||
Friend Const Header_Browser As String = "Sec-Ch-Ua"
|
||||
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:="x-csrftoken", ControlToolTip:="Can be automatically extracted from cookies", IsAuth:=True, AllowNull:=True), ControlNumber(2), PClonable(Clone:=False)>
|
||||
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)>
|
||||
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)>
|
||||
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)>
|
||||
Friend Property HH_IG_WWW_CLAIM As PropertyValue
|
||||
<PropertyOption(ControlText:="sec-ch-ua", IsAuth:=True, AllowNull:=True), ControlNumber(6), PClonable>
|
||||
Private Property HH_BROWSER As PropertyValue
|
||||
<PropertyOption(ControlText:="sec-ch-ua-full", ControlToolTip:="sec-ch-ua-full-version-list", IsAuth:=True, AllowNull:=True), ControlNumber(7), PClonable>
|
||||
Private Property HH_BROWSER_EXT As PropertyValue
|
||||
<PropertyOption(ControlText:="sec-ch-ua-platform-ver", ControlToolTip:="sec-ch-ua-platform-version", IsAuth:=True, AllowNull:=True), ControlNumber(8), PClonable>
|
||||
Private Property HH_PLATFORM As PropertyValue
|
||||
<PropertyOption(ControlText:="UserAgent", IsAuth:=True, AllowNull:=True), ControlNumber(9), PClonable>
|
||||
Private Property HH_USER_AGENT As PropertyValue
|
||||
Friend ReadOnly Property HH_IG_WWW_CLAIM As PropertyValue
|
||||
Private ReadOnly Property HH_IG_WWW_CLAIM_IS_ZERO As Boolean
|
||||
Get
|
||||
Dim v$ = AConvert(Of String)(HH_IG_WWW_CLAIM.Value, String.Empty)
|
||||
Return Not v.IsEmptyString AndAlso v = "0"
|
||||
End Get
|
||||
End Property
|
||||
<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
|
||||
Return Responser.CookiesExists And ACheck(HH_IG_APP_ID.Value) And ACheck(HH_CSRF_TOKEN.Value)
|
||||
End Function
|
||||
@@ -95,7 +113,7 @@ Namespace API.Instagram
|
||||
Case NameOf(HH_CSRF_TOKEN) : f = Header_CSRF_TOKEN
|
||||
Case NameOf(HH_BROWSER) : f = Header_Browser
|
||||
Case NameOf(HH_BROWSER_EXT) : f = Header_BrowserExt
|
||||
Case NameOf(HH_PLATFORM) : f = Header_Platform
|
||||
Case NameOf(HH_PLATFORM) : f = Header_Platform_Verion
|
||||
Case NameOf(HH_USER_AGENT) : isUserAgent = True
|
||||
End Select
|
||||
If Not f.IsEmptyString Then
|
||||
@@ -106,29 +124,76 @@ Namespace API.Instagram
|
||||
End If
|
||||
End If
|
||||
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
|
||||
#Region "Download properties"
|
||||
<PropertyOption(ControlText:="Request timer", AllowNull:=False), PXML("RequestsWaitTimer"), ControlNumber(20), PClonable>
|
||||
<PropertyOption(ControlText:="DownDetector",
|
||||
ControlToolTip:="Use 'DownDetector' to determine if the site is accessible. -1 to disable." & vbCr &
|
||||
"The value represents the average number of error reports over the last 4 hours"),
|
||||
PClonable, PXML, ControlNumber(17)>
|
||||
Private ReadOnly Property DownDetectorValue As PropertyValue
|
||||
<Provider(NameOf(DownDetectorValue), FieldsChecker:=True)>
|
||||
Private ReadOnly Property DownDetectorValueProvider As IFormatProvider
|
||||
<PropertyOption(ControlText:="Add 'DownDetector' information to the log."), PClonable, PXML, ControlNumber(18), HiddenControl>
|
||||
Private ReadOnly Property DownDetectorValueAddToLog As PropertyValue
|
||||
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, Category:=DN.CAT_Timers),
|
||||
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, Category:=DN.CAT_Timers), PXML, ControlNumber(20), PClonable>
|
||||
Friend ReadOnly Property RequestsWaitTimer As PropertyValue
|
||||
<Provider(NameOf(RequestsWaitTimer), FieldsChecker:=True)>
|
||||
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, Category:=DN.CAT_Timers), PXML, ControlNumber(21), PClonable>
|
||||
Friend ReadOnly Property RequestsWaitTimerTaskCount As PropertyValue
|
||||
<Provider(NameOf(RequestsWaitTimerTaskCount), FieldsChecker:=True)>
|
||||
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, Category:=DN.CAT_Timers), PXML, ControlNumber(22), PClonable>
|
||||
Friend ReadOnly Property SleepTimerOnPostsLimit As PropertyValue
|
||||
<Provider(NameOf(SleepTimerOnPostsLimit), FieldsChecker:=True)>
|
||||
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", Category:=DN.CAT_UserDefs), PXML, ControlNumber(23), PClonable>
|
||||
Friend ReadOnly Property GetTimeline As PropertyValue
|
||||
<PropertyOption(ControlText:="Get reels", ControlToolTip:="Default value for new users"), PXML, ControlNumber(24), PClonable>
|
||||
<PropertyOption(ControlText:="Get reels", ControlToolTip:="Default value for new users", Category:=DN.CAT_UserDefs), PXML, ControlNumber(24), PClonable>
|
||||
Friend ReadOnly Property GetReels As PropertyValue
|
||||
<PropertyOption(ControlText:="Get stories", ControlToolTip:="Default value for new users"), PXML, ControlNumber(25), PClonable>
|
||||
<PropertyOption(ControlText:="Get stories", ControlToolTip:="Default value for new users", Category:=DN.CAT_UserDefs), PXML, ControlNumber(25), PClonable>
|
||||
Friend ReadOnly Property GetStories As PropertyValue
|
||||
<PropertyOption(ControlText:="Get stories: user", ControlToolTip:="Default value for new users"), PXML, ControlNumber(26), PClonable>
|
||||
<PropertyOption(ControlText:="Get stories: user", ControlToolTip:="Default value for new users", Category:=DN.CAT_UserDefs), PXML, ControlNumber(26), PClonable>
|
||||
Friend ReadOnly Property GetStoriesUser As PropertyValue
|
||||
<PropertyOption(ControlText:="Get tagged photos", ControlToolTip:="Default value for new users"), PXML, ControlNumber(27), PClonable>
|
||||
<PropertyOption(ControlText:="Get tagged photos", ControlToolTip:="Default value for new users", Category:=DN.CAT_UserDefs), PXML, ControlNumber(27), PClonable>
|
||||
Friend ReadOnly Property GetTagged As PropertyValue
|
||||
<PropertyOption(ControlText:="Tagged notify limit",
|
||||
ControlToolTip:="If the number of tagged posts exceeds this number you will be notified." & vbCr &
|
||||
@@ -138,20 +203,40 @@ Namespace API.Instagram
|
||||
Private ReadOnly Property TaggedNotifyLimitProvider As IFormatProvider
|
||||
#End Region
|
||||
#Region "Download ready"
|
||||
<PropertyOption(ControlText:="Download timeline", ControlToolTip:="Download timeline"), PXML, ControlNumber(10), PClonable>
|
||||
<PropertyOption(ControlText:="Download timeline", ControlToolTip:="Download timeline", Category:=CAT_DOWN), PXML, ControlNumber(10), PClonable>
|
||||
Friend ReadOnly Property DownloadTimeline As PropertyValue
|
||||
<PropertyOption(ControlText:="Download reels", ControlToolTip:="Download reels"), PXML, ControlNumber(11), PClonable>
|
||||
<PXML> Private ReadOnly Property DownloadTimeline_Def As PropertyValue
|
||||
<PropertyOption(ControlText:="Download reels", ControlToolTip:="Download reels", Category:=CAT_DOWN), PXML, ControlNumber(11), PClonable>
|
||||
Friend ReadOnly Property DownloadReels As PropertyValue
|
||||
<PropertyOption(ControlText:="Download stories", ControlToolTip:="Download stories"), PXML, ControlNumber(12), PClonable>
|
||||
<PXML> Private ReadOnly Property DownloadReels_Def As PropertyValue
|
||||
<PropertyOption(ControlText:="Download stories", ControlToolTip:="Download stories", Category:=CAT_DOWN), PXML, ControlNumber(12), PClonable>
|
||||
Friend ReadOnly Property DownloadStories As PropertyValue
|
||||
<PropertyOption(ControlText:="Download stories: user", ControlToolTip:="Download stories (user)"), PXML, ControlNumber(13), PClonable>
|
||||
<PXML> Private ReadOnly Property DownloadStories_Def As PropertyValue
|
||||
<PropertyOption(ControlText:="Download stories: user", ControlToolTip:="Download stories (user)", Category:=CAT_DOWN), PXML, ControlNumber(13), PClonable>
|
||||
Friend ReadOnly Property DownloadStoriesUser As PropertyValue
|
||||
<PropertyOption(ControlText:="Download tagged", ControlToolTip:="Download tagged posts"), PXML, ControlNumber(14), PClonable>
|
||||
<PXML> Private ReadOnly Property DownloadStoriesUser_Def As PropertyValue
|
||||
<PropertyOption(ControlText:="Download tagged", ControlToolTip:="Download tagged posts", Category:=CAT_DOWN), PXML, ControlNumber(14), PClonable>
|
||||
Friend ReadOnly Property DownloadTagged As PropertyValue
|
||||
<PXML> Private ReadOnly Property DownloadTagged_Def As PropertyValue
|
||||
#End Region
|
||||
#Region "429 bypass"
|
||||
<PXML("InstagramDownloadingErrorDate")>
|
||||
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 ReadOnly Property ReadyForDownload As Boolean
|
||||
Get
|
||||
@@ -165,11 +250,64 @@ Namespace API.Instagram
|
||||
End With
|
||||
End Get
|
||||
End Property
|
||||
Private Const LastDownloadDateResetInterval As Integer = 60
|
||||
<PXML> Private ReadOnly Property LastDownloadDate 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)>
|
||||
Private Property LastRequestsCountLabel As PropertyValue
|
||||
Private ReadOnly LastRequestsCountLabelStr As Func(Of Integer, String) = Function(r) $"Number of spent requests: {r.NumToGroupIntegral}"
|
||||
Private ReadOnly Property LastRequestsCountLabel As PropertyValue
|
||||
Private TooManyRequestsReadyForCatch As Boolean = True
|
||||
Friend Function GetWaitDate() As Date
|
||||
With DownloadingErrorDate
|
||||
@@ -224,13 +362,19 @@ Namespace API.Instagram
|
||||
asbd = .Value(Header_ASBD_ID)
|
||||
browser = .Value(Header_Browser)
|
||||
browserExt = .Value(Header_BrowserExt)
|
||||
platform = .Value(Header_Platform)
|
||||
platform = .Value(Header_Platform_Verion)
|
||||
End If
|
||||
'.Add(Header_IG_WWW_CLAIM, 0)
|
||||
.Add("Origin", "https://www.instagram.com")
|
||||
.Add("authority", "www.instagram.com")
|
||||
.Add("Dnt", 1)
|
||||
'.Add("Dpr", 1)
|
||||
.Remove("Dpr")
|
||||
.Add("Sec-Ch-Ua-Mobile", "?0")
|
||||
.Add("Sec-Ch-Ua-Model", """""")
|
||||
.Add("Sec-Ch-Ua-Platform", """Windows""")
|
||||
.Add("Sec-Fetch-Dest", "empty")
|
||||
.Add("Sec-Fetch-Mode", "cors")
|
||||
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecFetchDest, "empty"))
|
||||
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecFetchMode, "cors"))
|
||||
.Add("Sec-Fetch-Site", "same-origin")
|
||||
.Add("X-Requested-With", "XMLHttpRequest")
|
||||
End With
|
||||
@@ -248,12 +392,31 @@ Namespace API.Instagram
|
||||
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))
|
||||
|
||||
DownloadTimeline = New PropertyValue(True)
|
||||
DownloadReels = New PropertyValue(False)
|
||||
DownloadStories = New PropertyValue(True)
|
||||
DownloadStoriesUser = New PropertyValue(True)
|
||||
DownloadTagged = New PropertyValue(False)
|
||||
HH_IG_WWW_CLAIM_UPDATE_INTERVAL = New PropertyValue(120)
|
||||
HH_IG_WWW_CLAIM_ALWAYS_ZERO = New PropertyValue(False)
|
||||
HH_IG_WWW_CLAIM_RESET_EACH_SESSION = New PropertyValue(True)
|
||||
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))
|
||||
|
||||
DownDetectorValue = New PropertyValue(20)
|
||||
DownDetectorValueProvider = New TimersChecker(-1)
|
||||
DownDetectorValueAddToLog = New PropertyValue(False)
|
||||
RequestsWaitTimer_Any = New PropertyValue(1000)
|
||||
RequestsWaitTimer_AnyProvider = New TimersChecker(0)
|
||||
RequestsWaitTimer = New PropertyValue(1000)
|
||||
RequestsWaitTimerProvider = New TimersChecker(100)
|
||||
RequestsWaitTimerTaskCount = New PropertyValue(1)
|
||||
@@ -269,17 +432,32 @@ Namespace API.Instagram
|
||||
TaggedNotifyLimit = New PropertyValue(200)
|
||||
TaggedNotifyLimitProvider = New TaggedNotifyLimitChecker
|
||||
|
||||
DownloadingErrorDate = New PropertyValue(Nothing, GetType(Date))
|
||||
DownloadingErrorDate = New PropertyValue(Now.AddYears(-10), GetType(Date))
|
||||
LastDownloadDate = New PropertyValue(Now.AddDays(-1))
|
||||
LastRequestsCount = New PropertyValue(0)
|
||||
LastRequestsCountLabel = New PropertyValue(LastRequestsCountLabelStr.Invoke(LastRequestsCount.Value))
|
||||
LastRequestsCount.OnChangeFunction = Sub(vv) LastRequestsCountLabel.Value = LastRequestsCountLabelStr.Invoke(vv)
|
||||
LastRequestsCountLabel = New PropertyValue(String.Empty, GetType(String))
|
||||
MyLastRequests = New Dictionary(Of Date, Integer)
|
||||
|
||||
_AllowUserAgentUpdate = False
|
||||
UrlPatternUser = "https://www.instagram.com/{0}/"
|
||||
UserRegex = RParams.DMS(String.Format(UserRegexDefaultPattern, "instagram.com/"), 1)
|
||||
ImageVideoContains = "instagram.com"
|
||||
End Sub
|
||||
Private Const SettingsVersionCurrent As Integer = 2
|
||||
Friend Overrides Sub EndInit()
|
||||
Try : MyLastRequests.Add(LastDownloadDate.Value, LastRequestsCount.Value) : Catch : End Try
|
||||
If Not CBool(HH_IG_WWW_CLAIM_USE.Value) Then Responser.Headers.Remove(Header_IG_WWW_CLAIM)
|
||||
If CInt(SettingsVersion.Value) < SettingsVersionCurrent Then
|
||||
SettingsVersion.Value = SettingsVersionCurrent
|
||||
HH_IG_WWW_CLAIM_UPDATE_INTERVAL.Value = 120
|
||||
HH_IG_WWW_CLAIM_ALWAYS_ZERO.Value = False
|
||||
HH_IG_WWW_CLAIM_RESET_EACH_SESSION.Value = True
|
||||
HH_IG_WWW_CLAIM_RESET_EACH_TARGET.Value = True
|
||||
HH_IG_WWW_CLAIM_USE.Value = True
|
||||
HH_IG_WWW_CLAIM_USE_DEFAULT_ALGO.Value = True
|
||||
End If
|
||||
MyBase.EndInit()
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "PropertiesDataChecker"
|
||||
<PropertiesDataChecker({NameOf(TaggedNotifyLimit)})>
|
||||
@@ -307,16 +485,112 @@ Namespace API.Instagram
|
||||
End Function
|
||||
#End Region
|
||||
#Region "Downloading"
|
||||
Private ____DownloadStarted As Boolean = False
|
||||
Private ____AvailableRequested As Boolean = False
|
||||
Private ____AvailableSilent As Boolean = True
|
||||
Private ____AvailableChecked As Boolean = False
|
||||
Private ____AvailableResult As Boolean = False
|
||||
Private Sub ResetDownloadOptions()
|
||||
If ActiveJobs < 1 Then
|
||||
____DownloadStarted = False
|
||||
____AvailableRequested = False
|
||||
____AvailableChecked = False
|
||||
____AvailableSilent = True
|
||||
____AvailableResult = False
|
||||
If ActiveSessionRequestsExists Then RefreshMyLastRequests(Now)
|
||||
ActiveSessionRequestsExists = False
|
||||
_NextWNM = UserData.WNM.Notify
|
||||
_NextTagged = True
|
||||
SkipUntilNextSession = False
|
||||
AvailableText = String.Empty
|
||||
ActiveJobs = 0
|
||||
End If
|
||||
End Sub
|
||||
Friend Overrides Function Available(ByVal What As Download, ByVal Silent As Boolean) As Boolean
|
||||
If MyBase.Available(What, Silent) And ActiveJobs < 2 Then
|
||||
If CInt(DownDetectorValue.Value) >= 0 Then
|
||||
If ____DownloadStarted Then
|
||||
____AvailableRequested = True
|
||||
____AvailableSilent = Silent
|
||||
Return True
|
||||
Else
|
||||
Return AvailableImpl(What, Silent)
|
||||
End If
|
||||
Else
|
||||
Return True
|
||||
End If
|
||||
Else
|
||||
Return False
|
||||
End If
|
||||
End Function
|
||||
#Disable Warning IDE0060
|
||||
Private Function AvailableImpl(ByVal What As Download, ByVal Silent As Boolean) As Boolean
|
||||
#Enable Warning
|
||||
Try
|
||||
AvailableText = String.Empty
|
||||
If CInt(DownDetectorValue.Value) = -1 Then
|
||||
Return True
|
||||
Else
|
||||
Dim dl As List(Of DownDetector.Data) = DownDetector.GetData("instagram")
|
||||
If dl.ListExists Then
|
||||
dl = dl.Take(4).ToList
|
||||
Dim avg% = dl.Average(Function(d) d.Value)
|
||||
If avg > CInt(DownDetectorValue.Value) Then
|
||||
AvailableText = "Over the past hour, Instagram has received an average of " &
|
||||
avg.NumToString(New ANumbers With {.FormatOptions = ANumbers.Options.GroupIntegral}) & " outage reports:" & vbCr &
|
||||
dl.ListToString(vbCr)
|
||||
If CBool(DownDetectorValueAddToLog.Value) Then MyMainLOG = AvailableText
|
||||
If Silent Then
|
||||
Return False
|
||||
Else
|
||||
Return MsgBoxE({$"{AvailableText}{vbCr}{vbCr}Do you want to continue parsing Instagram data?",
|
||||
"There are outage reports on Instagram"}, vbYesNo) = vbYes
|
||||
End If
|
||||
End If
|
||||
End If
|
||||
Return True
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Return ErrorsDescriber.Execute(EDP.SendToLog + EDP.ReturnValue, ex, "[API.Instagram.SiteSettings.Available]", True)
|
||||
End Try
|
||||
End Function
|
||||
Friend Property SkipUntilNextSession As Boolean = False
|
||||
Friend Overrides Function ReadyToDownload(ByVal What As Download) As Boolean
|
||||
Return ActiveJobs < 2 AndAlso Not SkipUntilNextSession AndAlso ReadyForDownload AndAlso BaseAuthExists() AndAlso DownloadTimeline.Value
|
||||
If ActiveJobs < 2 AndAlso Not SkipUntilNextSession AndAlso ReadyForDownload AndAlso BaseAuthExists() AndAlso CBool(DownloadTimeline.Value) Then
|
||||
If ____DownloadStarted And ____AvailableRequested Then
|
||||
____AvailableResult = AvailableImpl(What, ____AvailableSilent)
|
||||
____AvailableChecked = True
|
||||
____AvailableRequested = False
|
||||
Return ____AvailableResult
|
||||
ElseIf ____AvailableChecked Then
|
||||
Return ____AvailableResult
|
||||
Else
|
||||
Return True
|
||||
End If
|
||||
Else
|
||||
Return False
|
||||
End If
|
||||
End Function
|
||||
Private ActiveJobs As Integer = 0
|
||||
Private ActiveSessionDate As Date
|
||||
Private ActiveSessionRequestsExists As Boolean = False
|
||||
Private _NextWNM As UserData.WNM = UserData.WNM.Notify
|
||||
Private _NextTagged As Boolean = True
|
||||
Friend Overrides Sub DownloadStarted(ByVal What As Download)
|
||||
ResetDownloadOptions()
|
||||
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 ____DownloadStarted = True : 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
|
||||
Friend Overrides Sub BeforeStartDownload(ByVal User As Object, ByVal What As Download)
|
||||
With DirectCast(User, UserData)
|
||||
@@ -324,10 +598,9 @@ Namespace API.Instagram
|
||||
.WaitNotificationMode = _NextWNM
|
||||
.TaggedCheckSession = _NextTagged
|
||||
End If
|
||||
If CDate(LastDownloadDate.Value).AddMinutes(60) > Now Then
|
||||
.RequestsCount = LastRequestsCount.Value
|
||||
If MyLastRequestsDate.AddMinutes(LastDownloadDateResetInterval) > Now Then
|
||||
.RequestsCount = MyLastRequestsCount
|
||||
Else
|
||||
LastRequestsCount.Value = 0
|
||||
.RequestsCount = 0
|
||||
End If
|
||||
End With
|
||||
@@ -337,8 +610,8 @@ Namespace API.Instagram
|
||||
_NextWNM = .WaitNotificationMode
|
||||
If _NextWNM = UserData.WNM.SkipTemp Or _NextWNM = UserData.WNM.SkipCurrent Then _NextWNM = UserData.WNM.Notify
|
||||
_NextTagged = .TaggedCheckSession
|
||||
LastDownloadDate.Value = Now
|
||||
LastRequestsCount.Value = .RequestsCount
|
||||
MyLastRequestsCount = .RequestsCountSession
|
||||
If .RequestsCountSession > 0 Then ActiveSessionRequestsExists = True
|
||||
_FieldsChangerSuspended = True
|
||||
HH_IG_WWW_CLAIM.Value = Responser.Headers.Value(Header_IG_WWW_CLAIM)
|
||||
HH_CSRF_TOKEN.Value = Responser.Headers.Value(Header_CSRF_TOKEN)
|
||||
@@ -346,11 +619,8 @@ Namespace API.Instagram
|
||||
End With
|
||||
End Sub
|
||||
Friend Overrides Sub DownloadDone(ByVal What As Download)
|
||||
_NextWNM = UserData.WNM.Notify
|
||||
_NextTagged = True
|
||||
LastDownloadDate.Value = Now
|
||||
ActiveJobs -= 1
|
||||
SkipUntilNextSession = False
|
||||
ResetDownloadOptions()
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Settings"
|
||||
@@ -362,7 +632,17 @@ Namespace API.Instagram
|
||||
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)
|
||||
@@ -371,6 +651,11 @@ Namespace API.Instagram
|
||||
____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()
|
||||
@@ -383,12 +668,33 @@ Namespace API.Instagram
|
||||
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
|
||||
Not Responser.Cookies.ListEquals(____Cookies) Then HH_IG_WWW_CLAIM.Value = 0 : credentialsUpdated = True
|
||||
If Responser.CookiesExists Then
|
||||
Dim csrf$ = If(Responser.Cookies.FirstOrDefault(Function(c) c.Name.StringToLower = Header_CSRF_TOKEN_COOKIE)?.Value, String.Empty)
|
||||
If Not csrf.IsEmptyString Then HH_CSRF_TOKEN.Value = csrf
|
||||
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
|
||||
@@ -415,6 +721,12 @@ Namespace API.Instagram
|
||||
Return ErrorsDescriber.Execute(EDP.SendToLog, ex, "Can't open user's post", String.Empty)
|
||||
End Try
|
||||
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 Class
|
||||
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
|
||||
@@ -69,7 +69,6 @@ Namespace API.Instagram
|
||||
Return New EContainer("Post", ID, {New EAttribute(Name_Section, CInt(Section)), New EAttribute(Name_Code, Code)})
|
||||
End Function
|
||||
End Structure
|
||||
Friend Const Header_FB_LSD As String = "x-fb-lsd"
|
||||
Private ReadOnly Property MySiteSettings As SiteSettings
|
||||
Get
|
||||
Return DirectCast(HOST.Source, SiteSettings)
|
||||
@@ -139,14 +138,19 @@ Namespace API.Instagram
|
||||
Friend Sub New()
|
||||
PostsKVIDs = New List(Of PostKV)
|
||||
PostsToReparse = New List(Of PostKV)
|
||||
_ResponserAutoUpdateCookies = True
|
||||
End Sub
|
||||
#End Region
|
||||
#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
|
||||
Friend Err5xx As Integer = -1
|
||||
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)
|
||||
If Not Source.E560Thrown Then
|
||||
MyMainLOG = $"{Source.ToStringForLog}: ({IIf(Source.Err5xx > 0, Source.Err5xx, 560)}) Download skipped until next session"
|
||||
@@ -154,6 +158,10 @@ Namespace API.Instagram
|
||||
End If
|
||||
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 Class
|
||||
Private ReadOnly Property MyFilePostsKV As SFile
|
||||
Get
|
||||
@@ -235,8 +243,75 @@ Namespace API.Instagram
|
||||
Private _DownloadingInProgress As Boolean = False
|
||||
Private _Limit As Integer = -1
|
||||
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)
|
||||
ResetBaseTokens()
|
||||
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 errorFound As Boolean = False
|
||||
Try
|
||||
@@ -251,6 +326,7 @@ Namespace API.Instagram
|
||||
Dim dt As Func(Of Boolean) = Function() (CBool(MySiteSettings.DownloadTimeline.Value) And GetTimeline) Or IsSavedPosts
|
||||
If dt.Invoke And Not LastCursor.IsEmptyString Then
|
||||
s = IIf(IsSavedPosts, Sections.SavedPosts, Sections.Timeline)
|
||||
upClaimRequest.Invoke
|
||||
DownloadData(LastCursor, s, Token)
|
||||
ProgressPre.Done()
|
||||
ThrowAny(Token)
|
||||
@@ -258,27 +334,51 @@ Namespace API.Instagram
|
||||
End If
|
||||
If dt.Invoke And Not HasError Then
|
||||
s = IIf(IsSavedPosts, Sections.SavedPosts, Sections.Timeline)
|
||||
upClaimRequest.Invoke
|
||||
ChangeResponserMode(_UseGQL)
|
||||
DownloadData(String.Empty, s, Token)
|
||||
ProgressPre.Done()
|
||||
ThrowAny(Token)
|
||||
If Not HasError Then FirstLoadingDone = True
|
||||
End If
|
||||
DefaultParser_ElemNode = Nothing
|
||||
If FirstLoadingDone Then LastCursor = String.Empty
|
||||
If Not IsSavedPosts AndAlso MySiteSettings.BaseAuthExists() Then
|
||||
DefaultParser_ElemNode = Nothing
|
||||
ChangeResponserMode(_UseGQL)
|
||||
If CBool(MySiteSettings.DownloadReels.Value) And GetReels Then
|
||||
s = Sections.Reels
|
||||
DefaultParser_ElemNode = {"node", "media"}
|
||||
upClaimRequest.Invoke
|
||||
ChangeResponserMode(True)
|
||||
DownloadData(String.Empty, s, Token)
|
||||
DefaultParser_ElemNode = Nothing
|
||||
DownloadReels_SetEnvir = False
|
||||
GetReelsGQL_SetEnvir = False
|
||||
ProgressPre.Done()
|
||||
End If
|
||||
If CBool(MySiteSettings.DownloadStories.Value) And GetStories Then s = Sections.Stories : DownloadData(String.Empty, s, Token) : ProgressPre.Done()
|
||||
If CBool(MySiteSettings.DownloadStoriesUser.Value) And GetStoriesUser Then s = Sections.UserStories : DownloadData(String.Empty, s, Token) : ProgressPre.Done()
|
||||
If CBool(MySiteSettings.DownloadTagged.Value) And GetTaggedData Then
|
||||
s = Sections.Tagged
|
||||
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
|
||||
@@ -289,7 +389,7 @@ Namespace API.Instagram
|
||||
Throw ex
|
||||
Finally
|
||||
DefaultParser_ElemNode = Nothing
|
||||
DownloadReels_SetEnvir = False
|
||||
GetReelsGQL_SetEnvir = False
|
||||
E560Thrown = False
|
||||
UpdateResponser()
|
||||
ValidateExtension()
|
||||
@@ -315,13 +415,13 @@ Namespace API.Instagram
|
||||
If _DownloadingInProgress AndAlso Not Responser Is Nothing AndAlso Not Responser.Disposed Then
|
||||
_DownloadingInProgress = False
|
||||
Responser_ResponseReceived_RemoveHandler()
|
||||
Declarations.UpdateResponser(Responser, MySiteSettings.Responser)
|
||||
Declarations.UpdateResponser(Responser, MySiteSettings.Responser, WwwClaimUpdate)
|
||||
End If
|
||||
Catch
|
||||
End Try
|
||||
End Sub
|
||||
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
|
||||
Protected Enum Sections : Timeline : Reels : Tagged : Stories : UserStories : SavedPosts : End Enum
|
||||
Protected Const StoriesFolder As String = "Stories"
|
||||
@@ -329,6 +429,12 @@ Namespace API.Instagram
|
||||
#Region "429 bypass"
|
||||
Private Const MaxPostsCount As Integer = 200
|
||||
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
|
||||
Notify = 0
|
||||
SkipCurrent = 1
|
||||
@@ -468,46 +574,74 @@ Namespace API.Instagram
|
||||
Dim HasNextPage As Boolean = False
|
||||
Dim EndCursor$ = String.Empty
|
||||
Dim PostID$ = String.Empty, PostDate$ = String.Empty, SpecFolder$ = String.Empty
|
||||
Dim TokensErrData$ = String.Empty
|
||||
Dim PostIDKV As PostKV
|
||||
Dim ENode() As Object = Nothing
|
||||
Dim processGetResponse As Boolean = True
|
||||
NextRequest(True)
|
||||
|
||||
'Check environment
|
||||
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 _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
|
||||
|
||||
'Create query
|
||||
Select Case Section
|
||||
Case Sections.Timeline
|
||||
URL = $"https://www.instagram.com/api/v1/feed/user/{NameTrue}/username/?count=50" &
|
||||
If(Cursor.IsEmptyString, String.Empty, $"&max_id={Cursor}")
|
||||
ENode = Nothing
|
||||
If _UseGQL Then
|
||||
EndCursor = GetTimelineGQL(Cursor, Token)
|
||||
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
|
||||
r = DownloadReels(Cursor, Token)
|
||||
ChangeResponserMode(True)
|
||||
r = GetReelsGQL(Cursor)
|
||||
ENode = {"data", "xdt_api__v1__clips__user__connection_v2"}
|
||||
processGetResponse = False
|
||||
Case Sections.SavedPosts
|
||||
SavedPostsDownload(String.Empty, Token)
|
||||
Exit Sub
|
||||
ChangeResponserMode(False)
|
||||
EndCursor = SavedPostsDownload(String.Empty, Token)
|
||||
HasNextPage = Not EndCursor.IsEmptyString
|
||||
MySiteSettings.TooManyRequests(False)
|
||||
ThrowAny(Token)
|
||||
GoTo NextPageBlock
|
||||
Case Sections.Tagged
|
||||
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"}
|
||||
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
|
||||
If Not StoriesRequested Then
|
||||
StoriesList = GetStoriesList()
|
||||
StoriesList = If(_UseGQL, GetHighlightsGQL_List(), GetStoriesList())
|
||||
StoriesRequested = True
|
||||
MySiteSettings.TooManyRequests(False)
|
||||
RequestsCount += 1
|
||||
ThrowAny(Token)
|
||||
Continue Do
|
||||
End If
|
||||
If StoriesList.ListExists Then
|
||||
GetStoriesData(StoriesList, False, Token)
|
||||
If _UseGQL Then
|
||||
GetHighlightsGQL(StoriesList, Token)
|
||||
Else
|
||||
GetStoriesData(StoriesList, False, Token)
|
||||
End If
|
||||
MySiteSettings.TooManyRequests(False)
|
||||
RequestsCount += 1
|
||||
End If
|
||||
If StoriesList.ListExists Then
|
||||
Continue Do
|
||||
@@ -515,16 +649,17 @@ Namespace API.Instagram
|
||||
Throw New ExitException
|
||||
End If
|
||||
Case Sections.UserStories
|
||||
GetStoriesData(Nothing, True, Token)
|
||||
If _UseGQL Then GetUserStoriesGQL(Token) Else GetStoriesData(Nothing, True, Token)
|
||||
MySiteSettings.TooManyRequests(False)
|
||||
RequestsCount += 1
|
||||
Throw New ExitException
|
||||
End Select
|
||||
|
||||
'Get response
|
||||
If Not Section = Sections.Reels Then r = Responser.GetResponse(URL,, EDP.ThrowException)
|
||||
If processGetResponse Then
|
||||
UpdateRequestNumber()
|
||||
r = Responser.GetResponse(URL)
|
||||
End If
|
||||
MySiteSettings.TooManyRequests(False)
|
||||
RequestsCount += 1
|
||||
ThrowAny(Token)
|
||||
|
||||
'Parsing
|
||||
@@ -608,9 +743,11 @@ Namespace API.Instagram
|
||||
Else
|
||||
Throw New ExitException
|
||||
End If
|
||||
NextPageBlock:
|
||||
dValue = 0
|
||||
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
|
||||
Catch jsonNull As JsonDocumentException When jsonNull.State = WebDocumentEventArgs.States.Error And
|
||||
(Section = Sections.Reels Or Section = Sections.SavedPosts)
|
||||
Throw jsonNull
|
||||
Catch eex As ExitException
|
||||
Throw eex
|
||||
@@ -618,10 +755,14 @@ Namespace API.Instagram
|
||||
dValue = ProcessException(ex, Token, $"data downloading error [{URL}]",, Section, False)
|
||||
End Try
|
||||
Loop
|
||||
Catch jsonNull2 As JsonDocumentException When jsonNull2.State = WebDocumentEventArgs.States.Error And Section = Sections.Reels
|
||||
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
|
||||
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
|
||||
@@ -638,6 +779,7 @@ Namespace API.Instagram
|
||||
Dim before%
|
||||
Dim specFolder$ = IIf(IsTagged, "Tagged", String.Empty)
|
||||
If PostsToReparse.Count > 0 Then ProgressPre.ChangeMax(PostsToReparse.Count)
|
||||
ChangeResponserMode(False)
|
||||
Try
|
||||
Do While dValue = 1
|
||||
ThrowAny(Token)
|
||||
@@ -657,9 +799,9 @@ Namespace API.Instagram
|
||||
ThrowAny(Token)
|
||||
NextRequest(((i + 1) Mod 5) = 0)
|
||||
ThrowAny(Token)
|
||||
UpdateRequestNumber()
|
||||
r = Responser.GetResponse(URL,, e)
|
||||
MySiteSettings.TooManyRequests(False)
|
||||
RequestsCount += 1
|
||||
If Not r.IsEmptyString Then
|
||||
j = JsonDocument.Parse(r)
|
||||
If Not j Is Nothing Then
|
||||
@@ -692,40 +834,48 @@ Namespace API.Instagram
|
||||
ProcessException(DoEx, Token, $"downloading posts error [{URL}]",, Sections.Tagged)
|
||||
End Try
|
||||
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 HasNextPage As Boolean = False
|
||||
Dim NextCursor$ = String.Empty
|
||||
ThrowAny(Token)
|
||||
Dim processNext As Boolean = False
|
||||
UpdateRequestNumber()
|
||||
Dim r$ = Responser.GetResponse(URL)
|
||||
Dim nodes As IEnumerable(Of EContainer) = Nothing
|
||||
If Not r.IsEmptyString Then
|
||||
Using e As EContainer = JsonDocument.Parse(r)
|
||||
If If(e?.Count, 0) > 0 Then
|
||||
If e.ListExists Then
|
||||
With e
|
||||
HasNextPage = .Value("more_available").FromXML(Of Boolean)(False)
|
||||
NextCursor = .Value("next_max_id")
|
||||
If .Contains("items") Then nodes = (From ee As EContainer In .Item("items") Where ee.Count > 0 Select ee(0))
|
||||
End With
|
||||
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 Using
|
||||
End If
|
||||
End Sub
|
||||
Return If(processNext, NextCursor, String.Empty)
|
||||
End Function
|
||||
Protected DefaultParser_ElemNode() As Object = Nothing
|
||||
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_Pinned As Func(Of IEnumerable(Of EContainer), Integer, Boolean) = Nothing
|
||||
Protected DefaultParser_SkipPost As Func(Of IEnumerable(Of EContainer), Integer, PostKV, Boolean) = Nothing
|
||||
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 Attempts As Integer = 0) As Boolean
|
||||
ThrowAny(Token)
|
||||
If Items.Count > 0 Then
|
||||
If Items.ListExists Then
|
||||
Dim PostIDKV As PostKV
|
||||
Dim Pinned As Boolean
|
||||
Dim PostDate$, PostOriginUrl$
|
||||
Dim before%
|
||||
Dim i%, before%
|
||||
Dim usePinFunc As Boolean = Not DefaultParser_Pinned Is Nothing
|
||||
Dim skipPostFuncExists As Boolean = Not DefaultParser_SkipPost Is Nothing
|
||||
Dim nn As EContainer
|
||||
If SpecFolder.IsEmptyString Then
|
||||
Select Case Section
|
||||
Case Sections.Tagged : SpecFolder = TaggedFolder
|
||||
@@ -734,28 +884,39 @@ Namespace API.Instagram
|
||||
End Select
|
||||
End If
|
||||
ProgressPre.ChangeMax(Items.Count)
|
||||
For Each nn In Items
|
||||
For i = 0 To Items.Count - 1
|
||||
nn = Items(i)
|
||||
ProgressPre.Perform()
|
||||
With If(Not DefaultParser_ElemNode Is Nothing, nn.ItemF(DefaultParser_ElemNode), nn)
|
||||
PostIDKV = New PostKV(.Value("code"), .Value("id"), Section)
|
||||
PostOriginUrl = DefaultParser_PostUrlCreator(PostIDKV)
|
||||
Pinned = .Contains("timeline_pinned_user_ids")
|
||||
If Not DefaultParser_IgnorePass AndAlso PostKvExists(PostIDKV) Then
|
||||
If Not Pinned Then Return False
|
||||
Else
|
||||
_TempPostsList.Add(PostIDKV.ID)
|
||||
PostsKVIDs.ListAddValue(PostIDKV, LNC)
|
||||
PostDate = .Value("taken_at")
|
||||
If Not DefaultParser_IgnorePass And Not IsSavedPosts Then
|
||||
Select Case CheckDatesLimit(PostDate, UnixDate32Provider)
|
||||
Case DateResult.Skip : Continue For
|
||||
Case DateResult.Exit : If Not Pinned Then Return False
|
||||
End Select
|
||||
If .ListExists Then
|
||||
PostIDKV = New PostKV(.Value("code"), .Value("id"), Section)
|
||||
PostOriginUrl = DefaultParser_PostUrlCreator(PostIDKV)
|
||||
'Pinned = .Contains("timeline_pinned_user_ids")
|
||||
If usePinFunc Then
|
||||
Pinned = DefaultParser_Pinned.Invoke(Items, i)
|
||||
Else
|
||||
Pinned = If(.Item("timeline_pinned_user_ids")?.Count, 0) > 0
|
||||
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
|
||||
If skipPostFuncExists AndAlso DefaultParser_SkipPost.Invoke(Items, i, PostIDKV) Then
|
||||
ElseIf Not DefaultParser_IgnorePass AndAlso PostKvExists(PostIDKV) Then
|
||||
If Not Section = Sections.Timeline OrElse Not Pinned Then Return False
|
||||
Else
|
||||
_TempPostsList.Add(PostIDKV.ID)
|
||||
PostsKVIDs.ListAddValue(PostIDKV, LNC)
|
||||
PostDate = .Value("taken_at")
|
||||
If Not DefaultParser_IgnorePass And Not IsSavedPosts Then
|
||||
Select Case CheckDatesLimit(PostDate, UnixDate32Provider)
|
||||
Case DateResult.Skip : Continue For
|
||||
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
|
||||
Else
|
||||
Return False
|
||||
End If
|
||||
End With
|
||||
Next
|
||||
@@ -765,106 +926,6 @@ Namespace API.Instagram
|
||||
End If
|
||||
End Function
|
||||
#End Region
|
||||
#Region "Get reels"
|
||||
Private _GetReels_LSD As String = String.Empty
|
||||
Private _GetReels_dtsg As String = String.Empty
|
||||
Private ReadOnly Property DownloadReels_Tokens_Valid As Boolean
|
||||
Get
|
||||
Return Not _GetReels_LSD.IsEmptyString And Not _GetReels_dtsg.IsEmptyString
|
||||
End Get
|
||||
End Property
|
||||
Private WriteOnly Property DownloadReels_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
|
||||
Private Class Responser2 : Inherits Responser
|
||||
Friend Sub New(ByVal Source As Responser)
|
||||
MyBase.New
|
||||
Copy(Source)
|
||||
ErrorProcessor = New ResponserErrorProcessor(Source)
|
||||
End Sub
|
||||
End Class
|
||||
''' <returns>Response</returns>
|
||||
Private Function DownloadReels(ByVal Cursor As String, ByVal Token As CancellationToken) As String
|
||||
Const requestPattern$ = "https://www.instagram.com/api/graphql?fb_dtsg={0}&fb_api_req_friendly_name=PolarisProfileReelsTabContentQuery&lsd={1}&doc_id=7191572580905225&variables={2}"
|
||||
|
||||
DownloadReels_SetEnvir = True
|
||||
|
||||
If Cursor.IsEmptyString And Not DownloadReels_Tokens_Valid Then GetPageTokens()
|
||||
If Cursor.IsEmptyString And Not DownloadReels_Tokens_Valid Then Throw New ExitException
|
||||
|
||||
Using resp As New Responser2(Responser)
|
||||
Try
|
||||
resp.Method = "POST"
|
||||
AddHandler resp.ResponseReceived, AddressOf Responser_ResponseReceived
|
||||
resp.Headers.Add(Header_FB_LSD, _GetReels_LSD)
|
||||
|
||||
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 = "{" & vars & "}"
|
||||
|
||||
Dim url$ = String.Format(requestPattern, _GetReels_dtsg, _GetReels_LSD, SymbolsConverter.ASCII.EncodeSymbolsOnly(vars))
|
||||
|
||||
Return resp.GetResponse(url,, EDP.ThrowException)
|
||||
Finally
|
||||
With resp
|
||||
Responser.Cookies.Update(.Cookies)
|
||||
With .Headers
|
||||
If .Contains(SiteSettings.Header_IG_WWW_CLAIM) Then Responser.Headers.Add(SiteSettings.Header_IG_WWW_CLAIM, .Value(SiteSettings.Header_IG_WWW_CLAIM))
|
||||
If .Contains(SiteSettings.Header_CSRF_TOKEN) Then Responser.Headers.Add(SiteSettings.Header_CSRF_TOKEN, .Value(SiteSettings.Header_CSRF_TOKEN))
|
||||
End With
|
||||
End With
|
||||
End Try
|
||||
End Using
|
||||
End Function
|
||||
Private Function GetPageTokens() As Boolean
|
||||
_GetReels_LSD = String.Empty
|
||||
_GetReels_dtsg = String.Empty
|
||||
Try
|
||||
Dim r$ = Responser.GetResponse(MySiteSettings.GetUserUrl(Me),, EDP.ThrowException)
|
||||
If Not r.IsEmptyString Then
|
||||
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 _GetReels_LSD.IsEmptyString And Not _GetReels_dtsg.IsEmptyString Then
|
||||
Exit For
|
||||
Else
|
||||
ttVal = RegexReplace(tt, rr)
|
||||
If Not ttVal.IsEmptyString Then
|
||||
If ttVal.Contains(":") Then
|
||||
If _GetReels_dtsg.IsEmptyString Then _GetReels_dtsg = ttVal
|
||||
Else
|
||||
If _GetReels_LSD.IsEmptyString Then _GetReels_LSD = ttVal
|
||||
End If
|
||||
End If
|
||||
End If
|
||||
Next
|
||||
End If
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Dim notFound$ = String.Empty
|
||||
If _GetReels_dtsg.IsEmptyString Then notFound.StringAppend(Header_FB_LSD)
|
||||
If _GetReels_LSD.IsEmptyString Then notFound.StringAppend("lsd")
|
||||
LogError(ex, $"failed to update some{IIf(notFound.IsEmptyString, String.Empty, $" ({notFound})")} credentials", EDP.SendToLog)
|
||||
End Try
|
||||
Return DownloadReels_Tokens_Valid
|
||||
End Function
|
||||
#End Region
|
||||
#Region "Code ID converters"
|
||||
Protected Function CodeToID(ByVal Code As String) As String
|
||||
Const CodeSymbols$ = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
|
||||
@@ -909,7 +970,8 @@ Namespace API.Instagram
|
||||
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 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
|
||||
Dim maxSize As Func(Of EContainer, Integer) = Function(ByVal _ss As EContainer) As Integer
|
||||
Dim w% = AConvert(Of Integer)(_ss.Value("width"), 0)
|
||||
@@ -953,7 +1015,10 @@ Namespace API.Instagram
|
||||
'2 - one video
|
||||
'1 - one picture
|
||||
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
|
||||
t = 2
|
||||
abstractDecision = True
|
||||
@@ -964,7 +1029,7 @@ Namespace API.Instagram
|
||||
End If
|
||||
If t >= 0 Then
|
||||
Select Case t
|
||||
Case 1
|
||||
Case 1 'one picture
|
||||
If n.Contains(img) Then
|
||||
If Not abstractDecision Then t = n.Value("media_type").FromXML(Of Integer)(-1)
|
||||
DateObj = mDate(n)
|
||||
@@ -983,7 +1048,7 @@ Namespace API.Instagram
|
||||
End With
|
||||
End If
|
||||
End If
|
||||
Case 2
|
||||
Case 2 'one video
|
||||
If n.Contains(vid) Then
|
||||
DateObj = mDate(n)
|
||||
With n.ItemF({vid}).XmlIfNothing
|
||||
@@ -999,7 +1064,8 @@ Namespace API.Instagram
|
||||
End If
|
||||
End With
|
||||
End If
|
||||
Case 8
|
||||
If Not TryExtractImage Then ObtainMedia(n, PostID, SpecialFolder, DateObj, InitialType, PostOriginUrl, State, Attempts, True)
|
||||
Case 8 'gallery
|
||||
DateObj = mDate(n)
|
||||
With n("carousel_media").XmlIfNothing
|
||||
If .Count > 0 Then
|
||||
@@ -1016,11 +1082,12 @@ Namespace API.Instagram
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "GetUserId, GetUserName"
|
||||
Private Sub GetUserId()
|
||||
Private Sub GetUserData()
|
||||
Dim __idFound As Boolean = False
|
||||
Try
|
||||
RequestsCount += 1
|
||||
Dim r$ = Responser.GetResponse($"https://i.instagram.com/api/v1/users/web_profile_info/?username={NameTrue}",, EDP.ThrowException)
|
||||
ChangeResponserMode(False)
|
||||
UpdateRequestNumber()
|
||||
Dim r$ = Responser.GetResponse($"https://i.instagram.com/api/v1/users/web_profile_info/?username={NameTrue}")
|
||||
If Not r.IsEmptyString Then
|
||||
Using j As EContainer = JsonDocument.Parse(r)
|
||||
If Not j Is Nothing AndAlso j.Contains({"data", "user"}) Then
|
||||
@@ -1034,7 +1101,7 @@ Namespace API.Instagram
|
||||
Dim eUrl$ = .Value("external_url")
|
||||
If Not eUrl.IsEmptyString AndAlso (descr.IsEmptyString OrElse Not descr.Contains(eUrl)) Then descr.StringAppendLine(eUrl)
|
||||
UserDescriptionUpdate(descr)
|
||||
Dim f As New SFile With {.Path = 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
|
||||
Dim profilePicture$ = .Value("profile_pic_url_hd")
|
||||
If profilePicture.IsEmptyString OrElse Not GetWebFile(profilePicture, f, EDP.ReturnValue) Then
|
||||
@@ -1054,13 +1121,15 @@ Namespace API.Instagram
|
||||
LogError(ex, "get Instagram user ID")
|
||||
End If
|
||||
End If
|
||||
Finally
|
||||
ChangeResponserMode(_UseGQL)
|
||||
End Try
|
||||
End Sub
|
||||
Private Function GetUserNameById() As Boolean
|
||||
UserNameRequested = True
|
||||
Try
|
||||
If Not ID.IsEmptyString Then
|
||||
RequestsCount += 1
|
||||
UpdateRequestNumber()
|
||||
Dim r$ = Responser.GetResponse($"https://i.instagram.com/api/v1/users/{ID}/info/",, EDP.ReturnValue)
|
||||
If Not r.IsEmptyString Then
|
||||
Using j As EContainer = JsonDocument.Parse(r, EDP.ReturnValue)
|
||||
@@ -1093,9 +1162,9 @@ Namespace API.Instagram
|
||||
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}"
|
||||
Dim tmpList As IEnumerable(Of String) = Nothing
|
||||
Dim qStr$, r$, sFolder$, storyID$, pid$
|
||||
Dim qStr$, r$
|
||||
Dim i% = -1
|
||||
Dim jj As EContainer, s As EContainer
|
||||
Dim jj As EContainer
|
||||
ThrowAny(Token)
|
||||
If StoriesList.ListExists Or GetUserStory Then
|
||||
If Not GetUserStory Then tmpList = StoriesList.Take(5)
|
||||
@@ -1105,38 +1174,14 @@ Namespace API.Instagram
|
||||
Else
|
||||
qStr = String.Format(ReqUrl, tmpList.Select(Function(q) $"reel_ids=highlight:{q}").ListToString("&"))
|
||||
End If
|
||||
UpdateRequestNumber()
|
||||
r = Responser.GetResponse(qStr,, EDP.ThrowException)
|
||||
ThrowAny(Token)
|
||||
If Not r.IsEmptyString Then
|
||||
Using j As EContainer = JsonDocument.Parse(r).XmlIfNothing
|
||||
If j.Contains("reels") Then
|
||||
ProgressPre.ChangeMax(j("reels").Count)
|
||||
For Each jj In j("reels")
|
||||
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
|
||||
For Each jj In j("reels") : GetStoriesData_ParseSingleHighlight(jj, i, GetUserStory, Token) : Next
|
||||
End If
|
||||
End Using
|
||||
End If
|
||||
@@ -1144,8 +1189,39 @@ Namespace API.Instagram
|
||||
End If
|
||||
End If
|
||||
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)
|
||||
Try
|
||||
UpdateRequestNumber()
|
||||
Dim r$ = Responser.GetResponse($"https://i.instagram.com/api/v1/highlights/{ID}/highlights_tray/",, EDP.ThrowException)
|
||||
If Not r.IsEmptyString Then
|
||||
Dim ee As New ErrorsDescriber(EDP.ReturnValue) With {.DeclaredMessage = New MMessage($"{ToStringForLog()}:")}
|
||||
@@ -1169,6 +1245,7 @@ Namespace API.Instagram
|
||||
Protected Overrides Sub EraseData_AdditionalDataFiles()
|
||||
Dim f As SFile = MyFilePostsKV
|
||||
If f.Exists Then f.Delete(SFO.File, SFODelete.DeleteToRecycleBin, EDP.ReturnValue)
|
||||
FirstLoadingDone = False
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Exceptions"
|
||||
@@ -1204,6 +1281,9 @@ Namespace API.Instagram
|
||||
ElseIf Responser.StatusCode = 560 Or Responser.StatusCode = HttpStatusCode.InternalServerError Then '560, 500
|
||||
MySiteSettings.SkipUntilNextSession = True
|
||||
Err5xx = Responser.StatusCode
|
||||
ElseIf Responser.StatusCode = -1 And Responser.Status = -1 Then
|
||||
MySiteSettings.SkipUntilNextSession = True
|
||||
Err5xx = Responser.StatusCode
|
||||
Else
|
||||
MyMainLOG = $"Something is wrong. Your credentials may have expired [{CInt(Responser.StatusCode)}/{CInt(Responser.Status)}]: {ToString()} [{s}]"
|
||||
DisableSection(s)
|
||||
@@ -1215,16 +1295,26 @@ Namespace API.Instagram
|
||||
Private Sub DisableSection(ByVal Section As Object)
|
||||
If Not IsNothing(Section) AndAlso TypeOf Section Is Sections Then
|
||||
Dim s As Sections = DirectCast(Section, Sections)
|
||||
Select Case s
|
||||
Case Sections.Timeline : MySiteSettings.DownloadTimeline.Value = False
|
||||
Case Sections.Reels : MySiteSettings.DownloadReels.Value = False
|
||||
Case Sections.Tagged : MySiteSettings.DownloadTagged.Value = False
|
||||
Case Sections.Stories, Sections.UserStories
|
||||
MySiteSettings.DownloadTimeline.Value = False
|
||||
MySiteSettings.DownloadStories.Value = False
|
||||
MySiteSettings.DownloadStoriesUser.Value = False
|
||||
End Select
|
||||
MyMainLOG = $"[{s}] downloading is disabled until you update your credentials".ToUpper
|
||||
Dim ss As New List(Of Sections)([Enum].GetValues(GetType(Sections)).ToObjectsList(Of Sections))
|
||||
If s = Sections.Reels And Not _UseGQL Then
|
||||
ss.Clear()
|
||||
ss.Add(s)
|
||||
ElseIf s = Sections.Tagged Then
|
||||
ss.Clear()
|
||||
ss.Add(s)
|
||||
End If
|
||||
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 Sub
|
||||
#End Region
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports System.Threading
|
||||
Imports System.Net
|
||||
Imports SCrawler.API.Base
|
||||
Imports PersonalUtilities.Tools
|
||||
Imports PersonalUtilities.Tools.Web
|
||||
@@ -18,8 +18,10 @@ Namespace API.JustForFans
|
||||
Friend NotInheritable Class M3U8 : Implements IDisposable
|
||||
#Region "Declarations"
|
||||
Friend Const AllVid As UTypes = UTypes.m3u8 + UTypes.VideoPre
|
||||
Private ReadOnly DataVideo As List(Of String)
|
||||
Private ReadOnly DataAudio As List(Of String)
|
||||
Private Structure M3U8URL_Indexed
|
||||
Friend Index As Integer
|
||||
Friend File As SFile
|
||||
End Structure
|
||||
Private Media As UserMedia
|
||||
Private DestinationFile As SFile
|
||||
Private ReadOnly Thrower As Plugin.IThrower
|
||||
@@ -32,31 +34,37 @@ Namespace API.JustForFans
|
||||
Private UrlAudio As String
|
||||
Private FileVideo 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 ReadOnly Cache As CacheKeeper
|
||||
Private ReadOnly Progress As MyProgress
|
||||
Private ReadOnly ProgressPre As PreProgress
|
||||
Private ReadOnly ProgressExists 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
|
||||
#Region "Initializer"
|
||||
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
|
||||
DataVideo = New List(Of String)
|
||||
DataAudio = New List(Of String)
|
||||
DestinationFile = Destination
|
||||
Thrower = _Thrower
|
||||
'Responser = Resp
|
||||
Responser = New Responser
|
||||
ResponserInternal = True
|
||||
FileVideo_IndexedParts = New List(Of M3U8URL_Indexed)
|
||||
FileAudio_IndexedParts = New List(Of M3U8URL_Indexed)
|
||||
Progress = _Progress
|
||||
ProgressExists = Not Progress Is Nothing
|
||||
If ProgressExists Then ProgressPre = New PreProgress(Progress)
|
||||
UsePreProgress = _UsePreProgress
|
||||
Token = _Token
|
||||
Cache = New CacheKeeper($"{DestinationFile.PathWithSeparator}_{M3U8Base.TempCacheFolderName}\")
|
||||
MyFileNumberProvider = M3U8Base.NumberProviderDefault
|
||||
Cache = New CacheKeeper($"{DestinationFile.PathWithSeparator}_{M3U8Base.TempCacheFolderName}\") With {.DisposeSuspended = True}
|
||||
With Cache
|
||||
.CacheDeleteError = CacheDeletionError(Cache)
|
||||
.DisposeSuspended = True
|
||||
@@ -91,30 +99,138 @@ Namespace API.JustForFans
|
||||
UrlVideo = RegexReplace(r, RParams.DMS(R_VIDEO_REGEX_PATTERN, 6, EDP.ReturnValue))
|
||||
UrlAudio = RegexReplace(r, REGEX_AUDIO_URL)
|
||||
If UrlVideo.IsEmptyString Then Throw New ArgumentException("Unable to identify m3u8 video track", "M3U8 video track")
|
||||
|
||||
Thrower.ThrowAny()
|
||||
GetFiles(UrlVideo, FileVideo, False)
|
||||
GetFileParts(UrlVideo, FileVideo_M3U8, FileVideo_IndexedParts, False)
|
||||
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()
|
||||
MergeFiles()
|
||||
End If
|
||||
End If
|
||||
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
|
||||
Dim r$ = Responser.GetResponse(URL)
|
||||
If Not r.IsEmptyString Then
|
||||
Dim data As List(Of RegexMatchStruct) = RegexFields(Of RegexMatchStruct)(r, {REGEX_PLS_FILES}, {1, 2}, EDP.ReturnValue)
|
||||
If data.ListExists Then
|
||||
File = $"{Cache.RootDirectory.PathWithSeparator}{IIf(IsAudio, "AUDIO.aac", "VIDEO.mp4")}"
|
||||
Using b As New TokenBatch(Token) With {.Encoding = Settings.CMDEncoding, .MainProcessName = "ffmpeg"}
|
||||
AddHandler b.ErrorDataReceived, AddressOf Batch_OutputDataReceived
|
||||
ProgressChangeMax(data.Count)
|
||||
b.ChangeDirectory(Cache.RootDirectory)
|
||||
b.Execute($"""{Settings.FfmpegFile}"" -i {URL} -vcodec copy -strict -2 ""{File}""")
|
||||
Token.ThrowIfCancellationRequested()
|
||||
If Not File.Exists Then File = Nothing
|
||||
End Using
|
||||
Dim appender$ = URL.Replace(URL.Split("/").LastOrDefault, String.Empty)
|
||||
Dim createM3U8URL As Func(Of String, M3U8URL) =
|
||||
Function(input) New M3U8URL(M3U8Base.CreateUrl(appender, input), RegexReplace(input, REGEX_FILE_EXT))
|
||||
With (From d As RegexMatchStruct In data
|
||||
Where Not d.Arr(0).IfNullOrEmpty(d.Arr(1)).IsEmptyString
|
||||
Select createM3U8URL.Invoke(d.Arr(0).IfNullOrEmpty(d.Arr(1)).StringTrim))
|
||||
If .ListExists Then
|
||||
ProgressChangeMax(.Count)
|
||||
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
|
||||
Catch oex As OperationCanceledException
|
||||
@@ -123,7 +239,7 @@ Namespace API.JustForFans
|
||||
Throw dex
|
||||
Catch ex As Exception
|
||||
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 Sub
|
||||
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)
|
||||
If Not FileVideo.IsEmptyString And Not FileAudio.IsEmptyString Then
|
||||
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
|
||||
Else
|
||||
Throw New Exception($"Unable to download file ({Media.URL_BASE})")
|
||||
End If
|
||||
Catch ex As Exception
|
||||
ErrorsDescriber.Execute(EDP.SendToLog + EDP.ThrowException, ex, $"[M3U8.MergeFiles]")
|
||||
@@ -165,8 +283,8 @@ Namespace API.JustForFans
|
||||
#End Region
|
||||
#Region "Static Download"
|
||||
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
|
||||
Using m As New M3U8(Media, DestinationFile, Resp, Thrower, Progress, UsePreProgress, _Token)
|
||||
ByVal Progress As MyProgress, ByVal UsePreProgress As Boolean) As SFile
|
||||
Using m As New M3U8(Media, DestinationFile, Resp, Thrower, Progress, UsePreProgress)
|
||||
m.Download()
|
||||
If m.DestinationFile.Exists Then Return m.DestinationFile Else Return Nothing
|
||||
End Using
|
||||
@@ -177,8 +295,8 @@ Namespace API.JustForFans
|
||||
Private Overloads Sub Dispose(ByVal disposing As Boolean)
|
||||
If Not disposedValue Then
|
||||
If disposing Then
|
||||
DataVideo.Clear()
|
||||
DataAudio.Clear()
|
||||
FileVideo_IndexedParts.Clear()
|
||||
FileAudio_IndexedParts.Clear()
|
||||
ProgressPre.DisposeIfReady
|
||||
Cache.Dispose()
|
||||
If ResponserInternal Then Responser.DisposeIfReady
|
||||
|
||||
@@ -20,9 +20,14 @@ Namespace API.JustForFans
|
||||
Friend ReadOnly Property UserID As PropertyValue
|
||||
<PropertyOption, PXML, PClonable(Clone:=False)>
|
||||
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>
|
||||
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)
|
||||
Select Case HeaderName
|
||||
Case NameOf(HeaderAccept) : If HeaderValue.IsEmptyString Then Responser.Accept = Nothing Else Responser.Accept = HeaderValue
|
||||
@@ -60,7 +65,7 @@ Namespace API.JustForFans
|
||||
Private Sub UpdateUserHash4()
|
||||
If Responser.CookiesExists Then
|
||||
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
|
||||
End If
|
||||
End Sub
|
||||
|
||||
@@ -336,7 +336,7 @@ Namespace API.JustForFans
|
||||
DownloadContentDefault(Token)
|
||||
End Sub
|
||||
Protected Overrides Function DownloadM3U8(ByVal URL As String, ByVal Media As UserMedia, ByVal DestinationFile As SFile, ByVal Token As CancellationToken) As SFile
|
||||
Return M3U8.Download(Media, DestinationFile, ResponserNoHandlers, Me, Progress, Not IsSingleObjectDownload, Token)
|
||||
Return M3U8.Download(Media, DestinationFile, ResponserNoHandlers, Me, Progress, Not IsSingleObjectDownload)
|
||||
End Function
|
||||
#End Region
|
||||
#Region "DownloadSingleObject"
|
||||
|
||||
@@ -14,6 +14,7 @@ Namespace API.Mastodon
|
||||
<PSetting(Address:=SettingAddress.None)> Friend Overrides Property DownloadModelProfile As Boolean
|
||||
<PSetting(Address:=SettingAddress.None)> Friend Overrides Property DownloadModelSearch As Boolean
|
||||
<PSetting(Address:=SettingAddress.None)> Friend Overrides Property DownloadModelForceApply As Boolean
|
||||
<PSetting(Address:=SettingAddress.None)> Friend Overrides Property DownloadModelLikes As Boolean
|
||||
Friend Sub New(ByVal s As SiteSettings)
|
||||
MyBase.New(s)
|
||||
End Sub
|
||||
|
||||
@@ -63,15 +63,15 @@ Namespace API.Mastodon
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Other properties"
|
||||
<PropertyOption(IsAuth:=False, ControlText:=DN.GifsDownloadCaption), PXML, PClonable>
|
||||
<PropertyOption(IsAuth:=False, ControlText:=DN.GifsDownloadCaption, Category:=DN.CAT_UserDefs), PXML, PClonable>
|
||||
Friend ReadOnly Property GifsDownload As PropertyValue
|
||||
<PropertyOption(IsAuth:=False, ControlText:=DN.GifsSpecialFolderCaption, ControlToolTip:=DN.GifsSpecialFolderToolTip), PXML, PClonable>
|
||||
<PropertyOption(IsAuth:=False, ControlText:=DN.GifsSpecialFolderCaption, ControlToolTip:=DN.GifsSpecialFolderToolTip, Category:=DN.CAT_UserDefs), PXML, PClonable>
|
||||
Friend ReadOnly Property GifsSpecialFolder As PropertyValue
|
||||
<PropertyOption(IsAuth:=False, ControlText:=DN.GifsPrefixCaption, ControlToolTip:=DN.GifsPrefixToolTip), PXML, PClonable>
|
||||
<PropertyOption(IsAuth:=False, ControlText:=DN.GifsPrefixCaption, ControlToolTip:=DN.GifsPrefixToolTip, Category:=DN.CAT_UserDefs), PXML, PClonable>
|
||||
Friend ReadOnly Property GifsPrefix As PropertyValue
|
||||
<Provider(NameOf(GifsSpecialFolder), Interaction:=True), Provider(NameOf(GifsPrefix), Interaction:=True)>
|
||||
Private ReadOnly Property GifStringChecker As IFormatProvider
|
||||
<PropertyOption(IsAuth:=False, ControlText:=DN.UseMD5ComparisonCaption, ControlToolTip:=DN.UseMD5ComparisonToolTip), PXML, PClonable>
|
||||
<PropertyOption(IsAuth:=False, ControlText:=DN.UseMD5ComparisonCaption, ControlToolTip:=DN.UseMD5ComparisonToolTip, Category:=DN.CAT_UserDefs), PXML, PClonable>
|
||||
Friend ReadOnly Property UseMD5Comparison As PropertyValue
|
||||
<PropertyOption(IsAuth:=False, ControlText:="User related to my domain",
|
||||
ControlToolTip:="Open user profiles and user posts through my domain."), PXML, PClonable>
|
||||
|
||||
@@ -11,5 +11,6 @@ Namespace API.OnlyFans
|
||||
Friend Module Declarations
|
||||
Friend ReadOnly DateProvider As New ADateTime("O")
|
||||
Friend ReadOnly RegExPostID As RParams = RParams.DM("(?<=onlyfans\.com/)(\d+)", 0, EDP.ReturnValue)
|
||||
Friend Property Rules As DynamicRulesEnv
|
||||
End Module
|
||||
End Namespace
|
||||
7
SCrawler/API/OnlyFans/DynamicRules.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
https://github.com/datawhores/onlyfans-dynamic-rules/blob/main/dynamicRules.json
|
||||
https://github.com/riley-access-labs/onlyfans-dynamic-rules-1/blob/main/dynamicRules.json
|
||||
https://github.com/riley-access-labs/onlyfans-dynamic-rules-1/blob/patch-1/dynamicRules.json
|
||||
https://github.com/DATAHOARDERS/dynamic-rules/blob/main/onlyfans.json
|
||||
https://github.com/DIGITALCRIMINAL/dynamic-rules/blob/main/onlyfans.json
|
||||
https://github.com/deviint/onlyfans-dynamic-rules/blob/main/dynamicRules.json
|
||||
https://github.com/rafa-9/dynamic-rules/blob/main/rules.json
|
||||
11
SCrawler/API/OnlyFans/DynamicRulesAll.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
https://github.com/datawhores/onlyfans-dynamic-rules/blob/main/dynamicRules.json
|
||||
https://github.com/datawhores/onlyfans-dynamic-rules/blob/main/rules.json
|
||||
https://github.com/riley-access-labs/onlyfans-dynamic-rules-1/blob/main/dynamicRules.json
|
||||
https://github.com/riley-access-labs/onlyfans-dynamic-rules-1/blob/patch-1/dynamicRules.json
|
||||
https://github.com/DATAHOARDERS/dynamic-rules/blob/main/onlyfans.json
|
||||
https://github.com/DIGITALCRIMINAL/dynamic-rules/blob/main/onlyfans.json
|
||||
https://github.com/deviint/onlyfans-dynamic-rules/blob/main/dynamicRules.json
|
||||
https://github.com/rafa-9/dynamic-rules/blob/main/rules.json
|
||||
|
||||
https://github.com/SneakyOvis/onlyfans-dynamic-rules/blob/main/rules.json
|
||||
https://github.com/Growik/onlyfans-dynamic-rules/blob/main/rules.json
|
||||
753
SCrawler/API/OnlyFans/DynamicRulesEnv.vb
Normal file
@@ -0,0 +1,753 @@
|
||||
' Copyright (C) Andy https://github.com/AAndyProgram
|
||||
' This program is free software: you can redistribute it and/or modify
|
||||
' it under the terms of the GNU General Public License as published by
|
||||
' the Free Software Foundation, either version 3 of the License, or
|
||||
' (at your option) any later version.
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports PersonalUtilities.Functions.XML
|
||||
Imports PersonalUtilities.Functions.XML.Base
|
||||
Imports PersonalUtilities.Functions.RegularExpressions
|
||||
Imports PersonalUtilities.Tools
|
||||
Imports PersonalUtilities.Tools.Web.Clients
|
||||
Imports PersonalUtilities.Tools.Web.Clients.Base
|
||||
Imports PersonalUtilities.Tools.Web.Documents.JSON
|
||||
Imports System.Text.RegularExpressions
|
||||
Namespace API.OnlyFans
|
||||
Friend Structure DynamicRulesValue : Implements IComparable(Of DynamicRulesValue), IEquatable(Of DynamicRulesValue), IEContainerProvider
|
||||
#Region "XML names"
|
||||
Private Const Name_UrlRepo As String = "UrlRepo"
|
||||
Private Const Name_UrlRaw As String = "UrlRaw"
|
||||
Private Const Name_UrlLatestCommit As String = "UrlLatestCommit"
|
||||
Private Const Name_UpdatedAt As String = "UpdatedAt"
|
||||
Private Const Name_Broken As String = "Broken"
|
||||
Private Const Name_Exists As String = "Exists"
|
||||
#End Region
|
||||
#Region "Declarations"
|
||||
Friend UrlRepo As String
|
||||
Friend UrlRaw As String
|
||||
Friend UrlLatestCommit As String
|
||||
Friend UpdatedAt As Date
|
||||
Friend Broken As Boolean
|
||||
Friend Exists As Boolean
|
||||
Friend ReadOnly Property Valid As Boolean
|
||||
Get
|
||||
Return Not UrlRepo.IsEmptyString And Not UrlRaw.IsEmptyString
|
||||
End Get
|
||||
End Property
|
||||
#End Region
|
||||
#Region "Initializers"
|
||||
Friend Sub New(ByVal e As EContainer)
|
||||
UrlRepo = e.Value(Name_UrlRepo)
|
||||
UrlRaw = e.Value(Name_UrlRaw)
|
||||
UrlLatestCommit = e.Value(Name_UrlLatestCommit)
|
||||
UpdatedAt = e.Value(Name_UpdatedAt).ToDateDef(Now.AddYears(-10))
|
||||
Broken = e.Value(Name_Broken).FromXML(Of Boolean)(False)
|
||||
Exists = e.Value(Name_Exists).FromXML(Of Boolean)(True)
|
||||
End Sub
|
||||
Public Shared Widening Operator CType(ByVal e As EContainer) As DynamicRulesValue
|
||||
Return New DynamicRulesValue(e)
|
||||
End Operator
|
||||
Public Shared Widening Operator CType(ByVal rule As DynamicRulesValue) As String
|
||||
Return rule.ToString
|
||||
End Operator
|
||||
#End Region
|
||||
#Region "Base functions"
|
||||
Public Overrides Function GetHashCode() As Integer
|
||||
Return ToString.GetHashCode
|
||||
End Function
|
||||
Public Overrides Function ToString() As String
|
||||
Return UrlRaw
|
||||
End Function
|
||||
#End Region
|
||||
#Region "IComparable Support"
|
||||
Private Function CompareTo(ByVal Other As DynamicRulesValue) As Integer Implements IComparable(Of DynamicRulesValue).CompareTo
|
||||
Return UpdatedAt.CompareTo(Other.UpdatedAt) * -1
|
||||
End Function
|
||||
#End Region
|
||||
#Region "IEquatable Support"
|
||||
Public Overloads Overrides Function Equals(ByVal Obj As Object) As Boolean
|
||||
If Not IsNothing(Obj) Then
|
||||
If TypeOf Obj Is String Then
|
||||
Dim _obj$ = CStr(Obj).StringTrim.StringToLower
|
||||
Return UrlRepo = _obj Or UrlRaw = _obj
|
||||
Else
|
||||
Return Equals(DirectCast(Obj, DynamicRulesValue))
|
||||
End If
|
||||
Else
|
||||
Return False
|
||||
End If
|
||||
End Function
|
||||
Friend Overloads Function Equals(ByVal Other As DynamicRulesValue) As Boolean Implements IEquatable(Of DynamicRulesValue).Equals
|
||||
Return UrlRepo = Other.UrlRepo Or UrlRaw = Other.UrlRaw
|
||||
End Function
|
||||
#End Region
|
||||
#Region "IEContainerProvider Support"
|
||||
Private Function ToEContainer(Optional ByVal e As ErrorsDescriber = Nothing) As EContainer Implements IEContainerProvider.ToEContainer
|
||||
Return New EContainer("Rule") From {
|
||||
New EContainer(Name_UrlRepo, UrlRepo),
|
||||
New EContainer(Name_UrlRaw, UrlRaw),
|
||||
New EContainer(Name_UrlLatestCommit, UrlLatestCommit),
|
||||
New EContainer(Name_UpdatedAt, UpdatedAt.ToStringDateDef),
|
||||
New EContainer(Name_Broken, Broken.BoolToInteger),
|
||||
New EContainer(Name_Exists, Exists.BoolToInteger)
|
||||
}
|
||||
End Function
|
||||
#End Region
|
||||
End Structure
|
||||
Friend Class DynamicRulesEnv : Implements ICopier, IEnumerable(Of DynamicRulesValue), IMyEnumerator(Of DynamicRulesValue), IDisposable
|
||||
Friend Enum Modes As Integer
|
||||
List = 0
|
||||
Personal = 1
|
||||
End Enum
|
||||
#Region "Constants"
|
||||
Friend Const UpdateIntervalDefault As Integer = 1440 '60 * 24
|
||||
Friend Const DynamicRulesConfigNodeName_URL As String = "DYNAMIC_GENERIC_URL"
|
||||
Friend Const DynamicRulesConfigNodeName_RULES As String = "DYNAMIC_RULE"
|
||||
|
||||
Friend Const DynamicRulesConfig_Mode_NodeName As String = "dynamic-mode-default"
|
||||
'Friend Const DynamicRulesConfig_Mode_NodeValue As String = "generic"
|
||||
|
||||
Friend Const DynamicRulesConfigNodeName_URL_CONST_NAME As String = "RULE_VALUE"
|
||||
#End Region
|
||||
#Region "XML names"
|
||||
Private Const Name_LastUpdateTimeFile As String = "LastUpdateTimeFile"
|
||||
Private Const Name_LastUpdateTimeRules As String = "LastUpdateTimeRules"
|
||||
Private Const Name_ProtectFile As String = "ProtectFile"
|
||||
Private Const Name_UpdateInterval As String = "UpdateInterval"
|
||||
Private Const Name_Mode As String = "Mode"
|
||||
Private Const Name_PersonalRule As String = "PersonalRule"
|
||||
Private Const Name_RulesForceUpdateRequired As String = "RulesForceUpdateRequired"
|
||||
Private Const Name_AddErrorsToLog As String = "AddErrorsToLog"
|
||||
Private Const Name_ConfigLastDateUpdate As String = "ConfigLastDateUpdate"
|
||||
Private Const Name_ConfigAutoUpdate As String = "ConfigAutoUpdate"
|
||||
Private Const Name_RulesConfigManualMode As String = "RulesConfigManualMode"
|
||||
Private Const Name_RulesUpdateConst As String = "RulesUpdateConst"
|
||||
Private Const Name_RulesReplaceConfig As String = "RulesReplaceConfig"
|
||||
#End Region
|
||||
#Region "Declarations"
|
||||
Private ReadOnly Rules As List(Of DynamicRulesValue)
|
||||
Friend ReadOnly Property RulesConstants As Dictionary(Of String, String)
|
||||
#Region "Regex patterns"
|
||||
Private ReadOnly ReplacePattern_RepoToRaw As RParams
|
||||
Private ReadOnly ReplacePattern_RawToRepo As RParams
|
||||
Private ReadOnly ReplacePattern_JsonInfo As RParams
|
||||
Private ReadOnly ConfigRulesExtract As RParams
|
||||
#End Region
|
||||
#Region "Dates"
|
||||
Private LastUpdateTimeFile As Date = Now.AddYears(-1)
|
||||
Private LastUpdateTimeRules As Date = Now.AddYears(-1)
|
||||
#End Region
|
||||
#Region "Files"
|
||||
Friend ReadOnly OFScraperConfigPatternFile As SFile = $"{SettingsFolderName}\OFScraperConfigPattern.json"
|
||||
Friend ReadOnly OFScraperConfigPatternFileConst As SFile = $"{SettingsFolderName}\OFScraperConfigPatternConstants.txt"
|
||||
Friend ReadOnly Property AuthFile As New SFile($"{SettingsFolderName}\OnlyFans_Auth.json")
|
||||
Private ReadOnly DynamicRulesFile As SFile
|
||||
Private ReadOnly DynamicRulesXml As SFile
|
||||
Private Shared ReadOnly Property DynamicRulesFileImpl As SFile
|
||||
Get
|
||||
Return $"{SettingsFolderName}\OnlyFansDynamicRules.txt"
|
||||
End Get
|
||||
End Property
|
||||
Friend Shared Sub ValidateRulesFile()
|
||||
Dim f As SFile = DynamicRulesFileImpl
|
||||
If Not f.Exists Then TextSaver.SaveTextToFile(My.Resources.OFResources.DynamicRules, DynamicRulesFileImpl, True)
|
||||
End Sub
|
||||
Friend Property ProtectFile As Boolean = False
|
||||
#End Region
|
||||
Friend Property UpdateInterval As Integer = UpdateIntervalDefault
|
||||
Friend Property Mode As Modes = Modes.List
|
||||
Friend Property PersonalRule As String = String.Empty
|
||||
Friend Property RulesForceUpdateRequired As Boolean = False
|
||||
Friend Property RulesUpdateConst As Boolean = True
|
||||
Friend Property RulesReplaceConfig As Boolean = True
|
||||
Private ReadOnly Responser As New Responser With {.Accept = "application/json"}
|
||||
Private ReadOnly RulesLinesComparer As New FComparer(Of String)(Function(x, y) x.StringToLower = y.StringToLower)
|
||||
Private ReadOnly OFLOG As TextSaver
|
||||
Private ReadOnly OFError As ErrorsDescriber
|
||||
Friend Property AddErrorsToLog As Boolean = True
|
||||
Friend Property NeedToSave As Boolean = False
|
||||
Private ReadOnly Property ConfigAddress As DynamicRulesValue
|
||||
Private ReadOnly Property ConfigConstAddress As DynamicRulesValue
|
||||
Private Property ConfigLastDateUpdate As Date = Now.AddYears(-1)
|
||||
Friend Property ConfigAutoUpdate As Boolean = True
|
||||
Friend Property RulesConfigManualMode As Boolean = True
|
||||
#End Region
|
||||
#Region "Current, Item, Count"
|
||||
Private _CurrentRule As DynamicRulesValue
|
||||
Private _CurrentContainer As EContainer
|
||||
Private _CurrentContainerRulesText As String = String.Empty
|
||||
Friend ReadOnly Property CurrentRule As DynamicRulesValue
|
||||
Get
|
||||
Return _CurrentRule
|
||||
End Get
|
||||
End Property
|
||||
Friend ReadOnly Property CurrentContainer As EContainer
|
||||
Get
|
||||
Return _CurrentContainer
|
||||
End Get
|
||||
End Property
|
||||
Friend ReadOnly Property CurrentContainerRulesText As String
|
||||
Get
|
||||
If _CurrentContainerRulesText.IsEmptyString AndAlso AuthFile.Exists Then _
|
||||
_CurrentContainerRulesText = AuthFile.GetText(OFError).StringTrim
|
||||
Return _CurrentContainerRulesText
|
||||
End Get
|
||||
End Property
|
||||
Friend ReadOnly Property Exists As Boolean
|
||||
Get
|
||||
Return CurrentContainer.ListExists
|
||||
End Get
|
||||
End Property
|
||||
Default Friend ReadOnly Property Item(ByVal Index As Integer) As DynamicRulesValue Implements IMyEnumerator(Of DynamicRulesValue).MyEnumeratorObject
|
||||
Get
|
||||
Return Rules(Index)
|
||||
End Get
|
||||
End Property
|
||||
Friend ReadOnly Property Count As Integer Implements IMyEnumerator(Of DynamicRulesValue).MyEnumeratorCount
|
||||
Get
|
||||
Return Rules.Count
|
||||
End Get
|
||||
End Property
|
||||
#End Region
|
||||
#Region "Initializer"
|
||||
Friend Sub New()
|
||||
Rules = New List(Of DynamicRulesValue)
|
||||
DynamicRulesFile = DynamicRulesFileImpl
|
||||
DynamicRulesXml = DynamicRulesFile
|
||||
DynamicRulesXml.Extension = "xml"
|
||||
ReplacePattern_RepoToRaw = New RParams("(.*github.com/([^/]+)/([^/]+)/blob/(.+))", Nothing, 0,
|
||||
RegexReturn.ReplaceChangeListMatch, EDP.ReturnValue) With {
|
||||
.PatternReplacement = "https://raw.githubusercontent.com/{2}/{3}/{4}"}
|
||||
ReplacePattern_JsonInfo = ReplacePattern_RepoToRaw.Copy
|
||||
ReplacePattern_JsonInfo.PatternReplacement = "https://github.com/{2}/{3}/latest-commit/{4}"
|
||||
ReplacePattern_RawToRepo = ReplacePattern_RepoToRaw.Copy
|
||||
ReplacePattern_RawToRepo.Pattern = "(.*raw.githubusercontent.com/([^/]+)/([^/]+)/([^/]+)/(.+))"
|
||||
ReplacePattern_RawToRepo.PatternReplacement = "https://github.com/{2}/{3}/blob/{4}/{5}"
|
||||
ConfigRulesExtract = RParams.DMS("DYNAMIC_RULE"":(\{.+?\}[\r\n]+)", 1, RegexOptions.Singleline, EDP.ReturnValue)
|
||||
OFLOG = New TextSaver($"LOGs\OF_{Now:yyyyMMdd_HHmmss}.txt") With {.LogMode = True, .AutoSave = True, .AutoClear = True}
|
||||
AddHandler OFLOG.TextSaved, AddressOf OFLOG_TextSaved
|
||||
OFError = New ErrorsDescriber(EDP.SendToLog + EDP.ReturnValue) With {.DeclaredMessage = New MMessage With {.Loggers = {OFLOG}, .Exists = True}}
|
||||
Responser.DeclaredError = OFError
|
||||
Responser.ProcessExceptionDecision =
|
||||
Function(ByVal Status As IResponserStatus, ByVal NullArg As Object, ByVal CurrentError As ErrorsDescriber) As ErrorsDescriber
|
||||
If Status.StatusCode = Net.HttpStatusCode.NotFound Then
|
||||
CurrentError.SendToLogOnlyMessage = True
|
||||
Dim m As MMessage = CurrentError.DeclaredMessage.Clone
|
||||
m.Text = $"Nothing found at URL: {Responser.LatestUrlString}"
|
||||
CurrentError.DeclaredMessage = m
|
||||
Status.ErrorException = New ErrorsDescriberException(m.Text,,, Status.ErrorException) With {.ReplaceMainMessage = True}
|
||||
End If
|
||||
Return CurrentError
|
||||
End Function
|
||||
ConfigAddress = ParseURL("https://github.com/AAndyProgram/SCrawler/blob/main/SCrawler/API/OnlyFans/OFScraperConfigPattern.json")
|
||||
ConfigConstAddress = ParseURL("https://github.com/AAndyProgram/SCrawler/blob/main/SCrawler/API/OnlyFans/OFScraperConfigPatternConstants.txt")
|
||||
RulesConstants = New Dictionary(Of String, String)
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Log handlers"
|
||||
Private _OFLOG_ProcessNotify As Boolean = True
|
||||
Private Sub OFLOG_TextSaved(sender As Object, e As EventArgs)
|
||||
If _OFLOG_ProcessNotify And AddErrorsToLog Then _OFLOG_ProcessNotify = False : MyMainLOG = $"The OnlyFans log contains errors: {OFLOG.File}"
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "ParseURL"
|
||||
Private Const SiteGitHub As String = "github.com"
|
||||
Private Const SiteGitHubRaw As String = "raw.githubusercontent.com"
|
||||
Friend Function ParseURL(ByVal URL As String) As DynamicRulesValue
|
||||
URL = URL.StringTrim
|
||||
If Not URL.IsEmptyString Then
|
||||
Dim r As New DynamicRulesValue
|
||||
Dim rGet As Func(Of String, RParams, String) = Function(__url, pattern) DirectCast(RegexReplace(__url, pattern), IEnumerable(Of String)).FirstOrDefault
|
||||
If URL.ToLower.Contains(SiteGitHubRaw) Then
|
||||
r.UrlRaw = URL
|
||||
r.UrlRepo = rGet(URL, ReplacePattern_RawToRepo)
|
||||
ElseIf URL.ToLower.Contains(SiteGitHub) Then
|
||||
r.UrlRepo = URL
|
||||
r.UrlRaw = rGet(URL, ReplacePattern_RepoToRaw)
|
||||
End If
|
||||
|
||||
If r.Valid Then
|
||||
r.UpdatedAt = Now.AddYears(-1)
|
||||
r.UrlLatestCommit = rGet(r.UrlRepo, ReplacePattern_JsonInfo)
|
||||
r.Exists = True
|
||||
Return r
|
||||
End If
|
||||
End If
|
||||
Return Nothing
|
||||
End Function
|
||||
#End Region
|
||||
#Region "GetFormat"
|
||||
Private Shared ReadOnly Property ConfigNodes As String()
|
||||
Get
|
||||
Return {"advanced_options", "DYNAMIC_RULE"}
|
||||
End Get
|
||||
End Property
|
||||
Private Const FormatMidPart As String = ":{0}:{1:x}:"
|
||||
Private ReadOnly FormatExtract As RParams = RParams.DM("(\S+)\s*:\s*\{\s*\d?\s*\}\s*:\s*\{\s*\d?\s*:\s*x\s*\}\s*:\s*(\S+)", 0, RegexReturn.ListByMatch, EDP.ReturnValue)
|
||||
Private ReadOnly ContainerStrConv As New CustomProvider(Function(input) If(ACheck(Of Integer)(input), input, $"""{input}"""))
|
||||
Private ReadOnly ContainerConv As New CustomProvider(Function(ByVal e As Object) As Object
|
||||
With DirectCast(e, EContainer)
|
||||
Dim value$ = String.Empty
|
||||
If .ListExists Then
|
||||
value = .Select(Function(ee) ee(0).Value).ListToStringE(",", ContainerStrConv, False, String.Empty, EDP.ReturnValue)
|
||||
If Not value.IsEmptyString Then value = $"[{value}]"
|
||||
Else
|
||||
value = AConvert(Of String)(.Value, ContainerStrConv, String.Empty, EDP.SendToLog, EDP.ReturnValue)
|
||||
End If
|
||||
If Not value.IsEmptyString Then
|
||||
value = $"""{ .Name}"": {value}"
|
||||
Else
|
||||
value = $"""{ .Name}"": """""
|
||||
End If
|
||||
Return value
|
||||
End With
|
||||
End Function)
|
||||
Friend Shared Function GetFormat(ByVal j As EContainer, Optional ByVal Check As Boolean = False,
|
||||
Optional ByRef CheckResult As Boolean = False,
|
||||
Optional ByVal TryConfig As Boolean = False, Optional ByRef IsConfig As Boolean = False) As String
|
||||
Dim pattern$ = String.Empty
|
||||
With If(TryConfig, j(ConfigNodes), j)
|
||||
If .ListExists Then
|
||||
If Not .Value("format").IsEmptyString Then
|
||||
pattern = .Value("format").Replace("{}", "{0}").Replace("{:x}", "{1:x}")
|
||||
ElseIf Not .Value("prefix").IsEmptyString And Not .Value("suffix").IsEmptyString Then
|
||||
pattern = .Value("prefix") & FormatMidPart & .Value("suffix")
|
||||
ElseIf Not .Value("start").IsEmptyString And Not .Value("end").IsEmptyString Then
|
||||
pattern = .Value("start") & FormatMidPart & .Value("end")
|
||||
End If
|
||||
|
||||
Dim result As Boolean = Not pattern.IsEmptyString And .Item("checksum_indexes").ListExists And
|
||||
Not .Value("static_param").IsEmptyString And Not .Value("checksum_constant").IsEmptyString
|
||||
If Check Then CheckResult = result
|
||||
If Not result And Not TryConfig Then Return GetFormat(j, Check, CheckResult, True, IsConfig)
|
||||
End If
|
||||
End With
|
||||
Return pattern
|
||||
End Function
|
||||
Private Function ConvertAuthText() As String
|
||||
Dim result$ = String.Empty
|
||||
With CurrentContainer
|
||||
If .ListExists Then
|
||||
Dim f$ = GetFormat(.Self)
|
||||
If Not f.IsEmptyString Then
|
||||
Dim l As List(Of String) = RegexReplace(f, FormatExtract)
|
||||
If l.ListExists(3) Then
|
||||
Dim s$ = l(1), e$ = l(2)
|
||||
.Value("format") = s & FormatMidPart & e
|
||||
.Value("prefix") = s
|
||||
.Value("suffix") = e
|
||||
.Value("start") = s
|
||||
.Value("end") = e
|
||||
Dim t$ = .ListToStringE(",", ContainerConv, False)
|
||||
If Not t.IsEmptyString Then t = "{" & t & "}"
|
||||
Return t
|
||||
End If
|
||||
End If
|
||||
End If
|
||||
End With
|
||||
Return String.Empty
|
||||
End Function
|
||||
#End Region
|
||||
#Region "Load, Save"
|
||||
Private Function GetTextLines(ByVal Input As String) As List(Of String)
|
||||
If Not Input.IsEmptyString Then
|
||||
Return ListAddList(Nothing, Input.StringTrim.Split(vbLf), LAP.NotContainsOnly, EDP.ReturnValue,
|
||||
CType(Function(inp$) inp.StringTrim, Func(Of Object, Object)))
|
||||
Else
|
||||
Return New List(Of String)
|
||||
End If
|
||||
End Function
|
||||
Private Sub ParseConsts(ByVal Source As String)
|
||||
If Not Source.IsEmptyString Then
|
||||
Dim l As List(Of String) = GetTextLines(Source)
|
||||
Dim v$()
|
||||
If l.ListExists Then
|
||||
RulesConstants.Clear()
|
||||
For Each value$ In l
|
||||
If Not value.IsEmptyString Then
|
||||
v = value.Split("=")
|
||||
If v.ListExists(2) Then RulesConstants.Add(v(0), v(1))
|
||||
End If
|
||||
Next
|
||||
End If
|
||||
End If
|
||||
End Sub
|
||||
Private Const RulesNode As String = "Rules"
|
||||
Private _InitialValuesLoaded As Boolean = False
|
||||
Private Sub LoadInitialValues()
|
||||
If Not _InitialValuesLoaded Then
|
||||
_InitialValuesLoaded = True
|
||||
|
||||
If Not OFScraperConfigPatternFile.Exists Then
|
||||
Dim t$ = Text.Encoding.UTF8.GetString(My.Resources.OFResources.OFScraperConfigPattern)
|
||||
TextSaver.SaveTextToFile(t, OFScraperConfigPatternFile, True)
|
||||
End If
|
||||
|
||||
If Not OFScraperConfigPatternFileConst.Exists Then _
|
||||
TextSaver.SaveTextToFile(My.Resources.OFResources.OFScraperConfigPatternConstants, OFScraperConfigPatternFileConst, True)
|
||||
|
||||
If OFScraperConfigPatternFileConst.Exists Then ParseConsts(OFScraperConfigPatternFileConst.GetText(OFError))
|
||||
|
||||
If DynamicRulesXml.Exists Then
|
||||
Rules.Clear()
|
||||
Using x As New XmlFile(DynamicRulesXml, Protector.Modes.All, False) With {.XmlReadOnly = True, .AllowSameNames = True}
|
||||
x.LoadData(OFError)
|
||||
Dim dNull As Date = Now.AddYears(-1)
|
||||
LastUpdateTimeFile = x.Value(Name_LastUpdateTimeFile).ToDateDef(dNull)
|
||||
LastUpdateTimeRules = x.Value(Name_LastUpdateTimeRules).ToDateDef(dNull)
|
||||
ProtectFile = x.Value(Name_ProtectFile).FromXML(Of Boolean)(False)
|
||||
Mode = x.Value(Name_Mode).FromXML(Of Integer)(Modes.List)
|
||||
UpdateInterval = x.Value(Name_UpdateInterval).FromXML(Of Integer)(UpdateIntervalDefault)
|
||||
PersonalRule = x.Value(Name_PersonalRule)
|
||||
RulesForceUpdateRequired = x.Value(Name_RulesForceUpdateRequired).FromXML(Of Boolean)(False)
|
||||
RulesUpdateConst = x.Value(Name_RulesUpdateConst).FromXML(Of Boolean)(True)
|
||||
RulesReplaceConfig = x.Value(Name_RulesReplaceConfig).FromXML(Of Boolean)(True)
|
||||
AddErrorsToLog = x.Value(Name_AddErrorsToLog).FromXML(Of Boolean)(False)
|
||||
ConfigAutoUpdate = x.Value(Name_ConfigAutoUpdate).FromXML(Of Boolean)(True)
|
||||
RulesConfigManualMode = x.Value(Name_RulesConfigManualMode).FromXML(Of Boolean)(True)
|
||||
ConfigLastDateUpdate = x.Value(Name_ConfigLastDateUpdate).ToDateDef(Now.AddYears(-1))
|
||||
If x.Contains(RulesNode) Then Rules.ListAddList(x({RulesNode}), LAP.IgnoreICopier, OFError)
|
||||
End Using
|
||||
End If
|
||||
End If
|
||||
End Sub
|
||||
Friend Sub Save()
|
||||
Using x As New XmlFile With {.AllowSameNames = True, .Name = "DynamicRules"}
|
||||
x.Add(Name_LastUpdateTimeFile, LastUpdateTimeFile.ToStringDateDef)
|
||||
x.Add(Name_LastUpdateTimeRules, LastUpdateTimeRules.ToStringDateDef)
|
||||
x.Add(Name_ProtectFile, ProtectFile.BoolToInteger)
|
||||
x.Add(Name_Mode, CInt(Mode))
|
||||
x.Add(Name_UpdateInterval, UpdateInterval)
|
||||
x.Add(Name_PersonalRule, PersonalRule)
|
||||
x.Add(Name_RulesForceUpdateRequired, RulesForceUpdateRequired.BoolToInteger)
|
||||
x.Add(Name_RulesUpdateConst, RulesUpdateConst.BoolToInteger)
|
||||
x.Add(Name_RulesReplaceConfig, RulesReplaceConfig.BoolToInteger)
|
||||
x.Add(Name_AddErrorsToLog, AddErrorsToLog.BoolToInteger)
|
||||
x.Add(Name_ConfigAutoUpdate, ConfigAutoUpdate.BoolToInteger)
|
||||
x.Add(Name_RulesConfigManualMode, RulesConfigManualMode.BoolToInteger)
|
||||
x.Add(Name_ConfigLastDateUpdate, ConfigLastDateUpdate.ToStringDateDef)
|
||||
If Count > 0 Then
|
||||
Rules.Sort()
|
||||
x.Add(New EContainer(RulesNode))
|
||||
x.Last.AddRange(Rules)
|
||||
End If
|
||||
x.Save(DynamicRulesXml, OFError)
|
||||
End Using
|
||||
If Count > 0 Then
|
||||
Using t As New TextSaver(DynamicRulesFile)
|
||||
Rules.ForEach(Sub(r) If Not r.UrlRepo.IsEmptyString Then t.AppendLine(r.UrlRepo))
|
||||
t.Save(OFError)
|
||||
End Using
|
||||
End If
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Update"
|
||||
Private _UpdateInProgress As Boolean = False
|
||||
Private _ForcedUpdate As Boolean = False
|
||||
Friend Function Update(ByVal Force As Boolean, Optional ByVal LoadListOnly As Boolean = False) As Boolean
|
||||
Dim skip As Boolean = _UpdateInProgress
|
||||
If skip And _ForcedUpdate Then Force = False
|
||||
_ForcedUpdate = Force
|
||||
While _UpdateInProgress : Threading.Thread.Sleep(200) : End While
|
||||
If Not skip Or Force Then UpdateImpl(Force Or RulesForceUpdateRequired, LoadListOnly)
|
||||
Return Exists
|
||||
End Function
|
||||
Private Sub UpdateImpl(ByVal Force As Boolean, Optional ByVal LoadListOnly As Boolean = False)
|
||||
Try
|
||||
If Not _UpdateInProgress Then
|
||||
_UpdateInProgress = True
|
||||
|
||||
LoadInitialValues()
|
||||
|
||||
Dim r$
|
||||
Dim process As Boolean = False, updated As Boolean = False
|
||||
Dim forceSave As Boolean = RulesForceUpdateRequired Or Not DynamicRulesFile.Exists Or Not DynamicRulesXml.Exists
|
||||
Dim textLocal As List(Of String)
|
||||
Dim i%
|
||||
Dim rule As DynamicRulesValue
|
||||
Dim e As EContainer
|
||||
Dim errDate As Date = Now.AddYears(-1)
|
||||
Dim d As Date?
|
||||
'2024-06-12T12:44:06.000-05:00
|
||||
Dim dateProvider As New ADateTime("yyyy-MM-ddTHH:mm:ss.fff%K")
|
||||
|
||||
RulesForceUpdateRequired = False
|
||||
|
||||
If Not DynamicRulesFile.Exists Then process = True : ValidateRulesFile()
|
||||
|
||||
'update rules list
|
||||
If Not LoadListOnly And (LastUpdateTimeFile.AddMinutes(UpdateInterval) < Now Or process Or Force) Then
|
||||
LastUpdateTimeFile = Now
|
||||
r = Responser.GetResponse("https://raw.githubusercontent.com/AAndyProgram/SCrawler/main/SCrawler/API/OnlyFans/DynamicRules.txt")
|
||||
If Not r.IsEmptyString Then
|
||||
Dim textWeb As List(Of String) = GetTextLines(r)
|
||||
Dim fileText$
|
||||
If textWeb.ListExists Then
|
||||
Using t As New TextSaver(DynamicRulesFile)
|
||||
If ProtectFile Then
|
||||
fileText = DynamicRulesFile.GetText(OFError)
|
||||
t.Append(fileText)
|
||||
textLocal = GetTextLines(fileText)
|
||||
If textLocal.ListExists Then _
|
||||
textLocal.ForEach(Sub(tt) If Not tt.IsEmptyString AndAlso Not textWeb.Contains(tt, RulesLinesComparer) Then _
|
||||
t.AppendLine(tt) : updated = True) : textLocal.Clear()
|
||||
Else
|
||||
t.Append(r)
|
||||
updated = True
|
||||
End If
|
||||
t.Save(OFError)
|
||||
End Using
|
||||
textWeb.Clear()
|
||||
End If
|
||||
End If
|
||||
End If
|
||||
|
||||
'update config and consts
|
||||
If Not LoadListOnly AndAlso ConfigAutoUpdate AndAlso ConfigLastDateUpdate.AddMinutes(UpdateInterval) < Now Then
|
||||
Dim __upConf As Boolean = False
|
||||
Dim __dConf As Date = ConfigLastDateUpdate
|
||||
Dim parseConfigFiles As Action(Of DynamicRulesValue, SFile, Boolean) =
|
||||
Sub(ByVal __rule As DynamicRulesValue, ByVal __fileSave As SFile, ByVal isConstFile As Boolean)
|
||||
r = Responser.GetResponse(__rule.UrlLatestCommit)
|
||||
If Not r.IsEmptyString Then
|
||||
e = JsonDocument.Parse(r, OFError)
|
||||
If e.ListExists Then
|
||||
d = AConvert(Of Date)(e.Value("date"), dateProvider, Nothing)
|
||||
Dim dConf As Date = If(d, errDate)
|
||||
If dConf > __dConf Then
|
||||
__dConf = dConf
|
||||
__upConf = True
|
||||
updated = True
|
||||
r = Responser.GetResponse(__rule.UrlRaw)
|
||||
If Not r.IsEmptyString Then
|
||||
TextSaver.SaveTextToFile(r, __fileSave, True, False, OFError)
|
||||
If isConstFile Then ParseConsts(r)
|
||||
End If
|
||||
End If
|
||||
e.Dispose()
|
||||
End If
|
||||
End If
|
||||
End Sub
|
||||
'Update consts
|
||||
If RulesUpdateConst Then parseConfigFiles(ConfigConstAddress, OFScraperConfigPatternFileConst, True)
|
||||
'Update config
|
||||
parseConfigFiles(ConfigAddress, OFScraperConfigPatternFile, False)
|
||||
If __upConf Then ConfigLastDateUpdate = Now
|
||||
End If
|
||||
|
||||
'generate rules, update rules dates
|
||||
If LastUpdateTimeRules.AddMinutes(UpdateInterval) < Now Or updated Or Force Or LoadListOnly Then
|
||||
process = True
|
||||
If Mode = Modes.Personal And Not PersonalRule.IsEmptyString Then
|
||||
If Not LoadListOnly Then LastUpdateTimeRules = Now : updated = True
|
||||
Else
|
||||
If Not LoadListOnly Then LastUpdateTimeRules = Now : updated = True
|
||||
textLocal = GetTextLines(DynamicRulesFile.GetText(OFError))
|
||||
If textLocal.ListExists Then
|
||||
If Not LoadListOnly And Count > 0 Then
|
||||
For i = 0 To Count - 1
|
||||
rule = Rules(i)
|
||||
rule.Exists = False
|
||||
Rules(i) = rule
|
||||
Next
|
||||
End If
|
||||
For Each url$ In textLocal
|
||||
url = url.StringTrim
|
||||
If Not url.IsEmptyString Then
|
||||
i = IndexOf(url)
|
||||
If i >= 0 Then
|
||||
rule = Rules(i)
|
||||
Else
|
||||
rule = ParseURL(url)
|
||||
If rule.Valid Then
|
||||
i = Add(rule, False, False)
|
||||
Else
|
||||
rule = Nothing
|
||||
End If
|
||||
End If
|
||||
|
||||
If Not LoadListOnly Then
|
||||
If i >= 0 And rule.Valid And Not rule.UrlLatestCommit.IsEmptyString Then
|
||||
rule.Exists = True
|
||||
r = Responser.GetResponse(rule.UrlLatestCommit)
|
||||
If Not r.IsEmptyString Then
|
||||
e = JsonDocument.Parse(r, OFError)
|
||||
If e.ListExists Then
|
||||
d = AConvert(Of Date)(e.Value("date"), dateProvider, Nothing)
|
||||
rule.UpdatedAt = If(d, errDate)
|
||||
e.Dispose()
|
||||
Else
|
||||
rule.Broken = True
|
||||
End If
|
||||
Else
|
||||
rule.Broken = True
|
||||
End If
|
||||
Rules(i) = rule
|
||||
End If
|
||||
If Rules.RemoveAll(Function(rr) Not rr.Exists) > 0 Then updated = True
|
||||
End If
|
||||
End If
|
||||
Next
|
||||
End If
|
||||
End If
|
||||
End If
|
||||
|
||||
If Count > 0 Then Rules.Sort()
|
||||
|
||||
'download and load the rule
|
||||
If (LoadListOnly And AuthFile.Exists) Or (Not LoadListOnly And ((updated And Count > 0) Or Not AuthFile.Exists)) Then
|
||||
_CurrentRule = Nothing
|
||||
_CurrentContainer.DisposeIfReady
|
||||
_CurrentContainer = Nothing
|
||||
Dim processRule As Func(Of DynamicRulesValue, Boolean, DialogResult) =
|
||||
Function(ByVal __rule As DynamicRulesValue, ByVal reparseAuth As Boolean) As DialogResult
|
||||
Dim fromAuthFile As Boolean = (LoadListOnly Or reparseAuth) AndAlso AuthFile.Exists
|
||||
If fromAuthFile Then
|
||||
r = AuthFile.GetText(OFError)
|
||||
Else
|
||||
r = GetWebString(__rule.UrlRaw,, OFError)
|
||||
End If
|
||||
Dim j As EContainer = JsonDocument.Parse(r, OFError)
|
||||
Dim checkResult As Boolean = False
|
||||
Dim isConfig As Boolean = False
|
||||
Dim textToSave As String = r
|
||||
If j.ListExists AndAlso Not GetFormat(j, True, checkResult,, isConfig).IsEmptyString AndAlso checkResult Then
|
||||
If isConfig Then textToSave = RegexReplace(r, ConfigRulesExtract)
|
||||
If textToSave.IsEmptyString Then
|
||||
Return DialogResult.Retry
|
||||
Else
|
||||
_CurrentRule = __rule
|
||||
_CurrentContainer = If(isConfig, j(ConfigNodes), j)
|
||||
textToSave = ConvertAuthText()
|
||||
_CurrentContainerRulesText = textToSave
|
||||
If (Not fromAuthFile Or Not textToSave.StringTrim = r.StringTrim) And Not textToSave.IsEmptyString Then
|
||||
TextSaver.SaveTextToFile(textToSave, AuthFile, True, False, OFError)
|
||||
If Not reparseAuth Then processRule(__rule, True)
|
||||
End If
|
||||
Return DialogResult.OK
|
||||
End If
|
||||
End If
|
||||
Return DialogResult.No
|
||||
End Function
|
||||
If Mode = Modes.Personal And Not PersonalRule.IsEmptyString Then
|
||||
processRule(New DynamicRulesValue With {.UrlRepo = PersonalRule, .UrlRaw = PersonalRule}, False)
|
||||
Else
|
||||
For Each rule In Rules
|
||||
If rule.Valid And Not rule.Broken Then
|
||||
Select Case processRule(rule, False)
|
||||
Case DialogResult.Retry : Continue For
|
||||
Case DialogResult.OK : Exit For
|
||||
End Select
|
||||
End If
|
||||
Next
|
||||
End If
|
||||
End If
|
||||
|
||||
If updated Or forceSave Then Save()
|
||||
|
||||
_UpdateInProgress = False
|
||||
End If
|
||||
Catch ex As Exception
|
||||
ErrorsDescriber.Execute(OFError, ex, "[OnlyFans.DynamicRulesEnv.UpdateImpl]")
|
||||
_UpdateInProgress = False
|
||||
End Try
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Add, IndexOf"
|
||||
Friend Function Add(ByVal Rule As DynamicRulesValue, Optional ByVal AutoSort As Boolean = True, Optional ByVal AutoSave As Boolean = False) As Integer
|
||||
If Rule.Valid Then
|
||||
Dim i% = IndexOf(Rule)
|
||||
If i = -1 Then
|
||||
Rules.Add(Rule)
|
||||
i = Count - 1
|
||||
If AutoSort Then Rules.Sort() : i = IndexOf(Rule)
|
||||
If AutoSave Then Save()
|
||||
End If
|
||||
Return i
|
||||
Else
|
||||
Return -1
|
||||
End If
|
||||
End Function
|
||||
Friend Function RemoveAt(ByVal Index As Integer) As Boolean
|
||||
If Index.ValueBetween(0, Count - 1) Then
|
||||
Rules.RemoveAt(Index)
|
||||
Return True
|
||||
Else
|
||||
Return False
|
||||
End If
|
||||
End Function
|
||||
Friend Function IndexOf(ByVal URL As String) As Integer
|
||||
If Count > 0 Then
|
||||
URL = URL.StringToLower.Trim
|
||||
Return Rules.FindIndex(Function(r) r.UrlRepo.StringToLower = URL Or r.UrlRaw.StringToLower = URL Or r.UrlLatestCommit.StringToLower = URL)
|
||||
Else
|
||||
Return -1
|
||||
End If
|
||||
End Function
|
||||
#End Region
|
||||
#Region "ICopier Support"
|
||||
Friend Overloads Function Copy() As Object Implements ICopier.Copy
|
||||
Return (New DynamicRulesEnv).Copy(Me)
|
||||
End Function
|
||||
Friend Overloads Function Copy(ByVal Source As Object) As Object Implements ICopier.Copy
|
||||
Return Copy(Source, False)
|
||||
End Function
|
||||
Friend Overloads Function Copy(ByVal Source As Object, ByVal UpdateForceProperty As Boolean) As Object
|
||||
If Not Source Is Nothing Then
|
||||
With DirectCast(Source, DynamicRulesEnv)
|
||||
If Not RulesForceUpdateRequired And UpdateForceProperty Then _
|
||||
RulesForceUpdateRequired = Not Rules.ListEquals(.Rules) Or Not Mode = .Mode Or
|
||||
(.Mode = Modes.Personal And Not PersonalRule = .PersonalRule)
|
||||
ProtectFile = .ProtectFile
|
||||
Mode = .Mode
|
||||
UpdateInterval = .UpdateInterval
|
||||
PersonalRule = .PersonalRule
|
||||
If Not RulesForceUpdateRequired Then RulesForceUpdateRequired = .RulesForceUpdateRequired
|
||||
RulesUpdateConst = .RulesUpdateConst
|
||||
RulesReplaceConfig = .RulesReplaceConfig
|
||||
AddErrorsToLog = .AddErrorsToLog
|
||||
ConfigAutoUpdate = .ConfigAutoUpdate
|
||||
RulesConfigManualMode = .RulesConfigManualMode
|
||||
Rules.Clear()
|
||||
If .Count > 0 Then Rules.AddRange(.Rules)
|
||||
End With
|
||||
Return Me
|
||||
Else
|
||||
Return Nothing
|
||||
End If
|
||||
End Function
|
||||
#End Region
|
||||
#Region "IEnumerable Support"
|
||||
Private Function GetEnumerator() As IEnumerator(Of DynamicRulesValue) Implements IEnumerable(Of DynamicRulesValue).GetEnumerator
|
||||
Return New MyEnumerator(Of DynamicRulesValue)(Me)
|
||||
End Function
|
||||
Private Function IEnumerable_GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator
|
||||
Return GetEnumerator()
|
||||
End Function
|
||||
#End Region
|
||||
#Region "IDisposable Support"
|
||||
Private disposedValue As Boolean = False
|
||||
Protected Overridable Overloads Sub Dispose(ByVal disposing As Boolean)
|
||||
If Not disposedValue Then
|
||||
If disposing Then
|
||||
Rules.Clear()
|
||||
_CurrentContainer.DisposeIfReady
|
||||
Responser.DisposeIfReady
|
||||
End If
|
||||
_CurrentContainer = Nothing
|
||||
disposedValue = True
|
||||
End If
|
||||
End Sub
|
||||
Protected Overrides Sub Finalize()
|
||||
Dispose(False)
|
||||
MyBase.Finalize()
|
||||
End Sub
|
||||
Friend Overloads Sub Dispose() Implements IDisposable.Dispose
|
||||
Dispose(True)
|
||||
GC.SuppressFinalize(Me)
|
||||
End Sub
|
||||
#End Region
|
||||
End Class
|
||||
End Namespace
|
||||
24
SCrawler/API/OnlyFans/OFResources.Designer.vb
generated
@@ -64,6 +64,20 @@ Namespace My.Resources
|
||||
End Set
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' Looks up a localized string similar to https://github.com/datawhores/onlyfans-dynamic-rules/blob/main/dynamicRules.json
|
||||
'''https://github.com/riley-access-labs/onlyfans-dynamic-rules-1/blob/main/dynamicRules.json
|
||||
'''https://github.com/riley-access-labs/onlyfans-dynamic-rules-1/blob/patch-1/dynamicRules.json
|
||||
'''https://github.com/DATAHOARDERS/dynamic-rules/blob/main/onlyfans.json
|
||||
'''https://github.com/DIGITALCRIMINAL/dynamic-rules/blob/main/onlyfans.json
|
||||
'''https://github.com/deviint/onlyfans-dynamic-rules/blob/main/dynamicRules.json.
|
||||
'''</summary>
|
||||
Friend Shared ReadOnly Property DynamicRules() As String
|
||||
Get
|
||||
Return ResourceManager.GetString("DynamicRules", resourceCulture)
|
||||
End Get
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' Looks up a localized resource of type System.Byte[].
|
||||
'''</summary>
|
||||
@@ -73,5 +87,15 @@ Namespace My.Resources
|
||||
Return CType(obj,Byte())
|
||||
End Get
|
||||
End Property
|
||||
|
||||
'''<summary>
|
||||
''' Looks up a localized string similar to dynamic-mode-default=generic
|
||||
'''RULE_VALUE=DYNAMIC_GENERIC_URL.
|
||||
'''</summary>
|
||||
Friend Shared ReadOnly Property OFScraperConfigPatternConstants() As String
|
||||
Get
|
||||
Return ResourceManager.GetString("OFScraperConfigPatternConstants", resourceCulture)
|
||||
End Get
|
||||
End Property
|
||||
End Class
|
||||
End Namespace
|
||||
|
||||
@@ -118,7 +118,13 @@
|
||||
<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="DynamicRules" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>DynamicRules.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
|
||||
</data>
|
||||
<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>
|
||||
<data name="OFScraperConfigPatternConstants" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>OFScraperConfigPatternConstants.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -1,61 +1,63 @@
|
||||
{
|
||||
"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"
|
||||
"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_max": 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": "generic",
|
||||
"backend": "aio",
|
||||
"downloadbars": false,
|
||||
"cache-mode": "sqlite",
|
||||
"appendlog": true,
|
||||
"custom": null,
|
||||
"sanitize_text": false,
|
||||
"avatar": true,
|
||||
"custom_values": {
|
||||
"DYNAMIC_GENERIC_URL": "https://raw.githubusercontent.com/datawhores/onlyfans-dynamic-rules/main/dynamicRules.json",
|
||||
"CDRM": "https://old.cdrm-project.com/wv"
|
||||
}
|
||||
},
|
||||
"responsetype": {
|
||||
"timeline": "Posts",
|
||||
"message": "Messages",
|
||||
"archived": "Archived",
|
||||
"paid": "Messages",
|
||||
"stories": "Stories",
|
||||
"highlights": "Stories",
|
||||
"profile": "Profile",
|
||||
"pinned": "Posts"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
dynamic-mode-default=generic
|
||||
RULE_VALUE=DYNAMIC_GENERIC_URL
|
||||
361
SCrawler/API/OnlyFans/OnlyFansAdvancedSettingsForm.Designer.vb
generated
Normal file
@@ -0,0 +1,361 @@
|
||||
' Copyright (C) Andy https://github.com/AAndyProgram
|
||||
' This program is free software: you can redistribute it and/or modify
|
||||
' it under the terms of the GNU General Public License as published by
|
||||
' the Free Software Foundation, either version 3 of the License, or
|
||||
' (at your option) any later version.
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Namespace API.OnlyFans
|
||||
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()>
|
||||
Partial Friend Class OnlyFansAdvancedSettingsForm : Inherits System.Windows.Forms.Form
|
||||
<System.Diagnostics.DebuggerNonUserCode()>
|
||||
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
|
||||
Try
|
||||
If disposing AndAlso components IsNot Nothing Then
|
||||
components.Dispose()
|
||||
End If
|
||||
Finally
|
||||
MyBase.Dispose(disposing)
|
||||
End Try
|
||||
End Sub
|
||||
Private components As System.ComponentModel.IContainer
|
||||
<System.Diagnostics.DebuggerStepThrough()>
|
||||
Private Sub InitializeComponent()
|
||||
Me.components = New System.ComponentModel.Container()
|
||||
Dim CONTAINER_MAIN As System.Windows.Forms.ToolStripContainer
|
||||
Dim TP_MAIN As System.Windows.Forms.TableLayoutPanel
|
||||
Dim ActionButton7 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||
Dim resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(OnlyFansAdvancedSettingsForm))
|
||||
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 TP_RULES_LIST As System.Windows.Forms.TableLayoutPanel
|
||||
Dim TP_RULES_LIST_LEFT As System.Windows.Forms.TableLayoutPanel
|
||||
Dim TT_MAIN As System.Windows.Forms.ToolTip
|
||||
Me.TXT_UP_INTERVAL = New PersonalUtilities.Forms.Controls.TextBoxExtended()
|
||||
Me.TXT_PERSONAL_RULE = New PersonalUtilities.Forms.Controls.TextBoxExtended()
|
||||
Me.CONTAINER_LIST = New System.Windows.Forms.ToolStripContainer()
|
||||
Me.LIST_RULES = New System.Windows.Forms.ListBox()
|
||||
Me.OPT_RULES_LIST = New System.Windows.Forms.RadioButton()
|
||||
Me.CH_PROTECTED = New System.Windows.Forms.CheckBox()
|
||||
Me.CH_FORCE_UPDATE = New System.Windows.Forms.CheckBox()
|
||||
Me.CH_LOG_ERR = New System.Windows.Forms.CheckBox()
|
||||
Me.CH_RULES_REPLACE_CONFIG = New System.Windows.Forms.CheckBox()
|
||||
Me.CH_UPDATE_CONF = New System.Windows.Forms.CheckBox()
|
||||
Me.CH_UPDATE_RULES_CONST = New System.Windows.Forms.CheckBox()
|
||||
Me.CH_CONFIG_MANUAL_MODE = New System.Windows.Forms.CheckBox()
|
||||
CONTAINER_MAIN = New System.Windows.Forms.ToolStripContainer()
|
||||
TP_MAIN = New System.Windows.Forms.TableLayoutPanel()
|
||||
TP_RULES_LIST = New System.Windows.Forms.TableLayoutPanel()
|
||||
TP_RULES_LIST_LEFT = New System.Windows.Forms.TableLayoutPanel()
|
||||
TT_MAIN = New System.Windows.Forms.ToolTip(Me.components)
|
||||
CONTAINER_MAIN.ContentPanel.SuspendLayout()
|
||||
CONTAINER_MAIN.SuspendLayout()
|
||||
TP_MAIN.SuspendLayout()
|
||||
CType(Me.TXT_UP_INTERVAL, System.ComponentModel.ISupportInitialize).BeginInit()
|
||||
CType(Me.TXT_PERSONAL_RULE, System.ComponentModel.ISupportInitialize).BeginInit()
|
||||
TP_RULES_LIST.SuspendLayout()
|
||||
Me.CONTAINER_LIST.ContentPanel.SuspendLayout()
|
||||
Me.CONTAINER_LIST.SuspendLayout()
|
||||
TP_RULES_LIST_LEFT.SuspendLayout()
|
||||
Me.SuspendLayout()
|
||||
'
|
||||
'CONTAINER_MAIN
|
||||
'
|
||||
'
|
||||
'CONTAINER_MAIN.ContentPanel
|
||||
'
|
||||
CONTAINER_MAIN.ContentPanel.Controls.Add(TP_MAIN)
|
||||
CONTAINER_MAIN.ContentPanel.Size = New System.Drawing.Size(464, 341)
|
||||
CONTAINER_MAIN.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
CONTAINER_MAIN.LeftToolStripPanelVisible = False
|
||||
CONTAINER_MAIN.Location = New System.Drawing.Point(0, 0)
|
||||
CONTAINER_MAIN.Name = "CONTAINER_MAIN"
|
||||
CONTAINER_MAIN.RightToolStripPanelVisible = False
|
||||
CONTAINER_MAIN.Size = New System.Drawing.Size(464, 341)
|
||||
CONTAINER_MAIN.TabIndex = 1
|
||||
CONTAINER_MAIN.TopToolStripPanelVisible = False
|
||||
'
|
||||
'TP_MAIN
|
||||
'
|
||||
TP_MAIN.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.[Single]
|
||||
TP_MAIN.ColumnCount = 1
|
||||
TP_MAIN.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
||||
TP_MAIN.Controls.Add(Me.TXT_UP_INTERVAL, 0, 5)
|
||||
TP_MAIN.Controls.Add(Me.TXT_PERSONAL_RULE, 0, 6)
|
||||
TP_MAIN.Controls.Add(TP_RULES_LIST, 0, 7)
|
||||
TP_MAIN.Controls.Add(Me.CH_LOG_ERR, 0, 0)
|
||||
TP_MAIN.Controls.Add(Me.CH_RULES_REPLACE_CONFIG, 0, 1)
|
||||
TP_MAIN.Controls.Add(Me.CH_UPDATE_CONF, 0, 4)
|
||||
TP_MAIN.Controls.Add(Me.CH_UPDATE_RULES_CONST, 0, 2)
|
||||
TP_MAIN.Controls.Add(Me.CH_CONFIG_MANUAL_MODE, 0, 3)
|
||||
TP_MAIN.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
TP_MAIN.Location = New System.Drawing.Point(0, 0)
|
||||
TP_MAIN.Name = "TP_MAIN"
|
||||
TP_MAIN.RowCount = 8
|
||||
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
|
||||
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
|
||||
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
|
||||
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
|
||||
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
|
||||
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
|
||||
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
|
||||
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
||||
TP_MAIN.Size = New System.Drawing.Size(464, 341)
|
||||
TP_MAIN.TabIndex = 0
|
||||
'
|
||||
'TXT_UP_INTERVAL
|
||||
'
|
||||
ActionButton7.BackgroundImage = CType(resources.GetObject("ActionButton7.BackgroundImage"), System.Drawing.Image)
|
||||
ActionButton7.Name = "Refresh"
|
||||
ActionButton7.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Refresh
|
||||
ActionButton8.BackgroundImage = CType(resources.GetObject("ActionButton8.BackgroundImage"), System.Drawing.Image)
|
||||
ActionButton8.Name = "Clear"
|
||||
ActionButton8.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
|
||||
Me.TXT_UP_INTERVAL.Buttons.Add(ActionButton7)
|
||||
Me.TXT_UP_INTERVAL.Buttons.Add(ActionButton8)
|
||||
Me.TXT_UP_INTERVAL.CaptionText = "Dynamic rules update"
|
||||
Me.TXT_UP_INTERVAL.CaptionToolTipEnabled = True
|
||||
Me.TXT_UP_INTERVAL.CaptionToolTipText = "'Dynamic rules' update interval (minutes). Default: 1440"
|
||||
Me.TXT_UP_INTERVAL.CaptionWidth = 120.0R
|
||||
Me.TXT_UP_INTERVAL.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
Me.TXT_UP_INTERVAL.Location = New System.Drawing.Point(4, 134)
|
||||
Me.TXT_UP_INTERVAL.Name = "TXT_UP_INTERVAL"
|
||||
Me.TXT_UP_INTERVAL.Size = New System.Drawing.Size(456, 22)
|
||||
Me.TXT_UP_INTERVAL.TabIndex = 5
|
||||
'
|
||||
'TXT_PERSONAL_RULE
|
||||
'
|
||||
Me.TXT_PERSONAL_RULE.AutoShowClearButton = True
|
||||
ActionButton9.BackgroundImage = CType(resources.GetObject("ActionButton9.BackgroundImage"), System.Drawing.Image)
|
||||
ActionButton9.Enabled = False
|
||||
ActionButton9.Name = "Clear"
|
||||
ActionButton9.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
|
||||
ActionButton9.Visible = False
|
||||
Me.TXT_PERSONAL_RULE.Buttons.Add(ActionButton9)
|
||||
Me.TXT_PERSONAL_RULE.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.RadioButton
|
||||
Me.TXT_PERSONAL_RULE.CaptionText = "Dynamic rules URL"
|
||||
Me.TXT_PERSONAL_RULE.CaptionToolTipEnabled = True
|
||||
Me.TXT_PERSONAL_RULE.CaptionToolTipText = "Overwrite 'Dynamic rules' with this URL." & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "Change this value only if you know what" &
|
||||
" you are doing."
|
||||
Me.TXT_PERSONAL_RULE.CaptionWidth = 120.0R
|
||||
Me.TXT_PERSONAL_RULE.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
Me.TXT_PERSONAL_RULE.LeaveDefaultButtons = True
|
||||
Me.TXT_PERSONAL_RULE.Location = New System.Drawing.Point(4, 163)
|
||||
Me.TXT_PERSONAL_RULE.Name = "TXT_PERSONAL_RULE"
|
||||
Me.TXT_PERSONAL_RULE.Size = New System.Drawing.Size(456, 22)
|
||||
Me.TXT_PERSONAL_RULE.TabIndex = 6
|
||||
'
|
||||
'TP_RULES_LIST
|
||||
'
|
||||
TP_RULES_LIST.ColumnCount = 2
|
||||
TP_RULES_LIST.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 120.0!))
|
||||
TP_RULES_LIST.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
||||
TP_RULES_LIST.Controls.Add(Me.CONTAINER_LIST, 1, 0)
|
||||
TP_RULES_LIST.Controls.Add(TP_RULES_LIST_LEFT, 0, 0)
|
||||
TP_RULES_LIST.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
TP_RULES_LIST.Location = New System.Drawing.Point(4, 192)
|
||||
TP_RULES_LIST.Name = "TP_RULES_LIST"
|
||||
TP_RULES_LIST.RowCount = 1
|
||||
TP_RULES_LIST.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
||||
TP_RULES_LIST.Size = New System.Drawing.Size(456, 145)
|
||||
TP_RULES_LIST.TabIndex = 7
|
||||
'
|
||||
'CONTAINER_LIST
|
||||
'
|
||||
Me.CONTAINER_LIST.BottomToolStripPanelVisible = False
|
||||
'
|
||||
'CONTAINER_LIST.ContentPanel
|
||||
'
|
||||
Me.CONTAINER_LIST.ContentPanel.Controls.Add(Me.LIST_RULES)
|
||||
Me.CONTAINER_LIST.ContentPanel.Size = New System.Drawing.Size(330, 114)
|
||||
Me.CONTAINER_LIST.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
Me.CONTAINER_LIST.LeftToolStripPanelVisible = False
|
||||
Me.CONTAINER_LIST.Location = New System.Drawing.Point(123, 3)
|
||||
Me.CONTAINER_LIST.Name = "CONTAINER_LIST"
|
||||
Me.CONTAINER_LIST.RightToolStripPanelVisible = False
|
||||
Me.CONTAINER_LIST.Size = New System.Drawing.Size(330, 139)
|
||||
Me.CONTAINER_LIST.TabIndex = 1
|
||||
'
|
||||
'LIST_RULES
|
||||
'
|
||||
Me.LIST_RULES.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
Me.LIST_RULES.FormattingEnabled = True
|
||||
Me.LIST_RULES.Location = New System.Drawing.Point(0, 0)
|
||||
Me.LIST_RULES.Name = "LIST_RULES"
|
||||
Me.LIST_RULES.Size = New System.Drawing.Size(330, 114)
|
||||
Me.LIST_RULES.TabIndex = 0
|
||||
'
|
||||
'TP_RULES_LIST_LEFT
|
||||
'
|
||||
TP_RULES_LIST_LEFT.ColumnCount = 1
|
||||
TP_RULES_LIST_LEFT.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
||||
TP_RULES_LIST_LEFT.Controls.Add(Me.OPT_RULES_LIST, 0, 0)
|
||||
TP_RULES_LIST_LEFT.Controls.Add(Me.CH_PROTECTED, 0, 1)
|
||||
TP_RULES_LIST_LEFT.Controls.Add(Me.CH_FORCE_UPDATE, 0, 2)
|
||||
TP_RULES_LIST_LEFT.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
TP_RULES_LIST_LEFT.Location = New System.Drawing.Point(0, 0)
|
||||
TP_RULES_LIST_LEFT.Margin = New System.Windows.Forms.Padding(0)
|
||||
TP_RULES_LIST_LEFT.Name = "TP_RULES_LIST_LEFT"
|
||||
TP_RULES_LIST_LEFT.RowCount = 4
|
||||
TP_RULES_LIST_LEFT.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
|
||||
TP_RULES_LIST_LEFT.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
|
||||
TP_RULES_LIST_LEFT.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
|
||||
TP_RULES_LIST_LEFT.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
||||
TP_RULES_LIST_LEFT.Size = New System.Drawing.Size(120, 145)
|
||||
TP_RULES_LIST_LEFT.TabIndex = 0
|
||||
'
|
||||
'OPT_RULES_LIST
|
||||
'
|
||||
Me.OPT_RULES_LIST.AutoSize = True
|
||||
Me.OPT_RULES_LIST.CheckAlign = System.Drawing.ContentAlignment.MiddleRight
|
||||
Me.OPT_RULES_LIST.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
Me.OPT_RULES_LIST.Location = New System.Drawing.Point(3, 3)
|
||||
Me.OPT_RULES_LIST.Margin = New System.Windows.Forms.Padding(3, 3, 0, 3)
|
||||
Me.OPT_RULES_LIST.Name = "OPT_RULES_LIST"
|
||||
Me.OPT_RULES_LIST.Size = New System.Drawing.Size(117, 19)
|
||||
Me.OPT_RULES_LIST.TabIndex = 0
|
||||
Me.OPT_RULES_LIST.TabStop = True
|
||||
Me.OPT_RULES_LIST.Text = "Dynamic rules list"
|
||||
Me.OPT_RULES_LIST.TextAlign = System.Drawing.ContentAlignment.MiddleRight
|
||||
TT_MAIN.SetToolTip(Me.OPT_RULES_LIST, "List of dynamic rules sources." & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "If selected, the most recently updated source wil" &
|
||||
"l be selected.")
|
||||
Me.OPT_RULES_LIST.UseVisualStyleBackColor = True
|
||||
'
|
||||
'CH_PROTECTED
|
||||
'
|
||||
Me.CH_PROTECTED.AutoSize = True
|
||||
Me.CH_PROTECTED.CheckAlign = System.Drawing.ContentAlignment.MiddleRight
|
||||
Me.CH_PROTECTED.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
Me.CH_PROTECTED.Location = New System.Drawing.Point(3, 28)
|
||||
Me.CH_PROTECTED.Margin = New System.Windows.Forms.Padding(3, 3, 0, 3)
|
||||
Me.CH_PROTECTED.Name = "CH_PROTECTED"
|
||||
Me.CH_PROTECTED.Size = New System.Drawing.Size(117, 19)
|
||||
Me.CH_PROTECTED.TabIndex = 1
|
||||
Me.CH_PROTECTED.Text = "Protected list"
|
||||
Me.CH_PROTECTED.TextAlign = System.Drawing.ContentAlignment.MiddleRight
|
||||
TT_MAIN.SetToolTip(Me.CH_PROTECTED, "If checked, the new source will be added, but the rules list will not be overwrit" &
|
||||
"ten by the updated one.")
|
||||
Me.CH_PROTECTED.UseVisualStyleBackColor = True
|
||||
'
|
||||
'CH_FORCE_UPDATE
|
||||
'
|
||||
Me.CH_FORCE_UPDATE.AutoSize = True
|
||||
Me.CH_FORCE_UPDATE.CheckAlign = System.Drawing.ContentAlignment.MiddleRight
|
||||
Me.CH_FORCE_UPDATE.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
Me.CH_FORCE_UPDATE.Location = New System.Drawing.Point(3, 53)
|
||||
Me.CH_FORCE_UPDATE.Margin = New System.Windows.Forms.Padding(3, 3, 0, 3)
|
||||
Me.CH_FORCE_UPDATE.Name = "CH_FORCE_UPDATE"
|
||||
Me.CH_FORCE_UPDATE.Size = New System.Drawing.Size(117, 19)
|
||||
Me.CH_FORCE_UPDATE.TabIndex = 2
|
||||
Me.CH_FORCE_UPDATE.Text = "Force update"
|
||||
Me.CH_FORCE_UPDATE.TextAlign = System.Drawing.ContentAlignment.MiddleRight
|
||||
TT_MAIN.SetToolTip(Me.CH_FORCE_UPDATE, "Check this if you want to force the rules to update.")
|
||||
Me.CH_FORCE_UPDATE.UseVisualStyleBackColor = True
|
||||
'
|
||||
'CH_LOG_ERR
|
||||
'
|
||||
Me.CH_LOG_ERR.AutoSize = True
|
||||
Me.CH_LOG_ERR.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
Me.CH_LOG_ERR.Location = New System.Drawing.Point(4, 4)
|
||||
Me.CH_LOG_ERR.Name = "CH_LOG_ERR"
|
||||
Me.CH_LOG_ERR.Size = New System.Drawing.Size(456, 19)
|
||||
Me.CH_LOG_ERR.TabIndex = 0
|
||||
Me.CH_LOG_ERR.Text = "Add dynamic rules errors to the log"
|
||||
TT_MAIN.SetToolTip(Me.CH_LOG_ERR, "OnlyFans errors will be added to a separate log." & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "A checked checkbox means that e" &
|
||||
"rror notification will be added to the main log.")
|
||||
Me.CH_LOG_ERR.UseVisualStyleBackColor = True
|
||||
'
|
||||
'CH_RULES_REPLACE_CONFIG
|
||||
'
|
||||
Me.CH_RULES_REPLACE_CONFIG.AutoSize = True
|
||||
Me.CH_RULES_REPLACE_CONFIG.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
Me.CH_RULES_REPLACE_CONFIG.Location = New System.Drawing.Point(4, 30)
|
||||
Me.CH_RULES_REPLACE_CONFIG.Name = "CH_RULES_REPLACE_CONFIG"
|
||||
Me.CH_RULES_REPLACE_CONFIG.Size = New System.Drawing.Size(456, 19)
|
||||
Me.CH_RULES_REPLACE_CONFIG.TabIndex = 1
|
||||
Me.CH_RULES_REPLACE_CONFIG.Text = "Replace rules in OF-Scraper configuration file"
|
||||
TT_MAIN.SetToolTip(Me.CH_RULES_REPLACE_CONFIG, "If checked, the dynamic rules (in the config) will be replaced with actual values" &
|
||||
".")
|
||||
Me.CH_RULES_REPLACE_CONFIG.UseVisualStyleBackColor = True
|
||||
'
|
||||
'CH_UPDATE_CONF
|
||||
'
|
||||
Me.CH_UPDATE_CONF.AutoSize = True
|
||||
Me.CH_UPDATE_CONF.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
Me.CH_UPDATE_CONF.Location = New System.Drawing.Point(4, 108)
|
||||
Me.CH_UPDATE_CONF.Name = "CH_UPDATE_CONF"
|
||||
Me.CH_UPDATE_CONF.Size = New System.Drawing.Size(456, 19)
|
||||
Me.CH_UPDATE_CONF.TabIndex = 4
|
||||
Me.CH_UPDATE_CONF.Text = "Update configuration file during update"
|
||||
TT_MAIN.SetToolTip(Me.CH_UPDATE_CONF, "Update the configuration pattern from the site during update.")
|
||||
Me.CH_UPDATE_CONF.UseVisualStyleBackColor = True
|
||||
'
|
||||
'CH_UPDATE_RULES_CONST
|
||||
'
|
||||
Me.CH_UPDATE_RULES_CONST.AutoSize = True
|
||||
Me.CH_UPDATE_RULES_CONST.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
Me.CH_UPDATE_RULES_CONST.Location = New System.Drawing.Point(4, 56)
|
||||
Me.CH_UPDATE_RULES_CONST.Name = "CH_UPDATE_RULES_CONST"
|
||||
Me.CH_UPDATE_RULES_CONST.Size = New System.Drawing.Size(456, 19)
|
||||
Me.CH_UPDATE_RULES_CONST.TabIndex = 2
|
||||
Me.CH_UPDATE_RULES_CONST.Text = "Update rules constants file during update"
|
||||
TT_MAIN.SetToolTip(Me.CH_UPDATE_RULES_CONST, "Update rules constants from the site during update")
|
||||
Me.CH_UPDATE_RULES_CONST.UseVisualStyleBackColor = True
|
||||
'
|
||||
'CH_CONFIG_MANUAL_MODE
|
||||
'
|
||||
Me.CH_CONFIG_MANUAL_MODE.AutoSize = True
|
||||
Me.CH_CONFIG_MANUAL_MODE.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
Me.CH_CONFIG_MANUAL_MODE.Location = New System.Drawing.Point(4, 82)
|
||||
Me.CH_CONFIG_MANUAL_MODE.Name = "CH_CONFIG_MANUAL_MODE"
|
||||
Me.CH_CONFIG_MANUAL_MODE.Size = New System.Drawing.Size(456, 19)
|
||||
Me.CH_CONFIG_MANUAL_MODE.TabIndex = 3
|
||||
Me.CH_CONFIG_MANUAL_MODE.Text = "Dynamic rules 'Manual' mode"
|
||||
TT_MAIN.SetToolTip(Me.CH_CONFIG_MANUAL_MODE, "The rules will be added to the config as is, without using a link.")
|
||||
Me.CH_CONFIG_MANUAL_MODE.UseVisualStyleBackColor = True
|
||||
'
|
||||
'OnlyFansAdvancedSettingsForm
|
||||
'
|
||||
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
|
||||
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
|
||||
Me.ClientSize = New System.Drawing.Size(464, 341)
|
||||
Me.Controls.Add(CONTAINER_MAIN)
|
||||
Me.Icon = Global.SCrawler.My.Resources.SiteResources.OnlyFansIcon_32
|
||||
Me.KeyPreview = True
|
||||
Me.MinimizeBox = False
|
||||
Me.MinimumSize = New System.Drawing.Size(480, 380)
|
||||
Me.Name = "OnlyFansAdvancedSettingsForm"
|
||||
Me.ShowInTaskbar = False
|
||||
Me.Text = "Settings"
|
||||
CONTAINER_MAIN.ContentPanel.ResumeLayout(False)
|
||||
CONTAINER_MAIN.ResumeLayout(False)
|
||||
CONTAINER_MAIN.PerformLayout()
|
||||
TP_MAIN.ResumeLayout(False)
|
||||
TP_MAIN.PerformLayout()
|
||||
CType(Me.TXT_UP_INTERVAL, System.ComponentModel.ISupportInitialize).EndInit()
|
||||
CType(Me.TXT_PERSONAL_RULE, System.ComponentModel.ISupportInitialize).EndInit()
|
||||
TP_RULES_LIST.ResumeLayout(False)
|
||||
Me.CONTAINER_LIST.ContentPanel.ResumeLayout(False)
|
||||
Me.CONTAINER_LIST.ResumeLayout(False)
|
||||
Me.CONTAINER_LIST.PerformLayout()
|
||||
TP_RULES_LIST_LEFT.ResumeLayout(False)
|
||||
TP_RULES_LIST_LEFT.PerformLayout()
|
||||
Me.ResumeLayout(False)
|
||||
|
||||
End Sub
|
||||
|
||||
Private WithEvents TXT_UP_INTERVAL As PersonalUtilities.Forms.Controls.TextBoxExtended
|
||||
Private WithEvents TXT_PERSONAL_RULE As PersonalUtilities.Forms.Controls.TextBoxExtended
|
||||
Private WithEvents CONTAINER_LIST As ToolStripContainer
|
||||
Private WithEvents LIST_RULES As ListBox
|
||||
Private WithEvents OPT_RULES_LIST As RadioButton
|
||||
Private WithEvents CH_PROTECTED As CheckBox
|
||||
Private WithEvents CH_FORCE_UPDATE As CheckBox
|
||||
Private WithEvents CH_LOG_ERR As CheckBox
|
||||
Private WithEvents CH_RULES_REPLACE_CONFIG As CheckBox
|
||||
Private WithEvents CH_UPDATE_CONF As CheckBox
|
||||
Private WithEvents CH_UPDATE_RULES_CONST As CheckBox
|
||||
Private WithEvents CH_CONFIG_MANUAL_MODE As CheckBox
|
||||
End Class
|
||||
End Namespace
|
||||
186
SCrawler/API/OnlyFans/OnlyFansAdvancedSettingsForm.resx
Normal file
@@ -0,0 +1,186 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<metadata name="CONTAINER_MAIN.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>False</value>
|
||||
</metadata>
|
||||
<metadata name="TP_MAIN.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>False</value>
|
||||
</metadata>
|
||||
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<data name="ActionButton7.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="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>
|
||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
||||
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
|
||||
tbbB43rK5xSAQq1VYFtmeQBoqZTSreVZvgTknM8yyyjA/qodsDF9gspD2Bj6B+DH+NqzhQQAG+POMnSX
|
||||
AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC
|
||||
</value>
|
||||
</data>
|
||||
<metadata name="TP_RULES_LIST.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>False</value>
|
||||
</metadata>
|
||||
<metadata name="TP_RULES_LIST_LEFT.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>False</value>
|
||||
</metadata>
|
||||
<metadata name="TT_MAIN.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>False</value>
|
||||
</metadata>
|
||||
<metadata name="TT_MAIN.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 17</value>
|
||||
</metadata>
|
||||
<metadata name="TT_MAIN.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>False</value>
|
||||
</metadata>
|
||||
<metadata name="TT_MAIN.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 17</value>
|
||||
</metadata>
|
||||
<metadata name="TP_RULES_LIST_LEFT.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>False</value>
|
||||
</metadata>
|
||||
<metadata name="TT_MAIN.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>False</value>
|
||||
</metadata>
|
||||
<metadata name="TT_MAIN.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 17</value>
|
||||
</metadata>
|
||||
</root>
|
||||
165
SCrawler/API/OnlyFans/OnlyFansAdvancedSettingsForm.vb
Normal file
@@ -0,0 +1,165 @@
|
||||
' Copyright (C) Andy https://github.com/AAndyProgram
|
||||
' This program is free software: you can redistribute it and/or modify
|
||||
' it under the terms of the GNU General Public License as published by
|
||||
' the Free Software Foundation, either version 3 of the License, or
|
||||
' (at your option) any later version.
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports PersonalUtilities.Forms
|
||||
Imports PersonalUtilities.Forms.Controls.Base
|
||||
Imports PersonalUtilities.Forms.Toolbars
|
||||
Namespace API.OnlyFans
|
||||
Friend Class OnlyFansAdvancedSettingsForm
|
||||
#Region "Declarations"
|
||||
Private WithEvents MyDefs As DefaultFormOptions
|
||||
Friend Property CurrentRulesEnv As DynamicRulesEnv
|
||||
Private ReadOnly Property CurrentRulesEnv_LIST As DynamicRulesEnv
|
||||
#End Region
|
||||
#Region "Initializer"
|
||||
Friend Sub New(ByVal rules As DynamicRulesEnv)
|
||||
InitializeComponent()
|
||||
MyDefs = New DefaultFormOptions(Me, Settings.Design)
|
||||
CurrentRulesEnv = rules
|
||||
CurrentRulesEnv_LIST = New DynamicRulesEnv
|
||||
CurrentRulesEnv_LIST.Copy(rules, False)
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Form handlers"
|
||||
Private Sub OnlyFansAdvancedSettingsForm_Load(sender As Object, e As EventArgs) Handles Me.Load
|
||||
With MyDefs
|
||||
.MyViewInitialize()
|
||||
.AddOkCancelToolbar()
|
||||
|
||||
.MyEditToolbar = New EditToolbar(Me,, CONTAINER_LIST.TopToolStripPanel) With {.Buttons = Nothing}
|
||||
.MyEditToolbar.AddThisToolbar()
|
||||
|
||||
With CurrentRulesEnv
|
||||
Select Case .Mode
|
||||
Case DynamicRulesEnv.Modes.List : OPT_RULES_LIST.Checked = True
|
||||
Case DynamicRulesEnv.Modes.Personal : TXT_PERSONAL_RULE.Checked = True
|
||||
End Select
|
||||
CH_LOG_ERR.Checked = .AddErrorsToLog
|
||||
CH_RULES_REPLACE_CONFIG.Checked = .RulesReplaceConfig
|
||||
CH_UPDATE_RULES_CONST.Checked = .RulesUpdateConst
|
||||
CH_CONFIG_MANUAL_MODE.Checked = .RulesConfigManualMode
|
||||
CH_UPDATE_CONF.Checked = .ConfigAutoUpdate
|
||||
TXT_UP_INTERVAL.Text = .UpdateInterval
|
||||
If Not .PersonalRule.IsEmptyString Then TXT_PERSONAL_RULE.Text = .PersonalRule
|
||||
Refill()
|
||||
CH_PROTECTED.Checked = .ProtectFile
|
||||
CH_FORCE_UPDATE.Checked = .RulesForceUpdateRequired
|
||||
End With
|
||||
|
||||
.MyFieldsCheckerE = New FieldsChecker
|
||||
.MyFieldsCheckerE.AddControl(Of Integer)(TXT_UP_INTERVAL, TXT_UP_INTERVAL.CaptionText,,
|
||||
New FieldsCheckerProviderSimple(Function(v) IIf(AConvert(Of Integer)(v, 0, EDP.ReturnValue) > 0, v, Nothing),
|
||||
"The value of [{0}] field must be greater than 0"))
|
||||
.MyFieldsCheckerE.EndLoaderOperations()
|
||||
|
||||
.EndLoaderOperations()
|
||||
End With
|
||||
End Sub
|
||||
Private Sub OnlyFansAdvancedSettingsForm_Disposed(sender As Object, e As EventArgs) Handles Me.Disposed
|
||||
CurrentRulesEnv_LIST.Dispose()
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Refill"
|
||||
Private Sub Refill()
|
||||
With CurrentRulesEnv_LIST
|
||||
Dim ls% = _LatestSelected
|
||||
LIST_RULES.Items.Clear()
|
||||
If .Count > 0 Then LIST_RULES.Items.AddRange(.Select(Function(r) r.UrlRepo).Cast(Of Object).ToArray)
|
||||
Dim lim% = LIST_RULES.Items.Count - 1
|
||||
If (ls - 1).ValueBetween(0, lim) Then
|
||||
ls -= 1
|
||||
ElseIf (ls + 1).ValueBetween(0, lim) Then
|
||||
ls += 1
|
||||
End If
|
||||
If ls.ValueBetween(0, lim) Then LIST_RULES.SelectedIndex = ls Else ls = -1
|
||||
_LatestSelected = ls
|
||||
End With
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "OK, Cancel"
|
||||
Private Sub MyDefs_ButtonOkClick(ByVal Sender As Object, ByVal e As KeyHandleEventArgs) Handles MyDefs.ButtonOkClick
|
||||
If MyDefs.MyFieldsChecker.AllParamsOK Then
|
||||
With CurrentRulesEnv
|
||||
.Copy(CurrentRulesEnv_LIST, True)
|
||||
.ProtectFile = CH_PROTECTED.Checked
|
||||
.UpdateInterval = AConvert(Of Integer)(TXT_UP_INTERVAL.Text, DynamicRulesEnv.UpdateIntervalDefault)
|
||||
.Mode = If(TXT_PERSONAL_RULE.Checked, DynamicRulesEnv.Modes.Personal, DynamicRulesEnv.Modes.List)
|
||||
.PersonalRule = TXT_PERSONAL_RULE.Text
|
||||
.RulesReplaceConfig = CH_RULES_REPLACE_CONFIG.Checked
|
||||
.RulesUpdateConst = CH_UPDATE_RULES_CONST.Checked
|
||||
.RulesConfigManualMode = CH_CONFIG_MANUAL_MODE.Checked
|
||||
.ConfigAutoUpdate = CH_UPDATE_CONF.Checked
|
||||
.AddErrorsToLog = CH_LOG_ERR.Checked
|
||||
If CH_FORCE_UPDATE.Checked Then .RulesForceUpdateRequired = True
|
||||
.NeedToSave = True
|
||||
End With
|
||||
MyDefs.CloseForm()
|
||||
End If
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Add, Delete"
|
||||
Private Sub MyDefs_ButtonAddClick(ByVal Sender As Object, ByVal e As EditToolbarEventArgs) Handles MyDefs.ButtonAddClick
|
||||
Const msgTitle$ = "Add a rule"
|
||||
Dim i%
|
||||
Dim rule As DynamicRulesValue
|
||||
Dim r$ = InputBoxE("Enter a valid rules URL:", msgTitle)
|
||||
If Not r.IsEmptyString Then
|
||||
rule = Rules.ParseURL(r)
|
||||
If rule.Valid Then
|
||||
i = CurrentRulesEnv_LIST.IndexOf(r)
|
||||
If i >= 0 Then
|
||||
MsgBoxE({$"The rule you entered already exists:{vbCr}{rule.UrlRepo}", msgTitle}, vbCritical)
|
||||
Else
|
||||
CurrentRulesEnv_LIST.Add(rule, False, False)
|
||||
Refill()
|
||||
End If
|
||||
Else
|
||||
MsgBoxE({$"The rule you entered has an incompatible format:{vbCr}{r}", msgTitle}, vbCritical)
|
||||
End If
|
||||
End If
|
||||
End Sub
|
||||
Private Sub MyDefs_ButtonDeleteClickE(ByVal Sender As Object, ByVal e As EditToolbarEventArgs) Handles MyDefs.ButtonDeleteClickE
|
||||
If _LatestSelected.ValueBetween(0, LIST_RULES.Items.Count - 1) Then
|
||||
Dim r$ = LIST_RULES.Items(_LatestSelected)
|
||||
If MsgBoxE({$"Are you sure you want to delete the following rule?{vbCr}{r}", "Delete a rule"}, vbExclamation,,, {"Process", "Cancel"}) = 0 Then
|
||||
If CurrentRulesEnv_LIST.RemoveAt(_LatestSelected) Then
|
||||
LIST_RULES.Items.RemoveAt(_LatestSelected)
|
||||
Refill()
|
||||
Else
|
||||
MsgBoxE({$"The following rule cannot be deleted:{vbCr}{r}", "Delete a rule"}, vbCritical)
|
||||
End If
|
||||
End If
|
||||
End If
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Options"
|
||||
Private Sub TXT_UP_INTERVAL_ActionOnButtonClick(ByVal Sender As Object, ByVal e As ActionButtonEventArgs) Handles TXT_UP_INTERVAL.ActionOnButtonClick
|
||||
If e.DefaultButton = ActionButton.DefaultButtons.Refresh Then TXT_UP_INTERVAL.Text = DynamicRulesEnv.UpdateIntervalDefault
|
||||
End Sub
|
||||
Private Sub TXT_PERSONAL_RULE_ActionOnCheckedChange(ByVal Sender As Object, ByVal e As EventArgs, ByVal Checked As Boolean) Handles TXT_PERSONAL_RULE.ActionOnCheckedChange
|
||||
Mode_CheckedChanged()
|
||||
End Sub
|
||||
Private Sub OPT_RULES_LIST_CheckedChanged(sender As Object, e As EventArgs)
|
||||
Mode_CheckedChanged()
|
||||
End Sub
|
||||
Private Sub Mode_CheckedChanged()
|
||||
Dim e As Boolean = TXT_PERSONAL_RULE.Checked
|
||||
TXT_PERSONAL_RULE.Enabled(False) = e
|
||||
CONTAINER_LIST.Enabled = Not e
|
||||
CH_PROTECTED.Enabled = Not e
|
||||
CH_FORCE_UPDATE.Enabled = Not e
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "List handlers"
|
||||
Private _LatestSelected As Integer = -1
|
||||
Private Sub LIST_RULES_SelectedIndexChanged(sender As Object, e As EventArgs) Handles LIST_RULES.SelectedIndexChanged
|
||||
_LatestSelected = LIST_RULES.SelectedIndex
|
||||
End Sub
|
||||
#End Region
|
||||
End Class
|
||||
End Namespace
|
||||
@@ -9,34 +9,42 @@
|
||||
Imports SCrawler.API.Base
|
||||
Imports SCrawler.Plugin
|
||||
Imports SCrawler.Plugin.Attributes
|
||||
Imports PersonalUtilities.Forms
|
||||
Imports PersonalUtilities.Tools.Web.Clients
|
||||
Imports PersonalUtilities.Tools.Web.Cookies
|
||||
Imports PersonalUtilities.Functions.RegularExpressions
|
||||
Imports DN = SCrawler.API.Base.DeclaredNames
|
||||
Namespace API.OnlyFans
|
||||
<Manifest("AndyProgram_OnlyFans"), SavedPosts, SpecialForm(False), SeparatedTasks(1)>
|
||||
<Manifest("AndyProgram_OnlyFans"), SavedPosts, SpecialForm(False), SpecialForm(True), SeparatedTasks(1)>
|
||||
Friend Class SiteSettings : Inherits SiteSettingsBase
|
||||
#Region "Declarations"
|
||||
#Region "Categories"
|
||||
Private Const CAT_OFS As String = "OF-Scraper support"
|
||||
#End Region
|
||||
#Region "Options"
|
||||
<PropertyOption(ControlText:="Download highlights", ControlToolTip:="Download profile highlights if they exists"), PXML, PClonable>
|
||||
Friend Property DownloadHighlights As PropertyValue
|
||||
<PropertyOption(ControlText:="Download chat", ControlToolTip:="Download unlocked chat media"), PXML, PClonable>
|
||||
Friend Property DownloadChatMedia As PropertyValue
|
||||
<PropertyOption(ControlText:="Download timeline", ControlToolTip:="Download user timeline", Category:=DN.CAT_UserDefs), PXML, PClonable>
|
||||
Friend ReadOnly Property DownloadTimeline As PropertyValue
|
||||
<PropertyOption(ControlText:="Download stories", ControlToolTip:="Download profile stories if they exists", Category:=DN.CAT_UserDefs), PXML, PClonable>
|
||||
Friend ReadOnly Property DownloadStories As PropertyValue
|
||||
<PropertyOption(ControlText:="Download highlights", ControlToolTip:="Download profile highlights if they exists", Category:=DN.CAT_UserDefs), PXML, PClonable>
|
||||
Friend ReadOnly Property DownloadHighlights As PropertyValue
|
||||
<PropertyOption(ControlText:="Download chat", ControlToolTip:="Download unlocked chat media", Category:=DN.CAT_UserDefs), PXML, PClonable>
|
||||
Friend ReadOnly Property DownloadChatMedia As PropertyValue
|
||||
#End Region
|
||||
#Region "Headers"
|
||||
Private Const HeaderBrowser As String = "sec-ch-ua"
|
||||
Private Const HeaderUserID As String = "User-Id"
|
||||
Friend Const HeaderXBC As String = "X-Bc"
|
||||
Friend Const HeaderAppToken As String = "App-Token"
|
||||
<PropertyOption(ControlText:=HeaderUserID, AllowNull:=False), PClonable(Clone:=False)>
|
||||
<PropertyOption(ControlText:=HeaderUserID, AllowNull:=False, IsAuth:=True), PClonable(Clone:=False)>
|
||||
Friend ReadOnly Property HH_USER_ID As PropertyValue
|
||||
<PropertyOption(ControlText:=HeaderXBC, AllowNull:=False), PClonable(Clone:=False)>
|
||||
<PropertyOption(ControlText:=HeaderXBC, AllowNull:=False, IsAuth:=True), PClonable(Clone:=False)>
|
||||
Private ReadOnly Property HH_X_BC As PropertyValue
|
||||
<PropertyOption(ControlText:=HeaderAppToken, AllowNull:=False), PClonable(Clone:=False)>
|
||||
<PropertyOption(ControlText:=HeaderAppToken, AllowNull:=False, IsAuth:=True), PClonable(Clone:=False)>
|
||||
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, IsAuth:=True), PClonable, PXML(OnlyForChecked:=True)>
|
||||
Private ReadOnly Property HH_BROWSER As PropertyValue
|
||||
<PropertyOption(AllowNull:=False), PClonable>
|
||||
<PropertyOption(AllowNull:=False, InheritanceName:=SettingsCLS.HEADER_DEF_UserAgent, IsAuth:=True), PClonable, PXML(OnlyForChecked:=True)>
|
||||
Friend ReadOnly Property UserAgent As PropertyValue
|
||||
Private Sub UpdateHeader(ByVal PropertyName As String, ByVal Value As String)
|
||||
Dim hName$ = String.Empty
|
||||
@@ -54,33 +62,20 @@ Namespace API.OnlyFans
|
||||
Responser.UserAgent = Value
|
||||
End If
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Rules"
|
||||
<PXML("LastDateUpdated")> Private ReadOnly Property LastDateUpdated_XML As PropertyValue
|
||||
Friend Property LastDateUpdated As Date
|
||||
Get
|
||||
Return LastDateUpdated_XML.Value
|
||||
End Get
|
||||
Set(ByVal d As Date)
|
||||
LastDateUpdated_XML.Value = d
|
||||
End Set
|
||||
End Property
|
||||
<PropertyOption(ControlText:="Use old authorization rules",
|
||||
ControlToolTip:="Use old dynamic rules (from 'DATAHOARDERS') or new ones (from 'DIGITALCRIMINALS')." & vbCr &
|
||||
"Change this value only if you know what you are doing."), PXML, PClonable>
|
||||
Friend ReadOnly Property UseOldAuthRules As PropertyValue
|
||||
<PropertyOption(ControlText:="Dynamic rules update", ControlToolTip:="'Dynamic rules' update interval (minutes). Default: 1440", LeftOffset:=110), PXML, PClonable>
|
||||
Friend ReadOnly Property DynamicRulesUpdateInterval As PropertyValue
|
||||
<Provider(NameOf(DynamicRulesUpdateInterval), FieldsChecker:=True)>
|
||||
Private ReadOnly Property DynamicRulesUpdateIntervalProvider As IFormatProvider
|
||||
<PropertyOption(ControlText:="Dynamic rules",
|
||||
ControlToolTip:="Overwrite 'Dynamic rules' with this URL" & vbCr &
|
||||
"Change this value only if you know what you are doing."), PXML, PClonable>
|
||||
Friend ReadOnly Property DynamicRules As PropertyValue
|
||||
<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
|
||||
#Region "OFScraper"
|
||||
<PClonable, PXML("OFScraperPath")> Private ReadOnly Property OFScraperPath_XML As PropertyValue
|
||||
<PropertyOption(ControlText:="OF-Scraper path", ControlToolTip:="The path to the 'ofscraper.exe'")>
|
||||
<PropertyOption(ControlText:="OF-Scraper path", ControlToolTip:="The path to the 'ofscraper.exe'", Category:=CAT_OFS)>
|
||||
Friend ReadOnly Property OFScraperPath As PropertyValue
|
||||
Get
|
||||
If Not DefaultInstance Is Nothing Then
|
||||
@@ -91,7 +86,7 @@ Namespace API.OnlyFans
|
||||
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'")>
|
||||
<PropertyOption(ControlText:="mp4decrypt path", ControlToolTip:="The path to the 'mp4decrypt.exe'", Category:=CAT_OFS)>
|
||||
Friend ReadOnly Property OFScraperMP4decrypt As PropertyValue
|
||||
Get
|
||||
If Not DefaultInstance Is Nothing Then
|
||||
@@ -103,7 +98,7 @@ Namespace API.OnlyFans
|
||||
End Property
|
||||
Friend Const KeyModeDefault_Default As String = "cdrm"
|
||||
<PClonable, PXML("KeyModeDefault")> Private ReadOnly Property KeyModeDefault_XML As PropertyValue
|
||||
<PropertyOption(ControlText:="key-mode-default")>
|
||||
<PropertyOption(ControlText:="key-mode-default", Category:=CAT_OFS)>
|
||||
Friend ReadOnly Property KeyModeDefault As PropertyValue
|
||||
Get
|
||||
If Not DefaultInstance Is Nothing Then
|
||||
@@ -113,12 +108,30 @@ Namespace API.OnlyFans
|
||||
End If
|
||||
End Get
|
||||
End Property
|
||||
<PClonable, PXML("keydb_api")> Private ReadOnly Property Keydb_Api_XML As PropertyValue
|
||||
<PropertyOption(ControlText:="keydb_api", Category:=CAT_OFS)>
|
||||
Friend ReadOnly Property Keydb_Api As PropertyValue
|
||||
Get
|
||||
If Not DefaultInstance Is Nothing Then
|
||||
Return DirectCast(DefaultInstance, SiteSettings).Keydb_Api_XML
|
||||
Else
|
||||
Return Keydb_Api_XML
|
||||
End If
|
||||
End Get
|
||||
End Property
|
||||
#End Region
|
||||
#End Region
|
||||
#Region "Initializer"
|
||||
Friend Sub New(ByVal AccName As String, ByVal Temp As Boolean)
|
||||
MyBase.New("OnlyFans", ".onlyfans.com", AccName, Temp, My.Resources.SiteResources.OnlyFansIcon_32, My.Resources.SiteResources.OnlyFansPic_32)
|
||||
|
||||
If Rules Is Nothing Then
|
||||
Rules = New DynamicRulesEnv
|
||||
Rules.Update(False, True)
|
||||
End If
|
||||
|
||||
_AllowUserAgentUpdate = False
|
||||
|
||||
With Responser
|
||||
.Accept = "application/json, text/plain, */*"
|
||||
.AutomaticDecompression = Net.DecompressionMethods.GZip
|
||||
@@ -144,16 +157,11 @@ Namespace API.OnlyFans
|
||||
UserAgent = New PropertyValue(IIf(.UserAgentExists, .UserAgent, String.Empty), GetType(String), Sub(v) UpdateHeader(NameOf(UserAgent), v))
|
||||
End With
|
||||
|
||||
DownloadTimeline = New PropertyValue(True)
|
||||
DownloadStories = New PropertyValue(True)
|
||||
DownloadHighlights = New PropertyValue(True)
|
||||
DownloadChatMedia = New PropertyValue(True)
|
||||
|
||||
LastDateUpdated_XML = New PropertyValue(Now.AddYears(-1), GetType(Date))
|
||||
'URGENT: OF [UseOldAuthRules = True]
|
||||
UseOldAuthRules = New PropertyValue(True)
|
||||
DynamicRulesUpdateInterval = New PropertyValue(60 * 24)
|
||||
DynamicRulesUpdateIntervalProvider = New FieldsCheckerProviderSimple(Function(v) IIf(AConvert(Of Integer)(v, 0) > 0, v, Nothing),
|
||||
"The value of [{0}] field must be greater than 0")
|
||||
DynamicRules = New PropertyValue(String.Empty, GetType(String))
|
||||
OFScraperPath_XML = New PropertyValue(String.Empty, GetType(String))
|
||||
If ACheck(OFScraperPath_XML.Value) Then
|
||||
Dim f As SFile = OFScraperPath_XML.Value
|
||||
@@ -168,6 +176,7 @@ Namespace API.OnlyFans
|
||||
End If
|
||||
OFScraperMP4decrypt_XML = New PropertyValue(String.Empty, GetType(String))
|
||||
KeyModeDefault_XML = New PropertyValue(KeyModeDefault_Default)
|
||||
Keydb_Api_XML = New PropertyValue(String.Empty, GetType(String))
|
||||
|
||||
UserRegex = RParams.DMS(String.Format(UserRegexDefaultPattern, "onlyfans.com/"), 1, EDP.ReturnValue)
|
||||
UrlPatternUser = "https://onlyfans.com/{0}"
|
||||
@@ -180,10 +189,28 @@ Namespace API.OnlyFans
|
||||
End Function
|
||||
#End Region
|
||||
#Region "Update"
|
||||
Friend Overrides Sub BeginUpdate()
|
||||
_TempSettingsEnv.DisposeIfReady
|
||||
_TempSettingsEnv = Nothing
|
||||
MyBase.BeginUpdate()
|
||||
End Sub
|
||||
Friend Overrides Sub Update()
|
||||
If _SiteEditorFormOpened Then Responser.Cookies.Changed = False
|
||||
If _SiteEditorFormOpened Then
|
||||
Responser.Cookies.Changed = False
|
||||
If If(_TempSettingsEnv?.NeedToSave, False) Then Rules.Copy(_TempSettingsEnv, True) : Rules.Save()
|
||||
End If
|
||||
MyBase.Update()
|
||||
End Sub
|
||||
Friend Overrides Sub EndUpdate()
|
||||
_TempSettingsEnv.DisposeIfReady
|
||||
_TempSettingsEnv = Nothing
|
||||
MyBase.EndUpdate()
|
||||
End Sub
|
||||
Private _TempSettingsEnv As DynamicRulesEnv = Nothing
|
||||
Friend Overrides Sub OpenSettingsForm()
|
||||
If _TempSettingsEnv Is Nothing Then _TempSettingsEnv = New DynamicRulesEnv : _TempSettingsEnv.Copy(Rules, False)
|
||||
Using f As New OnlyFansAdvancedSettingsForm(_TempSettingsEnv) : f.ShowDialog() : End Using
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Download"
|
||||
Friend Overrides Function BaseAuthExists() As Boolean
|
||||
|
||||
@@ -22,6 +22,8 @@ Imports UStates = SCrawler.API.Base.UserMedia.States
|
||||
Namespace API.OnlyFans
|
||||
Friend Class UserData : Inherits UserDataBase
|
||||
#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_MediaDownloadChatMedia As String = "DownloadChatMedia"
|
||||
#End Region
|
||||
@@ -30,6 +32,8 @@ Namespace API.OnlyFans
|
||||
Private Const HeaderSign As String = "Sign"
|
||||
Private Const HeaderTime As String = "Time"
|
||||
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 MediaDownloadChatMedia As Boolean = True
|
||||
Private ReadOnly Property MySettings As SiteSettings
|
||||
@@ -42,9 +46,13 @@ Namespace API.OnlyFans
|
||||
Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean)
|
||||
With Container
|
||||
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)
|
||||
MediaDownloadChatMedia = .Value(Name_MediaDownloadChatMedia).FromXML(Of Boolean)(True)
|
||||
Else
|
||||
.Add(Name_MediaDownloadTimeline, MediaDownloadTimeline.BoolToInteger)
|
||||
.Add(Name_MediaDownloadStories, MediaDownloadStories.BoolToInteger)
|
||||
.Add(Name_MediaDownloadHighlights, MediaDownloadHighlights.BoolToInteger)
|
||||
.Add(Name_MediaDownloadChatMedia, MediaDownloadChatMedia.BoolToInteger)
|
||||
End If
|
||||
@@ -58,6 +66,8 @@ Namespace API.OnlyFans
|
||||
Friend Overrides Sub ExchangeOptionsSet(ByVal Obj As Object)
|
||||
If Not Obj Is Nothing AndAlso TypeOf Obj Is UserExchangeOptions Then
|
||||
With DirectCast(Obj, UserExchangeOptions)
|
||||
MediaDownloadTimeline = .DownloadTimeline
|
||||
MediaDownloadStories = .DownloadStories
|
||||
MediaDownloadHighlights = .DownloadHighlights
|
||||
MediaDownloadChatMedia = .DownloadChatMedia
|
||||
End With
|
||||
@@ -81,6 +91,7 @@ Namespace API.OnlyFans
|
||||
End Sub
|
||||
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
|
||||
Try
|
||||
_DownloadingException_AuthFileUpdate = False
|
||||
If Not MySettings.SessionAborted Then
|
||||
ValidateOFScraper()
|
||||
_AbsMediaIndex = 0
|
||||
@@ -90,8 +101,15 @@ Namespace API.OnlyFans
|
||||
Responser.Cookies.Clear()
|
||||
AddHandler Responser.ResponseReceived, AddressOf Responser_ResponseReceived
|
||||
UpdateCookieHeader()
|
||||
DownloadTimeline(IIf(IsSavedPosts, 0, String.Empty), Token)
|
||||
|
||||
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
|
||||
@@ -114,6 +132,7 @@ Namespace API.OnlyFans
|
||||
Return ErrValue <> 2
|
||||
End Function
|
||||
Friend Const A_HIGHLIGHT As String = "HL"
|
||||
Friend Const A_STORIES As String = "ST"
|
||||
Friend Const A_MESSAGE As String = "MSG"
|
||||
Private Const BaseUrlPattern As String = "https://onlyfans.com{0}"
|
||||
#Region "Download timeline"
|
||||
@@ -133,9 +152,6 @@ Namespace API.OnlyFans
|
||||
If IsSavedPosts Then
|
||||
path = $"/api2/v2/posts/bookmarks/all/?format=infinite&limit=10&offset={Cursor}"
|
||||
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"
|
||||
If Not Cursor.IsEmptyString Then path &= $"&counters=0&beforePublishTime={Cursor}" Else path &= "&counters=1"
|
||||
End If
|
||||
@@ -250,7 +266,7 @@ Namespace API.OnlyFans
|
||||
If Not r.IsEmptyString Then
|
||||
Using j As EContainer = JsonDocument.Parse(r)
|
||||
If j.ListExists Then
|
||||
specFolder = j.Value("title").StringRemoveWinForbiddenSymbols.IfNullOrEmpty(HLID)
|
||||
specFolder = "Highlights\" & j.Value("title").StringRemoveWinForbiddenSymbols.IfNullOrEmpty(HLID)
|
||||
specFolder &= "*"
|
||||
With j("stories")
|
||||
If .ListExists Then
|
||||
@@ -280,6 +296,57 @@ Namespace API.OnlyFans
|
||||
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
|
||||
Loop While Not _complete
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Download chat media"
|
||||
Private Sub DownloadChatMedia(ByVal Cursor As Integer, ByVal Token As CancellationToken)
|
||||
Dim url$ = String.Empty
|
||||
@@ -329,7 +396,8 @@ Namespace API.OnlyFans
|
||||
#End Region
|
||||
Private Function TryCreateMedia(ByVal n As EContainer, ByVal PostID As String, Optional ByVal PostDate As String = Nothing,
|
||||
Optional ByRef Result As Boolean = False, Optional ByVal IsHL As Boolean = False,
|
||||
Optional ByVal SpecFolder As String = Nothing, Optional ByVal PostUserID As String = Nothing) As List(Of UserMedia)
|
||||
Optional ByVal SpecFolder As String = Nothing, Optional ByVal PostUserID As String = Nothing,
|
||||
Optional ByVal TryUseOFS As Boolean = True) As List(Of UserMedia)
|
||||
Dim postUrl$, postUrlBase$, ext$
|
||||
Dim t As UTypes
|
||||
Dim mList As New List(Of UserMedia)
|
||||
@@ -348,7 +416,7 @@ Namespace API.OnlyFans
|
||||
Case "video"
|
||||
t = UTypes.Video
|
||||
ext = "mp4"
|
||||
If postUrl.IsEmptyString And Not IsHL Then
|
||||
If postUrl.IsEmptyString And Not IsHL And TryUseOFS Then
|
||||
t = UTypes.VideoPre
|
||||
_AbsMediaIndex += 1
|
||||
If Not PostUserID.IsEmptyString And IsSingleObjectDownload Then _
|
||||
@@ -470,36 +538,16 @@ Namespace API.OnlyFans
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Auth"
|
||||
Private ReadOnly Property AuthFile As SFile
|
||||
Get
|
||||
Dim f As SFile = MySettings.Responser.File
|
||||
f.Name &= "_Auth"
|
||||
f.Extension = "json"
|
||||
Return f
|
||||
End Get
|
||||
End Property
|
||||
Private Function UpdateSignature(ByVal Path As String, Optional ByVal ForceUpdateAuth As Boolean = False,
|
||||
Optional ByVal Round As Integer = 0) As Boolean
|
||||
Private Function UpdateSignature(ByVal Path As String) As Boolean
|
||||
Try
|
||||
If UpdateAuthFile(ForceUpdateAuth) Then
|
||||
Const nullMsg$ = "The auth parameter is null"
|
||||
Dim j As EContainer
|
||||
Try
|
||||
j = JsonDocument.Parse(AuthFile.GetText)
|
||||
Catch jex As Exception
|
||||
If Round = 0 Then
|
||||
AuthFile.Delete()
|
||||
UpdateAuthFile(True)
|
||||
Return UpdateSignature(Path, ForceUpdateAuth, Round + 1)
|
||||
Else
|
||||
MySettings.SessionAborted = True
|
||||
Return False
|
||||
End If
|
||||
End Try
|
||||
If Not Rules.Update(False) Then Rules.Update(True)
|
||||
If Rules.Exists Then
|
||||
Const nullMsg$ = "The auth parameter(s) is null"
|
||||
Dim j As EContainer = Rules.CurrentContainer
|
||||
If Not j Is Nothing Then
|
||||
Dim pattern$ = j.Value("format")
|
||||
Dim pattern$ = DynamicRulesEnv.GetFormat(j)
|
||||
|
||||
If pattern.IsEmptyString Then Throw New ArgumentNullException("format", nullMsg)
|
||||
pattern = pattern.Replace("{}", "{0}").Replace("{:x}", "{1:x}")
|
||||
|
||||
Dim li%() = j("checksum_indexes").Select(Function(e) CInt(e(0).Value)).ToArray
|
||||
|
||||
@@ -523,38 +571,16 @@ Namespace API.OnlyFans
|
||||
Responser.Headers.Add(HeaderSign, sign)
|
||||
Responser.Headers.Add(HeaderTime, t)
|
||||
|
||||
j.Dispose()
|
||||
Return True
|
||||
End If
|
||||
Else
|
||||
MySettings.SessionAborted = True
|
||||
End If
|
||||
Return False
|
||||
Catch ex As Exception
|
||||
Return ErrorsDescriber.Execute(EDP.SendToLog + EDP.ReturnValue, ex, $"{ToStringForLog()}: UpdateSignature", False)
|
||||
End Try
|
||||
End Function
|
||||
Private Function UpdateAuthFile(ByVal Force As Boolean) As Boolean
|
||||
Const urlOld$ = "https://raw.githubusercontent.com/DATAHOARDERS/dynamic-rules/main/onlyfans.json"
|
||||
Const urlNew$ = "https://raw.githubusercontent.com/DIGITALCRIMINALS/dynamic-rules/main/onlyfans.json"
|
||||
Try
|
||||
If MySettings.LastDateUpdated.AddMinutes(CInt(MySettings.DynamicRulesUpdateInterval.Value)) < Now Or Not AuthFile.Exists Or Force Then
|
||||
Dim r$ = GetWebString(If(ACheck(Of String)(MySettings.DynamicRules.Value),
|
||||
CStr(MySettings.DynamicRules.Value),
|
||||
IIf(MySettings.UseOldAuthRules.Value, urlOld, urlNew)),, EDP.ReturnValue)
|
||||
If Not r.IsEmptyString Then
|
||||
Using j As EContainer = JsonDocument.Parse(r, EDP.ReturnValue)
|
||||
If j.ListExists Then
|
||||
If Not j.Value("format").IsEmptyString And j("checksum_indexes").ListExists And
|
||||
Not j.Value("static_param").IsEmptyString And Not j.Value("checksum_constant").IsEmptyString Then _
|
||||
TextSaver.SaveTextToFile(r, AuthFile, True, False, EDP.ThrowException) : MySettings.LastDateUpdated = Now
|
||||
End If
|
||||
End Using
|
||||
End If
|
||||
End If
|
||||
Return AuthFile.Exists
|
||||
Catch ex As Exception
|
||||
Return ErrorsDescriber.Execute(EDP.SendToLog + EDP.ReturnValue, ex, $"{ToStringForLog()}: UpdateAuthFile", False)
|
||||
End Try
|
||||
End Function
|
||||
Private Function GetHashSha1(ByVal Input As String) As String
|
||||
Dim s As New Security.Cryptography.SHA1CryptoServiceProvider
|
||||
Dim inputBytes() As Byte = System.Text.Encoding.UTF8.GetBytes(Input)
|
||||
@@ -586,37 +612,51 @@ Namespace API.OnlyFans
|
||||
Private Function OFS_CreateConfig() As SFile
|
||||
Try
|
||||
Const confMainPattern$ = "{0}"": ""([^""]*)"""
|
||||
Const confMainPatternRulesManual$ = "DYNAMIC_RULE"": (""[^""]*"")"
|
||||
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 f As SFile = Rules.OFScraperConfigPatternFile
|
||||
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
|
||||
Dim updateConf As Action(Of String, String, Boolean) =
|
||||
Sub(ByVal patternValue As String, ByVal __replaceValue As String, ByVal __isRules As Boolean)
|
||||
rp.Pattern = String.Format(IIf(__isRules, confMainPatternRulesManual, confMainPattern), patternValue)
|
||||
rp.Nothing = configText
|
||||
replaceValue = __replaceValue
|
||||
configText = RegexReplace(configText, rp)
|
||||
End Sub
|
||||
If Not configText.IsEmptyString Then
|
||||
updateConf("save_location", cacheRoot.PathNoSeparator.Replace("\", "/"))
|
||||
updateConf("save_location", cacheRoot.PathNoSeparator.Replace("\", "/"), False)
|
||||
If ACheck(MySettings.OFScraperMP4decrypt.Value) Then
|
||||
ff = CStr(MySettings.OFScraperMP4decrypt.Value)
|
||||
If ff.Exists Then updateConf("mp4decrypt", ff.ToString.Replace("\", "/"))
|
||||
If ff.Exists Then updateConf("mp4decrypt", ff.ToString.Replace("\", "/"), False)
|
||||
End If
|
||||
If Settings.FfmpegFile.Exists Then updateConf("ffmpeg", Settings.FfmpegFile.File.ToString.Replace("\", "/"), False)
|
||||
updateConf("key-mode-default", CStr(MySettings.KeyModeDefault.Value).IfNullOrEmpty(SiteSettings.KeyModeDefault_Default), False)
|
||||
updateConf("keydb_api", CStr(MySettings.Keydb_Api.Value), False)
|
||||
If Rules.RulesReplaceConfig Then
|
||||
If Rules.RulesConfigManualMode Then
|
||||
updateConf(DynamicRulesEnv.DynamicRulesConfig_Mode_NodeName, "manual", False)
|
||||
configText = configText.Replace(DynamicRulesEnv.DynamicRulesConfigNodeName_URL, DynamicRulesEnv.DynamicRulesConfigNodeName_RULES)
|
||||
updateConf(DynamicRulesEnv.DynamicRulesConfigNodeName_RULES, Rules.CurrentContainerRulesText, True)
|
||||
Else
|
||||
Dim confUrlNode$ = If(Rules.RulesConstants.ContainsKey(DynamicRulesEnv.DynamicRulesConfigNodeName_URL_CONST_NAME),
|
||||
Rules.RulesConstants(DynamicRulesEnv.DynamicRulesConfigNodeName_URL_CONST_NAME),
|
||||
DynamicRulesEnv.DynamicRulesConfigNodeName_URL)
|
||||
updateConf(DynamicRulesEnv.DynamicRulesConfigNodeName_URL, Rules.CurrentRule.UrlRaw, False)
|
||||
configText = configText.Replace(DynamicRulesEnv.DynamicRulesConfigNodeName_URL, confUrlNode)
|
||||
If Rules.RulesConstants.ContainsKey(DynamicRulesEnv.DynamicRulesConfig_Mode_NodeName) Then _
|
||||
updateConf(DynamicRulesEnv.DynamicRulesConfig_Mode_NodeName, Rules.RulesConstants(DynamicRulesEnv.DynamicRulesConfig_Mode_NodeName), False)
|
||||
End If
|
||||
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"
|
||||
@@ -710,7 +750,7 @@ Namespace API.OnlyFans
|
||||
Protected Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False,
|
||||
Optional ByVal EObj As Object = Nothing) As Integer
|
||||
If Responser.StatusCode = Net.HttpStatusCode.BadRequest Then '400
|
||||
If Not _DownloadingException_AuthFileUpdate AndAlso UpdateAuthFile(True) Then
|
||||
If Not _DownloadingException_AuthFileUpdate AndAlso Rules.Update(True) Then
|
||||
_DownloadingException_AuthFileUpdate = True
|
||||
Return 2
|
||||
Else
|
||||
|
||||
@@ -9,17 +9,25 @@
|
||||
Imports SCrawler.Plugin.Attributes
|
||||
Namespace API.OnlyFans
|
||||
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))>
|
||||
Friend Property DownloadHighlights As Boolean
|
||||
<PSetting(NameOf(SiteSettings.DownloadChatMedia), NameOf(MySettings))>
|
||||
Friend Property DownloadChatMedia As Boolean
|
||||
Private ReadOnly MySettings As SiteSettings
|
||||
Friend Sub New(ByVal u As UserData)
|
||||
DownloadTimeline = u.MediaDownloadTimeline
|
||||
DownloadStories = u.MediaDownloadStories
|
||||
DownloadHighlights = u.MediaDownloadHighlights
|
||||
DownloadChatMedia = u.MediaDownloadChatMedia
|
||||
MySettings = u.HOST.Source
|
||||
End Sub
|
||||
Friend Sub New(ByVal s As SiteSettings)
|
||||
DownloadTimeline = s.DownloadTimeline.Value
|
||||
DownloadStories = s.DownloadStories.Value
|
||||
DownloadHighlights = s.DownloadHighlights.Value
|
||||
DownloadChatMedia = s.DownloadChatMedia.Value
|
||||
MySettings = s
|
||||
|
||||
@@ -287,7 +287,7 @@ Namespace API.Pinterest
|
||||
End Function
|
||||
End Class
|
||||
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
|
||||
If Not URL.IsEmptyString Then
|
||||
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_DownloadGifs As String = "DownloadGifs"
|
||||
Private Const Name_DownloadPhotoOnlyFromModelHub As String = "DownloadPhotoOnlyFromModelHub"
|
||||
<Obsolete> Private Const Name_IsUser As String = "IsUser"
|
||||
#End Region
|
||||
#Region "Structures"
|
||||
Private Structure FlashVar : Implements IRegExCreator
|
||||
@@ -254,14 +253,7 @@ Namespace API.PornHub
|
||||
DownloadFavorite = .Value(Name_DownloadFavorite).FromXML(Of Boolean)(False)
|
||||
DownloadGifs = .Value(Name_DownloadGifs).FromXML(Of Integer)(False)
|
||||
DownloadPhotoOnlyFromModelHub = .Value(Name_DownloadPhotoOnlyFromModelHub).FromXML(Of Boolean)(True)
|
||||
If .Contains(Name_SiteMode) Then
|
||||
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
|
||||
SiteMode = .Value(Name_SiteMode).FromXML(Of Integer)(SiteModes.User)
|
||||
UpdateUserOptions()
|
||||
Else
|
||||
If UpdateUserOptions() Then .Value(Name_LabelsName) = LabelsString
|
||||
@@ -404,7 +396,8 @@ Namespace API.PornHub
|
||||
Dim r$ = Responser.GetResponse(URL)
|
||||
If Not r.IsEmptyString Then
|
||||
Dim l As List(Of UserVideo) = RegexFields(Of UserVideo)(r, {RegexUserVideos}, {6, 7, 3, 10})
|
||||
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 IsUser Then
|
||||
If Type = VideoTypes.Favorite Then
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports System.Net
|
||||
Imports System.Threading
|
||||
Imports SCrawler.API.Base
|
||||
Imports SCrawler.API.Reddit.M3U8_Declarations
|
||||
Imports PersonalUtilities.Tools
|
||||
Imports PersonalUtilities.Tools.Web
|
||||
@@ -61,15 +62,18 @@ Namespace API.Reddit
|
||||
Private ReadOnly ProgressExists As Boolean
|
||||
Private ReadOnly Property ProgressPre As PreProgress
|
||||
Private ReadOnly UsePreProgress As Boolean
|
||||
Private ReadOnly Media As UserMedia
|
||||
#End Region
|
||||
Private Sub New(ByVal URL As String, ByVal OutFile As SFile, ByVal Progress As MyProgress, ByVal UsePreProgress As Boolean)
|
||||
Private Sub New(ByVal URL As String, ByVal Media As UserMedia, ByVal OutFile As SFile, ByVal Progress As MyProgress, ByVal UsePreProgress As Boolean)
|
||||
PlayListURL = URL
|
||||
Me.Media = Media
|
||||
BaseURL = RegexReplace(URL, BaseUrlPattern)
|
||||
Video = New List(Of String)
|
||||
Audio = New List(Of String)
|
||||
Me.OutFile = OutFile
|
||||
Me.OutFile.Name = "PlayListFile"
|
||||
Me.OutFile.Extension = "mp4"
|
||||
If Media.Post.Date.HasValue Then Me.OutFile.Name = Media.Post.Date.Value.ToString("yyyyMMdd_HHmmss")
|
||||
Me.Progress = Progress
|
||||
ProgressExists = Not Me.Progress Is Nothing
|
||||
ProgressPre = New PreProgress(Progress)
|
||||
@@ -202,9 +206,9 @@ Namespace API.Reddit
|
||||
End Function
|
||||
#End Region
|
||||
#Region "Statics"
|
||||
Friend Shared Function Download(ByVal URL As String, ByVal f As SFile, ByVal Token As CancellationToken,
|
||||
Friend Shared Function Download(ByVal URL As String, ByVal Media As UserMedia, ByVal f As SFile, ByVal Token As CancellationToken,
|
||||
ByVal Progress As MyProgress, ByVal UsePreProgress As Boolean) As SFile
|
||||
Using m As New M3U8(URL, f, Progress, UsePreProgress) : Return m.Download(Token) : End Using
|
||||
Using m As New M3U8(URL, Media, f, Progress, UsePreProgress) : Return m.Download(Token) : End Using
|
||||
End Function
|
||||
#End Region
|
||||
#Region "IDisposable Support"
|
||||
|
||||
@@ -10,6 +10,7 @@ Imports SCrawler.API.Base
|
||||
Imports SCrawler.Plugin
|
||||
Imports SCrawler.Plugin.Attributes
|
||||
Imports PersonalUtilities.Tools.Web.Clients
|
||||
Imports PersonalUtilities.Tools.Web.Clients.Base
|
||||
Imports PersonalUtilities.Tools.Web.Documents.JSON
|
||||
Imports PersonalUtilities.Functions.XML
|
||||
Imports PersonalUtilities.Functions.RegularExpressions
|
||||
@@ -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 &
|
||||
"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
|
||||
<PropertyOption(ControlText:="Use 'cUrl' to get a token", IsAuth:=True), PXML, PClonable, HiddenControl>
|
||||
Private ReadOnly Property BearerTokenUseCurl As PropertyValue
|
||||
#Region "TokenUpdateInterval"
|
||||
<PropertyOption(ControlText:="Token refresh interval", ControlToolTip:="Interval (in minutes) to refresh the token",
|
||||
AllowNull:=False, LeftOffset:=120, IsAuth:=True), PXML, PClonable>
|
||||
@@ -55,11 +58,14 @@ Namespace API.Reddit
|
||||
Return {AuthUserName.Value, AuthPassword.Value, ApiClientID.Value, ApiClientSecret.Value}.All(Function(v$) Not v.IsEmptyString)
|
||||
End Get
|
||||
End Property
|
||||
|
||||
#End Region
|
||||
#Region "Other"
|
||||
<PropertyOption(ControlText:="Use M3U8", ControlToolTip:="Use M3U8 or mp4 for Reddit videos", IsAuth:=False), PXML, PClonable>
|
||||
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
|
||||
#Region "Initializer"
|
||||
@@ -78,7 +84,8 @@ Namespace API.Reddit
|
||||
ApiClientID = New PropertyValue(String.Empty, GetType(String))
|
||||
ApiClientSecret = New PropertyValue(String.Empty, GetType(String))
|
||||
BearerToken = New PropertyValue(token, GetType(String), Sub(v) Responser.Headers.Add(DeclaredNames.Header_Authorization, v))
|
||||
TokenUpdateInterval = New PropertyValue(60 * 12)
|
||||
BearerTokenUseCurl = New PropertyValue(True)
|
||||
TokenUpdateInterval = New PropertyValue(360)
|
||||
TokenUpdateIntervalProvider = New TokenRefreshIntervalProvider
|
||||
BearerTokenDateUpdate = New PropertyValue(Now.AddYears(-1))
|
||||
UseTokenForTimelines = New PropertyValue(False)
|
||||
@@ -87,11 +94,21 @@ Namespace API.Reddit
|
||||
SavedPostsUserName = New PropertyValue(String.Empty, GetType(String))
|
||||
|
||||
UseM3U8 = New PropertyValue(True)
|
||||
CheckImage = New PropertyValue(False)
|
||||
CheckImageReturnOrig = New PropertyValue(True)
|
||||
|
||||
UrlPatternUser = "https://www.reddit.com/{0}/{1}/"
|
||||
ImageVideoContains = "reddit.com"
|
||||
UserRegex = RParams.DM("[htps:/]{7,8}.*?reddit.com/([user]{1,4})/([^/\?&]+)", 0, RegexReturn.ListByMatch, EDP.ReturnValue)
|
||||
End Sub
|
||||
Private Const SettingsVersionCurrent As Integer = 1
|
||||
Friend Overrides Sub EndInit()
|
||||
If CInt(SettingsVersion.Value) < SettingsVersionCurrent Then
|
||||
SettingsVersion.Value = SettingsVersionCurrent
|
||||
TokenUpdateInterval.Value = 360
|
||||
End If
|
||||
MyBase.EndInit()
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "GetInstance"
|
||||
Friend Overrides Function GetInstance(ByVal What As Download) As IPluginContentProvider
|
||||
@@ -263,29 +280,51 @@ Namespace API.Reddit
|
||||
result = False
|
||||
Dim r$ = String.Empty
|
||||
Dim c% = 0
|
||||
Dim _found As Boolean
|
||||
Dim useCurl As Boolean = Settings.CurlFile.Exists And CBool(BearerTokenUseCurl.Value)
|
||||
Dim curlUsed As Boolean = useCurl
|
||||
Do
|
||||
c += 1
|
||||
Using resp As New Responser With {
|
||||
.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 .PayLoadValues
|
||||
.Add("grant_type", "password")
|
||||
.Add("username", UserName)
|
||||
.Add("password", Password)
|
||||
End With
|
||||
.CredentialsUserName = ClientID
|
||||
.CredentialsPassword = ClientSecret
|
||||
.PreAuthenticate = True
|
||||
If useCurl Then
|
||||
If Settings.CurlFile.Exists Then
|
||||
curlUsed = True
|
||||
.Mode = Responser.Modes.Curl
|
||||
.CurlPath = Settings.CurlFile
|
||||
.CurlArgumentsLeft = $"-d ""grant_type=password&username={UserName}&password={Password}"" --user ""{ClientID}:{ClientSecret}"""
|
||||
Else
|
||||
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
|
||||
r = resp.GetResponse("https://www.reddit.com/api/v1/access_token",, EDP.ThrowException)
|
||||
End Using
|
||||
If Not r.IsEmptyString Then
|
||||
Using j As EContainer = JsonDocument.Parse(r)
|
||||
If j.ListExists Then
|
||||
_found = True
|
||||
Dim newToken$ = j.Value("access_token")
|
||||
If Not newToken.IsEmptyString Then
|
||||
BearerToken.Value = $"Bearer {newToken}"
|
||||
@@ -296,7 +335,7 @@ Namespace API.Reddit
|
||||
End If
|
||||
End Using
|
||||
End If
|
||||
Loop While c < 5 And Not _found
|
||||
Loop While c < 5 And Not result
|
||||
End If
|
||||
Return result
|
||||
Catch ex As Exception
|
||||
|
||||
@@ -225,6 +225,7 @@ Namespace API.Reddit
|
||||
#End Region
|
||||
#Region "Download Overrides"
|
||||
Friend Overrides Sub DownloadData(ByVal Token As CancellationToken)
|
||||
Err429Count = 0
|
||||
_CrossPosts.Clear()
|
||||
If CreatedByChannel And Settings.FromChannelDownloadTopUse And Settings.FromChannelDownloadTop > 0 Then _
|
||||
DownloadTopCount = Settings.FromChannelDownloadTop.Value
|
||||
@@ -287,6 +288,7 @@ Namespace API.Reddit
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Download Functions (User, Channel)"
|
||||
Private Err429Count As Integer = 0
|
||||
Private _TotalPostsDownloaded As Integer = 0
|
||||
Private ReadOnly _CrossPosts As List(Of String)
|
||||
Private Const SiteGfycatKey As String = "gfycat"
|
||||
@@ -375,6 +377,7 @@ Namespace API.Reddit
|
||||
Loop While Not _completed
|
||||
End Sub
|
||||
Private Sub DownloadDataChannel(ByVal POST As String, ByVal Token As CancellationToken)
|
||||
Const savedPostsSleepTimer% = 2000
|
||||
Dim eObj% = 0
|
||||
Dim round% = 0
|
||||
Dim URL$ = String.Empty
|
||||
@@ -392,12 +395,14 @@ Namespace API.Reddit
|
||||
|
||||
If IsSavedPosts Then
|
||||
URL = $"https://www.reddit.com/user/{TrueName}/saved.json?after={POST}"
|
||||
If Not POST.IsEmptyString Then Thread.Sleep(savedPostsSleepTimer)
|
||||
Else
|
||||
URL = $"https://reddit.com/r/{TrueName}/{View}.json?allow_quarantined=true&allow_over18=1&include=identity&after={POST}&dist=25&sort={View}&t={Period}&layout=classic"
|
||||
End If
|
||||
|
||||
ThrowAny(Token)
|
||||
Dim r$ = Responser.GetResponse(URL)
|
||||
If IsSavedPosts Then Err429Count = 0
|
||||
If Not r.IsEmptyString Then
|
||||
Using w As EContainer = JsonDocument.Parse(r).XmlIfNothing
|
||||
If w.Count > 0 Then
|
||||
@@ -458,8 +463,12 @@ Namespace API.Reddit
|
||||
End If
|
||||
_completed = True
|
||||
Catch ex As Exception
|
||||
If ProcessException(ex, Token, $"channel data downloading error [{URL}]",, eObj) = HttpStatusCode.InternalServerError Then
|
||||
Dim errValue% = ProcessException(ex, Token, $"{IIf(IsSavedPosts, "saved posts", "channel")} data downloading error [{URL}]",, eObj)
|
||||
If errValue = HttpStatusCode.InternalServerError Then
|
||||
If round = 2 Then eObj = HttpStatusCode.InternalServerError
|
||||
ElseIf errValue = 429 And round = 0 Then
|
||||
Thread.Sleep(savedPostsSleepTimer)
|
||||
round += 1
|
||||
Else
|
||||
_completed = True
|
||||
End If
|
||||
@@ -681,12 +690,16 @@ Namespace API.Reddit
|
||||
End Function
|
||||
Private Function TryImage(ByVal URL As String) As Boolean
|
||||
Try
|
||||
Dim img As Image = GetImage(SFile.GetBytesFromNet(URL, EDP.ThrowException), EDP.ThrowException)
|
||||
If Not img Is Nothing Then
|
||||
img.Dispose()
|
||||
Return True
|
||||
If Not CBool(MySiteSettings.CheckImage.Value) Then
|
||||
Return MySiteSettings.CheckImageReturnOrig.Value
|
||||
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
|
||||
Catch
|
||||
Return False
|
||||
@@ -901,19 +914,24 @@ Namespace API.Reddit
|
||||
Dim RedGifsHost As SettingsHost = Settings(RedGifs.RedGifsSiteKey, RedGifsAccount)
|
||||
If RedGifsHost Is Nothing Then RedGifsHost = Settings(RedGifs.RedGifsSiteKey).Default
|
||||
RedGifsResponser = RedGifsHost.Responser.Copy
|
||||
Dim respNoHeaders As Responser = Responser.Copy
|
||||
Dim m As UserMedia, m2 As UserMedia
|
||||
Dim r$
|
||||
Dim r$, url$
|
||||
Dim j As EContainer
|
||||
Dim lastCount%, li%
|
||||
Dim rv As New ErrorsDescriber(EDP.ReturnValue)
|
||||
respNoHeaders.Headers.Clear()
|
||||
ProgressPre.ChangeMax(_ContentList.Count)
|
||||
For i% = 0 To _ContentList.Count - 1
|
||||
m = _ContentList(i)
|
||||
ProgressPre.Perform()
|
||||
If m.State = UStates.Missing AndAlso Not m.Post.ID.IsEmptyString Then
|
||||
ThrowAny(Token)
|
||||
r = Responser.GetResponse($"https://www.reddit.com/comments/{m.Post.ID.Split("_").LastOrDefault}/.json",, EDP.ReturnValue)
|
||||
url = $"https://www.reddit.com/comments/{m.Post.ID.Split("_").LastOrDefault}/.json"
|
||||
r = Responser.GetResponse(url,, rv)
|
||||
If r.IsEmptyString Then r = respNoHeaders.GetResponse(url,, rv)
|
||||
If Not r.IsEmptyString Then
|
||||
j = JsonDocument.Parse(r, EDP.ReturnValue)
|
||||
j = JsonDocument.Parse(r, rv)
|
||||
If Not j Is Nothing Then
|
||||
If j.Count > 0 Then
|
||||
lastCount = _TempMediaList.Count
|
||||
@@ -971,7 +989,7 @@ Namespace API.Reddit
|
||||
Dim m As New UserMedia(_URL, t) With {.Post = New UserPost With {.ID = PostID, .UserID = _UserID}}
|
||||
If t = UTypes.Picture Or t = UTypes.GIF Then m.File = CreateFileFromUrl(m.URL) Else m.File = Nothing
|
||||
If ReplacePreview And m.URL.Contains("preview") And Not t = UTypes.Picture Then m.URL = $"https://i.redd.it/{m.File.File}"
|
||||
If Not PostDate.IsEmptyString Then m.Post.Date = AConvert(Of Date)(PostDate, DateTrueProvider(IsChannel), Nothing) Else m.Post.Date = Nothing
|
||||
If Not PostDate.IsEmptyString Then m.Post.Date = AConvert(Of Date)(PostDate, DateTrueProvider(IsChannel Or IsSavedPosts), Nothing) Else m.Post.Date = Nothing
|
||||
Return m
|
||||
End Function
|
||||
Private Function TryFile(ByVal URL As String) As Boolean
|
||||
@@ -1023,7 +1041,7 @@ Namespace API.Reddit
|
||||
Return URL.Contains(SiteRedGifsKey)
|
||||
End Function
|
||||
Protected Overrides Function DownloadM3U8(ByVal URL As String, ByVal Media As UserMedia, ByVal DestinationFile As SFile, ByVal Token As CancellationToken) As SFile
|
||||
Return M3U8.Download(URL, DestinationFile, Token, Progress, Not IsSingleObjectDownload)
|
||||
Return M3U8.Download(URL, Media, DestinationFile, Token, Progress, Not IsSingleObjectDownload)
|
||||
End Function
|
||||
Protected Overrides Function ChangeFileNameByProvider(ByVal f As SFile, ByVal m As UserMedia) As SFile
|
||||
If Not IsChannel Or Not SaveToCache Then
|
||||
@@ -1053,8 +1071,11 @@ Namespace API.Reddit
|
||||
ElseIf .StatusCode = HttpStatusCode.InternalServerError Then '500
|
||||
If Not IsNothing(EObj) AndAlso IsNumeric(EObj) AndAlso CInt(EObj) = HttpStatusCode.InternalServerError Then Return 1
|
||||
Return HttpStatusCode.InternalServerError
|
||||
ElseIf .StatusCode = 429 And IsSavedPosts And Err429Count = 0 Then
|
||||
Err429Count += 1
|
||||
Return 429
|
||||
ElseIf .StatusCode = 429 AndAlso
|
||||
((Not IsSavedPosts And CBool(MySiteSettings.UseTokenForTimelines.Value)) Or (IsSavedPosts And MySiteSettings.UseTokenForSavedPosts.Value)) AndAlso
|
||||
((Not IsSavedPosts And CBool(MySiteSettings.UseTokenForTimelines.Value)) Or (IsSavedPosts And CBool(MySiteSettings.UseTokenForSavedPosts.Value))) AndAlso
|
||||
Not MySiteSettings.CredentialsExists Then '429
|
||||
MyMainLOG = $"{ToStringForLog()}: [{CInt(Responser.StatusCode)}] You should use OAuth authorization or disable " &
|
||||
IIf(IsSavedPosts, "token usage for downloading saved posts", "the use of token and cookies for downloading timelines")
|
||||
|
||||
@@ -18,9 +18,9 @@ Namespace API.RedGifs
|
||||
<Manifest(RedGifsSiteKey)>
|
||||
Friend Class SiteSettings : Inherits SiteSettingsBase
|
||||
#Region "Declarations"
|
||||
<PropertyOption(ControlToolTip:="Bearer token", AllowNull:=False), DependentFields(NameOf(UserAgent)), ControlNumber(1), PClonable(Clone:=False)>
|
||||
<PropertyOption(ControlToolTip:="Bearer token", AllowNull:=False), DependentFields(NameOf(UserAgent)), ControlNumber(1), PClonable(Clone:=False), HiddenControl>
|
||||
Friend ReadOnly Property Token As PropertyValue
|
||||
<PropertyOption, ControlNumber(2), PClonable>
|
||||
<PropertyOption, ControlNumber(2), PClonable, HiddenControl>
|
||||
Private ReadOnly Property UserAgent As PropertyValue
|
||||
<PXML> Friend ReadOnly Property TokenLastDateUpdated As PropertyValue
|
||||
Private Const TokenName As String = "authorization"
|
||||
@@ -107,7 +107,9 @@ Namespace API.RedGifs
|
||||
Friend Overrides Sub Update()
|
||||
If _SiteEditorFormOpened Then
|
||||
Dim NewToken$ = AConvert(Of String)(Token.Value, AModes.Var, String.Empty)
|
||||
If Not _LastTokenValue = NewToken Then TokenLastDateUpdated.Value = Now
|
||||
If Not _LastTokenValue = NewToken And Not NewToken.IsEmptyString Then TokenLastDateUpdated.Value = Now
|
||||
If Responser.CookiesExists AndAlso MsgBoxE({"RedGifs doesn't require cookies! Do you still want to use cookies?", "RedGifs cookies"},
|
||||
vbExclamation,,, {"Use", "Don't use"}) = 1 Then Responser.Cookies.Clear()
|
||||
End If
|
||||
MyBase.Update()
|
||||
End Sub
|
||||
|
||||
@@ -13,8 +13,9 @@ Imports PersonalUtilities.Tools.Web.Clients
|
||||
Imports PersonalUtilities.Tools.Web.Cookies
|
||||
Imports PersonalUtilities.Functions.RegularExpressions
|
||||
Imports IG = SCrawler.API.Instagram.SiteSettings
|
||||
Imports DN = SCrawler.API.Base.DeclaredNames
|
||||
Namespace API.ThreadsNet
|
||||
<Manifest("AndyProgram_ThreadsNet"), SeparatedTasks(1)>
|
||||
<Manifest("AndyProgram_ThreadsNet"), SavedPosts, SeparatedTasks(1)>
|
||||
Friend Class SiteSettings : Inherits SiteSettingsBase
|
||||
#Region "Declarations"
|
||||
#Region "Authorization"
|
||||
@@ -25,6 +26,10 @@ Namespace API.ThreadsNet
|
||||
Return __HH_CSRF_TOKEN
|
||||
End Get
|
||||
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
|
||||
<PropertyOption(ControlText:="x-ig-app-id", AllowNull:=False, IsAuth:=True), ControlNumber(10)>
|
||||
Friend Overridable ReadOnly Property HH_IG_APP_ID As PropertyValue
|
||||
@@ -34,13 +39,17 @@ Namespace API.ThreadsNet
|
||||
End Property
|
||||
<PropertyOption(ControlText:="x-asbd-id", AllowNull:=True, IsAuth:=True), ControlNumber(20), PClonable>
|
||||
Friend ReadOnly Property HH_ASBD_ID As PropertyValue
|
||||
<PropertyOption(ControlText:="sec-ch-ua", AllowNull:=True, IsAuth:=True), ControlNumber(30), PClonable>
|
||||
Private ReadOnly Property HH_BROWSER As PropertyValue
|
||||
<PropertyOption(ControlText:="sec-ch-ua-full", ControlToolTip:="sec-ch-ua-full-version-list", AllowNull:=True, IsAuth:=True), ControlNumber(40), PClonable>
|
||||
Private ReadOnly Property HH_BROWSER_EXT As PropertyValue
|
||||
<PropertyOption(ControlText:="sec-ch-ua-platform", ControlToolTip:="sec-ch-ua-platform", AllowNull:=True, IsAuth:=True, LeftOffset:=120), ControlNumber(50), PClonable>
|
||||
Private ReadOnly Property HH_PLATFORM As PropertyValue
|
||||
<PropertyOption(ControlText:="UserAgent", IsAuth:=True), ControlNumber(60), PClonable>
|
||||
<PropertyOption(ControlText:="sec-ch-ua", AllowNull:=True, IsAuth:=True,
|
||||
InheritanceName:=SettingsCLS.HEADER_DEF_sec_ch_ua), ControlNumber(30), PClonable, PXML(OnlyForChecked:=True)>
|
||||
Friend ReadOnly Property HH_BROWSER As PropertyValue
|
||||
<PropertyOption(ControlText:="sec-ch-ua-full", ControlToolTip:=SettingsCLS.HEADER_DEF_sec_ch_ua_full_version_list, AllowNull:=True, IsAuth:=True,
|
||||
InheritanceName:=SettingsCLS.HEADER_DEF_sec_ch_ua_full_version_list), ControlNumber(40), PClonable, PXML(OnlyForChecked:=True)>
|
||||
Friend ReadOnly Property HH_BROWSER_EXT As PropertyValue
|
||||
<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 Sub ChangeResponserFields(ByVal PropName As String, ByVal Value As Object)
|
||||
If Not PropName.IsEmptyString Then
|
||||
@@ -52,7 +61,7 @@ Namespace API.ThreadsNet
|
||||
Case NameOf(HH_CSRF_TOKEN) : f = IG.Header_CSRF_TOKEN
|
||||
Case NameOf(HH_BROWSER) : f = IG.Header_Browser
|
||||
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
|
||||
End Select
|
||||
If Not f.IsEmptyString Then
|
||||
@@ -64,6 +73,19 @@ Namespace API.ThreadsNet
|
||||
End If
|
||||
End Sub
|
||||
#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, Category:=DN.CAT_Timers),
|
||||
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.", Category:="Download"), PXML>
|
||||
Friend ReadOnly Property DownloadData_Impl As PropertyValue
|
||||
#End Region
|
||||
#End Region
|
||||
#Region "Initializer"
|
||||
Friend Sub New(ByVal AccName As String, ByVal Temp As Boolean)
|
||||
@@ -86,8 +108,6 @@ Namespace API.ThreadsNet
|
||||
|
||||
With Responser
|
||||
.Accept = "*/*"
|
||||
'URGENT: remove after debug
|
||||
.DeclaredError = EDP.SendToLog + EDP.ThrowException
|
||||
If .UserAgentExists Then useragent = .UserAgent
|
||||
With .Headers
|
||||
If .Count > 0 Then
|
||||
@@ -96,7 +116,7 @@ Namespace API.ThreadsNet
|
||||
asbd = .Value(IG.Header_ASBD_ID)
|
||||
browser = .Value(IG.Header_Browser)
|
||||
browserExt = .Value(IG.Header_BrowserExt)
|
||||
platform = .Value(IG.Header_Platform)
|
||||
platform = .Value(IG.Header_Platform_Verion)
|
||||
End If
|
||||
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.Authority, "www.threads.net"))
|
||||
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.Origin, "https://www.threads.net"))
|
||||
@@ -108,7 +128,10 @@ Namespace API.ThreadsNet
|
||||
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecFetchMode, "cors"))
|
||||
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecFetchSite, "same-origin"))
|
||||
.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
|
||||
.CookiesExtractMode = Responser.CookiesExtractModes.Any
|
||||
.CookiesUpdateMode = CookieKeeper.UpdateModes.ReplaceByNameAll
|
||||
@@ -122,9 +145,13 @@ Namespace API.ThreadsNet
|
||||
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_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))
|
||||
|
||||
RequestsWaitTimer_Any = New PropertyValue(1000)
|
||||
RequestsWaitTimer_AnyProvider = New IG.TimersChecker(0)
|
||||
DownloadData_Impl = New PropertyValue(True)
|
||||
|
||||
UrlPatternUser = "https://www.threads.net/@{0}"
|
||||
UserRegex = RParams.DMS(String.Format(UserRegexDefaultPattern, "threads.net/@"), 1)
|
||||
ImageVideoContains = "threads.net"
|
||||
@@ -151,7 +178,7 @@ Namespace API.ThreadsNet
|
||||
#End Region
|
||||
#Region "BaseAuthExists, GetUserUrl, GetUserPostUrl"
|
||||
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
|
||||
Friend Overrides Function GetUserUrl(ByVal User As IPluginContentProvider) As String
|
||||
Return String.Format(UrlPatternUser, DirectCast(User, UserData).NameTrue)
|
||||
@@ -167,13 +194,23 @@ Namespace API.ThreadsNet
|
||||
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$ = If(Responser.Cookies.FirstOrDefault(Function(c) c.Name.StringToLower = IG.Header_CSRF_TOKEN_COOKIE)?.Value, String.Empty)
|
||||
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 Class
|
||||
End Namespace
|
||||